Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC

Commits:

21 changed files:

Changes:

  • compiler/GHC/Cmm/Node.hs
    ... ... @@ -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> ...
    

  • compiler/GHC/Core.hs
    ... ... @@ -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.
    

  • compiler/GHC/Core/Lint.hs
    ... ... @@ -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 ->
    

  • compiler/GHC/Core/Opt/Arity.hs
    ... ... @@ -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
     *                                                                      *
    

  • compiler/GHC/Core/Opt/FloatIn.hs
    ... ... @@ -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
    

  • compiler/GHC/Core/Opt/FloatOut.hs
    ... ... @@ -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

  • compiler/GHC/Core/Opt/OccurAnal.hs
    ... ... @@ -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
    

  • compiler/GHC/Core/Opt/Simplify/Iteration.hs
    ... ... @@ -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
    

  • compiler/GHC/Core/SimpleOpt.hs
    ... ... @@ -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
     *                                                                      *
    

  • compiler/GHC/Core/Utils.hs
    ... ... @@ -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
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    

  • compiler/GHC/CoreToStg/Prep.hs
    ... ... @@ -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
     
    

  • compiler/GHC/Driver/Config/Core/Lint.hs
    ... ... @@ -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
                                  }

  • compiler/GHC/Iface/Tidy.hs
    ... ... @@ -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
     
    

  • compiler/GHC/StgToCmm/Expr.hs
    ... ... @@ -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
            }

  • compiler/GHC/Types/Basic.hs
    ... ... @@ -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
    

  • compiler/GHC/Types/Id/Info.hs
    ... ... @@ -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
     
    

  • compiler/GHC/Types/Tickish.hs
    ... ... @@ -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
    

  • testsuite/tests/codeGen/should_compile/debug.stdout
    ... ... @@ -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

  • testsuite/tests/simplCore/should_compile/T26642.hs
    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

  • testsuite/tests/simplCore/should_compile/TrickyJoins.hs
    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)

  • testsuite/tests/simplCore/should_compile/all.T
    ... ... @@ -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, [''])