tagToEnum# x should never be thunked.
Motivation
Consider this STG code generated for the enumFromThen specialization:
let {
sat_s4wA [Occ=Once]
:: Main.MyEnum
[LclId] =
[x_s4wy] \u []
tagToEnum# [x_s4wy];
} in
: [sat_s4wA
sat_s4wC];
Here we capture an x_s4wy :: Int#
, and build a thunk which will evaluate to one of the Enum constructors.
This is always going to be more expensive than just executing tagToEnum# directly and storing the result.
Since Enums compatible with tagToEnum don't have fields it's very cheap.
tagToEnum#
compiles down to this in Cmm: I64[(R2 << 3) + Main.MyEnum_closure_tbl];
which is just a few instructions.
Meanwhile, the thunk above is compiled down to:
sat_s4wk_entry() // [R1]
{ info_tbls: [(c4RT,
label: sat_s4wk_info
rep: HeapRep 1 nonptrs { Thunk }
srt: Nothing)]
stack_info: arg_space: 8 updfr_space: Just 8
}
{offset
c4RT: // global
if ((Sp + -16) < SpLim) (likely: False) goto c4RU; else goto c4RV;
c4RU: // global
call (stg_gc_enter_1)(R1) args: 8, res: 0, upd: 8;
c4RV: // global
I64[Sp - 16] = stg_upd_frame_info;
P64[Sp - 8] = R1;
R1 = I64[(I64[R1 + 16] << 3) + Main.MyEnum_closure_tbl];
Sp = Sp - 16;
call (P64[Sp])(R1) args: 24, res: 0, upd: 24;
}
},
And that's without the overhead of allocating the thunk in the heap.
Proposal
Code of the form:
let val = tagToEnum# x
in ... val ...
should be optimized into:
case tagToEnum# x of val -> ... val ...
This is safe:
- x is of type Int# and as such will have been forced either way.
- tagToEnum#, while returning a lifted result can't bottom.