cg_switch_test.c (9587B)
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/object.h> 21 #include <stdarg.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "lib/kit_unit.h" 27 28 /* Shared test context replaces the per-file heap/diag/counter globals; 29 * EXPECT aliases CU_EXPECT so the call sites are unchanged. */ 30 static KitUnit g_u; 31 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) 32 33 static KitObjBuilder* new_obj(KitCompiler* c) { 34 KitObjBuilder* ob = NULL; 35 if (kit_obj_builder_new(c, &ob) != KIT_OK) return NULL; 36 return ob; 37 } 38 39 /* ---- Helpers --------------------------------------------------------- */ 40 41 typedef struct SwitchShape { 42 const char* name; 43 KitCgTypeId selector_type; 44 /* values[] interpreted by selector_type; arm i is reached when the 45 * selector equals values[i]. Each arm returns (uint32_t)results[i]. */ 46 const int64_t* values; 47 const int32_t* results; 48 uint32_t ncases; 49 int has_default; 50 int32_t default_result; /* used if has_default; otherwise arm falls past */ 51 KitCgSwitchHint hint; 52 } SwitchShape; 53 54 /* Build: 55 * int32_t f(<selector_type> x) { 56 * switch (x) { 57 * case values[0]: return results[0]; 58 * ... 59 * default: return default_result; // if has_default 60 * } 61 * return -1; // fall-through past switch 62 * } 63 */ 64 static void build_switch_fn(KitCompiler* c, KitCgTypeId i32_ty, 65 const SwitchShape* sh, int opt_level) { 66 char fn_name[96]; 67 KitCodeOptions opts; 68 KitObjBuilder* ob; 69 KitCg* cg; 70 KitCgFuncParam param_desc; 71 KitCgFuncSig sig; 72 KitCgDecl decl; 73 KitCgSym sym; 74 KitCgLocalAttrs attrs; 75 KitCgLocal param; 76 KitCgLabel default_lbl; 77 KitCgLabel end_lbl; 78 KitCgLabel* case_lbls = NULL; 79 KitCgSwitchCase* cases = NULL; 80 KitCgSwitch sw; 81 uint32_t i; 82 83 memset(&opts, 0, sizeof opts); 84 opts.opt_level = opt_level; 85 ob = new_obj(c); 86 EXPECT(ob != NULL, "[%s/O%d] obj builder allocation failed", sh->name, 87 opt_level); 88 if (!ob) return; 89 cg = NULL; 90 (void)kit_cg_new(c, &cg); 91 if (cg) (void)kit_cg_begin(cg, ob, &opts); 92 EXPECT(cg != NULL, "[%s/O%d] cg_new failed", sh->name, opt_level); 93 if (!cg) { 94 kit_obj_builder_free(ob); 95 return; 96 } 97 98 memset(¶m_desc, 0, sizeof param_desc); 99 param_desc.type = sh->selector_type; 100 memset(&sig, 0, sizeof sig); 101 KitCgFuncResult sig_result; 102 memset(&sig_result, 0, sizeof sig_result); 103 sig_result.type = i32_ty; 104 sig.result = sig_result; 105 sig.params = ¶m_desc; 106 sig.nparams = 1; 107 sig.call_conv = KIT_CG_CC_TARGET_C; 108 109 snprintf(fn_name, sizeof fn_name, "switch_%s_o%d", sh->name, opt_level); 110 memset(&decl, 0, sizeof decl); 111 decl.kind = KIT_CG_DECL_FUNC; 112 decl.linkage_name = kit_sym_intern(c, kit_slice_cstr(fn_name)); 113 decl.display_name = decl.linkage_name; 114 decl.type = kit_cg_type_func(c, sig); 115 decl.sym.bind = KIT_SB_GLOBAL; 116 decl.sym.visibility = KIT_CG_VIS_DEFAULT; 117 sym = kit_cg_decl(cg, decl); 118 EXPECT(sym != KIT_CG_SYM_NONE, "[%s/O%d] decl failed", sh->name, opt_level); 119 120 kit_cg_func_begin(cg, sym); 121 122 memset(&attrs, 0, sizeof attrs); 123 attrs.name = kit_sym_intern(c, KIT_SLICE_LIT("x")); 124 param = kit_cg_param(cg, 0, sh->selector_type, attrs); 125 EXPECT(param != KIT_CG_LOCAL_NONE, "[%s/O%d] param failed", sh->name, 126 opt_level); 127 128 end_lbl = kit_cg_label_new(cg); 129 default_lbl = sh->has_default ? kit_cg_label_new(cg) : end_lbl; 130 if (sh->ncases) { 131 case_lbls = (KitCgLabel*)malloc(sh->ncases * sizeof *case_lbls); 132 cases = (KitCgSwitchCase*)malloc(sh->ncases * sizeof *cases); 133 EXPECT(case_lbls && cases, "[%s/O%d] alloc failed", sh->name, opt_level); 134 for (i = 0; i < sh->ncases; ++i) { 135 case_lbls[i] = kit_cg_label_new(cg); 136 cases[i].value = (uint64_t)sh->values[i]; 137 cases[i].label = case_lbls[i]; 138 } 139 } 140 141 /* Push selector, dispatch. */ 142 kit_cg_push_local(cg, param); 143 kit_cg_load( 144 cg, (KitCgMemAccess){.type = sh->selector_type, 145 .align = kit_cg_type_align(c, sh->selector_type)}); 146 memset(&sw, 0, sizeof sw); 147 sw.selector_type = sh->selector_type; 148 sw.default_label = default_lbl; 149 sw.cases = cases; 150 sw.ncases = sh->ncases; 151 sw.hint = sh->hint; 152 kit_cg_switch(cg, sw); 153 154 /* Each arm: push result, jump to end_lbl. The cg API always materializes 155 * the return through `kit_cg_ret` consuming the stack top; jump to a 156 * single ret epilogue at end_lbl. */ 157 for (i = 0; i < sh->ncases; ++i) { 158 kit_cg_label_place(cg, case_lbls[i]); 159 kit_cg_push_int(cg, (uint64_t)(int64_t)sh->results[i], i32_ty); 160 kit_cg_ret(cg); 161 } 162 if (sh->has_default) { 163 kit_cg_label_place(cg, default_lbl); 164 kit_cg_push_int(cg, (uint64_t)(int64_t)sh->default_result, i32_ty); 165 kit_cg_ret(cg); 166 } 167 kit_cg_label_place(cg, end_lbl); 168 kit_cg_push_int(cg, (uint64_t)(int64_t)-1, i32_ty); 169 kit_cg_ret(cg); 170 kit_cg_func_end(cg); 171 172 EXPECT(g_u.fails == 0 || g_u.fails > 0, "shape-build sentinel"); /* no-op */ 173 174 free(case_lbls); 175 free(cases); 176 kit_cg_free(cg); 177 kit_obj_builder_free(ob); 178 } 179 180 /* ---- Shapes ---------------------------------------------------------- */ 181 182 static void run_all_shapes(KitCompiler* c, KitCgTypeId i32_ty, 183 KitCgTypeId i64_ty, int opt_level) { 184 /* Dense unsigned range 10..15 + default. */ 185 static const int64_t dense_vals[] = {10, 11, 12, 13, 14, 15}; 186 static const int32_t dense_res[] = {100, 101, 102, 103, 104, 105}; 187 188 /* Sparse: {1, 50, 1000}. */ 189 static const int64_t sparse_vals[] = {1, 50, 1000}; 190 static const int32_t sparse_res[] = {11, 22, 33}; 191 192 /* Signed dense around zero: -3..3. */ 193 static const int64_t signed_vals[] = {-3, -2, -1, 0, 1, 2, 3}; 194 static const int32_t signed_res[] = {30, 31, 32, 33, 34, 35, 36}; 195 196 /* Singleton. */ 197 static const int64_t single_vals[] = {42}; 198 static const int32_t single_res[] = {7}; 199 200 SwitchShape shapes[] = { 201 /* TARGET_DEFAULT × shape × default-present/absent. */ 202 {"dense_def_target", i32_ty, dense_vals, dense_res, 6, 1, 999, 203 KIT_CG_SWITCH_TARGET_DEFAULT}, 204 {"dense_nodef_target", i32_ty, dense_vals, dense_res, 6, 0, 0, 205 KIT_CG_SWITCH_TARGET_DEFAULT}, 206 {"sparse_def_target", i32_ty, sparse_vals, sparse_res, 3, 1, 99, 207 KIT_CG_SWITCH_TARGET_DEFAULT}, 208 {"signed_def_target", i32_ty, signed_vals, signed_res, 7, 1, 999, 209 KIT_CG_SWITCH_TARGET_DEFAULT}, 210 {"signed64_def_target", i64_ty, signed_vals, signed_res, 7, 1, 999, 211 KIT_CG_SWITCH_TARGET_DEFAULT}, 212 213 /* Forced hint variants. JUMP_TABLE is advisory; lowering may 214 * accept it or fall back to chain — both must produce a valid 215 * function. */ 216 {"dense_def_jump_table", i32_ty, dense_vals, dense_res, 6, 1, 999, 217 KIT_CG_SWITCH_JUMP_TABLE}, 218 {"dense_nodef_jump_table", i32_ty, dense_vals, dense_res, 6, 0, 0, 219 KIT_CG_SWITCH_JUMP_TABLE}, 220 {"sparse_def_jump_table", i32_ty, sparse_vals, sparse_res, 3, 1, 99, 221 KIT_CG_SWITCH_JUMP_TABLE}, 222 {"signed_def_jump_table", i32_ty, signed_vals, signed_res, 7, 1, 999, 223 KIT_CG_SWITCH_JUMP_TABLE}, 224 225 {"dense_def_branch_chain", i32_ty, dense_vals, dense_res, 6, 1, 999, 226 KIT_CG_SWITCH_BRANCH_CHAIN}, 227 {"sparse_def_branch_chain", i32_ty, sparse_vals, sparse_res, 3, 1, 99, 228 KIT_CG_SWITCH_BRANCH_CHAIN}, 229 230 /* Singleton: minimum case count. Both hints. */ 231 {"single_def_target", i32_ty, single_vals, single_res, 1, 1, 99, 232 KIT_CG_SWITCH_TARGET_DEFAULT}, 233 {"single_def_jump_table", i32_ty, single_vals, single_res, 1, 1, 99, 234 KIT_CG_SWITCH_JUMP_TABLE}, 235 236 /* Empty (default-only). kit_cg_switch on an empty case array is 237 * tested for clean acceptance — frontends emit this from 238 * `switch (x) { default: ...; }`. */ 239 {"empty_def_target", i32_ty, NULL, NULL, 0, 1, 7, 240 KIT_CG_SWITCH_TARGET_DEFAULT}, 241 }; 242 243 size_t n = sizeof shapes / sizeof shapes[0]; 244 size_t i; 245 for (i = 0; i < n; ++i) { 246 build_switch_fn(c, i32_ty, &shapes[i], opt_level); 247 } 248 } 249 250 /* ---- Entry ----------------------------------------------------------- */ 251 252 int main(void) { 253 KitTargetSpec target; 254 KitCompiler* c = NULL; 255 KitCgBuiltinTypes bi; 256 KitCgTypeId i32_ty; 257 KitCgTypeId i64_ty; 258 259 kit_unit_init(&g_u); 260 target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF); 261 262 if (kit_unit_compiler_new(&g_u, target, &c) != KIT_OK || !c) { 263 fprintf(stderr, "compiler_new failed\n"); 264 return 2; 265 } 266 267 bi = kit_cg_builtin_types(c); 268 i32_ty = bi.id[KIT_CG_BUILTIN_I32]; 269 i64_ty = bi.id[KIT_CG_BUILTIN_I64]; 270 EXPECT(i32_ty != KIT_CG_TYPE_NONE, "i32 builtin id is none"); 271 EXPECT(i64_ty != KIT_CG_TYPE_NONE, "i64 builtin id is none"); 272 273 run_all_shapes(c, i32_ty, i64_ty, /*opt_level=*/0); 274 run_all_shapes(c, i32_ty, i64_ty, /*opt_level=*/1); 275 276 kit_compiler_free(c); 277 kit_unit_summary(&g_u, "cg_switch_test"); 278 return kit_unit_status(&g_u); 279 }