Skip to content

Selector thunk confusion -- codegen not generating selectors when it should

Not sure if this is a bug or not, but something interesting regarding selector thunk generation.

This closure uses a standard selector thunk info table in the generated code:

data T = T Integer Integer

selector :: T -> Maybe Integer
selector p = Just (case p of T x y -> x)

Cmm:

[Test.selector_entry() { //  [R2]
         { info_tbls: [(cp9,
                        label: Test.selector_info
                        rep: HeapRep static { Fun {arity: 1 fun_type: ArgSpec 5} }
                        srt: Nothing)]
           stack_info: arg_space: 8 updfr_space: Just 8
         }
     {offset
       cp9: // global
           Hp = Hp + 40;
           if (Hp > HpLim) (likely: False) goto cpd; else goto cpc;
       cpd: // global
           HpAlloc = 40;
           R2 = R2;
           R1 = Test.selector_closure;
           call (stg_gc_fun)(R2, R1) args: 8, res: 0, upd: 8;
       cpc: // global
           I64[Hp - 32] = stg_sel_0_upd_info;
           P64[Hp - 16] = R2;
           I64[Hp - 8] = GHC.Maybe.Just_con_info;
           P64[Hp] = Hp - 32;
           R1 = Hp - 6;
           call (P64[Sp])(R1) args: 8, res: 0, upd: 8;
     }
 },
 section ""data" . Test.selector_closure" {
     Test.selector_closure:
         const Test.selector_info;
 }]

This is as expected. However this closure with the same expression:

not_selector = case t of T a b -> a

does not use a standard thunk info table:

[Test.not_selector_entry() { //  [R1]
         { info_tbls: [(cps,
                        label: Test.not_selector_info
                        rep: HeapRep static { Thunk }
                        srt: Nothing)]
           stack_info: arg_space: 8 updfr_space: Just 8
         }
     {offset
       cps: // global
           if ((Sp + -16) < SpLim) (likely: False) goto cpt; else goto cpu;
       cpt: // global
           R1 = R1;
           call (stg_gc_enter_1)(R1) args: 8, res: 0, upd: 8;
       cpu: // global
           (_cpo::I64) = call "ccall" arg hints:  [PtrHint,
                                                   PtrHint]  result hints:  [PtrHint] newCAF(BaseReg, R1);
           if (_cpo::I64 == 0) goto cpq; else goto cpp;
       cpq: // global
           call (I64[R1])() args: 8, res: 0, upd: 8;
       cpp: // global
           I64[Sp - 16] = stg_bh_upd_frame_info;
           I64[Sp - 8] = _cpo::I64;
           R1 = P64[Test.t_closure+8] & (-8);
           Sp = Sp - 16;
           call (I64[R1])(R1) args: 24, res: 0, upd: 24;
     }
 },
 section ""data" . Test.not_selector_closure" {
     Test.not_selector_closure:
         const Test.not_selector_info;
         const 0;
         const 0;
         const 0;
 }]

Using the standard selector thunks we could generate this for not_selector_closure:

[section ""data" . Test.not_selector_closure" {
     Test.not_selector_closure:
         const stg_sel_0_upd_info;
         const 0; // padding for thunk updates
         const Main.t_closure; // selectee
         ...
 }]

and avoid generating code for it. This would make code smaller.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information