DmdAnal: `dmdAnalStar` could yield better usage for trivial args and bindings
Consider
f :: (Bool, Bool) -> (Bool, Bool)
f pr = (pr `seq` True, case pr of (a,b) -> a && b)
{-# NOINLINE f #-}
-- idDmdSig f = LP(1L,1L)
g :: (Bool, Bool) -> ()
g pr = f pr `seq` ()
-- idDmdSig g = L
g
simply calls f
, yet g
puts a worse demand on pr
than f
; it loses the information that pr
's components are only evaluated once.
That is due to dmdAnalStar e (n :* sd)
, which is used when analysing the argument pr
in the call f pr
in demand LP(1L,1L)
. That will
- Call
dmdAnal "pr" "P(1L,1L)"
, as ifpr
was evaluated exactly once in sub-demandP(1L,1L)
. We get back[pr |-> 1P(1L,1L)]
(which of course is too optimistic). - Then it worsens the resulting demand type to account for
n=L
, by multiplying it withL
viamultDmdType
. That ultimately callsmultDmd "L"
on the demand ofx
:multDmd L 1P(1L,1L) === LP(LL,LL) === L
.
It's alright to turn the outer 1P(..)
into LP(..)
, but we don't actually need to worsen the inner P(1L,1L)
, because it is the exact demand that is put on the argument already.
NB: This ticket is specifically concerned with upper bounds. The multDmd "L"
situation (with a used more than once upper boudn) only occurs when an arg pr
is trivial (or if the RHS of a bindings is trivial). Otherwise, dmdTransformThunkDmd
anticipates the memoisation of complex expressions and call oneifyDmd
instead. So if we had a call like f (x, True)
instead, then we'd get dmdAnalStar "(x, True)" "1P(1L,1L)"
, e.g., with the oneified demand 1P(..)
and all will be well, as we ultimately get a demand of 1L
on x
.