kit

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

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(&param_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 = &param_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 }