kit

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

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(&param, 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 = &param;
     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 }