Drop redundant evals, perhaps
Consider this code
f xs = xs `seq`
(let {-# NOINLINE t #-}
t = reverse xs in
case xs of
[] -> (t,True)
(_:_) -> (t,False))
We get this Core:
f = \ (@a_ayt) (xs_ah4 :: [a_ayt]) ->
case xs_ah4 of xs1_X0 { __DEFAULT -> <------------- Redundant eval
let {
t_sz4 [InlPrag=NOINLINE] :: [a_ayt]
[LclId]
t_sz4
= GHC.Internal.List.reverse1
@a_ayt xs1_X0 (GHC.Types.[] @a_ayt) } in
case xs1_X0 of {
[] -> (t_sz4, GHC.Types.True);
: ds_dyY ds1_dyZ -> (t_sz4, GHC.Types.False)
}
}
Notice that we evaluate xs
and then (after the let
) case-analyse it. We used to drop the first eval, on the grounds that xs
was evaluated "later", but we stopped doing that in #24251 (closed), for good reason.
If the let
wasn't in the way we'd have
case xs_ah4 of xs1_X0 { __DEFAULT -> <------------- Redundant eval
case xs1_X0 of {
[] -> (t_sz4, GHC.Types.True);
: ds_dyY ds1_dyZ -> (t_sz4, GHC.Types.False)
and "case-merging" combines the two case
expressions into one; see Note [Merge Nested Cases]
and mergeCaseAlts
. But in the main example there is a let
in the way.
This ticket asks the question Should we seek to eliminate the redundant eval?. The MR !12239 shows one way to do so, in the case where xs
is evaluated "soon". (See the MR for details.)
It's not obvious that is worth doing at all -- see discussion on !12239. Doing so doesn't save much (see the MR), and it might change evaluation order, which in turn might have consequences for space behaviour. And yet worker/wrapper also changes space behaviour.
This ticket is a place to discuss the issue.