Note [Case to let transformation]
If a case over a lifted type has a single alternative, and is being
used as a strict 'let' (all isDeadBinder bndrs), we may want to do
this transformation:
case e of r ===> let r = e in ...r...
_ -> ...r...
We treat the unlifted and lifted cases separately:
* Unlifted case: 'e' satisfies exprOkForSpeculation
(ok-for-spec is needed to satisfy the let-can-float invariant).
This turns case a +# b of r -> ...r...
into let r = a +# b in ...r...
and thence .....(a +# b)....
However, if we have
case indexArray# a i of r -> ...r...
we might like to do the same, and inline the (indexArray# a i).
But indexArray# is not okForSpeculation, so we don't build a let
in rebuildCase (lest it get floated *out*), so the inlining doesn't
happen either. Annoying.
* Lifted case: we need to be sure that the expression is already
evaluated (exprIsHNF). If it's not already evaluated
- we risk losing exceptions, divergence or
user-specified thunk-forcing
- even if 'e' is guaranteed to converge, we don't want to
create a thunk (call by need) instead of evaluating it
right away (call by value)
However, we can turn the case into a /strict/ let if the 'r' is
used strictly in the body. Then we won't lose divergence; and
we won't build a thunk because the let is strict.
See also Note [Case-to-let for strictly-used binders] References 1
- Case-to-let for strictly-used binders GHC.Core.Opt.Simplify.Iteration
Referenced by 2
- GHC.Core.Make call site
- GHC.Core.Opt.Simplify.Iteration call site