kit

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

cg_ir_lower_test.c (6251B)


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