sheaf pushed to branch wip/andreask/ticked_joins at Glasgow Haskell Compiler / GHC

Commits:

6 changed files:

Changes:

  • compiler/GHC/Core/Lint.hs
    ... ... @@ -672,7 +672,7 @@ lintRhs :: Id -> CoreExpr -> LintM (OutType, UsageEnv)
    672 672
     lintRhs bndr rhs
    
    673 673
         | JoinPoint arity <- idJoinPointHood bndr
    
    674 674
         = lintJoinLams arity (Just bndr) rhs
    
    675
    -    | AlwaysTailCalled arity _ <- tailCallInfo (idOccInfo bndr)
    
    675
    +    | AlwaysTailCalled { tailCallArity = arity } <- tailCallInfo (idOccInfo bndr)
    
    676 676
         = lintJoinLams arity Nothing rhs
    
    677 677
     
    
    678 678
     -- Allow applications of the data constructor @StaticPtr@ at the top
    

  • compiler/GHC/Core/Opt/OccurAnal.hs
    ... ... @@ -797,10 +797,10 @@ function call and a jump by looking at the occurrence (because the same pass
    797 797
     changes the 'IdDetails' and propagates the binders to their occurrence sites).
    
    798 798
     
    
    799 799
     To track potential join points, we use the 'occ_tail' field of OccInfo. A value
    
    800
    -of `AlwaysTailCalled n` indicates that every occurrence of the variable is a
    
    801
    -tail call with `n` arguments (counting both value and type arguments). Otherwise
    
    802
    -'occ_tail' will be 'NoTailCallInfo'. The tail call info flows bottom-up with the
    
    803
    -rest of 'OccInfo' until it goes on the binder.
    
    800
    +of `AlwaysTailCalled { tailCallArity = n }` indicates that every occurrence of
    
    801
    +the variable is a tail call with `n` arguments (counting both value and type
    
    802
    +arguments). Otherwise 'occ_tail' will be 'NoTailCallInfo'. The tail call info
    
    803
    +flows bottom-up with the rest of 'OccInfo' until it goes on the binder.
    
    804 804
     
    
    805 805
     Note [Join arity prediction based on joinRhsArity]
    
    806 806
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    ... ... @@ -2588,9 +2588,9 @@ occAnal env (Tick tickish body)
    2588 2588
         WUD usage body' = occAnal env' body
    
    2589 2589
     
    
    2590 2590
         env' = case tickish of
    
    2591
    -      -- Set that we are inside a profiling tick
    
    2592
    -      -- SLD TODO: explain why we need this info
    
    2593
    -      ProfNote {} -> setInProfTick env
    
    2591
    +      -- setInsideProfTick: join points under profiling ticks turn
    
    2592
    +      -- into quasi-join points. See Note [Quasi join points]
    
    2593
    +      ProfNote {} -> setInsideProfTick env
    
    2594 2594
           _           -> env
    
    2595 2595
     
    
    2596 2596
         usage'
    
    ... ... @@ -2621,11 +2621,12 @@ occAnal env (Tick tickish body)
    2621 2621
         -- See #14242.
    
    2622 2622
     
    
    2623 2623
     occAnal env (Cast expr co)
    
    2624
    -  = let  (WUD usage expr') = occAnal env expr
    
    2625
    -         usage1 = addManyOccs usage (coVarsOfCo co)
    
    2626
    -             -- usage2: see Note [Gather occurrences of coercion variables]
    
    2627
    -         usage2 = markAllNonTail usage1
    
    2628
    -             -- usage3: calls inside expr aren't tail calls any more
    
    2624
    +  = let (WUD usage expr') = occAnal (setInsideCast env) expr
    
    2625
    +          -- setInsideCasts: join points inside casts turn into quasi-join-points
    
    2626
    +          -- See Note [Quasi join points]
    
    2627
    +        usage1 = addManyOccs usage (coVarsOfCo co)
    
    2628
    +            -- usage2: see Note [Gather occurrences of coercion variables]
    
    2629
    +        usage2 = markAllNonTail usage1
    
    2629 2630
         in WUD usage2 (Cast expr' co)
    
    2630 2631
     
    
    2631 2632
     occAnal env app@(App _ _)
    
    ... ... @@ -2942,7 +2943,8 @@ scrutinised y).
    2942 2943
     
    
    2943 2944
     data OccEnv
    
    2944 2945
       = OccEnv { occ_encl       :: !OccEncl      -- Enclosing context information
    
    2945
    -           , occ_prof_ticks :: !Int
    
    2946
    +           , occ_prof_ticks :: !Int -- ^ How many profiling ticks are we under? See Note [Quasi join points]
    
    2947
    +           , occ_casts      :: !Int -- ^ How many casts are we under? See Note [Quasi join points]
    
    2946 2948
                , occ_one_shots  :: !OneShots     -- See Note [OneShots]
    
    2947 2949
                , occ_unf_act    :: Id -> Bool    -- Which Id unfoldings are active
    
    2948 2950
                , occ_rule_act   :: ActivationGhc -> Bool  -- Which rules are active
    
    ... ... @@ -3009,6 +3011,7 @@ initOccEnv :: OccEnv
    3009 3011
     initOccEnv
    
    3010 3012
       = OccEnv { occ_encl      = OccVanilla
    
    3011 3013
                , occ_prof_ticks = 0
    
    3014
    +           , occ_casts      = 0
    
    3012 3015
                , occ_one_shots = []
    
    3013 3016
     
    
    3014 3017
                      -- To be conservative, we say that all
    
    ... ... @@ -3087,8 +3090,11 @@ setTailCtxt !env = env { occ_encl = OccVanilla }
    3087 3090
         -- Preserve occ_one_shots, occ_join points
    
    3088 3091
         -- Do not use OccRhs for the RHS of a join point (which is a tail ctxt):
    
    3089 3092
     
    
    3090
    -setInProfTick :: OccEnv -> OccEnv
    
    3091
    -setInProfTick !env = env { occ_prof_ticks = 1 + occ_prof_ticks env }
    
    3093
    +setInsideProfTick :: OccEnv -> OccEnv
    
    3094
    +setInsideProfTick !env = env { occ_prof_ticks = 1 + occ_prof_ticks env }
    
    3095
    +
    
    3096
    +setInsideCast :: OccEnv -> OccEnv
    
    3097
    +setInsideCast !env = env { occ_casts = 1 + occ_casts env }
    
    3092 3098
     
    
    3093 3099
     mkRhsOccEnv :: OccEnv -> RecFlag -> OccEncl -> JoinPointHood -> Id -> CoreExpr -> OccEnv
    
    3094 3100
     -- See Note [The OccEnv for a right hand side]
    
    ... ... @@ -3736,7 +3742,7 @@ type OccInfoEnv = IdEnv LocalOcc -- A finite map from an expression's
    3736 3742
     data LocalOcc  -- See Note [LocalOcc]
    
    3737 3743
          = OneOccL { lo_n_br  :: {-# UNPACK #-} !BranchCount  -- Number of syntactic occurrences
    
    3738 3744
                    , lo_tail  :: !TailCallInfo
    
    3739
    -                   -- Combining (AlwaysTailCalled 2) and (AlwaysTailCalled 3)
    
    3745
    +                   -- NB: combining 'TailCallInfo's with different arities
    
    3740 3746
                        -- gives NoTailCallInfo
    
    3741 3747
                   , lo_int_cxt :: !InterestingCxt }
    
    3742 3748
     
    
    ... ... @@ -3829,9 +3835,20 @@ mkOneOcc !env id int_cxt arity
    3829 3835
       = mkSimpleDetails (unitVarEnv id occ)
    
    3830 3836
     
    
    3831 3837
       where
    
    3832
    -    occ = OneOccL { lo_n_br = 1
    
    3833
    -                  , lo_int_cxt = int_cxt
    
    3834
    -                  , lo_tail = AlwaysTailCalled arity (occ_prof_ticks env) }
    
    3838
    +    occ =
    
    3839
    +      OneOccL
    
    3840
    +        { lo_n_br = 1
    
    3841
    +        , lo_int_cxt = int_cxt
    
    3842
    +        , lo_tail =
    
    3843
    +            AlwaysTailCalled
    
    3844
    +              { tailCallArity = arity
    
    3845
    +
    
    3846
    +                -- See Note [Quasi join points] for justification of these
    
    3847
    +                -- two fields.
    
    3848
    +              , tailCallUnderProfTicks = occ_prof_ticks env
    
    3849
    +              , tailCallUnderCasts     = occ_casts env
    
    3850
    +              }
    
    3851
    +        }
    
    3835 3852
     
    
    3836 3853
     -- Add several occurrences, assumed not to be tail calls
    
    3837 3854
     add_many_occ :: Var -> OccInfoEnv -> OccInfoEnv
    
    ... ... @@ -4040,7 +4057,7 @@ tagNonRecBinder :: TopLevelFlag -- At top level?
    4040 4057
     -- Precondition: OccInfo is not IAmDead
    
    4041 4058
     tagNonRecBinder lvl occ bndr
    
    4042 4059
       | okForJoinPoint lvl bndr tail_call_info
    
    4043
    -  , AlwaysTailCalled ar _ <- tail_call_info
    
    4060
    +  , AlwaysTailCalled { tailCallArity = ar } <- tail_call_info
    
    4044 4061
       = (setBinderOcc occ bndr,        JoinPoint ar)
    
    4045 4062
       | otherwise
    
    4046 4063
       = (setBinderOcc zapped_occ bndr, NotJoinPoint)
    
    ... ... @@ -4127,7 +4144,7 @@ okForJoinPoint lvl bndr tail_call_info
    4127 4144
       = False
    
    4128 4145
       where
    
    4129 4146
         valid_join | NotTopLevel <- lvl
    
    4130
    -               , AlwaysTailCalled arity _ <- tail_call_info
    
    4147
    +               , AlwaysTailCalled { tailCallArity = arity } <- tail_call_info
    
    4131 4148
     
    
    4132 4149
                    , -- Invariant 1 as applied to LHSes of rules
    
    4133 4150
                      all (ok_rule arity) (idCoreRules bndr)
    
    ... ... @@ -4144,9 +4161,9 @@ okForJoinPoint lvl bndr tail_call_info
    4144 4161
     
    
    4145 4162
         lost_join | JoinPoint ja <- idJoinPointHood bndr
    
    4146 4163
                   = not valid_join ||
    
    4147
    -                (case tail_call_info of  -- Valid join but arity differs
    
    4148
    -                   AlwaysTailCalled ja' _ -> ja /= ja'
    
    4149
    -                   _                      -> False)
    
    4164
    +                (case tail_call_info of -- Valid join but arity differs
    
    4165
    +                   AlwaysTailCalled { tailCallArity = ja' } -> ja /= ja'
    
    4166
    +                   _ -> False)
    
    4150 4167
                   | otherwise = False
    
    4151 4168
     
    
    4152 4169
         ok_rule _ BuiltinRule{} = False -- only possible with plugin shenanigans
    
    ... ... @@ -4168,7 +4185,7 @@ okForJoinPoint lvl bndr tail_call_info
    4168 4185
                  , text "tc:" <+> ppr tail_call_info
    
    4169 4186
                  , text "rules:" <+> ppr (idCoreRules bndr)
    
    4170 4187
                  , case tail_call_info of
    
    4171
    -                 AlwaysTailCalled arity _ ->
    
    4188
    +                 AlwaysTailCalled { tailCallArity = arity } ->
    
    4172 4189
                         vcat [ text "ok_unf:" <+> ppr (ok_unfolding arity (realIdUnfolding bndr))
    
    4173 4190
                              , text "ok_type:" <+> ppr (isValidJoinPointType arity (idType bndr)) ]
    
    4174 4191
                      _ -> empty ]
    
    ... ... @@ -4231,6 +4248,6 @@ orLocalOcc (OneOccL { lo_n_br = nbr1, lo_int_cxt = int_cxt1, lo_tail = tci1 })
    4231 4248
     orLocalOcc occ1 occ2 = andLocalOcc occ1 occ2
    
    4232 4249
     
    
    4233 4250
     andTailCallInfo :: TailCallInfo -> TailCallInfo -> TailCallInfo
    
    4234
    -andTailCallInfo (AlwaysTailCalled arity1 p1) (AlwaysTailCalled arity2 p2)
    
    4235
    -  | arity1 == arity2 = AlwaysTailCalled arity1 (max p1 p2)
    
    4251
    +andTailCallInfo (AlwaysTailCalled arity1 p1 c1) (AlwaysTailCalled arity2 p2 c2)
    
    4252
    +  | arity1 == arity2 = AlwaysTailCalled arity1 (max p1 p2) (max c1 c2)
    
    4236 4253
     andTailCallInfo _ _  = NoTailCallInfo

  • compiler/GHC/Core/Opt/Simplify/Env.hs
    ... ... @@ -201,7 +201,8 @@ data SimplEnv
    201 201
     
    
    202 202
           , seCaseDepth   :: !Int  -- Depth of multi-branch case alternatives
    
    203 203
     
    
    204
    -      , seProfTicks   :: !Int  -- SLD TODO
    
    204
    +      , seProfTicks   :: !Int  -- Current depth of profiling ticks; see Note [Quasi join points]
    
    205
    +      , seCasts       :: !Int  -- Current depth of casts; see Note [Quasi join points]
    
    205 206
     
    
    206 207
           , seInlineDepth :: !Int  -- 0 initially, 1 when we inline an already-simplified
    
    207 208
                                    -- unfolding, and simplify again; and so on
    
    ... ... @@ -591,6 +592,7 @@ mkSimplEnv mode fam_envs
    591 592
                  , seRecIds      = emptyUnVarSet
    
    592 593
                  , seCaseDepth   = 0
    
    593 594
                  , seProfTicks   = 0
    
    595
    +             , seCasts       = 0
    
    594 596
                  , seInlineDepth = 0 }
    
    595 597
             -- The top level "enclosing CC" is "SUBSUMED".
    
    596 598
     
    

  • compiler/GHC/Core/Opt/Simplify/Iteration.hs
    ... ... @@ -61,7 +61,7 @@ import GHC.Types.Var ( isTyCoVar )
    61 61
     import GHC.Builtin.Types.Prim( realWorldStatePrimTy )
    
    62 62
     import GHC.Builtin.Names( runRWKey, seqHashKey )
    
    63 63
     
    
    64
    -import GHC.Data.Maybe   ( isNothing, orElse, fromMaybe, mapMaybe )
    
    64
    +import GHC.Data.Maybe   ( isNothing, orElse, mapMaybe )
    
    65 65
     import GHC.Data.FastString
    
    66 66
     import GHC.Unit.Module ( moduleName )
    
    67 67
     import GHC.Utils.Outputable
    
    ... ... @@ -1684,39 +1684,54 @@ optOutCoercion env co already_optimised
    1684 1684
         empty_subst = mkEmptySubst (seInScope env)
    
    1685 1685
         opts = seOptCoercionOpts env
    
    1686 1686
     
    
    1687
    +-- | Number of casts we are adding around an expression as we process a 'Cast'.
    
    1688
    +--
    
    1689
    +-- We need the cast depth to implement the logic of Note [Quasi join points].
    
    1690
    +type NbCastsAdded = Int
    
    1691
    +
    
    1687 1692
     simplCast :: SimplEnv -> InExpr -> InCoercion -> SimplCont
    
    1688 1693
               -> SimplM (SimplFloats, OutExpr)
    
    1689 1694
     simplCast env body co0 cont0
    
    1690 1695
       = do  { co1   <- {-#SCC "simplCast-simplCoercion" #-} simplCoercion env co0
    
    1691
    -        ; cont1 <- {-#SCC "simplCast-addCoerce" #-}
    
    1692
    -                   if isReflCo co1
    
    1693
    -                   then return cont0  -- See Note [Optimising reflexivity]
    
    1694
    -                   else addCoerce co1 True cont0
    
    1695
    -                        -- True <=> co1 is optimised
    
    1696
    -        ; {-#SCC "simplCast-simplExprF" #-} simplExprF env body cont1 }
    
    1696
    +        ; (cont1, nbAddedCasts) <- {-#SCC "simplCast-addCoerce" #-}
    
    1697
    +            if isReflCo co1
    
    1698
    +            then return (cont0, 0) -- See Note [Optimising reflexivity]
    
    1699
    +            else addCoerce co1 True cont0
    
    1700
    +                 -- True <=> co1 is optimised
    
    1701
    +
    
    1702
    +          -- Keep track of how many casts we have added, because we need this
    
    1703
    +          -- information for Note [Quasi join points].
    
    1704
    +        ; let env' = env { seCasts = seCasts env + nbAddedCasts }
    
    1705
    +        ; {-#SCC "simplCast-simplExprF" #-} simplExprF env' body cont1 }
    
    1697 1706
       where
    
    1698 1707
     
    
    1699 1708
             -- If the first parameter is MRefl, then simplifying revealed a
    
    1700 1709
             -- reflexive coercion. Omit.
    
    1701
    -        addCoerceM :: MOutCoercion -> Bool -> SimplCont -> SimplM SimplCont
    
    1702
    -        addCoerceM MRefl    _   cont = return cont
    
    1710
    +        addCoerceM :: MOutCoercion -> Bool -> SimplCont -> SimplM (SimplCont, NbCastsAdded)
    
    1711
    +        addCoerceM MRefl    _   cont = return (cont, 0)
    
    1703 1712
             addCoerceM (MCo co) opt cont = addCoerce co opt cont
    
    1704 1713
     
    
    1705
    -        addCoerce :: OutCoercion -> Bool -> SimplCont -> SimplM SimplCont
    
    1714
    +        addCoerce :: OutCoercion -> Bool -> SimplCont -> SimplM (SimplCont, NbCastsAdded)
    
    1706 1715
             addCoerce co1 _ (CastIt { sc_co = co2, sc_cont = cont })  -- See Note [Optimising reflexivity]
    
    1707
    -          = addCoerce (mkTransCo co1 co2) False cont
    
    1708
    -                      -- False: (mkTransCo co1 co2) is not fully optimised
    
    1709
    -                      -- See Note [Avoid re-simplifying coercions]
    
    1716
    +          = do { (cont', nbCastsAdded) <- addCoerce (mkTransCo co1 co2) False cont
    
    1717
    +                                -- False: (mkTransCo co1 co2) is not fully optimised
    
    1718
    +                                -- See Note [Avoid re-simplifying coercions]
    
    1719
    +               ; return (cont', nbCastsAdded - 1)
    
    1720
    +                  -- -1: the coercion coalesced with an existing coercion.
    
    1721
    +               }
    
    1710 1722
     
    
    1711 1723
             addCoerce co co_is_opt (ApplyToTy { sc_arg_ty = arg_ty, sc_cont = tail })
    
    1712 1724
               | Just (arg_ty', m_co') <- pushCoTyArg co arg_ty
    
    1713 1725
               = {-#SCC "addCoerce-pushCoTyArg" #-}
    
    1714
    -            do { tail' <- addCoerceM m_co' co_is_opt tail
    
    1715
    -               ; return (ApplyToTy { sc_arg_ty  = arg_ty'
    
    1716
    -                                   , sc_cont    = tail'
    
    1717
    -                                   , sc_hole_ty = coercionLKind co }) }
    
    1718
    -                                        -- NB!  As the cast goes past, the
    
    1719
    -                                        -- type of the hole changes (#16312)
    
    1726
    +            do { (tail', nbCastsAdded) <- addCoerceM m_co' co_is_opt tail
    
    1727
    +               ; return ( ApplyToTy { sc_arg_ty  = arg_ty'
    
    1728
    +                                    , sc_cont    = tail'
    
    1729
    +                                    , sc_hole_ty = coercionLKind co }
    
    1730
    +                                      -- NB!  As the cast goes past, the
    
    1731
    +                                      -- type of the hole changes (#16312)
    
    1732
    +                        , nbCastsAdded )
    
    1733
    +               }
    
    1734
    +
    
    1720 1735
             -- (f |> co) e   ===>   (f (e |> co1)) |> co2
    
    1721 1736
             -- where   co :: (s1->s2) ~ (t1->t2)
    
    1722 1737
             --         co1 :: t1 ~ s1
    
    ... ... @@ -1729,10 +1744,12 @@ simplCast env body co0 cont0
    1729 1744
     
    
    1730 1745
               | Just (m_co1, m_co2) <- pushCoValArg co
    
    1731 1746
               = {-#SCC "addCoerce-pushCoValArg" #-}
    
    1732
    -            do { tail' <- addCoerceM m_co2 co_is_opt tail
    
    1747
    +            do { (tail', nbCastsAdded) <- addCoerceM m_co2 co_is_opt tail
    
    1733 1748
                    ; case m_co1 of {
    
    1734
    -                   MRefl -> return (cont { sc_cont = tail'
    
    1735
    -                                         , sc_hole_ty = coercionLKind co }) ;
    
    1749
    +                   MRefl -> return
    
    1750
    +                     ( cont { sc_cont = tail'
    
    1751
    +                            , sc_hole_ty = coercionLKind co }
    
    1752
    +                     , nbCastsAdded ) ;
    
    1736 1753
                           -- See Note [Avoiding simplifying repeatedly]
    
    1737 1754
     
    
    1738 1755
                        MCo co1 ->
    
    ... ... @@ -1742,17 +1759,23 @@ simplCast env body co0 cont0
    1742 1759
                         -- to make it all consistent.  It's a bit messy.
    
    1743 1760
                         -- But it isn't a common case.
    
    1744 1761
                         -- Example of use: #995
    
    1745
    -               ; return (ApplyToVal { sc_arg  = mkCast arg' co1
    
    1746
    -                                    , sc_env  = arg_se'
    
    1747
    -                                    , sc_dup  = dup'
    
    1748
    -                                    , sc_cont = tail'
    
    1749
    -                                    , sc_hole_ty = coercionLKind co }) } } }
    
    1762
    +               ; return
    
    1763
    +                   ( ApplyToVal { sc_arg  = mkCast arg' co1
    
    1764
    +                                , sc_env  = arg_se'
    
    1765
    +                                , sc_dup  = dup'
    
    1766
    +                                , sc_cont = tail'
    
    1767
    +                                , sc_hole_ty = coercionLKind co }
    
    1768
    +                   , nbCastsAdded ) } } }
    
    1750 1769
     
    
    1751 1770
             addCoerce co co_is_opt cont
    
    1752
    -          | isReflCo co = return cont  -- Having this at the end makes a huge
    
    1753
    -                                       -- difference in T12227, for some reason
    
    1754
    -                                       -- See Note [Optimising reflexivity]
    
    1755
    -          | otherwise = return (CastIt { sc_co = co, sc_opt = co_is_opt, sc_cont = cont })
    
    1771
    +          | isReflCo co = return (cont, 0 :: NbCastsAdded )
    
    1772
    +            -- Having this at the end makes a huge
    
    1773
    +            -- difference in T12227, for some reason
    
    1774
    +            -- See Note [Optimising reflexivity]
    
    1775
    +          | otherwise =
    
    1776
    +            return
    
    1777
    +              ( CastIt { sc_co = co, sc_opt = co_is_opt, sc_cont = cont }
    
    1778
    +              , 1 :: NbCastsAdded )
    
    1756 1779
     
    
    1757 1780
     simplLazyArg :: SimplEnvIS              -- ^ Used only for its InScopeSet
    
    1758 1781
                  -> DupFlag
    
    ... ... @@ -2067,12 +2090,10 @@ simplNonRecJoinPoint env bndr rhs body cont
    2067 2090
             ; (floats2, body') <- simplExprF env3 body cont
    
    2068 2091
             ; return (floats1 `addFloats` floats2, body') }
    
    2069 2092
       where
    
    2070
    -    do_case_case
    
    2071
    -      | Just occMaxProfTicks <- occursUnderProfTick (idOccInfo bndr)
    
    2072
    -      , occMaxProfTicks > seProfTicks env
    
    2073
    -      = False
    
    2074
    -      | otherwise
    
    2075
    -      = seCaseCase env
    
    2093
    +    do_case_case =
    
    2094
    +      if isTrueJoinPoint env bndr
    
    2095
    +      then seCaseCase env
    
    2096
    +      else False
    
    2076 2097
     
    
    2077 2098
     simplRecJoinPoint :: SimplEnv -> [(InId, InExpr)]
    
    2078 2099
                       -> InExpr -> SimplCont
    
    ... ... @@ -2089,11 +2110,26 @@ simplRecJoinPoint env pairs body cont
    2089 2110
            ; (floats2, body') <- simplExprF env2 body cont
    
    2090 2111
            ; return (floats1 `addFloats` floats2, body') }
    
    2091 2112
       where
    
    2092
    -    do_case_case
    
    2093
    -      | any ((seProfTicks env <) . fromMaybe 0 . occursUnderProfTick . idOccInfo . fst) pairs
    
    2094
    -      = False
    
    2095
    -      | otherwise
    
    2096
    -      = seCaseCase env
    
    2113
    +    do_case_case =
    
    2114
    +      if all (isTrueJoinPoint env . fst) pairs
    
    2115
    +      then seCaseCase env
    
    2116
    +      else False
    
    2117
    +
    
    2118
    +-- | Is this a true join point, or only a quasi join point?
    
    2119
    +--
    
    2120
    +-- See Note [Quasi join points]
    
    2121
    +isTrueJoinPoint :: SimplEnv -> InId -> Bool
    
    2122
    +isTrueJoinPoint env id
    
    2123
    +  | Just occMaxProfTicks <- occursUnderProfTicks (idOccInfo id)
    
    2124
    +  , occMaxProfTicks > seProfTicks env
    
    2125
    +  -- The join point occurs under more profiling ticks that its binding.
    
    2126
    +  = False
    
    2127
    +  | Just occMaxCasts <- occursUnderCasts (idOccInfo id)
    
    2128
    +  , occMaxCasts > seCasts env
    
    2129
    +  -- The join point occurs under more casts than its binding.
    
    2130
    +  = False
    
    2131
    +  | otherwise
    
    2132
    +  = True
    
    2097 2133
     
    
    2098 2134
     --------------------
    
    2099 2135
     wrapJoinCont :: Bool
    
    ... ... @@ -2217,6 +2253,100 @@ inwards altogether at any join point. Instead simplify the (join ... in ...)
    2217 2253
     with a Stop continuation, and wrap the original continuation around the
    
    2218 2254
     outside.  Surprisingly tricky!
    
    2219 2255
     
    
    2256
    +Note [Quasi join points]
    
    2257
    +~~~~~~~~~~~~~~~~~~~~~~~~
    
    2258
    +We currently classify join points into two separate categories
    
    2259
    +
    
    2260
    +  - true join points
    
    2261
    +  - quasi join points
    
    2262
    +
    
    2263
    +Definition:
    
    2264
    +  A join point binding defines a *quasi* join point if any of the join point
    
    2265
    +  binders occur under profiling ticks or casts.
    
    2266
    +
    
    2267
    +  If a join point binding is not a quasi join point, it is a *true* join point.
    
    2268
    +
    
    2269
    +For true join points, we can push a continuation into a join point, as described
    
    2270
    +in Note [Join points and case-of-case]:
    
    2271
    +
    
    2272
    +  K[ join j = rhs in body ]  -->   join j = K[ rhs ] in K[ body ]
    
    2273
    +
    
    2274
    +This transformation is not valid if the occurrences of 'j' in 'body' appear:
    
    2275
    +
    
    2276
    +  1. under casts, see #26422
    
    2277
    +  2. under profiling ticks, see #26693 #26157 #26642
    
    2278
    +
    
    2279
    +For example, consider (a minimisation of) the program in #26693:
    
    2280
    +
    
    2281
    +  join { j :: Bool -> IO (); j _ = guts }
    
    2282
    +  in case pass of
    
    2283
    +    False -> scctick<foo> jump j True
    
    2284
    +    True  -> scctick<bar> jump j False
    
    2285
    +
    
    2286
    +Let's try to push an application to an argument 'arg' into this expression.
    
    2287
    +As per Note [Join points and case-of-case], we proceed by first applying the
    
    2288
    +argument to both the join point RHS and the case alternatives:
    
    2289
    +
    
    2290
    +  join { j :: Bool -> IO (); j _ = guts arg ] }
    
    2291
    +    in case pass of
    
    2292
    +      False -> (scctick<foo> jump j True ) arg
    
    2293
    +      True  -> (scctick<bar> jump j False) arg
    
    2294
    +
    
    2295
    +Then we rely on 'trimJoinCont' to remove the argument, but this fails because
    
    2296
    +there are intervening profiling ticks. Even if we addressed that issue, it
    
    2297
    +remains unclear what to do without misattributing costs.
    
    2298
    +We could transform to the following:
    
    2299
    +
    
    2300
    +  join { j :: Bool -> IO (); j scc _ = (setSCC# scc guts) arg ] }
    
    2301
    +    in case pass of
    
    2302
    +      False -> jump j <foo> True
    
    2303
    +      True  -> jump j <bar> False
    
    2304
    +
    
    2305
    +where `setSCC#` is a new primop that would set the current cost centre point.
    
    2306
    +This doesn't exist yet, so for now we just disallow the case-of-case
    
    2307
    +transformation for 'j'.
    
    2308
    +
    
    2309
    +Similarly for casts:
    
    2310
    +
    
    2311
    +    join { j = blah }
    
    2312
    +      in case e of
    
    2313
    +        False -> j True  |> co1
    
    2314
    +        True  -> j False |> co2
    
    2315
    +
    
    2316
    +if we want to apply this to an argument 'arg', we would need to perform the
    
    2317
    +following transformation:
    
    2318
    +
    
    2319
    +    join { j co = ( blah |> co ) arg }
    
    2320
    +      in case e of
    
    2321
    +        False -> j co1 True
    
    2322
    +        True  -> j co2 False
    
    2323
    +
    
    2324
    +in which we add a coercion argument to the join point. Again, this is not a
    
    2325
    +transformation we currently implement, so we instead prevent case-of-case for
    
    2326
    +such join points.
    
    2327
    +
    
    2328
    +To achieve this classification, we proceed as follows:
    
    2329
    +
    
    2330
    +  1. In occurrence analysis, compute how many profiling ticks/casts each
    
    2331
    +     join point Id occurs under.
    
    2332
    +
    
    2333
    +     This is stored in the 'tailCallUnderProfTicks' and 'tailCallUnderCasts'
    
    2334
    +     fields of 'TailCallInfo', and populated by keeping track of how many
    
    2335
    +     profiling ticks and casts we are under when doing occurrence analysis
    
    2336
    +     (see 'occ_prof_ticks' and 'occ_casts').
    
    2337
    +
    
    2338
    +  2. In the simplifier, we keep track of how many profiling ticks/casts we are
    
    2339
    +     currently inside.  See 'seProfTicks' and 'seCasts', which are updated
    
    2340
    +     in 'simplTick' and 'simplCast', respectively.
    
    2341
    +
    
    2342
    +  3. In the simplifier, when we come across a join point (in either
    
    2343
    +     'simplNonRecJoinPoint' or 'simplRecJoinPoint'), we compare the current
    
    2344
    +     cast depth/profiling tick depth with the cast depth/profiling tick depth
    
    2345
    +     of the occurrences.
    
    2346
    +
    
    2347
    +     If the join point occurs under more profiling ticks/casts than it is bound,
    
    2348
    +     then it is a quasi join point and we switch off the case-of-case
    
    2349
    +     transformation.
    
    2220 2350
     
    
    2221 2351
     ************************************************************************
    
    2222 2352
     *                                                                      *
    

  • compiler/GHC/Core/SimpleOpt.hs
    ... ... @@ -1076,7 +1076,7 @@ joinPointBinding_maybe bndr rhs
    1076 1076
       | isJoinId bndr
    
    1077 1077
       = Just (bndr, rhs)
    
    1078 1078
     
    
    1079
    -  | AlwaysTailCalled join_arity _ <- tailCallInfo (idOccInfo bndr)
    
    1079
    +  | AlwaysTailCalled { tailCallArity = join_arity } <- tailCallInfo (idOccInfo bndr)
    
    1080 1080
       , (bndrs, body) <- etaExpandToJoinPoint join_arity rhs
    
    1081 1081
       , let str_sig   = idDmdSig bndr
    
    1082 1082
             str_arity = count isId bndrs  -- Strictness demands are for Ids only
    

  • compiler/GHC/Types/Basic.hs
    ... ... @@ -70,7 +70,7 @@ module GHC.Types.Basic (
    70 70
             BranchCount, oneBranch,
    
    71 71
             InterestingCxt(..),
    
    72 72
             TailCallInfo(..), tailCallInfo, zapOccTailCallInfo,
    
    73
    -        isAlwaysTailCalled, occursUnderProfTick,
    
    73
    +        isAlwaysTailCalled, occursUnderProfTicks, occursUnderCasts,
    
    74 74
     
    
    75 75
             EP(..),
    
    76 76
     
    
    ... ... @@ -1149,8 +1149,14 @@ instance Monoid InsideLam where
    1149 1149
       mappend = (Semi.<>)
    
    1150 1150
     
    
    1151 1151
     -----------------
    
    1152
    +
    
    1153
    +-- | See Note [TailCallInfo]
    
    1152 1154
     data TailCallInfo
    
    1153
    -  = AlwaysTailCalled {-# UNPACK #-} !JoinArity !Int-- See Note [TailCallInfo]
    
    1155
    +  = AlwaysTailCalled
    
    1156
    +     { tailCallArity :: {-# UNPACK #-} !JoinArity
    
    1157
    +     , tailCallUnderProfTicks :: !Int -- See Note [Quasi join points]
    
    1158
    +     , tailCallUnderCasts     :: !Int -- See Note [Quasi join points]
    
    1159
    +     }
    
    1154 1160
       | NoTailCallInfo
    
    1155 1161
       deriving (Eq)
    
    1156 1162
     
    
    ... ... @@ -1167,15 +1173,26 @@ isAlwaysTailCalled occ
    1167 1173
       = case tailCallInfo occ of AlwaysTailCalled{} -> True
    
    1168 1174
                                  NoTailCallInfo     -> False
    
    1169 1175
     
    
    1170
    -occursUnderProfTick :: OccInfo -> Maybe Int
    
    1171
    -occursUnderProfTick occ =
    
    1176
    +-- | If this 'Id' is always tail called, how many profiling ticks does
    
    1177
    +-- it occur under? See Note [Quasi join points].
    
    1178
    +occursUnderProfTicks :: OccInfo -> Maybe Int
    
    1179
    +occursUnderProfTicks occ =
    
    1172 1180
       case tailCallInfo occ of
    
    1173
    -    AlwaysTailCalled _ b -> Just b
    
    1181
    +    AlwaysTailCalled { tailCallUnderProfTicks = nb } -> Just nb
    
    1182
    +    NoTailCallInfo -> Nothing
    
    1183
    +
    
    1184
    +-- | If this 'Id' is always tail called, how many casts does
    
    1185
    +-- it occur under? See Note [Quasi join points].
    
    1186
    +occursUnderCasts :: OccInfo -> Maybe Int
    
    1187
    +occursUnderCasts occ =
    
    1188
    +  case tailCallInfo occ of
    
    1189
    +    AlwaysTailCalled { tailCallUnderCasts = nb } -> Just nb
    
    1174 1190
         NoTailCallInfo -> Nothing
    
    1175 1191
     
    
    1176 1192
     instance Outputable TailCallInfo where
    
    1177
    -  ppr (AlwaysTailCalled ar b) = sep [ text "Tail", brackets (int b), int ar ]
    
    1178
    -  ppr _                       = text "NoTailCallInfo" --empty
    
    1193
    +  ppr (AlwaysTailCalled ar p c) =
    
    1194
    +    sep [ text "Tail", brackets (int p <> comma <> int c), int ar ]
    
    1195
    +  ppr NoTailCallInfo = text "NoTailCallInfo"
    
    1179 1196
     
    
    1180 1197
     -----------------
    
    1181 1198
     strongLoopBreaker, weakLoopBreaker :: OccInfo
    
    ... ... @@ -1223,8 +1240,10 @@ instance Outputable OccInfo where
    1223 1240
               pp_tail                = pprShortTailCallInfo tail_info
    
    1224 1241
     
    
    1225 1242
     pprShortTailCallInfo :: TailCallInfo -> SDoc
    
    1226
    -pprShortTailCallInfo (AlwaysTailCalled ar p)
    
    1227
    -  = char 'T' <> (brackets (text "P" <+> int p)) <> brackets (int ar)
    
    1243
    +pprShortTailCallInfo (AlwaysTailCalled ar p c)
    
    1244
    +  = char 'T' <> (brackets (text "P" <+> int p))
    
    1245
    +             <> (brackets (text "C" <+> int c))
    
    1246
    +             <> brackets (int ar)
    
    1228 1247
     pprShortTailCallInfo NoTailCallInfo        = empty
    
    1229 1248
     
    
    1230 1249
     {-
    
    ... ... @@ -1258,6 +1277,9 @@ point can also be invoked from other join points, not just from case branches:
    1258 1277
     Here both 'j1' and 'j2' will get marked AlwaysTailCalled, but j1 will get
    
    1259 1278
     ManyOccs and j2 will get `OneOcc { occ_n_br = 2 }`.
    
    1260 1279
     
    
    1280
    +We also store how many profiling ticks and casts the join point occurs under.
    
    1281
    +The rationale is described in Note [Quasi join points].
    
    1282
    +
    
    1261 1283
     ************************************************************************
    
    1262 1284
     *                                                                      *
    
    1263 1285
                     Default method specification