From 097dbdcc2a72c988ed206292fe0ff06c74254918 Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Sat, 21 Mar 2026 14:38:30 +0800 Subject: [PATCH] =?UTF-8?q?feat(ir2mcode):=20=E5=AE=9E=E7=8E=B0AMD64?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90=E5=99=A8=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E6=B5=81=E5=92=8C=E5=87=BD=E6=95=B0=E8=B0=83?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了条件分支、无条件跳转和函数调用的机器码生成 - 添加了跳转目标地址回填机制,处理条件分支和跳转指令的偏移量 - 改进了寄存器分配逻辑,支持函数调用返回值的处理 - 重构了位置解析函数,从返回指针改为传入引用参数 fix(ast2ir): 移除无用的注释代码 - 删除了关于一元操作符映射的注释代码 test: 更新测试框架和测试用例 - 修改测试框架以支持新的可执行文件输出格式 - 添加了条件分支、循环和函数调用的测试用例 - 使用TOML配置文件管理期望的返回值 - 替换标准库头文件为自定义头文件以减少依赖 --- libs/ast2ir/src/scc_ast2ir.c | 1 - libs/ir2mcode/src/ir2amd64.c | 266 +++++++++++++++++++++++++++------ libs/ir2mcode/src/reg_alloc.c | 14 ++ libs/ir2mcode/tests/test_run.c | 34 ++++- tests/simple/12_include.c | 6 +- tests/simple/expect.toml | 13 ++ tests/simple/scc_stdio.h | 6 + tests/simple/test.py | 101 +++++++------ 8 files changed, 340 insertions(+), 101 deletions(-) create mode 100644 tests/simple/expect.toml create mode 100644 tests/simple/scc_stdio.h diff --git a/libs/ast2ir/src/scc_ast2ir.c b/libs/ast2ir/src/scc_ast2ir.c index 24b6a24..02daca5 100644 --- a/libs/ast2ir/src/scc_ast2ir.c +++ b/libs/ast2ir/src/scc_ast2ir.c @@ -234,7 +234,6 @@ scc_ir_node_ref_t scc_ast2ir_expr(scc_ast2ir_ctx_t *ctx, scc_ast_expr_t *expr, // SCC_AST_OP_PREFIX_DECREMENT, // -- (前缀) // SCC_AST_OP_POSTFIX_INCREMENT, // ++ (后缀) // SCC_AST_OP_POSTFIX_DECREMENT, // -- (后缀) - // 映射一元操作符 switch (expr->unary.op) { case SCC_AST_OP_UNARY_MINUS: // 负号 diff --git a/libs/ir2mcode/src/ir2amd64.c b/libs/ir2mcode/src/ir2amd64.c index d39ba40..a8faa60 100644 --- a/libs/ir2mcode/src/ir2amd64.c +++ b/libs/ir2mcode/src/ir2amd64.c @@ -3,12 +3,14 @@ #include #include -static scc_reg_loc_t *parse_location(scc_ir2mcode_ctx_t *ctx, - scc_ir_bblock_ref_t node_ref) { +static void parse_location(scc_ir2mcode_ctx_t *ctx, scc_reg_loc_t *loc, + scc_ir_bblock_ref_t node_ref) { + Assert(ctx != null && loc != null); scc_ir_node_t *node = scc_ir_ctx_get_node(ctx->ir_ctx, node_ref); if (node == null) { LOG_FATAL("invalid node ref"); - return null; + UNREACHABLE(); + return; } usize idx = 0; switch (node->tag) { @@ -16,11 +18,11 @@ static scc_reg_loc_t *parse_location(scc_ir2mcode_ctx_t *ctx, scc_ir_type_t *type = scc_ir_ctx_get_type(ctx->ir_ctx, node->type); Assert(type != 0); Assert(type->tag == SCC_IR_TYPE_U32 || type->tag == SCC_IR_TYPE_I32); - scc_reg_loc_t loc = {.kind = SCC_REG_KIND_IMM, - .idx = (usize)node->data.const_int.int32}; - scc_vec_push(ctx->reg_alloc.reg_loc_vec, loc); - idx = scc_vec_size(ctx->reg_alloc.reg_loc_vec); - break; + *loc = (scc_reg_loc_t){ + .kind = SCC_REG_KIND_IMM, + .idx = (usize)node->data.const_int.int32, + }; + return; case SCC_IR_NODE_CONST_UINT: case SCC_IR_NODE_CONST_FLOAT: TODO(); @@ -32,7 +34,7 @@ static scc_reg_loc_t *parse_location(scc_ir2mcode_ctx_t *ctx, } Assert(idx > 0 && idx <= scc_vec_size(ctx->reg_alloc.reg_loc_vec)); - return &scc_vec_at(ctx->reg_alloc.reg_loc_vec, idx - 1); + *loc = scc_vec_at(ctx->reg_alloc.reg_loc_vec, idx - 1); } static void load_value_to_reg(scc_mcode_t *mcode, scc_reg_loc_t *loc, int reg) { @@ -77,8 +79,15 @@ static void store_value_from_reg(scc_mcode_t *mcode, scc_reg_loc_t *loc, } } +// 临时存储待修补条目 +typedef struct { + usize pos; + usize target_bb_ref; +} patch_t; +typedef SCC_VEC(patch_t) patch_vec_t; + static void parse_node(scc_ir2mcode_ctx_t *ctx, scc_ir_bblock_ref_t node_ref, - int idx) { + patch_vec_t *patches) { scc_ir_node_t *node = scc_ir_ctx_get_node(ctx->ir_ctx, node_ref); if (node == null) { LOG_ERROR("invalid node ref"); @@ -97,32 +106,39 @@ static void parse_node(scc_ir2mcode_ctx_t *ctx, scc_ir_bblock_ref_t node_ref, case SCC_IR_NODE_LOAD: ///< 加载数据 { // node->data.load.target - scc_reg_loc_t *from = parse_location(ctx, node->data.load.target); - scc_reg_loc_t *to = parse_location(ctx, node_ref); - load_value_to_reg(&ctx->mcode, from, SCC_AMD64_RAX); - store_value_from_reg(&ctx->mcode, to, SCC_AMD64_RAX); + scc_reg_loc_t from; + scc_reg_loc_t to; + parse_location(ctx, &from, node->data.load.target); + parse_location(ctx, &to, node_ref); + load_value_to_reg(&ctx->mcode, &from, SCC_AMD64_RAX); + store_value_from_reg(&ctx->mcode, &to, SCC_AMD64_RAX); break; } case SCC_IR_NODE_STORE: ///< 存储数据 { - scc_reg_loc_t *from = parse_location(ctx, node->data.store.value); - scc_reg_loc_t *to = parse_location(ctx, node->data.store.target); - load_value_to_reg(&ctx->mcode, from, SCC_AMD64_RAX); - store_value_from_reg(&ctx->mcode, to, SCC_AMD64_RAX); + scc_reg_loc_t from; + scc_reg_loc_t to; + parse_location(ctx, &from, node->data.store.value); + parse_location(ctx, &to, node->data.store.target); + load_value_to_reg(&ctx->mcode, &from, SCC_AMD64_RAX); + store_value_from_reg(&ctx->mcode, &to, SCC_AMD64_RAX); break; } case SCC_IR_NODE_GET_PTR: ///< 获取指针 case SCC_IR_NODE_GET_ELEM_PTR: ///< 获取元素指针(used by array) TODO(); case SCC_IR_NODE_OP: ///< 二元运算 - scc_reg_loc_t *loc_lhs = parse_location(ctx, node->data.op.lhs); - scc_reg_loc_t *loc_rhs = parse_location(ctx, node->data.op.rhs); - scc_reg_loc_t *loc_res = parse_location(ctx, node_ref); + scc_reg_loc_t loc_lhs; + parse_location(ctx, &loc_lhs, node->data.op.lhs); + scc_reg_loc_t loc_rhs; + parse_location(ctx, &loc_rhs, node->data.op.rhs); + scc_reg_loc_t loc_res; + parse_location(ctx, &loc_res, node_ref); // 将左操作数加载到 RAX(临时结果寄存器) - load_value_to_reg(&ctx->mcode, loc_lhs, SCC_AMD64_RAX); + load_value_to_reg(&ctx->mcode, &loc_lhs, SCC_AMD64_RAX); // 将右操作数加载到 RCX - load_value_to_reg(&ctx->mcode, loc_rhs, SCC_AMD64_RCX); + load_value_to_reg(&ctx->mcode, &loc_rhs, SCC_AMD64_RCX); switch (node->data.op.op) { case SCC_IR_OP_ADD: scc_mcode_amd64_add_r64_r64(&ctx->mcode, SCC_AMD64_RAX, @@ -135,59 +151,195 @@ static void parse_node(scc_ir2mcode_ctx_t *ctx, scc_ir_bblock_ref_t node_ref, case SCC_IR_OP_MUL: scc_mcode_amd64_mul_r64(&ctx->mcode, SCC_AMD64_RCX); break; + // [SCC_IR_OP_EMPTY] = "empty", [SCC_IR_OP_NEQ] = "!=", + // [SCC_IR_OP_EQ] = "==", [SCC_IR_OP_GT] = ">", + // [SCC_IR_OP_LT] = "<", [SCC_IR_OP_GE] = ">=", + // [SCC_IR_OP_LE] = "<=", [SCC_IR_OP_ADD] = "+", + // [SCC_IR_OP_SUB] = "-", [SCC_IR_OP_MUL] = "*", + // [SCC_IR_OP_DIV] = "/", [SCC_IR_OP_MOD] = "%", + // [SCC_IR_OP_AND] = "&", [SCC_IR_OP_OR] = "|", + // [SCC_IR_OP_XOR] = "^", [SCC_IR_OP_NOT] = "~", + // [SCC_IR_OP_SHL] = "<<", [SCC_IR_OP_SHR] = ">>", + // [SCC_IR_OP_SAR] = ">>a", // Arithmetic shift right + case SCC_IR_OP_NEQ: + scc_mcode_amd64_cmp_r64_r64(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RCX); + scc_mcode_amd64_setcc_r8(&ctx->mcode, SCC_AMD64_COND_NE, + SCC_AMD64_RAX); + scc_mcode_amd64_movzx_r64_r8(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RAX); + break; + case SCC_IR_OP_EQ: + scc_mcode_amd64_cmp_r64_r64(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RCX); + scc_mcode_amd64_setcc_r8(&ctx->mcode, SCC_AMD64_COND_E, + SCC_AMD64_RAX); + scc_mcode_amd64_movzx_r64_r8(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RAX); + break; + case SCC_IR_OP_GT: + scc_mcode_amd64_cmp_r64_r64(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RCX); + scc_mcode_amd64_setcc_r8(&ctx->mcode, SCC_AMD64_COND_G, + SCC_AMD64_RAX); + scc_mcode_amd64_movzx_r64_r8(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RAX); + break; + case SCC_IR_OP_LT: + scc_mcode_amd64_cmp_r64_r64(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RCX); + scc_mcode_amd64_setcc_r8(&ctx->mcode, SCC_AMD64_COND_L, + SCC_AMD64_RAX); + scc_mcode_amd64_movzx_r64_r8(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RAX); + break; + case SCC_IR_OP_GE: + scc_mcode_amd64_cmp_r64_r64(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RCX); + scc_mcode_amd64_setcc_r8(&ctx->mcode, SCC_AMD64_COND_GE, + SCC_AMD64_RAX); + scc_mcode_amd64_movzx_r64_r8(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RAX); + break; + case SCC_IR_OP_LE: + scc_mcode_amd64_cmp_r64_r64(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RCX); + scc_mcode_amd64_setcc_r8(&ctx->mcode, SCC_AMD64_COND_LE, + SCC_AMD64_RAX); + scc_mcode_amd64_movzx_r64_r8(&ctx->mcode, SCC_AMD64_RAX, + SCC_AMD64_RAX); + break; default: LOG_FATAL("unknown op: %d", node->data.op.op); break; } // 将 RAX 中的结果存储到 res 位置 - store_value_from_reg(&ctx->mcode, loc_res, SCC_AMD64_RAX); - // [SCC_IR_OP_EMPTY] = "empty", [SCC_IR_OP_NEQ] = "!=", - // [SCC_IR_OP_EQ] = "==", [SCC_IR_OP_GT] = ">", - // [SCC_IR_OP_LT] = "<", [SCC_IR_OP_GE] = ">=", - // [SCC_IR_OP_LE] = "<=", [SCC_IR_OP_ADD] = "+", - // [SCC_IR_OP_SUB] = "-", [SCC_IR_OP_MUL] = "*", - // [SCC_IR_OP_DIV] = "/", [SCC_IR_OP_MOD] = "%", - // [SCC_IR_OP_AND] = "&", [SCC_IR_OP_OR] = "|", - // [SCC_IR_OP_XOR] = "^", [SCC_IR_OP_NOT] = "~", - // [SCC_IR_OP_SHL] = "<<", [SCC_IR_OP_SHR] = ">>", - // [SCC_IR_OP_SAR] = ">>a", // Arithmetic shift right + store_value_from_reg(&ctx->mcode, &loc_res, SCC_AMD64_RAX); break; - case SCC_IR_NODE_BRANCH: ///< 有条件分支 - case SCC_IR_NODE_JUMP: ///< 无条件跳转 - case SCC_IR_NODE_CALL: ///< 调用函数 - LOG_FATAL("Unsupported node type: %d", node->tag); + ///< 有条件分支 + case SCC_IR_NODE_BRANCH: { + scc_reg_loc_t loc; + parse_location(ctx, &loc, node->data.branch.cond); + // (void)loc; + load_value_to_reg(&ctx->mcode, &loc, SCC_AMD64_RAX); + scc_mcode_amd64_cmp_r64_imm32(&ctx->mcode, SCC_AMD64_RAX, 0); + + scc_mcode_amd64_jcc_rel32(&ctx->mcode, SCC_AMD64_COND_NE, 0); + patch_t patch_true = {.pos = scc_vec_size(ctx->mcode.mcode), + .target_bb_ref = + (usize)node->data.branch.true_bblock}; + scc_vec_push(*patches, patch_true); + scc_mcode_amd64_jmp_rel32(&ctx->mcode, 0); + patch_t patch_false = {.pos = scc_vec_size(ctx->mcode.mcode), + .target_bb_ref = + (usize)node->data.branch.false_bblock}; + scc_vec_push(*patches, patch_false); break; - case SCC_IR_NODE_RET: ///< 函数返回 + } + ///< 无条件跳转 + case SCC_IR_NODE_JUMP: { + scc_mcode_amd64_jmp_rel32(&ctx->mcode, 0); + usize pos = scc_vec_size(ctx->mcode.mcode); + patch_t patch = {.pos = pos, + .target_bb_ref = (usize)node->data.jump.target_bblock}; + scc_vec_push(*patches, patch); + break; + } + ///< 调用函数 + case SCC_IR_NODE_CALL: { + scc_reg_loc_t loc; + /* + ABI + RAX 不稳定的 返回值寄存器 + RCX 不稳定的 第一个整型自变量 + RDX 不稳定的 第二个整型自变量 + R8 不稳定的 第三个整型自变量 + R9 不稳定的 第四个整型自变量 + */ + scc_vec_foreach(node->data.call.args, i) { + parse_location(ctx, &loc, node->data.branch.cond); + if (i == 0) { + load_value_to_reg(&ctx->mcode, &loc, SCC_AMD64_RCX); + } else if (i == 1) { + load_value_to_reg(&ctx->mcode, &loc, SCC_AMD64_RDX); + } else if (i == 2) { + load_value_to_reg(&ctx->mcode, &loc, SCC_AMD64_R8); + } else if (i == 3) { + load_value_to_reg(&ctx->mcode, &loc, SCC_AMD64_R9); + } else { + LOG_FATAL("not support more than 4 args"); + } + // scc_mcode_amd64_push_r64(); + } + + scc_ir_func_t *func = + scc_ir_ctx_get_func(ctx->ir_ctx, node->data.call.callee); + if (!func) { + LOG_ERROR("invalid function reference"); + return; + } + + scc_mcode_amd64_call_rel32(&ctx->mcode, 0); + + // 处理返回值 + scc_ir_type_t *ret_type = scc_ir_ctx_get_type(ctx->ir_ctx, node->type); + if (ret_type && ret_type->tag != SCC_IR_TYPE_VOID) { + parse_location(ctx, &loc, node_ref); + store_value_from_reg(&ctx->mcode, &loc, SCC_AMD64_RAX); + } + break; + } + ///< 函数返回 + case SCC_IR_NODE_RET: { if (node->data.ret.ret_val) { - scc_reg_loc_t *loc = parse_location(ctx, node->data.ret.ret_val); - load_value_to_reg(&ctx->mcode, loc, SCC_AMD64_RAX); + scc_reg_loc_t loc; + parse_location(ctx, &loc, node->data.ret.ret_val); + load_value_to_reg(&ctx->mcode, &loc, SCC_AMD64_RAX); } scc_mcode_amd64_mov_r64_r64(&ctx->mcode, SCC_AMD64_RSP, SCC_AMD64_RBP); scc_mcode_amd64_pop_r64(&ctx->mcode, SCC_AMD64_RBP); scc_mcode_amd64_ret(&ctx->mcode); break; + } default: UNREACHABLE(); break; } } -static void parse_bblock(scc_ir2mcode_ctx_t *ctx, scc_ir_bblock_t *bblock) { +static void parse_bblock(scc_ir2mcode_ctx_t *ctx, scc_ir_bblock_t *bblock, + patch_vec_t *patches) { // 打印基本块中的每条指令 for (usize i = 0; i < scc_vec_size(bblock->instrs); i++) { scc_ir_node_ref_t node_ref = scc_vec_at(bblock->instrs, i); - parse_node(ctx, node_ref, i); + parse_node(ctx, node_ref, patches); } } -static void parse_function(scc_ir2mcode_ctx_t *ctx, scc_ir_func_t *func) { - scc_hashtable_t bblock2offset; +static u32 hash_func(const void *key) { return (u32)(usize)key; } +static int equal_func(const void *key1, const void *key2) { + return (usize)key1 - (usize)key2; +} +static void parse_function(scc_ir2mcode_ctx_t *ctx, scc_ir_func_t *func) { ctx->noderef2regloc = scc_reg_alloc(&ctx->reg_alloc, func); // 对齐到 16 字节 usize stack_size = (ctx->reg_alloc.alloc_stack_size + 15) & ~15; + usize bblock_cnt = scc_vec_size(func->bblocks); + usize *bblock_offsets = scc_calloc(bblock_cnt, sizeof(usize)); + // 建立 bblock_ref -> id 的映射 + scc_hashtable_t ref2id; + scc_hashtable_init(&ref2id, hash_func, equal_func); + for (usize i = 0; i < bblock_cnt; i++) { + scc_ir_bblock_ref_t ref = scc_vec_at(func->bblocks, i); + scc_hashtable_set(&ref2id, (void *)(usize)ref, (void *)i); + } + + patch_vec_t patches; + scc_vec_init(patches); + scc_mcode_amd64_push_r64(&ctx->mcode, SCC_AMD64_RBP); scc_mcode_amd64_mov_r64_r64(&ctx->mcode, SCC_AMD64_RBP, SCC_AMD64_RSP); scc_mcode_amd64_sub_rsp_imm32(&ctx->mcode, stack_size); @@ -201,8 +353,30 @@ static void parse_function(scc_ir2mcode_ctx_t *ctx, scc_ir_func_t *func) { LOG_FATAL("\n"); return; } - parse_bblock(ctx, bblock); + + bblock_offsets[i] = scc_vec_size(ctx->mcode.mcode); + parse_bblock(ctx, bblock, &patches); } + + // 回填所有跳转偏移 + u8 *buf = scc_vec_unsafe_get_data(ctx->mcode.mcode); + scc_vec_foreach(patches, idx) { + patch_t *p = &scc_vec_at(patches, idx); + usize target_id = + (usize)scc_hashtable_get(&ref2id, (void *)(usize)p->target_bb_ref); + usize target_off = bblock_offsets[target_id]; + usize next_off = p->pos; + i32 rel = (i32)(target_off - next_off); + // 写入到指令的偏移字段(小端) + *(u32 *)(&buf[p->pos - 4]) = rel; + // buf[p->pos + 1] = (u8)(rel >> 0); + // buf[p->pos + 2] = (u8)(rel >> 8); + // buf[p->pos + 3] = (u8)(rel >> 16); + // buf[p->pos + 4] = (u8)(rel >> 24); + } + scc_free(bblock_offsets); + scc_vec_free(patches); + scc_hashtable_drop(&ref2id); } void scc_ir2amd64(scc_ir2mcode_ctx_t *ctx) { diff --git a/libs/ir2mcode/src/reg_alloc.c b/libs/ir2mcode/src/reg_alloc.c index d0a8260..e846856 100644 --- a/libs/ir2mcode/src/reg_alloc.c +++ b/libs/ir2mcode/src/reg_alloc.c @@ -50,6 +50,20 @@ scc_hashtable_t *scc_reg_alloc_with_stack(scc_reg_alloc_t *ctx, (void *)scc_vec_size(ctx->reg_loc_vec)); break; } + case SCC_IR_NODE_CALL: { + scc_ir_type_t *ret_type = + scc_ir_ctx_get_type(ctx->ir_ctx, node->type); + if (ret_type && ret_type->tag != SCC_IR_TYPE_VOID) { + loc.kind = SCC_REG_KIND_STACK; + loc.idx = ctx->alloc_stack_size; + ctx->alloc_stack_size += 8; + scc_vec_push(ctx->reg_loc_vec, loc); + scc_hashtable_set(&ctx->node_ref2reg_loc, + (void *)(usize)node_ref, + (void *)scc_vec_size(ctx->reg_loc_vec)); + } + break; + } default: break; } diff --git a/libs/ir2mcode/tests/test_run.c b/libs/ir2mcode/tests/test_run.c index 65b803d..a8bb1eb 100644 --- a/libs/ir2mcode/tests/test_run.c +++ b/libs/ir2mcode/tests/test_run.c @@ -6,7 +6,7 @@ #include -void test_example(const char *input, cbool need_sema) { +void test_example(const char *input, cbool need_sema, const char *name) { int res = 0; scc_sstream_t mem_stream; res = scc_sstream_init_by_buffer(&mem_stream, input, scc_strlen(input), @@ -42,7 +42,10 @@ void test_example(const char *input, cbool need_sema) { const sccf_t *sccf = sccf_builder_to_sccf(&mcode_ctx.builder); scc_pe_builder_t pe_builder; sccf2pe(&pe_builder, sccf); - scc_pe_dump_to_file(&pe_builder, __FILE__ "/../../test.exe"); + + char fname[1024] = {0}; + scc_snprintf(fname, sizeof(fname), "%s%s%s", __FILE__, "/../../", name); + scc_pe_dump_to_file(&pe_builder, fname); } int main() { @@ -54,6 +57,31 @@ int main() { " a = a - b + 1;\n" " return a;\n" "}\n", - true); + true, "02_decl_expr.exe"); + test_example("int main(void) {\n" + " int a;\n" + " a = 1;\n" + " if (a) {\n" + " a = 1;\n" + " } else {\n" + " a = 2;\n" + " }\n" + " return a;\n" + "}\n", + true, "04_if.exe"); + test_example("int main() {\n" + " int i = 0;\n" + " while (i < 10) i = i + 1;\n" + " return i;\n" + "}\n", + true, "07_while.exe"); + test_example("int add(int a, int b) {\n" + " return a + b;\n" + "}\n" + "\n" + "int main(void) {\n" + " return add(1, 2);\n" + "}\n", + true, "10_call.exe"); return 0; } diff --git a/tests/simple/12_include.c b/tests/simple/12_include.c index 667cde3..14c6bb2 100644 --- a/tests/simple/12_include.c +++ b/tests/simple/12_include.c @@ -1,6 +1,6 @@ -#include +#include "scc_stdio.h" int main(void) { - printf("hello world"); + puts("hello world"); return 0; -} \ No newline at end of file +} diff --git a/tests/simple/expect.toml b/tests/simple/expect.toml new file mode 100644 index 0000000..c7e6083 --- /dev/null +++ b/tests/simple/expect.toml @@ -0,0 +1,13 @@ +[return_val_cases] +"./01_return.c" = 65536 +"./02_decl_expr.c" = 1 +"./03_decl_init.c" = 11 +"./04_if.c" = 1 +"./05_else.c" = 2 +"./06_fcall.c" = 3 +"./07_while.c" = 10 +"./08_do_while.c" = 128 +"./09_for.c" = 10 +"./10_main.c" = 3 +"./11_recursive.c" = 120 +[stdout_val_cases] diff --git a/tests/simple/scc_stdio.h b/tests/simple/scc_stdio.h new file mode 100644 index 0000000..e709413 --- /dev/null +++ b/tests/simple/scc_stdio.h @@ -0,0 +1,6 @@ +#ifndef __SCC_STDIO_H__ +#define __SCC_STDIO_H__ + +extern int puts(const char *str); + +#endif diff --git a/tests/simple/test.py b/tests/simple/test.py index 3dbd496..f5811de 100644 --- a/tests/simple/test.py +++ b/tests/simple/test.py @@ -1,84 +1,89 @@ +from pprint import PrettyPrinter import subprocess import os from pathlib import Path +import tomllib # 配置参数 -TEST_DIR = Path(".") -CC_PATH = Path("../../src/smcc.exe") -VM_PATH = Path("../rv32-vm.exe") -WORKSPACE = Path(".") # 测试工作目录 +WORKSPACE = Path(__file__).resolve().parent # 测试工作目录 +TEST_DIR = Path(WORKSPACE) +CC_PATH = Path(WORKSPACE / "../../build/dev/scc") -# 测试用例映射表(示例) -TEST_CASE_MAP = { - "./01_return.c": 65536, - "./02_decl_expr.c": 1, - "./03_decl_init.c": 11, - "./04_if.c": 1, - "./05_else.c": 2, - "./06_fcall.c": 3, - "./07_while.c": 10, - "./08_do_while.c": 128, - "./09_for.c": 10, - "./10_main.c": 3, - "./11_recursive.c": 120, -} - -def run_command(cmd, capture_stderr=True): - """执行命令并捕获stderr""" - result = subprocess.run( - cmd, - cwd=WORKSPACE, - stderr=subprocess.PIPE if capture_stderr else None, - text=True, - timeout=1, - ) - return result.stderr.strip() if capture_stderr else None +def run_command(cmd, capture_output=True): + """执行命令并捕获 stdout""" + try: + result = subprocess.run( + cmd, + cwd=WORKSPACE, + stdout=subprocess.PIPE if capture_output else None, + stderr=subprocess.PIPE, + text=True, + timeout=5, # 增加超时时间以防虚拟机启动慢 + ) + # 返回 stdout 用于获取返回值,同时检查是否有运行时错误 + return result.stdout.strip(), result.stderr.strip(), result.returncode + except subprocess.TimeoutExpired: + return None, "Timeout expired", -1 + except Exception as e: + return None, str(e), -1 def run_test(test_file, expected): print(f"\nTesting {test_file}...") - # 1. 编译生成flat.bin - compile_cmd = [str(CC_PATH), str(test_file)] - compile_err = run_command(compile_cmd) + # 1. 编译 + compile_cmd = [str(CC_PATH), str(test_file), "-o", "test.exe"] + # 编译时关注 stderr 和返回码 + _, compile_err, compile_ret = run_command(compile_cmd) - if not (WORKSPACE / "flat.bin").exists(): + exe_path = WORKSPACE / "test.exe" + if not exe_path.exists() or compile_ret != 0: print(f" Compilation failed: {compile_err}") return False - # 2. 执行虚拟机 - vm_cmd = [str(VM_PATH), "flat.bin"] + # 2. 执行虚拟机并获取输出 + vm_cmd = [str(exe_path)] + actual_output, vm_err, vm_ret = run_command(vm_cmd) - # 3. 解析返回值(假设最后一行是返回值) - try: - vm_err = run_command(vm_cmd) - actual = int(vm_err.split()[-1]) - except (ValueError, IndexError) as e: - print(f" Invalid VM output: {vm_err}") - return False - except subprocess.TimeoutExpired: - print(" Timeout expired") + # 如果存在 stderr 且返回码异常(例如负数表示信号终止),则视为运行时错误 + if vm_err and vm_ret < 0: + print(f" Runtime error: {vm_err}") return False + # 3. 获取返回值 (修改进程返回值而非 stdout) + actual = vm_ret + # 4. 验证结果 + # 注意:toml 中读取的 expected 可能是整数,actual 也是整数,直接比较 if actual == expected: print(f" PASSED {test_file}") return True else: - print(f" FAILED: Expected {expected}, got {actual}") + print(f" FAILED: Expected '{expected}', got '{actual}'") return False def main(): passed = 0 total = 0 + config = {} + config_path = WORKSPACE / "expect.toml" + + if not config_path.exists(): + print(f"Config file not found: {config_path}") + return - for test_file, expected in TEST_CASE_MAP.items(): + with open(config_path, "rb") as f: + config = tomllib.load(f) + PrettyPrinter().pprint(config) + + for test_file, expected in config.get("return_val_cases", {}).items(): total += 1 if run_test(TEST_DIR / test_file, expected): passed += 1 # 清理中间文件 - if (WORKSPACE / "flat.bin").exists(): - os.remove(WORKSPACE / "flat.bin") + exe_path = WORKSPACE / "test.exe" + if exe_path.exists(): + os.remove(exe_path) print(f"\nTest Summary: {passed}/{total} passed")