cg_control_test.c (11313B)
1 /* cg_control_test — drives the conditional structured control-flow ops 2 * (break_true / break_false / continue_true / continue_false) through the 3 * public KitCg API. break_true / continue_true / continue_false have zero 4 * callers in any frontend (toy uses only break / break_false / continue), so 5 * like the unordered FP compares they are reachable *only* through this entry 6 * point — this is the guard against an advertise-but-ignore gap in the 7 * structured-control surface. 8 * 9 * Each variant builds the same loop, `int f(int n) { return 0+1+..+(n-1); }`, 10 * spelled with a different conditional break/continue, and: 11 * 12 * Execution — captures each into the in-process interpreter (target- 13 * independent IR) and asserts the sum for a spread of n. 14 * 15 * Emission — builds each for aarch64 / x86-64 / riscv64 at -O0 and -O1 and 16 * finalizes; a backend that mishandles the lowered branches panics, which 17 * fails the test. 18 * 19 * Rejection — confirms `continue` on a forward-only block scope (which has no 20 * loop header) is rejected with a clean diagnostic rather than emitting a 21 * jump to a nonexistent label. 22 * 23 * Run by: make test-cg-api 24 */ 25 26 #include <kit/cg.h> 27 #include <kit/core.h> 28 #include <kit/frontend.h> 29 #include <kit/interp.h> 30 #include <kit/object.h> 31 #include <stdint.h> 32 #include <stdio.h> 33 #include <string.h> 34 35 #include "lib/kit_unit.h" 36 37 static KitUnit g_u; 38 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) 39 40 typedef enum { 41 V_BREAK_TRUE, 42 V_BREAK_FALSE, 43 V_CONTINUE_TRUE, 44 V_CONTINUE_FALSE 45 } Variant; 46 47 typedef struct { 48 Variant variant; 49 const char* name; 50 } VariantDesc; 51 52 static const VariantDesc VARIANTS[] = { 53 {V_BREAK_TRUE, "break_true"}, 54 {V_BREAK_FALSE, "break_false"}, 55 {V_CONTINUE_TRUE, "continue_true"}, 56 {V_CONTINUE_FALSE, "continue_false"}, 57 }; 58 enum { NVAR = (int)(sizeof VARIANTS / sizeof VARIANTS[0]) }; 59 60 /* Reference: sum 0 + 1 + ... + (n-1), with n clamped at 0. */ 61 static int64_t loop_expected(int64_t n) { 62 int64_t acc = 0, i; 63 for (i = 0; i < n; ++i) acc += i; 64 return acc; 65 } 66 67 /* Build `int <name>(int n)` computing sum(0..n-1) via `variant`. 68 * 69 * break_* : cond-at-top loop — test, conditionally exit, else body, continue. 70 * continue_*: cond-at-bottom loop — body, test, conditionally re-loop. 71 * All four are equivalent; they exercise the four conditional ops plus plain 72 * `continue` (in the break_* shapes). */ 73 static void build_loop_fn(KitCompiler* c, KitCg* cg, const char* name, 74 Variant variant) { 75 KitCgBuiltinTypes bi = kit_cg_builtin_types(c); 76 KitCgTypeId i32 = bi.id[KIT_CG_BUILTIN_I32]; 77 KitCgFuncParam param; 78 KitCgFuncResult result; 79 KitCgFuncSig sig; 80 KitCgDecl decl; 81 KitCgSym sym; 82 KitCgLocalAttrs la; 83 KitCgLocal n_param, i_local, acc_local; 84 KitCgMemAccess ma; 85 KitCgScope scope; 86 87 memset(¶m, 0, sizeof param); 88 param.type = i32; 89 memset(&result, 0, sizeof result); 90 result.type = i32; 91 memset(&sig, 0, sizeof sig); 92 sig.result = result; 93 sig.params = ¶m; 94 sig.nparams = 1; 95 sig.call_conv = KIT_CG_CC_TARGET_C; 96 97 memset(&decl, 0, sizeof decl); 98 decl.kind = KIT_CG_DECL_FUNC; 99 decl.linkage_name = kit_sym_intern(c, kit_slice_cstr(name)); 100 decl.display_name = decl.linkage_name; 101 decl.type = kit_cg_type_func(c, sig); 102 decl.sym.bind = KIT_SB_GLOBAL; 103 decl.sym.visibility = KIT_CG_VIS_DEFAULT; 104 sym = kit_cg_decl(cg, decl); 105 EXPECT(sym != KIT_CG_SYM_NONE, "%s: decl failed", name); 106 107 kit_cg_func_begin(cg, sym); 108 memset(&la, 0, sizeof la); 109 n_param = kit_cg_param(cg, 0, i32, la); 110 i_local = kit_cg_local(cg, i32, la); 111 acc_local = kit_cg_local(cg, i32, la); 112 113 memset(&ma, 0, sizeof ma); 114 ma.type = i32; 115 ma.align = kit_cg_type_align(c, i32); 116 117 /* i = 0; acc = 0; */ 118 kit_cg_push_int(cg, 0, i32); 119 kit_cg_local_write(cg, i_local, ma); 120 kit_cg_push_int(cg, 0, i32); 121 kit_cg_local_write(cg, acc_local, ma); 122 123 scope = kit_cg_scope_begin(cg, KIT_CG_TYPE_NONE); 124 125 if (variant == V_BREAK_TRUE || variant == V_BREAK_FALSE) { 126 /* test at top: break out when i has reached n */ 127 kit_cg_local_read(cg, i_local, ma); 128 kit_cg_local_read(cg, n_param, ma); 129 if (variant == V_BREAK_TRUE) { 130 kit_cg_int_cmp(cg, KIT_CG_INT_GE_S); /* i >= n */ 131 kit_cg_break_true(cg, scope); /* exit when true */ 132 } else { 133 kit_cg_int_cmp(cg, KIT_CG_INT_LT_S); /* i < n */ 134 kit_cg_break_false(cg, scope); /* exit when false (i >= n) */ 135 } 136 /* acc += i; i += 1; */ 137 kit_cg_local_read(cg, acc_local, ma); 138 kit_cg_local_read(cg, i_local, ma); 139 kit_cg_int_binop(cg, KIT_CG_INT_ADD, 0); 140 kit_cg_local_write(cg, acc_local, ma); 141 kit_cg_local_read(cg, i_local, ma); 142 kit_cg_push_int(cg, 1, i32); 143 kit_cg_int_binop(cg, KIT_CG_INT_ADD, 0); 144 kit_cg_local_write(cg, i_local, ma); 145 kit_cg_continue(cg, scope); 146 } else { 147 /* body first, test at bottom: re-loop while i < n */ 148 kit_cg_local_read(cg, acc_local, ma); 149 kit_cg_local_read(cg, i_local, ma); 150 kit_cg_int_binop(cg, KIT_CG_INT_ADD, 0); 151 kit_cg_local_write(cg, acc_local, ma); 152 kit_cg_local_read(cg, i_local, ma); 153 kit_cg_push_int(cg, 1, i32); 154 kit_cg_int_binop(cg, KIT_CG_INT_ADD, 0); 155 kit_cg_local_write(cg, i_local, ma); 156 kit_cg_local_read(cg, i_local, ma); 157 kit_cg_local_read(cg, n_param, ma); 158 if (variant == V_CONTINUE_TRUE) { 159 kit_cg_int_cmp(cg, KIT_CG_INT_LT_S); /* i < n */ 160 kit_cg_continue_true(cg, scope); /* re-loop while true */ 161 } else { 162 kit_cg_int_cmp(cg, KIT_CG_INT_GE_S); /* i >= n */ 163 kit_cg_continue_false(cg, scope); /* re-loop while false (i < n) */ 164 } 165 } 166 167 kit_cg_scope_end(cg, scope); 168 kit_cg_local_read(cg, acc_local, ma); 169 kit_cg_ret(cg); 170 kit_cg_func_end(cg); 171 } 172 173 /* ---- Execution coverage (in-process interpreter) -------------------- */ 174 175 static const int64_t NS[] = {0, 1, 2, 5, 10}; 176 enum { NNS = (int)(sizeof NS / sizeof NS[0]) }; 177 178 static void run_exec(void) { 179 KitTargetSpec tgt = 180 kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); 181 KitCompiler* c = NULL; 182 KitInterpProgram* pp; 183 KitObjBuilder* ob = NULL; 184 KitCg* cg = NULL; 185 KitCodeOptions opts; 186 int i, j; 187 char nm[32]; 188 189 if (kit_unit_compiler_new(&g_u, tgt, &c) != KIT_OK || !c) { 190 EXPECT(0, "exec: compiler_new failed"); 191 return; 192 } 193 pp = kit_interp_program_new(c); 194 EXPECT(pp != NULL, "exec: interp_program_new failed"); 195 kit_interp_program_attach(pp, c); 196 197 EXPECT(kit_obj_builder_new(c, &ob) == KIT_OK && ob, "exec: obj_new"); 198 EXPECT(kit_cg_new(c, &cg) == KIT_OK && cg, "exec: cg_new"); 199 memset(&opts, 0, sizeof opts); 200 opts.opt_level = 1; /* interp capture requires the optimizer pass */ 201 kit_cg_begin(cg, ob, &opts); 202 203 for (i = 0; i < NVAR; ++i) { 204 snprintf(nm, sizeof nm, "loop_%s", VARIANTS[i].name); 205 build_loop_fn(c, cg, nm, VARIANTS[i].variant); 206 } 207 EXPECT(kit_cg_finish(cg, NULL) == KIT_OK, "exec: finish"); 208 EXPECT(kit_cg_detach(cg) == KIT_OK, "exec: detach"); 209 210 for (i = 0; i < NVAR; ++i) { 211 KitInterpFunc* fn; 212 snprintf(nm, sizeof nm, "loop_%s", VARIANTS[i].name); 213 fn = kit_interp_lookup(pp, kit_slice_cstr(nm)); 214 EXPECT(fn != NULL, "exec: %s not captured", VARIANTS[i].name); 215 if (!fn) continue; 216 for (j = 0; j < NNS; ++j) { 217 uint64_t args[1] = {(uint64_t)NS[j]}; 218 int64_t ret = -1; 219 int64_t want = loop_expected(NS[j]); 220 KitInterpStatus s = kit_interp_call_args(pp, fn, args, 1, &ret); 221 EXPECT(s == KIT_INTERP_DONE && ret == want, 222 "%s(%lld): want %lld got %lld (status %d)", VARIANTS[i].name, 223 (long long)NS[j], (long long)want, (long long)ret, (int)s); 224 } 225 } 226 227 kit_cg_free(cg); 228 kit_obj_builder_free(ob); 229 kit_interp_program_free(pp); 230 kit_compiler_free(c); 231 } 232 233 /* ---- Emission coverage (every native backend, no execution) --------- */ 234 235 static void run_emit(KitArchKind arch, KitOSKind os, KitObjFmt fmt, 236 const char* tag, int opt_level) { 237 KitTargetSpec tgt = kit_unit_target(arch, os, fmt); 238 KitCompiler* c = NULL; 239 KitObjBuilder* ob = NULL; 240 KitCg* cg = NULL; 241 KitCodeOptions opts; 242 int i; 243 char nm[48]; 244 245 if (kit_unit_compiler_new(&g_u, tgt, &c) != KIT_OK || !c) { 246 EXPECT(0, "%s/O%d: compiler_new failed", tag, opt_level); 247 return; 248 } 249 EXPECT(kit_obj_builder_new(c, &ob) == KIT_OK && ob, "%s: obj_new", tag); 250 EXPECT(kit_cg_new(c, &cg) == KIT_OK && cg, "%s: cg_new", tag); 251 memset(&opts, 0, sizeof opts); 252 opts.opt_level = opt_level; 253 kit_cg_begin(cg, ob, &opts); 254 255 for (i = 0; i < NVAR; ++i) { 256 snprintf(nm, sizeof nm, "emit_%s_o%d_%s", tag, opt_level, VARIANTS[i].name); 257 build_loop_fn(c, cg, nm, VARIANTS[i].variant); 258 } 259 EXPECT(kit_cg_finish(cg, NULL) == KIT_OK, "%s/O%d: finish failed", tag, 260 opt_level); 261 EXPECT(kit_cg_detach(cg) == KIT_OK, "%s/O%d: detach failed", tag, opt_level); 262 263 kit_cg_free(cg); 264 kit_obj_builder_free(ob); 265 kit_compiler_free(c); 266 } 267 268 /* ---- Rejection: continue on a forward-only block scope -------------- */ 269 270 typedef struct { 271 KitObjBuilder* ob; 272 KitCg* cg; 273 } RejectCtx; 274 275 static KitStatus reject_body(KitCompiler* c, void* user) { 276 RejectCtx* r = (RejectCtx*)user; 277 KitCgBuiltinTypes bi = kit_cg_builtin_types(c); 278 KitCgFuncSig sig; 279 KitCgDecl decl; 280 KitCgSym sym; 281 KitCgScope blk; 282 (void)bi; 283 memset(&sig, 0, sizeof sig); 284 sig.call_conv = KIT_CG_CC_TARGET_C; /* void() */ 285 memset(&decl, 0, sizeof decl); 286 decl.kind = KIT_CG_DECL_FUNC; 287 decl.linkage_name = kit_sym_intern(c, kit_slice_cstr("reject_fn")); 288 decl.display_name = decl.linkage_name; 289 decl.type = kit_cg_type_func(c, sig); 290 decl.sym.bind = KIT_SB_GLOBAL; 291 decl.sym.visibility = KIT_CG_VIS_DEFAULT; 292 sym = kit_cg_decl(r->cg, decl); 293 kit_cg_func_begin(r->cg, sym); 294 blk = kit_cg_block_begin(r->cg, KIT_CG_TYPE_NONE); 295 kit_cg_continue(r->cg, blk); /* <- must panic: blocks have no loop header */ 296 kit_cg_scope_end(r->cg, blk); 297 kit_cg_ret(r->cg); 298 kit_cg_func_end(r->cg); 299 return KIT_OK; 300 } 301 302 static void run_reject(void) { 303 KitTargetSpec tgt = 304 kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); 305 KitCompiler* c = NULL; 306 RejectCtx r; 307 KitCodeOptions opts; 308 KitStatus st; 309 memset(&r, 0, sizeof r); 310 if (kit_unit_compiler_new(&g_u, tgt, &c) != KIT_OK || !c) { 311 EXPECT(0, "reject: compiler_new failed"); 312 return; 313 } 314 EXPECT(kit_obj_builder_new(c, &r.ob) == KIT_OK && r.ob, "reject: obj_new"); 315 EXPECT(kit_cg_new(c, &r.cg) == KIT_OK && r.cg, "reject: cg_new"); 316 memset(&opts, 0, sizeof opts); 317 kit_cg_begin(r.cg, r.ob, &opts); 318 319 g_u.suppress_fatal = 1; 320 st = kit_frontend_run(c, reject_body, &r); 321 g_u.suppress_fatal = 0; 322 323 EXPECT(st == KIT_ERR, "continue on block scope should be rejected"); 324 EXPECT(strstr(g_u.last_diag, "forward-only block scope") != NULL, 325 "rejection diagnostic should name the block scope (got: %s)", 326 g_u.last_diag); 327 328 kit_compiler_free(c); 329 } 330 331 int main(void) { 332 int opt; 333 kit_unit_init(&g_u); 334 335 run_exec(); 336 337 for (opt = 0; opt <= 1; ++opt) { 338 run_emit(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF, "aa64", opt); 339 run_emit(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF, "x64", opt); 340 run_emit(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF, "rv64", opt); 341 } 342 343 run_reject(); 344 345 kit_unit_summary(&g_u, "cg_control_test"); 346 return kit_unit_status(&g_u); 347 }