DmdAnal: Don't reduce trivial product demands to polymorphic counterparts
Consider (from #18231)
inc :: State Int ()
inc = modify' (+1)
m :: State Int ()
m = forever inc
Today, we'll get the following Core after the first round of strictness analysis:
Rec {
-- RHS size: {terms: 6, types: 1, coercions: 0, joins: 0/0}
lvl_sLL
:: GHC.Prim.Int# -> Data.Functor.Identity.Identity ((), Int)
[LclId,
Arity=1,
Str=<B>b,
Cpr=b,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=NEVER}]
lvl_sLL
= \ (x_aLn [Dmd=B] :: GHC.Prim.Int#) ->
a'_sLD (GHC.Types.I# (GHC.Prim.+# x_aLn 1#))
-- RHS size: {terms: 6, types: 3, coercions: 0, joins: 0/0}
a'_sLD [Occ=LoopBreaker]
:: Int -> Data.Functor.Identity.Identity ((), Int)
[LclId,
Arity=1,
Str=<SB>b,
Cpr=b,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=NEVER}]
a'_sLD
= \ (eta2_aL7 [Dmd=SB] :: Int) ->
case eta2_aL7 of { GHC.Types.I# x_aLn [Dmd=B] -> lvl_sLL x_aLn }
end Rec }
-- RHS size: {terms: 1, types: 0, coercions: 5, joins: 0/0}
m :: State Int ()
[LclIdX,
Arity=1,
Str=<SB>b,
Cpr=b,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=True)}]
m = a'_sLI
`cast` (Sym (Control.Monad.Trans.State.Strict.N:StateT[0]
<Int>_N <Data.Functor.Identity.Identity>_R <()>_N)
:: (Int -> Data.Functor.Identity.Identity ((), Int))
~R# StateT Int Data.Functor.Identity.Identity ())
Note the demand SB
on the argument to a'
. Since it is not a product demand, we will not unbox it and go down a strange path of subsequent transformations, outlined here, that ultimately leads to the following simplified Core:
Rec {
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
lvl_rJM :: Data.Functor.Identity.Identity ((), Int)
[GblId, Str=b, Cpr=b]
lvl_rJM = lvl_rJM
end Rec }
-- RHS size: {terms: 5, types: 3, coercions: 0, joins: 0/0}
T18231.m1 :: Int -> Data.Functor.Identity.Identity ((), Int)
[GblId, Arity=1, Str=<SB>b, Cpr=b, Unf=OtherCon []]
T18231.m1
= \ (eta2_aLc :: Int) ->
case eta2_aLc of { GHC.Types.I# x_aLs -> lvl_rJM }
The endless loop has been replaced by a looping thunk that will crash rather than loop. It's not devastating, because it's just trading one imprecise bottom (divergence) for another (imprecise exception).
But if we instead said PS(B)
for the demand on a'
's argument above (so, a product demand), we'd unbox the integer (remember that product demands convey boxity information, e.g. that the thing is used unboxed) and will preserve the endless loop until code gen and get something like
$wa'_sLj = \ (eta2_aKN :: (# #)) -> $wa'_sLj eta2_aKN
a'_sLj
= \ (eta2_aKN [Dmd=<SP(B)>] :: Int) ->
case eta2_aKN of { GHC.Types.I# x_aL3 [Dmd=B] ->
$wa'_sLj (# #)
}
As I said above, this is not yet enough to make a case. But why don't we give a'
's argument the more natural product demand in the first place?
The reason is the use of mkProd
in the Case
case of demand analysis. It performs rewrites along the "semantic equality" P(d,d,...) === d
for any polymorphic demand d
. It's the single remaining call site of mkProd
. The thing is, since P
also conveys boxity information, that semantic equality actually isn't a semantic equality anymore! That's also encoded in Note [Don't optimise UP(U,U,...) to U]
and mkProd
will actually only perform the rewrite if d
is B
or A
.
I argue we drop that function completely and just unbox P(B,B,...)
as well as P(A,A,...)
. It's worth it just for being able to delete mkProd
(and adjust Note [Don't optimise UP(U,U,...) to U]
so that it says why P(d,d,...) === d
does not hold).
Edit: I just realise that we have this special little function that we call in GHC.Core.Opt.WorkWrap.Utils.wantToUnbox
:
split_prod_dmd_arity dmd arity
-- For seqDmd, it should behave like <S(AAAA)>, for some
-- suitable arity
| isSeqDmd dmd = Just (replicate arity absDmd)
| _ :* Prod ds <- dmd = Just ds
| otherwise = Nothing
Note how it painstakingly reverses the collapsing of S(AAAA)
into SA
introduced in mkProd
! So really we are only talking about whether or not we want to collapse S(BBBB)
into SB
.