sheaf pushed to branch wip/andreask/ticked_joins at Glasgow Haskell Compiler / GHC
Commits:
-
443c2b50
by sheaf at 2026-01-22T17:49:54+01:00
6 changed files:
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Opt/OccurAnal.hs
- compiler/GHC/Core/Opt/Simplify/Env.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/SimpleOpt.hs
- compiler/GHC/Types/Basic.hs
Changes:
| ... | ... | @@ -672,7 +672,7 @@ lintRhs :: Id -> CoreExpr -> LintM (OutType, UsageEnv) |
| 672 | 672 | lintRhs bndr rhs
|
| 673 | 673 | | JoinPoint arity <- idJoinPointHood bndr
|
| 674 | 674 | = lintJoinLams arity (Just bndr) rhs
|
| 675 | - | AlwaysTailCalled arity _ <- tailCallInfo (idOccInfo bndr)
|
|
| 675 | + | AlwaysTailCalled { tailCallArity = arity } <- tailCallInfo (idOccInfo bndr)
|
|
| 676 | 676 | = lintJoinLams arity Nothing rhs
|
| 677 | 677 | |
| 678 | 678 | -- Allow applications of the data constructor @StaticPtr@ at the top
|
| ... | ... | @@ -797,10 +797,10 @@ function call and a jump by looking at the occurrence (because the same pass |
| 797 | 797 | changes the 'IdDetails' and propagates the binders to their occurrence sites).
|
| 798 | 798 | |
| 799 | 799 | To track potential join points, we use the 'occ_tail' field of OccInfo. A value
|
| 800 | -of `AlwaysTailCalled n` indicates that every occurrence of the variable is a
|
|
| 801 | -tail call with `n` arguments (counting both value and type arguments). Otherwise
|
|
| 802 | -'occ_tail' will be 'NoTailCallInfo'. The tail call info flows bottom-up with the
|
|
| 803 | -rest of 'OccInfo' until it goes on the binder.
|
|
| 800 | +of `AlwaysTailCalled { tailCallArity = n }` indicates that every occurrence of
|
|
| 801 | +the variable is a tail call with `n` arguments (counting both value and type
|
|
| 802 | +arguments). Otherwise 'occ_tail' will be 'NoTailCallInfo'. The tail call info
|
|
| 803 | +flows bottom-up with the rest of 'OccInfo' until it goes on the binder.
|
|
| 804 | 804 | |
| 805 | 805 | Note [Join arity prediction based on joinRhsArity]
|
| 806 | 806 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -2588,9 +2588,9 @@ occAnal env (Tick tickish body) |
| 2588 | 2588 | WUD usage body' = occAnal env' body
|
| 2589 | 2589 | |
| 2590 | 2590 | env' = case tickish of
|
| 2591 | - -- Set that we are inside a profiling tick
|
|
| 2592 | - -- SLD TODO: explain why we need this info
|
|
| 2593 | - ProfNote {} -> setInProfTick env
|
|
| 2591 | + -- setInsideProfTick: join points under profiling ticks turn
|
|
| 2592 | + -- into quasi-join points. See Note [Quasi join points]
|
|
| 2593 | + ProfNote {} -> setInsideProfTick env
|
|
| 2594 | 2594 | _ -> env
|
| 2595 | 2595 | |
| 2596 | 2596 | usage'
|
| ... | ... | @@ -2621,11 +2621,12 @@ occAnal env (Tick tickish body) |
| 2621 | 2621 | -- See #14242.
|
| 2622 | 2622 | |
| 2623 | 2623 | occAnal env (Cast expr co)
|
| 2624 | - = let (WUD usage expr') = occAnal env expr
|
|
| 2625 | - usage1 = addManyOccs usage (coVarsOfCo co)
|
|
| 2626 | - -- usage2: see Note [Gather occurrences of coercion variables]
|
|
| 2627 | - usage2 = markAllNonTail usage1
|
|
| 2628 | - -- usage3: calls inside expr aren't tail calls any more
|
|
| 2624 | + = let (WUD usage expr') = occAnal (setInsideCast env) expr
|
|
| 2625 | + -- setInsideCasts: join points inside casts turn into quasi-join-points
|
|
| 2626 | + -- See Note [Quasi join points]
|
|
| 2627 | + usage1 = addManyOccs usage (coVarsOfCo co)
|
|
| 2628 | + -- usage2: see Note [Gather occurrences of coercion variables]
|
|
| 2629 | + usage2 = markAllNonTail usage1
|
|
| 2629 | 2630 | in WUD usage2 (Cast expr' co)
|
| 2630 | 2631 | |
| 2631 | 2632 | occAnal env app@(App _ _)
|
| ... | ... | @@ -2942,7 +2943,8 @@ scrutinised y). |
| 2942 | 2943 | |
| 2943 | 2944 | data OccEnv
|
| 2944 | 2945 | = OccEnv { occ_encl :: !OccEncl -- Enclosing context information
|
| 2945 | - , occ_prof_ticks :: !Int
|
|
| 2946 | + , occ_prof_ticks :: !Int -- ^ How many profiling ticks are we under? See Note [Quasi join points]
|
|
| 2947 | + , occ_casts :: !Int -- ^ How many casts are we under? See Note [Quasi join points]
|
|
| 2946 | 2948 | , occ_one_shots :: !OneShots -- See Note [OneShots]
|
| 2947 | 2949 | , occ_unf_act :: Id -> Bool -- Which Id unfoldings are active
|
| 2948 | 2950 | , occ_rule_act :: ActivationGhc -> Bool -- Which rules are active
|
| ... | ... | @@ -3009,6 +3011,7 @@ initOccEnv :: OccEnv |
| 3009 | 3011 | initOccEnv
|
| 3010 | 3012 | = OccEnv { occ_encl = OccVanilla
|
| 3011 | 3013 | , occ_prof_ticks = 0
|
| 3014 | + , occ_casts = 0
|
|
| 3012 | 3015 | , occ_one_shots = []
|
| 3013 | 3016 | |
| 3014 | 3017 | -- To be conservative, we say that all
|
| ... | ... | @@ -3087,8 +3090,11 @@ setTailCtxt !env = env { occ_encl = OccVanilla } |
| 3087 | 3090 | -- Preserve occ_one_shots, occ_join points
|
| 3088 | 3091 | -- Do not use OccRhs for the RHS of a join point (which is a tail ctxt):
|
| 3089 | 3092 | |
| 3090 | -setInProfTick :: OccEnv -> OccEnv
|
|
| 3091 | -setInProfTick !env = env { occ_prof_ticks = 1 + occ_prof_ticks env }
|
|
| 3093 | +setInsideProfTick :: OccEnv -> OccEnv
|
|
| 3094 | +setInsideProfTick !env = env { occ_prof_ticks = 1 + occ_prof_ticks env }
|
|
| 3095 | + |
|
| 3096 | +setInsideCast :: OccEnv -> OccEnv
|
|
| 3097 | +setInsideCast !env = env { occ_casts = 1 + occ_casts env }
|
|
| 3092 | 3098 | |
| 3093 | 3099 | mkRhsOccEnv :: OccEnv -> RecFlag -> OccEncl -> JoinPointHood -> Id -> CoreExpr -> OccEnv
|
| 3094 | 3100 | -- See Note [The OccEnv for a right hand side]
|
| ... | ... | @@ -3736,7 +3742,7 @@ type OccInfoEnv = IdEnv LocalOcc -- A finite map from an expression's |
| 3736 | 3742 | data LocalOcc -- See Note [LocalOcc]
|
| 3737 | 3743 | = OneOccL { lo_n_br :: {-# UNPACK #-} !BranchCount -- Number of syntactic occurrences
|
| 3738 | 3744 | , lo_tail :: !TailCallInfo
|
| 3739 | - -- Combining (AlwaysTailCalled 2) and (AlwaysTailCalled 3)
|
|
| 3745 | + -- NB: combining 'TailCallInfo's with different arities
|
|
| 3740 | 3746 | -- gives NoTailCallInfo
|
| 3741 | 3747 | , lo_int_cxt :: !InterestingCxt }
|
| 3742 | 3748 | |
| ... | ... | @@ -3829,9 +3835,20 @@ mkOneOcc !env id int_cxt arity |
| 3829 | 3835 | = mkSimpleDetails (unitVarEnv id occ)
|
| 3830 | 3836 | |
| 3831 | 3837 | where
|
| 3832 | - occ = OneOccL { lo_n_br = 1
|
|
| 3833 | - , lo_int_cxt = int_cxt
|
|
| 3834 | - , lo_tail = AlwaysTailCalled arity (occ_prof_ticks env) }
|
|
| 3838 | + occ =
|
|
| 3839 | + OneOccL
|
|
| 3840 | + { lo_n_br = 1
|
|
| 3841 | + , lo_int_cxt = int_cxt
|
|
| 3842 | + , lo_tail =
|
|
| 3843 | + AlwaysTailCalled
|
|
| 3844 | + { tailCallArity = arity
|
|
| 3845 | + |
|
| 3846 | + -- See Note [Quasi join points] for justification of these
|
|
| 3847 | + -- two fields.
|
|
| 3848 | + , tailCallUnderProfTicks = occ_prof_ticks env
|
|
| 3849 | + , tailCallUnderCasts = occ_casts env
|
|
| 3850 | + }
|
|
| 3851 | + }
|
|
| 3835 | 3852 | |
| 3836 | 3853 | -- Add several occurrences, assumed not to be tail calls
|
| 3837 | 3854 | add_many_occ :: Var -> OccInfoEnv -> OccInfoEnv
|
| ... | ... | @@ -4040,7 +4057,7 @@ tagNonRecBinder :: TopLevelFlag -- At top level? |
| 4040 | 4057 | -- Precondition: OccInfo is not IAmDead
|
| 4041 | 4058 | tagNonRecBinder lvl occ bndr
|
| 4042 | 4059 | | okForJoinPoint lvl bndr tail_call_info
|
| 4043 | - , AlwaysTailCalled ar _ <- tail_call_info
|
|
| 4060 | + , AlwaysTailCalled { tailCallArity = ar } <- tail_call_info
|
|
| 4044 | 4061 | = (setBinderOcc occ bndr, JoinPoint ar)
|
| 4045 | 4062 | | otherwise
|
| 4046 | 4063 | = (setBinderOcc zapped_occ bndr, NotJoinPoint)
|
| ... | ... | @@ -4127,7 +4144,7 @@ okForJoinPoint lvl bndr tail_call_info |
| 4127 | 4144 | = False
|
| 4128 | 4145 | where
|
| 4129 | 4146 | valid_join | NotTopLevel <- lvl
|
| 4130 | - , AlwaysTailCalled arity _ <- tail_call_info
|
|
| 4147 | + , AlwaysTailCalled { tailCallArity = arity } <- tail_call_info
|
|
| 4131 | 4148 | |
| 4132 | 4149 | , -- Invariant 1 as applied to LHSes of rules
|
| 4133 | 4150 | all (ok_rule arity) (idCoreRules bndr)
|
| ... | ... | @@ -4144,9 +4161,9 @@ okForJoinPoint lvl bndr tail_call_info |
| 4144 | 4161 | |
| 4145 | 4162 | lost_join | JoinPoint ja <- idJoinPointHood bndr
|
| 4146 | 4163 | = not valid_join ||
|
| 4147 | - (case tail_call_info of -- Valid join but arity differs
|
|
| 4148 | - AlwaysTailCalled ja' _ -> ja /= ja'
|
|
| 4149 | - _ -> False)
|
|
| 4164 | + (case tail_call_info of -- Valid join but arity differs
|
|
| 4165 | + AlwaysTailCalled { tailCallArity = ja' } -> ja /= ja'
|
|
| 4166 | + _ -> False)
|
|
| 4150 | 4167 | | otherwise = False
|
| 4151 | 4168 | |
| 4152 | 4169 | ok_rule _ BuiltinRule{} = False -- only possible with plugin shenanigans
|
| ... | ... | @@ -4168,7 +4185,7 @@ okForJoinPoint lvl bndr tail_call_info |
| 4168 | 4185 | , text "tc:" <+> ppr tail_call_info
|
| 4169 | 4186 | , text "rules:" <+> ppr (idCoreRules bndr)
|
| 4170 | 4187 | , case tail_call_info of
|
| 4171 | - AlwaysTailCalled arity _ ->
|
|
| 4188 | + AlwaysTailCalled { tailCallArity = arity } ->
|
|
| 4172 | 4189 | vcat [ text "ok_unf:" <+> ppr (ok_unfolding arity (realIdUnfolding bndr))
|
| 4173 | 4190 | , text "ok_type:" <+> ppr (isValidJoinPointType arity (idType bndr)) ]
|
| 4174 | 4191 | _ -> empty ]
|
| ... | ... | @@ -4231,6 +4248,6 @@ orLocalOcc (OneOccL { lo_n_br = nbr1, lo_int_cxt = int_cxt1, lo_tail = tci1 }) |
| 4231 | 4248 | orLocalOcc occ1 occ2 = andLocalOcc occ1 occ2
|
| 4232 | 4249 | |
| 4233 | 4250 | andTailCallInfo :: TailCallInfo -> TailCallInfo -> TailCallInfo
|
| 4234 | -andTailCallInfo (AlwaysTailCalled arity1 p1) (AlwaysTailCalled arity2 p2)
|
|
| 4235 | - | arity1 == arity2 = AlwaysTailCalled arity1 (max p1 p2)
|
|
| 4251 | +andTailCallInfo (AlwaysTailCalled arity1 p1 c1) (AlwaysTailCalled arity2 p2 c2)
|
|
| 4252 | + | arity1 == arity2 = AlwaysTailCalled arity1 (max p1 p2) (max c1 c2)
|
|
| 4236 | 4253 | andTailCallInfo _ _ = NoTailCallInfo |
| ... | ... | @@ -201,7 +201,8 @@ data SimplEnv |
| 201 | 201 | |
| 202 | 202 | , seCaseDepth :: !Int -- Depth of multi-branch case alternatives
|
| 203 | 203 | |
| 204 | - , seProfTicks :: !Int -- SLD TODO
|
|
| 204 | + , seProfTicks :: !Int -- Current depth of profiling ticks; see Note [Quasi join points]
|
|
| 205 | + , seCasts :: !Int -- Current depth of casts; see Note [Quasi join points]
|
|
| 205 | 206 | |
| 206 | 207 | , seInlineDepth :: !Int -- 0 initially, 1 when we inline an already-simplified
|
| 207 | 208 | -- unfolding, and simplify again; and so on
|
| ... | ... | @@ -591,6 +592,7 @@ mkSimplEnv mode fam_envs |
| 591 | 592 | , seRecIds = emptyUnVarSet
|
| 592 | 593 | , seCaseDepth = 0
|
| 593 | 594 | , seProfTicks = 0
|
| 595 | + , seCasts = 0
|
|
| 594 | 596 | , seInlineDepth = 0 }
|
| 595 | 597 | -- The top level "enclosing CC" is "SUBSUMED".
|
| 596 | 598 |
| ... | ... | @@ -61,7 +61,7 @@ import GHC.Types.Var ( isTyCoVar ) |
| 61 | 61 | import GHC.Builtin.Types.Prim( realWorldStatePrimTy )
|
| 62 | 62 | import GHC.Builtin.Names( runRWKey, seqHashKey )
|
| 63 | 63 | |
| 64 | -import GHC.Data.Maybe ( isNothing, orElse, fromMaybe, mapMaybe )
|
|
| 64 | +import GHC.Data.Maybe ( isNothing, orElse, mapMaybe )
|
|
| 65 | 65 | import GHC.Data.FastString
|
| 66 | 66 | import GHC.Unit.Module ( moduleName )
|
| 67 | 67 | import GHC.Utils.Outputable
|
| ... | ... | @@ -1684,39 +1684,54 @@ optOutCoercion env co already_optimised |
| 1684 | 1684 | empty_subst = mkEmptySubst (seInScope env)
|
| 1685 | 1685 | opts = seOptCoercionOpts env
|
| 1686 | 1686 | |
| 1687 | +-- | Number of casts we are adding around an expression as we process a 'Cast'.
|
|
| 1688 | +--
|
|
| 1689 | +-- We need the cast depth to implement the logic of Note [Quasi join points].
|
|
| 1690 | +type NbCastsAdded = Int
|
|
| 1691 | + |
|
| 1687 | 1692 | simplCast :: SimplEnv -> InExpr -> InCoercion -> SimplCont
|
| 1688 | 1693 | -> SimplM (SimplFloats, OutExpr)
|
| 1689 | 1694 | simplCast env body co0 cont0
|
| 1690 | 1695 | = do { co1 <- {-#SCC "simplCast-simplCoercion" #-} simplCoercion env co0
|
| 1691 | - ; cont1 <- {-#SCC "simplCast-addCoerce" #-}
|
|
| 1692 | - if isReflCo co1
|
|
| 1693 | - then return cont0 -- See Note [Optimising reflexivity]
|
|
| 1694 | - else addCoerce co1 True cont0
|
|
| 1695 | - -- True <=> co1 is optimised
|
|
| 1696 | - ; {-#SCC "simplCast-simplExprF" #-} simplExprF env body cont1 }
|
|
| 1696 | + ; (cont1, nbAddedCasts) <- {-#SCC "simplCast-addCoerce" #-}
|
|
| 1697 | + if isReflCo co1
|
|
| 1698 | + then return (cont0, 0) -- See Note [Optimising reflexivity]
|
|
| 1699 | + else addCoerce co1 True cont0
|
|
| 1700 | + -- True <=> co1 is optimised
|
|
| 1701 | + |
|
| 1702 | + -- Keep track of how many casts we have added, because we need this
|
|
| 1703 | + -- information for Note [Quasi join points].
|
|
| 1704 | + ; let env' = env { seCasts = seCasts env + nbAddedCasts }
|
|
| 1705 | + ; {-#SCC "simplCast-simplExprF" #-} simplExprF env' body cont1 }
|
|
| 1697 | 1706 | where
|
| 1698 | 1707 | |
| 1699 | 1708 | -- If the first parameter is MRefl, then simplifying revealed a
|
| 1700 | 1709 | -- reflexive coercion. Omit.
|
| 1701 | - addCoerceM :: MOutCoercion -> Bool -> SimplCont -> SimplM SimplCont
|
|
| 1702 | - addCoerceM MRefl _ cont = return cont
|
|
| 1710 | + addCoerceM :: MOutCoercion -> Bool -> SimplCont -> SimplM (SimplCont, NbCastsAdded)
|
|
| 1711 | + addCoerceM MRefl _ cont = return (cont, 0)
|
|
| 1703 | 1712 | addCoerceM (MCo co) opt cont = addCoerce co opt cont
|
| 1704 | 1713 | |
| 1705 | - addCoerce :: OutCoercion -> Bool -> SimplCont -> SimplM SimplCont
|
|
| 1714 | + addCoerce :: OutCoercion -> Bool -> SimplCont -> SimplM (SimplCont, NbCastsAdded)
|
|
| 1706 | 1715 | addCoerce co1 _ (CastIt { sc_co = co2, sc_cont = cont }) -- See Note [Optimising reflexivity]
|
| 1707 | - = addCoerce (mkTransCo co1 co2) False cont
|
|
| 1708 | - -- False: (mkTransCo co1 co2) is not fully optimised
|
|
| 1709 | - -- See Note [Avoid re-simplifying coercions]
|
|
| 1716 | + = do { (cont', nbCastsAdded) <- addCoerce (mkTransCo co1 co2) False cont
|
|
| 1717 | + -- False: (mkTransCo co1 co2) is not fully optimised
|
|
| 1718 | + -- See Note [Avoid re-simplifying coercions]
|
|
| 1719 | + ; return (cont', nbCastsAdded - 1)
|
|
| 1720 | + -- -1: the coercion coalesced with an existing coercion.
|
|
| 1721 | + }
|
|
| 1710 | 1722 | |
| 1711 | 1723 | addCoerce co co_is_opt (ApplyToTy { sc_arg_ty = arg_ty, sc_cont = tail })
|
| 1712 | 1724 | | Just (arg_ty', m_co') <- pushCoTyArg co arg_ty
|
| 1713 | 1725 | = {-#SCC "addCoerce-pushCoTyArg" #-}
|
| 1714 | - do { tail' <- addCoerceM m_co' co_is_opt tail
|
|
| 1715 | - ; return (ApplyToTy { sc_arg_ty = arg_ty'
|
|
| 1716 | - , sc_cont = tail'
|
|
| 1717 | - , sc_hole_ty = coercionLKind co }) }
|
|
| 1718 | - -- NB! As the cast goes past, the
|
|
| 1719 | - -- type of the hole changes (#16312)
|
|
| 1726 | + do { (tail', nbCastsAdded) <- addCoerceM m_co' co_is_opt tail
|
|
| 1727 | + ; return ( ApplyToTy { sc_arg_ty = arg_ty'
|
|
| 1728 | + , sc_cont = tail'
|
|
| 1729 | + , sc_hole_ty = coercionLKind co }
|
|
| 1730 | + -- NB! As the cast goes past, the
|
|
| 1731 | + -- type of the hole changes (#16312)
|
|
| 1732 | + , nbCastsAdded )
|
|
| 1733 | + }
|
|
| 1734 | + |
|
| 1720 | 1735 | -- (f |> co) e ===> (f (e |> co1)) |> co2
|
| 1721 | 1736 | -- where co :: (s1->s2) ~ (t1->t2)
|
| 1722 | 1737 | -- co1 :: t1 ~ s1
|
| ... | ... | @@ -1729,10 +1744,12 @@ simplCast env body co0 cont0 |
| 1729 | 1744 | |
| 1730 | 1745 | | Just (m_co1, m_co2) <- pushCoValArg co
|
| 1731 | 1746 | = {-#SCC "addCoerce-pushCoValArg" #-}
|
| 1732 | - do { tail' <- addCoerceM m_co2 co_is_opt tail
|
|
| 1747 | + do { (tail', nbCastsAdded) <- addCoerceM m_co2 co_is_opt tail
|
|
| 1733 | 1748 | ; case m_co1 of {
|
| 1734 | - MRefl -> return (cont { sc_cont = tail'
|
|
| 1735 | - , sc_hole_ty = coercionLKind co }) ;
|
|
| 1749 | + MRefl -> return
|
|
| 1750 | + ( cont { sc_cont = tail'
|
|
| 1751 | + , sc_hole_ty = coercionLKind co }
|
|
| 1752 | + , nbCastsAdded ) ;
|
|
| 1736 | 1753 | -- See Note [Avoiding simplifying repeatedly]
|
| 1737 | 1754 | |
| 1738 | 1755 | MCo co1 ->
|
| ... | ... | @@ -1742,17 +1759,23 @@ simplCast env body co0 cont0 |
| 1742 | 1759 | -- to make it all consistent. It's a bit messy.
|
| 1743 | 1760 | -- But it isn't a common case.
|
| 1744 | 1761 | -- Example of use: #995
|
| 1745 | - ; return (ApplyToVal { sc_arg = mkCast arg' co1
|
|
| 1746 | - , sc_env = arg_se'
|
|
| 1747 | - , sc_dup = dup'
|
|
| 1748 | - , sc_cont = tail'
|
|
| 1749 | - , sc_hole_ty = coercionLKind co }) } } }
|
|
| 1762 | + ; return
|
|
| 1763 | + ( ApplyToVal { sc_arg = mkCast arg' co1
|
|
| 1764 | + , sc_env = arg_se'
|
|
| 1765 | + , sc_dup = dup'
|
|
| 1766 | + , sc_cont = tail'
|
|
| 1767 | + , sc_hole_ty = coercionLKind co }
|
|
| 1768 | + , nbCastsAdded ) } } }
|
|
| 1750 | 1769 | |
| 1751 | 1770 | addCoerce co co_is_opt cont
|
| 1752 | - | isReflCo co = return cont -- Having this at the end makes a huge
|
|
| 1753 | - -- difference in T12227, for some reason
|
|
| 1754 | - -- See Note [Optimising reflexivity]
|
|
| 1755 | - | otherwise = return (CastIt { sc_co = co, sc_opt = co_is_opt, sc_cont = cont })
|
|
| 1771 | + | isReflCo co = return (cont, 0 :: NbCastsAdded )
|
|
| 1772 | + -- Having this at the end makes a huge
|
|
| 1773 | + -- difference in T12227, for some reason
|
|
| 1774 | + -- See Note [Optimising reflexivity]
|
|
| 1775 | + | otherwise =
|
|
| 1776 | + return
|
|
| 1777 | + ( CastIt { sc_co = co, sc_opt = co_is_opt, sc_cont = cont }
|
|
| 1778 | + , 1 :: NbCastsAdded )
|
|
| 1756 | 1779 | |
| 1757 | 1780 | simplLazyArg :: SimplEnvIS -- ^ Used only for its InScopeSet
|
| 1758 | 1781 | -> DupFlag
|
| ... | ... | @@ -2067,12 +2090,10 @@ simplNonRecJoinPoint env bndr rhs body cont |
| 2067 | 2090 | ; (floats2, body') <- simplExprF env3 body cont
|
| 2068 | 2091 | ; return (floats1 `addFloats` floats2, body') }
|
| 2069 | 2092 | where
|
| 2070 | - do_case_case
|
|
| 2071 | - | Just occMaxProfTicks <- occursUnderProfTick (idOccInfo bndr)
|
|
| 2072 | - , occMaxProfTicks > seProfTicks env
|
|
| 2073 | - = False
|
|
| 2074 | - | otherwise
|
|
| 2075 | - = seCaseCase env
|
|
| 2093 | + do_case_case =
|
|
| 2094 | + if isTrueJoinPoint env bndr
|
|
| 2095 | + then seCaseCase env
|
|
| 2096 | + else False
|
|
| 2076 | 2097 | |
| 2077 | 2098 | simplRecJoinPoint :: SimplEnv -> [(InId, InExpr)]
|
| 2078 | 2099 | -> InExpr -> SimplCont
|
| ... | ... | @@ -2089,11 +2110,26 @@ simplRecJoinPoint env pairs body cont |
| 2089 | 2110 | ; (floats2, body') <- simplExprF env2 body cont
|
| 2090 | 2111 | ; return (floats1 `addFloats` floats2, body') }
|
| 2091 | 2112 | where
|
| 2092 | - do_case_case
|
|
| 2093 | - | any ((seProfTicks env <) . fromMaybe 0 . occursUnderProfTick . idOccInfo . fst) pairs
|
|
| 2094 | - = False
|
|
| 2095 | - | otherwise
|
|
| 2096 | - = seCaseCase env
|
|
| 2113 | + do_case_case =
|
|
| 2114 | + if all (isTrueJoinPoint env . fst) pairs
|
|
| 2115 | + then seCaseCase env
|
|
| 2116 | + else False
|
|
| 2117 | + |
|
| 2118 | +-- | Is this a true join point, or only a quasi join point?
|
|
| 2119 | +--
|
|
| 2120 | +-- See Note [Quasi join points]
|
|
| 2121 | +isTrueJoinPoint :: SimplEnv -> InId -> Bool
|
|
| 2122 | +isTrueJoinPoint env id
|
|
| 2123 | + | Just occMaxProfTicks <- occursUnderProfTicks (idOccInfo id)
|
|
| 2124 | + , occMaxProfTicks > seProfTicks env
|
|
| 2125 | + -- The join point occurs under more profiling ticks that its binding.
|
|
| 2126 | + = False
|
|
| 2127 | + | Just occMaxCasts <- occursUnderCasts (idOccInfo id)
|
|
| 2128 | + , occMaxCasts > seCasts env
|
|
| 2129 | + -- The join point occurs under more casts than its binding.
|
|
| 2130 | + = False
|
|
| 2131 | + | otherwise
|
|
| 2132 | + = True
|
|
| 2097 | 2133 | |
| 2098 | 2134 | --------------------
|
| 2099 | 2135 | wrapJoinCont :: Bool
|
| ... | ... | @@ -2217,6 +2253,100 @@ inwards altogether at any join point. Instead simplify the (join ... in ...) |
| 2217 | 2253 | with a Stop continuation, and wrap the original continuation around the
|
| 2218 | 2254 | outside. Surprisingly tricky!
|
| 2219 | 2255 | |
| 2256 | +Note [Quasi join points]
|
|
| 2257 | +~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 2258 | +We currently classify join points into two separate categories
|
|
| 2259 | + |
|
| 2260 | + - true join points
|
|
| 2261 | + - quasi join points
|
|
| 2262 | + |
|
| 2263 | +Definition:
|
|
| 2264 | + A join point binding defines a *quasi* join point if any of the join point
|
|
| 2265 | + binders occur under profiling ticks or casts.
|
|
| 2266 | + |
|
| 2267 | + If a join point binding is not a quasi join point, it is a *true* join point.
|
|
| 2268 | + |
|
| 2269 | +For true join points, we can push a continuation into a join point, as described
|
|
| 2270 | +in Note [Join points and case-of-case]:
|
|
| 2271 | + |
|
| 2272 | + K[ join j = rhs in body ] --> join j = K[ rhs ] in K[ body ]
|
|
| 2273 | + |
|
| 2274 | +This transformation is not valid if the occurrences of 'j' in 'body' appear:
|
|
| 2275 | + |
|
| 2276 | + 1. under casts, see #26422
|
|
| 2277 | + 2. under profiling ticks, see #26693 #26157 #26642
|
|
| 2278 | + |
|
| 2279 | +For example, consider (a minimisation of) the program in #26693:
|
|
| 2280 | + |
|
| 2281 | + join { j :: Bool -> IO (); j _ = guts }
|
|
| 2282 | + in case pass of
|
|
| 2283 | + False -> scctick<foo> jump j True
|
|
| 2284 | + True -> scctick<bar> jump j False
|
|
| 2285 | + |
|
| 2286 | +Let's try to push an application to an argument 'arg' into this expression.
|
|
| 2287 | +As per Note [Join points and case-of-case], we proceed by first applying the
|
|
| 2288 | +argument to both the join point RHS and the case alternatives:
|
|
| 2289 | + |
|
| 2290 | + join { j :: Bool -> IO (); j _ = guts arg ] }
|
|
| 2291 | + in case pass of
|
|
| 2292 | + False -> (scctick<foo> jump j True ) arg
|
|
| 2293 | + True -> (scctick<bar> jump j False) arg
|
|
| 2294 | + |
|
| 2295 | +Then we rely on 'trimJoinCont' to remove the argument, but this fails because
|
|
| 2296 | +there are intervening profiling ticks. Even if we addressed that issue, it
|
|
| 2297 | +remains unclear what to do without misattributing costs.
|
|
| 2298 | +We could transform to the following:
|
|
| 2299 | + |
|
| 2300 | + join { j :: Bool -> IO (); j scc _ = (setSCC# scc guts) arg ] }
|
|
| 2301 | + in case pass of
|
|
| 2302 | + False -> jump j <foo> True
|
|
| 2303 | + True -> jump j <bar> False
|
|
| 2304 | + |
|
| 2305 | +where `setSCC#` is a new primop that would set the current cost centre point.
|
|
| 2306 | +This doesn't exist yet, so for now we just disallow the case-of-case
|
|
| 2307 | +transformation for 'j'.
|
|
| 2308 | + |
|
| 2309 | +Similarly for casts:
|
|
| 2310 | + |
|
| 2311 | + join { j = blah }
|
|
| 2312 | + in case e of
|
|
| 2313 | + False -> j True |> co1
|
|
| 2314 | + True -> j False |> co2
|
|
| 2315 | + |
|
| 2316 | +if we want to apply this to an argument 'arg', we would need to perform the
|
|
| 2317 | +following transformation:
|
|
| 2318 | + |
|
| 2319 | + join { j co = ( blah |> co ) arg }
|
|
| 2320 | + in case e of
|
|
| 2321 | + False -> j co1 True
|
|
| 2322 | + True -> j co2 False
|
|
| 2323 | + |
|
| 2324 | +in which we add a coercion argument to the join point. Again, this is not a
|
|
| 2325 | +transformation we currently implement, so we instead prevent case-of-case for
|
|
| 2326 | +such join points.
|
|
| 2327 | + |
|
| 2328 | +To achieve this classification, we proceed as follows:
|
|
| 2329 | + |
|
| 2330 | + 1. In occurrence analysis, compute how many profiling ticks/casts each
|
|
| 2331 | + join point Id occurs under.
|
|
| 2332 | + |
|
| 2333 | + This is stored in the 'tailCallUnderProfTicks' and 'tailCallUnderCasts'
|
|
| 2334 | + fields of 'TailCallInfo', and populated by keeping track of how many
|
|
| 2335 | + profiling ticks and casts we are under when doing occurrence analysis
|
|
| 2336 | + (see 'occ_prof_ticks' and 'occ_casts').
|
|
| 2337 | + |
|
| 2338 | + 2. In the simplifier, we keep track of how many profiling ticks/casts we are
|
|
| 2339 | + currently inside. See 'seProfTicks' and 'seCasts', which are updated
|
|
| 2340 | + in 'simplTick' and 'simplCast', respectively.
|
|
| 2341 | + |
|
| 2342 | + 3. In the simplifier, when we come across a join point (in either
|
|
| 2343 | + 'simplNonRecJoinPoint' or 'simplRecJoinPoint'), we compare the current
|
|
| 2344 | + cast depth/profiling tick depth with the cast depth/profiling tick depth
|
|
| 2345 | + of the occurrences.
|
|
| 2346 | + |
|
| 2347 | + If the join point occurs under more profiling ticks/casts than it is bound,
|
|
| 2348 | + then it is a quasi join point and we switch off the case-of-case
|
|
| 2349 | + transformation.
|
|
| 2220 | 2350 | |
| 2221 | 2351 | ************************************************************************
|
| 2222 | 2352 | * *
|
| ... | ... | @@ -1076,7 +1076,7 @@ joinPointBinding_maybe bndr rhs |
| 1076 | 1076 | | isJoinId bndr
|
| 1077 | 1077 | = Just (bndr, rhs)
|
| 1078 | 1078 | |
| 1079 | - | AlwaysTailCalled join_arity _ <- tailCallInfo (idOccInfo bndr)
|
|
| 1079 | + | AlwaysTailCalled { tailCallArity = join_arity } <- tailCallInfo (idOccInfo bndr)
|
|
| 1080 | 1080 | , (bndrs, body) <- etaExpandToJoinPoint join_arity rhs
|
| 1081 | 1081 | , let str_sig = idDmdSig bndr
|
| 1082 | 1082 | str_arity = count isId bndrs -- Strictness demands are for Ids only
|
| ... | ... | @@ -70,7 +70,7 @@ module GHC.Types.Basic ( |
| 70 | 70 | BranchCount, oneBranch,
|
| 71 | 71 | InterestingCxt(..),
|
| 72 | 72 | TailCallInfo(..), tailCallInfo, zapOccTailCallInfo,
|
| 73 | - isAlwaysTailCalled, occursUnderProfTick,
|
|
| 73 | + isAlwaysTailCalled, occursUnderProfTicks, occursUnderCasts,
|
|
| 74 | 74 | |
| 75 | 75 | EP(..),
|
| 76 | 76 | |
| ... | ... | @@ -1149,8 +1149,14 @@ instance Monoid InsideLam where |
| 1149 | 1149 | mappend = (Semi.<>)
|
| 1150 | 1150 | |
| 1151 | 1151 | -----------------
|
| 1152 | + |
|
| 1153 | +-- | See Note [TailCallInfo]
|
|
| 1152 | 1154 | data TailCallInfo
|
| 1153 | - = AlwaysTailCalled {-# UNPACK #-} !JoinArity !Int-- See Note [TailCallInfo]
|
|
| 1155 | + = AlwaysTailCalled
|
|
| 1156 | + { tailCallArity :: {-# UNPACK #-} !JoinArity
|
|
| 1157 | + , tailCallUnderProfTicks :: !Int -- See Note [Quasi join points]
|
|
| 1158 | + , tailCallUnderCasts :: !Int -- See Note [Quasi join points]
|
|
| 1159 | + }
|
|
| 1154 | 1160 | | NoTailCallInfo
|
| 1155 | 1161 | deriving (Eq)
|
| 1156 | 1162 | |
| ... | ... | @@ -1167,15 +1173,26 @@ isAlwaysTailCalled occ |
| 1167 | 1173 | = case tailCallInfo occ of AlwaysTailCalled{} -> True
|
| 1168 | 1174 | NoTailCallInfo -> False
|
| 1169 | 1175 | |
| 1170 | -occursUnderProfTick :: OccInfo -> Maybe Int
|
|
| 1171 | -occursUnderProfTick occ =
|
|
| 1176 | +-- | If this 'Id' is always tail called, how many profiling ticks does
|
|
| 1177 | +-- it occur under? See Note [Quasi join points].
|
|
| 1178 | +occursUnderProfTicks :: OccInfo -> Maybe Int
|
|
| 1179 | +occursUnderProfTicks occ =
|
|
| 1172 | 1180 | case tailCallInfo occ of
|
| 1173 | - AlwaysTailCalled _ b -> Just b
|
|
| 1181 | + AlwaysTailCalled { tailCallUnderProfTicks = nb } -> Just nb
|
|
| 1182 | + NoTailCallInfo -> Nothing
|
|
| 1183 | + |
|
| 1184 | +-- | If this 'Id' is always tail called, how many casts does
|
|
| 1185 | +-- it occur under? See Note [Quasi join points].
|
|
| 1186 | +occursUnderCasts :: OccInfo -> Maybe Int
|
|
| 1187 | +occursUnderCasts occ =
|
|
| 1188 | + case tailCallInfo occ of
|
|
| 1189 | + AlwaysTailCalled { tailCallUnderCasts = nb } -> Just nb
|
|
| 1174 | 1190 | NoTailCallInfo -> Nothing
|
| 1175 | 1191 | |
| 1176 | 1192 | instance Outputable TailCallInfo where
|
| 1177 | - ppr (AlwaysTailCalled ar b) = sep [ text "Tail", brackets (int b), int ar ]
|
|
| 1178 | - ppr _ = text "NoTailCallInfo" --empty
|
|
| 1193 | + ppr (AlwaysTailCalled ar p c) =
|
|
| 1194 | + sep [ text "Tail", brackets (int p <> comma <> int c), int ar ]
|
|
| 1195 | + ppr NoTailCallInfo = text "NoTailCallInfo"
|
|
| 1179 | 1196 | |
| 1180 | 1197 | -----------------
|
| 1181 | 1198 | strongLoopBreaker, weakLoopBreaker :: OccInfo
|
| ... | ... | @@ -1223,8 +1240,10 @@ instance Outputable OccInfo where |
| 1223 | 1240 | pp_tail = pprShortTailCallInfo tail_info
|
| 1224 | 1241 | |
| 1225 | 1242 | pprShortTailCallInfo :: TailCallInfo -> SDoc
|
| 1226 | -pprShortTailCallInfo (AlwaysTailCalled ar p)
|
|
| 1227 | - = char 'T' <> (brackets (text "P" <+> int p)) <> brackets (int ar)
|
|
| 1243 | +pprShortTailCallInfo (AlwaysTailCalled ar p c)
|
|
| 1244 | + = char 'T' <> (brackets (text "P" <+> int p))
|
|
| 1245 | + <> (brackets (text "C" <+> int c))
|
|
| 1246 | + <> brackets (int ar)
|
|
| 1228 | 1247 | pprShortTailCallInfo NoTailCallInfo = empty
|
| 1229 | 1248 | |
| 1230 | 1249 | {-
|
| ... | ... | @@ -1258,6 +1277,9 @@ point can also be invoked from other join points, not just from case branches: |
| 1258 | 1277 | Here both 'j1' and 'j2' will get marked AlwaysTailCalled, but j1 will get
|
| 1259 | 1278 | ManyOccs and j2 will get `OneOcc { occ_n_br = 2 }`.
|
| 1260 | 1279 | |
| 1280 | +We also store how many profiling ticks and casts the join point occurs under.
|
|
| 1281 | +The rationale is described in Note [Quasi join points].
|
|
| 1282 | + |
|
| 1261 | 1283 | ************************************************************************
|
| 1262 | 1284 | * *
|
| 1263 | 1285 | Default method specification
|