Note [exprOkForSpeculation: case expressions]
exprOkForSpeculation accepts very special case expressions.
Reason: (a ==# b) is ok-for-speculation, but the litEq rules
in GHC.Core.Opt.ConstantFold convert it (a ==# 3#) to
case a of { DEFAULT -> 0#; 3# -> 1# }
for excellent reasons described in
GHC.Core.Opt.ConstantFold Note [The litEq rule: converting equality to case].
So, annoyingly, we want that case expression to be
ok-for-speculation too. Bother.
But we restrict it sharply:
* We restrict it to unlifted scrutinees. Consider this:
case x of y {
DEFAULT -> ... (let v::Int# = case y of { True -> e1
; False -> e2 }
in ...) ...
Does the RHS of v satisfy the let-can-float invariant? Previously we said
yes, on the grounds that y is evaluated. But the binder-swap done
by GHC.Core.Opt.SetLevels would transform the inner alternative to
DEFAULT -> ... (let v::Int# = case x of { ... }
in ...) ....
which does /not/ satisfy the let-can-float invariant, because x is
not evaluated. See Note [Binder-swap during float-out]
in GHC.Core.Opt.SetLevels. To avoid this awkwardness it seems simpler
to stick to unlifted scrutinees where the issue does not
arise.
* We restrict it to exhaustive alternatives. A non-exhaustive
case manifestly isn't ok-for-speculation. for example,
this is a valid program (albeit a slightly dodgy one)
let v = case x of { B -> ...; C -> ... }
in case x of
A -> ...
_ -> ...v...v....
Should v be considered ok-for-speculation? Its scrutinee may be
evaluated, but the alternatives are incomplete so we should not
evaluate it strictly.
Now, all this is for lifted types, but it'd be the same for any
finite unlifted type. We don't have many of them, but we might
add unlifted algebraic types in due course.
Historical note: #15696: --------
Previously GHC.Core.Opt.SetLevels used exprOkForSpeculation to guide
floating of single-alternative cases; it now uses exprIsHNF
Note [Floating single-alternative cases].
But in those days, consider
case e of x { DEAFULT ->
...(case x of y
A -> ...
_ -> ...(case (case x of { B -> p; C -> p }) of
I# r -> blah)...
If GHC.Core.Opt.SetLevels considers the inner nested case as
ok-for-speculation it can do case-floating (in GHC.Core.Opt.SetLevels).
So we'd float to:
case e of x { DEAFULT ->
case (case x of { B -> p; C -> p }) of I# r ->
...(case x of y
A -> ...
_ -> ...blah...)...
which is utterly bogus (seg fault); see #5453.
Historical note: #3717: --------
foo :: Int -> Int
foo 0 = 0
foo n = (if n < 5 then 1 else 2) `seq` foo (n-1)
In earlier GHCs, we got this:
T.$wfoo =
\ (ww :: GHC.Prim.Int#) ->
case ww of ds {
__DEFAULT -> case (case <# ds 5 of _ {
GHC.Types.False -> lvl1;
GHC.Types.True -> lvl})
of _ { __DEFAULT ->
T.$wfoo (GHC.Prim.-# ds_XkE 1) };
0 -> 0 }
Before join-points etc we could only get rid of two cases (which are
redundant) by recognising that the (case <# ds 5 of { ... }) is
ok-for-speculation, even though it has /lifted/ type. But now join
points do the job nicely.
End of historical note ------------ References 2
- The litEq rule: converting equality to case GHC.Core.Opt.ConstantFold
- Floating single-alternative cases GHC.Core.Opt.SetLevels
Referenced by 1
- GHC.Core.Utils call site