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.