From 31ba557627e8314151ffd1c933c6b5b320b10d91 Mon Sep 17 00:00:00 2001 From: chentianya Date: Fri, 21 Aug 2026 11:45:32 +0800 Subject: [PATCH] =?UTF-8?q?12.8=20=E5=88=87=E7=89=87=205=EF=BC=9AWHILE=20?= =?UTF-8?q?=E5=9B=9E=E7=8E=AF=E4=B8=8E=E5=9B=9B=E5=88=99=E8=BF=90=E7=AE=97?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WHILE 编成 L_loop: JF t, L_end JMP L_loop L_end:,JF/JMP 双回填 - 四则 ADD/SUB/MUL/DIV(用例 5 循环体自增需要,同模式实现,用例 7 顺带点亮) - codegen_test:115 断言(用例 05 回环 JMP -8、07 四则+比较、08 TIME 常量表),ctest 9/9 --- compiler/src/Codegen.cpp | 80 +++++++++++++++++++++++++++---- tests/src/codegen_test.cpp | 97 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 9 deletions(-) diff --git a/compiler/src/Codegen.cpp b/compiler/src/Codegen.cpp index 7395d79..796e036 100644 --- a/compiler/src/Codegen.cpp +++ b/compiler/src/Codegen.cpp @@ -1,14 +1,15 @@ /** * @file Codegen.cpp - * @brief 寄存器码生成(12.8,切片 4:+ CMP_*、IF/ELSIF/ELSE) + * @brief 寄存器码生成(12.8,切片 5:+ WHILE、四则运算) * @author * @date 2026-08-21 * * @details 设计说明(详见 Doc/compiler/寄存器码.md): - * - 切片 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) + * - 切片 1~4:帧/字面量/MOVE/RET;全局数据区;NOT 与短路 AND/OR;CMP 与 IF。 + * - 切片 5:WHILE 编成 + * L_loop: JF t, L_end JMP L_loop L_end: + * (用例 5 循环体含自增,四则 ADD/SUB/MUL/DIV 同模式一并实现,用例 7 可测) + * - 跳转偏移相对下一条指令(目标 = 当前 + 1 + off,见 Doc/isa/指令与映像.md) * - 其余构造报 codegen error(后续切片展开) * * 函数清单: @@ -20,11 +21,12 @@ * - find_pou 按名查 POU AST * - build_function 编译一个 POU 的语句体(帧寄存器分配) * - begin_stmt / alloc_temp 临时寄存器:语句内递增、语句结束复用基址 - * - compile_stmt 语句编译(切片 4:赋值 / IF) + * - compile_stmt 语句编译(切片 5:赋值 / IF / WHILE) * - compile_if IF/ELSIF/ELSE:条件链 + 分支尾部 JMP,JF/JMP 回填 + * - compile_while WHILE:L_loop + 条件 JF + 体 + JMP 回环 * - store_target 赋值左值:STORE_* 到全局槽 - * - compile_expr 表达式编译到寄存器(含 CMP_xx / NOT / 短路 AND/OR) - * - cmp_op 比较符 → CMP_xx 操作码 + * - compile_expr 表达式编译到寄存器(含 CMP/算术/NOT/短路 AND/OR) + * - cmp_op / arith_op 比较符/算术符 → 操作码 * - load_op / store_op 按 io 绑定选读取/写入操作码 * - global_offset 逻辑槽号 → 数据区字节偏移 * - patch_jump 回填跳转偏移(相对下一条指令) @@ -246,8 +248,11 @@ namespace { if (st.kind == StmtKind::If) { return compile_if(f, st); } + if (st.kind == StmtKind::While) { + return compile_while(f, st); + } if (st.kind != StmtKind::Assign) { - return fail("statement not supported in slice 4"); + return fail("statement not supported in slice 5"); } const auto it = f.regs.find(st.target); if (it == f.regs.end()) { @@ -258,6 +263,35 @@ namespace { return compile_expr(f, *st.value, rd); } + /** + * @brief 编译 WHILE + * @details L_loop: JF t, L_end JMP L_loop L_end: + * 条件 JF 与回环 JMP 双回填 + * @param f 当前函数 + * @param st WHILE 语句 AST + * @return true 成功;false(err 已写) + */ + bool compile_while(FuncCtx& f, const Stmt& st) { + const size_t loop = f.code.size(); + const uint8_t t = alloc_temp(f); + if (!compile_expr(f, *st.cond, t)) { + return false; + } + const size_t jf_idx = f.code.size(); + f.code.push_back(isa::enc_jc(isa::Op::JF, t, 0)); // 假 → L_end + for (const Stmt& s : st.body) { + begin_stmt(f); + if (!compile_stmt(f, s)) { + return false; + } + } + const size_t jmp_idx = f.code.size(); + f.code.push_back(isa::enc_jmp(0)); // 回环 + patch_jump(f, jmp_idx, loop); + patch_jump(f, jf_idx, f.code.size()); + return true; + } + /** * @brief 编译 IF / ELSIF / ELSE * @details 每分支: JF t, L_next → → JMP L_end; @@ -369,6 +403,19 @@ namespace { } return fail("no register or slot for '" + e.name + "'"); } + if (e.kind == ExprKind::Add || e.kind == ExprKind::Sub || + e.kind == ExprKind::Mul || e.kind == ExprKind::Div) { + 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(arith_op(e.kind), rd, l, r)); + return true; + } if (e.kind == ExprKind::Cmp) { const uint8_t l = alloc_temp(f); if (!compile_expr(f, *e.lhs, l)) { @@ -423,6 +470,21 @@ namespace { static_cast((static_cast(off) >> 8) & 0xFFu)); } + /** + * @brief 算术运算 → 操作码 + * @param k 表达式种类(Add/Sub/Mul/Div) + * @return 对应操作码 + */ + isa::Op arith_op(ExprKind k) const { + switch (k) { + case ExprKind::Add: return isa::Op::ADD; + case ExprKind::Sub: return isa::Op::SUB; + case ExprKind::Mul: return isa::Op::MUL; + case ExprKind::Div: return isa::Op::DIV; + default: return isa::Op::ADD; + } + } + /** * @brief 比较符 → CMP_xx 操作码 * @param op 比较运算 diff --git a/tests/src/codegen_test.cpp b/tests/src/codegen_test.cpp index c1022bd..8dac32a 100644 --- a/tests/src/codegen_test.cpp +++ b/tests/src/codegen_test.cpp @@ -397,6 +397,100 @@ static bool test_case04() { return true; } +// ---- 10. 用例 05:WHILE 回环(JF 出口 + JMP 回填)---- + +static bool test_case05() { + std::vector img; + std::string err; + CHECK(compile_case("05_while_normal", &img, &err)); + + const isa::ImageView v = isa::ImageView::from(img); + CHECK(v.ok()); + const isa::FuncRow r = v.func_row(0); + CHECK(r.nregs == 4); // n + 3 临时(CMP 目的 + 两操作数) + CHECK(r.code_len == 10); + + char buf[64]; + const uint8_t* base = v.code_bytes(); + const uint32_t* ins = reinterpret_cast(base); + isa::disasm(ins[0], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r0, 0") == 0); // n := 0 + // L_loop [1]:n < 10 → r1 + isa::disasm(ins[1], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r2, r0") == 0); + isa::disasm(ins[2], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r3, 1") == 0); // 常量 1 = 10 + isa::disasm(ins[3], buf, sizeof buf); + CHECK(std::strcmp(buf, "CMP_LT r1, r2, r3") == 0); + isa::disasm(ins[4], buf, sizeof buf); + CHECK(std::strcmp(buf, "JF r1, +4") == 0); // 假 → RET([9]) + // n := n + 1(体语句临时复用 r1/r2) + isa::disasm(ins[5], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r1, r0") == 0); + isa::disasm(ins[6], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r2, 2") == 0); // 常量 2 = 1 + isa::disasm(ins[7], buf, sizeof buf); + CHECK(std::strcmp(buf, "ADD r0, r1, r2") == 0); + isa::disasm(ins[8], buf, sizeof buf); + CHECK(std::strcmp(buf, "JMP -8") == 0); // 回 L_loop([1]) + isa::disasm(ins[9], buf, sizeof buf); + CHECK(std::strcmp(buf, "RET") == 0); + return true; +} + +// ---- 11. 用例 07:四则 + 比较 ---- + +static bool test_case07() { + std::vector img; + std::string err; + CHECK(compile_case("07_int_arith", &img, &err)); + + const isa::ImageView v = isa::ImageView::from(img); + CHECK(v.ok()); + const isa::FuncRow r = v.func_row(0); + CHECK(r.nregs == 6); // a b c eq + 2 临时(语句间复用) + + char buf[64]; + const uint8_t* base = v.code_bytes(); + const uint32_t* ins = reinterpret_cast(base); + bool saw_mul = false, saw_div = false, saw_sub = false, saw_gt = false; + for (uint32_t i = 0; i < r.code_len; ++i) { + isa::disasm(ins[i], buf, sizeof buf); + if (std::strstr(buf, "MUL r0, ")) saw_mul = true; + if (std::strstr(buf, "DIV r1, ")) saw_div = true; + if (std::strstr(buf, "SUB r2, ")) saw_sub = true; + if (std::strstr(buf, "CMP_GT r3, ")) saw_gt = true; + } + CHECK(saw_mul && saw_div && saw_sub && saw_gt); + return true; +} + +// ---- 12. 用例 08:TIME 字面量进常量表 ---- + +static bool test_case08() { + std::vector img; + std::string err; + CHECK(compile_case("08_time_literal", &img, &err)); + + const isa::ImageView v = isa::ImageView::from(img); + CHECK(v.ok()); + CHECK(v.header().n_consts == 2); + const isa::ConstEntry c0 = v.const_entry(0); + CHECK(c0.tag == isa::types::Time && c0.value == 10); // T#10ms + const isa::ConstEntry c1 = v.const_entry(1); + CHECK(c1.tag == isa::types::Time && c1.value == 1250); // T#1s250ms + + const isa::FuncRow r = v.func_row(0); + CHECK(r.nregs == 2); + char buf[64]; + const uint32_t* ins = reinterpret_cast(v.code_bytes()); + isa::disasm(ins[0], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r0, 0") == 0); + isa::disasm(ins[1], buf, sizeof buf); + CHECK(std::strcmp(buf, "LOADK r1, 1") == 0); + return true; +} + int main() { if (!test_case01()) return 1; if (!test_case02()) return 1; @@ -407,6 +501,9 @@ int main() { if (!test_case03()) return 1; if (!test_not()) return 1; if (!test_case04()) return 1; + if (!test_case05()) return 1; + if (!test_case07()) return 1; + if (!test_case08()) return 1; std::printf("codegen_test: %d checks passed\n", g_checks); return 0; }