summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Peyton Jones <simonpj@microsoft.com>2017-12-08 15:39:05 +0000
committerSimon Peyton Jones <simonpj@microsoft.com>2017-12-08 15:42:55 +0000
commit5695f462f604fc63cbb45a7f3073bc114f9b475f (patch)
tree06794ef33eb835ee89022c8385fef3aadb63f2f0
parent800009d9b78a9b2877e7efc889e8a0b21873990d (diff)
downloadhaskell-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.hs6
-rw-r--r--compiler/simplCore/OccurAnal.hs54
-rw-r--r--compiler/simplCore/SimplCore.hs7
-rw-r--r--compiler/simplCore/SimplUtils.hs12
-rw-r--r--compiler/simplCore/Simplify.hs4
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))