X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=compiler%2FsimplCore%2FSimplCore.lhs;h=beb1ed0e7cfb74c8b75d28cd5b28611c9c2a611f;hp=2fd10268a368ea7ba75f296356cb8ecd87ae7ab4;hb=2662dbc5b2c30fc11ccb99e7f9b2dba794d680ba;hpb=647546977c3b6869c027b33c3de841ff13afc912 diff --git a/compiler/simplCore/SimplCore.lhs b/compiler/simplCore/SimplCore.lhs index 2fd1026..beb1ed0 100644 --- a/compiler/simplCore/SimplCore.lhs +++ b/compiler/simplCore/SimplCore.lhs @@ -4,37 +4,48 @@ \section[SimplCore]{Driver for simplifying @Core@ programs} \begin{code} +{-# OPTIONS -w #-} +-- The above warning supression flag is a temporary kludge. +-- While working on this module you are encouraged to remove it and fix +-- any warnings in the module. See +-- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings +-- for details + module SimplCore ( core2core, simplifyExpr ) where #include "HsVersions.h" import DynFlags ( CoreToDo(..), SimplifierSwitch(..), SimplifierMode(..), DynFlags, DynFlag(..), dopt, - getCoreToDo ) + getCoreToDo, shouldDumpSimplPhase ) import CoreSyn -import HscTypes ( HscEnv(..), ModGuts(..), ExternalPackageState(..), - Dependencies( dep_mods ), - hscEPS, hptRules ) +import CoreSubst +import HscTypes import CSE ( cseProgram ) import Rules ( RuleBase, emptyRuleBase, mkRuleBase, unionRuleBase, - extendRuleBaseList, pprRuleBase, ruleCheckProgram, + extendRuleBaseList, pprRuleBase, pprRulesForUser, + ruleCheckProgram, rulesOfBinds, addSpecInfo, addIdSpecialisations ) import PprCore ( pprCoreBindings, pprCoreExpr, pprRules ) import OccurAnal ( occurAnalysePgm, occurAnalyseExpr ) -import IdInfo ( setNewStrictnessInfo, newStrictnessInfo, - setWorkerInfo, workerInfo, - setInlinePragInfo, inlinePragInfo, - setSpecInfo, specInfo, specInfoRules ) +import IdInfo import CoreUtils ( coreBindsSize ) import Simplify ( simplTopBinds, simplExpr ) -import SimplEnv ( SimplEnv, simplBinders, mkSimplEnv, setInScopeSet ) +import SimplUtils ( simplEnvForGHCi, simplEnvForRules ) +import SimplEnv import SimplMonad -import ErrUtils ( dumpIfSet, dumpIfSet_dyn, showPass ) -import CoreLint ( endPass ) +import CoreMonad +import qualified ErrUtils as Err +import CoreLint +import CoreMonad ( endPass ) import FloatIn ( floatInwards ) import FloatOut ( floatOutwards ) -import Id ( Id, modifyIdInfo, idInfo, isExportedId, isLocalId, - idSpecialisation, idName ) +import FamInstEnv +import Id +import DataCon +import TyCon ( tyConDataCons ) +import Class ( classSelIds ) +import BasicTypes ( CompilerPhase, isActive, isDefaultInlinePragma ) import VarSet import VarEnv import NameEnv ( lookupNameEnv ) @@ -44,16 +55,16 @@ import Specialise ( specProgram) 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 import UniqSupply ( UniqSupply, mkSplitUniqSupply, splitUniqSupply ) -import IO ( hPutStr, stderr ) import Outputable -import List ( partition ) -import Maybes ( orElse ) +import Control.Monad +import Data.List +import System.IO +import Maybes \end{code} %************************************************************************ @@ -67,136 +78,211 @@ core2core :: HscEnv -> ModGuts -> IO ModGuts -core2core hsc_env guts - = do - let dflags = hsc_dflags hsc_env - core_todos = getCoreToDo dflags +core2core hsc_env guts = do + let dflags = hsc_dflags hsc_env + + us <- mkSplitUniqSupply 's' + let (cp_us, ru_us) = splitUniqSupply us + + -- COMPUTE THE ANNOTATIONS TO USE + ann_env <- prepareAnnotations hsc_env (Just guts) - us <- mkSplitUniqSupply 's' - let (cp_us, ru_us) = splitUniqSupply us + -- COMPUTE THE RULE BASE TO USE + (hpt_rule_base, guts1) <- prepareRules hsc_env guts ru_us - -- COMPUTE THE RULE BASE TO USE - (imp_rule_base, guts') <- 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 + -- consume the ModGuts to find the module) but somewhat ugly because mg_module may + -- _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 hpt_rule_base cp_us mod $ do + -- FIND BUILT-IN PASSES + let builtin_core_todos = getCoreToDo dflags - -- DO THE BUSINESS - (stats, guts'') <- doCorePasses hsc_env imp_rule_base cp_us - (zeroSimplCount dflags) - guts' core_todos + -- DO THE BUSINESS + doCorePasses builtin_core_todos guts1 - dumpIfSet_dyn dflags Opt_D_dump_simpl_stats - "Grand total simplifier statistics" - (pprSimplCount stats) + Err.dumpIfSet_dyn dflags Opt_D_dump_simpl_stats + "Grand total simplifier statistics" + (pprSimplCount stats) - return guts'' + return guts2 +type CorePass = CoreToDo + simplifyExpr :: DynFlags -- includes spec of what core-to-core passes to do -> CoreExpr -> 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 { - ; showPass dflags "Simplify" + ; Err.showPass dflags "Simplify" ; us <- mkSplitUniqSupply 's' - ; let (expr', _counts) = initSmpl dflags us $ - simplExprGently gentleSimplEnv expr + ; let (expr', _counts) = initSmpl dflags emptyRuleBase emptyFamInstEnvs us $ + simplExprGently simplEnvForGHCi expr - ; dumpIfSet_dyn dflags Opt_D_dump_simpl "Simplified expression" + ; Err.dumpIfSet_dyn dflags Opt_D_dump_simpl "Simplified expression" (pprCoreExpr expr') ; return expr' } -gentleSimplEnv :: SimplEnv -gentleSimplEnv = mkSimplEnv SimplGently - (isAmongSimpl []) - emptyRuleBase - -doCorePasses :: HscEnv - -> RuleBase -- the imported main rule base - -> UniqSupply -- uniques - -> SimplCount -- simplifier stats - -> ModGuts -- local binds in (with rules attached) - -> [CoreToDo] -- which passes to do - -> IO (SimplCount, ModGuts) - -doCorePasses hsc_env rb us stats guts [] - = return (stats, guts) - -doCorePasses hsc_env rb us stats guts (to_do : to_dos) - = do - let (us1, us2) = splitUniqSupply us - (stats1, guts1) <- doCorePass to_do hsc_env us1 rb guts - doCorePasses hsc_env rb us2 (stats `plusSimplCount` stats1) guts1 to_dos - -doCorePass (CoreDoSimplify mode sws) = _scc_ "Simplify" simplifyPgm mode sws -doCorePass CoreCSE = _scc_ "CommonSubExpr" trBinds cseProgram -doCorePass CoreLiberateCase = _scc_ "LiberateCase" trBinds liberateCase -doCorePass CoreDoFloatInwards = _scc_ "FloatInwards" trBinds floatInwards -doCorePass (CoreDoFloatOutwards f) = _scc_ "FloatOutwards" trBindsU (floatOutwards f) -doCorePass CoreDoStaticArgs = _scc_ "StaticArgs" trBinds doStaticArgs -doCorePass CoreDoStrictness = _scc_ "Stranal" trBinds dmdAnalPgm -doCorePass CoreDoWorkerWrapper = _scc_ "WorkWrap" trBindsU wwTopBinds -doCorePass CoreDoSpecialising = _scc_ "Specialise" trBindsU specProgram -doCorePass CoreDoSpecConstr = _scc_ "SpecConstr" trBindsU specConstrProgram -doCorePass CoreDoGlomBinds = trBinds glomBinds -doCorePass CoreDoPrintCore = observe printCore -doCorePass (CoreDoRuleCheck phase pat) = observe (ruleCheck phase pat) -doCorePass CoreDoNothing = observe (\ _ _ -> return ()) -#ifdef OLD_STRICTNESS -doCorePass CoreDoOldStrictness = _scc_ "OldStrictness" trBinds doOldStrictness -#endif - -#ifdef OLD_STRICTNESS -doOldStrictness dfs binds - = do binds1 <- saBinds dfs binds - binds2 <- cprAnalyse dfs binds1 - return binds2 -#endif - -printCore _ binds = dumpIfSet True "Print Core" (pprCoreBindings binds) - -ruleCheck phase pat dflags binds = do showPass dflags "RuleCheck" - printDump (ruleCheckProgram phase pat binds) - --- Most passes return no stats and don't change rules -trBinds :: (DynFlags -> [CoreBind] -> IO [CoreBind]) - -> HscEnv -> UniqSupply -> RuleBase -> ModGuts - -> IO (SimplCount, ModGuts) -trBinds do_pass hsc_env us rb guts - = do { binds' <- do_pass dflags (mg_binds guts) - ; return (zeroSimplCount dflags, guts { mg_binds = binds' }) } - where - dflags = hsc_dflags hsc_env - -trBindsU :: (DynFlags -> UniqSupply -> [CoreBind] -> IO [CoreBind]) - -> HscEnv -> UniqSupply -> RuleBase -> ModGuts - -> IO (SimplCount, ModGuts) -trBindsU do_pass hsc_env us rb guts - = do { binds' <- do_pass dflags us (mg_binds guts) - ; return (zeroSimplCount dflags, guts { mg_binds = binds' }) } - where - dflags = hsc_dflags hsc_env +doCorePasses :: [CorePass] -> ModGuts -> CoreM ModGuts +doCorePasses passes guts = foldM (flip doCorePass) guts passes --- Observer passes just peek; don't modify the bindings at all -observe :: (DynFlags -> [CoreBind] -> IO a) - -> HscEnv -> UniqSupply -> RuleBase -> ModGuts - -> IO (SimplCount, ModGuts) -observe do_pass hsc_env us rb guts - = do { binds <- do_pass dflags (mg_binds guts) - ; return (zeroSimplCount dflags, guts) } - where - dflags = hsc_dflags hsc_env +doCorePass :: CorePass -> ModGuts -> CoreM ModGuts +doCorePass (CoreDoSimplify mode sws) = {-# SCC "Simplify" #-} + simplifyPgm mode sws + +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 + +doCorePass CoreDoNothing = return +doCorePass (CoreDoPasses passes) = doCorePasses passes \end{code} +%************************************************************************ +%* * +\subsection{Core pass combinators} +%* * +%************************************************************************ + +\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 $ Err.showPass dflags name + guts' <- pass guts + liftIO $ endPass dflags name dflag (mg_binds guts') (mg_rules 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)) + return guts + + +doPassDMS :: (DynFlags -> [CoreBind] -> IO (SimplCount, [CoreBind])) -> ModGuts -> CoreM ModGuts +doPassDMS do_pass = doPassM $ \binds -> do + dflags <- getDynFlags + liftIOWithCount $ do_pass dflags binds + +doPassDUM :: (DynFlags -> UniqSupply -> [CoreBind] -> IO [CoreBind]) -> ModGuts -> CoreM ModGuts +doPassDUM do_pass = doPassM $ \binds -> do + dflags <- getDynFlags + us <- getUniqueSupplyM + liftIO $ do_pass dflags us binds + +doPassDM :: (DynFlags -> [CoreBind] -> IO [CoreBind]) -> ModGuts -> CoreM ModGuts +doPassDM do_pass = doPassDUM (\dflags -> const (do_pass dflags)) + +doPassD :: (DynFlags -> [CoreBind] -> [CoreBind]) -> ModGuts -> CoreM ModGuts +doPassD do_pass = doPassDM (\dflags -> return . do_pass dflags) + +doPassDU :: (DynFlags -> UniqSupply -> [CoreBind] -> [CoreBind]) -> ModGuts -> CoreM ModGuts +doPassDU do_pass = doPassDUM (\dflags us -> return . do_pass dflags us) + +doPassU :: (UniqSupply -> [CoreBind] -> [CoreBind]) -> ModGuts -> CoreM ModGuts +doPassU do_pass = doPassDU (const do_pass) + +-- Most passes return no stats and don't change rules: these combinators +-- let us lift them to the full blown ModGuts+CoreM world +doPassM :: Monad m => ([CoreBind] -> m [CoreBind]) -> ModGuts -> m ModGuts +doPassM bind_f guts = do + binds' <- bind_f (mg_binds guts) + return (guts { mg_binds = binds' }) + +doPassMG :: Monad m => (ModGuts -> m [CoreBind]) -> ModGuts -> m ModGuts +doPassMG bind_f guts = do + binds' <- bind_f guts + return (guts { mg_binds = binds' }) + +doPass :: ([CoreBind] -> [CoreBind]) -> ModGuts -> CoreM ModGuts +doPass bind_f guts = return $ guts { mg_binds = bind_f (mg_binds guts) } + +-- Observer passes just peek; don't modify the bindings at all +observe :: (DynFlags -> [CoreBind] -> IO a) -> ModGuts -> CoreM ModGuts +observe do_pass = doPassM $ \binds -> do + dflags <- getDynFlags + liftIO $ do_pass dflags binds + return binds +\end{code} %************************************************************************ %* * -\subsection{Dealing with rules} + Dealing with rules %* * %************************************************************************ @@ -215,89 +301,103 @@ prepareRules :: HscEnv 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 }) + 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 us (mapSmpl (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 - - ; dumpIfSet_dyn dflags Opt_D_dump_rules "Transformation rules" - (vcat [text "Local rules", pprRules better_rules, - text "", - text "Imported rules", pprRuleBase imp_rule_base]) - - ; return (imp_rule_base, guts { mg_binds = binds_w_rules, + 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 simpl_rules, + blankLine, + text "Imported rules", pprRuleBase hpt_rule_base]) + + ; 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} - -We must do some gentle simplification on the template (but not the RHS) -of each rule. The case that forced me to add this was the fold/build rule, +Note [Simplifying the left-hand side of a RULE] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We must do some gentle simplification on the lhs (template) of each +rule. The case that forced me to add this was the fold/build rule, which without simplification looked like: fold k z (build (/\a. g a)) ==> ... This doesn't match unless you do eta reduction on the build argument. +Similarly for a LHS 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 + 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 {}) - = returnSmpl rule + = return rule simplRule env rule@(Rule { ru_bndrs = bndrs, ru_args = args, ru_rhs = rhs }) - = simplBinders env bndrs `thenSmpl` \ (env, bndrs') -> - mapSmpl (simplExprGently env) args `thenSmpl` \ args' -> - simplExprGently env rhs `thenSmpl` \ rhs' -> - returnSmpl (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. + = 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 = occurAnalyseExpr rhs' }) \end{code} \begin{code} @@ -308,13 +408,19 @@ simplExprGently :: SimplEnv -> CoreExpr -> SimplM CoreExpr -- alone leaves tons of crud. -- Used (a) for user expressions typed in at the interactive prompt -- (b) the LHS and RHS of a RULE +-- (c) Template Haskell splices -- -- The name 'Gently' suggests that the SimplifierMode is SimplGently, -- and in fact that is so.... but the 'Gently' in simplExprGently doesn't -- enforce that; it just simplifies the expression twice -simplExprGently env expr - = simplExpr env (occurAnalyseExpr expr) `thenSmpl` \ expr1 -> +-- It's important that simplExprGently does eta reduction; see +-- Note [Simplifying the left-hand side of a RULE] above. The +-- simplifier does indeed do eta reduction (it's in Simplify.completeLam) +-- but only if -O is on. + +simplExprGently env expr = do + expr1 <- simplExpr env (occurAnalyseExpr expr) simplExpr env (occurAnalyseExpr expr1) \end{code} @@ -353,7 +459,7 @@ glomBinds :: DynFlags -> [CoreBind] -> IO [CoreBind] -- analyser as free in f. glomBinds dflags binds - = do { showPass dflags "GlomBinds" ; + = do { Err.showPass dflags "GlomBinds" ; let { recd_binds = [Rec (flattenBinds binds)] } ; return recd_binds } -- Not much point in printing the result... @@ -368,66 +474,74 @@ glomBinds dflags binds %************************************************************************ \begin{code} -simplifyPgm :: SimplifierMode - -> [SimplifierSwitch] - -> HscEnv - -> UniqSupply - -> RuleBase - -> ModGuts - -> IO (SimplCount, ModGuts) -- New bindings - -simplifyPgm mode switches hsc_env us imp_rule_base guts +simplifyPgm :: SimplifierMode -> [SimplifierSwitch] -> ModGuts -> CoreM ModGuts +simplifyPgm mode switches + = describePassD doc Opt_D_dump_simpl_phases $ \guts -> + do { hsc_env <- getHscEnv + ; us <- getUniqueSupplyM + ; rb <- getRuleBase + ; liftIOWithCount $ + simplifyPgmIO mode switches hsc_env us rb guts } + where + doc = ptext (sLit "Simplifier Phase") <+> text (showPpr mode) + +simplifyPgmIO :: SimplifierMode + -> [SimplifierSwitch] + -> HscEnv + -> UniqSupply + -> RuleBase + -> ModGuts + -> IO (SimplCount, ModGuts) -- New bindings + +simplifyPgmIO mode switches hsc_env us hpt_rule_base + guts@(ModGuts { mg_binds = binds, mg_rules = rules + , mg_fam_inst_env = fam_inst_env }) = do { - showPass dflags "Simplify"; - - (termination_msg, it_count, counts_out, binds') - <- do_iteration us 1 (zeroSimplCount dflags) (mg_binds guts) ; + (termination_msg, it_count, counts_out, guts') + <- do_iteration us 1 (zeroSimplCount dflags) binds rules ; - dumpIfSet (dopt Opt_D_verbose_core2core dflags - && dopt Opt_D_dump_simpl_stats dflags) - "Simplifier statistics" + 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]); - endPass dflags ("Simplify phase " ++ phase_info ++ " done") Opt_D_verbose_core2core binds'; - - return (counts_out, guts { mg_binds = binds' }) + return (counts_out, guts') } where - dflags = hsc_dflags hsc_env - phase_info = case mode of - SimplGently -> "gentle" - SimplPhase n -> show n + dflags = hsc_dflags hsc_env + dump_phase = shouldDumpSimplPhase dflags mode sw_chkr = isAmongSimpl switches max_iterations = intSwitchSet sw_chkr MaxSimplifierIterations `orElse` 2 - do_iteration us iteration_no counts binds + 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 - = do { -#ifdef DEBUG - if max_iterations > 2 then - hPutStr stderr ("NOTE: Simplifier still going after " ++ - show max_iterations ++ - " iterations; bailing out.\n") - else - return (); -#endif + = WARN(debugIsOn && (max_iterations > 2), + text ("Simplifier still going after " ++ + show max_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 baled 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 } ; - dumpIfSet_dyn dflags Opt_D_dump_occur_anal "Occurrence analysis" + 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 @@ -436,8 +550,12 @@ simplifyPgm mode switches hsc_env us imp_rule_base guts -- 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 rule_base' } ; + 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) } ; -- Simplify the program -- We do this with a *case* not a *let* because lazy pattern @@ -450,19 +568,18 @@ simplifyPgm mode switches hsc_env us imp_rule_base guts -- case t of {(_,counts') -> if counts'=0 then ... } -- So the conditional didn't force counts', because the -- selection got duplicated. Sigh! - case initSmpl dflags us1 (_scc_ "SimplTopBinds" simplTopBinds simpl_env tagged_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 phase " ++ phase_info ++ - ", iteration " ++ show iteration_no ++ - " out of " ++ show max_iterations + let { all_counts = counts `plusSimplCount` counts1 + ; binds1 = getFloats env1 + ; rules1 = substRulesForImportedIds (mkCoreSubst 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 @@ -472,18 +589,30 @@ simplifyPgm mode switches hsc_env us imp_rule_base guts -- -- 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 - dumpIfSet_dyn dflags Opt_D_dump_simpl_iterations herald - (pprSimplCount counts') ; - endPass dflags herald Opt_D_dump_simpl_iterations binds'' ; + end_iteration dflags mode iteration_no max_iterations 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 -> SimplifierMode -> Int -> Int + -> SimplCount -> [CoreBind] -> [CoreRule] -> IO () +-- Same as endIteration but with simplifier counts +end_iteration dflags mode iteration_no max_iterations counts binds rules + = do { Err.dumpIfSet_dyn dflags Opt_D_dump_simpl_iterations pass_name + (pprSimplCount counts) ; + + ; endIteration dflags pass_name Opt_D_dump_simpl_iterations binds rules } + where + pass_name = "Simplifier mode " ++ showPpr mode ++ + ", iteration " ++ show iteration_no ++ + " out of " ++ show max_iterations \end{code} @@ -510,23 +639,21 @@ save a gratuitous jump (from \tr{x_exported} to \tr{x_local}), and makes strictness information propagate better. This used to happen in the final phase, but it's tidier to do it here. +Note [Transferring IdInfo] +~~~~~~~~~~~~~~~~~~~~~~~~~~ +We want to propagage any useful IdInfo on x_local to x_exported. + STRICTNESS: if we have done strictness analysis, we want the strictness info on x_local to transfer to x_exported. Hence the copyIdInfo call. RULES: we want to *add* any RULES for x_local to x_exported. -Note [Rules and indirection-zapping] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Problem: what if x_exported has a RULE that mentions something in ...bindings...? -Then the things mentioned can be out of scope! Solution - a) Make sure that in this pass the usage-info from x_exported is - available for ...bindings... - b) If there are any such RULES, rec-ify the entire top-level. - It'll get sorted out next time round -Messing up the rules -~~~~~~~~~~~~~~~~~~~~ -The example that went bad on me at one stage was this one: +Note [Messing up the exported Id's RULES] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +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] @@ -559,13 +686,28 @@ And now we get an infinite loop in the rule system -> iterateFB (:) f x -> iterate f x -Tiresome old solution: - don't do shorting out if f has rewrite rules (see shortableIdInfo) - -New solution (I think): +Old "solution": use rule switching-off pragmas to get rid of iterateList in the first place +But in principle the user *might* want rules that only apply to the Id +he says. And inline pragmas are similar + {-# NOINLINE f #-} + f = local + local = +Then we do not want to get rid of the NOINLINE. + +Hence hasShortableIdinfo. + + +Note [Rules and indirection-zapping] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Problem: what if x_exported has a RULE that mentions something in ...bindings...? +Then the things mentioned can be out of scope! Solution + a) Make sure that in this pass the usage-info from x_exported is + available for ...bindings... + b) If there are any such RULES, rec-ify the entire top-level. + It'll get sorted out next time round Other remarks ~~~~~~~~~~~~~ @@ -636,6 +778,7 @@ makeIndEnv binds add_pair (exported_id, rhs) env = env +----------------- shortMeOut ind_env exported_id local_id -- The if-then-else stuff is just so I can get a pprTrace to see -- how often I don't get shorting out becuase of IdInfo stuff @@ -650,23 +793,27 @@ shortMeOut ind_env exported_id local_id not (local_id `elemVarEnv` ind_env) -- Only if not already substituted for then - True - -{- No longer needed - if isEmptySpecInfo (specInfo (idInfo exported_id)) -- Only if no rules - then True -- See note on "Messing up rules" - else -#ifdef DEBUG - pprTrace "shortMeOut:" (ppr exported_id) -#endif - False --} + if hasShortableIdInfo exported_id + then True -- See Note [Messing up the exported Id's IdInfo] + else WARN( True, ptext (sLit "Not shorting out:") <+> ppr exported_id ) + False else - False + False +----------------- +hasShortableIdInfo :: Id -> Bool +-- True if there is no user-attached IdInfo on exported_id, +-- so we can safely discard it +-- See Note [Messing up the exported Id's IdInfo] +hasShortableIdInfo id + = isEmptySpecInfo (specInfo info) + && isDefaultInlinePragma (inlinePragInfo info) + where + info = idInfo id ----------------- transferIdInfo :: Id -> Id -> Id +-- See Note [Transferring IdInfo] -- If we have -- lcl_id = e; exp_id = lcl_id -- and lcl_id has useful IdInfo, we don't want to discard it by going @@ -677,9 +824,12 @@ transferIdInfo exported_id local_id = 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) - (specInfo local_info) + `setSpecInfo` addSpecInfo (specInfo exp_info) new_info + new_info = setSpecInfoHead (idName exported_id) + (specInfo local_info) + -- Remember to set the function-name field of the + -- rules as we transfer them from one function to another \end{code}