Skip to content

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.
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information