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(¶m_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 = ¶m_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(¶m_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 = ¶m_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 }