Note [Scrutinee Constant Folding]
case x op# k# of _ { ===> case x of _ {
a1# -> e1 (a1# inv_op# k#) -> e1
a2# -> e2 (a2# inv_op# k#) -> e2
... ...
DEFAULT -> ed DEFAULT -> ed
where (x op# k#) inv_op# k# == x
And similarly for commuted arguments and for some unary operations.
The purpose of this transformation is not only to avoid an arithmetic
operation at runtime but to allow other transformations to apply in cascade.
Example with the "Merge Nested Cases" optimization (from #12877):
main = case t of t0
0## -> ...
DEFAULT -> case t0 `minusWord#` 1## of t1
0## -> ...
DEFAULT -> case t1 `minusWord#` 1## of t2
0## -> ...
DEFAULT -> case t2 `minusWord#` 1## of _
0## -> ...
DEFAULT -> ...
becomes:
main = case t of _
0## -> ...
1## -> ...
2## -> ...
3## -> ...
DEFAULT -> ...
There are some wrinkles.
Wrinkle 1:
Do not apply caseRules if there is just a single DEFAULT alternative,
unless the case-binder is dead. Example:
case e +# 3# of b { DEFAULT -> rhs }
If we applied the transformation here we would (stupidly) get
case e of b' { DEFAULT -> let b = b' +# 3# in rhs }
and now the process may repeat, because that let will really
be a case. But if the original case binder b is dead, we instead get
case e of b' { DEFAULT -> rhs }
and there is no such problem.
See Note [Example of case-merging and caseRules] for a compelling
example of why this dead-binder business can be really important.
Wrinkle 2:
The type of the scrutinee might change. E.g.
case tagToEnum (x :: Int#) of (b::Bool)
False -> e1
True -> e2
==>
case x of (b'::Int#)
DEFAULT -> e1
1# -> e2
Wrinkle 3:
The case binder may be used in the right hand sides, so we need
to make a local binding for it, if it is alive. e.g.
case e +# 10# of b
DEFAULT -> blah...b...
44# -> blah2...b...
===>
case e of b'
DEFAULT -> let b = b' +# 10# in blah...b...
34# -> let b = 44# in blah2...b...
Note that in the non-DEFAULT cases we know what to bind 'b' to,
whereas in the DEFAULT case we must reconstruct the original value.
But NB: we use b'; we do not duplicate 'e'.
Wrinkle 4:
In dataToTag we might need to make up some fake binders;
see Note [caseRules for dataToTag] in GHC.Core.Opt.ConstantFold References 2
- caseRules for dataToTag GHC.Core.Opt.ConstantFold
- Example of case-merging and caseRules GHC.Core.Opt.Simplify.Utils
Referenced by 3
- GHC.Core.Opt.Simplify.Utils call site ×3