kit

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

cg_ir_lower_test.c (6215B)


      1 #include <kit/core.h>
      2 #include <stdarg.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 
      7 #include "cg/ir.h"
      8 #include "lib/kit_unit.h"
      9 #include "opt/opt.h"
     10 
     11 #undef Operand
     12 #undef CGFuncDesc
     13 #undef CGParamDesc
     14 #undef CGCallDesc
     15 #undef CGLocalStorage
     16 
     17 /* Shared test context replaces the per-file heap/diag/counter globals;
     18  * EXPECT aliases CU_EXPECT so the call sites are unchanged. ctx.now = -1 is
     19  * preserved by setting it once after kit_unit_init in main(). */
     20 static KitUnit g_u;
     21 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__)
     22 
     23 typedef struct TestCtx {
     24   Compiler* c;
     25   KitCgTypeId i32;
     26 } TestCtx;
     27 
     28 static void tc_init(TestCtx* tc) {
     29   KitTargetSpec target;
     30   memset(tc, 0, sizeof *tc);
     31   target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO);
     32   if (kit_unit_compiler_new(&g_u, target, (KitCompiler**)&tc->c) != KIT_OK ||
     33       !tc->c) {
     34     fprintf(stderr, "fatal: compiler allocation failed\n");
     35     abort();
     36   }
     37   tc->i32 = kit_cg_type_builtin(tc->c, KIT_CG_BUILTIN_I32);
     38 }
     39 
     40 static void tc_fini(TestCtx* tc) {
     41   kit_compiler_free(tc->c);
     42   tc->c = NULL;
     43 }
     44 
     45 static Operand local_op(CGLocal local, KitCgTypeId type) {
     46   Operand o;
     47   memset(&o, 0, sizeof o);
     48   o.kind = OPK_LOCAL;
     49   o.type = type;
     50   o.v.local = local;
     51   return o;
     52 }
     53 
     54 static Operand imm_op(i64 value, KitCgTypeId type) {
     55   Operand o;
     56   memset(&o, 0, sizeof o);
     57   o.kind = OPK_IMM;
     58   o.type = type;
     59   o.v.imm = value;
     60   return o;
     61 }
     62 
     63 static CGLocal add_local(CgIrFunc* f, KitCgTypeId type, const char* name) {
     64   CGLocalDesc d;
     65   (void)name;
     66   memset(&d, 0, sizeof d);
     67   d.type = type;
     68   d.size = 4;
     69   d.align = 4;
     70   return cg_ir_func_add_local(f, &d, 0, 0);
     71 }
     72 
     73 static CgIrInst* emit_ops(CgIrFunc* f, CgIrOp op, const Operand* ops, u32 n) {
     74   CgIrInst* in = cg_ir_emit(f, op, (SrcLoc){0, 0, 0});
     75   in->opnds = cg_ir_dup_operands(f->arena, ops, n);
     76   in->nopnds = n;
     77   return in;
     78 }
     79 
     80 static void converter_builds_cfg_and_pregs(void) {
     81   TestCtx tc;
     82   tc_init(&tc);
     83 
     84   CGFuncDesc fd;
     85   memset(&fd, 0, sizeof fd);
     86   fd.fn_type = tc.i32;
     87   fd.result_type = tc.i32;
     88   CgIrFunc* cg = cg_ir_func_new(tc.c, &fd);
     89   CGLocal a = add_local(cg, tc.i32, "a");
     90   CGLocal b = add_local(cg, tc.i32, "b");
     91   Label done = cg_ir_func_add_label(cg);
     92 
     93   Operand one[] = {local_op(a, tc.i32)};
     94   CgIrInst* li = emit_ops(cg, CG_IR_LOAD_IMM, one, 1);
     95   li->extra.imm = 1;
     96 
     97   Operand cmp[] = {local_op(a, tc.i32), imm_op(0, tc.i32)};
     98   CgIrInst* br = emit_ops(cg, CG_IR_CMP_BRANCH, cmp, 2);
     99   CgIrCmpBranchAux* br_aux = arena_znew(cg->arena, CgIrCmpBranchAux);
    100   br_aux->op = CMP_NE;
    101   br_aux->target = done;
    102   br->extra.aux = br_aux;
    103 
    104   Operand two[] = {local_op(b, tc.i32)};
    105   CgIrInst* li2 = emit_ops(cg, CG_IR_LOAD_IMM, two, 1);
    106   li2->extra.imm = 2;
    107 
    108   CgIrInst* label = cg_ir_emit(cg, CG_IR_LABEL, (SrcLoc){0, 0, 0});
    109   label->extra.imm = (i64)done;
    110   cg_ir_func_note_label_place(cg, done, (SrcLoc){0, 0, 0});
    111 
    112   CgIrInst* li3 = emit_ops(cg, CG_IR_LOAD_IMM, two, 1);
    113   li3->extra.imm = 3;
    114 
    115   CgIrRetAux* ret_aux = arena_znew(cg->arena, CgIrRetAux);
    116   ret_aux->value = b;
    117   ret_aux->present = 1;
    118   CgIrInst* ret = cg_ir_emit(cg, CG_IR_RET, (SrcLoc){0, 0, 0});
    119   ret->extra.aux = ret_aux;
    120 
    121   Func* f = opt_func_from_cg_ir(tc.c, cg);
    122   EXPECT(f != NULL, "converter returned NULL");
    123   EXPECT(f->nlocals == 2, "expected 2 locals, got %u", f->nlocals);
    124   EXPECT(f->npregs == 3, "expected two PRegs plus sentinel, got %u", f->npregs);
    125   EXPECT(f->nblocks >= 3, "expected at least 3 blocks, got %u", f->nblocks);
    126   EXPECT(f->blocks[f->entry].nsucc == 2, "entry should branch two ways");
    127   EXPECT(f->blocks[f->entry].ninsts == 2, "entry should contain load+branch");
    128   EXPECT(f->blocks[f->entry].insts[0].op == IR_LOAD_IMM,
    129          "first inst should be IR_LOAD_IMM");
    130   EXPECT(f->blocks[f->entry].insts[0].opnds[0].kind == OPK_REG,
    131          "local value should lower to PReg operand");
    132 
    133   tc_fini(&tc);
    134 }
    135 
    136 static void jump_cleanup_threads_empty_fallthrough_target(void) {
    137   TestCtx tc;
    138   tc_init(&tc);
    139 
    140   Func f;
    141   memset(&f, 0, sizeof f);
    142   f.c = tc.c;
    143   f.arena = tc.c->tu;
    144   f.entry = ir_block_new(&f);
    145   u32 then_block = ir_block_new(&f);
    146   u32 scope_block = ir_block_new(&f);
    147   u32 empty_block = ir_block_new(&f);
    148   u32 merge_block = ir_block_new(&f);
    149   ir_note_emit(&f, f.entry);
    150   ir_note_emit(&f, then_block);
    151   ir_note_emit(&f, scope_block);
    152   ir_note_emit(&f, empty_block);
    153   ir_note_emit(&f, merge_block);
    154 
    155   Inst* br = ir_emit(&f, f.entry, IR_CMP_BRANCH);
    156   br->extra.imm = CMP_EQ;
    157   f.blocks[f.entry].succ[0] = scope_block;
    158   f.blocks[f.entry].succ[1] = then_block;
    159   f.blocks[f.entry].nsucc = 2;
    160 
    161   Inst* then_body = ir_emit(&f, then_block, IR_LOAD_IMM);
    162   (void)then_body;
    163   Inst* then_br = ir_emit(&f, then_block, IR_BR);
    164   (void)then_br;
    165   f.blocks[then_block].succ[0] = merge_block;
    166   f.blocks[then_block].nsucc = 1;
    167 
    168   Inst* scope_end = ir_emit(&f, scope_block, IR_SCOPE_END);
    169   (void)scope_end;
    170   f.blocks[scope_block].succ[0] = empty_block;
    171   f.blocks[scope_block].nsucc = 1;
    172 
    173   f.blocks[empty_block].succ[0] = merge_block;
    174   f.blocks[empty_block].nsucc = 1;
    175 
    176   Inst* ret = ir_emit(&f, merge_block, IR_RET);
    177   (void)ret;
    178 
    179   opt_build_cfg(&f);
    180   EXPECT(f.blocks[f.entry].succ[0] == scope_block,
    181          "precondition: branch should target scope block");
    182   EXPECT(f.blocks[scope_block].npreds == 1,
    183          "precondition: scope block should have one predecessor");
    184 
    185   opt_jump_cleanup(&f, OPT_JUMP_CLEANUP_CFG);
    186   opt_build_cfg(&f);
    187 
    188   EXPECT(f.blocks[f.entry].succ[0] == merge_block,
    189          "branch target should forward through empty fallthrough block");
    190   EXPECT(f.blocks[scope_block].npreds == 0 && f.blocks[scope_block].nsucc == 0,
    191          "scope block should become unreachable after forwarding");
    192   EXPECT(f.blocks[empty_block].npreds == 0 && f.blocks[empty_block].nsucc == 0,
    193          "empty block should become unreachable after forwarding");
    194 
    195   tc_fini(&tc);
    196 }
    197 
    198 int main(void) {
    199   kit_unit_init(&g_u);
    200   g_u.ctx.now = -1;
    201   converter_builds_cfg_and_pregs();
    202   jump_cleanup_threads_empty_fallthrough_target();
    203   if (g_u.fails) {
    204     fprintf(stderr, "cg-ir-lower: %d/%d failed\n", g_u.fails, g_u.checks);
    205     return 1;
    206   }
    207   printf("cg-ir-lower: %d checks, 0 failures\n", g_u.checks);
    208   return 0;
    209 }