Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
-
77b48b37
by sheaf at 2026-03-01T06:30:28-05:00
21 changed files:
- compiler/GHC/Cmm/Node.hs
- compiler/GHC/Core.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Opt/Arity.hs
- compiler/GHC/Core/Opt/FloatIn.hs
- compiler/GHC/Core/Opt/FloatOut.hs
- compiler/GHC/Core/Opt/OccurAnal.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/SimpleOpt.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Config/Core/Lint.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/StgToCmm/Expr.hs
- compiler/GHC/Types/Basic.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Tickish.hs
- testsuite/tests/codeGen/should_compile/debug.stdout
- + testsuite/tests/simplCore/should_compile/T26642.hs
- + testsuite/tests/simplCore/should_compile/TrickyJoins.hs
- testsuite/tests/simplCore/should_compile/all.T
Changes:
| ... | ... | @@ -819,8 +819,8 @@ data CmmTickScope |
| 819 | 819 | |
| 820 | 820 | | SubScope !U.Unique CmmTickScope
|
| 821 | 821 | -- ^ Constructs a new sub-scope to an existing scope. This allows
|
| 822 | - -- us to translate Core-style scoping rules (see @tickishScoped@)
|
|
| 823 | - -- into the Cmm world. Suppose the following code:
|
|
| 822 | + -- us to translate Core-style scoping rules (see Note [Scoping ticks and counting ticks]
|
|
| 823 | + -- in GHC.Types.Tickish) into the Cmm world. Suppose the following code:
|
|
| 824 | 824 | --
|
| 825 | 825 | -- tick<1> case ... of
|
| 826 | 826 | -- A -> tick<2> ...
|
| ... | ... | @@ -1035,6 +1035,143 @@ tail position: A cast changes the type, but the type must be the same. But |
| 1035 | 1035 | operationally, casts are vacuous, so this is a bit unfortunate! See #14610 for
|
| 1036 | 1036 | ideas how to fix this.
|
| 1037 | 1037 | |
| 1038 | +Note [Join points, casts, and ticks]
|
|
| 1039 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1040 | +Point (1) of Note [Invariants on join points] says that a join point
|
|
| 1041 | +must always be tail called. But what precisely does "tail called" mean
|
|
| 1042 | +in the presence of (a) casts and (b) ticks?
|
|
| 1043 | + |
|
| 1044 | +Example (CAST)
|
|
| 1045 | + let j x = rhs in
|
|
| 1046 | + case y of { True -> j 1 |> co; False -> j 2 }
|
|
| 1047 | + |
|
| 1048 | +Example (TICK)
|
|
| 1049 | + let j x = rhs in
|
|
| 1050 | + case y of { True -> <tick t> (j 1); False -> j 2 }
|
|
| 1051 | + |
|
| 1052 | +Answer: in Core:
|
|
| 1053 | + |
|
| 1054 | + (JCT1) A tail call cannot be under a cast.
|
|
| 1055 | + |
|
| 1056 | + Thus, in (CAST), `j` is not a join point.
|
|
| 1057 | + |
|
| 1058 | + (JCT2) A tail call cannot be under a cost-centre-scoped tick.
|
|
| 1059 | + |
|
| 1060 | + Thus, in (TICK), `j` is a join point only if tick `t` has soft scope
|
|
| 1061 | + (as per Note [Scoping ticks and counting ticks] in GHC.Tickish).
|
|
| 1062 | + |
|
| 1063 | +The Big Reason for these choices is that the Simplifier moves the continuation
|
|
| 1064 | +into the RHS of a join point, as explained in Note [Join points and case-of-case]
|
|
| 1065 | +in GHC.Core.Opt.Simplify.Iteration:
|
|
| 1066 | + |
|
| 1067 | + K[ join j x = rhs in body ] --> join j x = K[rhs] in K[body]
|
|
| 1068 | + |
|
| 1069 | +and K then evaporates when it encounters the tail call:
|
|
| 1070 | + |
|
| 1071 | + K[jump j v] --> jump j v
|
|
| 1072 | + |
|
| 1073 | +These transformations:
|
|
| 1074 | + * Are ill-typed if the tail is under a cast, hence (JCT1)
|
|
| 1075 | + * Change cost semantics if the tick has cost-centre scope, hence (JCT2)
|
|
| 1076 | + |
|
| 1077 | +The occurrence analyser is careful not to treat an occurrence as a tail call if
|
|
| 1078 | +it falls under (JCT1) or (JCT2), by using 'markAllNonTail'.
|
|
| 1079 | + |
|
| 1080 | +However, during /code generation/ the key thing about a join point is that
|
|
| 1081 | + * The binding does no allocation
|
|
| 1082 | + * A tail call can be implemented by "adjust stack pointer and jump".
|
|
| 1083 | + |
|
| 1084 | +This code-gen strategy works fine even if the "tail call" occurs under
|
|
| 1085 | +/arbitrary/ ticks and casts. Hence:
|
|
| 1086 | + |
|
| 1087 | +(JCT3) In CorePrep, the occurrence analyser is called with a special flag that
|
|
| 1088 | + /does/ treat `j` as tail-called in Example (CAST) and Example (TICK).
|
|
| 1089 | + Core Prep then uses 'joinPointBinding_maybe', which turns always-tail-called
|
|
| 1090 | + let bindings into join points, thus recovering join-point-hood.
|
|
| 1091 | + |
|
| 1092 | +See also Note [Linting join points with casts or ticks] in GHC.Core.Lint.
|
|
| 1093 | + |
|
| 1094 | +Examples
|
|
| 1095 | +========
|
|
| 1096 | + |
|
| 1097 | + Join point jumps under ticks (#14242, #26157, #26642, #26693)
|
|
| 1098 | + ============================
|
|
| 1099 | + In #26693 we had:
|
|
| 1100 | + |
|
| 1101 | + join { j :: Bool -> Int -> IO (); j _ = guts }
|
|
| 1102 | + in case b of
|
|
| 1103 | + False -> scc<foo> jump j True
|
|
| 1104 | + True -> jump j False
|
|
| 1105 | + |
|
| 1106 | + If we try to push the application to an argument 'arg :: Int' into this
|
|
| 1107 | + expression, we first get:
|
|
| 1108 | + |
|
| 1109 | + join { j :: Bool -> IO (); j _ = guts arg ] }
|
|
| 1110 | + in case b of
|
|
| 1111 | + False -> (scc<foo> jump j True) arg
|
|
| 1112 | + True -> jump j False arg
|
|
| 1113 | + |
|
| 1114 | + We then rely on 'trimJoinCont' to remove the argument. In this case, this fails
|
|
| 1115 | + for the first branch, because 'trimJoinCont' doesn't look through profiling
|
|
| 1116 | + ticks. Were we to address this, it's still not clear what code we would want to
|
|
| 1117 | + end up with, as we don't want to misattribute profiling costs.
|
|
| 1118 | + We could plausibly transform to the following:
|
|
| 1119 | + |
|
| 1120 | + join { j :: Bool -> IO (); j scc_or_null _ = (setSCC# scc_or_null guts) arg ] }
|
|
| 1121 | + in case b of
|
|
| 1122 | + False -> jump j <foo> True
|
|
| 1123 | + True -> jump j null False
|
|
| 1124 | + |
|
| 1125 | + where `setSCC#` is a new primop that would set the current cost centre pointer
|
|
| 1126 | + (or no-op if the given pointer is null). However:
|
|
| 1127 | + - this primop doesn't exist today,
|
|
| 1128 | + - it requires adding an argument to the join point (hence changing its arity)
|
|
| 1129 | + |
|
| 1130 | + Note that soft scope ticks are floated out by the simplifier (see the
|
|
| 1131 | + 'tickishHasSoftScope' guard in 'GHC.Core.Opt.Simplify.Iteration.simplTick'),
|
|
| 1132 | + so don't suffer from the same problem.
|
|
| 1133 | + |
|
| 1134 | + Join point jumps under casts (#14610, #21716, #26422)
|
|
| 1135 | + ============================
|
|
| 1136 | + Consider:
|
|
| 1137 | + |
|
| 1138 | + newtype Age = MkAge Int -- axAge :: Age ~ Int
|
|
| 1139 | + f :: Int -> ...
|
|
| 1140 | + |
|
| 1141 | + f (join j :: Bool -> Age
|
|
| 1142 | + j x = (rhs1 :: Age)
|
|
| 1143 | + in case v of
|
|
| 1144 | + Just x -> ((j x) |> axAge) :: Int
|
|
| 1145 | + Nothing -> rhs2)
|
|
| 1146 | + |
|
| 1147 | + If we try to use the case of case transformation to push 'f' inwards, we would
|
|
| 1148 | + get:
|
|
| 1149 | + |
|
| 1150 | + join j' x = f (rhs1 :: Age)
|
|
| 1151 | + in case v of
|
|
| 1152 | + Just x -> (j' x |> axAge)
|
|
| 1153 | + Nothing -> f rhs2
|
|
| 1154 | + |
|
| 1155 | + which is utterly bogus, as we are now passing an argument of type 'Age' to
|
|
| 1156 | + 'f', which expects an 'Int'.
|
|
| 1157 | + |
|
| 1158 | + The alternative would be to implement a transformation of the form
|
|
| 1159 | + |
|
| 1160 | + join { j x = blah }
|
|
| 1161 | + in case e of
|
|
| 1162 | + False -> j True |> co1
|
|
| 1163 | + True -> j False |> co2
|
|
| 1164 | + |
|
| 1165 | + ====>
|
|
| 1166 | + |
|
| 1167 | + join { j x co = blah |> co }
|
|
| 1168 | + in case e of
|
|
| 1169 | + False -> j True co1
|
|
| 1170 | + True -> j False co2
|
|
| 1171 | + |
|
| 1172 | + by adding a coercion argument to the join point. We don't do this currently.
|
|
| 1173 | + |
|
| 1174 | + |
|
| 1038 | 1175 | Note [Strict fields in Core]
|
| 1039 | 1176 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 1040 | 1177 | In Core, evaluating a data constructor worker evaluates its strict fields.
|
| ... | ... | @@ -106,6 +106,7 @@ import Data.List.NonEmpty ( NonEmpty(..), groupWith, nonEmpty ) |
| 106 | 106 | import Data.Maybe
|
| 107 | 107 | import Data.IntMap.Strict ( IntMap )
|
| 108 | 108 | import qualified Data.IntMap.Strict as IntMap ( lookup, keys, empty, fromList )
|
| 109 | +import GHC.Types.Unique.Map
|
|
| 109 | 110 | |
| 110 | 111 | {-
|
| 111 | 112 | Note [Core Lint guarantee]
|
| ... | ... | @@ -914,8 +915,8 @@ lintCoreExpr (Lit lit) |
| 914 | 915 | ; return (literalType lit, zeroUE) }
|
| 915 | 916 | |
| 916 | 917 | lintCoreExpr (Cast expr co)
|
| 917 | - = do { (expr_ty, ue) <- markAllJoinsBad (lintCoreExpr expr)
|
|
| 918 | - -- markAllJoinsBad: see Note [Join points and casts]
|
|
| 918 | + = do { (expr_ty, ue) <- markAllJoinsUnderCast (lintCoreExpr expr)
|
|
| 919 | + -- markAllJoinsUnderCast: see Note [Linting join points with casts or ticks]
|
|
| 919 | 920 | |
| 920 | 921 | ; lintCoercion co
|
| 921 | 922 | ; lintRole co Representational (coercionRole co)
|
| ... | ... | @@ -929,14 +930,7 @@ lintCoreExpr (Tick tickish expr) |
| 929 | 930 | = do { case tickish of
|
| 930 | 931 | Breakpoint _ _ ids -> forM_ ids $ \id -> lintIdOcc id 0
|
| 931 | 932 | _ -> return ()
|
| 932 | - ; markAllJoinsBadIf block_joins $ lintCoreExpr expr }
|
|
| 933 | - where
|
|
| 934 | - block_joins = not (tickish `tickishScopesLike` SoftScope)
|
|
| 935 | - -- TODO Consider whether this is the correct rule. It is consistent with
|
|
| 936 | - -- the simplifier's behaviour - cost-centre-scoped ticks become part of
|
|
| 937 | - -- the continuation, and thus they behave like part of an evaluation
|
|
| 938 | - -- context, but soft-scoped and non-scoped ticks simply wrap the result
|
|
| 939 | - -- (see Simplify.simplTick).
|
|
| 933 | + ; markAllJoinsUnderTick tickish $ lintCoreExpr expr }
|
|
| 940 | 934 | |
| 941 | 935 | lintCoreExpr (Let (NonRec tv (Type ty)) body)
|
| 942 | 936 | | isTyVar tv
|
| ... | ... | @@ -1017,22 +1011,16 @@ lintCoreExpr e@(App _ _) |
| 1017 | 1011 | |
| 1018 | 1012 | ; return app_pair}
|
| 1019 | 1013 | where
|
| 1020 | - skipTick t = case collectFunSimple e of
|
|
| 1021 | - (Var v) -> etaExpansionTick v t
|
|
| 1022 | - _ -> tickishFloatable t
|
|
| 1023 | - (fun, args, _source_ticks) = collectArgsTicks skipTick e
|
|
| 1024 | - -- We must look through source ticks to avoid #21152, for example:
|
|
| 1025 | - --
|
|
| 1026 | - -- reallyUnsafePtrEquality
|
|
| 1027 | - -- = \ @a ->
|
|
| 1028 | - -- (src<loc> reallyUnsafePtrEquality#)
|
|
| 1029 | - -- @Lifted @a @Lifted @a
|
|
| 1014 | + skipTick t =
|
|
| 1015 | + case collectFunSimple e of
|
|
| 1016 | + Var v -> canCollectArgsThroughTick v t
|
|
| 1017 | + _ -> tickishFloatable t
|
|
| 1018 | + (fun, args, _ticks) = collectArgsTicks skipTick e
|
|
| 1019 | + -- We must look through ticks, otherwise we may fail to spot a
|
|
| 1020 | + -- saturated application. We use 'canCollectArgsThroughTicks', which is
|
|
| 1021 | + -- the same predicate that Core Prep uses.
|
|
| 1030 | 1022 | --
|
| 1031 | - -- To do this, we use `collectArgsTicks tickishFloatable` to match
|
|
| 1032 | - -- the eta expansion behaviour, as per Note [Eta expansion and source notes]
|
|
| 1033 | - -- in GHC.Core.Opt.Arity.
|
|
| 1034 | - -- Sadly this was not quite enough. So we now also accept things that CorePrep will allow.
|
|
| 1035 | - -- See Note [Ticks and mandatory eta expansion]
|
|
| 1023 | + -- See Note [Ticks and mandatory eta expansion] in GHC.CoreToStg.Prep.
|
|
| 1036 | 1024 | |
| 1037 | 1025 | lintCoreExpr (Lam var expr)
|
| 1038 | 1026 | = markAllJoinsBad $
|
| ... | ... | @@ -1131,7 +1119,7 @@ checkDeadIdOcc id |
| 1131 | 1119 | ------------------
|
| 1132 | 1120 | lintJoinBndrType :: OutType -- Type of the body
|
| 1133 | 1121 | -> OutId -- Possibly a join Id
|
| 1134 | - -> LintM ()
|
|
| 1122 | + -> LintM ()
|
|
| 1135 | 1123 | -- Checks that the return type of a join Id matches the body
|
| 1136 | 1124 | -- E.g. join j x = rhs in body
|
| 1137 | 1125 | -- The type of 'rhs' must be the same as the type of 'body'
|
| ... | ... | @@ -1139,13 +1127,29 @@ lintJoinBndrType body_ty bndr |
| 1139 | 1127 | | JoinPoint arity <- idJoinPointHood bndr
|
| 1140 | 1128 | , let bndr_ty = idType bndr
|
| 1141 | 1129 | , (bndrs, res) <- splitPiTys bndr_ty
|
| 1142 | - = do let msg =
|
|
| 1143 | - hang (text "Join point returns different type than body")
|
|
| 1144 | - 2 (vcat [ text "Join bndr:" <+> ppr bndr <+> dcolon <+> ppr (idType bndr)
|
|
| 1145 | - , text "Join arity:" <+> ppr arity
|
|
| 1146 | - , text "Body type:" <+> ppr body_ty ])
|
|
| 1147 | - checkL (length bndrs >= arity) msg
|
|
| 1148 | - ensureEqTys body_ty (mkPiTys (drop arity bndrs) res) msg
|
|
| 1130 | + = do let
|
|
| 1131 | + ty_msg =
|
|
| 1132 | + hang (text "Join point returns different type than body")
|
|
| 1133 | + 2 (vcat [ text "Join bndr:" <+> ppr bndr <+> dcolon <+> ppr (idType bndr)
|
|
| 1134 | + , text "Join arity:" <+> ppr arity
|
|
| 1135 | + , text "Body type:" <+> ppr body_ty ])
|
|
| 1136 | + arity_msg =
|
|
| 1137 | + hang (text "Join point is not saturated")
|
|
| 1138 | + 2 (vcat [ text "Join bndr:" <+> ppr bndr <+> dcolon <+> ppr (idType bndr)
|
|
| 1139 | + , text "Join arity:" <+> ppr arity
|
|
| 1140 | + , text "Arguments:" <+> ppr bndrs ])
|
|
| 1141 | + |
|
| 1142 | + mb_join_info <- lookupJoinId bndr
|
|
| 1143 | + case mb_join_info of
|
|
| 1144 | + Nothing ->
|
|
| 1145 | + pprPanic "lintJoinBndrType: valid join marked bad" (ppr bndr)
|
|
| 1146 | + Just (_, occ_info) -> do
|
|
| 1147 | + checkL (length bndrs >= arity) arity_msg
|
|
| 1148 | + |
|
| 1149 | + -- See Note [Linting join points with casts or ticks] for why
|
|
| 1150 | + -- we skip this check if there is an intervening cast.
|
|
| 1151 | + unless (occ_info == JoinOccUnderCast) $
|
|
| 1152 | + ensureEqTys body_ty (mkPiTys (drop arity bndrs) res) ty_msg
|
|
| 1149 | 1153 | | otherwise
|
| 1150 | 1154 | = return ()
|
| 1151 | 1155 | |
| ... | ... | @@ -1156,11 +1160,11 @@ checkJoinOcc var n_args |
| 1156 | 1160 | | JoinPoint join_arity_occ <- idJoinPointHood var
|
| 1157 | 1161 | = do { mb_join_arity_bndr <- lookupJoinId var
|
| 1158 | 1162 | ; case mb_join_arity_bndr of {
|
| 1159 | - NotJoinPoint -> do { join_set <- getValidJoins
|
|
| 1160 | - ; addErrL (text "join set " <+> ppr join_set $$
|
|
| 1161 | - invalidJoinOcc var) } ;
|
|
| 1163 | + Nothing -> do { valid_joins <- getValidJoins
|
|
| 1164 | + ; addErrL (text "valid joins:" <+> ppr valid_joins $$
|
|
| 1165 | + invalidJoinOcc var) } ;
|
|
| 1162 | 1166 | |
| 1163 | - JoinPoint join_arity_bndr ->
|
|
| 1167 | + Just (join_arity_bndr, _join_occ) ->
|
|
| 1164 | 1168 | |
| 1165 | 1169 | do { checkL (join_arity_bndr == join_arity_occ) $
|
| 1166 | 1170 | -- Arity differs at binding site and occurrence
|
| ... | ... | @@ -1333,39 +1337,34 @@ checkLinearity body_ue lam_var = |
| 1333 | 1337 | return body_ue'
|
| 1334 | 1338 | Nothing -> return body_ue -- A type variable
|
| 1335 | 1339 | |
| 1336 | -{- Note [Join points and casts]
|
|
| 1337 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1338 | -You might think that this should be OK:
|
|
| 1339 | - join j x = rhs
|
|
| 1340 | - in (case e of
|
|
| 1341 | - A -> alt1
|
|
| 1342 | - B x -> (jump j x) |> co)
|
|
| 1340 | +{- Note [Linting join points with casts or ticks]
|
|
| 1341 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1342 | +As per Note [Join points, casts, and ticks] in GHC.Core, we have to be careful
|
|
| 1343 | +when a cast or tick occurs in between a join point binding and a corresponding
|
|
| 1344 | +join point occurrence.
|
|
| 1343 | 1345 | |
| 1344 | -You might think that, since the cast is ultimately erased, the jump to
|
|
| 1345 | -`j` should still be OK as a join point. But no! See #21716. Suppose
|
|
| 1346 | +Generally speaking:
|
|
| 1346 | 1347 | |
| 1347 | - newtype Age = MkAge Int -- axAge :: Age ~ Int
|
|
| 1348 | - f :: Int -> ... -- f strict in it's first argument
|
|
| 1348 | + - The simplifier cannot handle intervening casts or non-soft-scope ticks, so
|
|
| 1349 | + we must check for that to avoid producing invalid Core.
|
|
| 1350 | + - However, as per (JCT3), Core Prep **can** produce join points with
|
|
| 1351 | + intervening casts or non-soft-scope ticks, which means we must expect them.
|
|
| 1349 | 1352 | |
| 1350 | -and consider the expression
|
|
| 1353 | +Casts present an additional challenge. Consider for example:
|
|
| 1351 | 1354 | |
| 1352 | - f (join j :: Bool -> Age
|
|
| 1353 | - j x = (rhs1 :: Age)
|
|
| 1354 | - in case v of
|
|
| 1355 | - Just x -> (j x |> axAge :: Int)
|
|
| 1356 | - Nothing -> rhs2)
|
|
| 1355 | + join { j :: Bool -> Age; j x = (blah :: Age) }
|
|
| 1356 | + in case e of
|
|
| 1357 | + False -> j True |> (co1 :: Age ~ Int)
|
|
| 1358 | + True -> other :: Int
|
|
| 1357 | 1359 | |
| 1358 | -Then, if the Simplifier pushes the strict call into the join points
|
|
| 1359 | -and alternatives we'll get
|
|
| 1360 | +It is **not** the case that the type of 'blah' is the same as the type of
|
|
| 1361 | +the body of the join point binding! Indeed:
|
|
| 1360 | 1362 | |
| 1361 | - join j' x = f (rhs1 :: Age)
|
|
| 1362 | - in case v of
|
|
| 1363 | - Just x -> j' x |> axAge
|
|
| 1364 | - Nothing -> f rhs2
|
|
| 1363 | + - RHS of the join-point binding: blah :: Age
|
|
| 1364 | + - The body of the join point has type Int.
|
|
| 1365 | 1365 | |
| 1366 | -Utterly bogus. `f` expects an `Int` and we are giving it an `Age`.
|
|
| 1367 | -No no no. Casts destroy the tail-call property. Henc markAllJoinsBad
|
|
| 1368 | -in the (Cast expr co) case of lintCoreExpr.
|
|
| 1366 | +So we skip the 'exprType(join_rhs) == exprType(join_body)' check when casts
|
|
| 1367 | +occur in between.
|
|
| 1369 | 1368 | |
| 1370 | 1369 | Note [No alternatives lint check]
|
| 1371 | 1370 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -2977,9 +2976,10 @@ data LintEnv |
| 2977 | 2976 | -- type variables, and coercion variables)
|
| 2978 | 2977 | -- Used at an occurrence of the InVar
|
| 2979 | 2978 | |
| 2980 | - , le_joins :: IdSet -- Join points in scope that are valid
|
|
| 2981 | - -- A subset of the InScopeSet in le_subst
|
|
| 2982 | - -- See Note [Join points]
|
|
| 2979 | + , le_joins :: UniqMap Id JoinOcc
|
|
| 2980 | + -- ^ Join points in scope that are valid
|
|
| 2981 | + -- A subset of the InScopeSet in le_subst
|
|
| 2982 | + -- See Note [Join points]
|
|
| 2983 | 2983 | |
| 2984 | 2984 | , le_ue_aliases :: NameEnv UsageEnv
|
| 2985 | 2985 | -- See Note [Linting linearity]
|
| ... | ... | @@ -2999,6 +2999,7 @@ data LintFlags |
| 2999 | 2999 | , lf_check_linearity :: Bool -- ^ See Note [Linting linearity]
|
| 3000 | 3000 | , lf_check_fixed_rep :: Bool -- ^ See Note [Checking for representation polymorphism]
|
| 3001 | 3001 | , lf_check_rubbish_lits :: Bool -- ^ See Note [Checking for rubbish literals]
|
| 3002 | + , lf_allow_weak_joins :: Bool -- ^ See Note [Linting join points with casts or ticks]
|
|
| 3002 | 3003 | }
|
| 3003 | 3004 | |
| 3004 | 3005 | -- See Note [Checking StaticPtrs]
|
| ... | ... | @@ -3307,6 +3308,20 @@ data LintLocInfo |
| 3307 | 3308 | | InCo Coercion -- Inside a coercion
|
| 3308 | 3309 | | InAxiom (CoAxiom Branched) -- Inside a CoAxiom
|
| 3309 | 3310 | |
| 3311 | +-- | Does this join point 'Id' occur inside a cast?
|
|
| 3312 | +--
|
|
| 3313 | +-- See Note [Linting join points with casts or ticks].
|
|
| 3314 | +data JoinOcc
|
|
| 3315 | + -- | A normal occurrence of a 'JoinId'.
|
|
| 3316 | + = NormalJoinOcc
|
|
| 3317 | + -- | An occurrence of a 'JoinId' with an intervening cast between the
|
|
| 3318 | + -- join point binder definition and the jump.
|
|
| 3319 | + | JoinOccUnderCast
|
|
| 3320 | + deriving stock Eq
|
|
| 3321 | +instance Outputable JoinOcc where
|
|
| 3322 | + ppr NormalJoinOcc = text "Normal"
|
|
| 3323 | + ppr JoinOccUnderCast = text "UnderCast"
|
|
| 3324 | + |
|
| 3310 | 3325 | data LintConfig = LintConfig
|
| 3311 | 3326 | { l_diagOpts :: !DiagOpts -- ^ Diagnostics opts
|
| 3312 | 3327 | , l_platform :: !Platform -- ^ Target platform
|
| ... | ... | @@ -3328,7 +3343,7 @@ initL cfg m |
| 3328 | 3343 | env = LE { le_flags = l_flags cfg
|
| 3329 | 3344 | , le_subst = mkEmptySubst (mkInScopeSetList vars)
|
| 3330 | 3345 | , le_in_vars = mkVarEnv [ (v,(v, varType v)) | v <- vars ]
|
| 3331 | - , le_joins = emptyVarSet
|
|
| 3346 | + , le_joins = emptyUniqMap
|
|
| 3332 | 3347 | , le_loc = []
|
| 3333 | 3348 | , le_ue_aliases = emptyNameEnv
|
| 3334 | 3349 | , le_platform = l_platform cfg
|
| ... | ... | @@ -3428,11 +3443,11 @@ addInScopeId in_id out_ty thing_inside |
| 3428 | 3443 | in unLintM (thing_inside out_id) env' errs
|
| 3429 | 3444 | |
| 3430 | 3445 | where
|
| 3431 | - add env@(LE { le_in_vars = id_vars, le_joins = join_set
|
|
| 3446 | + add env@(LE { le_in_vars = id_vars, le_joins = valid_joins
|
|
| 3432 | 3447 | , le_ue_aliases = aliases, le_subst = subst })
|
| 3433 | 3448 | = (out_id, env1)
|
| 3434 | 3449 | where
|
| 3435 | - env1 = env { le_in_vars = in_vars', le_joins = join_set', le_ue_aliases = aliases' }
|
|
| 3450 | + env1 = env { le_in_vars = in_vars', le_joins = valid_joins', le_ue_aliases = aliases' }
|
|
| 3436 | 3451 | |
| 3437 | 3452 | in_vars' = extendVarEnv id_vars in_id (in_id, out_ty)
|
| 3438 | 3453 | aliases' = delFromNameEnv aliases (idName in_id)
|
| ... | ... | @@ -3446,9 +3461,9 @@ addInScopeId in_id out_ty thing_inside |
| 3446 | 3461 | out_id | isEmptyTCvSubst subst = in_id
|
| 3447 | 3462 | | otherwise = setIdType in_id out_ty
|
| 3448 | 3463 | |
| 3449 | - join_set'
|
|
| 3450 | - | isJoinId out_id = extendVarSet join_set in_id -- Overwrite with new arity
|
|
| 3451 | - | otherwise = delVarSet join_set in_id -- Remove any existing binding
|
|
| 3464 | + valid_joins'
|
|
| 3465 | + | isJoinId out_id = addToUniqMap valid_joins in_id NormalJoinOcc -- Overwrite with new arity
|
|
| 3466 | + | otherwise = delFromUniqMap valid_joins in_id -- Remove any existing binding
|
|
| 3452 | 3467 | |
| 3453 | 3468 | addInScopeTyCoVar :: InTyCoVar -> OutType -> (OutTyCoVar -> LintM a) -> LintM a
|
| 3454 | 3469 | -- This function clones to avoid shadowing of TyCoVars
|
| ... | ... | @@ -3485,13 +3500,35 @@ extendTvSubstL tv ty m |
| 3485 | 3500 | |
| 3486 | 3501 | markAllJoinsBad :: LintM a -> LintM a
|
| 3487 | 3502 | markAllJoinsBad m
|
| 3488 | - = LintM $ \ env errs -> unLintM m (env { le_joins = emptyVarSet }) errs
|
|
| 3503 | + = LintM $ \ env errs -> unLintM m (env { le_joins = emptyUniqMap }) errs
|
|
| 3504 | + |
|
| 3505 | +-- | Mark all join points as occurring under a tick.
|
|
| 3506 | +--
|
|
| 3507 | +-- See Note [Linting join points with casts or ticks].
|
|
| 3508 | +markAllJoinsUnderTick :: CoreTickish -> LintM a -> LintM a
|
|
| 3509 | +markAllJoinsUnderTick tick m
|
|
| 3510 | + = LintM $ \ env errs ->
|
|
| 3511 | + let env' = if tickishHasSoftScope tick || lf_allow_weak_joins (le_flags env)
|
|
| 3512 | + then env
|
|
| 3513 | + else env { le_joins = emptyUniqMap }
|
|
| 3514 | + in unLintM m env' errs
|
|
| 3515 | + |
|
| 3516 | +-- | Mark all join points as occurring under a cast.
|
|
| 3517 | +--
|
|
| 3518 | +-- See Note [Linting join points with casts or ticks].
|
|
| 3519 | +markAllJoinsUnderCast :: LintM a -> LintM a
|
|
| 3520 | +markAllJoinsUnderCast m
|
|
| 3521 | + = LintM $ \ env errs ->
|
|
| 3522 | + let !env' = if lf_allow_weak_joins (le_flags env)
|
|
| 3523 | + then env { le_joins = fmap (const JoinOccUnderCast) (le_joins env) }
|
|
| 3524 | + else env { le_joins = emptyUniqMap }
|
|
| 3525 | + in unLintM m env' errs
|
|
| 3489 | 3526 | |
| 3490 | 3527 | markAllJoinsBadIf :: Bool -> LintM a -> LintM a
|
| 3491 | 3528 | markAllJoinsBadIf True m = markAllJoinsBad m
|
| 3492 | 3529 | markAllJoinsBadIf False m = m
|
| 3493 | 3530 | |
| 3494 | -getValidJoins :: LintM IdSet
|
|
| 3531 | +getValidJoins :: LintM (UniqMap Id JoinOcc)
|
|
| 3495 | 3532 | getValidJoins = LintM (\ env errs -> fromBoxedLResult (Just (le_joins env), errs))
|
| 3496 | 3533 | |
| 3497 | 3534 | getSubst :: LintM Subst
|
| ... | ... | @@ -3552,14 +3589,14 @@ lintVarOcc v_occ |
| 3552 | 3589 | | otherwise
|
| 3553 | 3590 | = return ()
|
| 3554 | 3591 | |
| 3555 | -lookupJoinId :: Id -> LintM JoinPointHood
|
|
| 3592 | +lookupJoinId :: Id -> LintM (Maybe (JoinArity, JoinOcc))
|
|
| 3556 | 3593 | -- Look up an Id which should be a join point, valid here
|
| 3557 | 3594 | -- If so, return its arity, if not return Nothing
|
| 3558 | 3595 | lookupJoinId id
|
| 3559 | - = do { join_set <- getValidJoins
|
|
| 3560 | - ; case lookupVarSet join_set id of
|
|
| 3561 | - Just id' -> return (idJoinPointHood id')
|
|
| 3562 | - Nothing -> return NotJoinPoint }
|
|
| 3596 | + = do { valid_joins <- getValidJoins
|
|
| 3597 | + ; case lookupUniqMap valid_joins id of
|
|
| 3598 | + Just join_occ -> return $ Just (idJoinArity id, join_occ)
|
|
| 3599 | + Nothing -> return Nothing }
|
|
| 3563 | 3600 | |
| 3564 | 3601 | addAliasUE :: OutId -> UsageEnv -> LintM a -> LintM a
|
| 3565 | 3602 | addAliasUE id ue thing_inside = LintM $ \ env errs ->
|
| ... | ... | @@ -90,7 +90,6 @@ import GHC.Utils.Misc |
| 90 | 90 | |
| 91 | 91 | import Data.List.NonEmpty ( nonEmpty )
|
| 92 | 92 | import qualified Data.List.NonEmpty as NE
|
| 93 | -import Data.Maybe( isJust )
|
|
| 94 | 93 | |
| 95 | 94 | {-
|
| 96 | 95 | ************************************************************************
|
| ... | ... | @@ -2835,21 +2834,6 @@ tryEtaReduce rec_ids bndrs body eval_sd |
| 2835 | 2834 | |
| 2836 | 2835 | ok_arg _ _ _ _ = Nothing
|
| 2837 | 2836 | |
| 2838 | --- | Can we eta-reduce the given function
|
|
| 2839 | --- See Note [Eta reduction soundness], criteria (B), (J), and (W).
|
|
| 2840 | -cantEtaReduceFun :: Id -> Bool
|
|
| 2841 | -cantEtaReduceFun fun
|
|
| 2842 | - = hasNoBinding fun -- (B)
|
|
| 2843 | - -- Don't undersaturate functions with no binding.
|
|
| 2844 | - |
|
| 2845 | - || isJoinId fun -- (J)
|
|
| 2846 | - -- Don't undersaturate join points.
|
|
| 2847 | - -- See Note [Invariants on join points] in GHC.Core, and #20599
|
|
| 2848 | - |
|
| 2849 | - || (isJust (idCbvMarks_maybe fun)) -- (W)
|
|
| 2850 | - -- Don't undersaturate StrictWorkerIds.
|
|
| 2851 | - -- See Note [CBV Function Ids: overview] in GHC.Types.Id.Info.
|
|
| 2852 | - |
|
| 2853 | 2837 | |
| 2854 | 2838 | {- *********************************************************************
|
| 2855 | 2839 | * *
|
| ... | ... | @@ -375,7 +375,7 @@ We don't float lets inwards past an SCC. |
| 375 | 375 | -}
|
| 376 | 376 | |
| 377 | 377 | fiExpr platform to_drop (_, AnnTick tickish expr)
|
| 378 | - | tickish `tickishScopesLike` SoftScope
|
|
| 378 | + | tickishHasSoftScope tickish
|
|
| 379 | 379 | = Tick tickish (fiExpr platform to_drop expr)
|
| 380 | 380 | |
| 381 | 381 | | otherwise -- Wimp out for now - we could push values in
|
| ... | ... | @@ -365,25 +365,28 @@ floatExpr lam@(Lam (TB _ lam_spec) _) |
| 365 | 365 | (add_to_stats fs floats, floats, mkLams bndrs body') }
|
| 366 | 366 | |
| 367 | 367 | floatExpr (Tick tickish expr)
|
| 368 | - | tickish `tickishScopesLike` SoftScope -- not scoped, can just float
|
|
| 368 | + -- If possible, float out past the tick
|
|
| 369 | + | let float_out_of_tick
|
|
| 370 | + -- See Note [Floating past breakpoints]
|
|
| 371 | + | Breakpoint{} <- tickish
|
|
| 372 | + = True
|
|
| 373 | + | otherwise
|
|
| 374 | + -- We can float code out of non-scoped ticks
|
|
| 375 | + = tickishHasNoScope tickish
|
|
| 376 | + , float_out_of_tick
|
|
| 369 | 377 | = case (floatExpr expr) of { (fs, floating_defns, expr') ->
|
| 370 | 378 | (fs, floating_defns, Tick tickish expr') }
|
| 371 | 379 | |
| 372 | - | not (tickishCounts tickish) || tickishCanSplit tickish
|
|
| 373 | - = case (floatExpr expr) of { (fs, floating_defns, expr') ->
|
|
| 374 | - let -- Annotate bindings floated outwards past an scc expression
|
|
| 375 | - -- with the cc. We mark that cc as "duplicated", though.
|
|
| 376 | - annotated_defns = wrapTick (mkNoCount tickish) floating_defns
|
|
| 380 | + -- We can't move code out of the tick
|
|
| 381 | + | otherwise
|
|
| 382 | + = assert (not (tickishCounts tickish) || tickishCanSplit tickish) $
|
|
| 383 | + case (floatExpr expr) of { (fs, floating_defns, expr') ->
|
|
| 384 | + -- Wrap floated code with the correct tick scope, but using 'mkNoCount'
|
|
| 385 | + -- to ensure we don't duplicate counters.
|
|
| 386 | + let annotated_defns = wrapTick (mkNoCount tickish) floating_defns
|
|
| 377 | 387 | in
|
| 378 | 388 | (fs, annotated_defns, Tick tickish expr') }
|
| 379 | 389 | |
| 380 | - -- See Note [Floating past breakpoints]
|
|
| 381 | - | Breakpoint{} <- tickish
|
|
| 382 | - = case (floatExpr expr) of { (fs, floating_defns, expr') ->
|
|
| 383 | - (fs, floating_defns, Tick tickish expr') }
|
|
| 384 | - |
|
| 385 | - | otherwise
|
|
| 386 | - = pprPanic "floatExpr tick" (ppr tickish)
|
|
| 387 | 390 | |
| 388 | 391 | floatExpr (Cast expr co)
|
| 389 | 392 | = case (floatExpr expr) of { (fs, floating_defns, expr') ->
|
| ... | ... | @@ -661,7 +664,8 @@ partitionByLevel (Level major minor) (FB tops defns) |
| 661 | 664 | |
| 662 | 665 | wrapTick :: CoreTickish -> FloatBinds -> FloatBinds
|
| 663 | 666 | wrapTick t (FB tops defns)
|
| 664 | - = FB (mapBag wrap_bind tops)
|
|
| 667 | + = assert (not $ tickishCounts t) $
|
|
| 668 | + FB (mapBag wrap_bind tops)
|
|
| 665 | 669 | (M.map (M.map wrap_defns) defns)
|
| 666 | 670 | where
|
| 667 | 671 | wrap_defns = mapBag wrap_one
|
| ... | ... | @@ -672,10 +676,13 @@ wrapTick t (FB tops defns) |
| 672 | 676 | wrap_one (FloatLet bind) = FloatLet (wrap_bind bind)
|
| 673 | 677 | wrap_one (FloatCase e b c bs) = FloatCase (maybe_tick e) b c bs
|
| 674 | 678 | |
| 675 | - maybe_tick e | exprIsHNF e = tickHNFArgs t e
|
|
| 676 | - | otherwise = mkTick t e
|
|
| 677 | - -- we don't need to wrap a tick around an HNF when we float it
|
|
| 678 | - -- outside a tick: that is an invariant of the tick semantics
|
|
| 679 | + maybe_tick
|
|
| 680 | + -- We don't need to wrap an SCC tick around HNFs that we floated out of
|
|
| 681 | + -- the SCC, as that is an invariant of the semantics for SCCs.
|
|
| 679 | 682 | -- Conversely, inlining of HNFs inside an SCC is allowed, and
|
| 680 | 683 | -- indeed the HNF we're floating here might well be inlined back
|
| 681 | 684 | -- again, and we don't want to end up with duplicate ticks.
|
| 685 | + | tickishPlace t == PlaceCostCentre
|
|
| 686 | + = mkTickNoHNF t
|
|
| 687 | + | otherwise
|
|
| 688 | + = mkTick t |
| ... | ... | @@ -27,7 +27,7 @@ core expression with (hopefully) improved usage information. |
| 27 | 27 | |
| 28 | 28 | module GHC.Core.Opt.OccurAnal (
|
| 29 | 29 | occurAnalysePgm,
|
| 30 | - occurAnalyseExpr,
|
|
| 30 | + occurAnalyseExpr, occurAnalyseExpr_Prep,
|
|
| 31 | 31 | zapLambdaBndrs
|
| 32 | 32 | ) where
|
| 33 | 33 | |
| ... | ... | @@ -85,6 +85,15 @@ occurAnalyseExpr expr = expr' |
| 85 | 85 | where
|
| 86 | 86 | WUD _ expr' = occAnal initOccEnv expr
|
| 87 | 87 | |
| 88 | +-- | A version of 'occurAnalyseExpr' suitable for CorePrep.
|
|
| 89 | +--
|
|
| 90 | +-- Different from 'occurAnalyseExpr' due to (JCT3)
|
|
| 91 | +-- in Note [Join points, casts, and ticks] in GHC.Core.
|
|
| 92 | +occurAnalyseExpr_Prep :: CoreExpr -> CoreExpr
|
|
| 93 | +occurAnalyseExpr_Prep expr = expr'
|
|
| 94 | + where
|
|
| 95 | + WUD _ expr' = occAnal (initOccEnv { occ_allow_weak_joins = True }) expr
|
|
| 96 | + |
|
| 88 | 97 | occurAnalysePgm :: Module -- Used only in debug output
|
| 89 | 98 | -> (Id -> Bool) -- Active unfoldings
|
| 90 | 99 | -> (ActivationGhc -> Bool) -- Active rules
|
| ... | ... | @@ -2300,12 +2309,8 @@ occ_anal_lam_tail env (Cast expr co) |
| 2300 | 2309 | Var {} | isRhsEnv env -> markAllMany usage1
|
| 2301 | 2310 | _ -> usage1
|
| 2302 | 2311 | |
| 2303 | - -- usage3: you might think this was not necessary, because of
|
|
| 2304 | - -- the markAllNonTail in adjustTailUsage; but not so! For a
|
|
| 2305 | - -- join point, adjustTailUsage doesn't do this; yet if there is
|
|
| 2306 | - -- a cast, we must! Also: why markAllNonTail? See
|
|
| 2307 | - -- GHC.Core.Lint: Note Note [Join points and casts]
|
|
| 2308 | - usage3 = markAllNonTail usage2
|
|
| 2312 | + -- usage3: see (JCT1) in Note [Join points, casts, and ticks] in GHC.Core.
|
|
| 2313 | + usage3 = markAllNonTail_CastOrTick env usage2
|
|
| 2309 | 2314 | |
| 2310 | 2315 | in WUD usage3 (Cast expr' co)
|
| 2311 | 2316 | |
| ... | ... | @@ -2587,42 +2592,39 @@ But it is not necessary to gather CoVars from the types of other binders. |
| 2587 | 2592 | -}
|
| 2588 | 2593 | |
| 2589 | 2594 | occAnal env (Tick tickish body)
|
| 2590 | - = WUD usage' (Tick tickish body')
|
|
| 2595 | + = WUD usage2 (Tick tickish body')
|
|
| 2591 | 2596 | where
|
| 2592 | 2597 | WUD usage body' = occAnal env body
|
| 2593 | 2598 | |
| 2594 | - usage'
|
|
| 2595 | - | tickish `tickishScopesLike` SoftScope
|
|
| 2596 | - = usage -- For soft-scoped ticks (including SourceNotes) we don't want
|
|
| 2597 | - -- to lose join-point-hood, so we don't mess with `usage` (#24078)
|
|
| 2599 | + usage1
|
|
| 2600 | + -- We don't want to lose join-point-hood. We can move soft-scoped ticks
|
|
| 2601 | + -- out of the way, so don't mess with `usage` (#24078).
|
|
| 2602 | + | tickishHasSoftScope tickish
|
|
| 2603 | + = usage
|
|
| 2598 | 2604 | |
| 2599 | - -- For a non-soft tick scope, we can inline lambdas only, so we
|
|
| 2600 | - -- abandon tail calls, and do markAllInsideLam too: usage_lam
|
|
| 2605 | + -- Otherwise, we can inline lambdas only, so use 'markAllInsideLam'.
|
|
| 2606 | + | otherwise
|
|
| 2607 | + = markAllNonTail_CastOrTick env $ markAllInsideLam usage
|
|
| 2608 | + -- markAllNonTail_CastOrTick: abandon tail calls.
|
|
| 2609 | + -- See (JCT2) in Note [Join points, casts, and ticks] in GHC.Core.
|
|
| 2601 | 2610 | |
| 2611 | + usage2
|
|
| 2602 | 2612 | | Breakpoint _ _ ids <- tickish
|
| 2603 | 2613 | = -- Never substitute for any of the Ids in a Breakpoint
|
| 2604 | - addManyOccs usage_lam (mkVarSet ids)
|
|
| 2614 | + addManyOccs usage1 (mkVarSet ids)
|
|
| 2605 | 2615 | |
| 2606 | 2616 | | otherwise
|
| 2607 | - = usage_lam
|
|
| 2608 | - |
|
| 2609 | - usage_lam = markAllNonTail (markAllInsideLam usage)
|
|
| 2610 | - |
|
| 2611 | - -- TODO There may be ways to make ticks and join points play
|
|
| 2612 | - -- nicer together, but right now there are problems:
|
|
| 2613 | - -- let j x = ... in tick<t> (j 1)
|
|
| 2614 | - -- Making j a join point may cause the simplifier to drop t
|
|
| 2615 | - -- (if the tick is put into the continuation). So we don't
|
|
| 2616 | - -- count j 1 as a tail call.
|
|
| 2617 | - -- See #14242.
|
|
| 2617 | + = usage1
|
|
| 2618 | 2618 | |
| 2619 | 2619 | occAnal env (Cast expr co)
|
| 2620 | - = let (WUD usage expr') = occAnal env expr
|
|
| 2621 | - usage1 = addManyOccs usage (coVarsOfCo co)
|
|
| 2622 | - -- usage2: see Note [Gather occurrences of coercion variables]
|
|
| 2623 | - usage2 = markAllNonTail usage1
|
|
| 2624 | - -- usage3: calls inside expr aren't tail calls any more
|
|
| 2625 | - in WUD usage2 (Cast expr' co)
|
|
| 2620 | + = let
|
|
| 2621 | + WUD usage expr' = occAnal env expr
|
|
| 2622 | + -- usage1: see Note [Gather occurrences of coercion variables]
|
|
| 2623 | + usage1 = addManyOccs usage (coVarsOfCo co)
|
|
| 2624 | + -- usage2: see (JCT1) in Note [Join points, casts, and ticks] in GHC.Core.
|
|
| 2625 | + usage2 = markAllNonTail_CastOrTick env usage1
|
|
| 2626 | + in
|
|
| 2627 | + WUD usage2 (Cast expr' co)
|
|
| 2626 | 2628 | |
| 2627 | 2629 | occAnal env app@(App _ _)
|
| 2628 | 2630 | = occAnalApp env (collectArgsTicks tickishFloatable app)
|
| ... | ... | @@ -2936,6 +2938,11 @@ data OccEnv |
| 2936 | 2938 | , occ_rule_act :: ActivationGhc -> Bool -- Which rules are active
|
| 2937 | 2939 | -- See Note [Finding rule RHS free vars]
|
| 2938 | 2940 | |
| 2941 | + , occ_allow_weak_joins :: !Bool
|
|
| 2942 | + -- ^ Allow a join point jump to occur inside casts or profiling ticks?
|
|
| 2943 | + --
|
|
| 2944 | + -- See (JCT3) in Note [Join points, casts, and ticks] in GHC.Core.Opt.
|
|
| 2945 | + |
|
| 2939 | 2946 | -- See Note [The binder-swap substitution]
|
| 2940 | 2947 | -- If x :-> (y, co) is in the env,
|
| 2941 | 2948 | -- then please replace x by (y |> mco)
|
| ... | ... | @@ -3003,6 +3010,8 @@ initOccEnv |
| 3003 | 3010 | , occ_unf_act = \_ -> True
|
| 3004 | 3011 | , occ_rule_act = \_ -> True
|
| 3005 | 3012 | |
| 3013 | + , occ_allow_weak_joins = False
|
|
| 3014 | + |
|
| 3006 | 3015 | , occ_join_points = emptyVarEnv
|
| 3007 | 3016 | , occ_bs_env = emptyVarEnv
|
| 3008 | 3017 | , occ_bs_rng = emptyVarSet
|
| ... | ... | @@ -3026,6 +3035,15 @@ setScrutCtxt !env alts |
| 3026 | 3035 | -- non-default alternative. That in turn influences
|
| 3027 | 3036 | -- pre/postInlineUnconditionally. Grep for "occ_int_cxt"!
|
| 3028 | 3037 | |
| 3038 | +-- | Mark occurrences under a cast/non-soft-scope tick as non-tail-called,
|
|
| 3039 | +-- except if 'occ_allow_weak_joins = True'.
|
|
| 3040 | +--
|
|
| 3041 | +-- See Note [Join points, casts, and ticks] in GHC.Core.
|
|
| 3042 | +markAllNonTail_CastOrTick :: OccEnv -> UsageDetails -> UsageDetails
|
|
| 3043 | +markAllNonTail_CastOrTick env =
|
|
| 3044 | + markAllNonTailIf
|
|
| 3045 | + (not $ occ_allow_weak_joins env)
|
|
| 3046 | + |
|
| 3029 | 3047 | {- Note [The OccEnv for a right hand side]
|
| 3030 | 3048 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 3031 | 3049 | How do we create the OccEnv for a RHS (in mkRhsOccEnv)?
|
| ... | ... | @@ -4075,7 +4093,10 @@ okForJoinPoint :: TopLevelFlag -> Id -> TailCallInfo -> Bool |
| 4075 | 4093 | -- See Note [Invariants on join points]; invariants cited by number below.
|
| 4076 | 4094 | -- Invariant 2 is always satisfiable by the simplifier by eta expansion.
|
| 4077 | 4095 | okForJoinPoint lvl bndr tail_call_info
|
| 4078 | - | isJoinId bndr -- A current join point should still be one!
|
|
| 4096 | + -- A current join point should still be one!
|
|
| 4097 | + --
|
|
| 4098 | + -- See Note [JoinId vs TailCallInfo] in GHC.Core.SimpleOpt.
|
|
| 4099 | + | isJoinId bndr
|
|
| 4079 | 4100 | = warnPprTrace lost_join "Lost join point" lost_join_doc $
|
| 4080 | 4101 | True
|
| 4081 | 4102 | | valid_join
|
| ... | ... | @@ -814,9 +814,9 @@ prepareRhs env top_lvl occ rhs0 |
| 814 | 814 | = return (emptyLetFloats, Var fun)
|
| 815 | 815 | |
| 816 | 816 | anfise (Tick t rhs)
|
| 817 | - -- We want to be able to float bindings past this
|
|
| 818 | - -- tick. Non-scoping ticks don't care.
|
|
| 819 | - | tickishScoped t == NoScope
|
|
| 817 | + -- We want to be able to float bindings past this tick.
|
|
| 818 | + -- Non-scoping ticks don't care.
|
|
| 819 | + | tickishHasNoScope t
|
|
| 820 | 820 | = do { (floats, rhs') <- anfise rhs
|
| 821 | 821 | ; return (floats, Tick t rhs') }
|
| 822 | 822 | |
| ... | ... | @@ -1413,7 +1413,7 @@ simplTick env tickish expr cont |
| 1413 | 1413 | -- bottom, then rebuildCall will discard the continuation.
|
| 1414 | 1414 | |
| 1415 | 1415 | --------------------------
|
| 1416 | --- | tickishScoped tickish && not (tickishCounts tickish)
|
|
| 1416 | +-- | not (tickishHasNoScope tickish) && not (tickishCounts tickish)
|
|
| 1417 | 1417 | -- = simplExprF env expr (TickIt tickish cont)
|
| 1418 | 1418 | -- XXX: we cannot do this, because the simplifier assumes that
|
| 1419 | 1419 | -- the context can be pushed into a case with a single branch. e.g.
|
| ... | ... | @@ -1425,12 +1425,11 @@ simplTick env tickish expr cont |
| 1425 | 1425 | -- simplifier iterations that necessary in some cases.
|
| 1426 | 1426 | --------------------------
|
| 1427 | 1427 | |
| 1428 | - -- For unscoped or soft-scoped ticks, we are allowed to float in new
|
|
| 1429 | - -- cost, so we simply push the continuation inside the tick. This
|
|
| 1430 | - -- has the effect of moving the tick to the outside of a case or
|
|
| 1431 | - -- application context, allowing the normal case and application
|
|
| 1432 | - -- optimisations to fire.
|
|
| 1433 | - | tickish `tickishScopesLike` SoftScope
|
|
| 1428 | + -- For soft-scoped ticks, we are allowed to float in new cost, so we simply
|
|
| 1429 | + -- push the continuation inside the tick. This has the effect of moving the
|
|
| 1430 | + -- tick to the outside of a case or application context, allowing the normal
|
|
| 1431 | + -- 'case' and 'application' optimisations to fire.
|
|
| 1432 | + | tickishHasSoftScope tickish
|
|
| 1434 | 1433 | = do { (floats, expr') <- simplExprF env expr cont
|
| 1435 | 1434 | ; return (floats, mkTick tickish expr')
|
| 1436 | 1435 | }
|
| ... | ... | @@ -1459,14 +1458,14 @@ simplTick env tickish expr cont |
| 1459 | 1458 | _other -> Nothing
|
| 1460 | 1459 | where (ticks, expr0) = stripTicksTop movable (Tick tickish expr)
|
| 1461 | 1460 | movable t = not (tickishCounts t) ||
|
| 1462 | - t `tickishScopesLike` NoScope ||
|
|
| 1461 | + tickishHasNoScope t ||
|
|
| 1463 | 1462 | tickishCanSplit t
|
| 1464 | 1463 | tickScrut e = foldr mkTick e ticks
|
| 1465 | 1464 | -- Alternatives get annotated with all ticks that scope in some way,
|
| 1466 | 1465 | -- but we don't want to count entries.
|
| 1467 | 1466 | tickAlt (Alt c bs e) = Alt c bs (foldr mkTick e ts_scope)
|
| 1468 | 1467 | ts_scope = map mkNoCount $
|
| 1469 | - filter (not . (`tickishScopesLike` NoScope)) ticks
|
|
| 1468 | + filter (not . tickishHasNoScope) ticks
|
|
| 1470 | 1469 | |
| 1471 | 1470 | no_floating_past_tick =
|
| 1472 | 1471 | do { let (inc,outc) = splitCont cont
|
| ... | ... | @@ -2180,16 +2179,15 @@ evaluation context E): |
| 2180 | 2179 | |
| 2181 | 2180 | As is evident from the example, there are two components to this behavior:
|
| 2182 | 2181 | |
| 2183 | - 1. When entering the RHS of a join point, copy the context inside.
|
|
| 2184 | - 2. When a join point is invoked, discard the outer context.
|
|
| 2182 | + (wrapJoinCont) When entering the RHS of a join point, copy the context inside.
|
|
| 2183 | + (trimJoinCont) When a join point is invoked, discard the outer context.
|
|
| 2185 | 2184 | |
| 2186 | 2185 | We need to be very careful here to remain consistent---neither part is
|
| 2187 | 2186 | optional!
|
| 2188 | 2187 | |
| 2189 | -We need do make the continuation E duplicable (since we are duplicating it)
|
|
| 2188 | +We need to make the continuation E duplicable (since we are duplicating it)
|
|
| 2190 | 2189 | with mkDupableCont.
|
| 2191 | 2190 | |
| 2192 | - |
|
| 2193 | 2191 | Note [Join points with -fno-case-of-case]
|
| 2194 | 2192 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 2195 | 2193 | Supose case-of-case is switched off, and we are simplifying
|
| ... | ... | @@ -2213,7 +2211,8 @@ case-of-case we may then end up with this totally bogus result |
| 2213 | 2211 | This would be OK in the language of the paper, but not in GHC: j is no longer
|
| 2214 | 2212 | a join point. We can only do the "push continuation into the RHS of the
|
| 2215 | 2213 | join point j" if we also push the continuation right down to the /jumps/ to
|
| 2216 | -j, so that it can evaporate there. If we are doing case-of-case, we'll get to
|
|
| 2214 | +j, so that it can evaporate there (trimJoinCont). Then, if we are doing
|
|
| 2215 | +case-of-case, we'll get to:
|
|
| 2217 | 2216 | |
| 2218 | 2217 | join x = case <j-rhs> of <outer-alts> in
|
| 2219 | 2218 | case y of
|
| ... | ... | @@ -3656,9 +3655,11 @@ addBinderUnfolding env bndr unf |
| 3656 | 3655 | = modifyInScope env (bndr `setIdUnfolding` unf)
|
| 3657 | 3656 | |
| 3658 | 3657 | zapBndrOccInfo :: Bool -> Id -> Id
|
| 3659 | --- Consider case e of b { (a,b) -> ... }
|
|
| 3660 | --- Then if we bind b to (a,b) in "...", and b is not dead,
|
|
| 3661 | --- then we must zap the deadness info on a,b
|
|
| 3658 | +-- ^ Consider:
|
|
| 3659 | +-- > case e of e' { (a,b) -> rhs }
|
|
| 3660 | +--
|
|
| 3661 | +-- We bind @e'@ to @(a,b)@ in @rhs@. If @e'@ is not dead,
|
|
| 3662 | +-- then we must zap the deadness info on @a@ and @b@.
|
|
| 3662 | 3663 | zapBndrOccInfo keep_occ_info pat_id
|
| 3663 | 3664 | | keep_occ_info = pat_id
|
| 3664 | 3665 | | otherwise = zapIdOccInfo pat_id
|
| ... | ... | @@ -437,7 +437,7 @@ simple_app env e@(Lam {}) [] |
| 437 | 437 | |
| 438 | 438 | simple_app env (Tick t e) as
|
| 439 | 439 | -- Okay to do "(Tick t e) x ==> Tick t (e x)"?
|
| 440 | - | t `tickishScopesLike` SoftScope
|
|
| 440 | + | tickishHasSoftScope t
|
|
| 441 | 441 | = mkTick t $ simple_app env e as
|
| 442 | 442 | |
| 443 | 443 | -- (let x = e in b) a1 .. an => let x = e in (b a1 .. an)
|
| ... | ... | @@ -1059,23 +1059,33 @@ and again its arity increases (#15517) |
| 1059 | 1059 | -}
|
| 1060 | 1060 | |
| 1061 | 1061 | |
| 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.
|
|
| 1062 | +-- | Returns @Just (bndr, rhs)@ if the binding is a join point, or can be made
|
|
| 1063 | +-- into a join poin. Returns @Nothing@ otherwise.
|
|
| 1064 | +--
|
|
| 1065 | +-- - If the input binder is a 'JoinId', just return it;
|
|
| 1066 | +-- - if it's not yet a 'JoinId' but is always tail-called,
|
|
| 1067 | +-- make it into a 'JoinId' and return that.
|
|
| 1068 | +--
|
|
| 1066 | 1069 | -- In the latter case, eta-expand the RHS if necessary, to make the
|
| 1067 | --- lambdas explicit, as is required for join points
|
|
| 1070 | +-- lambdas explicit, as is required for join points.
|
|
| 1071 | +--
|
|
| 1072 | +-- Precondition: the 'TailCallInfo' of the 'InBndr' is conservative:
|
|
| 1068 | 1073 | --
|
| 1069 | --- Precondition: the InBndr has been occurrence-analysed,
|
|
| 1070 | --- so its OccInfo is valid
|
|
| 1074 | +-- - if it says 'AlwaysTailCalled', it is definitely always tail called,
|
|
| 1075 | +-- - if it says 'NoTailCallInfo', then we're not sure.
|
|
| 1076 | +--
|
|
| 1077 | +-- See Note [JoinId vs TailCallInfo].
|
|
| 1071 | 1078 | joinPointBinding_maybe :: InBndr -> InExpr -> Maybe (InBndr, InExpr)
|
| 1072 | 1079 | joinPointBinding_maybe bndr rhs
|
| 1073 | 1080 | | not (isId bndr)
|
| 1074 | 1081 | = Nothing
|
| 1075 | 1082 | |
| 1083 | + -- Being a JoinId is robust: preserve that. See Note [JoinId vs TailCallInfo].
|
|
| 1076 | 1084 | | isJoinId bndr
|
| 1077 | 1085 | = Just (bndr, rhs)
|
| 1078 | 1086 | |
| 1087 | + -- If the 'TailCallInfo' of 'bndr' says 'AlwaysTailCalled', then we know for
|
|
| 1088 | + -- sure that it can be made into a join point.
|
|
| 1079 | 1089 | | AlwaysTailCalled join_arity <- tailCallInfo (idOccInfo bndr)
|
| 1080 | 1090 | , (bndrs, body) <- etaExpandToJoinPoint join_arity rhs
|
| 1081 | 1091 | , let str_sig = idDmdSig bndr
|
| ... | ... | @@ -1091,6 +1101,48 @@ joinPointBindings_maybe :: [(InBndr, InExpr)] -> Maybe [(InBndr, InExpr)] |
| 1091 | 1101 | joinPointBindings_maybe bndrs
|
| 1092 | 1102 | = mapM (uncurry joinPointBinding_maybe) bndrs
|
| 1093 | 1103 | |
| 1104 | +{- Note [JoinId vs TailCallInfo]
|
|
| 1105 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1106 | +* Occurrence information is /fundamentally fragile/; that is, it may
|
|
| 1107 | + be invalidated by the Simplifier.
|
|
| 1108 | + Example 1:
|
|
| 1109 | + \y -> let x = y in ...x..x...
|
|
| 1110 | + Here `y` is marked "occurs exactly once" but, after inlining `x`,
|
|
| 1111 | + `y` now occurs many times.
|
|
| 1112 | + Example 2:
|
|
| 1113 | + f (let h x = ... in case y of { True -> h 1; False -> h 2 })
|
|
| 1114 | + Here `h` is tail-called; but if `f` is strict we could transform to
|
|
| 1115 | + let h x = ... in
|
|
| 1116 | + case y of { True -> f (h 1); False -> f (h 2) }
|
|
| 1117 | + Now `h` is not tail called any more.
|
|
| 1118 | + |
|
| 1119 | + Exception: Dead things (with no occurrences) usually stay dead.
|
|
| 1120 | + There are exceptions e.g.
|
|
| 1121 | + case x of y { (a,b) -> case y of (p,q) -> p }
|
|
| 1122 | + Here `a` and `b` look dead, but we may well transform to
|
|
| 1123 | + case x of y { (a,b) -> a }
|
|
| 1124 | + |
|
| 1125 | + Because occurrence info is fragile, we recompute occurrence info
|
|
| 1126 | + (including tail call info) before each run of the Simplifier.
|
|
| 1127 | + |
|
| 1128 | + Whenever the simplifier performs a transformation that **might** invalidate
|
|
| 1129 | + occurrence information, it calls 'zapFragileIdInfo'. This sets the
|
|
| 1130 | + 'TailCallInfo' to 'NoTailCallInfo' (among other things).
|
|
| 1131 | + |
|
| 1132 | +* Being a JoinId is /robust/, and is rigorously maintained by the
|
|
| 1133 | + Simplifier. In Example 2 above, if `h` was marked as a JoinId,
|
|
| 1134 | + that transformation would not have happened. Instead we'd have
|
|
| 1135 | + transformed to
|
|
| 1136 | + let h x = f (...) in
|
|
| 1137 | + case y of { True -> h 1; False -> h 2 }
|
|
| 1138 | + |
|
| 1139 | + The Simplifier takes an Id whose occurrences are marked as
|
|
| 1140 | + `AlwaysTailCalled` and turns it into robust `JoinId`. This is
|
|
| 1141 | + done by `joinPointBinding_maybe`.
|
|
| 1142 | + |
|
| 1143 | + There is one exception: float-out, the only caller of 'zapJoinId'.
|
|
| 1144 | + See Note [Zapping JoinId when floating].
|
|
| 1145 | +-}
|
|
| 1094 | 1146 | |
| 1095 | 1147 | {- *********************************************************************
|
| 1096 | 1148 | * *
|
| ... | ... | @@ -34,7 +34,8 @@ module GHC.Core.Utils ( |
| 34 | 34 | exprIsTickedString, exprIsTickedString_maybe,
|
| 35 | 35 | exprIsTopLevelBindable,
|
| 36 | 36 | exprIsUnaryClassFun, isUnaryClassId,
|
| 37 | - altsAreExhaustive, etaExpansionTick,
|
|
| 37 | + altsAreExhaustive,
|
|
| 38 | + canCollectArgsThroughTick, cantEtaReduceFun,
|
|
| 38 | 39 | |
| 39 | 40 | -- * Equality
|
| 40 | 41 | cheapEqExpr, cheapEqExpr', diffBinds,
|
| ... | ... | @@ -680,7 +681,7 @@ mergeCaseAlts :: CoreExpr -> Id -> [CoreAlt] -> Maybe ([CoreBind], [CoreAlt]) |
| 680 | 681 | mergeCaseAlts scrut outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts)
|
| 681 | 682 | | Just (joins, inner_alts) <- go deflt_rhs
|
| 682 | 683 | , Just aux_binds <- mk_aux_binds joins
|
| 683 | - = Just ( aux_binds ++ joins, mergeAlts outer_alts inner_alts )
|
|
| 684 | + = Just (aux_binds ++ joins, mergeAlts outer_alts inner_alts )
|
|
| 684 | 685 | -- NB: mergeAlts gives priority to the left
|
| 685 | 686 | -- case x of
|
| 686 | 687 | -- A -> e1
|
| ... | ... | @@ -727,7 +728,7 @@ mergeCaseAlts scrut outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts) |
| 727 | 728 | , Just tc <- tyConAppTyCon_maybe type_arg
|
| 728 | 729 | , Just (dc1:dcs) <- tyConDataCons_maybe tc -- At least one data constructor
|
| 729 | 730 | , dcs `lengthAtMost` 3 -- Arbitrary
|
| 730 | - = return ( [], mk_alts dc1 dcs)
|
|
| 731 | + = return ([], mk_alts dc1 dcs)
|
|
| 731 | 732 | where
|
| 732 | 733 | mk_lit dc = mkLitIntUnchecked $ toInteger $ dataConTagZ dc
|
| 733 | 734 | mk_rhs dc = Var (dataConWorkId dc)
|
| ... | ... | @@ -748,11 +749,16 @@ mergeCaseAlts scrut outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts) |
| 748 | 749 | | otherwise
|
| 749 | 750 | = Nothing
|
| 750 | 751 | |
| 751 | - -- We don't want ticks to get in the way; just push them inwards.
|
|
| 752 | - -- (This happens when you add SourceTicks e.g. GHC.Num.Integer.integerLt#)
|
|
| 752 | + -- Push ticks **inwards** (when possible).
|
|
| 753 | + -- See (MC5) in Note [Merge Nested Cases].
|
|
| 753 | 754 | go (Tick t body)
|
| 754 | - = do { (joins, alts) <- go body
|
|
| 755 | - ; return (joins, [Alt con bs (Tick t rhs) | Alt con bs rhs <- alts]) }
|
|
| 755 | + = do { (joins, alts) <- go body -- (MC4): any join points inside are floated out of the tick.
|
|
| 756 | + |
|
| 757 | + -- Abort if this would put a non-soft-scope tick in between
|
|
| 758 | + -- a join point binding and its jumps. See (MC6).
|
|
| 759 | + ; guard $ null joins || tickishHasSoftScope t
|
|
| 760 | + ; return (joins, [Alt con bs (mkTick t rhs) | Alt con bs rhs <- alts])
|
|
| 761 | + }
|
|
| 756 | 762 | |
| 757 | 763 | go _ = Nothing
|
| 758 | 764 | |
| ... | ... | @@ -974,12 +980,74 @@ Wrinkles |
| 974 | 980 | |
| 975 | 981 | So `mergeCaseAlts` floats out any join points. It doesn't float out
|
| 976 | 982 | non-join-points unless the /outer/ case has just one alternative; doing
|
| 977 | - so would risk more allocation
|
|
| 983 | + so would risk more allocation.
|
|
| 984 | + |
|
| 985 | + Note also that `mergeCaseAlts` floats join points out of ticks, for which
|
|
| 986 | + we need to be extra careful; see (MC6).
|
|
| 978 | 987 | |
| 979 | 988 | Floating out join points isn't entirely straightforward.
|
| 980 | 989 | See Note [Floating join points out of DEFAULT alternatives]
|
| 981 | 990 | |
| 982 | -(MC5) See Note [Cascading case merge]
|
|
| 991 | +(MC5) We want to move ticks out of the way if possible, to prevent them from
|
|
| 992 | + inhibiting optimisation. For example, say we have:
|
|
| 993 | + |
|
| 994 | + case expensive of r {
|
|
| 995 | + C1 -> rhs1; -- happy path
|
|
| 996 | + _ -> scctick<doEdgeCase> (case r of { C2 -> rhs2; C3 -> rhs3 })
|
|
| 997 | + }
|
|
| 998 | + |
|
| 999 | + In this situation, we push the "doEdgeCase" tick **inwards** and proceed
|
|
| 1000 | + to merge cases, like so:
|
|
| 1001 | + |
|
| 1002 | + case expensive of
|
|
| 1003 | + C1 -> rhs1
|
|
| 1004 | + C2 -> scctick<doEdgeCase> rhs2
|
|
| 1005 | + C3 -> scctick<doEdgeCase> rhs3
|
|
| 1006 | + |
|
| 1007 | + This preserves the tick semantics (see Note [Scoping ticks and counting ticks]
|
|
| 1008 | + in GHC.Types.Tickish), because this transformation:
|
|
| 1009 | + |
|
| 1010 | + 1. preserves counts,
|
|
| 1011 | + 2. does not move cost in or out of the tick scope.
|
|
| 1012 | + |
|
| 1013 | + (1) is clear: we will tick 'doEdgeCase' exactly in the C2/C3 alternatives,
|
|
| 1014 | + and we won't otherwise.
|
|
| 1015 | + For (2), recall that case is strict in Core. We already evaluated 'expensive',
|
|
| 1016 | + so re-scrutinising 'r' is free.
|
|
| 1017 | + |
|
| 1018 | + This means that, perhaps surprisingly, this transformation is valid for
|
|
| 1019 | + **all** ticks, including non-floatable ones.
|
|
| 1020 | + |
|
| 1021 | + In contrast, we would not want to move the tick outwards, because this:
|
|
| 1022 | + |
|
| 1023 | + - will lead to additional counting of 'doEdgeCase' in the 'C1' (happy path) case,
|
|
| 1024 | + - risks attributing the cost of evaluating 'expensive' to 'doEdgeCase'.
|
|
| 1025 | + |
|
| 1026 | +(MC6) There is a dangerous interaction between (MC4) and (MC5), which can lead
|
|
| 1027 | + to invalid Core (as reported in #26642, #26929). Suppose we have:
|
|
| 1028 | + |
|
| 1029 | + case f x of r ->
|
|
| 1030 | + scctick<foo>
|
|
| 1031 | + join j y = rhs in
|
|
| 1032 | + case r of { C1 -> j 1; C2 -> bar }
|
|
| 1033 | + |
|
| 1034 | + If we naively carried out (MC4) and (MC5) together, this would result in:
|
|
| 1035 | + |
|
| 1036 | + join j y = rhs in
|
|
| 1037 | + case f x of
|
|
| 1038 | + C1 -> scctick<foo> (j 1)
|
|
| 1039 | + C2 -> scctick<foo> bar
|
|
| 1040 | + |
|
| 1041 | + This has moved the tick in between the join point binding 'j' and the
|
|
| 1042 | + join point jump, which is invalid as per Note [Join points, casts, and ticks]
|
|
| 1043 | + in GHC.Core. The simplifier cannot deal with such Core, resulting in #26642.
|
|
| 1044 | + |
|
| 1045 | + The solution: abort whenever we would position a non-soft-scope tick
|
|
| 1046 | + inside a join point in this manner.
|
|
| 1047 | + An alternative would be to float the tick outwards, but as we saw in (MC5)
|
|
| 1048 | + this risks a grave misattribution of profiling costs, so we don't do that.
|
|
| 1049 | + |
|
| 1050 | +(MC7) See Note [Cascading case merge]
|
|
| 983 | 1051 | |
| 984 | 1052 | See also Note [Example of case-merging and caseRules] in GHC.Core.Opt.Simplify.Utils
|
| 985 | 1053 | |
| ... | ... | @@ -2076,14 +2144,31 @@ altsAreExhaustive (Alt con1 _ _ : alts) |
| 2076 | 2144 | -- we behave conservatively here -- I don't think it's important
|
| 2077 | 2145 | -- enough to deserve special treatment
|
| 2078 | 2146 | |
| 2079 | --- | Should we look past this tick when eta-expanding the given function?
|
|
| 2147 | +-- | Should we look past this tick when collecting arguments
|
|
| 2148 | +-- for the given function?
|
|
| 2080 | 2149 | --
|
| 2081 | 2150 | -- See Note [Ticks and mandatory eta expansion]
|
| 2082 | --- Takes the function we are applying as argument.
|
|
| 2083 | -etaExpansionTick :: Id -> GenTickish pass -> Bool
|
|
| 2084 | -etaExpansionTick id t
|
|
| 2085 | - = hasNoBinding id &&
|
|
| 2086 | - ( tickishFloatable t || isProfTick t )
|
|
| 2151 | +canCollectArgsThroughTick
|
|
| 2152 | + :: Id -- ^ function at the head of the application
|
|
| 2153 | + -> GenTickish pass -- ^ tick we want to collect arguments past
|
|
| 2154 | + -> Bool
|
|
| 2155 | +canCollectArgsThroughTick id t
|
|
| 2156 | + = tickishFloatable t || cantEtaReduceFun id
|
|
| 2157 | + |
|
| 2158 | +-- | Can we eta-reduce the given function?
|
|
| 2159 | +-- See Note [Eta reduction soundness], criteria (B), (J), and (W).
|
|
| 2160 | +cantEtaReduceFun :: Id -> Bool
|
|
| 2161 | +cantEtaReduceFun fun
|
|
| 2162 | + = hasNoBinding fun -- (B)
|
|
| 2163 | + -- Don't undersaturate functions with no binding.
|
|
| 2164 | + |
|
| 2165 | + || isJoinId fun -- (J)
|
|
| 2166 | + -- Don't undersaturate join points.
|
|
| 2167 | + -- See Note [Invariants on join points] in GHC.Core, and #20599
|
|
| 2168 | + |
|
| 2169 | + || isJust (idCbvMarks_maybe fun) -- (W)
|
|
| 2170 | + -- Don't undersaturate StrictWorkerIds.
|
|
| 2171 | + -- See Note [CBV Function Ids: overview] in GHC.Types.Id.Info.
|
|
| 2087 | 2172 | |
| 2088 | 2173 | {- Note [exprOkForSpeculation and type classes]
|
| 2089 | 2174 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -39,7 +39,8 @@ import GHC.Core.Type |
| 39 | 39 | import GHC.Core.Coercion
|
| 40 | 40 | import GHC.Core.TyCon
|
| 41 | 41 | import GHC.Core.DataCon
|
| 42 | -import GHC.Core.Opt.OccurAnal
|
|
| 42 | +import GHC.Core.Opt.OccurAnal ( occurAnalyseExpr_Prep )
|
|
| 43 | +import GHC.Core.SimpleOpt ( joinPointBinding_maybe, joinPointBindings_maybe )
|
|
| 43 | 44 | |
| 44 | 45 | import GHC.Data.Maybe
|
| 45 | 46 | import GHC.Data.OrdList
|
| ... | ... | @@ -575,7 +576,18 @@ cpeBind :: TopLevelFlag -> CorePrepEnv -> CoreBind |
| 575 | 576 | Maybe CoreBind) -- Just bind' <=> returned new bind; no float
|
| 576 | 577 | -- Nothing <=> added bind' to floats instead
|
| 577 | 578 | cpeBind top_lvl env (NonRec bndr rhs)
|
| 578 | - | not (isJoinId bndr)
|
|
| 579 | + -- A join point.
|
|
| 580 | + -- NB: use 'joinPointBinding_maybe' instead of 'isJoinId' as per the plan
|
|
| 581 | + -- described in (JCT3) in Note [Join points, casts, and ticks].
|
|
| 582 | + | Just (bndr, rhs) <- joinPointBinding_maybe bndr rhs
|
|
| 583 | + = assert (not (isTopLevel top_lvl)) $ -- can't have top-level join point; see Note [Join points and floating]
|
|
| 584 | + do { (_, bndr1) <- cpCloneBndr env bndr
|
|
| 585 | + ; (bndr2, rhs1) <- cpeJoinPair env bndr1 rhs
|
|
| 586 | + ; return (extendCorePrepEnv env bndr bndr2,
|
|
| 587 | + emptyFloats,
|
|
| 588 | + Just (NonRec bndr2 rhs1)) }
|
|
| 589 | + |
|
| 590 | + | otherwise
|
|
| 579 | 591 | = do { (env1, bndr1) <- cpCloneBndr env bndr
|
| 580 | 592 | ; let dmd = idDemandInfo bndr
|
| 581 | 593 | lev = typeLevity (idType bndr)
|
| ... | ... | @@ -594,16 +606,23 @@ cpeBind top_lvl env (NonRec bndr rhs) |
| 594 | 606 | |
| 595 | 607 | ; return (env2, floats1, Nothing) }
|
| 596 | 608 | |
| 597 | - | otherwise -- A join point; see Note [Join points and floating]
|
|
| 598 | - = assert (not (isTopLevel top_lvl)) $ -- can't have top-level join point
|
|
| 599 | - do { (_, bndr1) <- cpCloneBndr env bndr
|
|
| 600 | - ; (bndr2, rhs1) <- cpeJoinPair env bndr1 rhs
|
|
| 601 | - ; return (extendCorePrepEnv env bndr bndr2,
|
|
| 602 | - emptyFloats,
|
|
| 603 | - Just (NonRec bndr2 rhs1)) }
|
|
| 604 | - |
|
| 605 | 609 | cpeBind top_lvl env (Rec pairs)
|
| 606 | - | not (isJoinId (head bndrs))
|
|
| 610 | + -- A recursive join point.
|
|
| 611 | + -- NB: use 'joinPointBindings_maybe' instead of 'isJoinId' as per the plan
|
|
| 612 | + -- described in (JCT3) in Note [Join points, casts, and ticks].
|
|
| 613 | + | Just pairs <- joinPointBindings_maybe pairs
|
|
| 614 | + , let (bndrs, rhss) = unzip pairs
|
|
| 615 | + = do { (env, bndrs1) <- cpCloneBndrs env bndrs
|
|
| 616 | + ; let env' = enterRecGroupRHSs env bndrs1
|
|
| 617 | + ; pairs1 <- zipWithM (cpeJoinPair env') bndrs1 rhss
|
|
| 618 | + |
|
| 619 | + ; let bndrs2 = map fst pairs1
|
|
| 620 | + -- use env below, so that we reset cpe_rec_ids
|
|
| 621 | + ; return (extendCorePrepEnvList env (bndrs `zip` bndrs2),
|
|
| 622 | + emptyFloats,
|
|
| 623 | + Just (Rec pairs1)) }
|
|
| 624 | + | otherwise
|
|
| 625 | + , let (bndrs, rhss) = unzip pairs
|
|
| 607 | 626 | = do { (env, bndrs1) <- cpCloneBndrs env bndrs
|
| 608 | 627 | ; let env' = enterRecGroupRHSs env bndrs1
|
| 609 | 628 | ; stuff <- zipWithM (cpePair top_lvl Recursive topDmd Lifted env')
|
| ... | ... | @@ -626,19 +645,9 @@ cpeBind top_lvl env (Rec pairs) |
| 626 | 645 | (Float (Rec all_pairs) LetBound TopLvlFloatable),
|
| 627 | 646 | Nothing) }
|
| 628 | 647 | |
| 629 | - | otherwise -- See Note [Join points and floating]
|
|
| 630 | - = do { (env, bndrs1) <- cpCloneBndrs env bndrs
|
|
| 631 | - ; let env' = enterRecGroupRHSs env bndrs1
|
|
| 632 | - ; pairs1 <- zipWithM (cpeJoinPair env') bndrs1 rhss
|
|
| 633 | - |
|
| 634 | - ; let bndrs2 = map fst pairs1
|
|
| 635 | - -- use env below, so that we reset cpe_rec_ids
|
|
| 636 | - ; return (extendCorePrepEnvList env (bndrs `zip` bndrs2),
|
|
| 637 | - emptyFloats,
|
|
| 638 | - Just (Rec pairs1)) }
|
|
| 639 | 648 | where
|
| 640 | - (bndrs, rhss) = unzip pairs
|
|
| 641 | - |
|
| 649 | + -- See Note [Join points and floating]
|
|
| 650 | + --
|
|
| 642 | 651 | -- Flatten all the floats, and the current
|
| 643 | 652 | -- group into a single giant Rec
|
| 644 | 653 | add_float (Float bind bound _) prs2
|
| ... | ... | @@ -653,7 +662,6 @@ cpeBind top_lvl env (Rec pairs) |
| 653 | 662 | Rec prs1 -> prs1 ++ prs2
|
| 654 | 663 | add_float f _ = pprPanic "cpeBind" (ppr f)
|
| 655 | 664 | |
| 656 | - |
|
| 657 | 665 | ---------------
|
| 658 | 666 | cpePair :: TopLevelFlag -> RecFlag -> Demand -> Levity
|
| 659 | 667 | -> CorePrepEnv -> OutId -> CoreExpr
|
| ... | ... | @@ -661,7 +669,7 @@ cpePair :: TopLevelFlag -> RecFlag -> Demand -> Levity |
| 661 | 669 | -- Used for all bindings
|
| 662 | 670 | -- The binder is already cloned, hence an OutId
|
| 663 | 671 | cpePair top_lvl is_rec dmd lev env0 bndr rhs
|
| 664 | - = assert (not (isJoinId bndr)) $ -- those should use cpeJoinPair
|
|
| 672 | + = assert (isNothing $ joinPointBinding_maybe bndr rhs) $ -- those should use cpeJoinPair
|
|
| 665 | 673 | do { (floats1, rhs1) <- cpeRhsE env rhs
|
| 666 | 674 | |
| 667 | 675 | -- See if we are allowed to float this stuff out of the RHS
|
| ... | ... | @@ -926,7 +934,7 @@ rhsToBody :: CorePrepEnv -> CpeRhs -> UniqSM (Floats, CpeBody) |
| 926 | 934 | -- Remove top level lambdas by let-binding
|
| 927 | 935 | |
| 928 | 936 | rhsToBody env (Tick t expr)
|
| 929 | - | tickishScoped t == NoScope -- only float out of non-scoped annotations
|
|
| 937 | + | tickishHasNoScope t -- only float out of non-scoped annotations
|
|
| 930 | 938 | = do { (floats, expr') <- rhsToBody env expr
|
| 931 | 939 | ; return (floats, mkTick t expr') }
|
| 932 | 940 | |
| ... | ... | @@ -984,43 +992,74 @@ instance Outputable ArgInfo where |
| 984 | 992 | |
| 985 | 993 | {- Note [Ticks and mandatory eta expansion]
|
| 986 | 994 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 987 | -Something like
|
|
| 988 | - `foo x = ({-# SCC foo #-} tagToEnum#) x :: Bool`
|
|
| 989 | -caused a compiler panic in #20938. Why did this happen?
|
|
| 990 | -The simplifier will eta-reduce the rhs giving us a partial
|
|
| 991 | -application of tagToEnum#. The tick is then pushed inside the
|
|
| 992 | -type argument. That is we get
|
|
| 993 | - `(Tick<foo> tagToEnum#) @Bool`
|
|
| 995 | +We must look through ticks when they get in the way of seeing the arguments to
|
|
| 996 | +'Id's that cannot be eta-reduced.
|
|
| 997 | + |
|
| 998 | +For example, we may have
|
|
| 999 | + |
|
| 1000 | + myReallyUnsafePtrEquality
|
|
| 1001 | + = \ @a x y ->
|
|
| 1002 | + (src<loc> reallyUnsafePtrEquality#)
|
|
| 1003 | + @Lifted @a @Lifted @a x y
|
|
| 1004 | + |
|
| 1005 | +If we don't move the SourceNote out of the way, this looks like an unsaturated
|
|
| 1006 | +occurrence of the PrimOp "reallyUnsafePtrEquality#", which we cannot generate
|
|
| 1007 | +code for.
|
|
| 1008 | + |
|
| 1009 | +Moreover, we must also move out non-floatable ticks. Case in point: #20938,
|
|
| 1010 | +of the form:
|
|
| 1011 | + |
|
| 1012 | + foo x = ({-# SCC foo #-} tagToEnum#) x :: Bool
|
|
| 1013 | + |
|
| 1014 | +If we don't look past the tick "foo", the simplifier will eta-reduce the RHS,
|
|
| 1015 | +giving us a partial application of 'tagToEnum#'. The tick is then pushed inside
|
|
| 1016 | +the type argument, resulting in:
|
|
| 1017 | + |
|
| 1018 | + (Tick<foo> tagToEnum#) @Bool
|
|
| 1019 | + |
|
| 994 | 1020 | CorePrep would go on to see a undersaturated tagToEnum# application
|
| 995 | -and eta expand the expression under the tick. Giving us:
|
|
| 1021 | +and eta-expand the expression under the tick. Giving us:
|
|
| 1022 | + |
|
| 996 | 1023 | (Tick<scc> (\forall a. x -> tagToEnum# @a x) @Bool
|
| 997 | -Suddenly tagToEnum# is applied to a polymorphic type and the code generator
|
|
| 1024 | + |
|
| 1025 | +Suddenly, 'tagToEnum#' is applied to a polymorphic type and the code generator
|
|
| 998 | 1026 | panics as it needs a concrete type to determine the representation.
|
| 999 | 1027 | |
| 1000 | -The problem in my eyes was that the tick covers a partial application
|
|
| 1001 | -of a primop. There is no clear semantic for such a construct as we can't
|
|
| 1002 | -partially apply a primop since they do not have bindings.
|
|
| 1003 | -We fix this by expanding the scope of such ticks slightly to cover the body
|
|
| 1004 | -of the eta-expanded expression.
|
|
| 1005 | - |
|
| 1006 | -We do this by:
|
|
| 1007 | -* Checking if an application is headed by a primOpish thing.
|
|
| 1008 | -* If so we collect floatable ticks and usually but also profiling ticks
|
|
| 1009 | - along with regular arguments.
|
|
| 1010 | -* When rebuilding the application we check if any profiling ticks appear
|
|
| 1011 | - before the primop is fully saturated.
|
|
| 1012 | -* If the primop isn't fully satured we eta expand the primop application
|
|
| 1013 | - and scope the tick to scope over the body of the saturated expression.
|
|
| 1014 | - |
|
| 1015 | -Going back to #20938 this means starting with
|
|
| 1016 | - `(Tick<foo> tagToEnum#) @Bool`
|
|
| 1017 | -we check if the function head is a primop (yes). This means we collect the
|
|
| 1018 | -profiling tick like if it was floatable. Giving us
|
|
| 1019 | - (tagToEnum#, [CpeTick foo, CpeApp @Bool]).
|
|
| 1028 | +The problem was that the tick covered a partial application of a primop.
|
|
| 1029 | +There is no clear semantic for such a construct: we can't partially apply a
|
|
| 1030 | +primop, since primops do not have bindings.
|
|
| 1031 | + |
|
| 1032 | +To fix this, we expand the scope of ticks slightly to cover the body
|
|
| 1033 | +of the eta-expanded expression, even when the tick isn't normally floatable.
|
|
| 1034 | + |
|
| 1035 | +This is achieved by using 'GHC.Core.Utils.canCollectArgsThroughTick', which
|
|
| 1036 | +responds 'True' in the following two situations:
|
|
| 1037 | + |
|
| 1038 | + - The tick is floatable (i.e. satisfies 'tickishFloatable'), meaning that it
|
|
| 1039 | + is OK to float it out slightly, moving in more code under it.
|
|
| 1040 | + See also Note [Eta expansion and source notes] in GHC.Core.Opt.Arity.
|
|
| 1041 | + - The tick is around an application that is headed by an 'Id' that cannot be
|
|
| 1042 | + undersaturated, such as a PrimOp (see 'GHC.Core.Utils.cantEtaReduceFun').
|
|
| 1043 | + |
|
| 1044 | +This solves #20938. Indeed, starting with
|
|
| 1045 | + |
|
| 1046 | + (scctick<foo> tagToEnum#) @Bool
|
|
| 1047 | + |
|
| 1048 | +we see that the head of the application is 'tagToEnum#', which is a PrimOp and
|
|
| 1049 | +thus satisfies 'hasNoBinding = True'. As a result, we collect the profiling tick
|
|
| 1050 | +as if it was floatable, resulting in
|
|
| 1051 | + |
|
| 1052 | + (tagToEnum#, [CpeTick foo, CpeApp @Bool])
|
|
| 1053 | + |
|
| 1020 | 1054 | cpe_app filters out the tick as a underscoped tick on the expression
|
| 1021 | -`tagToEnum# @Bool`. During eta expansion we then put that tick back onto the
|
|
| 1022 | -body of the eta-expansion lambdas. Giving us `\x -> Tick<foo> (tagToEnum# @Bool x)`.
|
|
| 1055 | +`tagToEnum# @Bool`. During eta-expansion, we put that tick back onto the
|
|
| 1056 | +body of the eta-expansion lambda, resulting in
|
|
| 1057 | + |
|
| 1058 | + \x -> scctick<foo> (tagToEnum# @Bool x)
|
|
| 1059 | + |
|
| 1060 | +which is unproblematic.
|
|
| 1023 | 1061 | -}
|
| 1062 | + |
|
| 1024 | 1063 | cpeApp :: CorePrepEnv -> CoreExpr -> UniqSM (Floats, CpeRhs)
|
| 1025 | 1064 | -- May return a CpeRhs (instead of CpeApp) because of saturating primops
|
| 1026 | 1065 | cpeApp top_env expr
|
| ... | ... | @@ -1045,15 +1084,14 @@ cpeApp top_env expr |
| 1045 | 1084 | go (Cast fun co) as
|
| 1046 | 1085 | = go fun (AICast co : as)
|
| 1047 | 1086 | go (Tick tickish fun) as
|
| 1048 | - -- Profiling ticks are slightly less strict so we expand their scope
|
|
| 1049 | - -- if they cover partial applications of things like primOps.
|
|
| 1050 | - -- See Note [Ticks and mandatory eta expansion]
|
|
| 1051 | - -- Here we look inside `fun` before we make the final decision about
|
|
| 1052 | - -- floating the tick which isn't optimal for perf. But this only makes
|
|
| 1053 | - -- a difference if we have a non-floatable tick which is somewhat rare.
|
|
| 1087 | + -- Try to move a tick out of the way, if:
|
|
| 1088 | + -- - the tick can be floated out of the way ('tickishFloatable'), or
|
|
| 1089 | + -- - the tick must be moved out of the way because it stands in between
|
|
| 1090 | + -- an 'Id' that must be saturated and some of its arguments;
|
|
| 1091 | + -- see Note [Ticks and mandatory eta expansion].
|
|
| 1054 | 1092 | | Var vh <- head
|
| 1055 | - , Var head' <- lookupCorePrepEnv top_env vh
|
|
| 1056 | - , etaExpansionTick head' tickish
|
|
| 1093 | + , Just head' <- getIdFromTrivialExpr_maybe (lookupCorePrepEnv top_env vh)
|
|
| 1094 | + , canCollectArgsThroughTick head' tickish
|
|
| 1057 | 1095 | = (head,as')
|
| 1058 | 1096 | where
|
| 1059 | 1097 | (head,as') = go fun (AITick tickish : as)
|
| ... | ... | @@ -1130,7 +1168,10 @@ cpeApp top_env expr |
| 1130 | 1168 | hd = getIdFromTrivialExpr_maybe e2
|
| 1131 | 1169 | -- Determine number of required arguments. See Note [Ticks and mandatory eta expansion]
|
| 1132 | 1170 | min_arity = case hd of
|
| 1133 | - Just v_hd -> if hasNoBinding v_hd then Just $! (idArity v_hd) else Nothing
|
|
| 1171 | + Just v_hd ->
|
|
| 1172 | + if cantEtaReduceFun v_hd
|
|
| 1173 | + then Just $! idArity v_hd
|
|
| 1174 | + else Nothing
|
|
| 1134 | 1175 | Nothing -> Nothing
|
| 1135 | 1176 | -- ; pprTraceM "cpe_app:stricts:" (ppr v <+> ppr args $$ ppr stricts $$ ppr (idCbvMarks_maybe v))
|
| 1136 | 1177 | ; (app, floats, unsat_ticks) <- rebuild_app env args e2 emptyFloats stricts min_arity
|
| ... | ... | @@ -2293,8 +2334,8 @@ deFloatTop floats |
| 2293 | 2334 | get b _ = pprPanic "deFloatTop" (ppr b)
|
| 2294 | 2335 | |
| 2295 | 2336 | -- See Note [Dead code in CorePrep]
|
| 2296 | - get_bind (NonRec x e) = NonRec x (occurAnalyseExpr e)
|
|
| 2297 | - get_bind (Rec xes) = Rec [(x, occurAnalyseExpr e) | (x, e) <- xes]
|
|
| 2337 | + get_bind (NonRec x e) = NonRec x (occurAnalyseExpr_Prep e)
|
|
| 2338 | + get_bind (Rec xes) = Rec [(x, occurAnalyseExpr_Prep e) | (x, e) <- xes]
|
|
| 2298 | 2339 | |
| 2299 | 2340 | ---------------------------------------------------------------------------
|
| 2300 | 2341 |
| ... | ... | @@ -115,7 +115,8 @@ perPassFlags dflags pass |
| 115 | 115 | , lf_check_inline_loop_breakers = check_lbs
|
| 116 | 116 | , lf_check_static_ptrs = check_static_ptrs
|
| 117 | 117 | , lf_check_linearity = check_linearity
|
| 118 | - , lf_check_rubbish_lits = check_rubbish }
|
|
| 118 | + , lf_check_rubbish_lits = check_rubbish
|
|
| 119 | + , lf_allow_weak_joins = allow_weak_joins }
|
|
| 119 | 120 | where
|
| 120 | 121 | -- See Note [Checking for global Ids]
|
| 121 | 122 | check_globals = case pass of
|
| ... | ... | @@ -152,6 +153,11 @@ perPassFlags dflags pass |
| 152 | 153 | CorePrep -> True
|
| 153 | 154 | _ -> False
|
| 154 | 155 | |
| 156 | + -- See Note [Linting join points with casts or ticks] in GHC.Core.Lint
|
|
| 157 | + allow_weak_joins = case pass of
|
|
| 158 | + CorePrep -> True
|
|
| 159 | + _ -> False
|
|
| 160 | + |
|
| 155 | 161 | initLintConfig :: DynFlags -> [Var] -> LintConfig
|
| 156 | 162 | initLintConfig dflags vars =LintConfig
|
| 157 | 163 | { l_diagOpts = initDiagOpts dflags
|
| ... | ... | @@ -168,4 +174,5 @@ defaultLintFlags dflags = LF { lf_check_global_ids = False |
| 168 | 174 | , lf_report_unsat_syns = True
|
| 169 | 175 | , lf_check_fixed_rep = True
|
| 170 | 176 | , lf_check_rubbish_lits = True
|
| 177 | + , lf_allow_weak_joins = False
|
|
| 171 | 178 | } |
| ... | ... | @@ -1272,7 +1272,7 @@ tidyTopIdInfo rhs_tidy_env name rhs_ty orig_rhs tidy_rhs idinfo show_unfold |
| 1272 | 1272 | is_external = isExternalName name
|
| 1273 | 1273 | |
| 1274 | 1274 | --------- OccInfo ------------
|
| 1275 | - robust_occ_info = zapFragileOcc (occInfo idinfo)
|
|
| 1275 | + robust_occ_info = zapFragileOccInfo (occInfo idinfo)
|
|
| 1276 | 1276 | -- It's important to keep loop-breaker information
|
| 1277 | 1277 | -- when we are doing -fexpose-all-unfoldings
|
| 1278 | 1278 |
| ... | ... | @@ -1273,5 +1273,5 @@ cgTick tick |
| 1273 | 1273 | ProfNote cc t p -> emitSetCCC cc t p
|
| 1274 | 1274 | HpcTick m n -> emit (mkTickBox platform m n)
|
| 1275 | 1275 | SourceNote s n -> emitTick $ SourceNote s n
|
| 1276 | - _other -> return () -- ignore
|
|
| 1276 | + Breakpoint {} -> return () -- ignore
|
|
| 1277 | 1277 | } |
| ... | ... | @@ -66,7 +66,7 @@ module GHC.Types.Basic ( |
| 66 | 66 | noOneShotInfo, hasNoOneShotInfo, isOneShotInfo,
|
| 67 | 67 | bestOneShot, worstOneShot,
|
| 68 | 68 | |
| 69 | - OccInfo(..), noOccInfo, seqOccInfo, zapFragileOcc, isOneOcc,
|
|
| 69 | + OccInfo(..), noOccInfo, seqOccInfo, zapFragileOccInfo, isOneOcc,
|
|
| 70 | 70 | isDeadOcc, isStrongLoopBreaker, isWeakLoopBreaker, isManyOccs,
|
| 71 | 71 | isNoOccInfo, strongLoopBreaker, weakLoopBreaker,
|
| 72 | 72 | |
| ... | ... | @@ -980,10 +980,13 @@ isOneOcc :: OccInfo -> Bool |
| 980 | 980 | isOneOcc (OneOcc {}) = True
|
| 981 | 981 | isOneOcc _ = False
|
| 982 | 982 | |
| 983 | -zapFragileOcc :: OccInfo -> OccInfo
|
|
| 984 | --- Keep only the most robust data: deadness, loop-breaker-hood
|
|
| 985 | -zapFragileOcc (OneOcc {}) = noOccInfo
|
|
| 986 | -zapFragileOcc occ = zapOccTailCallInfo occ
|
|
| 983 | +-- | Keep only the most robust occurrence info: deadness, loop-breaker-hood.
|
|
| 984 | +--
|
|
| 985 | +-- In particular, it zaps 'TailCallInfo': see Note [JoinId vs TailCallInfo]
|
|
| 986 | +-- in 'GHC.Core.Opt.Simplify.Env'.
|
|
| 987 | +zapFragileOccInfo :: OccInfo -> OccInfo
|
|
| 988 | +zapFragileOccInfo (OneOcc {}) = noOccInfo
|
|
| 989 | +zapFragileOccInfo occ = zapOccTailCallInfo occ
|
|
| 987 | 990 | |
| 988 | 991 | instance Outputable OccInfo where
|
| 989 | 992 | -- only used for debugging; never parsed. KSW 1999-07
|
| ... | ... | @@ -914,14 +914,15 @@ zapUsedOnceInfo info |
| 914 | 914 | , demandInfo = zapUsedOnceDemand (demandInfo info) }
|
| 915 | 915 | |
| 916 | 916 | zapFragileInfo :: IdInfo -> Maybe IdInfo
|
| 917 | --- ^ Zap info that depends on free variables
|
|
| 917 | +-- ^ Zap fragile 'IdInfo', such as info that depends on free variables
|
|
| 918 | +-- or fragile occurrence info (see 'zapFragileOccInfo').
|
|
| 918 | 919 | zapFragileInfo info@(IdInfo { occInfo = occ, realUnfoldingInfo = unf })
|
| 919 | 920 | = new_unf `seq` -- The unfolding field is not (currently) strict, so we
|
| 920 | 921 | -- force it here to avoid a (zapFragileUnfolding unf) thunk
|
| 921 | 922 | -- which might leak space
|
| 922 | 923 | Just (info `setRuleInfo` emptyRuleInfo
|
| 923 | 924 | `setUnfoldingInfo` new_unf
|
| 924 | - `setOccInfo` zapFragileOcc occ)
|
|
| 925 | + `setOccInfo` zapFragileOccInfo occ)
|
|
| 925 | 926 | where
|
| 926 | 927 | new_unf = zapFragileUnfolding unf
|
| 927 | 928 |
| ... | ... | @@ -6,9 +6,8 @@ module GHC.Types.Tickish ( |
| 6 | 6 | CoreTickish, StgTickish, CmmTickish,
|
| 7 | 7 | XTickishId,
|
| 8 | 8 | tickishCounts,
|
| 9 | - TickishScoping(..),
|
|
| 10 | - tickishScoped,
|
|
| 11 | - tickishScopesLike,
|
|
| 9 | + tickishHasNoScope,
|
|
| 10 | + tickishHasSoftScope,
|
|
| 12 | 11 | tickishFloatable,
|
| 13 | 12 | tickishCanSplit,
|
| 14 | 13 | mkNoCount,
|
| ... | ... | @@ -206,103 +205,177 @@ instance Binary BreakpointId where |
| 206 | 205 | |
| 207 | 206 | --------------------------------------------------------------------------------
|
| 208 | 207 | |
| 209 | --- | A "counting tick" (where tickishCounts is True) is one that
|
|
| 208 | +-- | A "counting tick" (for which 'tickishCounts' is True) is one that
|
|
| 210 | 209 | -- counts evaluations in some way. We cannot discard a counting tick,
|
| 211 | --- and the compiler should preserve the number of counting ticks as
|
|
| 210 | +-- and the compiler should preserve the number of counting ticks (as
|
|
| 211 | +-- far as possible).
|
|
| 212 | 212 | --
|
| 213 | ---
|
|
| 214 | --- However, we still allow the simplifier to increase or decrease
|
|
| 215 | --- sharing, so in practice the actual number of ticks may vary, except
|
|
| 213 | +-- See Note [Counting ticks]
|
|
| 216 | 214 | tickishCounts :: GenTickish pass -> Bool
|
| 217 | -tickishCounts :: GenTickish pass -> Bool
|
|
| 218 | -tickishCounts n@ProfNote{} = profNoteCount n
|
|
| 219 | -tickishCounts HpcTick{} = True
|
|
| 220 | -tickishCounts Breakpoint{} = True
|
|
| 221 | -tickishCounts _ = False
|
|
| 222 | - |
|
| 223 | - |
|
| 224 | --- | Specifies the scoping behaviour of ticks. This governs the
|
|
| 225 | --- behaviour of ticks that care about the covered code and the cost
|
|
| 226 | --- associated with it. Important for ticks relating to profiling.
|
|
| 227 | -data TickishScoping =
|
|
| 228 | - -- | No scoping: The tick does not care about what code it
|
|
| 229 | - -- covers. Transformations can freely move code inside as well as
|
|
| 230 | - -- outside without any additional annotation obligations
|
|
| 231 | - NoScope
|
|
| 232 | - |
|
| 233 | - -- | Soft scoping: We want all code that is covered to stay
|
|
| 234 | - -- covered. Note that this scope type does not forbid
|
|
| 235 | - -- transformations from happening, as long as all results of
|
|
| 236 | - -- the transformations are still covered by this tick or a copy of
|
|
| 237 | - -- it. For example
|
|
| 238 | - --
|
|
| 239 | - -- let x = tick<...> (let y = foo in bar) in baz
|
|
| 240 | - -- ===>
|
|
| 241 | - -- let x = tick<...> bar; y = tick<...> foo in baz
|
|
| 242 | - --
|
|
| 243 | - -- Is a valid transformation as far as "bar" and "foo" is
|
|
| 244 | - -- concerned, because both still are scoped over by the tick.
|
|
| 245 | - --
|
|
| 246 | - -- Note though that one might object to the "let" not being
|
|
| 247 | - -- covered by the tick any more. However, we are generally lax
|
|
| 248 | - -- with this - constant costs don't matter too much, and given
|
|
| 249 | - -- that the "let" was effectively merged we can view it as having
|
|
| 250 | - -- lost its identity anyway.
|
|
| 251 | - --
|
|
| 252 | - -- Also note that this scoping behaviour allows floating a tick
|
|
| 253 | - -- "upwards" in pretty much any situation. For example:
|
|
| 254 | - --
|
|
| 255 | - -- case foo of x -> tick<...> bar
|
|
| 256 | - -- ==>
|
|
| 257 | - -- tick<...> case foo of x -> bar
|
|
| 258 | - --
|
|
| 259 | - -- While this is always legal, we want to make a best effort to
|
|
| 260 | - -- only make us of this where it exposes transformation
|
|
| 261 | - -- opportunities.
|
|
| 262 | - | SoftScope
|
|
| 263 | - |
|
| 264 | - -- | Cost centre scoping: We don't want any costs to move to other
|
|
| 265 | - -- cost-centre stacks. This means we not only want no code or cost
|
|
| 266 | - -- to get moved out of their cost centres, but we also object to
|
|
| 267 | - -- code getting associated with new cost-centre ticks - or
|
|
| 268 | - -- changing the order in which they get applied.
|
|
| 269 | - --
|
|
| 270 | - -- A rule of thumb is that we don't want any code to gain new
|
|
| 271 | - -- annotations. However, there are notable exceptions, for
|
|
| 272 | - -- example:
|
|
| 273 | - --
|
|
| 274 | - -- let f = \y -> foo in tick<...> ... (f x) ...
|
|
| 275 | - -- ==>
|
|
| 276 | - -- tick<...> ... foo[x/y] ...
|
|
| 277 | - --
|
|
| 278 | - -- In-lining lambdas like this is always legal, because inlining a
|
|
| 279 | - -- function does not change the cost-centre stack when the
|
|
| 280 | - -- function is called.
|
|
| 281 | - | CostCentreScope
|
|
| 282 | - |
|
| 283 | - deriving (Eq)
|
|
| 284 | - |
|
| 285 | --- | Returns the intended scoping rule for a Tickish
|
|
| 286 | -tickishScoped :: GenTickish pass -> TickishScoping
|
|
| 287 | -tickishScoped n@ProfNote{}
|
|
| 288 | - | profNoteScope n = CostCentreScope
|
|
| 289 | - | otherwise = NoScope
|
|
| 290 | -tickishScoped HpcTick{} = NoScope
|
|
| 291 | -tickishScoped Breakpoint{} = CostCentreScope
|
|
| 292 | - -- Breakpoints are scoped: eventually we're going to do call
|
|
| 293 | - -- stacks, but also this helps prevent the simplifier from moving
|
|
| 294 | - -- breakpoints around and changing their result type (see #1531).
|
|
| 295 | -tickishScoped SourceNote{} = SoftScope
|
|
| 296 | - |
|
| 297 | --- | Returns whether the tick scoping rule is at least as permissive
|
|
| 298 | --- as the given scoping rule.
|
|
| 299 | -tickishScopesLike :: GenTickish pass -> TickishScoping -> Bool
|
|
| 300 | -tickishScopesLike t scope = tickishScoped t `like` scope
|
|
| 301 | - where NoScope `like` _ = True
|
|
| 302 | - _ `like` NoScope = False
|
|
| 303 | - SoftScope `like` _ = True
|
|
| 304 | - _ `like` SoftScope = False
|
|
| 215 | +tickishCounts = \case
|
|
| 216 | + ProfNote { profNoteCount = counts } -> counts
|
|
| 217 | + HpcTick {} -> True
|
|
| 218 | + Breakpoint {} -> True
|
|
| 219 | + SourceNote {} -> False
|
|
| 220 | + |
|
| 221 | +-- | Is this a non-scoping tick, for which we don't care about precisely
|
|
| 222 | +-- the extent of code that the tick encompasses?
|
|
| 223 | +--
|
|
| 224 | +-- See Note [Scoped ticks]
|
|
| 225 | +tickishHasNoScope :: GenTickish pass -> Bool
|
|
| 226 | +tickishHasNoScope = \case
|
|
| 227 | + ProfNote { profNoteScope = scopes } -> not scopes
|
|
| 228 | + HpcTick {} -> True
|
|
| 229 | + Breakpoint {} -> False
|
|
| 230 | + SourceNote {} -> False
|
|
| 231 | + |
|
| 232 | +-- | A "tick with soft scoping" (for which 'tickishHasSoftScope' is True) is
|
|
| 233 | +-- one that either does not scope at all (for which 'tickishHasNoScope' is True),
|
|
| 234 | +-- or that has a "soft" scope: we allow new code to be floated into to the scope,
|
|
| 235 | +-- as long as all code that was covered remains covered.
|
|
| 236 | +--
|
|
| 237 | +-- See Note [Scoped ticks]
|
|
| 238 | +tickishHasSoftScope :: GenTickish pass -> Bool
|
|
| 239 | +tickishHasSoftScope = \case
|
|
| 240 | + ProfNote { profNoteScope = scopes } -> not scopes
|
|
| 241 | + HpcTick {} -> True
|
|
| 242 | + Breakpoint {} -> False
|
|
| 243 | + SourceNote {} -> True
|
|
| 244 | + |
|
| 245 | +{- Note [Scoping ticks and counting ticks]
|
|
| 246 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 247 | +Ticks have two independent attributes:
|
|
| 248 | + |
|
| 249 | + * Whether the tick /counts/.
|
|
| 250 | + Counting ticks are used when we want a counter to be bumped, e.g. counting
|
|
| 251 | + how many times a function is called.
|
|
| 252 | + |
|
| 253 | + See Note [Counting ticks]
|
|
| 254 | + |
|
| 255 | + * What kind of /scope/ the tick has:
|
|
| 256 | + * Cost-centre scope: you cannot move a redex into the scope of the tick,
|
|
| 257 | + nor can you float a redex out.
|
|
| 258 | + * Soft scope: you can move a redex /into/ the scope of a tick,
|
|
| 259 | + but you cannot float a redex /out/
|
|
| 260 | + * No scope: there are no restrictions on floating in or out.
|
|
| 261 | + |
|
| 262 | + See Note [Scoped ticks]
|
|
| 263 | + |
|
| 264 | +Note [Counting ticks]
|
|
| 265 | +~~~~~~~~~~~~~~~~~~~~
|
|
| 266 | +The following ticks count:
|
|
| 267 | + - ProfNote ticks with profNoteCounts = True
|
|
| 268 | + - HPC ticks
|
|
| 269 | + - Breakpoints
|
|
| 270 | + |
|
| 271 | +Going past a counting tick implies bumping a counter.
|
|
| 272 | +Generally, the simplifier attempts to preserve counts when transforming
|
|
| 273 | +programs and moving ticks, for example by transforming:
|
|
| 274 | + |
|
| 275 | + case <tick> e of
|
|
| 276 | + alt1 -> rhs1
|
|
| 277 | + alt2 -> rhs2
|
|
| 278 | + |
|
| 279 | +to
|
|
| 280 | + |
|
| 281 | + case e of
|
|
| 282 | + alt1 -> <tick> rhs1
|
|
| 283 | + alt2 -> <tick> rhs2
|
|
| 284 | + |
|
| 285 | +which preserves the total count (as exactly one branch of the case
|
|
| 286 | +will be taken).
|
|
| 287 | + |
|
| 288 | +However, we still allow the simplifier to increase or decrease
|
|
| 289 | +sharing, so in practice the actual number of ticks may vary, except
|
|
| 290 | +that we never change the value from zero to non-zero or vice-versa.
|
|
| 291 | + |
|
| 292 | +Note [Scoped ticks]
|
|
| 293 | +~~~~~~~~~~~~~~~~~~~~
|
|
| 294 | +The following ticks are scoped:
|
|
| 295 | + - ProfNote ticks with profNoteScope = True
|
|
| 296 | + - Breakpoints
|
|
| 297 | + - Source notes
|
|
| 298 | + |
|
| 299 | +A scoped tick is one that scopes over a portion of code. For example,
|
|
| 300 | +an SCC anotation sets the cost centre for the code within; any allocations
|
|
| 301 | +within that piece of code should get attributed to that cost centre.
|
|
| 302 | + |
|
| 303 | +When the simplifier deals with a scoping tick, it ensures that all code that
|
|
| 304 | +was covered remains covered. For example
|
|
| 305 | + |
|
| 306 | + let x = tick<...> (let y = foo in bar) in baz
|
|
| 307 | + ===>
|
|
| 308 | + let x = tick<...> bar; y = tick<...> foo in baz
|
|
| 309 | + |
|
| 310 | +is a valid transformation as far as "bar" and "foo" are concerned, because
|
|
| 311 | +both still are scoped over by the tick. One might object to the "let" not
|
|
| 312 | +being covered by the tick any more. However, we are generally lax with this;
|
|
| 313 | +constant costs don't matter too much, and given that the "let" was effectively
|
|
| 314 | +merged we can view it as having lost its identity anyway.
|
|
| 315 | + |
|
| 316 | +Perhaps surprisingly, breakpoints are considered to be scoped, because we
|
|
| 317 | +don't want the simplifier to move them around, changing their result type (see #1531).
|
|
| 318 | + |
|
| 319 | +We specifically forbid floating code outside of a scoping tick, as cost
|
|
| 320 | +associated with the floated-out code would no longer be attributed to the
|
|
| 321 | +appropriate scope.
|
|
| 322 | + |
|
| 323 | +Whether we are allowed to float in additional cost depends on the tick:
|
|
| 324 | + |
|
| 325 | + Cost-centre scope ticks
|
|
| 326 | + - ProfNote with profNoteScope = True
|
|
| 327 | + - Breakpoints
|
|
| 328 | + |
|
| 329 | + A tick with cost-centre scope is one for which we can neither move
|
|
| 330 | + redexes into or move redexes outside of the tick. For example, we don't
|
|
| 331 | + want profiling costs to move to other cost-centre stacks.
|
|
| 332 | + Morever, we also object to changing the order in which such ticks
|
|
| 333 | + are applied.
|
|
| 334 | + |
|
| 335 | + A rule of thumb is that we don't want any code to gain new
|
|
| 336 | + lexically-enclosing ticks. For example, we should not transform:
|
|
| 337 | + |
|
| 338 | + f (scctick<foo> a) ==> scctick<foo> (f a)
|
|
| 339 | + |
|
| 340 | + as this would attribute the cost of evaluating the application 'f a'
|
|
| 341 | + to the cost centre 'foo'.
|
|
| 342 | + |
|
| 343 | + However, there are notable exceptions, for example:
|
|
| 344 | + |
|
| 345 | + let f = \y -> foo in tick<...> ... (f x) ...
|
|
| 346 | + ==>
|
|
| 347 | + tick<...> ... foo[x/y] ...
|
|
| 348 | + |
|
| 349 | + Inlining lambdas like this is always legal, because inlining a function
|
|
| 350 | + does not change the cost-centre stack when the function is called.
|
|
| 351 | + |
|
| 352 | + Soft scope ticks
|
|
| 353 | + - Source notes
|
|
| 354 | + |
|
| 355 | + A tick with soft scope is one for which we can move redexes inside the
|
|
| 356 | + tick, but cannot float redexes outside the tick. This is a slightly more
|
|
| 357 | + lenient notion of scoping than cost-centres, and is used only for source
|
|
| 358 | + note ticks (they are used to provide DWARF debug symbols, and for those
|
|
| 359 | + it matters less if code from outside gets moved under the tick).
|
|
| 360 | + |
|
| 361 | + Examples:
|
|
| 362 | + |
|
| 363 | + - FloatIn (GHC.Core.Opt.FloatIn.fiExpr)
|
|
| 364 | + |
|
| 365 | + let x = rhs in <tick> body
|
|
| 366 | + ==>
|
|
| 367 | + <tick> (let x = rhs in body)
|
|
| 368 | + |
|
| 369 | + - Moving a tick outside of a case or of an application
|
|
| 370 | + (GHC.Core.Opt.Simplify.Iteration.simplTick)
|
|
| 371 | + |
|
| 372 | + case <tick> e of alts ==> <tick> case e of alts
|
|
| 373 | + |
|
| 374 | + (<tick> e1) e2 ==> <tick> (e1 e2)
|
|
| 375 | + |
|
| 376 | + While these transformations are legal, we want to make a best effort to
|
|
| 377 | + only make use of them where it exposes transformation opportunities.
|
|
| 378 | +-}
|
|
| 305 | 379 | |
| 306 | 380 | -- | Returns @True@ for ticks that can be floated upwards easily even
|
| 307 | 381 | -- where it might change execution counts, such as:
|
| ... | ... | @@ -311,12 +384,11 @@ tickishScopesLike t scope = tickishScoped t `like` scope |
| 311 | 384 | -- ==>
|
| 312 | 385 | -- tick<...> (Just foo)
|
| 313 | 386 | --
|
| 314 | --- This is a combination of @tickishSoftScope@ and
|
|
| 315 | --- @tickishCounts@. Note that in principle splittable ticks can become
|
|
| 316 | --- floatable using @mkNoTick@ -- even though there's currently no
|
|
| 317 | --- tickish for which that is the case.
|
|
| 387 | +-- This is a combination of @tickishHasSoftScope@ and @tickishCounts@.
|
|
| 388 | +-- Note that in principle splittable ticks can become floatable using @mkNoTick@,
|
|
| 389 | +-- even though there's currently no tickish for which that is the case.
|
|
| 318 | 390 | tickishFloatable :: GenTickish pass -> Bool
|
| 319 | -tickishFloatable t = t `tickishScopesLike` SoftScope && not (tickishCounts t)
|
|
| 391 | +tickishFloatable t = tickishHasSoftScope t && not (tickishCounts t)
|
|
| 320 | 392 | |
| 321 | 393 | -- | Returns @True@ for a tick that is both counting /and/ scoping and
|
| 322 | 394 | -- can be split into its (tick, scope) parts using 'mkNoScope' and
|
| ... | ... | @@ -334,7 +406,7 @@ mkNoCount n@ProfNote{} = let n' = n {profNoteCount = False} |
| 334 | 406 | mkNoCount _ = panic "mkNoCount: Undefined split!"
|
| 335 | 407 | |
| 336 | 408 | mkNoScope :: GenTickish pass -> GenTickish pass
|
| 337 | -mkNoScope n | tickishScoped n == NoScope = n
|
|
| 409 | +mkNoScope n | tickishHasNoScope n = n
|
|
| 338 | 410 | | not (tickishCanSplit n) = panic "mkNoScope: Cannot split!"
|
| 339 | 411 | mkNoScope n@ProfNote{} = let n' = n {profNoteScope = False}
|
| 340 | 412 | in assert (profNoteCount n) n'
|
| ... | ... | @@ -357,7 +429,9 @@ mkNoScope _ = panic "mkNoScope: Undefined split!" |
| 357 | 429 | -- translate the code as if it found the latter.
|
| 358 | 430 | tickishIsCode :: GenTickish pass -> Bool
|
| 359 | 431 | tickishIsCode SourceNote{} = False
|
| 360 | -tickishIsCode _tickish = True -- all the rest for now
|
|
| 432 | +tickishIsCode ProfNote{} = True
|
|
| 433 | +tickishIsCode Breakpoint{} = True
|
|
| 434 | +tickishIsCode HpcTick{} = True
|
|
| 361 | 435 | |
| 362 | 436 | isProfTick :: GenTickish pass -> Bool
|
| 363 | 437 | isProfTick ProfNote{} = True
|
| ... | ... | @@ -18,7 +18,6 @@ src<debug.hs:4:9> |
| 18 | 18 | src<debug.hs:5:21-29>
|
| 19 | 19 | src<debug.hs:5:9-29>
|
| 20 | 20 | src<debug.hs:6:1-21>
|
| 21 | -src<debug.hs:6:16-21>
|
|
| 22 | 21 | == CBE ==
|
| 23 | 22 | src<debug.hs:4:9>
|
| 24 | 23 | 89 |
| 1 | +module T26642 ( saveClobberedTemps ) where
|
|
| 2 | + |
|
| 3 | +import Prelude ( IO, Bool(..), Int, (>>=), (==), return )
|
|
| 4 | +import Data.Word ( Word64 )
|
|
| 5 | + |
|
| 6 | +-------------------------------------------------------------------------------
|
|
| 7 | + |
|
| 8 | +data Word64Map a
|
|
| 9 | + = Bin (Word64Map a) (Word64Map a)
|
|
| 10 | + | Tip a
|
|
| 11 | + | Nil
|
|
| 12 | + |
|
| 13 | +{-# NOINLINE myFoldr #-}
|
|
| 14 | +myFoldr :: (a -> b -> b) -> b -> Word64Map a -> b
|
|
| 15 | +myFoldr f = go
|
|
| 16 | + where
|
|
| 17 | + {-# NOINLINE go #-}
|
|
| 18 | + go z' Nil = z'
|
|
| 19 | + go z' (Tip x) = f x z'
|
|
| 20 | + go z' (Bin l r) = go (go z' r) l
|
|
| 21 | + |
|
| 22 | +{-# NOINLINE nonDetFold #-}
|
|
| 23 | +nonDetFold :: (b -> elt -> IO b) -> b -> Word64Map elt -> IO b
|
|
| 24 | +nonDetFold f z0 xs = myFoldr c return xs z0
|
|
| 25 | + where
|
|
| 26 | + {-# NOINLINE c #-}
|
|
| 27 | + c x k z = f z x >>= k
|
|
| 28 | + |
|
| 29 | +{-# NOINLINE myFalse #-}
|
|
| 30 | +myFalse :: Bool
|
|
| 31 | +myFalse = False
|
|
| 32 | + |
|
| 33 | +type RealReg = Int
|
|
| 34 | +data Loc = InReg RealReg | InMem
|
|
| 35 | + |
|
| 36 | +saveClobberedTemps :: forall instr. [RealReg] -> IO [instr]
|
|
| 37 | +saveClobberedTemps clobbered = nonDetFold maybe_spill [] Nil
|
|
| 38 | + where
|
|
| 39 | + {-# NOINLINE maybe_spill #-}
|
|
| 40 | + maybe_spill :: [instr] -> Loc -> IO [instr]
|
|
| 41 | + maybe_spill instrs !loc =
|
|
| 42 | + case loc of
|
|
| 43 | + InReg reg
|
|
| 44 | + | myFalse
|
|
| 45 | + -> return []
|
|
| 46 | + _ -> return instrs |
| 1 | +{-# LANGUAGE DataKinds #-}
|
|
| 2 | +{-# LANGUAGE GADTs #-}
|
|
| 3 | +{-# LANGUAGE ScopedTypeVariables #-}
|
|
| 4 | +{-# LANGUAGE TypeApplications #-}
|
|
| 5 | +{-# LANGUAGE TypeFamilies #-}
|
|
| 6 | + |
|
| 7 | +{-# LANGUAGE MagicHash #-}
|
|
| 8 | +{-# LANGUAGE UnboxedTuples #-}
|
|
| 9 | + |
|
| 10 | +module TrickyJoinPoints where
|
|
| 11 | + |
|
| 12 | +import Data.Coerce
|
|
| 13 | + ( coerce )
|
|
| 14 | +import Data.Kind
|
|
| 15 | + ( Type )
|
|
| 16 | + |
|
| 17 | + |
|
| 18 | +import Data.Map.Strict (Map)
|
|
| 19 | +import qualified Data.Map.Strict as Map
|
|
| 20 | +import qualified Data.Set as Set
|
|
| 21 | + |
|
| 22 | +-----------------------------------
|
|
| 23 | +-- Join points and profiling ticks
|
|
| 24 | + |
|
| 25 | +data ModGuts2 = MkModGuts2
|
|
| 26 | + |
|
| 27 | +runCorePasses3 :: Bool -> ModGuts2 -> IO ModGuts2
|
|
| 28 | +runCorePasses3 pass guts = doCorePass3 pass guts
|
|
| 29 | + |
|
| 30 | +doCorePass3 :: Bool -> ModGuts2 -> IO ModGuts2
|
|
| 31 | +doCorePass3 pass guts = do
|
|
| 32 | + _ <- putStrLn "hi"
|
|
| 33 | + |
|
| 34 | + let
|
|
| 35 | + updateBinds _ = return guts
|
|
| 36 | + |
|
| 37 | + case pass of
|
|
| 38 | + True -> {-# SCC "XXX3" #-} updateBinds False
|
|
| 39 | + _ -> {-# SCC "YYY3" #-} updateBinds True
|
|
| 40 | + |
|
| 41 | +--------------------------
|
|
| 42 | +-- Join points & casts
|
|
| 43 | + |
|
| 44 | +newtype AdjacencyMap a = AM {
|
|
| 45 | + adjacencyMap :: Map a (Set.Set a) }
|
|
| 46 | + |
|
| 47 | +overlays :: Ord a => [AdjacencyMap a] -> AdjacencyMap a
|
|
| 48 | +overlays = AM . Map.unionsWith Set.union . map adjacencyMap
|
|
| 49 | + |
|
| 50 | + |
|
| 51 | +type SBool :: Bool -> Type
|
|
| 52 | +data SBool b where
|
|
| 53 | + SFalse :: SBool False
|
|
| 54 | + STrue :: SBool True
|
|
| 55 | + |
|
| 56 | +type N :: Bool -> Type
|
|
| 57 | +data family N b
|
|
| 58 | +newtype instance N False = NF ( Int -> Int )
|
|
| 59 | +newtype instance N True = NT ( Int -> Int )
|
|
| 60 | + |
|
| 61 | +testCast :: forall b. SBool b -> Int -> Int
|
|
| 62 | +testCast b n =
|
|
| 63 | + case
|
|
| 64 | + ( let
|
|
| 65 | + {-# NOINLINE juliet #-}
|
|
| 66 | + juliet :: Int -> Int -> Int
|
|
| 67 | + juliet x = \ y -> x + y + n
|
|
| 68 | + in
|
|
| 69 | + case b of
|
|
| 70 | + SFalse -> NF (juliet 1)
|
|
| 71 | + STrue -> NT (juliet 2)
|
|
| 72 | + ) :: N b of
|
|
| 73 | + n | SFalse <- b
|
|
| 74 | + , NF f <- n
|
|
| 75 | + -> f 100
|
|
| 76 | + | STrue <- b
|
|
| 77 | + , NT g <- n
|
|
| 78 | + -> g 200
|
|
| 79 | + |
|
| 80 | + |
|
| 81 | +------------------------------------------
|
|
| 82 | +-- Join points, profiling ticks and casts
|
|
| 83 | + |
|
| 84 | +newtype M = M ( Int -> Int -> Int )
|
|
| 85 | + |
|
| 86 | +testCastTick :: forall b. SBool b -> Int -> Int
|
|
| 87 | +testCastTick b n =
|
|
| 88 | + case
|
|
| 89 | + ( let
|
|
| 90 | + {-# NOINLINE j #-}
|
|
| 91 | + j :: Int -> Int -> Int
|
|
| 92 | + j x = \ y -> x + y + n
|
|
| 93 | + {-# NOINLINE k #-}
|
|
| 94 | + k :: M
|
|
| 95 | + k = coerce j
|
|
| 96 | + in
|
|
| 97 | + case b of
|
|
| 98 | + SFalse -> {-# SCC "ticked" #-} NF ( coerce @M @( Int -> Int -> Int ) k 1 )
|
|
| 99 | + STrue -> NT ( coerce @M @( Int -> Int -> Int ) k 2 )
|
|
| 100 | + ) :: N b of
|
|
| 101 | + n | SFalse <- b
|
|
| 102 | + , NF f <- n
|
|
| 103 | + -> f 100
|
|
| 104 | + | STrue <- b
|
|
| 105 | + , NT g <- n
|
|
| 106 | + -> g 200
|
|
| 107 | + |
|
| 108 | +------------------------------------------
|
|
| 109 | + |
|
| 110 | +{-# NOINLINE testJoinTransitivity #-}
|
|
| 111 | +testJoinTransitivity :: Bool -> Int -> Int
|
|
| 112 | +testJoinTransitivity b n =
|
|
| 113 | + let
|
|
| 114 | + f x = x ^ ( 99 :: Int ) + 7 * ( x - 19 )
|
|
| 115 | + {-# NOINLINE f #-}
|
|
| 116 | + in
|
|
| 117 | + f (
|
|
| 118 | + let
|
|
| 119 | + j1 :: Int -> Int
|
|
| 120 | + j1 x = x + n
|
|
| 121 | + {-# NOINLINE j1 #-}
|
|
| 122 | + |
|
| 123 | + j2 :: Int -> Int
|
|
| 124 | + j2 y = j1 (y * 2)
|
|
| 125 | + {-# NOINLINE j2 #-}
|
|
| 126 | + |
|
| 127 | + j3 :: Int -> Int
|
|
| 128 | + j3 z = j2 (z * 3)
|
|
| 129 | + {-# NOINLINE j3 #-}
|
|
| 130 | + |
|
| 131 | + in case b of
|
|
| 132 | + True -> {-# SCC "ticked" #-} j3 10
|
|
| 133 | + False -> j3 20
|
|
| 134 | + )
|
|
| 135 | + |
|
| 136 | +--------------------------------------------------------------------------------
|
|
| 137 | +-- Test relating to Note [JoinId vs TailCallInfo]
|
|
| 138 | + |
|
| 139 | +expt :: Int -> Int
|
|
| 140 | +expt _ = 3
|
|
| 141 | +{-# NOINLINE expt #-}
|
|
| 142 | + |
|
| 143 | +repro :: (Int, Int) -> (Int, Int)
|
|
| 144 | +repro (f0,e0) =
|
|
| 145 | + let
|
|
| 146 | + (f,e) =
|
|
| 147 | + let n = e0
|
|
| 148 | + in
|
|
| 149 | + case n > 0 of
|
|
| 150 | + True -> (f0, e0 + n)
|
|
| 151 | + False -> (f0, e0)
|
|
| 152 | + r = let be = expt e in f * be
|
|
| 153 | + in
|
|
| 154 | + (r, 7) |
| ... | ... | @@ -470,6 +470,9 @@ test('T22272', normal, multimod_compile, ['T22272', '-O -fexpose-all-unfoldings |
| 470 | 470 | # go should become a join point
|
| 471 | 471 | test('T22428', [grep_errmsg(r'jump go') ], compile, ['-O -ddump-simpl -dsuppress-uniques -dno-typeable-binds -dsuppress-unfoldings'])
|
| 472 | 472 | |
| 473 | +test('TrickyJoins', normal, compile, [''])
|
|
| 474 | +test('T26642', [unless(have_profiling(), skip)], compile, ['-O -prof -fprof-auto-calls'])
|
|
| 475 | + |
|
| 473 | 476 | test('T22459', normal, compile, [''])
|
| 474 | 477 | test('T22623', normal, multimod_compile, ['T22623', '-O -v0'])
|
| 475 | 478 | test('T22662', normal, compile, [''])
|