kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

cg_switch_test.c (20991B)


      1 /* cg_switch_test — drives kit_cg_switch with hand-built descriptors
      2  * covering the shapes the lowering must handle. Structural: asserts that
      3  * each shape lowers without panic and produces a defined function
      4  * symbol. Behavior under different lowering strategies (chain vs table)
      5  * is covered end-to-end by test/toy and test/parse — this file targets
      6  * the API surface itself.
      7  *
      8  * Shapes covered:
      9  *   - hint = TARGET_DEFAULT  / BRANCH_CHAIN  / JUMP_TABLE
     10  *   - dense range, sparse range, signed dense range straddling zero
     11  *   - default present / default absent
     12  *   - single case, empty cases (default-only)
     13  *   - both opt levels (0 and 1)
     14  *
     15  * Run by:  make test-cg-api
     16  */
     17 
     18 #include <kit/cg.h>
     19 #include <kit/core.h>
     20 #include <kit/disasm.h>
     21 #include <kit/interp.h>
     22 #include <kit/object.h>
     23 #include <stdarg.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 
     28 #include "lib/kit_unit.h"
     29 
     30 /* Shared test context replaces the per-file heap/diag/counter globals;
     31  * EXPECT aliases CU_EXPECT so the call sites are unchanged. */
     32 static KitUnit g_u;
     33 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__)
     34 
     35 static KitObjBuilder* new_obj(KitCompiler* c) {
     36   KitObjBuilder* ob = NULL;
     37   if (kit_obj_builder_new(c, &ob) != KIT_OK) return NULL;
     38   return ob;
     39 }
     40 
     41 /* ---- Helpers --------------------------------------------------------- */
     42 
     43 typedef struct SwitchShape {
     44   const char* name;
     45   KitCgTypeId selector_type;
     46   /* values[] interpreted by selector_type; arm i is reached when the
     47    * selector equals values[i]. Each arm returns (uint32_t)results[i]. */
     48   const int64_t* values;
     49   const int32_t* results;
     50   uint32_t ncases;
     51   int has_default;
     52   int32_t default_result; /* used if has_default; otherwise arm falls past */
     53   KitCgSwitchHint hint;
     54 } SwitchShape;
     55 
     56 /* Build:
     57  *   int32_t f(<selector_type> x) {
     58  *     switch (x) {
     59  *       case values[0]: return results[0];
     60  *       ...
     61  *       default: return default_result;        // if has_default
     62  *     }
     63  *     return -1;                               // fall-through past switch
     64  *   }
     65  */
     66 static void build_switch_fn(KitCompiler* c, KitCgTypeId i32_ty,
     67                             const SwitchShape* sh, int opt_level) {
     68   char fn_name[96];
     69   KitCodeOptions opts;
     70   KitObjBuilder* ob;
     71   KitCg* cg;
     72   KitCgFuncParam param_desc;
     73   KitCgFuncSig sig;
     74   KitCgDecl decl;
     75   KitCgSym sym;
     76   KitCgLocalAttrs attrs;
     77   KitCgLocal param;
     78   KitCgLabel default_lbl;
     79   KitCgLabel end_lbl;
     80   KitCgLabel* case_lbls = NULL;
     81   KitCgSwitchCase* cases = NULL;
     82   KitCgSwitch sw;
     83   uint32_t i;
     84 
     85   memset(&opts, 0, sizeof opts);
     86   opts.opt_level = opt_level;
     87   ob = new_obj(c);
     88   EXPECT(ob != NULL, "[%s/O%d] obj builder allocation failed", sh->name,
     89          opt_level);
     90   if (!ob) return;
     91   cg = NULL;
     92   (void)kit_cg_new(c, &cg);
     93   if (cg) (void)kit_cg_begin(cg, ob, &opts);
     94   EXPECT(cg != NULL, "[%s/O%d] cg_new failed", sh->name, opt_level);
     95   if (!cg) {
     96     kit_obj_builder_free(ob);
     97     return;
     98   }
     99 
    100   memset(&param_desc, 0, sizeof param_desc);
    101   param_desc.type = sh->selector_type;
    102   memset(&sig, 0, sizeof sig);
    103   KitCgFuncResult sig_result;
    104   memset(&sig_result, 0, sizeof sig_result);
    105   sig_result.type = i32_ty;
    106   sig.result = sig_result;
    107   sig.params = &param_desc;
    108   sig.nparams = 1;
    109   sig.call_conv = KIT_CG_CC_TARGET_C;
    110 
    111   snprintf(fn_name, sizeof fn_name, "switch_%s_o%d", sh->name, opt_level);
    112   memset(&decl, 0, sizeof decl);
    113   decl.kind = KIT_CG_DECL_FUNC;
    114   decl.linkage_name = kit_sym_intern(c, kit_slice_cstr(fn_name));
    115   decl.display_name = decl.linkage_name;
    116   decl.type = kit_cg_type_func(c, sig);
    117   decl.sym.bind = KIT_SB_GLOBAL;
    118   decl.sym.visibility = KIT_CG_VIS_DEFAULT;
    119   sym = kit_cg_decl(cg, decl);
    120   EXPECT(sym != KIT_CG_SYM_NONE, "[%s/O%d] decl failed", sh->name, opt_level);
    121 
    122   kit_cg_func_begin(cg, sym);
    123 
    124   memset(&attrs, 0, sizeof attrs);
    125   attrs.name = kit_sym_intern(c, KIT_SLICE_LIT("x"));
    126   param = kit_cg_param(cg, 0, sh->selector_type, attrs);
    127   EXPECT(param != KIT_CG_LOCAL_NONE, "[%s/O%d] param failed", sh->name,
    128          opt_level);
    129 
    130   end_lbl = kit_cg_label_new(cg);
    131   default_lbl = sh->has_default ? kit_cg_label_new(cg) : end_lbl;
    132   if (sh->ncases) {
    133     case_lbls = (KitCgLabel*)malloc(sh->ncases * sizeof *case_lbls);
    134     cases = (KitCgSwitchCase*)malloc(sh->ncases * sizeof *cases);
    135     EXPECT(case_lbls && cases, "[%s/O%d] alloc failed", sh->name, opt_level);
    136     for (i = 0; i < sh->ncases; ++i) {
    137       case_lbls[i] = kit_cg_label_new(cg);
    138       cases[i].value = (uint64_t)sh->values[i];
    139       cases[i].label = case_lbls[i];
    140     }
    141   }
    142 
    143   /* Push selector, dispatch. */
    144   kit_cg_push_local(cg, param);
    145   kit_cg_load(
    146       cg, (KitCgMemAccess){.type = sh->selector_type,
    147                            .align = kit_cg_type_align(c, sh->selector_type)});
    148   memset(&sw, 0, sizeof sw);
    149   sw.selector_type = sh->selector_type;
    150   sw.default_label = default_lbl;
    151   sw.cases = cases;
    152   sw.ncases = sh->ncases;
    153   sw.hint = sh->hint;
    154   kit_cg_switch(cg, sw);
    155 
    156   /* Each arm: push result, jump to end_lbl. The cg API always materializes
    157    * the return through `kit_cg_ret` consuming the stack top; jump to a
    158    * single ret epilogue at end_lbl. */
    159   for (i = 0; i < sh->ncases; ++i) {
    160     kit_cg_label_place(cg, case_lbls[i]);
    161     kit_cg_push_int(cg, (uint64_t)(int64_t)sh->results[i], i32_ty);
    162     kit_cg_ret(cg);
    163   }
    164   if (sh->has_default) {
    165     kit_cg_label_place(cg, default_lbl);
    166     kit_cg_push_int(cg, (uint64_t)(int64_t)sh->default_result, i32_ty);
    167     kit_cg_ret(cg);
    168   }
    169   kit_cg_label_place(cg, end_lbl);
    170   kit_cg_push_int(cg, (uint64_t)(int64_t)-1, i32_ty);
    171   kit_cg_ret(cg);
    172   kit_cg_func_end(cg);
    173 
    174   EXPECT(g_u.fails == 0 || g_u.fails > 0, "shape-build sentinel"); /* no-op */
    175 
    176   free(case_lbls);
    177   free(cases);
    178   kit_cg_free(cg);
    179   kit_obj_builder_free(ob);
    180 }
    181 
    182 /* ---- Shapes ---------------------------------------------------------- */
    183 
    184 static void run_all_shapes(KitCompiler* c, KitCgTypeId i32_ty,
    185                            KitCgTypeId i64_ty, int opt_level) {
    186   /* Dense unsigned range 10..15 + default. */
    187   static const int64_t dense_vals[] = {10, 11, 12, 13, 14, 15};
    188   static const int32_t dense_res[] = {100, 101, 102, 103, 104, 105};
    189 
    190   /* Sparse: {1, 50, 1000}. */
    191   static const int64_t sparse_vals[] = {1, 50, 1000};
    192   static const int32_t sparse_res[] = {11, 22, 33};
    193 
    194   /* Signed dense around zero: -3..3. */
    195   static const int64_t signed_vals[] = {-3, -2, -1, 0, 1, 2, 3};
    196   static const int32_t signed_res[] = {30, 31, 32, 33, 34, 35, 36};
    197 
    198   /* Singleton. */
    199   static const int64_t single_vals[] = {42};
    200   static const int32_t single_res[] = {7};
    201 
    202   SwitchShape shapes[] = {
    203       /* TARGET_DEFAULT × shape × default-present/absent. */
    204       {"dense_def_target", i32_ty, dense_vals, dense_res, 6, 1, 999,
    205        KIT_CG_SWITCH_TARGET_DEFAULT},
    206       {"dense_nodef_target", i32_ty, dense_vals, dense_res, 6, 0, 0,
    207        KIT_CG_SWITCH_TARGET_DEFAULT},
    208       {"sparse_def_target", i32_ty, sparse_vals, sparse_res, 3, 1, 99,
    209        KIT_CG_SWITCH_TARGET_DEFAULT},
    210       {"signed_def_target", i32_ty, signed_vals, signed_res, 7, 1, 999,
    211        KIT_CG_SWITCH_TARGET_DEFAULT},
    212       {"signed64_def_target", i64_ty, signed_vals, signed_res, 7, 1, 999,
    213        KIT_CG_SWITCH_TARGET_DEFAULT},
    214 
    215       /* Forced hint variants. JUMP_TABLE is advisory; lowering may
    216        * accept it or fall back to chain — both must produce a valid
    217        * function. */
    218       {"dense_def_jump_table", i32_ty, dense_vals, dense_res, 6, 1, 999,
    219        KIT_CG_SWITCH_JUMP_TABLE},
    220       {"dense_nodef_jump_table", i32_ty, dense_vals, dense_res, 6, 0, 0,
    221        KIT_CG_SWITCH_JUMP_TABLE},
    222       {"sparse_def_jump_table", i32_ty, sparse_vals, sparse_res, 3, 1, 99,
    223        KIT_CG_SWITCH_JUMP_TABLE},
    224       {"signed_def_jump_table", i32_ty, signed_vals, signed_res, 7, 1, 999,
    225        KIT_CG_SWITCH_JUMP_TABLE},
    226 
    227       {"dense_def_branch_chain", i32_ty, dense_vals, dense_res, 6, 1, 999,
    228        KIT_CG_SWITCH_BRANCH_CHAIN},
    229       {"sparse_def_branch_chain", i32_ty, sparse_vals, sparse_res, 3, 1, 99,
    230        KIT_CG_SWITCH_BRANCH_CHAIN},
    231 
    232       /* Singleton: minimum case count. Both hints. */
    233       {"single_def_target", i32_ty, single_vals, single_res, 1, 1, 99,
    234        KIT_CG_SWITCH_TARGET_DEFAULT},
    235       {"single_def_jump_table", i32_ty, single_vals, single_res, 1, 1, 99,
    236        KIT_CG_SWITCH_JUMP_TABLE},
    237 
    238       /* Empty (default-only). kit_cg_switch on an empty case array is
    239        * tested for clean acceptance — frontends emit this from
    240        * `switch (x) { default: ...; }`. */
    241       {"empty_def_target", i32_ty, NULL, NULL, 0, 1, 7,
    242        KIT_CG_SWITCH_TARGET_DEFAULT},
    243   };
    244 
    245   size_t n = sizeof shapes / sizeof shapes[0];
    246   size_t i;
    247   for (i = 0; i < n; ++i) {
    248     build_switch_fn(c, i32_ty, &shapes[i], opt_level);
    249   }
    250 }
    251 
    252 /* ---- Jump-table decision -------------------------------------------- *
    253  *
    254  * The shape checks above only assert that lowering succeeds. These build a
    255  * function, emit an ELF object for the test compiler's (aa64) target,
    256  * disassemble .text, and report whether the dispatch became a real jump
    257  * table — detectable as the AArch64 indirect register branch `br`, which
    258  * the cmp/branch chain never emits. This is what lets us assert the
    259  * *policy* decision (table vs chain), not just structural success. */
    260 
    261 /* Build `int32_t <name>(<sel> x){ switch(x){...} }` from `sh` into an
    262  * already-begun cg, shared by the disasm and interpreter checks. Returns 0
    263  * on success, -1 on a harness failure. */
    264 static int emit_switch_fn(KitCompiler* c, KitCg* cg, const char* name,
    265                           KitCgTypeId i32_ty, const SwitchShape* sh) {
    266   KitCgFuncParam param_desc;
    267   KitCgFuncResult sig_result;
    268   KitCgFuncSig sig;
    269   KitCgDecl decl;
    270   KitCgSym sym;
    271   KitCgLocalAttrs attrs;
    272   KitCgLocal param;
    273   KitCgLabel default_lbl;
    274   KitCgLabel end_lbl;
    275   KitCgLabel* case_lbls = NULL;
    276   KitCgSwitchCase* cases = NULL;
    277   KitCgSwitch sw;
    278   int rc = -1;
    279   uint32_t i;
    280 
    281   memset(&param_desc, 0, sizeof param_desc);
    282   param_desc.type = sh->selector_type;
    283   memset(&sig_result, 0, sizeof sig_result);
    284   sig_result.type = i32_ty;
    285   memset(&sig, 0, sizeof sig);
    286   sig.result = sig_result;
    287   sig.params = &param_desc;
    288   sig.nparams = 1;
    289   sig.call_conv = KIT_CG_CC_TARGET_C;
    290 
    291   memset(&decl, 0, sizeof decl);
    292   decl.kind = KIT_CG_DECL_FUNC;
    293   decl.linkage_name = kit_sym_intern(c, kit_slice_cstr(name));
    294   decl.display_name = decl.linkage_name;
    295   decl.type = kit_cg_type_func(c, sig);
    296   decl.sym.bind = KIT_SB_GLOBAL;
    297   decl.sym.visibility = KIT_CG_VIS_DEFAULT;
    298   sym = kit_cg_decl(cg, decl);
    299   if (sym == KIT_CG_SYM_NONE) return -1;
    300 
    301   kit_cg_func_begin(cg, sym);
    302   memset(&attrs, 0, sizeof attrs);
    303   attrs.name = kit_sym_intern(c, KIT_SLICE_LIT("x"));
    304   param = kit_cg_param(cg, 0, sh->selector_type, attrs);
    305   if (param == KIT_CG_LOCAL_NONE) return -1;
    306 
    307   end_lbl = kit_cg_label_new(cg);
    308   default_lbl = sh->has_default ? kit_cg_label_new(cg) : end_lbl;
    309   if (sh->ncases) {
    310     case_lbls = (KitCgLabel*)malloc(sh->ncases * sizeof *case_lbls);
    311     cases = (KitCgSwitchCase*)malloc(sh->ncases * sizeof *cases);
    312     if (!case_lbls || !cases) goto done;
    313     for (i = 0; i < sh->ncases; ++i) {
    314       case_lbls[i] = kit_cg_label_new(cg);
    315       cases[i].value = (uint64_t)sh->values[i];
    316       cases[i].label = case_lbls[i];
    317     }
    318   }
    319 
    320   kit_cg_push_local(cg, param);
    321   kit_cg_load(
    322       cg, (KitCgMemAccess){.type = sh->selector_type,
    323                            .align = kit_cg_type_align(c, sh->selector_type)});
    324   memset(&sw, 0, sizeof sw);
    325   sw.selector_type = sh->selector_type;
    326   sw.default_label = default_lbl;
    327   sw.cases = cases;
    328   sw.ncases = sh->ncases;
    329   sw.hint = sh->hint;
    330   kit_cg_switch(cg, sw);
    331 
    332   for (i = 0; i < sh->ncases; ++i) {
    333     kit_cg_label_place(cg, case_lbls[i]);
    334     kit_cg_push_int(cg, (uint64_t)(int64_t)sh->results[i], i32_ty);
    335     kit_cg_ret(cg);
    336   }
    337   if (sh->has_default) {
    338     kit_cg_label_place(cg, default_lbl);
    339     kit_cg_push_int(cg, (uint64_t)(int64_t)sh->default_result, i32_ty);
    340     kit_cg_ret(cg);
    341   }
    342   kit_cg_label_place(cg, end_lbl);
    343   kit_cg_push_int(cg, (uint64_t)(int64_t)-1, i32_ty);
    344   kit_cg_ret(cg);
    345   kit_cg_func_end(cg);
    346   rc = 0;
    347 
    348 done:
    349   free(case_lbls);
    350   free(cases);
    351   return rc;
    352 }
    353 
    354 /* 1 iff .text contains an aa64 indirect register branch (BR). -1 on harness
    355  * failure. */
    356 static int switch_lowers_to_table(KitCompiler* c, KitCgTypeId i32_ty,
    357                                   const SwitchShape* sh, int opt_level) {
    358   KitCodeOptions opts;
    359   KitObjBuilder* ob;
    360   KitCg* cg = NULL;
    361   KitWriter* writer = NULL;
    362   KitObjFile* file = NULL;
    363   KitSlice bytes;
    364   KitObjSection text_sec;
    365   const uint8_t* data = NULL;
    366   size_t len = 0;
    367   KitDisasmContext dc;
    368   KitDisasmIter* it = NULL;
    369   KitInsn insn;
    370   int found = -1;
    371 
    372   memset(&opts, 0, sizeof opts);
    373   opts.opt_level = opt_level;
    374   ob = new_obj(c);
    375   if (!ob) return -1;
    376   if (kit_cg_new(c, &cg) != KIT_OK || !cg) {
    377     kit_obj_builder_free(ob);
    378     return -1;
    379   }
    380   if (kit_cg_begin(cg, ob, &opts) != KIT_OK) goto done;
    381   if (emit_switch_fn(c, cg, "f", i32_ty, sh) != 0) goto done;
    382 
    383   if (kit_cg_finish(cg, NULL) != KIT_OK) goto done;
    384   if (kit_cg_detach(cg) != KIT_OK) goto done;
    385 
    386   if (kit_writer_mem(&g_u.heap, &writer) != KIT_OK || !writer) goto done;
    387   if (kit_obj_builder_emit(ob, writer) != KIT_OK) goto done;
    388   bytes.data = kit_writer_mem_bytes(writer, &len);
    389   bytes.len = len;
    390   if (kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("<sw-test>"), &bytes, &file) !=
    391       KIT_OK)
    392     goto done;
    393   if (kit_obj_section_by_name(file, KIT_SLICE_LIT(".text"), &text_sec) !=
    394       KIT_OK)
    395     goto done;
    396   if (kit_obj_section_data(file, text_sec, &data, &len) != KIT_OK) goto done;
    397 
    398   memset(&dc, 0, sizeof dc);
    399   dc.target = kit_compiler_target(c);
    400   dc.context = g_u.ctx;
    401   if (kit_disasm_iter_new(&dc, data, len, 0, file, &it) != KIT_OK || !it)
    402     goto done;
    403   found = 0;
    404   while (kit_disasm_iter_next(it, &insn) == KIT_ITER_ITEM) {
    405     if (insn.mnemonic.len == 2 && insn.mnemonic.s &&
    406         insn.mnemonic.s[0] == 'b' && insn.mnemonic.s[1] == 'r') {
    407       found = 1;
    408       break;
    409     }
    410   }
    411 
    412 done:
    413   if (it) kit_disasm_iter_free(it);
    414   if (file) kit_obj_free(file);
    415   if (writer) kit_writer_close(writer);
    416   if (cg) kit_cg_free(cg);
    417   kit_obj_builder_free(ob);
    418   return found;
    419 }
    420 
    421 static void run_table_decision_checks(KitCompiler* c, KitCgTypeId i32_ty,
    422                                       KitCgTypeId i64_ty) {
    423   /* Six contiguous 64-bit values straddling the signed midpoint 2^63.
    424    * As *unsigned* they span just [2^63-3, 2^63+2] (span 6, table-friendly),
    425    * but a signed-only reading sees them at both ends of the i64 range and
    426    * the table is rejected. The selector signedness isn't even visible to
    427    * the lowering (CG has only I64), so the planner must take the tighter of
    428    * the signed- and unsigned-seam windows. */
    429   static const uint64_t MID = (uint64_t)1 << 63;
    430   static int64_t mid_vals[6];
    431   static const int32_t mid_res[] = {200, 201, 202, 203, 204, 205};
    432   /* Dense small positive values: a table under either reading — the
    433    * positive control that the detector reports tables when one is emitted. */
    434   static const int64_t lo_vals[] = {10, 11, 12, 13, 14, 15};
    435   static const int32_t lo_res[] = {100, 101, 102, 103, 104, 105};
    436   /* Genuinely sparse: span far exceeds MAX_SPAN under either window, so no
    437    * table even with the JUMP_TABLE hint — the negative control. */
    438   static const int64_t sparse_vals[] = {0, 1ll << 20, 1ll << 40, 1ll << 60};
    439   static const int32_t sparse_res[] = {1, 2, 3, 4};
    440   SwitchShape mid;
    441   SwitchShape lo;
    442   SwitchShape sparse;
    443   int r;
    444   uint32_t i;
    445 
    446   for (i = 0; i < 6; ++i) mid_vals[i] = (int64_t)(MID - 3 + i);
    447 
    448   memset(&mid, 0, sizeof mid);
    449   mid.name = "u64_midpoint";
    450   mid.selector_type = i64_ty;
    451   mid.values = mid_vals;
    452   mid.results = mid_res;
    453   mid.ncases = 6;
    454   mid.has_default = 1;
    455   mid.default_result = 999;
    456   mid.hint = KIT_CG_SWITCH_JUMP_TABLE;
    457 
    458   lo = mid;
    459   lo.name = "dense_low";
    460   lo.values = lo_vals;
    461   lo.results = lo_res;
    462 
    463   sparse = mid;
    464   sparse.name = "sparse";
    465   sparse.values = sparse_vals;
    466   sparse.results = sparse_res;
    467   sparse.ncases = 4;
    468 
    469   /* Positive control: a dense low switch must produce a table. */
    470   r = switch_lowers_to_table(c, i32_ty, &lo, /*opt_level=*/0);
    471   EXPECT(r == 1, "dense low switch should lower to a jump table (got %d)", r);
    472 
    473   /* The fix: midpoint-straddling 64-bit cases are dense under the unsigned
    474    * window and must lower to a table rather than a cmp/branch chain. */
    475   r = switch_lowers_to_table(c, i32_ty, &mid, /*opt_level=*/0);
    476   EXPECT(r == 1,
    477          "u64 midpoint-straddling switch should lower to a jump table (got %d)",
    478          r);
    479 
    480   /* Negative control: a truly sparse switch stays a chain even when the
    481    * frontend forces the jump-table hint. */
    482   r = switch_lowers_to_table(c, i32_ty, &sparse, /*opt_level=*/0);
    483   EXPECT(r == 0, "sparse switch must not lower to a jump table (got %d)", r);
    484 }
    485 
    486 /* Execute the midpoint switch through the target-independent interpreter.
    487  * At O1 the JUMP_TABLE plan reaches cg_emit_switch_table, which records the
    488  * `idx = sel - vmin` / unsigned-bounds / table-load / indirect-branch IR; the
    489  * interp materializes the table as interp pcs and runs those very ops, so this
    490  * exercises the new unsigned-window index math end to end (not just that a
    491  * table was chosen). Every in-set selector must reach its arm and every
    492  * out-of-set selector must reach default. */
    493 static void run_table_exec_check(KitCompiler* c, KitCgTypeId i32_ty,
    494                                  KitCgTypeId i64_ty) {
    495   static const uint64_t MID = (uint64_t)1 << 63;
    496   static int64_t mid_vals[6];
    497   static const int32_t mid_res[] = {200, 201, 202, 203, 204, 205};
    498   SwitchShape mid;
    499   KitInterpProgram* pp;
    500   KitObjBuilder* ob = NULL;
    501   KitCg* cg = NULL;
    502   KitCodeOptions opts;
    503   KitInterpFunc* fn;
    504   uint32_t i;
    505 
    506   for (i = 0; i < 6; ++i) mid_vals[i] = (int64_t)(MID - 3 + i);
    507   memset(&mid, 0, sizeof mid);
    508   mid.name = "u64_midpoint_exec";
    509   mid.selector_type = i64_ty;
    510   mid.values = mid_vals;
    511   mid.results = mid_res;
    512   mid.ncases = 6;
    513   mid.has_default = 1;
    514   mid.default_result = 999;
    515   mid.hint = KIT_CG_SWITCH_JUMP_TABLE;
    516 
    517   pp = kit_interp_program_new(c);
    518   EXPECT(pp != NULL, "exec: interp_program_new failed");
    519   if (!pp) return;
    520   kit_interp_program_attach(pp, c);
    521 
    522   ob = new_obj(c);
    523   EXPECT(ob != NULL, "exec: obj_new");
    524   EXPECT(kit_cg_new(c, &cg) == KIT_OK && cg, "exec: cg_new");
    525   if (ob && cg) {
    526     memset(&opts, 0, sizeof opts);
    527     opts.opt_level = 1; /* interp capture requires the optimizer pass */
    528     kit_cg_begin(cg, ob, &opts);
    529     EXPECT(emit_switch_fn(c, cg, "mid", i32_ty, &mid) == 0, "exec: build");
    530     EXPECT(kit_cg_finish(cg, NULL) == KIT_OK, "exec: finish");
    531     EXPECT(kit_cg_detach(cg) == KIT_OK, "exec: detach");
    532 
    533     fn = kit_interp_lookup(pp, kit_slice_cstr("mid"));
    534     EXPECT(fn != NULL, "exec: mid not captured");
    535     if (fn) {
    536       for (i = 0; i < 6; ++i) {
    537         uint64_t args[1] = {(uint64_t)mid_vals[i]};
    538         int64_t ret = 0;
    539         KitInterpStatus s = kit_interp_call_args(pp, fn, args, 1, &ret);
    540         EXPECT(s == KIT_INTERP_DONE && ret == mid_res[i],
    541                "mid(%#llx): want %d got %lld (status %d)",
    542                (unsigned long long)args[0], mid_res[i], (long long)ret, (int)s);
    543       }
    544       /* Out-of-set selectors stress both bounds edges (just below vmin, just
    545        * above vmax) and a far-away value — all must reach default. */
    546       {
    547         uint64_t outs[] = {MID - 4, MID + 3, 0, MID + 1000};
    548         uint32_t no = (uint32_t)(sizeof outs / sizeof outs[0]);
    549         for (i = 0; i < no; ++i) {
    550           uint64_t args[1] = {outs[i]};
    551           int64_t ret = 0;
    552           KitInterpStatus s = kit_interp_call_args(pp, fn, args, 1, &ret);
    553           EXPECT(s == KIT_INTERP_DONE && ret == 999,
    554                  "mid(%#llx): want default 999 got %lld (status %d)",
    555                  (unsigned long long)outs[i], (long long)ret, (int)s);
    556         }
    557       }
    558     }
    559   }
    560 
    561   if (cg) kit_cg_free(cg);
    562   if (ob) kit_obj_builder_free(ob);
    563   kit_interp_program_free(pp);
    564 }
    565 
    566 /* ---- Entry ----------------------------------------------------------- */
    567 
    568 int main(void) {
    569   KitTargetSpec target;
    570   KitCompiler* c = NULL;
    571   KitCgTypeId i32_ty;
    572   KitCgTypeId i64_ty;
    573 
    574   kit_unit_init(&g_u);
    575   target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF);
    576 
    577   if (kit_unit_compiler_new(&g_u, target, &c) != KIT_OK || !c) {
    578     fprintf(stderr, "compiler_new failed\n");
    579     return 2;
    580   }
    581 
    582   i32_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
    583   i64_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I64);
    584   EXPECT(i32_ty != KIT_CG_TYPE_NONE, "i32 builtin id is none");
    585   EXPECT(i64_ty != KIT_CG_TYPE_NONE, "i64 builtin id is none");
    586 
    587   run_all_shapes(c, i32_ty, i64_ty, /*opt_level=*/0);
    588   run_all_shapes(c, i32_ty, i64_ty, /*opt_level=*/1);
    589   run_table_decision_checks(c, i32_ty, i64_ty);
    590   run_table_exec_check(c, i32_ty, i64_ty);
    591 
    592   kit_compiler_free(c);
    593   kit_unit_summary(&g_u, "cg_switch_test");
    594   return kit_unit_status(&g_u);
    595 }