Andreas Klebinger pushed to branch wip/andreask/ghc_par at Glasgow Haskell Compiler / GHC

Commits:

11 changed files:

Changes:

  • compiler/GHC/Core/Opt/CSE.hs
    ... ... @@ -25,6 +25,7 @@ import GHC.Core.Map.Expr
    25 25
     import GHC.Core.Opt.CompUnit (parMapCompUnits)
    
    26 26
     import GHC.Utils.Misc   ( filterOut, equalLength )
    
    27 27
     import GHC.Utils.Panic
    
    28
    +import GHC.Utils.Logger (Logger)
    
    28 29
     import Data.Functor.Identity ( Identity (..) )
    
    29 30
     import Data.List        ( mapAccumL )
    
    30 31
     
    
    ... ... @@ -380,8 +381,8 @@ body/rest of the module.
    380 381
     ************************************************************************
    
    381 382
     -}
    
    382 383
     
    
    383
    -cseProgram :: CoreProgram -> CoreProgram
    
    384
    -cseProgram = parMapCompUnits cseCoreCompUnit
    
    384
    +cseProgram :: Logger -> CoreProgram -> CoreProgram
    
    385
    +cseProgram logger = parMapCompUnits logger "CommonSubExpr" cseCoreCompUnit
    
    385 386
     
    
    386 387
     cseCoreCompUnit :: CoreCompUnit -> CoreCompUnit
    
    387 388
     cseCoreCompUnit (CoreCompUnit unit_binds unit_rules)
    

  • compiler/GHC/Core/Opt/CallArity.hs
    ... ... @@ -21,6 +21,7 @@ import GHC.Core.Opt.CompUnit (parMapCompUnits)
    21 21
     import GHC.Core.Utils ( exprIsCheap, exprIsTrivial )
    
    22 22
     import GHC.Data.Graph.UnVar
    
    23 23
     import GHC.Types.Demand
    
    24
    +import GHC.Utils.Logger (Logger)
    
    24 25
     import GHC.Utils.Misc
    
    25 26
     
    
    26 27
     import Control.Arrow ( first, second )
    
    ... ... @@ -434,8 +435,8 @@ choice, and hence Call Arity sets the call arity for join points as well.
    434 435
     
    
    435 436
     -- Main entry point
    
    436 437
     
    
    437
    -callArityAnalProgram :: CoreProgram -> CoreProgram
    
    438
    -callArityAnalProgram = parMapCompUnits callArityCompUnit
    
    438
    +callArityAnalProgram :: Logger -> CoreProgram -> CoreProgram
    
    439
    +callArityAnalProgram logger = parMapCompUnits logger "CallArity" callArityCompUnit
    
    439 440
       where
    
    440 441
         callArityCompUnit (CoreCompUnit binds unit_rules)
    
    441 442
           = let (_ae, binds') = callArityTopLvl [] emptyVarSet binds
    

  • compiler/GHC/Core/Opt/CompUnit.hs
    1 1
     module GHC.Core.Opt.CompUnit
    
    2 2
       ( parMapCompUnits
    
    3
    +  , coreCompUnitTimingDoc
    
    4
    +  , forceCompUnit
    
    3 5
       ) where
    
    4 6
     
    
    5 7
     import GHC.Prelude
    
    6 8
     
    
    7
    -import GHC.Conc (par, pseq)
    
    9
    +import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar)
    
    10
    +import Control.Exception (SomeException, evaluate, throwIO, try)
    
    11
    +import System.IO.Unsafe (unsafePerformIO)
    
    8 12
     
    
    13
    +import GHC.Driver.Flags (DumpFlag(Opt_D_dump_timings))
    
    9 14
     import GHC.Core
    
    10
    -import GHC.Core.Seq (seqBinds)
    
    15
    +import GHC.Core.Stats (coreBindsSize)
    
    16
    +import GHC.Core.Seq (seqBinds, seqRules)
    
    17
    +import GHC.Utils.Error (withTiming)
    
    18
    +import GHC.Utils.Logger (Logger, logHasDumpFlag)
    
    19
    +import GHC.Utils.Outputable
    
    20
    +import Debug.Trace (traceEventIO)
    
    11 21
     
    
    12
    -parMapCompUnits :: (CoreCompUnit -> CoreCompUnit) -> CoreProgram -> CoreProgram
    
    13
    -parMapCompUnits f = go
    
    22
    +parMapCompUnits :: Logger -> String -> (CoreCompUnit -> CoreCompUnit) -> CoreProgram -> CoreProgram
    
    23
    +parMapCompUnits logger pass_name f units = unsafePerformIO $ do
    
    24
    +    result_vars <- mapM (uncurry fork_unit) (zip [1 :: Int ..] units)
    
    25
    +    mapM take_unit result_vars
    
    14 26
       where
    
    15
    -    go [] = []
    
    16
    -    go (unit:units) = unit' `par` (units' `pseq` (unit' : units'))
    
    17
    -      where
    
    18
    -        unit' = forceCompUnit (f unit)
    
    19
    -        units' = go units
    
    20
    -
    
    21
    -    forceCompUnit unit@(CoreCompUnit unit_binds _unit_rules) =
    
    22
    -      seqBinds unit_binds `seq` unit
    27
    +    total_units = length units
    
    28
    +    do_timings = total_units > 1 && logHasDumpFlag logger Opt_D_dump_timings
    
    29
    +
    
    30
    +    fork_unit unit_no unit = do
    
    31
    +      result_var <- newEmptyMVar
    
    32
    +      _ <- forkIO $ do
    
    33
    +        traceEventIO ("parMapCompUnits: Start(" ++ (show unit_no) ++ "): " ++ pass_name)
    
    34
    +        result <- try $
    
    35
    +          if do_timings
    
    36
    +            then withTiming logger (coreCompUnitTimingDoc pass_name unit_no total_units unit) forceCompUnit $
    
    37
    +                   evaluate $ let unit' = f unit in forceCompUnit unit' `seq` unit'
    
    38
    +            else evaluate $ let unit' = f unit in forceCompUnit unit' `seq` unit'
    
    39
    +        putMVar result_var result
    
    40
    +        traceEventIO ("parMapCompUnits: End(" ++ (show unit_no) ++ "): " ++ pass_name)
    
    41
    +      pure result_var
    
    42
    +
    
    43
    +    take_unit result_var = do
    
    44
    +      result <- takeMVar result_var
    
    45
    +      case result of
    
    46
    +        Left err -> throwIO (err :: SomeException)
    
    47
    +        Right unit -> pure unit
    
    48
    +
    
    49
    +forceCompUnit :: CoreCompUnit -> ()
    
    50
    +forceCompUnit (CoreCompUnit unit_binds unit_rules) =
    
    51
    +  seqBinds unit_binds `seq` seqRules unit_rules `seq` ()
    
    52
    +
    
    53
    +coreCompUnitTimingDoc :: String -> Int -> Int -> CoreCompUnit -> SDoc
    
    54
    +coreCompUnitTimingDoc pass_name unit_no total_units (CoreCompUnit unit_binds unit_rules) =
    
    55
    +  text pass_name
    
    56
    +    <+> parens
    
    57
    +          (text "unit"
    
    58
    +           <+> int unit_no <> char '/' <> int total_units <> comma
    
    59
    +           <+> text "binds=" <> int (length unit_binds) <> comma
    
    60
    +           <+> text "rules=" <> int (length unit_rules) <> comma
    
    61
    +           <+> text "size=" <> int (coreBindsSize unit_binds))

  • compiler/GHC/Core/Opt/CprAnal.hs
    ... ... @@ -182,7 +182,7 @@ So currently we have
    182 182
     
    
    183 183
     cprAnalProgram :: Logger -> FamInstEnvs -> CoreProgram -> IO CoreProgram
    
    184 184
     cprAnalProgram logger fam_envs comp_units = do
    
    185
    -  let binds_plus_cpr = parMapCompUnits (cprAnalCompUnit (emptyAnalEnv fam_envs)) comp_units
    
    185
    +  let binds_plus_cpr = parMapCompUnits logger "CprAnal" (cprAnalCompUnit (emptyAnalEnv fam_envs)) comp_units
    
    186 186
       putDumpFileMaybe logger Opt_D_dump_cpr_signatures "Cpr signatures" FormatText $
    
    187 187
         dumpIdInfoOfProgram False (ppr . cprSigInfo) binds_plus_cpr
    
    188 188
       -- See Note [Stamp out space leaks in demand analysis] in GHC.Core.Opt.DmdAnal
    

  • compiler/GHC/Core/Opt/DmdAnal.hs
    ... ... @@ -38,6 +38,7 @@ import GHC.Builtin.PrimOps
    38 38
     import GHC.Builtin.Types.Prim ( realWorldStatePrimTy )
    
    39 39
     
    
    40 40
     import GHC.Types.Unique.Set
    
    41
    +import GHC.Utils.Logger (Logger)
    
    41 42
     import GHC.Types.Unique.MemoFun
    
    42 43
     import GHC.Types.RepType
    
    43 44
     import GHC.Types.ForeignCall ( isSafeForeignCall )
    
    ... ... @@ -91,9 +92,9 @@ data DmdResult a b = R !a !b
    91 92
     --
    
    92 93
     -- Note: use `seqBinds` on the result to avoid leaks due to laziness (cf Note
    
    93 94
     -- [Stamp out space leaks in demand analysis])
    
    94
    -dmdAnalProgram :: DmdAnalOpts -> FamInstEnvs -> [CoreRule] -> CoreProgram -> CoreProgram
    
    95
    -dmdAnalProgram opts fam_envs rules binds
    
    96
    -  = parMapCompUnits dmd_anal_comp_unit binds
    
    95
    +dmdAnalProgram :: Logger -> DmdAnalOpts -> FamInstEnvs -> [CoreRule] -> CoreProgram -> CoreProgram
    
    96
    +dmdAnalProgram logger opts fam_envs rules binds
    
    97
    +  = parMapCompUnits logger "DmdAnal" dmd_anal_comp_unit binds
    
    97 98
       where
    
    98 99
         dmd_anal_comp_unit (CoreCompUnit unit_binds unit_rules)
    
    99 100
           = let WithDmdType _unit_ty unit_binds' = go_unit (emptyAnalEnv opts fam_envs) [] unit_binds
    

  • compiler/GHC/Core/Opt/Exitify.hs
    ... ... @@ -39,6 +39,7 @@ import GHC.Prelude
    39 39
     import GHC.Builtin.Uniques
    
    40 40
     import GHC.Core
    
    41 41
     import GHC.Core.Opt.CompUnit (parMapCompUnits)
    
    42
    +import GHC.Utils.Logger (Logger)
    
    42 43
     import GHC.Core.Utils
    
    43 44
     import GHC.Core.FVs
    
    44 45
     import GHC.Core.Type
    
    ... ... @@ -60,8 +61,8 @@ import Control.Monad
    60 61
     
    
    61 62
     -- | Traverses the AST, simply to find all joinrecs and call 'exitify' on them.
    
    62 63
     -- The really interesting function is exitifyRec
    
    63
    -exitifyProgram :: CoreProgram -> CoreProgram
    
    64
    -exitifyProgram comp_units = parMapCompUnits exitifyCompUnit comp_units
    
    64
    +exitifyProgram :: Logger -> CoreProgram -> CoreProgram
    
    65
    +exitifyProgram logger comp_units = parMapCompUnits logger "Exitify" exitifyCompUnit comp_units
    
    65 66
       where
    
    66 67
         exitifyCompUnit (CoreCompUnit binds unit_rules) =
    
    67 68
           CoreCompUnit (map goTopLvl binds) unit_rules
    

  • compiler/GHC/Core/Opt/FloatIn.hs
    ... ... @@ -35,6 +35,7 @@ import GHC.Types.Tickish
    35 35
     import GHC.Types.Var
    
    36 36
     import GHC.Types.Var.Set
    
    37 37
     
    
    38
    +import GHC.Utils.Logger (Logger)
    
    38 39
     import GHC.Utils.Misc
    
    39 40
     import GHC.Utils.Panic.Plain
    
    40 41
     
    
    ... ... @@ -47,8 +48,8 @@ Top-level interface function, @floatInwards@. Note that we do not
    47 48
     actually float any bindings downwards from the top-level.
    
    48 49
     -}
    
    49 50
     
    
    50
    -floatInwards :: Platform -> CoreProgram -> CoreProgram
    
    51
    -floatInwards platform = parMapCompUnits floatCompUnit
    
    51
    +floatInwards :: Logger -> Platform -> CoreProgram -> CoreProgram
    
    52
    +floatInwards logger platform = parMapCompUnits logger "FloatInwards" floatCompUnit
    
    52 53
       where
    
    53 54
         floatCompUnit (CoreCompUnit binds unit_rules) =
    
    54 55
           CoreCompUnit (map (fi_top_bind platform) binds) unit_rules
    

  • compiler/GHC/Core/Opt/LiberateCase.hs
    ... ... @@ -19,6 +19,7 @@ import GHC.Core.Opt.Simplify.Inline
    19 19
     import GHC.Builtin.Types ( unitDataConId )
    
    20 20
     import GHC.Types.Id
    
    21 21
     import GHC.Types.Var.Env
    
    22
    +import GHC.Utils.Logger (Logger)
    
    22 23
     import GHC.Utils.Misc    ( notNull )
    
    23 24
     
    
    24 25
     {-
    
    ... ... @@ -105,8 +106,8 @@ and the level of @h@ is zero (NB not one).
    105 106
     ************************************************************************
    
    106 107
     -}
    
    107 108
     
    
    108
    -liberateCase :: LibCaseOpts -> CoreProgram -> CoreProgram
    
    109
    -liberateCase opts = parMapCompUnits liberateCaseCompUnit
    
    109
    +liberateCase :: Logger -> LibCaseOpts -> CoreProgram -> CoreProgram
    
    110
    +liberateCase logger opts = parMapCompUnits logger "LiberateCase" liberateCaseCompUnit
    
    110 111
       where
    
    111 112
         liberateCaseCompUnit (CoreCompUnit binds unit_rules) =
    
    112 113
           CoreCompUnit (snd (do_unit (initLiberateCaseEnv opts) binds)) unit_rules
    

  • compiler/GHC/Core/Opt/Pipeline.hs
    ... ... @@ -66,6 +66,7 @@ import GHC.Types.Unique.Supply ( UniqueTag(..) )
    66 66
     
    
    67 67
     import Control.Monad
    
    68 68
     import GHC.Unit.Module
    
    69
    +import GHC.Conc (getNumCapabilities)
    
    69 70
     
    
    70 71
     {-
    
    71 72
     ************************************************************************
    
    ... ... @@ -484,7 +485,8 @@ doCorePass pass guts = do
    484 485
                                      updateBindsAndRulesM (desugarOpt dflags logger (mg_module guts))
    
    485 486
     
    
    486 487
         CoreSplit                 -> {-# SCC "CoreSplit" #-}
    
    487
    -                                 do { let split_res = map (splitCompUnit (mg_module guts) (mg_boot_exports guts) (mg_rules guts)) (mg_binds guts)
    
    488
    +                                 do { n_threads <- liftIO getNumCapabilities
    
    489
    +                                    ; let split_res = map (splitCompUnit n_threads (mg_module guts) (mg_boot_exports guts) (mg_rules guts)) (mg_binds guts)
    
    488 490
                                               binds' = concatMap fst split_res
    
    489 491
                                               rules' = mg_rules guts ++ concatMap snd split_res
    
    490 492
                                         ; return guts { mg_binds = binds', mg_rules = rules' } }
    
    ... ... @@ -537,13 +539,13 @@ doCorePass pass guts = do
    537 539
                                      liftIOWithCount $ simplifyPgm logger (hsc_unit_env hsc_env) name_ppr_ctx opts guts
    
    538 540
     
    
    539 541
         CoreCSE                   -> {-# SCC "CommonSubExpr" #-}
    
    540
    -                                 updateBinds cseProgram
    
    542
    +                                 updateBinds (cseProgram logger)
    
    541 543
     
    
    542 544
         CoreLiberateCase          -> {-# SCC "LiberateCase" #-}
    
    543
    -                                 updateBinds (liberateCase (initLiberateCaseOpts dflags))
    
    545
    +                                 updateBinds (liberateCase logger (initLiberateCaseOpts dflags))
    
    544 546
     
    
    545 547
         CoreDoFloatInwards        -> {-# SCC "FloatInwards" #-}
    
    546
    -                                 updateBinds (floatInwards platform)
    
    548
    +                                 updateBinds (floatInwards logger platform)
    
    547 549
     
    
    548 550
         CoreDoFloatOutwards f     -> {-# SCC "FloatOutwards" #-}
    
    549 551
                                      updateBindsM $ \units -> liftIO $
    
    ... ... @@ -556,10 +558,10 @@ doCorePass pass guts = do
    556 558
                                      updateBinds (doStaticArgs us)
    
    557 559
     
    
    558 560
         CoreDoCallArity           -> {-# SCC "CallArity" #-}
    
    559
    -                                 updateBinds callArityAnalProgram
    
    561
    +                                 updateBinds (callArityAnalProgram logger)
    
    560 562
     
    
    561 563
         CoreDoExitify             -> {-# SCC "Exitify" #-}
    
    562
    -                                 updateBinds exitifyProgram
    
    564
    +                                 updateBinds (exitifyProgram logger)
    
    563 565
     
    
    564 566
         CoreDoDemand before_ww    -> {-# SCC "DmdAnal" #-}
    
    565 567
                                      updateBindsM (liftIO . dmdAnal logger before_ww dflags fam_envs (mg_rules guts))
    
    ... ... @@ -632,7 +634,7 @@ dmdAnal logger before_ww dflags fam_envs rules binds = do
    632 634
                    , dmd_unbox_width     = dmdUnboxWidth dflags
    
    633 635
                    , dmd_max_worker_args = maxWorkerArgs dflags
    
    634 636
                    }
    
    635
    -      binds_plus_dmds = dmdAnalProgram opts fam_envs rules binds
    
    637
    +      binds_plus_dmds = dmdAnalProgram logger opts fam_envs rules binds
    
    636 638
       Logger.putDumpFileMaybe logger Opt_D_dump_dmd_signatures "Demand signatures" FormatText $
    
    637 639
         dumpIdInfoOfProgram (hasPprDebug dflags) (ppr . zapDmdEnvSig . dmdSigInfo) binds_plus_dmds
    
    638 640
       -- See Note [Stamp out space leaks in demand analysis] in GHC.Core.Opt.DmdAnal
    

  • compiler/GHC/Core/Opt/Simplify.hs
    ... ... @@ -13,6 +13,7 @@ import GHC.Core
    13 13
     import GHC.Core.FVs (ruleFreeVars)
    
    14 14
     import GHC.Core.Rules
    
    15 15
     import GHC.Core.Ppr     ( pprCoreBindings, pprCoreExpr )
    
    16
    +import GHC.Core.Opt.CompUnit (coreCompUnitTimingDoc, forceCompUnit)
    
    16 17
     import GHC.Core.Opt.OccurAnal ( occurAnalyseCompUnit, occurAnalyseExpr )
    
    17 18
     import GHC.Core.Stats   ( coreBindsSize, coreBindsStats, exprSize )
    
    18 19
     import GHC.Core.Utils   ( mkTicks, stripTicksTop )
    
    ... ... @@ -230,17 +231,33 @@ simplifyPgm' logger unit_env name_ppr_ctx opts
    230 231
     
    
    231 232
         zero_counts = zeroSimplCount $ logHasDumpFlag logger Opt_D_dump_simpl_stats
    
    232 233
     
    
    233
    -    run_units :: (CoreCompUnit -> IO a) -> [CoreCompUnit] -> IO [a]
    
    234
    +    run_units
    
    235
    +      :: (CoreCompUnit -> IO (CoreCompUnit, (String, Int), SimplCount))
    
    236
    +      -> [CoreCompUnit]
    
    237
    +      -> IO [(CoreCompUnit, (String, Int), SimplCount)]
    
    234 238
         run_units f units
    
    235
    -      | parallel_units = mapParallelIO f units
    
    239
    +      | parallel_units = mapParallelIO timed_f (zip [1 :: Int ..] units)
    
    236 240
           | otherwise      = mapM f units
    
    237 241
           where
    
    242
    +        total_units = length units
    
    238 243
             parallel_units = length units > 1 && not disable_parallel
    
    239 244
     
    
    240 245
             disable_parallel =
    
    241 246
                  logHasDumpFlag logger Opt_D_dump_occur_anal
    
    242 247
               || logHasDumpFlag logger Opt_D_dump_simpl_iterations
    
    243 248
     
    
    249
    +        timed_f (unit_no, unit)
    
    250
    +          | logHasDumpFlag logger Opt_D_dump_timings
    
    251
    +          = withTiming logger
    
    252
    +              (coreCompUnitTimingDoc "Simplify" unit_no total_units unit)
    
    253
    +              force_unit_result
    
    254
    +              (f unit)
    
    255
    +          | otherwise
    
    256
    +          = f unit
    
    257
    +
    
    258
    +        force_unit_result (unit', _, count) =
    
    259
    +          forceCompUnit unit' `seq` count `seq` ()
    
    260
    +
    
    244 261
         mapParallelIO :: (a -> IO b) -> [a] -> IO [b]
    
    245 262
         mapParallelIO f xs = mask $ \restore -> do
    
    246 263
           workers <- forM xs $ \x -> do
    

  • compiler/GHC/Core/Opt/Split.hs
    ... ... @@ -9,15 +9,15 @@ import GHC.Prelude hiding ( head, init, last )
    9 9
     
    
    10 10
     import GHC.Core
    
    11 11
     import GHC.Core.FVs
    
    12
    -import GHC.Core.Opt.OccurAnal (occurAnalyseCompUnit)
    
    12
    +import GHC.Core.Ppr (pprRule)
    
    13 13
     import GHC.Core.Stats (coreBindsSize)
    
    14 14
     
    
    15 15
     import GHC.Data.Graph.Directed (SCC(..), Node(..), stronglyConnCompFromEdgedVerticesUniq)
    
    16
    -import GHC.Data.Maybe (orElse)
    
    16
    +import GHC.Data.Maybe (mapMaybe, orElse)
    
    17 17
     
    
    18 18
     import GHC.Types.Unique.Set
    
    19 19
     import GHC.Types.Name (Name, isExternalName, nameModule)
    
    20
    -import GHC.Types.Name.Set (NameSet, isEmptyNameSet)
    
    20
    +import GHC.Types.Name.Set (NameSet, elemNameSet, isEmptyNameSet, mkNameSet)
    
    21 21
     import GHC.Types.Var.Set
    
    22 22
     import GHC.Types.Var.Env
    
    23 23
     import GHC.Types.Var
    
    ... ... @@ -27,6 +27,8 @@ import GHC.Utils.Panic
    27 27
     
    
    28 28
     import GHC.Unit.Module (Module)
    
    29 29
     
    
    30
    +import Data.List (foldl', sortOn)
    
    31
    +import Data.Ord (Down(..))
    
    30 32
     import qualified Data.IntMap.Strict as IntMap
    
    31 33
     import qualified Data.IntSet as IntSet
    
    32 34
     
    
    ... ... @@ -96,6 +98,57 @@ scope. So we must ensure `fa1` and fa2 end up in the same compilation unit.
    96 98
     
    
    97 99
     But for now I think I will just disable splitting if there is a boot module.
    
    98 100
     
    
    101
    +--------------------------------
    
    102
    +
    
    103
    +Another wrinkle involving rules:
    
    104
    +Consider this:
    
    105
    +
    
    106
    +module A where
    
    107
    +
    
    108
    +{-# INLINE[1] foo #-}
    
    109
    +A.f1 x = 42
    
    110
    +A.f2 = A.f1
    
    111
    +-------------------
    
    112
    +module B where
    
    113
    +
    
    114
    +{-# RULES "rule-foo-bar" forall x. A.f1 x = bar x #-}
    
    115
    +
    
    116
    +foo x = A.f2 x
    
    117
    +
    
    118
    +bar x = 16
    
    119
    +
    
    120
    +
    
    121
    +Here rule-foo-bar only mentions one local binder so naively we would assume foo and bar are independent as the rule doesn't connect any two local binders directly.
    
    122
    +However consider this sequence of events:
    
    123
    +
    
    124
    +foo x = A.f2 x
    
    125
    +
    
    126
    +=> inline f2
    
    127
    +foo x = A.f1 x
    
    128
    +
    
    129
    +=> fire rule
    
    130
    +foo x = bar x
    
    131
    +
    
    132
    +
    
    133
    +Suddenly those two binders are not so independent anymore! The main point here is we might need to follow arbitrarily
    
    134
    +deep chains of imported unfoldings to avoid this. But doing so I think is prohibitively expensive especially if we don't
    
    135
    +know if we can actually uncover much parallelism for doing so.
    
    136
    +Where does this leave us?
    
    137
    +
    
    138
    +If a rule mentions zero local binders we can ignore it.
    
    139
    +If a rule mentions a local binder on both sides it's just an edge between those two binders. (easy)
    
    140
    +If a rule mentions a local binder on the lhs we can ignore it.
    
    141
    +If a rule mentions a single local binder on the rhs then unless we do a deep traversal of imported unfoldings we have to
    
    142
    +treat any imported bindings as potentially linking back to that local binder. (hard)
    
    143
    +
    
    144
    +We could the simple thing and say such a rule just makes splitting core unviable and compile any module with such a rule as a
    
    145
    +single compilation unit. And while rules of the last kind are rare specialzation rules are a notable exception:
    
    146
    +In particular if we have: imported :: C a => a -> T2 and {-# SPECIALISE imported :: T1 -> T2 #-} GHC translates this to a
    
    147
    +RULE `imported @T1 $dCT1 = $simported`; The exact shape the last point is talking about!
    
    148
    +It's unclear how to handle this. Do the unfolding traversel only if there are such rules? Might still be quite expensive.
    
    149
    +Especially with aggressive unfolding flags it could end up inspecting every unfolding in a project! Just give up on parallelism
    
    150
    +if there are such rules? Specialization pragmas aren't that rare, so seems like a big loss. Tricky!
    
    151
    +
    
    99 152
     -}
    
    100 153
     
    
    101 154
     data DepGraphNode
    
    ... ... @@ -121,6 +174,29 @@ maybeRuleEdges this_module rule =
    121 174
       where
    
    122 175
         local_fvs = filter (varFromModule this_module) (nonDetEltsUniqSet (ruleFreeVars rule))
    
    123 176
     
    
    177
    +data UnifyingRule = UnifyingRule
    
    178
    +  { unifyingRule       :: !CoreRule
    
    179
    +  , unifyingRuleRhsFvs :: ![Var]
    
    180
    +  }
    
    181
    +
    
    182
    +findUnifyingRule :: VarSet -> NameSet -> CoreRule -> Maybe UnifyingRule
    
    183
    +findUnifyingRule local_top_bndrs local_top_names rule
    
    184
    +  | lhs_has_no_local_binder && not (null rhs_local_fvs)
    
    185
    +  = Just (UnifyingRule rule rhs_local_fvs)
    
    186
    +  | otherwise
    
    187
    +  = Nothing
    
    188
    +  where
    
    189
    +    lhs_local_fvs = ruleLhsFreeIds rule `intersectVarSet` local_top_bndrs
    
    190
    +    lhs_has_no_local_binder =
    
    191
    +      isEmptyVarSet lhs_local_fvs && not (ruleHeadIsLocal local_top_names rule)
    
    192
    +
    
    193
    +    rhs_local_fvs =
    
    194
    +      nonDetEltsUniqSet (ruleRhsFreeVars rule `intersectVarSet` local_top_bndrs)
    
    195
    +
    
    196
    +ruleHeadIsLocal :: NameSet -> CoreRule -> Bool
    
    197
    +ruleHeadIsLocal local_top_names Rule { ru_fn = fn } = fn `elemNameSet` local_top_names
    
    198
    +ruleHeadIsLocal _               BuiltinRule {}      = False
    
    199
    +
    
    124 200
     bindNode :: VarSet -> CoreBind -> ([DepGraphNode], [Edge])
    
    125 201
     bindNode local_top_bndrs bind =
    
    126 202
       case bindersOf bind of
    
    ... ... @@ -197,7 +273,7 @@ assignLocalRules unit_rules binder_components =
    197 273
               []  -> (rule_map, rule : no_comp_rules)
    
    198 274
               is  -> pprPanic "splitCompUnit"
    
    199 275
                      ( text "Rule free vars span multiple components"
    
    200
    -                $$ text "rule:" <+> ppr rule
    
    276
    +                $$ text "rule:" <+> pprRule rule
    
    201 277
                     $$ text "components:" <+> ppr is
    
    202 278
                     $$ text "rule_fvs:" <+> pprVarsWithModule (nonDetEltsUniqSet (ruleFreeVars rule))
    
    203 279
                     $$ vcat [ text "component" <+> int i <> colon <+> pprVarsWithModule hits
    
    ... ... @@ -236,20 +312,26 @@ pprVarWithModule v
    236 312
     
    
    237 313
     -- After optimizations a rule might no longer reference binders from this module.
    
    238 314
     -- In these cases we return them here and then add them to mg_rules.
    
    239
    -splitCompUnit :: Module -> NameSet -> [CoreRule] -> CoreCompUnit -> ([CoreCompUnit], [CoreRule])
    
    240
    -splitCompUnit this_module boot_exported imp_rules unit
    
    315
    +splitCompUnit :: Int -> Module -> NameSet -> [CoreRule] -> CoreCompUnit -> ([CoreCompUnit], [CoreRule])
    
    316
    +splitCompUnit n_threads this_module boot_exported _imp_rules unit
    
    317
    +  | n_threads <= 1 = single_comp_unit
    
    241 318
       | not boot_exported_is_empty = single_comp_unit
    
    319
    +  | unifying_rule : _ <- unifying_rules
    
    320
    +  = pprTrace "splitCompUnit"
    
    321
    +      ( text "Not splitting build unit due to unifying rule"
    
    322
    +     $$ text "rule:" <+> pprRule (unifyingRule unifying_rule)
    
    323
    +     $$ text "local rhs binders:" <+> pprVarsWithModule (unifyingRuleRhsFvs unifying_rule) )
    
    324
    +      single_comp_unit
    
    242 325
       | otherwise
    
    243
    -  = let comp_units = map mk_comp_unit components_with_rules
    
    326
    +  = let comp_units = combineCompUnits max_units (map mk_comp_unit components_with_rules)
    
    244 327
             result = (comp_units, rules_for_imps ++ rules_without_component)
    
    245 328
         in -- pprTrace "CoreSplitTrace" (pprSplitTrace comp_units) $
    
    246 329
            checkNameClashes comp_units `seq`
    
    247 330
            result
    
    248 331
       where
    
    249
    -    CoreCompUnit occ_binds unit_rules =
    
    250
    -      occurAnalyseCompUnit this_module (const True) (const True) imp_rules unit
    
    332
    +    CoreCompUnit unit_binds unit_rules = unit
    
    251 333
     
    
    252
    -    top_level_bndrs = bindersOfBinds occ_binds
    
    334
    +    top_level_bndrs = bindersOfBinds unit_binds
    
    253 335
         checked_bndrs =
    
    254 336
           assertPpr (all isLocalVar top_level_bndrs)
    
    255 337
             ( text "splitCompUnit: non-local top-level binder(s)"
    
    ... ... @@ -257,8 +339,9 @@ splitCompUnit this_module boot_exported imp_rules unit
    257 339
           top_level_bndrs
    
    258 340
     
    
    259 341
         local_top_bndrs = mkVarSet checked_bndrs
    
    342
    +    local_top_names = mkNameSet (map varName checked_bndrs)
    
    260 343
     
    
    261
    -    bind_node_info = checked_bndrs `seq` map (bindNode local_top_bndrs) occ_binds
    
    344
    +    bind_node_info = checked_bndrs `seq` map (bindNode local_top_bndrs) unit_binds
    
    262 345
         bind_nodes = concatMap fst bind_node_info
    
    263 346
         bind_edges = concatMap snd bind_node_info
    
    264 347
     
    
    ... ... @@ -266,6 +349,8 @@ splitCompUnit this_module boot_exported imp_rules unit
    266 349
         rule_edges = concat [ es | (_, Just es) <- rule_edge_pairs ]
    
    267 350
         rules_for_imps = [ r | (r, Nothing) <- rule_edge_pairs ]
    
    268 351
         unit_rules_local = [ r | (r, Just _) <- rule_edge_pairs ]
    
    352
    +    unifying_rules =
    
    353
    +      mapMaybe (findUnifyingRule local_top_bndrs local_top_names) unit_rules
    
    269 354
     
    
    270 355
         all_edges = bind_edges ++ rule_edges
    
    271 356
         binder_components = splitCoreBinders bind_nodes all_edges
    
    ... ... @@ -275,9 +360,60 @@ splitCompUnit this_module boot_exported imp_rules unit
    275 360
         mk_comp_unit (_, binds, rules) = CoreCompUnit binds rules
    
    276 361
     
    
    277 362
         boot_exported_is_empty = isEmptyNameSet boot_exported
    
    363
    +    max_units = n_threads + 1
    
    278 364
     
    
    279 365
         single_comp_unit = ([unit], [])
    
    280 366
     
    
    367
    +combineCompUnits :: Int -> [CoreCompUnit] -> [CoreCompUnit]
    
    368
    +combineCompUnits max_units units
    
    369
    +  | length units <= max_units = units
    
    370
    +  | otherwise = map finishBucket (IntMap.elems final_buckets)
    
    371
    +  where
    
    372
    +    initial_buckets =
    
    373
    +      IntMap.fromDistinctAscList
    
    374
    +        [ (i, Bucket 0 [] [])
    
    375
    +        | i <- [0 .. max_units - 1]
    
    376
    +        ]
    
    377
    +
    
    378
    +    final_buckets =
    
    379
    +      foldl' assignUnit initial_buckets sorted_units
    
    380
    +
    
    381
    +    sorted_units =
    
    382
    +      sortOn (Down . unitSize . snd) (zip [0 :: Int ..] units)
    
    383
    +
    
    384
    +    assignUnit buckets (_, unit) =
    
    385
    +      IntMap.adjust (addUnit unit) target_bucket buckets
    
    386
    +      where
    
    387
    +        target_bucket = smallestBucket buckets
    
    388
    +
    
    389
    +    smallestBucket buckets =
    
    390
    +      case IntMap.toAscList buckets of
    
    391
    +        [] -> panic "combineCompUnits.smallestBucket: empty bucket map"
    
    392
    +        b : bs -> fst (foldl' choose_bucket b bs)
    
    393
    +
    
    394
    +    choose_bucket best@(i1, b1) candidate@(i2, b2)
    
    395
    +      | (bucketSize b2, i2) < (bucketSize b1, i1) = candidate
    
    396
    +      | otherwise                                  = best
    
    397
    +
    
    398
    +    finishBucket (Bucket _ binds_acc rules_acc) =
    
    399
    +      CoreCompUnit (reverse binds_acc) (reverse rules_acc)
    
    400
    +
    
    401
    +    unitSize = coreBindsSize . coreCompUnitBinds
    
    402
    +
    
    403
    +data Bucket = Bucket
    
    404
    +  { bucketSize  :: !Int
    
    405
    +  , bucketBinds :: [CoreBind]
    
    406
    +  , bucketRules :: [CoreRule]
    
    407
    +  }
    
    408
    +
    
    409
    +addUnit :: CoreCompUnit -> Bucket -> Bucket
    
    410
    +addUnit (CoreCompUnit binds rules) (Bucket sz binds_acc rules_acc) =
    
    411
    +  Bucket
    
    412
    +    { bucketSize = sz + coreBindsSize binds
    
    413
    +    , bucketBinds = foldl' (flip (:)) binds_acc binds
    
    414
    +    , bucketRules = foldl' (flip (:)) rules_acc rules
    
    415
    +    }
    
    416
    +
    
    281 417
     checkNameClashes :: [CoreCompUnit] -> ()
    
    282 418
     checkNameClashes comp_units
    
    283 419
       | null dup_bndrs = ()