diff --git a/compiler/src/Codegen.cpp b/compiler/src/Codegen.cpp index c37302e..7395d79 100644 --- a/compiler/src/Codegen.cpp +++ b/compiler/src/Codegen.cpp @@ -1,35 +1,34 @@ /** * @file Codegen.cpp - * @brief 寄存器码生成(12.8,切片 3:+ NOT、短路 AND/OR) + * @brief 寄存器码生成(12.8,切片 4:+ CMP_*、IF/ELSIF/ELSE) * @author * @date 2026-08-21 * * @details 设计说明(详见 Doc/compiler/寄存器码.md): - * - 切片 1:PROGRAM 标量变量帧、字面量 LOADK、变量 MOVE、RET。 - * - 切片 2:全局数据区(对齐 + 初值)、LOAD_I/STORE_Q/LOAD_GLOBAL/STORE_GLOBAL。 - * - 切片 3:NOT(enc_rr);AND/OR **短路编跳转**(禁止两边都算): - * JF(AND)/JT(OR) rd, L_end MOVE rd, t L_end: + * - 切片 1~3:帧/字面量/MOVE/RET;全局数据区;NOT 与短路 AND/OR。 + * - 切片 4:比较 CMP_xx(两侧求值到临时);IF 编成 + * JF t, L_next JMP L_end L_next: ... L_end: * 跳转偏移相对下一条指令(目标 = 当前 + 1 + off,见 Doc/isa/指令与映像.md) * - 其余构造报 codegen error(后续切片展开) * * 函数清单: * - put_le32 / put_le64 小端写入映像缓冲 - * - align_up 按 2 的幂宽度向上对齐 - * - width_of 类型名 → 槽宽(bool 1 / int 2 / time 8) + * - align_up / width_of 对齐与槽宽 * - Builder::Builder (构造)存工程/源文件/链接结果/输出,收集 io 绑定分类 * - Builder::run 布局数据区 → 逐 POU 建函数 → 拼映像 * - Builder::fail 组装 "codegen error: " 返回 false * - find_pou 按名查 POU AST * - build_function 编译一个 POU 的语句体(帧寄存器分配) * - begin_stmt / alloc_temp 临时寄存器:语句内递增、语句结束复用基址 - * - compile_stmt 语句编译(切片 3:仅赋值,左值可帧寄存器或全局) + * - compile_stmt 语句编译(切片 4:赋值 / IF) + * - compile_if IF/ELSIF/ELSE:条件链 + 分支尾部 JMP,JF/JMP 回填 * - store_target 赋值左值:STORE_* 到全局槽 - * - compile_expr 表达式编译到寄存器(字面量/变量读/NOT/短路 AND/OR) + * - compile_expr 表达式编译到寄存器(含 CMP_xx / NOT / 短路 AND/OR) + * - cmp_op 比较符 → CMP_xx 操作码 * - load_op / store_op 按 io 绑定选读取/写入操作码 * - global_offset 逻辑槽号 → 数据区字节偏移 * - patch_jump 回填跳转偏移(相对下一条指令) - * - layout_data 布局全局数据区(对齐 + 初值字节) - * - init_of 查全局声明的初值(AST) + * - layout_data / init_of 全局数据区布局与初值 * - const_id 取常量表 id(无则追加) * - assemble_image 拼头 + 常量表 + 函数表 + 字节码 + 数据段 * - codegen_project 对外入口 @@ -238,14 +237,17 @@ namespace { } /** - * @brief 编译一条语句(切片 2:仅赋值,左值可为帧寄存器或全局) + * @brief 编译一条语句(切片 4:赋值 / IF) * @param f 当前函数 * @param st 语句 AST * @return true 成功;false(err 已写) */ bool compile_stmt(FuncCtx& f, const Stmt& st) { + if (st.kind == StmtKind::If) { + return compile_if(f, st); + } if (st.kind != StmtKind::Assign) { - return fail("statement not supported in slice 2"); + return fail("statement not supported in slice 4"); } const auto it = f.regs.find(st.target); if (it == f.regs.end()) { @@ -256,6 +258,59 @@ namespace { return compile_expr(f, *st.value, rd); } + /** + * @brief 编译 IF / ELSIF / ELSE + * @details 每分支: JF t, L_next → → JMP L_end; + * 最后一个分支无 else 时 JF 直接落 L_end(不补 JMP) + * @param f 当前函数 + * @param st IF 语句 AST + * @return true 成功;false(err 已写) + */ + bool compile_if(FuncCtx& f, const Stmt& st) { + // 条件链:IF 体 + ELSIF 各体;ELSE 体可选 + std::vector*>> branches; + branches.push_back({st.cond.get(), &st.body}); + for (const IfBranch& b : st.elsifs) { + branches.push_back({b.cond.get(), &b.body}); + } + const bool has_else = !st.else_body.empty(); + std::vector end_jmps; // 各分支尾部 JMP(回填到 L_end) + + for (size_t i = 0; i < branches.size(); ++i) { + const uint8_t t = alloc_temp(f); + if (!compile_expr(f, *branches[i].first, t)) { + return false; + } + const size_t jf_idx = f.code.size(); + f.code.push_back(isa::enc_jc(isa::Op::JF, t, 0)); // 假 → 下一分支 + for (const Stmt& s : *branches[i].second) { + begin_stmt(f); + if (!compile_stmt(f, s)) { + return false; + } + } + if (i + 1 < branches.size() || has_else) { + const size_t jmp_idx = f.code.size(); + f.code.push_back(isa::enc_jmp(0)); // 分支尾部 → L_end + end_jmps.push_back(jmp_idx); + } + patch_jump(f, jf_idx, f.code.size()); // 回填 JF 到下一分支起点 + } + if (has_else) { + for (const Stmt& s : st.else_body) { + begin_stmt(f); + if (!compile_stmt(f, s)) { + return false; + } + } + } + const size_t end = f.code.size(); + for (const size_t j : end_jmps) { + patch_jump(f, j, end); + } + return true; + } + /** * @brief 赋值左值:帧寄存器直写或 STORE_* 到全局槽 * @param f 当前函数 @@ -314,6 +369,18 @@ namespace { } return fail("no register or slot for '" + e.name + "'"); } + if (e.kind == ExprKind::Cmp) { + const uint8_t l = alloc_temp(f); + if (!compile_expr(f, *e.lhs, l)) { + return false; + } + const uint8_t r = alloc_temp(f); + if (!compile_expr(f, *e.rhs, r)) { + return false; + } + f.code.push_back(isa::enc_rrr(cmp_op(e.op), rd, l, r)); + return true; + } if (e.kind == ExprKind::Not) { const uint8_t t = alloc_temp(f); if (!compile_expr(f, *e.operand, t)) { @@ -356,6 +423,23 @@ namespace { static_cast((static_cast(off) >> 8) & 0xFFu)); } + /** + * @brief 比较符 → CMP_xx 操作码 + * @param op 比较运算 + * @return 对应操作码(CMP_EQ / CMP_NE / CMP_LT / CMP_LE / CMP_GT / CMP_GE) + */ + isa::Op cmp_op(BinOp op) const { + switch (op) { + case BinOp::Eq: return isa::Op::CMP_EQ; + case BinOp::Ne: return isa::Op::CMP_NE; + case BinOp::Lt: return isa::Op::CMP_LT; + case BinOp::Le: return isa::Op::CMP_LE; + case BinOp::Gt: return isa::Op::CMP_GT; + case BinOp::Ge: return isa::Op::CMP_GE; + } + return isa::Op::CMP_EQ; + } + /** * @brief 按 io 绑定选读取操作码 * @param name 变量名(小写) diff --git a/tests/src/codegen_test.cpp b/tests/src/codegen_test.cpp index 464a232..c1022bd 100644 --- a/tests/src/codegen_test.cpp +++ b/tests/src/codegen_test.cpp @@ -347,6 +347,56 @@ static bool test_not() { return true; } +// ---- 9. 用例 04:IF / ELSIF / ELSE(CMP + JF + JMP 回填)---- + +static bool test_case04() { + std::vector img; + std::string err; + CHECK(compile_case("04_if_elsif_else", &img, &err)); + + const isa::ImageView v = isa::ImageView::from(img); + CHECK(v.ok()); + const isa::FuncRow r = v.func_row(0); + CHECK(r.nregs == 5); // sel out + 3 临时(CMP 目的 + 两操作数) + CHECK(r.code_len == 14); + + char buf[64]; + const uint8_t* base = v.code_bytes(); + const uint32_t* ins = reinterpret_cast(base); + // IF sel = 0 THEN:sel→r3、0→r4、CMP_EQ r2(常量序:0、10、1、20、30) + isa::disasm(ins[0], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r3, r0") == 0); + isa::disasm(ins[1], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r4, 0") == 0); + isa::disasm(ins[2], buf, sizeof buf); + CHECK(std::strcmp(buf, "CMP_EQ r2, r3, r4") == 0); + isa::disasm(ins[3], buf, sizeof buf); + CHECK(std::strcmp(buf, "JF r2, +2") == 0); // 假 → elsif([6]) + isa::disasm(ins[4], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r1, 1") == 0); // out := 10(常量 1) + isa::disasm(ins[5], buf, sizeof buf); + CHECK(std::strcmp(buf, "JMP +7") == 0); // → end([13]) + // ELSIF sel = 1 + isa::disasm(ins[6], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r3, r0") == 0); + isa::disasm(ins[7], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r4, 2") == 0); + isa::disasm(ins[8], buf, sizeof buf); + CHECK(std::strcmp(buf, "CMP_EQ r2, r3, r4") == 0); + isa::disasm(ins[9], buf, sizeof buf); + CHECK(std::strcmp(buf, "JF r2, +2") == 0); // 假 → else([12]) + isa::disasm(ins[10], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r1, 3") == 0); + isa::disasm(ins[11], buf, sizeof buf); + CHECK(std::strcmp(buf, "JMP +1") == 0); // → end([13]) + // ELSE + isa::disasm(ins[12], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r1, 4") == 0); + isa::disasm(ins[13], buf, sizeof buf); + CHECK(std::strcmp(buf, "RET") == 0); + return true; +} + int main() { if (!test_case01()) return 1; if (!test_case02()) return 1; @@ -356,6 +406,7 @@ int main() { if (!test_write_input()) return 1; if (!test_case03()) return 1; if (!test_not()) return 1; + if (!test_case04()) return 1; std::printf("codegen_test: %d checks passed\n", g_checks); return 0; }