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 }