diff options
author | Simon Peyton Jones <simonpj@microsoft.com> | 2017-12-08 15:39:05 +0000 |
---|---|---|
committer | Simon Peyton Jones <simonpj@microsoft.com> | 2017-12-08 15:42:55 +0000 |
commit | 5695f462f604fc63cbb45a7f3073bc114f9b475f (patch) | |
tree | 06794ef33eb835ee89022c8385fef3aadb63f2f0 | |
parent | 800009d9b78a9b2877e7efc889e8a0b21873990d (diff) | |
download | haskell-5695f462f604fc63cbb45a7f3073bc114f9b475f.tar.gz |
Occurrrence analysis improvements for NOINLINE functions
This patch fixes #14567. The idea is simple: if a function
is marked NOINLINE then it makes a great candidate for a loop
breaker.
Implementation is easy too, but it needs a little extra plubming,
notably the occ_unf_act field in OccEnv
-rw-r--r-- | compiler/coreSyn/CoreOpt.hs | 6 | ||||
-rw-r--r-- | compiler/simplCore/OccurAnal.hs | 54 | ||||
-rw-r--r-- | compiler/simplCore/SimplCore.hs | 7 | ||||
-rw-r--r-- | compiler/simplCore/SimplUtils.hs | 12 | ||||
-rw-r--r-- | compiler/simplCore/Simplify.hs | 4 |
5 files changed, 47 insertions, 36 deletions
diff --git a/compiler/coreSyn/CoreOpt.hs b/compiler/coreSyn/CoreOpt.hs index 47a4e35a67..4240647d58 100644 --- a/compiler/coreSyn/CoreOpt.hs +++ b/compiler/coreSyn/CoreOpt.hs @@ -136,8 +136,10 @@ simpleOptPgm dflags this_mod binds rules vects ; return (reverse binds', rules', vects') } where - occ_anald_binds = occurAnalysePgm this_mod (\_ -> False) {- No rules active -} - rules vects emptyVarSet binds + occ_anald_binds = occurAnalysePgm this_mod + (\_ -> True) {- All unfoldings active -} + (\_ -> False) {- No rules active -} + rules vects emptyVarSet binds (final_env, binds') = foldl do_one (emptyEnv, []) occ_anald_binds final_subst = soe_subst final_env diff --git a/compiler/simplCore/OccurAnal.hs b/compiler/simplCore/OccurAnal.hs index c2b4bd4fc5..e2beb742a8 100644 --- a/compiler/simplCore/OccurAnal.hs +++ b/compiler/simplCore/OccurAnal.hs @@ -58,11 +58,12 @@ import Control.Arrow ( second ) Here's the externally-callable interface: -} -occurAnalysePgm :: Module -- Used only in debug output - -> (Activation -> Bool) +occurAnalysePgm :: Module -- Used only in debug output + -> (Id -> Bool) -- Active unfoldings + -> (Activation -> Bool) -- Active rules -> [CoreRule] -> [CoreVect] -> VarSet -> CoreProgram -> CoreProgram -occurAnalysePgm this_mod active_rule imp_rules vects vectVars binds +occurAnalysePgm this_mod active_unf active_rule imp_rules vects vectVars binds | isEmptyDetails final_usage = occ_anald_binds @@ -71,7 +72,9 @@ occurAnalysePgm this_mod active_rule imp_rules vects vectVars binds 2 (ppr final_usage ) ) occ_anald_glommed_binds where - init_env = initOccEnv active_rule + init_env = initOccEnv { occ_rule_act = active_rule + , occ_unf_act = active_unf } + (final_usage, occ_anald_binds) = go init_env binds (_, occ_anald_glommed_binds) = occAnalRecBind init_env TopLevel imp_rule_edges @@ -120,9 +123,7 @@ occurAnalyseExpr' :: Bool -> CoreExpr -> CoreExpr occurAnalyseExpr' enable_binder_swap expr = snd (occAnal env expr) where - env = (initOccEnv all_active_rules) {occ_binder_swap = enable_binder_swap} - -- To be conservative, we say that all inlines and rules are active - all_active_rules = \_ -> True + env = initOccEnv { occ_binder_swap = enable_binder_swap } {- Note [Plugin rules] ~~~~~~~~~~~~~~~~~~~~~~ @@ -837,7 +838,7 @@ occAnalNonRecBind env lvl imp_rule_edges binder rhs body_usage occAnalRecBind :: OccEnv -> TopLevelFlag -> ImpRuleEdges -> [(Var,CoreExpr)] -> UsageDetails -> (UsageDetails, [CoreBind]) occAnalRecBind env lvl imp_rule_edges pairs body_usage - = foldr (occAnalRec lvl) (body_usage, []) sccs + = foldr (occAnalRec env lvl) (body_usage, []) sccs -- For a recursive group, we -- * occ-analyse all the RHSs -- * compute strongly-connected components @@ -864,14 +865,14 @@ calls for the purpose of finding join points. -} ----------------------------- -occAnalRec :: TopLevelFlag +occAnalRec :: OccEnv -> TopLevelFlag -> SCC Details -> (UsageDetails, [CoreBind]) -> (UsageDetails, [CoreBind]) -- The NonRec case is just like a Let (NonRec ...) above -occAnalRec lvl (AcyclicSCC (ND { nd_bndr = bndr, nd_rhs = rhs - , nd_uds = rhs_uds, nd_rhs_bndrs = rhs_bndrs })) +occAnalRec _ lvl (AcyclicSCC (ND { nd_bndr = bndr, nd_rhs = rhs + , nd_uds = rhs_uds, nd_rhs_bndrs = rhs_bndrs })) (body_uds, binds) | not (bndr `usedIn` body_uds) = (body_uds, binds) -- See Note [Dead code] @@ -887,7 +888,7 @@ occAnalRec lvl (AcyclicSCC (ND { nd_bndr = bndr, nd_rhs = rhs -- The Rec case is the interesting one -- See Note [Recursive bindings: the grand plan] -- See Note [Loop breaking] -occAnalRec lvl (CyclicSCC details_s) (body_uds, binds) +occAnalRec env lvl (CyclicSCC details_s) (body_uds, binds) | not (any (`usedIn` body_uds) bndrs) -- NB: look at body_uds, not total_uds = (body_uds, binds) -- See Note [Dead code] @@ -906,7 +907,7 @@ occAnalRec lvl (CyclicSCC details_s) (body_uds, binds) final_uds :: UsageDetails loop_breaker_nodes :: [LetrecNode] (final_uds, loop_breaker_nodes) - = mkLoopBreakerNodes lvl bndr_set body_uds details_s + = mkLoopBreakerNodes env lvl bndr_set body_uds details_s ------------------------------ weak_fvs :: VarSet @@ -1283,7 +1284,7 @@ makeNode env imp_rule_edges bndr_set (bndr, rhs) -- isn't the right thing (it tells about -- RULE activation), so we'd need more plumbing -mkLoopBreakerNodes :: TopLevelFlag +mkLoopBreakerNodes :: OccEnv -> TopLevelFlag -> VarSet -> UsageDetails -- for BODY of let -> [Details] @@ -1296,7 +1297,7 @@ mkLoopBreakerNodes :: TopLevelFlag -- the loop-breaker SCC analysis -- d) adjust each RHS's usage details according to -- the binder's (new) shotness and join-point-hood -mkLoopBreakerNodes lvl bndr_set body_uds details_s +mkLoopBreakerNodes env lvl bndr_set body_uds details_s = (final_uds, zipWith mk_lb_node details_s bndrs') where (final_uds, bndrs') = tagRecBinders lvl body_uds @@ -1312,7 +1313,7 @@ mkLoopBreakerNodes lvl bndr_set body_uds details_s -- Note [Deterministic SCC] in Digraph. where nd' = nd { nd_bndr = bndr', nd_score = score } - score = nodeScore bndr bndr' rhs lb_deps + score = nodeScore env bndr bndr' rhs lb_deps lb_deps = extendFvs_ rule_fv_env inl_fvs rule_fv_env :: IdEnv IdSet @@ -1328,18 +1329,22 @@ mkLoopBreakerNodes lvl bndr_set body_uds details_s ------------------------------------------ -nodeScore :: Id -- Binder has old occ-info (just for loop-breaker-ness) +nodeScore :: OccEnv + -> Id -- Binder has old occ-info (just for loop-breaker-ness) -> Id -- Binder with new occ-info -> CoreExpr -- RHS -> VarSet -- Loop-breaker dependencies -> NodeScore -nodeScore old_bndr new_bndr bind_rhs lb_deps +nodeScore env old_bndr new_bndr bind_rhs lb_deps | not (isId old_bndr) -- A type or cercion variable is never a loop breaker = (100, 0, False) | old_bndr `elemVarSet` lb_deps -- Self-recursive things are great loop breakers = (0, 0, True) -- See Note [Self-recursion and loop breakers] + | not (occ_unf_act env old_bndr) -- A binder whose inlining is inactive (e.g. has + = (0, 0, True) -- a NOINLINE pragam) makes a great loop breaker + | exprIsTrivial rhs = mk_score 10 -- Practically certain to be inlined -- Used to have also: && not (isExportedId bndr) @@ -2097,8 +2102,12 @@ data OccEnv = OccEnv { occ_encl :: !OccEncl -- Enclosing context information , occ_one_shots :: !OneShots -- See Note [OneShots] , occ_gbl_scrut :: GlobalScruts + + , occ_unf_act :: Id -> Bool -- Which Id unfoldings are active + , occ_rule_act :: Activation -> Bool -- Which rules are active -- See Note [Finding rule RHS free vars] + , occ_binder_swap :: !Bool -- enable the binder_swap -- See CorePrep Note [Dead code in CorePrep] } @@ -2127,12 +2136,15 @@ instance Outputable OccEncl where -- See note [OneShots] type OneShots = [OneShotInfo] -initOccEnv :: (Activation -> Bool) -> OccEnv -initOccEnv active_rule +initOccEnv :: OccEnv +initOccEnv = OccEnv { occ_encl = OccVanilla , occ_one_shots = [] , occ_gbl_scrut = emptyVarSet - , occ_rule_act = active_rule + -- To be conservative, we say that all + -- inlines and rules are active + , occ_unf_act = \_ -> True + , occ_rule_act = \_ -> True , occ_binder_swap = True } vanillaCtxt :: OccEnv -> OccEnv diff --git a/compiler/simplCore/SimplCore.hs b/compiler/simplCore/SimplCore.hs index be44ca86a9..6592ff60e1 100644 --- a/compiler/simplCore/SimplCore.hs +++ b/compiler/simplCore/SimplCore.hs @@ -26,7 +26,7 @@ import CoreUtils ( mkTicks, stripTicksTop ) import CoreLint ( endPass, lintPassResult, dumpPassResult, lintAnnots ) import Simplify ( simplTopBinds, simplExpr, simplRules ) -import SimplUtils ( simplEnvForGHCi, activeRule ) +import SimplUtils ( simplEnvForGHCi, activeRule, activeUnfolding ) import SimplEnv import SimplMonad import CoreMonad @@ -690,7 +690,8 @@ simplifyPgmIO pass@(CoreDoSimplify max_iterations mode) dflags = hsc_dflags hsc_env print_unqual = mkPrintUnqualified dflags rdr_env simpl_env = mkSimplEnv mode - active_rule = activeRule simpl_env + active_rule = activeRule mode + active_unf = activeUnfolding mode do_iteration :: UniqSupply -> Int -- Counts iterations @@ -744,7 +745,7 @@ simplifyPgmIO pass@(CoreDoSimplify max_iterations mode) InitialPhase -> (mg_vect_decls guts, vectVars) _ -> ([], vectVars) ; tagged_binds = {-# SCC "OccAnal" #-} - occurAnalysePgm this_mod active_rule rules + occurAnalysePgm this_mod active_unf active_rule rules maybeVects maybeVectVars binds } ; Err.dumpIfSet_dyn dflags Opt_D_dump_occur_anal "Occurrence analysis" diff --git a/compiler/simplCore/SimplUtils.hs b/compiler/simplCore/SimplUtils.hs index f2cf7a6606..3f42b0306c 100644 --- a/compiler/simplCore/SimplUtils.hs +++ b/compiler/simplCore/SimplUtils.hs @@ -935,8 +935,8 @@ mark it 'demanded', so when the RHS is simplified, it'll get an ArgOf continuation. -} -activeUnfolding :: SimplEnv -> Id -> Bool -activeUnfolding env id +activeUnfolding :: SimplMode -> Id -> Bool +activeUnfolding mode id | isCompulsoryUnfolding (realIdUnfolding id) = True -- Even sm_inline can't override compulsory unfoldings | otherwise @@ -947,8 +947,6 @@ activeUnfolding env id -- (a) they are active -- (b) sm_inline says so, except that for stable unfoldings -- (ie pragmas) we inline anyway - where - mode = getMode env getUnfoldingInRuleMatch :: SimplEnv -> InScopeEnv -- When matching in RULE, we want to "look through" an unfolding @@ -973,13 +971,11 @@ getUnfoldingInRuleMatch env | otherwise = isActive (sm_phase mode) (idInlineActivation id) ---------------------- -activeRule :: SimplEnv -> Activation -> Bool +activeRule :: SimplMode -> Activation -> Bool -- Nothing => No rules at all -activeRule env +activeRule mode | not (sm_rules mode) = \_ -> False -- Rewriting is off | otherwise = isActive (sm_phase mode) - where - mode = getMode env {- ************************************************************************ diff --git a/compiler/simplCore/Simplify.hs b/compiler/simplCore/Simplify.hs index b24163695e..3f60257d04 100644 --- a/compiler/simplCore/Simplify.hs +++ b/compiler/simplCore/Simplify.hs @@ -1673,7 +1673,7 @@ completeCall env var cont (lone_variable, arg_infos, call_cont) = contArgs cont n_val_args = length arg_infos interesting_cont = interestingCallContext env call_cont - unfolding = activeUnfolding env var + unfolding = activeUnfolding (getMode env) var dump_inline unfolding cont | not (dopt Opt_D_dump_inlinings dflags) = return () @@ -1898,7 +1898,7 @@ tryRules env rules fn args call_cont -} | Just (rule, rule_rhs) <- lookupRule dflags (getUnfoldingInRuleMatch env) - (activeRule env) fn + (activeRule (getMode env)) fn (argInfoAppArgs args) rules -- Fire a rule for the function = do { checkedTick (RuleFired (ruleName rule)) |