Skip to content

Exit join points inhibit CPR

Consider

f :: Int -> IO Int
f x = return $! sum [0..x]
{-# NOINLINE f #-}

I would expect CPR to unbox the nested Int. Yet it doesn't. Here's the Core after CPR analysis:

f_s15n
  = \ (x_aup [Dmd=1!L] :: Int)
      (eta_B0 [OS=OneShot] :: GHC.Prim.State# GHC.Prim.RealWorld) ->
      case x_aup of { GHC.Types.I# ipv_sP7 ->
      case GHC.Prim.># 0# ipv_sP7 of {
        __DEFAULT ->
          join {
            $j_s15t [Dmd=SCS(C1(!L))]
              :: GHC.Prim.Int#
                 -> Int -> (# GHC.Prim.State# GHC.Prim.RealWorld, Int #)
            [LclId[JoinId(2)(Nothing)], Arity=2, Str=<A><L>, Cpr=1]
            $j_s15t _ [Occ=Dead, Dmd=A, OS=OneShot]
                    (vx_aP2 [OS=OneShot]
                       :: Int
                       Unf=OtherCon [])
              = (# eta_B0, vx_aP2 #) } in
          joinrec {
            go3_a153 [Occ=LoopBreaker, Dmd=SCS(C1(!L))]
              :: GHC.Prim.Int#
                 -> Int -> (# GHC.Prim.State# GHC.Prim.RealWorld, Int #)
            [LclId[JoinId(2)(Nothing)],
             Arity=2,
             Str=<L><1!L> {s15t->SCS(C1(!L))},
             Cpr=1,
             Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True,
                     WorkFree=True, Expandable=True, Guidance=IF_ARGS [0 20] 66 0}]
            go3_a153 (x_a154 :: GHC.Prim.Int#)
                     (v_a13a [Dmd=1!L, OS=OneShot] :: Int)
              = case v_a13a of { GHC.Types.I# ipv_s14C ->
                case GHC.Prim.==# x_a154 ipv_sP7 of {
                  __DEFAULT ->
                    jump go3_a153
                      (GHC.Prim.+# x_a154 1#)
                      (GHC.Types.I# (GHC.Prim.+# ipv_s14C x_a154));
                  1# ->
                    let {
                      ipv_s158 :: GHC.Prim.Int#
                      [LclId,
                       Unf=Unf{Src=<vanilla>, TopLvl=False, Value=False, ConLike=False,
                               WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 1 0}]
                      ipv_s158 = GHC.Prim.+# ipv_s14C x_a154 } in
                    jump $j_s15t ipv_s158 (GHC.Types.I# ipv_s158)
                }
                }; } in
          jump go3_a153 0# lvl_s15p;
        1# -> (# eta_B0, lvl_s15p #)
      }
      }

Note that the single jump to $j_s15t passes the integer boxed. Unfortunately, we don't see that in the join body. (And neither do we have the SubDemand vocabulary that a call is with a certain boxity in the argument.)

As a result, we can't unbox the nestedly returned Int.

The short-term fix is simple: Inline $j. IMO its existence does more harm than good.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information