Note [Example of case-merging and caseRules]
The case-transformation rules are quite powerful. Here's a subtle example from #22375. We start with data T = A | B | ... deriving Eq f :: T -> String f x = if | x==A -> "one" | x==B -> "two" | ... In Core after a bit of simplification we get: f x = case dataToTagLarge# x of a# { _DEFAULT -> case a# of _DEFAULT -> case dataToTagLarge# x of b# { _DEFAULT -> case b# of _DEFAULT -> ... 1# -> "two" } 0# -> "one" } Now consider what mkCase does to these case expressions. The case-merge transformation Note [Merge Nested Cases] does this (affecting both pairs of cases): f x = case dataToTagLarge# x of a# { _DEFAULT -> case dataToTagLarge# x of b# { _DEFAULT -> ... 1# -> "two" } 0# -> "one" } Now Note [caseRules for dataToTag] does its work, again on both dataToTagLarge# cases: f x = case x of x1 { _DEFAULT -> case dataToTagLarge# x1 of a# { _DEFAULT -> case x of x2 { _DEFAULT -> case dataToTagLarge# x2 of b# { _DEFAULT -> ... } B -> "two" }} A -> "one" } The new dataToTagLarge# calls come from the "reconstruct scrutinee" part of caseRules (note that a# and b# were not dead in the original program before all this merging). However, since a# and b# /are/ in fact dead in the resulting program, we are left with redundant dataToTagLarge# calls. But they are easily eliminated by doing caseRules again, in the next Simplifier iteration, this time noticing that a# and b# are dead. Hence the "dead-binder" sub-case of Wrinkle 1 of Note [Scrutinee Constant Folding] above. Once we do this we get f x = case x of x1 { _DEFAULT -> case x1 of x2 { _DEFAULT -> case x1 of x2 { _DEFAULT -> case x2 of x3 { _DEFAULT -> ... } B -> "two" }} A -> "one" } and now we can do case-merge again, getting the desired f x = case x of A -> "one" B -> "two" ...
References 2
- caseRules for dataToTag GHC.Core.Opt.ConstantFold
- Merge Nested Cases GHC.Core.Utils
Referenced by 3
- Scrutinee Constant Folding GHC.Core.Opt.Simplify.Utils
- GHC.Core.Opt.Simplify.Utils call site
- Merge Nested Cases GHC.Core.Utils