#include "HsVersions.h"
-import DynFlags ( CoreToDo(..), SimplifierSwitch(..),
- SimplifierMode(..), DynFlags, DynFlag(..), dopt,
- getCoreToDo, shouldDumpSimplPhase )
+import DynFlags ( DynFlags, DynFlag(..), dopt )
import CoreSyn
+import CoreSubst
import HscTypes
import CSE ( cseProgram )
import Rules ( RuleBase, emptyRuleBase, mkRuleBase, unionRuleBase,
import IdInfo
import CoreUtils ( coreBindsSize )
import Simplify ( simplTopBinds, simplExpr )
-import SimplEnv ( SimplEnv, simplBinders, mkSimplEnv, setInScopeSet )
+import SimplUtils ( simplEnvForGHCi, simplEnvForRules )
+import SimplEnv
import SimplMonad
import CoreMonad
-import qualified ErrUtils as Err ( dumpIfSet_dyn, dumpIfSet, showPass )
-import CoreLint ( showPass, endPass, endPassIf, endIteration )
+import qualified ErrUtils as Err
+import CoreLint
import FloatIn ( floatInwards )
import FloatOut ( floatOutwards )
import FamInstEnv
import SpecConstr ( specConstrProgram)
import DmdAnal ( dmdAnalPgm )
import WorkWrap ( wwTopBinds )
-#ifdef OLD_STRICTNESS
-import StrictAnal ( saBinds )
-import CprAnalyse ( cprAnalyse )
-#endif
import Vectorise ( vectorise )
import FastString
import Util
us <- mkSplitUniqSupply 's'
let (cp_us, ru_us) = splitUniqSupply us
- -- COMPUTE THE ANNOTATIONS TO USE
- ann_env <- prepareAnnotations hsc_env (Just guts)
-
-- COMPUTE THE RULE BASE TO USE
- (imp_rule_base, guts1) <- prepareRules hsc_env guts ru_us
+ -- See Note [Overall plumbing for rules] in Rules.lhs
+ (hpt_rule_base, guts1) <- prepareRules hsc_env guts ru_us
-- Get the module out of the current HscEnv so we can retrieve it from the monad.
-- This is very convienent for the users of the monad (e.g. plugins do not have to
-- _theoretically_ be changed during the Core pipeline (it's part of ModGuts), which
-- would mean our cached value would go out of date.
let mod = mg_module guts
- (guts2, stats) <- runCoreM hsc_env ann_env imp_rule_base cp_us mod $ do
+ (guts2, stats) <- runCoreM hsc_env hpt_rule_base cp_us mod $ do
-- FIND BUILT-IN PASSES
let builtin_core_todos = getCoreToDo dflags
-> IO CoreExpr
-- simplifyExpr is called by the driver to simplify an
-- expression typed in at the interactive prompt
+--
+-- Also used by Template Haskell
simplifyExpr dflags expr
= do {
; Err.showPass dflags "Simplify"
; us <- mkSplitUniqSupply 's'
; let (expr', _counts) = initSmpl dflags emptyRuleBase emptyFamInstEnvs us $
- simplExprGently gentleSimplEnv expr
+ simplExprGently simplEnvForGHCi expr
; Err.dumpIfSet_dyn dflags Opt_D_dump_simpl "Simplified expression"
(pprCoreExpr expr')
; return expr'
}
-gentleSimplEnv :: SimplEnv
-gentleSimplEnv = mkSimplEnv SimplGently (isAmongSimpl [])
-
doCorePasses :: [CorePass] -> ModGuts -> CoreM ModGuts
-doCorePasses passes guts = foldM (flip doCorePass) guts passes
+doCorePasses passes guts
+ = foldM do_pass guts passes
+ where
+ do_pass guts CoreDoNothing = return guts
+ do_pass guts (CoreDoPasses ps) = doCorePasses ps guts
+ do_pass guts pass
+ = do { dflags <- getDynFlags
+ ; liftIO $ showPass dflags pass
+ ; guts' <- doCorePass pass guts
+ ; liftIO $ endPass dflags pass (mg_binds guts') (mg_rules guts')
+ ; return guts' }
doCorePass :: CorePass -> ModGuts -> CoreM ModGuts
-doCorePass (CoreDoSimplify mode sws) = {-# SCC "Simplify" #-}
- simplifyPgm mode sws
+doCorePass pass@(CoreDoSimplify {}) = {-# SCC "Simplify" #-}
+ simplifyPgm pass
doCorePass CoreCSE = {-# SCC "CommonSubExpr" #-}
- describePass "Common sub-expression" Opt_D_dump_cse $
doPass cseProgram
doCorePass CoreLiberateCase = {-# SCC "LiberateCase" #-}
- describePass "Liberate case" Opt_D_verbose_core2core $
doPassD liberateCase
doCorePass CoreDoFloatInwards = {-# SCC "FloatInwards" #-}
- describePass "Float inwards" Opt_D_verbose_core2core $
doPass floatInwards
doCorePass (CoreDoFloatOutwards f) = {-# SCC "FloatOutwards" #-}
- describePassD (text "Float out" <+> parens (ppr f))
- Opt_D_verbose_core2core $
doPassDUM (floatOutwards f)
doCorePass CoreDoStaticArgs = {-# SCC "StaticArgs" #-}
- describePass "Static argument" Opt_D_verbose_core2core $
doPassU doStaticArgs
doCorePass CoreDoStrictness = {-# SCC "Stranal" #-}
- describePass "Demand analysis" Opt_D_dump_stranal $
doPassDM dmdAnalPgm
doCorePass CoreDoWorkerWrapper = {-# SCC "WorkWrap" #-}
- describePass "Worker Wrapper binds" Opt_D_dump_worker_wrapper $
doPassU wwTopBinds
doCorePass CoreDoSpecialising = {-# SCC "Specialise" #-}
- describePassR "Specialise" Opt_D_dump_spec $
doPassU specProgram
doCorePass CoreDoSpecConstr = {-# SCC "SpecConstr" #-}
- describePassR "SpecConstr" Opt_D_dump_spec $
specConstrProgram
doCorePass (CoreDoVectorisation be) = {-# SCC "Vectorise" #-}
- describePass "Vectorisation" Opt_D_dump_vect $
vectorise be
-doCorePass CoreDoGlomBinds = dontDescribePass $ doPassDM glomBinds
-doCorePass CoreDoPrintCore = dontDescribePass $ observe printCore
-doCorePass (CoreDoRuleCheck phase pat) = dontDescribePass $ ruleCheck phase pat
-
-#ifdef OLD_STRICTNESS
-doCorePass CoreDoOldStrictness = {-# SCC "OldStrictness" #-} doOldStrictness
-#endif
-
+doCorePass CoreDoGlomBinds = doPassDM glomBinds
+doCorePass CoreDoPrintCore = observe printCore
+doCorePass (CoreDoRuleCheck phase pat) = ruleCheck phase pat
doCorePass CoreDoNothing = return
doCorePass (CoreDoPasses passes) = doCorePasses passes
-
-#ifdef OLD_STRICTNESS
-doOldStrictness :: ModGuts -> CoreM ModGuts
-doOldStrictness guts
- = do dfs <- getDynFlags
- guts' <- describePass "Strictness analysis" Opt_D_dump_stranal $
- doPassM (saBinds dfs) guts
- guts'' <- describePass "Constructed Product analysis" Opt_D_dump_cpranal $
- doPass cprAnalyse guts'
- return guts''
-#endif
-
\end{code}
%************************************************************************
%************************************************************************
\begin{code}
-
-dontDescribePass :: (ModGuts -> CoreM ModGuts) -> ModGuts -> CoreM ModGuts
-dontDescribePass = ($)
-
-describePass :: String -> DynFlag -> (ModGuts -> CoreM ModGuts) -> ModGuts -> CoreM ModGuts
-describePass name dflag pass guts = do
- dflags <- getDynFlags
-
- liftIO $ showPass dflags name
- guts' <- pass guts
- liftIO $ endPass dflags name dflag (mg_binds guts')
-
- return guts'
-
-describePassD :: SDoc -> DynFlag -> (ModGuts -> CoreM ModGuts) -> ModGuts -> CoreM ModGuts
-describePassD doc = describePass (showSDoc doc)
-
-describePassR :: String -> DynFlag -> (ModGuts -> CoreM ModGuts) -> ModGuts -> CoreM ModGuts
-describePassR name dflag pass guts = do
- guts' <- describePass name dflag pass guts
- dumpIfSet_dyn Opt_D_dump_rules "Top-level specialisations"
- (pprRulesForUser (rulesOfBinds (mg_binds guts')))
- return guts'
-
printCore _ binds = Err.dumpIfSet True "Print Core" (pprCoreBindings binds)
ruleCheck :: CompilerPhase -> String -> ModGuts -> CoreM ModGuts
ruleCheck current_phase pat guts = do
- let is_active = isActive current_phase
rb <- getRuleBase
dflags <- getDynFlags
liftIO $ Err.showPass dflags "RuleCheck"
- liftIO $ printDump (ruleCheckProgram is_active pat rb (mg_binds guts))
+ liftIO $ printDump (ruleCheckProgram current_phase pat rb (mg_binds guts))
return guts
ModGuts) -- Modified fields are
-- (a) Bindings have rules attached,
+ -- and INLINE rules simplified
-- (b) Rules are now just orphan rules
prepareRules hsc_env@(HscEnv { hsc_dflags = dflags, hsc_HPT = hpt })
guts@(ModGuts { mg_binds = binds, mg_deps = deps
, mg_rules = local_rules, mg_rdr_env = rdr_env })
us
- = do { let -- Simplify the local rules; boringly, we need to make an in-scope set
+ = do { us <- mkSplitUniqSupply 'w'
+
+ ; let -- Simplify the local rules; boringly, we need to make an in-scope set
-- from the local binders, to avoid warnings from Simplify.simplVar
local_ids = mkInScopeSet (mkVarSet (bindersOfBinds binds))
- env = setInScopeSet gentleSimplEnv local_ids
- (better_rules,_) = initSmpl dflags emptyRuleBase emptyFamInstEnvs us $
- (mapM (simplRule env) local_rules)
- home_pkg_rules = hptRules hsc_env (dep_mods deps)
-
- -- Find the rules for locally-defined Ids; then we can attach them
- -- to the binders in the top-level bindings
- --
- -- Reason
- -- - It makes the rules easier to look up
- -- - It means that transformation rules and specialisations for
- -- locally defined Ids are handled uniformly
- -- - It keeps alive things that are referred to only from a rule
- -- (the occurrence analyser knows about rules attached to Ids)
- -- - It makes sure that, when we apply a rule, the free vars
- -- of the RHS are more likely to be in scope
- -- - The imported rules are carried in the in-scope set
- -- which is extended on each iteration by the new wave of
- -- local binders; any rules which aren't on the binding will
- -- thereby get dropped
- (rules_for_locals, rules_for_imps) = partition isLocalRule better_rules
- local_rule_base = extendRuleBaseList emptyRuleBase rules_for_locals
- binds_w_rules = updateBinders local_rule_base binds
-
- hpt_rule_base = mkRuleBase home_pkg_rules
- imp_rule_base = extendRuleBaseList hpt_rule_base rules_for_imps
+ env = setInScopeSet simplEnvForRules local_ids
+ (simpl_rules, _) = initSmpl dflags emptyRuleBase emptyFamInstEnvs us $
+ mapM (simplRule env) local_rules
+
+ ; let (rules_for_locals, rules_for_imps) = partition isLocalRule simpl_rules
+
+ home_pkg_rules = hptRules hsc_env (dep_mods deps)
+ hpt_rule_base = mkRuleBase home_pkg_rules
+ binds_w_rules = updateBinders rules_for_locals binds
+
; Err.dumpIfSet_dyn dflags Opt_D_dump_rules "Transformation rules"
(withPprStyle (mkUserStyle (mkPrintUnqualified dflags rdr_env) AllTheWay) $
- vcat [text "Local rules", pprRules better_rules,
- text "",
- text "Imported rules", pprRuleBase imp_rule_base])
+ vcat [text "Local rules for local Ids", pprRules simpl_rules,
+ blankLine,
+ text "Local rules for imported Ids", pprRuleBase hpt_rule_base])
- ; return (imp_rule_base, guts { mg_binds = binds_w_rules,
+ ; return (hpt_rule_base, guts { mg_binds = binds_w_rules,
mg_rules = rules_for_imps })
}
-updateBinders :: RuleBase -> [CoreBind] -> [CoreBind]
-updateBinders local_rules binds
- = map update_bndrs binds
+-- Note [Attach rules to local ids]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Find the rules for locally-defined Ids; then we can attach them
+-- to the binders in the top-level bindings
+--
+-- Reason
+-- - It makes the rules easier to look up
+-- - It means that transformation rules and specialisations for
+-- locally defined Ids are handled uniformly
+-- - It keeps alive things that are referred to only from a rule
+-- (the occurrence analyser knows about rules attached to Ids)
+-- - It makes sure that, when we apply a rule, the free vars
+-- of the RHS are more likely to be in scope
+-- - The imported rules are carried in the in-scope set
+-- which is extended on each iteration by the new wave of
+-- local binders; any rules which aren't on the binding will
+-- thereby get dropped
+
+updateBinders :: [CoreRule] -> [CoreBind] -> [CoreBind]
+updateBinders rules_for_locals binds
+ = map update_bind binds
where
- update_bndrs (NonRec b r) = NonRec (update_bndr b) r
- update_bndrs (Rec prs) = Rec [(update_bndr b, r) | (b,r) <- prs]
-
- update_bndr bndr = case lookupNameEnv local_rules (idName bndr) of
- Nothing -> bndr
- Just rules -> bndr `addIdSpecialisations` rules
- -- The binder might have some existing rules,
- -- arising from specialisation pragmas
+ local_rules = extendRuleBaseList emptyRuleBase rules_for_locals
+
+ update_bind (NonRec b r) = NonRec (add_rules b) r
+ update_bind (Rec prs) = Rec (mapFst add_rules prs)
+
+ -- See Note [Attach rules to local ids]
+ -- NB: the binder might have some existing rules,
+ -- arising from specialisation pragmas
+ add_rules bndr
+ | Just rules <- lookupNameEnv local_rules (idName bndr)
+ = bndr `addIdSpecialisations` rules
+ | otherwise
+ = bndr
\end{code}
Note [Simplifying the left-hand side of a RULE]
otherwise we don't match when given an argument like
augment (\a. h a a) (build h)
+The simplifier does indeed do eta reduction (it's in
+Simplify.completeLam) but only if -O is on.
+
\begin{code}
+simplRule :: SimplEnv -> CoreRule -> SimplM CoreRule
simplRule env rule@(BuiltinRule {})
= return rule
simplRule env rule@(Rule { ru_bndrs = bndrs, ru_args = args, ru_rhs = rhs })
= do (env, bndrs') <- simplBinders env bndrs
args' <- mapM (simplExprGently env) args
rhs' <- simplExprGently env rhs
- return (rule { ru_bndrs = bndrs', ru_args = args', ru_rhs = rhs' })
-
--- It's important that simplExprGently does eta reduction.
--- For example, in a rule like:
--- augment g (build h)
--- we do not want to get
--- augment (\a. g a) (build h)
--- otherwise we don't match when given an argument like
--- (\a. h a a)
---
--- The simplifier does indeed do eta reduction (it's in
--- Simplify.completeLam) but only if -O is on.
+ return (rule { ru_bndrs = bndrs', ru_args = args'
+ , ru_rhs = occurAnalyseExpr rhs' })
\end{code}
\begin{code}
%************************************************************************
\begin{code}
-simplifyPgm :: SimplifierMode -> [SimplifierSwitch] -> ModGuts -> CoreM ModGuts
-simplifyPgm mode switches
- = describePassD doc Opt_D_dump_simpl_phases $ \guts ->
- do { hsc_env <- getHscEnv
+simplifyPgm :: CoreToDo -> ModGuts -> CoreM ModGuts
+simplifyPgm pass guts
+ = do { hsc_env <- getHscEnv
; us <- getUniqueSupplyM
; rb <- getRuleBase
- ; let fam_inst_env = mg_fam_inst_env guts
- dump_phase = shouldDumpSimplPhase (hsc_dflags hsc_env) mode
- simplify_pgm = simplifyPgmIO dump_phase mode switches
- hsc_env us rb fam_inst_env
-
- ; doPassM (liftIOWithCount . simplify_pgm) guts }
- where
- doc = ptext (sLit "Simplifier Phase") <+> text (showPpr mode)
-
-simplifyPgmIO :: Bool
- -> SimplifierMode
- -> [SimplifierSwitch]
- -> HscEnv
- -> UniqSupply
- -> RuleBase
- -> FamInstEnv
- -> [CoreBind]
- -> IO (SimplCount, [CoreBind]) -- New bindings
-
-simplifyPgmIO dump_phase mode switches hsc_env us imp_rule_base fam_inst_env binds
+ ; liftIOWithCount $
+ simplifyPgmIO pass hsc_env us rb guts }
+
+simplifyPgmIO :: CoreToDo
+ -> HscEnv
+ -> UniqSupply
+ -> RuleBase
+ -> ModGuts
+ -> IO (SimplCount, ModGuts) -- New bindings
+
+simplifyPgmIO pass@(CoreDoSimplify mode max_iterations switches)
+ hsc_env us hpt_rule_base
+ guts@(ModGuts { mg_binds = binds, mg_rules = rules
+ , mg_fam_inst_env = fam_inst_env })
= do {
- (termination_msg, it_count, counts_out, binds')
- <- do_iteration us 1 (zeroSimplCount dflags) binds ;
+ (termination_msg, it_count, counts_out, guts')
+ <- do_iteration us 1 (zeroSimplCount dflags) binds rules ;
Err.dumpIfSet (dump_phase && dopt Opt_D_dump_simpl_stats dflags)
"Simplifier statistics for following pass"
(vcat [text termination_msg <+> text "after" <+> ppr it_count <+> text "iterations",
- text "",
+ blankLine,
pprSimplCount counts_out]);
- return (counts_out, binds')
+ return (counts_out, guts')
}
where
- dflags = hsc_dflags hsc_env
-
- sw_chkr = isAmongSimpl switches
- max_iterations = intSwitchSet sw_chkr MaxSimplifierIterations `orElse` 2
-
- do_iteration us iteration_no counts binds
+ dflags = hsc_dflags hsc_env
+ dump_phase = dumpSimplPhase dflags mode
+ sw_chkr = isAmongSimpl switches
+ do_iteration :: UniqSupply
+ -> Int -- Counts iterations
+ -> SimplCount -- Logs optimisations performed
+ -> [CoreBind] -- Bindings in
+ -> [CoreRule] -- and orphan rules
+ -> IO (String, Int, SimplCount, ModGuts)
+
+ do_iteration us iteration_no counts binds rules
-- iteration_no is the number of the iteration we are
-- about to begin, with '1' for the first
| iteration_no > max_iterations -- Stop if we've run out of iterations
" iterations; bailing out. Size = " ++ show (coreBindsSize binds) ++ "\n" ))
-- Subtract 1 from iteration_no to get the
-- number of iterations we actually completed
- return ("Simplifier bailed out", iteration_no - 1, counts, binds)
+ return ("Simplifier bailed out", iteration_no - 1, counts,
+ guts { mg_binds = binds, mg_rules = rules })
-- Try and force thunks off the binds; significantly reduces
-- space usage, especially with -O. JRS, 000620.
| let sz = coreBindsSize binds in sz == sz
= do {
-- Occurrence analysis
- let { tagged_binds = {-# SCC "OccAnal" #-} occurAnalysePgm binds } ;
+ let { tagged_binds = {-# SCC "OccAnal" #-} occurAnalysePgm binds rules } ;
Err.dumpIfSet_dyn dflags Opt_D_dump_occur_anal "Occurrence analysis"
(pprCoreBindings tagged_binds);
-- Get any new rules, and extend the rule base
+ -- See Note [Overall plumbing for rules] in Rules.lhs
-- We need to do this regularly, because simplification can
-- poke on IdInfo thunks, which in turn brings in new rules
-- behind the scenes. Otherwise there's a danger we'll simply
-- miss the rules for Ids hidden inside imported inlinings
eps <- hscEPS hsc_env ;
- let { rule_base' = unionRuleBase imp_rule_base (eps_rule_base eps)
- ; simpl_env = mkSimplEnv mode sw_chkr
+ let { rule_base1 = unionRuleBase hpt_rule_base (eps_rule_base eps)
+ ; rule_base2 = extendRuleBaseList rule_base1 rules
+ ; simpl_env = mkSimplEnv sw_chkr mode
; simpl_binds = {-# SCC "SimplTopBinds" #-}
simplTopBinds simpl_env tagged_binds
; fam_envs = (eps_fam_inst_env eps, fam_inst_env) } ;
-- case t of {(_,counts') -> if counts'=0 then ... }
-- So the conditional didn't force counts', because the
-- selection got duplicated. Sigh!
- case initSmpl dflags rule_base' fam_envs us1 simpl_binds of {
- (binds', counts') -> do {
+ case initSmpl dflags rule_base2 fam_envs us1 simpl_binds of {
+ (env1, counts1) -> do {
- let { all_counts = counts `plusSimplCount` counts'
- ; herald = "Simplifier mode " ++ showPpr mode ++
- ", iteration " ++ show iteration_no ++
- " out of " ++ show max_iterations
+ let { all_counts = counts `plusSimplCount` counts1
+ ; binds1 = getFloats env1
+ ; rules1 = substRulesForImportedIds (mkCoreSubst (text "imp-rules") env1) rules
} ;
-- Stop if nothing happened; don't dump output
- if isZeroSimplCount counts' then
- return ("Simplifier reached fixed point", iteration_no,
- all_counts, binds')
+ if isZeroSimplCount counts1 then
+ return ("Simplifier reached fixed point", iteration_no, all_counts,
+ guts { mg_binds = binds1, mg_rules = rules1 })
else do {
-- Short out indirections
-- We do this *after* at least one run of the simplifier
--
-- ToDo: alas, this means that indirection-shorting does not happen at all
-- if the simplifier does nothing (not common, I know, but unsavoury)
- let { binds'' = {-# SCC "ZapInd" #-} shortOutIndirections binds' } ;
+ let { binds2 = {-# SCC "ZapInd" #-} shortOutIndirections binds1 } ;
-- Dump the result of this iteration
- Err.dumpIfSet_dyn dflags Opt_D_dump_simpl_iterations herald
- (pprSimplCount counts') ;
- endIteration dflags herald Opt_D_dump_simpl_iterations binds'' ;
+ end_iteration dflags pass iteration_no counts1 binds2 rules1 ;
-- Loop
- do_iteration us2 (iteration_no + 1) all_counts binds''
+ do_iteration us2 (iteration_no + 1) all_counts binds2 rules1
} } } }
where
(us1, us2) = splitUniqSupply us
+
+-------------------
+end_iteration :: DynFlags -> CoreToDo -> Int
+ -> SimplCount -> [CoreBind] -> [CoreRule] -> IO ()
+-- Same as endIteration but with simplifier counts
+end_iteration dflags pass iteration_no counts binds rules
+ = do { dumpIfSet (dopt Opt_D_dump_simpl_iterations dflags)
+ pass (ptext (sLit "Simplifier counts"))
+ (pprSimplCount counts)
+
+ ; endIteration dflags pass iteration_no binds rules }
\end{code}
RULES: we want to *add* any RULES for x_local to x_exported.
-Note [Messing up the exported Id's IdInfo]
+Note [Messing up the exported Id's RULES]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We must be careful about discarding the IdInfo on the old Id
-
-The example that went bad on me at one stage was this one:
+We must be careful about discarding (obviously) or even merging the
+RULES on the exported Id. The example that went bad on me at one stage
+was this one:
iterate :: (a -> a) -> a -> [a]
[Exported]
= modifyIdInfo transfer exported_id
where
local_info = idInfo local_id
- transfer exp_info = exp_info `setNewStrictnessInfo` newStrictnessInfo local_info
- `setWorkerInfo` workerInfo local_info
+ transfer exp_info = exp_info `setStrictnessInfo` strictnessInfo local_info
+ `setUnfoldingInfo` unfoldingInfo local_info
`setInlinePragInfo` inlinePragInfo local_info
`setSpecInfo` addSpecInfo (specInfo exp_info) new_info
new_info = setSpecInfoHead (idName exported_id)