StgToCmm: Don't generate code for no-op continuations
Motivation
In IO
and ST
code it's very common to see a function end with pure $! someExpression
.
After unarisation, that looks like case someExpression of vx { __DEFAULT -> Solo# [vx]; };
, where Solo#
is the constructor for the unary unboxed tuple. Today, the Cmm
code we generate this case
expression does the following:
- Push a stack frame for
someExpression
to return to, - Jump to the code for
someExpression
. - When that returns, the stack frame we pushed earlier does nothing except pop itself from the stack and jump to the next stack frame below it.
It would be faster and more direct if we didn't bother pushing a stack frame, skipping steps 1 and 3. We can get away with this because the return convention for a lifted type like that of someExpression
is compatible with the return convention for a unary unboxed tuple containing a lifted type like Solo# [vx]
. (The return conventions are not identical: The former promises to return a properly tagged pointer to an evaluated object, while the latter only promises to return a pointer to an object that can be evaluated if need be.)
Proposal
Detect these trivial of vx { __DEFAULT -> Solo# [vx] }
case continuations in StgToCmm
, and don't generate any code for them.
Alternative: It is also plausible to instead expand our Stg-CSE to detect this situation and rewrite case someExpression of vx { __DEFAULT -> Solo# [vx]; };
to just someExpression
, but this is representationally confusing and may cause trouble for one of our other Stg passes. Even if it doesn't, there is no clear benefit for such a rewrite above and beyond the cheap check proposed above.