[Git][ghc/ghc][wip/andreask/ticked_joins] Use the same logic for casts, see Note [Quasi join points]
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 Use the same logic for casts, see Note [Quasi join points] - - - - - 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: ===================================== compiler/GHC/Core/Lint.hs ===================================== @@ -672,7 +672,7 @@ lintRhs :: Id -> CoreExpr -> LintM (OutType, UsageEnv) lintRhs bndr rhs | JoinPoint arity <- idJoinPointHood bndr = lintJoinLams arity (Just bndr) rhs - | AlwaysTailCalled arity _ <- tailCallInfo (idOccInfo bndr) + | AlwaysTailCalled { tailCallArity = arity } <- tailCallInfo (idOccInfo bndr) = lintJoinLams arity Nothing rhs -- Allow applications of the data constructor @StaticPtr@ at the top ===================================== compiler/GHC/Core/Opt/OccurAnal.hs ===================================== @@ -797,10 +797,10 @@ function call and a jump by looking at the occurrence (because the same pass changes the 'IdDetails' and propagates the binders to their occurrence sites). To track potential join points, we use the 'occ_tail' field of OccInfo. A value -of `AlwaysTailCalled n` indicates that every occurrence of the variable is a -tail call with `n` arguments (counting both value and type arguments). Otherwise -'occ_tail' will be 'NoTailCallInfo'. The tail call info flows bottom-up with the -rest of 'OccInfo' until it goes on the binder. +of `AlwaysTailCalled { tailCallArity = n }` indicates that every occurrence of +the variable is a tail call with `n` arguments (counting both value and type +arguments). Otherwise 'occ_tail' will be 'NoTailCallInfo'. The tail call info +flows bottom-up with the rest of 'OccInfo' until it goes on the binder. Note [Join arity prediction based on joinRhsArity] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2588,9 +2588,9 @@ occAnal env (Tick tickish body) WUD usage body' = occAnal env' body env' = case tickish of - -- Set that we are inside a profiling tick - -- SLD TODO: explain why we need this info - ProfNote {} -> setInProfTick env + -- setInsideProfTick: join points under profiling ticks turn + -- into quasi-join points. See Note [Quasi join points] + ProfNote {} -> setInsideProfTick env _ -> env usage' @@ -2621,11 +2621,12 @@ occAnal env (Tick tickish body) -- See #14242. occAnal env (Cast expr co) - = let (WUD usage expr') = occAnal env expr - usage1 = addManyOccs usage (coVarsOfCo co) - -- usage2: see Note [Gather occurrences of coercion variables] - usage2 = markAllNonTail usage1 - -- usage3: calls inside expr aren't tail calls any more + = let (WUD usage expr') = occAnal (setInsideCast env) expr + -- setInsideCasts: join points inside casts turn into quasi-join-points + -- See Note [Quasi join points] + usage1 = addManyOccs usage (coVarsOfCo co) + -- usage2: see Note [Gather occurrences of coercion variables] + usage2 = markAllNonTail usage1 in WUD usage2 (Cast expr' co) occAnal env app@(App _ _) @@ -2942,7 +2943,8 @@ scrutinised y). data OccEnv = OccEnv { occ_encl :: !OccEncl -- Enclosing context information - , occ_prof_ticks :: !Int + , occ_prof_ticks :: !Int -- ^ How many profiling ticks are we under? See Note [Quasi join points] + , occ_casts :: !Int -- ^ How many casts are we under? See Note [Quasi join points] , occ_one_shots :: !OneShots -- See Note [OneShots] , occ_unf_act :: Id -> Bool -- Which Id unfoldings are active , occ_rule_act :: ActivationGhc -> Bool -- Which rules are active @@ -3009,6 +3011,7 @@ initOccEnv :: OccEnv initOccEnv = OccEnv { occ_encl = OccVanilla , occ_prof_ticks = 0 + , occ_casts = 0 , occ_one_shots = [] -- To be conservative, we say that all @@ -3087,8 +3090,11 @@ setTailCtxt !env = env { occ_encl = OccVanilla } -- Preserve occ_one_shots, occ_join points -- Do not use OccRhs for the RHS of a join point (which is a tail ctxt): -setInProfTick :: OccEnv -> OccEnv -setInProfTick !env = env { occ_prof_ticks = 1 + occ_prof_ticks env } +setInsideProfTick :: OccEnv -> OccEnv +setInsideProfTick !env = env { occ_prof_ticks = 1 + occ_prof_ticks env } + +setInsideCast :: OccEnv -> OccEnv +setInsideCast !env = env { occ_casts = 1 + occ_casts env } mkRhsOccEnv :: OccEnv -> RecFlag -> OccEncl -> JoinPointHood -> Id -> CoreExpr -> OccEnv -- See Note [The OccEnv for a right hand side] @@ -3736,7 +3742,7 @@ type OccInfoEnv = IdEnv LocalOcc -- A finite map from an expression's data LocalOcc -- See Note [LocalOcc] = OneOccL { lo_n_br :: {-# UNPACK #-} !BranchCount -- Number of syntactic occurrences , lo_tail :: !TailCallInfo - -- Combining (AlwaysTailCalled 2) and (AlwaysTailCalled 3) + -- NB: combining 'TailCallInfo's with different arities -- gives NoTailCallInfo , lo_int_cxt :: !InterestingCxt } @@ -3829,9 +3835,20 @@ mkOneOcc !env id int_cxt arity = mkSimpleDetails (unitVarEnv id occ) where - occ = OneOccL { lo_n_br = 1 - , lo_int_cxt = int_cxt - , lo_tail = AlwaysTailCalled arity (occ_prof_ticks env) } + occ = + OneOccL + { lo_n_br = 1 + , lo_int_cxt = int_cxt + , lo_tail = + AlwaysTailCalled + { tailCallArity = arity + + -- See Note [Quasi join points] for justification of these + -- two fields. + , tailCallUnderProfTicks = occ_prof_ticks env + , tailCallUnderCasts = occ_casts env + } + } -- Add several occurrences, assumed not to be tail calls add_many_occ :: Var -> OccInfoEnv -> OccInfoEnv @@ -4040,7 +4057,7 @@ tagNonRecBinder :: TopLevelFlag -- At top level? -- Precondition: OccInfo is not IAmDead tagNonRecBinder lvl occ bndr | okForJoinPoint lvl bndr tail_call_info - , AlwaysTailCalled ar _ <- tail_call_info + , AlwaysTailCalled { tailCallArity = ar } <- tail_call_info = (setBinderOcc occ bndr, JoinPoint ar) | otherwise = (setBinderOcc zapped_occ bndr, NotJoinPoint) @@ -4127,7 +4144,7 @@ okForJoinPoint lvl bndr tail_call_info = False where valid_join | NotTopLevel <- lvl - , AlwaysTailCalled arity _ <- tail_call_info + , AlwaysTailCalled { tailCallArity = arity } <- tail_call_info , -- Invariant 1 as applied to LHSes of rules all (ok_rule arity) (idCoreRules bndr) @@ -4144,9 +4161,9 @@ okForJoinPoint lvl bndr tail_call_info lost_join | JoinPoint ja <- idJoinPointHood bndr = not valid_join || - (case tail_call_info of -- Valid join but arity differs - AlwaysTailCalled ja' _ -> ja /= ja' - _ -> False) + (case tail_call_info of -- Valid join but arity differs + AlwaysTailCalled { tailCallArity = ja' } -> ja /= ja' + _ -> False) | otherwise = False ok_rule _ BuiltinRule{} = False -- only possible with plugin shenanigans @@ -4168,7 +4185,7 @@ okForJoinPoint lvl bndr tail_call_info , text "tc:" <+> ppr tail_call_info , text "rules:" <+> ppr (idCoreRules bndr) , case tail_call_info of - AlwaysTailCalled arity _ -> + AlwaysTailCalled { tailCallArity = arity } -> vcat [ text "ok_unf:" <+> ppr (ok_unfolding arity (realIdUnfolding bndr)) , text "ok_type:" <+> ppr (isValidJoinPointType arity (idType bndr)) ] _ -> empty ] @@ -4231,6 +4248,6 @@ orLocalOcc (OneOccL { lo_n_br = nbr1, lo_int_cxt = int_cxt1, lo_tail = tci1 }) orLocalOcc occ1 occ2 = andLocalOcc occ1 occ2 andTailCallInfo :: TailCallInfo -> TailCallInfo -> TailCallInfo -andTailCallInfo (AlwaysTailCalled arity1 p1) (AlwaysTailCalled arity2 p2) - | arity1 == arity2 = AlwaysTailCalled arity1 (max p1 p2) +andTailCallInfo (AlwaysTailCalled arity1 p1 c1) (AlwaysTailCalled arity2 p2 c2) + | arity1 == arity2 = AlwaysTailCalled arity1 (max p1 p2) (max c1 c2) andTailCallInfo _ _ = NoTailCallInfo ===================================== compiler/GHC/Core/Opt/Simplify/Env.hs ===================================== @@ -201,7 +201,8 @@ data SimplEnv , seCaseDepth :: !Int -- Depth of multi-branch case alternatives - , seProfTicks :: !Int -- SLD TODO + , seProfTicks :: !Int -- Current depth of profiling ticks; see Note [Quasi join points] + , seCasts :: !Int -- Current depth of casts; see Note [Quasi join points] , seInlineDepth :: !Int -- 0 initially, 1 when we inline an already-simplified -- unfolding, and simplify again; and so on @@ -591,6 +592,7 @@ mkSimplEnv mode fam_envs , seRecIds = emptyUnVarSet , seCaseDepth = 0 , seProfTicks = 0 + , seCasts = 0 , seInlineDepth = 0 } -- The top level "enclosing CC" is "SUBSUMED". ===================================== compiler/GHC/Core/Opt/Simplify/Iteration.hs ===================================== @@ -61,7 +61,7 @@ import GHC.Types.Var ( isTyCoVar ) import GHC.Builtin.Types.Prim( realWorldStatePrimTy ) import GHC.Builtin.Names( runRWKey, seqHashKey ) -import GHC.Data.Maybe ( isNothing, orElse, fromMaybe, mapMaybe ) +import GHC.Data.Maybe ( isNothing, orElse, mapMaybe ) import GHC.Data.FastString import GHC.Unit.Module ( moduleName ) import GHC.Utils.Outputable @@ -1684,39 +1684,54 @@ optOutCoercion env co already_optimised empty_subst = mkEmptySubst (seInScope env) opts = seOptCoercionOpts env +-- | Number of casts we are adding around an expression as we process a 'Cast'. +-- +-- We need the cast depth to implement the logic of Note [Quasi join points]. +type NbCastsAdded = Int + simplCast :: SimplEnv -> InExpr -> InCoercion -> SimplCont -> SimplM (SimplFloats, OutExpr) simplCast env body co0 cont0 = do { co1 <- {-#SCC "simplCast-simplCoercion" #-} simplCoercion env co0 - ; cont1 <- {-#SCC "simplCast-addCoerce" #-} - if isReflCo co1 - then return cont0 -- See Note [Optimising reflexivity] - else addCoerce co1 True cont0 - -- True <=> co1 is optimised - ; {-#SCC "simplCast-simplExprF" #-} simplExprF env body cont1 } + ; (cont1, nbAddedCasts) <- {-#SCC "simplCast-addCoerce" #-} + if isReflCo co1 + then return (cont0, 0) -- See Note [Optimising reflexivity] + else addCoerce co1 True cont0 + -- True <=> co1 is optimised + + -- Keep track of how many casts we have added, because we need this + -- information for Note [Quasi join points]. + ; let env' = env { seCasts = seCasts env + nbAddedCasts } + ; {-#SCC "simplCast-simplExprF" #-} simplExprF env' body cont1 } where -- If the first parameter is MRefl, then simplifying revealed a -- reflexive coercion. Omit. - addCoerceM :: MOutCoercion -> Bool -> SimplCont -> SimplM SimplCont - addCoerceM MRefl _ cont = return cont + addCoerceM :: MOutCoercion -> Bool -> SimplCont -> SimplM (SimplCont, NbCastsAdded) + addCoerceM MRefl _ cont = return (cont, 0) addCoerceM (MCo co) opt cont = addCoerce co opt cont - addCoerce :: OutCoercion -> Bool -> SimplCont -> SimplM SimplCont + addCoerce :: OutCoercion -> Bool -> SimplCont -> SimplM (SimplCont, NbCastsAdded) addCoerce co1 _ (CastIt { sc_co = co2, sc_cont = cont }) -- See Note [Optimising reflexivity] - = addCoerce (mkTransCo co1 co2) False cont - -- False: (mkTransCo co1 co2) is not fully optimised - -- See Note [Avoid re-simplifying coercions] + = do { (cont', nbCastsAdded) <- addCoerce (mkTransCo co1 co2) False cont + -- False: (mkTransCo co1 co2) is not fully optimised + -- See Note [Avoid re-simplifying coercions] + ; return (cont', nbCastsAdded - 1) + -- -1: the coercion coalesced with an existing coercion. + } addCoerce co co_is_opt (ApplyToTy { sc_arg_ty = arg_ty, sc_cont = tail }) | Just (arg_ty', m_co') <- pushCoTyArg co arg_ty = {-#SCC "addCoerce-pushCoTyArg" #-} - do { tail' <- addCoerceM m_co' co_is_opt tail - ; return (ApplyToTy { sc_arg_ty = arg_ty' - , sc_cont = tail' - , sc_hole_ty = coercionLKind co }) } - -- NB! As the cast goes past, the - -- type of the hole changes (#16312) + do { (tail', nbCastsAdded) <- addCoerceM m_co' co_is_opt tail + ; return ( ApplyToTy { sc_arg_ty = arg_ty' + , sc_cont = tail' + , sc_hole_ty = coercionLKind co } + -- NB! As the cast goes past, the + -- type of the hole changes (#16312) + , nbCastsAdded ) + } + -- (f |> co) e ===> (f (e |> co1)) |> co2 -- where co :: (s1->s2) ~ (t1->t2) -- co1 :: t1 ~ s1 @@ -1729,10 +1744,12 @@ simplCast env body co0 cont0 | Just (m_co1, m_co2) <- pushCoValArg co = {-#SCC "addCoerce-pushCoValArg" #-} - do { tail' <- addCoerceM m_co2 co_is_opt tail + do { (tail', nbCastsAdded) <- addCoerceM m_co2 co_is_opt tail ; case m_co1 of { - MRefl -> return (cont { sc_cont = tail' - , sc_hole_ty = coercionLKind co }) ; + MRefl -> return + ( cont { sc_cont = tail' + , sc_hole_ty = coercionLKind co } + , nbCastsAdded ) ; -- See Note [Avoiding simplifying repeatedly] MCo co1 -> @@ -1742,17 +1759,23 @@ simplCast env body co0 cont0 -- to make it all consistent. It's a bit messy. -- But it isn't a common case. -- Example of use: #995 - ; return (ApplyToVal { sc_arg = mkCast arg' co1 - , sc_env = arg_se' - , sc_dup = dup' - , sc_cont = tail' - , sc_hole_ty = coercionLKind co }) } } } + ; return + ( ApplyToVal { sc_arg = mkCast arg' co1 + , sc_env = arg_se' + , sc_dup = dup' + , sc_cont = tail' + , sc_hole_ty = coercionLKind co } + , nbCastsAdded ) } } } addCoerce co co_is_opt cont - | isReflCo co = return cont -- Having this at the end makes a huge - -- difference in T12227, for some reason - -- See Note [Optimising reflexivity] - | otherwise = return (CastIt { sc_co = co, sc_opt = co_is_opt, sc_cont = cont }) + | isReflCo co = return (cont, 0 :: NbCastsAdded ) + -- Having this at the end makes a huge + -- difference in T12227, for some reason + -- See Note [Optimising reflexivity] + | otherwise = + return + ( CastIt { sc_co = co, sc_opt = co_is_opt, sc_cont = cont } + , 1 :: NbCastsAdded ) simplLazyArg :: SimplEnvIS -- ^ Used only for its InScopeSet -> DupFlag @@ -2067,12 +2090,10 @@ simplNonRecJoinPoint env bndr rhs body cont ; (floats2, body') <- simplExprF env3 body cont ; return (floats1 `addFloats` floats2, body') } where - do_case_case - | Just occMaxProfTicks <- occursUnderProfTick (idOccInfo bndr) - , occMaxProfTicks > seProfTicks env - = False - | otherwise - = seCaseCase env + do_case_case = + if isTrueJoinPoint env bndr + then seCaseCase env + else False simplRecJoinPoint :: SimplEnv -> [(InId, InExpr)] -> InExpr -> SimplCont @@ -2089,11 +2110,26 @@ simplRecJoinPoint env pairs body cont ; (floats2, body') <- simplExprF env2 body cont ; return (floats1 `addFloats` floats2, body') } where - do_case_case - | any ((seProfTicks env <) . fromMaybe 0 . occursUnderProfTick . idOccInfo . fst) pairs - = False - | otherwise - = seCaseCase env + do_case_case = + if all (isTrueJoinPoint env . fst) pairs + then seCaseCase env + else False + +-- | Is this a true join point, or only a quasi join point? +-- +-- See Note [Quasi join points] +isTrueJoinPoint :: SimplEnv -> InId -> Bool +isTrueJoinPoint env id + | Just occMaxProfTicks <- occursUnderProfTicks (idOccInfo id) + , occMaxProfTicks > seProfTicks env + -- The join point occurs under more profiling ticks that its binding. + = False + | Just occMaxCasts <- occursUnderCasts (idOccInfo id) + , occMaxCasts > seCasts env + -- The join point occurs under more casts than its binding. + = False + | otherwise + = True -------------------- wrapJoinCont :: Bool @@ -2217,6 +2253,100 @@ inwards altogether at any join point. Instead simplify the (join ... in ...) with a Stop continuation, and wrap the original continuation around the outside. Surprisingly tricky! +Note [Quasi join points] +~~~~~~~~~~~~~~~~~~~~~~~~ +We currently classify join points into two separate categories + + - true join points + - quasi join points + +Definition: + A join point binding defines a *quasi* join point if any of the join point + binders occur under profiling ticks or casts. + + If a join point binding is not a quasi join point, it is a *true* join point. + +For true join points, we can push a continuation into a join point, as described +in Note [Join points and case-of-case]: + + K[ join j = rhs in body ] --> join j = K[ rhs ] in K[ body ] + +This transformation is not valid if the occurrences of 'j' in 'body' appear: + + 1. under casts, see #26422 + 2. under profiling ticks, see #26693 #26157 #26642 + +For example, consider (a minimisation of) the program in #26693: + + join { j :: Bool -> IO (); j _ = guts } + in case pass of + False -> scctick<foo> jump j True + True -> scctick<bar> jump j False + +Let's try to push an application to an argument 'arg' into this expression. +As per Note [Join points and case-of-case], we proceed by first applying the +argument to both the join point RHS and the case alternatives: + + join { j :: Bool -> IO (); j _ = guts arg ] } + in case pass of + False -> (scctick<foo> jump j True ) arg + True -> (scctick<bar> jump j False) arg + +Then we rely on 'trimJoinCont' to remove the argument, but this fails because +there are intervening profiling ticks. Even if we addressed that issue, it +remains unclear what to do without misattributing costs. +We could transform to the following: + + join { j :: Bool -> IO (); j scc _ = (setSCC# scc guts) arg ] } + in case pass of + False -> jump j <foo> True + True -> jump j <bar> False + +where `setSCC#` is a new primop that would set the current cost centre point. +This doesn't exist yet, so for now we just disallow the case-of-case +transformation for 'j'. + +Similarly for casts: + + join { j = blah } + in case e of + False -> j True |> co1 + True -> j False |> co2 + +if we want to apply this to an argument 'arg', we would need to perform the +following transformation: + + join { j co = ( blah |> co ) arg } + in case e of + False -> j co1 True + True -> j co2 False + +in which we add a coercion argument to the join point. Again, this is not a +transformation we currently implement, so we instead prevent case-of-case for +such join points. + +To achieve this classification, we proceed as follows: + + 1. In occurrence analysis, compute how many profiling ticks/casts each + join point Id occurs under. + + This is stored in the 'tailCallUnderProfTicks' and 'tailCallUnderCasts' + fields of 'TailCallInfo', and populated by keeping track of how many + profiling ticks and casts we are under when doing occurrence analysis + (see 'occ_prof_ticks' and 'occ_casts'). + + 2. In the simplifier, we keep track of how many profiling ticks/casts we are + currently inside. See 'seProfTicks' and 'seCasts', which are updated + in 'simplTick' and 'simplCast', respectively. + + 3. In the simplifier, when we come across a join point (in either + 'simplNonRecJoinPoint' or 'simplRecJoinPoint'), we compare the current + cast depth/profiling tick depth with the cast depth/profiling tick depth + of the occurrences. + + If the join point occurs under more profiling ticks/casts than it is bound, + then it is a quasi join point and we switch off the case-of-case + transformation. ************************************************************************ * * ===================================== compiler/GHC/Core/SimpleOpt.hs ===================================== @@ -1076,7 +1076,7 @@ joinPointBinding_maybe bndr rhs | isJoinId bndr = Just (bndr, rhs) - | AlwaysTailCalled join_arity _ <- tailCallInfo (idOccInfo bndr) + | AlwaysTailCalled { tailCallArity = join_arity } <- tailCallInfo (idOccInfo bndr) , (bndrs, body) <- etaExpandToJoinPoint join_arity rhs , let str_sig = idDmdSig bndr str_arity = count isId bndrs -- Strictness demands are for Ids only ===================================== compiler/GHC/Types/Basic.hs ===================================== @@ -70,7 +70,7 @@ module GHC.Types.Basic ( BranchCount, oneBranch, InterestingCxt(..), TailCallInfo(..), tailCallInfo, zapOccTailCallInfo, - isAlwaysTailCalled, occursUnderProfTick, + isAlwaysTailCalled, occursUnderProfTicks, occursUnderCasts, EP(..), @@ -1149,8 +1149,14 @@ instance Monoid InsideLam where mappend = (Semi.<>) ----------------- + +-- | See Note [TailCallInfo] data TailCallInfo - = AlwaysTailCalled {-# UNPACK #-} !JoinArity !Int-- See Note [TailCallInfo] + = AlwaysTailCalled + { tailCallArity :: {-# UNPACK #-} !JoinArity + , tailCallUnderProfTicks :: !Int -- See Note [Quasi join points] + , tailCallUnderCasts :: !Int -- See Note [Quasi join points] + } | NoTailCallInfo deriving (Eq) @@ -1167,15 +1173,26 @@ isAlwaysTailCalled occ = case tailCallInfo occ of AlwaysTailCalled{} -> True NoTailCallInfo -> False -occursUnderProfTick :: OccInfo -> Maybe Int -occursUnderProfTick occ = +-- | If this 'Id' is always tail called, how many profiling ticks does +-- it occur under? See Note [Quasi join points]. +occursUnderProfTicks :: OccInfo -> Maybe Int +occursUnderProfTicks occ = case tailCallInfo occ of - AlwaysTailCalled _ b -> Just b + AlwaysTailCalled { tailCallUnderProfTicks = nb } -> Just nb + NoTailCallInfo -> Nothing + +-- | If this 'Id' is always tail called, how many casts does +-- it occur under? See Note [Quasi join points]. +occursUnderCasts :: OccInfo -> Maybe Int +occursUnderCasts occ = + case tailCallInfo occ of + AlwaysTailCalled { tailCallUnderCasts = nb } -> Just nb NoTailCallInfo -> Nothing instance Outputable TailCallInfo where - ppr (AlwaysTailCalled ar b) = sep [ text "Tail", brackets (int b), int ar ] - ppr _ = text "NoTailCallInfo" --empty + ppr (AlwaysTailCalled ar p c) = + sep [ text "Tail", brackets (int p <> comma <> int c), int ar ] + ppr NoTailCallInfo = text "NoTailCallInfo" ----------------- strongLoopBreaker, weakLoopBreaker :: OccInfo @@ -1223,8 +1240,10 @@ instance Outputable OccInfo where pp_tail = pprShortTailCallInfo tail_info pprShortTailCallInfo :: TailCallInfo -> SDoc -pprShortTailCallInfo (AlwaysTailCalled ar p) - = char 'T' <> (brackets (text "P" <+> int p)) <> brackets (int ar) +pprShortTailCallInfo (AlwaysTailCalled ar p c) + = char 'T' <> (brackets (text "P" <+> int p)) + <> (brackets (text "C" <+> int c)) + <> brackets (int ar) pprShortTailCallInfo NoTailCallInfo = empty {- @@ -1258,6 +1277,9 @@ point can also be invoked from other join points, not just from case branches: Here both 'j1' and 'j2' will get marked AlwaysTailCalled, but j1 will get ManyOccs and j2 will get `OneOcc { occ_n_br = 2 }`. +We also store how many profiling ticks and casts the join point occurs under. +The rationale is described in Note [Quasi join points]. + ************************************************************************ * * Default method specification View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/443c2b505ef239e9d73f497775855200... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/443c2b505ef239e9d73f497775855200... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
sheaf (@sheaf)