Andreas Klebinger pushed to branch wip/andreask/ghc_par at Glasgow Haskell Compiler / GHC
Commits:
-
cde2e390
by Andreas Klebinger at 2026-03-07T13:39:29+00:00
-
82d089c0
by Andreas Klebinger at 2026-03-07T16:48:25+00:00
-
8de36fbf
by Andreas Klebinger at 2026-03-07T21:14:07+00:00
-
775a5b9f
by Andreas Klebinger at 2026-03-07T21:50:09+00:00
-
dd3df12c
by Andreas Klebinger at 2026-03-07T22:57:33+00:00
-
5527b495
by Andreas Klebinger at 2026-03-08T09:29:51+00:00
-
15a3537c
by Andreas Klebinger at 2026-03-08T09:35:41+00:00
-
e02db7ea
by Andreas Klebinger at 2026-03-08T10:00:25+00:00
-
08faa984
by Andreas Klebinger at 2026-03-08T13:42:08+00:00
12 changed files:
- compiler/GHC/Core/Opt/CSE.hs
- compiler/GHC/Core/Opt/Monad.hs
- compiler/GHC/Core/Opt/OccurAnal.hs
- compiler/GHC/Core/Opt/Pipeline.hs
- compiler/GHC/Core/Opt/Simplify.hs
- compiler/GHC/Core/Opt/SpecConstr.hs
- compiler/GHC/Core/Opt/Specialise.hs
- + compiler/GHC/Core/Opt/Split.hs
- compiler/GHC/Core/SimpleOpt.hs
- compiler/GHC/Core/Subst.hs
- compiler/ghc.cabal.in
- testsuite/driver/testlib.py
Changes:
| ... | ... | @@ -380,18 +380,18 @@ body/rest of the module. |
| 380 | 380 | -}
|
| 381 | 381 | |
| 382 | 382 | cseProgram :: CoreProgram -> CoreProgram
|
| 383 | -cseProgram comp_units
|
|
| 384 | - = snd (mapAccumL cse_comp_unit init_env comp_units)
|
|
| 383 | +cseProgram = map cseCoreCompUnit
|
|
| 384 | + |
|
| 385 | +cseCoreCompUnit :: CoreCompUnit -> CoreCompUnit
|
|
| 386 | +cseCoreCompUnit (CoreCompUnit unit_binds unit_rules)
|
|
| 387 | + = CoreCompUnit binds' unit_rules
|
|
| 385 | 388 | where
|
| 386 | - init_env = emptyCSEnv $
|
|
| 387 | - mkInScopeSetList (bindersOfBinds (concatMap coreCompUnitBinds comp_units))
|
|
| 388 | - -- Put all top-level binders into scope; it is possible to have
|
|
| 389 | - -- forward references. See Note [Glomming] in GHC.Core.Opt.OccurAnal
|
|
| 390 | - -- Missing this caused #25468
|
|
| 391 | - |
|
| 392 | - cse_comp_unit env (CoreCompUnit binds unit_rules)
|
|
| 393 | - = let (env', binds') = mapAccumL (cseBind TopLevel) env binds
|
|
| 394 | - in (env', CoreCompUnit binds' unit_rules)
|
|
| 389 | + init_env = emptyCSEnv $
|
|
| 390 | + mkInScopeSetList (bindersOfBinds unit_binds)
|
|
| 391 | + -- Put all top-level binders in this compilation unit into
|
|
| 392 | + -- scope; it is possible to have forward references.
|
|
| 393 | + -- See Note [Glomming] in GHC.Core.Opt.OccurAnal.
|
|
| 394 | + (_env', binds') = mapAccumL (cseBind TopLevel) init_env unit_binds
|
|
| 395 | 395 | |
| 396 | 396 | cseBind :: TopLevelFlag -> CSEnv -> CoreBind -> (CSEnv, CoreBind)
|
| 397 | 397 | cseBind toplevel env (NonRec b e)
|
| ... | ... | @@ -16,7 +16,7 @@ module GHC.Core.Opt.Monad ( |
| 16 | 16 | |
| 17 | 17 | -- ** Reading from the monad
|
| 18 | 18 | getModule,
|
| 19 | - initRuleEnv, getExternalRuleBase,
|
|
| 19 | + initRuleEnv, getHomeRuleBase, getExternalRuleBase,
|
|
| 20 | 20 | getDynFlags, getPackageFamInstEnv,
|
| 21 | 21 | getInteractiveContext,
|
| 22 | 22 | getUniqTag,
|
| ... | ... | @@ -28,7 +28,6 @@ core expression with (hopefully) improved usage information. |
| 28 | 28 | module GHC.Core.Opt.OccurAnal (
|
| 29 | 29 | occurAnalysePgm,
|
| 30 | 30 | occurAnalyseCompUnit,
|
| 31 | - occurSplitPgm,
|
|
| 32 | 31 | occurAnalyseExpr, occurAnalyseExpr_Prep,
|
| 33 | 32 | zapLambdaBndrs
|
| 34 | 33 | ) where
|
| ... | ... | @@ -57,7 +56,6 @@ import GHC.Types.Id.Info |
| 57 | 56 | import GHC.Types.InlinePragma ( ActivationGhc, isAlwaysActive )
|
| 58 | 57 | import GHC.Types.Basic
|
| 59 | 58 | import GHC.Types.Tickish
|
| 60 | -import GHC.Types.Name (isExternalName, nameModule)
|
|
| 61 | 59 | import GHC.Types.Var.Set
|
| 62 | 60 | import GHC.Types.Var.Env
|
| 63 | 61 | import GHC.Types.Var
|
| ... | ... | @@ -70,14 +68,12 @@ import GHC.Utils.Misc |
| 70 | 68 | import GHC.Builtin.Names( runRWKey )
|
| 71 | 69 | import GHC.Unit.Module( Module )
|
| 72 | 70 | |
| 73 | -import qualified Data.IntMap.Strict as IntMap
|
|
| 74 | -import qualified Data.IntSet as IntSet
|
|
| 75 | 71 | import Data.List (mapAccumL)
|
| 76 | 72 | |
| 77 | 73 | {-
|
| 78 | 74 | ************************************************************************
|
| 79 | 75 | * *
|
| 80 | - occurAnalysePgm, occurAnalyseExpr, occurSplitPgm
|
|
| 76 | + occurAnalysePgm, occurAnalyseExpr
|
|
| 81 | 77 | * *
|
| 82 | 78 | ************************************************************************
|
| 83 | 79 | |
| ... | ... | @@ -99,140 +95,6 @@ occurAnalyseExpr_Prep expr = expr' |
| 99 | 95 | where
|
| 100 | 96 | WUD _ expr' = occAnal (initOccEnv { occ_allow_weak_joins = True }) expr
|
| 101 | 97 | |
| 102 | --- After optimizations a rule might no longer reference binders from this module.
|
|
| 103 | --- In these cases we return them here and then add them to mg_rules.
|
|
| 104 | -occurSplitPgm :: Module -> [CoreRule] -> CoreCompUnit -> ([CoreCompUnit], [CoreRule])
|
|
| 105 | -occurSplitPgm mod imp_rules (CoreCompUnit unit_binds unit_rules)
|
|
| 106 | - =
|
|
| 107 | - -- pprTrace "occurSplitPgm"
|
|
| 108 | - -- ( vcat [
|
|
| 109 | - -- text "imp",
|
|
| 110 | - -- ppr imp_rules,
|
|
| 111 | - -- text "unit",
|
|
| 112 | - -- ppr (unit_rules, unit_binds)
|
|
| 113 | - -- ]
|
|
| 114 | - -- )
|
|
| 115 | - (zipWith mk_comp_unit comp_pairs [0..], rules_for_imps)
|
|
| 116 | - where
|
|
| 117 | - CoreCompUnit occ_binds _ =
|
|
| 118 | - occurAnalyseCompUnit mod (const True) (const True) imp_rules
|
|
| 119 | - (CoreCompUnit unit_binds unit_rules)
|
|
| 120 | - |
|
| 121 | - pairs = flattenBinds occ_binds
|
|
| 122 | - bndrs = map fst pairs
|
|
| 123 | - bndr_set = mkVarSet bndrs
|
|
| 124 | - |
|
| 125 | - imp_rule_edges :: ImpRuleEdges
|
|
| 126 | - imp_rule_edges = mkImpRuleEdges imp_rules
|
|
| 127 | - |
|
| 128 | - -- If a unit rule mentions multiple local binders, they must end up in
|
|
| 129 | - -- the same component; otherwise the rule cannot be attached to any one
|
|
| 130 | - -- split unit without creating cross-unit references.
|
|
| 131 | - rule_fv_edges :: IdEnv VarSet
|
|
| 132 | - rule_fv_edges
|
|
| 133 | - = foldr (plusVarEnv_C unionVarSet) emptyVarEnv
|
|
| 134 | - [ mapVarEnv (const local_rule_fvs) (getUniqSet local_rule_fvs)
|
|
| 135 | - | rule <- unit_rules
|
|
| 136 | - , let local_rule_fvs = ruleFreeVars rule `intersectVarSet` bndr_set
|
|
| 137 | - ]
|
|
| 138 | - |
|
| 139 | - dir_nodes :: [Node Unique (Id, CoreExpr)]
|
|
| 140 | - dir_nodes = map mk_dir_node pairs
|
|
| 141 | - |
|
| 142 | - mk_dir_node (bndr, rhs)
|
|
| 143 | - = DigraphNode { node_payload = (bndr, rhs)
|
|
| 144 | - , node_key = varUnique bndr
|
|
| 145 | - , node_dependencies = nonDetKeysUniqSet deps
|
|
| 146 | - }
|
|
| 147 | - where
|
|
| 148 | - deps = traced_fvs `intersectVarSet` bndr_set
|
|
| 149 | - |
|
| 150 | - traced_fvs = -- pprTrace "occurSplitPgm: binder fvs before unit assignment"
|
|
| 151 | - -- (ppr bndr $$ text "fvs:" <+> ppr dep_fvs)
|
|
| 152 | - dep_fvs
|
|
| 153 | - |
|
| 154 | - dep_fvs = exprFreeIds rhs
|
|
| 155 | - `unionVarSet` bndrRuleAndUnfoldingIds bndr
|
|
| 156 | - `unionVarSet` localRuleDeps bndr
|
|
| 157 | - `unionVarSet` impRuleDeps bndr
|
|
| 158 | - |
|
| 159 | - impRuleDeps b = foldr unionVarSet emptyVarSet [ vs | (_, vs) <- lookupImpRules imp_rule_edges b ]
|
|
| 160 | - |
|
| 161 | - localRuleDeps b = lookupVarEnv rule_fv_edges b `orElse` emptyVarSet
|
|
| 162 | - |
|
| 163 | - incoming_edges :: UniqFM Unique [Unique]
|
|
| 164 | - incoming_edges = foldr add_incoming emptyUFM dir_nodes
|
|
| 165 | - where
|
|
| 166 | - add_incoming DigraphNode { node_key = src, node_dependencies = dests } incoming
|
|
| 167 | - = foldr (\dest acc -> addToUFM_C (++) acc dest [src]) incoming dests
|
|
| 168 | - -- TODO: AI Garbage? -
|
|
| 169 | - -- We probably really should just do this on a undirected graph instead.
|
|
| 170 | - undir_nodes :: [Node Unique (Id, CoreExpr)]
|
|
| 171 | - undir_nodes =
|
|
| 172 | - [ node { node_dependencies = node_dependencies node ++ lookupWithDefaultUFM incoming_edges [] (node_key node) }
|
|
| 173 | - | node <- dir_nodes
|
|
| 174 | - ]
|
|
| 175 | - |
|
| 176 | - comp_pairs :: [[(Id, CoreExpr)]]
|
|
| 177 | - comp_pairs = map scc_payloads (stronglyConnCompFromEdgedVerticesUniq undir_nodes)
|
|
| 178 | - |
|
| 179 | - component_bndrs :: [[Id]]
|
|
| 180 | - component_bndrs = map (map fst) comp_pairs
|
|
| 181 | - |
|
| 182 | - mk_comp_unit prs i = CoreCompUnit (mk_comp_binds prs) (component_rules i)
|
|
| 183 | - |
|
| 184 | - mk_comp_binds [pr] = [NonRec (fst pr) (snd pr)]
|
|
| 185 | - mk_comp_binds prs = [Rec prs]
|
|
| 186 | - |
|
| 187 | - (component_rule_map, rules_for_imps) = foldr assign_rule (IntMap.empty, []) unit_rules
|
|
| 188 | - |
|
| 189 | - assign_rule rule (rule_map, imp_rules_acc)
|
|
| 190 | - = case rule_comp_index rule of
|
|
| 191 | - Just i -> (IntMap.insertWith (++) i [rule] rule_map, imp_rules_acc)
|
|
| 192 | - Nothing -> (rule_map, rule : imp_rules_acc)
|
|
| 193 | - |
|
| 194 | - component_rules i = IntMap.findWithDefault [] i component_rule_map
|
|
| 195 | - |
|
| 196 | - rule_comp_index rule
|
|
| 197 | - = case rule_component_indices of
|
|
| 198 | - [i] -> Just i
|
|
| 199 | - [] -> Nothing
|
|
| 200 | - is -> pprPanic "occurSplitPgm"
|
|
| 201 | - (text "Rule free vars span multiple components"
|
|
| 202 | - $$ text "rule:" <+> ppr rule
|
|
| 203 | - $$ text "components:" <+> ppr is
|
|
| 204 | - $$ text "rule_fvs:" <+> pprVarsWithModule (nonDetEltsUniqSet rule_fvs)
|
|
| 205 | - $$ vcat [ text "component" <+> int i <> colon <+> ppr hits
|
|
| 206 | - | (i, hits) <- component_hits ])
|
|
| 207 | - where
|
|
| 208 | - rule_component_indices :: [Int]
|
|
| 209 | - rule_component_indices = IntSet.toList $ IntSet.fromList
|
|
| 210 | - [ i
|
|
| 211 | - | (ids, i) <- zip component_bndrs [0..]
|
|
| 212 | - , not (isEmptyVarSet (local_rule_fvs `intersectVarSet` mkVarSet ids))
|
|
| 213 | - ]
|
|
| 214 | - |
|
| 215 | - rule_fvs = ruleFreeVars rule
|
|
| 216 | - local_rule_fvs = rule_fvs `intersectVarSet` bndr_set
|
|
| 217 | - component_hits =
|
|
| 218 | - [ (i, local_rule_fvs `intersectVarSet` mkVarSet ids)
|
|
| 219 | - | (ids, i) <- zip component_bndrs [0..]
|
|
| 220 | - , not (isEmptyVarSet (local_rule_fvs `intersectVarSet` mkVarSet ids))
|
|
| 221 | - ]
|
|
| 222 | - |
|
| 223 | - scc_payloads (AcyclicSCC p) = [p]
|
|
| 224 | - scc_payloads (CyclicSCC ps) = ps
|
|
| 225 | - |
|
| 226 | - pprVarsWithModule :: [Var] -> SDoc
|
|
| 227 | - pprVarsWithModule vars = braces (fsep (punctuate comma (map pprVarWithModule vars)))
|
|
| 228 | - |
|
| 229 | - pprVarWithModule :: Var -> SDoc
|
|
| 230 | - pprVarWithModule v
|
|
| 231 | - | isExternalName n = ppr v <+> parens (ppr (nameModule n))
|
|
| 232 | - | otherwise = ppr v
|
|
| 233 | - where
|
|
| 234 | - n = varName v
|
|
| 235 | - |
|
| 236 | 98 | occurAnalyseCompUnit
|
| 237 | 99 | :: Module
|
| 238 | 100 | -> (Id -> Bool)
|
| ... | ... | @@ -14,7 +14,7 @@ import GHC.Driver.DynFlags |
| 14 | 14 | import GHC.Driver.Plugins ( withPlugins, installCoreToDos )
|
| 15 | 15 | import GHC.Driver.Env
|
| 16 | 16 | import GHC.Driver.Config (initSimpleOpts)
|
| 17 | -import GHC.Driver.Config.Core.Lint ( endPass )
|
|
| 17 | +import GHC.Driver.Config.Core.Lint ( endPass, initLintConfig )
|
|
| 18 | 18 | import GHC.Driver.Config.Core.Opt.LiberateCase ( initLiberateCaseOpts )
|
| 19 | 19 | import GHC.Driver.Config.Core.Opt.Simplify ( initSimplifyOpts, initSimplMode, initGentleSimplMode )
|
| 20 | 20 | import GHC.Driver.Config.Core.Opt.WorkWrap ( initWorkWrapOpts )
|
| ... | ... | @@ -22,12 +22,12 @@ import GHC.Driver.Config.Core.Rules ( initRuleOpts ) |
| 22 | 22 | import GHC.Platform.Ways ( hasWay, Way(WayProf) )
|
| 23 | 23 | |
| 24 | 24 | import GHC.Core
|
| 25 | -import GHC.Core.SimpleOpt (simpleOptPgm)
|
|
| 25 | +import GHC.Core.SimpleOpt (simpleOptPgm, defaultSimpleOpts, so_inline, so_uf_opts)
|
|
| 26 | 26 | import GHC.Core.Opt.CSE ( cseProgram )
|
| 27 | 27 | import GHC.Core.Rules ( RuleBase, ruleCheckProgram, getRules )
|
| 28 | 28 | import GHC.Core.Ppr ( pprCoreProgram, pprRules )
|
| 29 | 29 | import GHC.Core.Utils ( dumpIdInfoOfProgram )
|
| 30 | -import GHC.Core.Lint ( lintAnnots )
|
|
| 30 | +import GHC.Core.Lint ( lintAnnots, lintCoreProgram', displayLintResults )
|
|
| 31 | 31 | import GHC.Core.Lint.Interactive ( interactiveInScope )
|
| 32 | 32 | import GHC.Core.Opt.Simplify ( simplifyExpr, simplifyPgm )
|
| 33 | 33 | import GHC.Core.Opt.Simplify.Monad
|
| ... | ... | @@ -43,7 +43,7 @@ import GHC.Core.Opt.DmdAnal |
| 43 | 43 | import GHC.Core.Opt.CprAnal ( cprAnalProgram )
|
| 44 | 44 | import GHC.Core.Opt.CallArity ( callArityAnalProgram )
|
| 45 | 45 | import GHC.Core.Opt.Exitify ( exitifyProgram )
|
| 46 | -import GHC.Core.Opt.OccurAnal ( occurSplitPgm )
|
|
| 46 | +import GHC.Core.Opt.Split ( splitCompUnit, checkNameClashes )
|
|
| 47 | 47 | import GHC.Core.Opt.WorkWrap ( wwTopBinds )
|
| 48 | 48 | import GHC.Core.Opt.CallerCC ( addCallerCostCentres )
|
| 49 | 49 | import GHC.Core.LateCC.TopLevelBinds (topLevelBindsCCMG)
|
| ... | ... | @@ -320,7 +320,11 @@ getCoreToDo dflags hpt_rule_base extra_vars |
| 320 | 320 | -- in wheel-sieve1), and I'm guessing that SpecConstr can too
|
| 321 | 321 | -- And CSE is a very cheap pass. So it seems worth doing here.
|
| 322 | 322 | runWhen ((liberate_case || spec_constr) && cse) $ CoreDoPasses
|
| 323 | - [ CoreCSE, simplify "post-final-cse" ],
|
|
| 323 | + [ runWhen split_core CoreMerge
|
|
| 324 | + , CoreCSE
|
|
| 325 | + , runWhen split_core CoreSplit
|
|
| 326 | + , simplify "post-final-cse"
|
|
| 327 | + ],
|
|
| 324 | 328 | |
| 325 | 329 | --------- End of -O2 passes --------------
|
| 326 | 330 | |
| ... | ... | @@ -457,6 +461,7 @@ doCorePass pass guts = do |
| 457 | 461 | dflags <- getDynFlags
|
| 458 | 462 | us <- getUniqueSupplyM
|
| 459 | 463 | p_fam_env <- getPackageFamInstEnv
|
| 464 | + let extra_vars = interactiveInScope (hsc_IC hsc_env)
|
|
| 460 | 465 | let platform = targetPlatform dflags
|
| 461 | 466 | let fam_envs = (p_fam_env, mg_fam_inst_env guts)
|
| 462 | 467 | let updateBinds f = return $ guts { mg_binds = f (mg_binds guts) }
|
| ... | ... | @@ -478,14 +483,16 @@ doCorePass pass guts = do |
| 478 | 483 | updateBindsAndRulesM (desugarOpt dflags logger (mg_module guts))
|
| 479 | 484 | |
| 480 | 485 | CoreSplit -> {-# SCC "CoreSplit" #-}
|
| 481 | - do { let split_res = map (occurSplitPgm (mg_module guts) (mg_rules guts)) (mg_binds guts)
|
|
| 486 | + do { let split_res = map (splitCompUnit (mg_module guts) (mg_rules guts)) (mg_binds guts)
|
|
| 482 | 487 | binds' = concatMap fst split_res
|
| 483 | 488 | rules' = mg_rules guts ++ concatMap snd split_res
|
| 484 | 489 | ; return guts { mg_binds = binds', mg_rules = rules' } }
|
| 485 | 490 | |
| 486 | 491 | CoreMerge -> {-# SCC "CoreMerge" #-}
|
| 487 | 492 | do { let binds_before = mg_binds guts
|
| 493 | + _ = checkNameClashes binds_before
|
|
| 488 | 494 | binds_after = flattenCoreProgram binds_before
|
| 495 | + _ = checkNameClashes binds_after
|
|
| 489 | 496 | ; liftIO $
|
| 490 | 497 | Logger.putDumpFileMaybe logger Opt_D_dump_split_core
|
| 491 | 498 | "Core before merge"
|
| ... | ... | @@ -497,7 +504,33 @@ doCorePass pass guts = do |
| 497 | 504 | "Core after merge"
|
| 498 | 505 | FormatCore
|
| 499 | 506 | (pprCoreProgram binds_after)
|
| 500 | - ; return $ guts { mg_binds = binds_after } }
|
|
| 507 | + ; liftIO $ do
|
|
| 508 | + let warns_and_errs = lintCoreProgram'
|
|
| 509 | + (initLintConfig dflags extra_vars)
|
|
| 510 | + binds_after
|
|
| 511 | + (mg_rules guts)
|
|
| 512 | + True
|
|
| 513 | + displayLintResults logger
|
|
| 514 | + (text "CoreMerge after flattenCoreProgram")
|
|
| 515 | + (pprCoreProgram binds_after)
|
|
| 516 | + warns_and_errs
|
|
| 517 | + ; let minimal_things = defaultSimpleOpts
|
|
| 518 | + { so_inline = False
|
|
| 519 | + , so_uf_opts = unfoldingOpts dflags }
|
|
| 520 | + (binds_w_unfolds, rules_for_imps, occ_anald_binds) = simpleOptPgm
|
|
| 521 | + minimal_things
|
|
| 522 | + (mg_module guts)
|
|
| 523 | + binds_after
|
|
| 524 | + (mg_rules guts)
|
|
| 525 | + _ = checkNameClashes binds_w_unfolds
|
|
| 526 | + ; liftIO $
|
|
| 527 | + Logger.putDumpFileMaybe logger Opt_D_dump_split_core
|
|
| 528 | + "Core after re-attaching unfolds"
|
|
| 529 | + FormatCore
|
|
| 530 | + (pprCoreProgram occ_anald_binds)
|
|
| 531 | + |
|
| 532 | + ; return $ guts { mg_binds = binds_w_unfolds
|
|
| 533 | + , mg_rules = rules_for_imps } }
|
|
| 501 | 534 | |
| 502 | 535 | CoreDoSimplify opts -> {-# SCC "Simplify" #-}
|
| 503 | 536 | liftIOWithCount $ simplifyPgm logger (hsc_unit_env hsc_env) name_ppr_ctx opts guts
|
| ... | ... | @@ -10,6 +10,7 @@ import GHC.Prelude |
| 10 | 10 | import GHC.Driver.Flags
|
| 11 | 11 | |
| 12 | 12 | import GHC.Core
|
| 13 | +import GHC.Core.FVs (ruleFreeVars)
|
|
| 13 | 14 | import GHC.Core.Rules
|
| 14 | 15 | import GHC.Core.Ppr ( pprCoreBindings, pprCoreExpr )
|
| 15 | 16 | import GHC.Core.Opt.OccurAnal ( occurAnalysePgm, occurAnalyseExpr )
|
| ... | ... | @@ -38,10 +39,10 @@ import GHC.Unit.Module.ModGuts |
| 38 | 39 | import GHC.Types.Id
|
| 39 | 40 | import GHC.Types.Id.Info
|
| 40 | 41 | import GHC.Types.InlinePragma
|
| 41 | -import GHC.Types.Var.Set
|
|
| 42 | 42 | import GHC.Types.Var.Env
|
| 43 | 43 | import GHC.Types.Tickish
|
| 44 | 44 | import GHC.Types.Unique.FM
|
| 45 | +import GHC.Types.Var.Set
|
|
| 45 | 46 | |
| 46 | 47 | import Control.Monad
|
| 47 | 48 | import Data.Foldable ( for_ )
|
| ... | ... | @@ -240,7 +241,7 @@ simplifyPgm' logger unit_env name_ppr_ctx opts |
| 240 | 241 | -- number of iterations we actually completed
|
| 241 | 242 | return ( "Simplifier bailed out", iteration_no - 1
|
| 242 | 243 | , totalise counts_so_far
|
| 243 | - , dyn_no_binds { dsd_binds = [CoreCompUnit bind_list unit_rules], dsd_rules = local_rules } )
|
|
| 244 | + , dyn_no_binds { dsd_binds = binds, dsd_rules = local_rules } )
|
|
| 244 | 245 | |
| 245 | 246 | -- Try and force thunks off the binds; significantly reduces
|
| 246 | 247 | -- space usage, especially with -O. JRS, 000620.
|
| ... | ... | @@ -271,38 +272,19 @@ simplifyPgm' logger unit_env name_ppr_ctx opts |
| 271 | 272 | -- (b) local rules (substituted), including unit rules from `binds`
|
| 272 | 273 | -- Forcing base_rule_env to avoid unnecessary allocations.
|
| 273 | 274 | -- Not doing so results in +25.6% allocations of LargeRecord.
|
| 274 | - ; !base_rule_env = updLocalRules hpt_rule_env (local_rules ++ unit_rules)
|
|
| 275 | - |
|
| 276 | - ; read_eps_rules :: IO PackageRuleBase
|
|
| 277 | - ; read_eps_rules = eps_rule_base <$> ueEPS unit_env
|
|
| 278 | - |
|
| 279 | - ; read_rule_env :: IO RuleEnv
|
|
| 280 | - ; read_rule_env = updExternalPackageRules base_rule_env <$> read_eps_rules
|
|
| 281 | - |
|
| 282 | 275 | ; fam_envs = (eps_fam_inst_env eps, fam_inst_env)
|
| 283 | 276 | ; simpl_env = mkSimplEnv mode fam_envs } ;
|
| 284 | 277 | |
| 285 | - -- Simplify the program
|
|
| 286 | - ((binds1, rules1, unit_rules1), counts1) <-
|
|
| 287 | - initSmpl logger read_rule_env top_env_cfg sz $
|
|
| 288 | - do { (floats, env1) <- {-# SCC "SimplTopBinds" #-}
|
|
| 289 | - simplTopBinds simpl_env tagged_bind_list
|
|
| 290 | - |
|
| 291 | - -- Apply the substitution to rules defined in this module
|
|
| 292 | - -- for imported Ids. Eg RULE map my_f = blah
|
|
| 293 | - -- If we have a substitution my_f :-> other_f, we'd better
|
|
| 294 | - -- apply it to the rule to, or it'll never match
|
|
| 295 | - ; rules1 <- simplImpRules env1 local_rules
|
|
| 296 | - ; unit_rules1 <- simplImpRules env1 unit_rules
|
|
| 297 | - |
|
| 298 | - ; return (getTopFloatBinds floats, rules1, unit_rules1) } ;
|
|
| 278 | + -- Simplify each compilation unit independently
|
|
| 279 | + ((binds1, rules1), counts1) <-
|
|
| 280 | + simpl_comp_units simpl_env local_rules tagged_binds sz ;
|
|
| 299 | 281 | |
| 300 | 282 | -- Stop if nothing happened; don't dump output
|
| 301 | 283 | -- See Note [Which transformations are innocuous] in GHC.Core.Opt.Stats
|
| 302 | 284 | if isZeroSimplCount counts1 then
|
| 303 | 285 | return ( "Simplifier reached fixed point", iteration_no
|
| 304 | 286 | , totalise (counts1 : counts_so_far) -- Include "free" ticks
|
| 305 | - , dyn_no_binds { dsd_binds = [CoreCompUnit binds1 unit_rules1], dsd_rules = rules1 } )
|
|
| 287 | + , dyn_no_binds { dsd_binds = binds1, dsd_rules = rules1 } )
|
|
| 306 | 288 | else do {
|
| 307 | 289 | -- Short out indirections
|
| 308 | 290 | -- We do this *after* at least one run of the simplifier
|
| ... | ... | @@ -312,26 +294,77 @@ simplifyPgm' logger unit_env name_ppr_ctx opts |
| 312 | 294 | --
|
| 313 | 295 | -- ToDo: alas, this means that indirection-shorting does not happen at all
|
| 314 | 296 | -- if the simplifier does nothing (not common, I know, but unsavoury)
|
| 315 | - let { binds2 = {-# SCC "ZapInd" #-} shortOutIndirections binds1 } ;
|
|
| 297 | + let { binds2 = {-# SCC "ZapInd" #-}
|
|
| 298 | + [ CoreCompUnit (shortOutIndirections unit_binds) unit_rules'
|
|
| 299 | + | CoreCompUnit unit_binds unit_rules' <- binds1 ] } ;
|
|
| 316 | 300 | |
| 317 | 301 | -- Dump the result of this iteration
|
| 318 | 302 | dump_end_iteration logger dump_core_sizes name_ppr_ctx iteration_no counts1
|
| 319 | - [CoreCompUnit binds2 unit_rules1] rules1 ;
|
|
| 303 | + binds2 rules1 ;
|
|
| 320 | 304 | |
| 321 | 305 | for_ (so_pass_result_cfg opts) $ \pass_result_cfg ->
|
| 322 | - lintPassResult logger pass_result_cfg [CoreCompUnit binds2 unit_rules1] rules1 ;
|
|
| 306 | + lintPassResult logger pass_result_cfg binds2 rules1 ;
|
|
| 323 | 307 | |
| 324 | 308 | -- Loop
|
| 325 | - do_iteration (iteration_no + 1) (counts1:counts_so_far) [CoreCompUnit binds2 unit_rules1] rules1
|
|
| 309 | + do_iteration (iteration_no + 1) (counts1:counts_so_far) binds2 rules1
|
|
| 326 | 310 | } }
|
| 327 | 311 | where
|
| 328 | 312 | bind_list = concatMap coreCompUnitBinds binds
|
| 329 | - unit_rules = concatMap cu_rules binds
|
|
| 330 | 313 | -- Remember the counts_so_far are reversed
|
| 331 | 314 | totalise :: [SimplCount] -> SimplCount
|
| 332 | 315 | totalise = foldr (\c acc -> acc `plusSimplCount` c)
|
| 333 | 316 | (zeroSimplCount $ logHasDumpFlag logger Opt_D_dump_simpl_stats)
|
| 334 | 317 | |
| 318 | + -- Keep top-level in-scope sets per-unit until CoreMerge.
|
|
| 319 | + simpl_comp_units
|
|
| 320 | + :: SimplEnv
|
|
| 321 | + -> [CoreRule]
|
|
| 322 | + -> CoreProgram
|
|
| 323 | + -> Int
|
|
| 324 | + -> IO ((CoreProgram, [CoreRule]), SimplCount)
|
|
| 325 | + simpl_comp_units simpl_env rules0 units0 sz = go rules0 [] zero_counts units0
|
|
| 326 | + where
|
|
| 327 | + zero_counts = zeroSimplCount $ logHasDumpFlag logger Opt_D_dump_simpl_stats
|
|
| 328 | + |
|
| 329 | + go !rules acc !counts [] = return ((reverse acc, rules), counts)
|
|
| 330 | + go !rules acc !counts (CoreCompUnit unit_binds unit_rules' : rest) = do
|
|
| 331 | + let unit_bndrs = mkVarSet (bindersOfBinds unit_binds)
|
|
| 332 | + (visible_rules, hidden_rules) = partitionVisibleImpRules unit_bndrs rules
|
|
| 333 | + !base_rule_env = updLocalRules hpt_rule_env (visible_rules ++ unit_rules')
|
|
| 334 | + read_eps_rules = eps_rule_base <$> ueEPS unit_env
|
|
| 335 | + read_rule_env = updExternalPackageRules base_rule_env <$> read_eps_rules
|
|
| 336 | + |
|
| 337 | + ((unit1, visible_rules1), counts1) <-
|
|
| 338 | + initSmpl logger read_rule_env top_env_cfg sz $ do
|
|
| 339 | + (floats, env1) <- {-# SCC "SimplTopBindsUnit" #-}
|
|
| 340 | + simplTopBinds simpl_env unit_binds
|
|
| 341 | + |
|
| 342 | + -- Apply substitutions from this unit to imported-head rules and
|
|
| 343 | + -- the unit's own rules. Keep each unit's local rules separate.
|
|
| 344 | + visible_rules1 <- simplImpRules env1 visible_rules
|
|
| 345 | + unit_rules1 <- simplImpRules env1 unit_rules'
|
|
| 346 | + |
|
| 347 | + let unit_binds1 = getTopFloatBinds floats
|
|
| 348 | + pure (CoreCompUnit unit_binds1 unit_rules1, visible_rules1)
|
|
| 349 | + |
|
| 350 | + let rules1 = visible_rules1 ++ hidden_rules
|
|
| 351 | + go rules1 (unit1 : acc) (counts `plusSimplCount` counts1) rest
|
|
| 352 | + |
|
| 353 | + partitionVisibleImpRules :: VarSet -> [CoreRule] -> ([CoreRule], [CoreRule])
|
|
| 354 | + partitionVisibleImpRules unit_bndrs = foldr go_rule ([], [])
|
|
| 355 | + where
|
|
| 356 | + go_rule rule (visible, hidden)
|
|
| 357 | + | rule_mentions_unit = (rule : visible, hidden)
|
|
| 358 | + | rule_has_local_fvs = (visible, rule : hidden)
|
|
| 359 | + | otherwise = (rule : visible, hidden)
|
|
| 360 | + where
|
|
| 361 | + local_fvs = ruleFreeVars rule `intersectVarSet` all_local_bndrs
|
|
| 362 | + rule_has_local_fvs = not (isEmptyVarSet local_fvs)
|
|
| 363 | + rule_mentions_unit = not (isEmptyVarSet (local_fvs `intersectVarSet` unit_bndrs))
|
|
| 364 | + |
|
| 365 | + all_local_bndrs :: VarSet
|
|
| 366 | + all_local_bndrs = mkVarSet (bindersOfBinds (concatMap coreCompUnitBinds (dsd_binds dyn)))
|
|
| 367 | + |
|
| 335 | 368 | dump_end_iteration :: Logger -> Bool -> NamePprCtx -> Int
|
| 336 | 369 | -> SimplCount -> CoreProgram -> [CoreRule] -> IO ()
|
| 337 | 370 | dump_end_iteration logger dump_core_sizes name_ppr_ctx iteration_no counts binds rules
|
| ... | ... | @@ -794,9 +794,12 @@ specConstrProgram guts |
| 794 | 794 | scTopCompUnits :: ScEnv -> CoreProgram -> UniqSM (ScUsage, CoreProgram, [SpecFailWarning])
|
| 795 | 795 | scTopCompUnits _env [] = return (nullUsage, [], [])
|
| 796 | 796 | scTopCompUnits env (CoreCompUnit unit_binds unit_rules:units) = do
|
| 797 | - (unit_usg, unit_binds', unit_warnings) <- scTopBinds env unit_binds
|
|
| 798 | - (units_usg, units', units_warnings) <- scTopCompUnits env units
|
|
| 799 | - return (unit_usg `combineUsage` units_usg, CoreCompUnit unit_binds' unit_rules : units', unit_warnings ++ units_warnings)
|
|
| 797 | + let unit_env = initScCompUnitEnv env unit_binds
|
|
| 798 | + (_unit_usg, unit_binds', unit_warnings) <- scTopBinds unit_env unit_binds
|
|
| 799 | + (_units_usg, units', units_warnings) <- scTopCompUnits env units
|
|
| 800 | + -- Before CoreMerge, different compilation units may legitimately reuse the
|
|
| 801 | + -- same top-level Id/Unique, so we must not combine ScUsage across units.
|
|
| 802 | + return (nullUsage, CoreCompUnit unit_binds' unit_rules : units', unit_warnings ++ units_warnings)
|
|
| 800 | 803 | |
| 801 | 804 | scTopBinds :: ScEnv -> [InBind] -> UniqSM (ScUsage, [OutBind], [SpecFailWarning])
|
| 802 | 805 | scTopBinds _env [] = return (nullUsage, [], [])
|
| ... | ... | @@ -1009,18 +1012,19 @@ initScEnv guts |
| 1009 | 1012 | ; this_mod <- getModule
|
| 1010 | 1013 | ; return (SCE { sc_opts = initScOpts dflags this_mod,
|
| 1011 | 1014 | sc_force = False,
|
| 1012 | - sc_subst = init_subst,
|
|
| 1015 | + sc_subst = mkEmptySubst emptyInScopeSet,
|
|
| 1013 | 1016 | sc_how_bound = emptyVarEnv,
|
| 1014 | 1017 | sc_vals = emptyVarEnv,
|
| 1015 | 1018 | sc_annotations = anns }) }
|
| 1019 | + |
|
| 1020 | +initScCompUnitEnv :: ScEnv -> [InBind] -> ScEnv
|
|
| 1021 | +initScCompUnitEnv env unit_binds
|
|
| 1022 | + = env { sc_subst = mkEmptySubst in_scope }
|
|
| 1016 | 1023 | where
|
| 1017 | - init_subst = mkEmptySubst $ foldl' addCompUnitBndrs emptyInScopeSet (mg_binds guts)
|
|
| 1018 | - -- Acccount for top-level bindings that are not in dependency order;
|
|
| 1019 | - -- see Note [Glomming] in GHC.Core.Opt.OccurAnal
|
|
| 1020 | - -- Easiest thing is to bring all the top level binders into scope at once,
|
|
| 1021 | - -- as if at once, as if all the top-level decls were mutually recursive.
|
|
| 1022 | - addCompUnitBndrs scope (CoreCompUnit unit_binds _) =
|
|
| 1023 | - scope `extendInScopeSetBndrs` unit_binds
|
|
| 1024 | + in_scope = emptyInScopeSet `extendInScopeSetBndrs` unit_binds
|
|
| 1025 | + -- Account for top-level bindings that are not in dependency order;
|
|
| 1026 | + -- see Note [Glomming] in GHC.Core.Opt.OccurAnal.
|
|
| 1027 | + -- Crucially, only add binders from the current compilation unit.
|
|
| 1024 | 1028 | |
| 1025 | 1029 | data HowBound = RecFun -- These are the recursive functions for which
|
| 1026 | 1030 | -- we seek interesting call patterns
|
| ... | ... | @@ -1443,7 +1447,8 @@ scBind top_lvl env (NonRec bndr rhs) do_body |
| 1443 | 1447 | -- but found some regressions (see !8135). So I backed off.
|
| 1444 | 1448 | = do { (rhs_usage, rhs', ws_rhs) <- scExpr env rhs
|
| 1445 | 1449 | |
| 1446 | - -- At top level, we've already put all binders into scope; see initScEnv
|
|
| 1450 | + -- At top level, we've already put the current compilation unit's
|
|
| 1451 | + -- binders into scope; see initScCompUnitEnv.
|
|
| 1447 | 1452 | -- Hence no need to call `extendBndr`. But we still want to
|
| 1448 | 1453 | -- extend the `ValueEnv` to record the value of this binder.
|
| 1449 | 1454 | ; let body_env = extendValEnv env bndr (isValue (sc_vals env) rhs')
|
| ... | ... | @@ -1491,7 +1496,8 @@ scBind top_lvl env (Rec prs) do_body |
| 1491 | 1496 | |
| 1492 | 1497 | (rhs_env1,bndrs') | isTopLevel top_lvl = (env, bndrs)
|
| 1493 | 1498 | | otherwise = extendRecBndrs env bndrs
|
| 1494 | - -- At top level, we've already put all binders into scope; see initScEnv
|
|
| 1499 | + -- At top level, we've already put the current compilation unit's
|
|
| 1500 | + -- binders into scope; see initScCompUnitEnv.
|
|
| 1495 | 1501 | |
| 1496 | 1502 | rhs_env2 = extendHowBound rhs_env1 bndrs' RecFun
|
| 1497 | 1503 |
| ... | ... | @@ -65,6 +65,7 @@ import GHC.Utils.Outputable |
| 65 | 65 | import GHC.Utils.Panic
|
| 66 | 66 | |
| 67 | 67 | import GHC.Unit.Module( Module )
|
| 68 | +import GHC.Unit.Module.Deps ( Dependencies )
|
|
| 68 | 69 | import GHC.Unit.Module.ModGuts
|
| 69 | 70 | import GHC.Core.Unfold
|
| 70 | 71 | |
| ... | ... | @@ -641,10 +642,9 @@ Hence, the invariant is this: |
| 641 | 642 | -- | Specialise calls to type-class overloaded functions occurring in a program.
|
| 642 | 643 | specProgram :: ModGuts -> CoreM ModGuts
|
| 643 | 644 | specProgram guts = do
|
| 644 | - rule_env <- initRuleEnv guts
|
|
| 645 | 645 | let static = StaticSpecInput
|
| 646 | 646 | { ssi_module = mg_module guts
|
| 647 | - , ssi_rule_env = rule_env
|
|
| 647 | + , ssi_deps = mg_deps guts
|
|
| 648 | 648 | }
|
| 649 | 649 | dyn = DynamicSpecData
|
| 650 | 650 | { dsd_binds = mg_binds guts
|
| ... | ... | @@ -655,7 +655,7 @@ specProgram guts = do |
| 655 | 655 | |
| 656 | 656 | data StaticSpecInput = StaticSpecInput
|
| 657 | 657 | { ssi_module :: !Module
|
| 658 | - , ssi_rule_env :: !RuleEnv
|
|
| 658 | + , ssi_deps :: !Dependencies
|
|
| 659 | 659 | }
|
| 660 | 660 | |
| 661 | 661 | data DynamicSpecData = DynamicSpecData
|
| ... | ... | @@ -680,20 +680,26 @@ specProgram' static dyn |
| 680 | 680 | -- | Specialise calls to type-class overloaded functions occurring in a program.
|
| 681 | 681 | specCompUnit :: StaticSpecInput -> DynFlags -> CoreCompUnit -> CoreM CoreCompUnit
|
| 682 | 682 | specCompUnit static dflags (CoreCompUnit unit_binds unit_rules)
|
| 683 | - = do { (unit_binds', uds) <- runSpecM (go unit_binds)
|
|
| 683 | + = do { hpt_rules <- getHomeRuleBase
|
|
| 684 | + ; eps_rules <- getExternalRuleBase
|
|
| 685 | + ; let rule_env = mkRuleEnv (ssi_module static) (ssi_deps static) [] [unit]
|
|
| 686 | + eps_rules hpt_rules
|
|
| 687 | + top_env = SE { se_subst = Core.mkEmptySubst in_scope
|
|
| 688 | + , se_module = ssi_module static
|
|
| 689 | + , se_rules = rule_env
|
|
| 690 | + , se_dflags = dflags }
|
|
| 691 | + ; (unit_binds', uds) <- runSpecM (go top_env unit_binds)
|
|
| 684 | 692 | ; (spec_rules, spec_binds) <- specImports top_env uds
|
| 685 | 693 | ; return (CoreCompUnit (spec_binds ++ unit_binds') (spec_rules ++ unit_rules)) }
|
| 686 | 694 | where
|
| 687 | - top_env = SE { se_subst = Core.mkEmptySubst in_scope
|
|
| 688 | - , se_module = ssi_module static
|
|
| 689 | - , se_rules = ssi_rule_env static
|
|
| 690 | - , se_dflags = dflags }
|
|
| 695 | + unit = CoreCompUnit unit_binds unit_rules
|
|
| 691 | 696 | in_scope = mkInScopeSetBndrs unit_binds
|
| 692 | 697 | |
| 693 | - go [] = return ([], emptyUDs)
|
|
| 694 | - go (bind:binds) = do (bind', binds', uds') <- specBind TopLevel top_env bind $ \_ ->
|
|
| 695 | - go binds
|
|
| 696 | - return (bind' ++ binds', uds')
|
|
| 698 | + go _ [] = return ([], emptyUDs)
|
|
| 699 | + go env (bind:binds)
|
|
| 700 | + = do (bind', binds', uds') <- specBind TopLevel env bind $ \_ ->
|
|
| 701 | + go env binds
|
|
| 702 | + return (bind' ++ binds', uds')
|
|
| 697 | 703 | |
| 698 | 704 | {-
|
| 699 | 705 | Note [Wrap bindings returned by specImports]
|
| 1 | +{-# LANGUAGE ViewPatterns #-}
|
|
| 2 | + |
|
| 3 | +module GHC.Core.Opt.Split
|
|
| 4 | + ( splitCompUnit
|
|
| 5 | + , checkNameClashes
|
|
| 6 | + ) where
|
|
| 7 | + |
|
| 8 | +import GHC.Prelude hiding ( head, init, last )
|
|
| 9 | + |
|
| 10 | +import GHC.Core
|
|
| 11 | +import GHC.Core.FVs
|
|
| 12 | +import GHC.Core.Opt.OccurAnal (occurAnalyseCompUnit)
|
|
| 13 | + |
|
| 14 | +import GHC.Data.Graph.Directed (SCC(..), Node(..), stronglyConnCompFromEdgedVerticesUniq)
|
|
| 15 | +import GHC.Data.Maybe (orElse)
|
|
| 16 | + |
|
| 17 | +import GHC.Types.Unique.Set
|
|
| 18 | +import GHC.Types.Name (isExternalName, nameModule)
|
|
| 19 | +import GHC.Types.Id (realIdUnfolding)
|
|
| 20 | +import GHC.Types.Var.Set
|
|
| 21 | +import GHC.Types.Var.Env
|
|
| 22 | +import GHC.Types.Var
|
|
| 23 | + |
|
| 24 | +import GHC.Utils.Outputable
|
|
| 25 | +import GHC.Utils.Panic
|
|
| 26 | + |
|
| 27 | +import GHC.Unit.Module (Module)
|
|
| 28 | + |
|
| 29 | +import qualified Data.IntMap.Strict as IntMap
|
|
| 30 | +import qualified Data.IntSet as IntSet
|
|
| 31 | +import Data.List (find)
|
|
| 32 | + |
|
| 33 | +{- Note [Splitting core programs]
|
|
| 34 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 35 | +splitCompUnit splits a single compilation unit into multiple.
|
|
| 36 | +To do so we:
|
|
| 37 | + |
|
| 38 | +Initially we run OccAnal on the compilation unit. I don't think it's strictly neccessary
|
|
| 39 | +but it zaps fragile unfoldings which speeds up and gets rid of glomming.
|
|
| 40 | +We will try getting rid of this later.
|
|
| 41 | + |
|
| 42 | +We split imported rules into those which only concern imported rules and those mentioning
|
|
| 43 | +local binders (called unit_rules).
|
|
| 44 | + |
|
| 45 | +Rules mentioning local binders introduce edges between any local binders they mention.
|
|
| 46 | + |
|
| 47 | +Next we build the graph nodes from binders:
|
|
| 48 | +* Fully nodes have one key, the first Id the binder defines. Their node data is the binder itself.
|
|
| 49 | +* Pseudo nodes for every id the binder defines (tail $ bindersOf bind) with no data.
|
|
| 50 | + |
|
| 51 | +Now we introduce edges:
|
|
| 52 | + * For binders from the first id to all other defined ids and those it mentions as fv
|
|
| 53 | + let key = (head bindersOf bind)
|
|
| 54 | + let edges = map (\x -> (key, x)) bindersOf bind ++ map (\x -> (key, x)) (bindFreeVars bind)
|
|
| 55 | + * For rules if they mention local binders introduce edges between any local binders they mention.
|
|
| 56 | + |
|
| 57 | +* All edges computed so far are directional. So we take all edges and also add their reversed version.
|
|
| 58 | + |
|
| 59 | +* After this we split the graph into independent components.
|
|
| 60 | + |
|
| 61 | +* As the last step we assign each unit rule to a unit from which it mentions variables
|
|
| 62 | +-}
|
|
| 63 | + |
|
| 64 | +data DepGraphNode
|
|
| 65 | + = BindNode
|
|
| 66 | + { depNodeKey :: !Var
|
|
| 67 | + , depNodeBind :: CoreBind
|
|
| 68 | + }
|
|
| 69 | + | PseudoNode
|
|
| 70 | + { depNodeKey :: !Var }
|
|
| 71 | + |
|
| 72 | +type Edge = (Var, Var)
|
|
| 73 | + |
|
| 74 | +-- | Is the given variable defined in the given module.
|
|
| 75 | +varFromModule :: Module -> Var -> Bool
|
|
| 76 | +varFromModule _ var = isLocalId var
|
|
| 77 | + |
|
| 78 | +maybeRuleEdges :: Module -> CoreRule -> Maybe [Edge]
|
|
| 79 | +maybeRuleEdges this_module rule =
|
|
| 80 | + case local_fvs of
|
|
| 81 | + [] -> Nothing
|
|
| 82 | + [_] -> Just []
|
|
| 83 | + _ -> Just (zip local_fvs (drop 1 local_fvs))
|
|
| 84 | + where
|
|
| 85 | + local_fvs = filter (varFromModule this_module) (nonDetEltsUniqSet (ruleFreeVars rule))
|
|
| 86 | + |
|
| 87 | +bindNode :: VarSet -> CoreBind -> ([DepGraphNode], [Edge])
|
|
| 88 | +bindNode local_top_bndrs bind =
|
|
| 89 | + case bindersOf bind of
|
|
| 90 | + [] -> ([], [])
|
|
| 91 | + key:rest ->
|
|
| 92 | + let split_fvs = bindSplitFreeVars local_top_bndrs bind
|
|
| 93 | + intern_edges = map (\v -> (key, v)) rest
|
|
| 94 | + ext_edges = map (\v -> (key, v)) (nonDetEltsUniqSet split_fvs)
|
|
| 95 | + node = BindNode key bind
|
|
| 96 | + pseudo_nodes = map PseudoNode rest
|
|
| 97 | + in (node : pseudo_nodes, intern_edges ++ ext_edges)
|
|
| 98 | + |
|
| 99 | +bindSplitFreeVars :: VarSet -> CoreBind -> VarSet
|
|
| 100 | +bindSplitFreeVars local_top_bndrs bind =
|
|
| 101 | + close_over_imported_unfoldings (bindMentionedVars bind `unionVarSet` bindBndrInfoVars bind)
|
|
| 102 | + where
|
|
| 103 | + close_over_imported_unfoldings fvs = go emptyVarSet fvs
|
|
| 104 | + |
|
| 105 | + go !seen !fvs =
|
|
| 106 | + case pick_new_import (fvs `minusVarSet` seen) of
|
|
| 107 | + Nothing -> fvs
|
|
| 108 | + Just v ->
|
|
| 109 | + let unfolding_fvs = unfoldingRefs v
|
|
| 110 | + local_unfolding_fvs = unfolding_fvs `intersectVarSet` local_top_bndrs
|
|
| 111 | + in go (extendVarSet seen v) (fvs `unionVarSet` local_unfolding_fvs `unionVarSet` unfolding_fvs)
|
|
| 112 | + |
|
| 113 | + pick_new_import vars =
|
|
| 114 | + find pickable (nonDetEltsUniqSet vars)
|
|
| 115 | + |
|
| 116 | + pickable v = isId v && not (v `elemVarSet` local_top_bndrs)
|
|
| 117 | + |
|
| 118 | + unfoldingRefs v =
|
|
| 119 | + case maybeUnfoldingTemplate (realIdUnfolding v) of
|
|
| 120 | + Just rhs -> exprSomeFreeVars (const True) rhs
|
|
| 121 | + Nothing -> emptyVarSet
|
|
| 122 | + |
|
| 123 | +bindMentionedVars :: CoreBind -> VarSet
|
|
| 124 | +bindMentionedVars (NonRec _ rhs) = exprSomeFreeVars (const True) rhs
|
|
| 125 | +bindMentionedVars (Rec prs) = exprsSomeFreeVars (const True) (map snd prs)
|
|
| 126 | + |
|
| 127 | +bindBndrInfoVars :: CoreBind -> VarSet
|
|
| 128 | +bindBndrInfoVars bind =
|
|
| 129 | + mkVarSet $
|
|
| 130 | + concatMap (dVarSetElems . bndrRuleAndUnfoldingVarsDSet) (bindersOf bind)
|
|
| 131 | + |
|
| 132 | +type Edges = IdEnv [Var]
|
|
| 133 | + |
|
| 134 | +-- Split core binders takes directed edges treating them as undirected by adding the reverse edge internally.
|
|
| 135 | +splitCoreBinders :: [DepGraphNode] -> [Edge] -> [(VarSet, [CoreBind])]
|
|
| 136 | +splitCoreBinders nodes edges =
|
|
| 137 | + [ (mkVarSet (concatMap bindersOf binds), binds)
|
|
| 138 | + | comp_nodes <- map scc_payloads (stronglyConnCompFromEdgedVerticesUniq (map mk_graph_node nodes))
|
|
| 139 | + , let binds = [ b | BindNode { depNodeBind = b } <- comp_nodes ]
|
|
| 140 | + ]
|
|
| 141 | + where
|
|
| 142 | + key_set = mkVarSet (map depNodeKey nodes)
|
|
| 143 | + undirected_edges = foldr add_edge emptyVarEnv (edges ++ map reverse_edge edges)
|
|
| 144 | + |
|
| 145 | + add_edge :: Edge -> Edges -> Edges
|
|
| 146 | + add_edge (src, dst) env = extendVarEnv_C (++) env src [dst]
|
|
| 147 | + |
|
| 148 | + reverse_edge :: Edge -> Edge
|
|
| 149 | + reverse_edge (src, dst) = (dst, src)
|
|
| 150 | + |
|
| 151 | + mk_graph_node node
|
|
| 152 | + = DigraphNode
|
|
| 153 | + { node_payload = node
|
|
| 154 | + , node_key = varUnique key
|
|
| 155 | + , node_dependencies =
|
|
| 156 | + [ varUnique dst
|
|
| 157 | + | dst <- lookupVarEnv undirected_edges key `orElse` []
|
|
| 158 | + , elemVarSet dst key_set
|
|
| 159 | + ]
|
|
| 160 | + }
|
|
| 161 | + where
|
|
| 162 | + key = depNodeKey node
|
|
| 163 | + |
|
| 164 | + scc_payloads (AcyclicSCC p) = [p]
|
|
| 165 | + scc_payloads (CyclicSCC ps) = ps
|
|
| 166 | + |
|
| 167 | +assignLocalRules
|
|
| 168 | + :: [CoreRule]
|
|
| 169 | + -> [(VarSet, [CoreBind])]
|
|
| 170 | + -> ([(VarSet, [CoreBind], [CoreRule])], [CoreRule])
|
|
| 171 | +assignLocalRules unit_rules binder_components =
|
|
| 172 | + (components_with_rules, rules_without_component)
|
|
| 173 | + where
|
|
| 174 | + (component_rule_map, rules_without_component)
|
|
| 175 | + = foldr assign_rule (IntMap.empty, []) unit_rules
|
|
| 176 | + |
|
| 177 | + assign_rule rule (rule_map, no_comp_rules)
|
|
| 178 | + = case rule_comp_indices rule of
|
|
| 179 | + [i] -> (IntMap.insertWith (++) i [rule] rule_map, no_comp_rules)
|
|
| 180 | + [] -> (rule_map, rule : no_comp_rules)
|
|
| 181 | + is -> pprPanic "splitCompUnit"
|
|
| 182 | + ( text "Rule free vars span multiple components"
|
|
| 183 | + $$ text "rule:" <+> ppr rule
|
|
| 184 | + $$ text "components:" <+> ppr is
|
|
| 185 | + $$ text "rule_fvs:" <+> pprVarsWithModule (nonDetEltsUniqSet (ruleFreeVars rule))
|
|
| 186 | + $$ vcat [ text "component" <+> int i <> colon <+> ppr hits
|
|
| 187 | + | (i, hits) <- comp_hits rule ] )
|
|
| 188 | + |
|
| 189 | + rule_comp_indices rule
|
|
| 190 | + = IntSet.toList $ IntSet.fromList
|
|
| 191 | + [ i
|
|
| 192 | + | ((bndrs, _), i) <- zip binder_components [0..]
|
|
| 193 | + , not (isEmptyVarSet (ruleFreeVars rule `intersectVarSet` bndrs))
|
|
| 194 | + ]
|
|
| 195 | + |
|
| 196 | + comp_hits rule =
|
|
| 197 | + [ (i, ruleFreeVars rule `intersectVarSet` bndrs)
|
|
| 198 | + | ((bndrs, _), i) <- zip binder_components [0..]
|
|
| 199 | + , not (isEmptyVarSet (ruleFreeVars rule `intersectVarSet` bndrs))
|
|
| 200 | + ]
|
|
| 201 | + |
|
| 202 | + components_with_rules =
|
|
| 203 | + [ (bndrs, binds, IntMap.findWithDefault [] i component_rule_map)
|
|
| 204 | + | ((bndrs, binds), i) <- zip binder_components [0..]
|
|
| 205 | + ]
|
|
| 206 | + |
|
| 207 | +pprVarsWithModule :: [Var] -> SDoc
|
|
| 208 | +pprVarsWithModule vars = braces (fsep (punctuate comma (map pprVarWithModule vars)))
|
|
| 209 | + |
|
| 210 | +pprVarWithModule :: Var -> SDoc
|
|
| 211 | +pprVarWithModule v
|
|
| 212 | + | isExternalName n = ppr v <+> parens (ppr (nameModule n))
|
|
| 213 | + | otherwise = ppr v
|
|
| 214 | + where
|
|
| 215 | + n = varName v
|
|
| 216 | + |
|
| 217 | +-- After optimizations a rule might no longer reference binders from this module.
|
|
| 218 | +-- In these cases we return them here and then add them to mg_rules.
|
|
| 219 | +splitCompUnit :: Module -> [CoreRule] -> CoreCompUnit -> ([CoreCompUnit], [CoreRule])
|
|
| 220 | +splitCompUnit this_module imp_rules unit
|
|
| 221 | + = let comp_units = map mk_comp_unit components_with_rules
|
|
| 222 | + in checkNameClashes comp_units `seq`
|
|
| 223 | + (comp_units, rules_for_imps ++ rules_without_component)
|
|
| 224 | + where
|
|
| 225 | + CoreCompUnit occ_binds unit_rules =
|
|
| 226 | + occurAnalyseCompUnit this_module (const True) (const True) imp_rules unit
|
|
| 227 | + |
|
| 228 | + top_level_bndrs = bindersOfBinds occ_binds
|
|
| 229 | + checked_bndrs =
|
|
| 230 | + assertPpr (all isLocalVar top_level_bndrs)
|
|
| 231 | + ( text "splitCompUnit: non-local top-level binder(s)"
|
|
| 232 | + $$ ppr top_level_bndrs )
|
|
| 233 | + top_level_bndrs
|
|
| 234 | + |
|
| 235 | + local_top_bndrs = mkVarSet checked_bndrs
|
|
| 236 | + |
|
| 237 | + (bind_nodes, bind_edges)
|
|
| 238 | + = checked_bndrs `seq`
|
|
| 239 | + foldr (\b (ns, es) -> let (ns', es') = bindNode local_top_bndrs b in (ns' ++ ns, es' ++ es))
|
|
| 240 | + ([], [])
|
|
| 241 | + occ_binds
|
|
| 242 | + |
|
| 243 | + rule_edge_pairs = [ (r, maybeRuleEdges this_module r) | r <- unit_rules ]
|
|
| 244 | + rule_edges = concat [ es | (_, Just es) <- rule_edge_pairs ]
|
|
| 245 | + rules_for_imps = [ r | (r, Nothing) <- rule_edge_pairs ]
|
|
| 246 | + unit_rules_local = [ r | (r, Just _) <- rule_edge_pairs ]
|
|
| 247 | + |
|
| 248 | + all_edges = bind_edges ++ rule_edges
|
|
| 249 | + binder_components = splitCoreBinders bind_nodes all_edges
|
|
| 250 | + (components_with_rules, rules_without_component) =
|
|
| 251 | + assignLocalRules unit_rules_local binder_components
|
|
| 252 | + |
|
| 253 | + mk_comp_unit (_, binds, rules) = CoreCompUnit binds rules
|
|
| 254 | + |
|
| 255 | +checkNameClashes :: [CoreCompUnit] -> ()
|
|
| 256 | +checkNameClashes comp_units
|
|
| 257 | + | null dup_bndrs = ()
|
|
| 258 | + | otherwise
|
|
| 259 | + = pprPanic "checkNameClashes"
|
|
| 260 | + ( text "Duplicate top-level binders across split compilation units"
|
|
| 261 | + $$ ppr dup_bndrs )
|
|
| 262 | + where
|
|
| 263 | + all_bndrs = concatMap (bindersOfBinds . coreCompUnitBinds) comp_units
|
|
| 264 | + |
|
| 265 | + dup_bndrs :: [Var]
|
|
| 266 | + dup_bndrs = go emptyVarSet all_bndrs
|
|
| 267 | + |
|
| 268 | + go _ [] = []
|
|
| 269 | + go seen (b:bs)
|
|
| 270 | + | b `elemVarSet` seen = b : go seen bs
|
|
| 271 | + | otherwise = go (extendVarSet seen b) bs |
| ... | ... | @@ -205,7 +205,8 @@ simpleOptPgm opts this_mod binds rules = |
| 205 | 205 | |
| 206 | 206 | do_unit (env, comp_units') (CoreCompUnit unit_binds unit_rules)
|
| 207 | 207 | = let (env', unit_binds') = foldl' do_one (env, []) unit_binds
|
| 208 | - in (env', CoreCompUnit (reverse unit_binds') unit_rules : comp_units')
|
|
| 208 | + unit_rules' = map (substRule (soe_subst env') id) unit_rules
|
|
| 209 | + in (env', CoreCompUnit (reverse unit_binds') unit_rules' : comp_units')
|
|
| 209 | 210 | |
| 210 | 211 | do_one (env, binds') bind
|
| 211 | 212 | = case simple_opt_bind env bind TopLevel of
|
| ... | ... | @@ -13,7 +13,7 @@ module GHC.Core.Subst ( |
| 13 | 13 | |
| 14 | 14 | -- ** Substituting into expressions and related types
|
| 15 | 15 | deShadowBinds, deShadowCompUnits,
|
| 16 | - substRuleInfo, substRulesForImportedIds,
|
|
| 16 | + substRule, substRuleInfo, substRulesForImportedIds,
|
|
| 17 | 17 | substTyUnchecked, substCo, substExpr, substExprSC, substBind, substBindSC,
|
| 18 | 18 | substUnfolding, substUnfoldingSC,
|
| 19 | 19 | lookupIdSubst, lookupIdSubst_maybe, substIdType, substIdOcc,
|
| ... | ... | @@ -393,6 +393,7 @@ Library |
| 393 | 393 | GHC.Core.Opt.Simplify.Utils
|
| 394 | 394 | GHC.Core.Opt.SpecConstr
|
| 395 | 395 | GHC.Core.Opt.Specialise
|
| 396 | + GHC.Core.Opt.Split
|
|
| 396 | 397 | GHC.Core.Opt.StaticArgs
|
| 397 | 398 | GHC.Core.Opt.Stats
|
| 398 | 399 | GHC.Core.Opt.WorkWrap
|
| ... | ... | @@ -2842,7 +2842,21 @@ def _normalised_outputs(expected_file: Path, |
| 2842 | 2842 | return expected, actual
|
| 2843 | 2843 | |
| 2844 | 2844 | def _sorted_lines(s: str) -> str:
|
| 2845 | - return '\n'.join(sorted(s.splitlines()))
|
|
| 2845 | + return '\n'.join(sorted(_drop_split_comp_unit_headers(s.splitlines())))
|
|
| 2846 | + |
|
| 2847 | +def _drop_split_comp_unit_headers(lines: List[str]) -> List[str]:
|
|
| 2848 | + kept: List[str] = []
|
|
| 2849 | + i = 0
|
|
| 2850 | + while i < len(lines):
|
|
| 2851 | + # Ignore an empty line immediately followed by the split-core unit header.
|
|
| 2852 | + if i + 1 < len(lines) \
|
|
| 2853 | + and lines[i] == '' \
|
|
| 2854 | + and lines[i + 1].startswith('=== Start of new compilation unit'):
|
|
| 2855 | + i += 2
|
|
| 2856 | + continue
|
|
| 2857 | + kept.append(lines[i])
|
|
| 2858 | + i += 1
|
|
| 2859 | + return kept
|
|
| 2846 | 2860 | |
| 2847 | 2861 | def _is_reordered_output_mismatch(expected_file: Path,
|
| 2848 | 2862 | actual_file: Path,
|