sheaf pushed to branch wip/debug-join-point at Glasgow Haskell Compiler / GHC
Commits:
-
14de70ed
by sheaf at 2026-02-03T15:21:02+01:00
3 changed files:
- compiler/GHC/Core/Opt/Simplify/Env.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/SimpleOpt.hs
Changes:
| ... | ... | @@ -56,6 +56,7 @@ import GHC.Prelude |
| 56 | 56 | |
| 57 | 57 | import GHC.Core.Coercion.Opt ( OptCoercionOpts )
|
| 58 | 58 | import GHC.Core.FamInstEnv ( FamInstEnv )
|
| 59 | +import GHC.Core.SimpleOpt ( isJoinPointBinding )
|
|
| 59 | 60 | import GHC.Core.Opt.Arity ( ArityOpts(..) )
|
| 60 | 61 | import GHC.Core.Opt.Simplify.Monad
|
| 61 | 62 | import GHC.Core.Rules.Config ( RuleOpts(..) )
|
| ... | ... | @@ -854,7 +855,7 @@ isEmptyJoinFloats = isNilOL |
| 854 | 855 | |
| 855 | 856 | unitLetFloat :: OutBind -> LetFloats
|
| 856 | 857 | -- This key function constructs a singleton float with the right form
|
| 857 | -unitLetFloat bind = assert (all (not . isJoinId) (bindersOf bind)) $
|
|
| 858 | +unitLetFloat bind = assert (all (not . isJoinPointBinding) (bindersOf bind)) $
|
|
| 858 | 859 | LetFloats (unitOL bind) (flag bind)
|
| 859 | 860 | where
|
| 860 | 861 | flag (Rec {}) = FltLifted
|
| ... | ... | @@ -867,7 +868,7 @@ unitLetFloat bind = assert (all (not . isJoinId) (bindersOf bind)) $ |
| 867 | 868 | | otherwise = FltCareful
|
| 868 | 869 | |
| 869 | 870 | unitJoinFloat :: OutBind -> JoinFloats
|
| 870 | -unitJoinFloat bind = assert (all isJoinId (bindersOf bind)) $
|
|
| 871 | +unitJoinFloat bind = assert (all isJoinPointBinding (bindersOf bind)) $
|
|
| 871 | 872 | unitOL bind
|
| 872 | 873 | |
| 873 | 874 | mkFloatBind :: SimplEnv -> OutBind -> (SimplFloats, SimplEnv)
|
| ... | ... | @@ -1123,7 +1124,7 @@ simplRecBndrs :: SimplEnv -> [InBndr] -> SimplM SimplEnv |
| 1123 | 1124 | -- Recursive let binders
|
| 1124 | 1125 | simplRecBndrs env@(SimplEnv {}) ids
|
| 1125 | 1126 | -- See Note [Bangs in the Simplifier]
|
| 1126 | - = assert (all (not . isJoinId) ids) $
|
|
| 1127 | + = assert (all (not . isJoinPointBinding) ids) $
|
|
| 1127 | 1128 | do { let (!env1, ids1) = mapAccumL substIdBndr env ids
|
| 1128 | 1129 | ; seqIds ids1 `seq` return env1 }
|
| 1129 | 1130 | |
| ... | ... | @@ -1256,7 +1257,7 @@ simplRecJoinBndrs :: SimplEnv -> [InBndr] |
| 1256 | 1257 | -- context being pushed inward may change types
|
| 1257 | 1258 | -- See Note [Return type for join points]
|
| 1258 | 1259 | simplRecJoinBndrs env@(SimplEnv {}) ids mult res_ty
|
| 1259 | - = assert (all isJoinId ids) $
|
|
| 1260 | + = assert (all isJoinPointBinding ids) $
|
|
| 1260 | 1261 | do { let (env1, ids1) = mapAccumL (simplJoinBndr mult res_ty) env ids
|
| 1261 | 1262 | ; seqIds ids1 `seq` return env1 }
|
| 1262 | 1263 | |
| ... | ... | @@ -1283,7 +1284,7 @@ adjustJoinPointType :: Mult |
| 1283 | 1284 | -- INVARIANT: If any of the first n binders are foralls, those tyvars
|
| 1284 | 1285 | -- cannot appear in the original result type. See isValidJoinPointType.
|
| 1285 | 1286 | adjustJoinPointType mult new_res_ty join_id
|
| 1286 | - = assert (isJoinId join_id) $
|
|
| 1287 | + = assert (isJoinPointBinding join_id) $
|
|
| 1287 | 1288 | setIdType join_id new_join_ty
|
| 1288 | 1289 | where
|
| 1289 | 1290 | join_arity = idJoinArity join_id
|
| ... | ... | @@ -38,7 +38,7 @@ import GHC.Core.Utils |
| 38 | 38 | import GHC.Core.Opt.Arity ( ArityType, exprArity, arityTypeBotSigs_maybe
|
| 39 | 39 | , pushCoTyArg, pushCoValArg, exprIsDeadEnd
|
| 40 | 40 | , typeArity, arityTypeArity, etaExpandAT )
|
| 41 | -import GHC.Core.SimpleOpt ( exprIsConApp_maybe, joinPointBinding_maybe, joinPointBindings_maybe )
|
|
| 41 | +import GHC.Core.SimpleOpt ( exprIsConApp_maybe, joinPointBinding_maybe, joinPointBindings_maybe, isJoinPointBinding )
|
|
| 42 | 42 | import GHC.Core.FVs ( mkRuleInfo {- exprsFreeIds -} )
|
| 43 | 43 | import GHC.Core.Rules ( lookupRule, getRules )
|
| 44 | 44 | import GHC.Core.Multiplicity
|
| ... | ... | @@ -316,8 +316,8 @@ simplLazyBind :: TopLevelFlag -> RecFlag |
| 316 | 316 | -- Precondition: Ids only, no TyVars; not a JoinId
|
| 317 | 317 | -- Precondition: rhs obeys the let-can-float invariant
|
| 318 | 318 | simplLazyBind top_lvl is_rec (bndr,unf_se) (bndr1,env) (rhs,rhs_se)
|
| 319 | - = assert (isId bndr )
|
|
| 320 | - assertPpr (not (isJoinId bndr)) (ppr bndr) $
|
|
| 319 | + = assert (isId bndr)
|
|
| 320 | + assertPpr (not $ isJoinPointBinding bndr) (ppr bndr) $
|
|
| 321 | 321 | -- pprTrace "simplLazyBind" ((ppr bndr <+> ppr bndr1) $$ ppr rhs $$ ppr (seIdSubst rhs_se)) $
|
| 322 | 322 | do { let !rhs_env = rhs_se `setInScopeFromE` env -- See Note [Bangs in the Simplifier]
|
| 323 | 323 | (tvs, body) = case collectTyAndValBinders rhs of
|
| ... | ... | @@ -399,7 +399,7 @@ simplAuxBind :: String |
| 399 | 399 | -- Precondition: rhs satisfies the let-can-float invariant
|
| 400 | 400 | |
| 401 | 401 | simplAuxBind _str env bndr new_rhs
|
| 402 | - | assertPpr (isId bndr && not (isJoinId bndr)) (ppr bndr) $
|
|
| 402 | + | assertPpr (isId bndr && not (isJoinPointBinding bndr)) (ppr bndr) $
|
|
| 403 | 403 | isDeadBinder bndr -- Not uncommon; e.g. case (a,b) of c { (p,q) -> p }
|
| 404 | 404 | = return (emptyFloats env, env) -- Here c is dead, and we avoid
|
| 405 | 405 | -- creating the binding c = (a,b)
|
| ... | ... | @@ -1905,7 +1905,7 @@ simplNonRecE :: HasDebugCallStack |
| 1905 | 1905 | -- Otherwise it may or may not satisfy it.
|
| 1906 | 1906 | |
| 1907 | 1907 | simplNonRecE env from_what bndr (rhs, rhs_se) body cont
|
| 1908 | - | assert (isId bndr && not (isJoinId bndr) ) $
|
|
| 1908 | + | assert (isId bndr && not (isJoinPointBinding bndr)) $
|
|
| 1909 | 1909 | is_strict_bind
|
| 1910 | 1910 | = -- Evaluate RHS strictly
|
| 1911 | 1911 | simplExprF (rhs_se `setInScopeFromE` env) rhs
|
| ... | ... | @@ -1943,7 +1943,7 @@ simplRecE :: SimplEnv |
| 1943 | 1943 | -- Precondition: not a join-point binding
|
| 1944 | 1944 | simplRecE env pairs body cont
|
| 1945 | 1945 | = do { let bndrs = map fst pairs
|
| 1946 | - ; massert (all (not . isJoinId) bndrs)
|
|
| 1946 | + ; massert (isNothing $ joinPointBindings_maybe pairs)
|
|
| 1947 | 1947 | ; env1 <- simplRecBndrs env bndrs
|
| 1948 | 1948 | -- NB: bndrs' don't have unfoldings or rules
|
| 1949 | 1949 | -- We add them as we go down
|
| ... | ... | @@ -2051,7 +2051,7 @@ simplNonRecJoinPoint :: SimplEnv -> InId -> InExpr |
| 2051 | 2051 | -> InExpr -> SimplCont
|
| 2052 | 2052 | -> SimplM (SimplFloats, OutExpr)
|
| 2053 | 2053 | simplNonRecJoinPoint env bndr rhs body cont
|
| 2054 | - = assert (isJoinId bndr ) $
|
|
| 2054 | + = assert (isJoinPointBinding bndr) $
|
|
| 2055 | 2055 | wrapJoinCont env cont $ \ env cont ->
|
| 2056 | 2056 | do { -- We push join_cont into the join RHS and the body;
|
| 2057 | 2057 | -- and wrap wrap_cont around the whole thing
|
| ... | ... | @@ -4574,22 +4574,24 @@ simplLetUnfolding env bind_cxt id new_rhs rhs_ty arity unf |
| 4574 | 4574 | | isStableUnfolding unf
|
| 4575 | 4575 | = simplStableUnfolding env bind_cxt id rhs_ty arity unf
|
| 4576 | 4576 | |
| 4577 | - | freshly_born_join_point id
|
|
| 4578 | - = -- This is a tricky one!
|
|
| 4579 | - -- See wrinkle (JU1) in Note [Do not add unfoldings to join points at birth]
|
|
| 4580 | - return noUnfolding
|
|
| 4581 | - |
|
| 4582 | 4577 | | isExitJoinId id
|
| 4583 | 4578 | = -- See Note [Do not inline exit join points] in GHC.Core.Opt.Exitify
|
| 4584 | 4579 | return noUnfolding
|
| 4585 | 4580 | |
| 4586 | - | otherwise
|
|
| 4587 | - = mkLetUnfolding env (bindContextLevel bind_cxt) VanillaSrc id is_join_point new_rhs
|
|
| 4581 | + | freshly_born_join_point
|
|
| 4582 | + = -- This is a tricky one!
|
|
| 4583 | + -- See wrinkle (JU1) in Note [Do not add unfoldings to join points at birth]
|
|
| 4584 | + return noUnfolding
|
|
| 4588 | 4585 | |
| 4586 | + | otherwise
|
|
| 4587 | + = mkLetUnfolding env (bindContextLevel bind_cxt) VanillaSrc id' is_join new_rhs'
|
|
| 4589 | 4588 | where
|
| 4590 | - is_join_point = isJoinId id
|
|
| 4591 | - freshly_born_join_point id = is_join_point && isManyOccs (idOccInfo id)
|
|
| 4592 | - -- OLD: too_many_occs (OneOcc { occ_n_br = n }) = n > 10 -- See #23627
|
|
| 4589 | + (id', new_rhs', is_join) =
|
|
| 4590 | + case joinPointBinding_maybe id new_rhs of
|
|
| 4591 | + Nothing -> (id, new_rhs, False)
|
|
| 4592 | + Just (id', new_rhs') -> (id', new_rhs', True)
|
|
| 4593 | + freshly_born_join_point =
|
|
| 4594 | + is_join && (not (isJoinId id) || isManyOccs (idOccInfo id))
|
|
| 4593 | 4595 | |
| 4594 | 4596 | -------------------
|
| 4595 | 4597 | mkLetUnfolding :: SimplEnv -> TopLevelFlag -> UnfoldingSource
|
| ... | ... | @@ -12,6 +12,7 @@ module GHC.Core.SimpleOpt ( |
| 12 | 12 | |
| 13 | 13 | -- ** Join points
|
| 14 | 14 | joinPointBinding_maybe, joinPointBindings_maybe,
|
| 15 | + isJoinPointBinding,
|
|
| 15 | 16 | |
| 16 | 17 | -- ** Predicates on expressions
|
| 17 | 18 | exprIsConApp_maybe, exprIsLiteral_maybe, exprIsLambda_maybe,
|
| ... | ... | @@ -1059,34 +1060,42 @@ and again its arity increases (#15517) |
| 1059 | 1060 | -}
|
| 1060 | 1061 | |
| 1061 | 1062 | |
| 1062 | --- | Returns Just (bndr,rhs) if the binding is a join point:
|
|
| 1063 | --- If it's a JoinId, just return it
|
|
| 1064 | --- If it's not yet a JoinId but is always tail-called,
|
|
| 1065 | --- make it into a JoinId and return it.
|
|
| 1066 | --- In the latter case, eta-expand the RHS if necessary, to make the
|
|
| 1067 | --- lambdas explicit, as is required for join points
|
|
| 1068 | ---
|
|
| 1069 | --- Precondition: the InBndr has been occurrence-analysed,
|
|
| 1070 | --- so its OccInfo is valid
|
|
| 1063 | +-- | Returns @Just (bndr,rhs)@ if the binding is a join point or can be made
|
|
| 1064 | +-- into a join point (it is always tail called). In the latter case, eta-expand
|
|
| 1065 | +-- the RHS if necessary, to make the lambdas explicit, as is required for join points.
|
|
| 1071 | 1066 | joinPointBinding_maybe :: InBndr -> InExpr -> Maybe (InBndr, InExpr)
|
| 1072 | 1067 | joinPointBinding_maybe bndr rhs
|
| 1073 | 1068 | | not (isId bndr)
|
| 1074 | 1069 | = Nothing
|
| 1075 | 1070 | |
| 1071 | + -- NB: the 'OccInfo' of the 'InBndr' may have been zapped, e.g. if we
|
|
| 1072 | + -- have inlined it. In this case, we may lose the join-point-hood of the
|
|
| 1073 | + -- original binder. A later occurrence analysis pass may recover it.
|
|
| 1076 | 1074 | | isJoinId bndr
|
| 1077 | - = Just (bndr, rhs)
|
|
| 1075 | + = case tailCallInfo (idOccInfo bndr) of
|
|
| 1076 | + NoTailCallInfo -> Nothing
|
|
| 1077 | + AlwaysTailCalled {} -> Just (bndr, rhs)
|
|
| 1078 | 1078 | |
| 1079 | 1079 | | AlwaysTailCalled 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
|
| 1083 | - join_bndr = bndr `asJoinId` join_arity
|
|
| 1083 | + join_bndr = bndr `asJoinId` join_arity
|
|
| 1084 | 1084 | `setIdDmdSig` etaConvertDmdSig str_arity str_sig
|
| 1085 | 1085 | = Just (join_bndr, mkLams bndrs body)
|
| 1086 | 1086 | |
| 1087 | 1087 | | otherwise
|
| 1088 | 1088 | = Nothing
|
| 1089 | 1089 | |
| 1090 | +isJoinPointBinding :: InBndr -> Bool
|
|
| 1091 | +isJoinPointBinding bndr
|
|
| 1092 | + | not (isId bndr)
|
|
| 1093 | + = False
|
|
| 1094 | + | AlwaysTailCalled {} <- tailCallInfo (idOccInfo bndr)
|
|
| 1095 | + = True
|
|
| 1096 | + | otherwise
|
|
| 1097 | + = False
|
|
| 1098 | + |
|
| 1090 | 1099 | joinPointBindings_maybe :: [(InBndr, InExpr)] -> Maybe [(InBndr, InExpr)]
|
| 1091 | 1100 | joinPointBindings_maybe bndrs
|
| 1092 | 1101 | = mapM (uncurry joinPointBinding_maybe) bndrs
|
| ... | ... | @@ -1443,7 +1452,7 @@ exprIsConApp_maybe ise@(ISE in_scope id_unf) expr |
| 1443 | 1452 | in go subst' (float:floats) body (CC args mco)
|
| 1444 | 1453 | |
| 1445 | 1454 | go subst floats (Let (NonRec bndr rhs) expr) cont
|
| 1446 | - | not (isJoinId bndr)
|
|
| 1455 | + | not (isJoinPointBinding bndr)
|
|
| 1447 | 1456 | -- Crucial guard! See Note [Don't float join points]
|
| 1448 | 1457 | = let rhs' = subst_expr subst rhs
|
| 1449 | 1458 | (subst', bndr') = subst_bndr subst bndr
|