From e7dee50cde56a7bb28093db5ae7f629c7cd3c674 Mon Sep 17 00:00:00 2001 From: chentianya Date: Fri, 21 Aug 2026 11:40:53 +0800 Subject: [PATCH] =?UTF-8?q?12.8=20=E5=88=87=E7=89=87=203=EF=BC=9ANOT=20?= =?UTF-8?q?=E4=B8=8E=E7=9F=AD=E8=B7=AF=20AND/OR=EF=BC=88=E7=BC=96=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=EF=BC=89=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 短路模式: JF(AND)/JT(OR) rd, L_end MOVE rd, t L_end: - patch_jump 回填偏移;约定相对下一条指令(目标 = 当前 + 1 + off),冻结进 isa 规格 - codegen_test:71 断言(用例 03 跳转序列 JF/JT +2、NOT 序列),ctest 9/9 --- Doc/isa/指令与映像.md | 2 +- compiler/src/Codegen.cpp | 71 +++++++++++++++++++++++++++++--------- tests/src/codegen_test.cpp | 70 +++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 17 deletions(-) diff --git a/Doc/isa/指令与映像.md b/Doc/isa/指令与映像.md index 7d2c0fb..2256c8a 100644 --- a/Doc/isa/指令与映像.md +++ b/Doc/isa/指令与映像.md @@ -82,7 +82,7 @@ C++20 定宽: - 三寄存器:`a`/`b` 为 `ra`/`rb`(`MOVE`/`NOT` 只用 `rd`+`a`) - `LOADK`:`a|b` 为 `const_id`(u16) -- `JMP` / `JT` / `JF`:`a|b` 为有符号相对 offset(指令条数) +- `JMP` / `JT` / `JF`:`a|b` 为有符号相对 offset(指令条数),**相对下一条指令**:目标下标 = 当前下标 + 1 + off - `CALL`:`a|b` 为 `fn_id`(u16) 操作码一次列全(`CMP_xx` 冻下面六种): diff --git a/compiler/src/Codegen.cpp b/compiler/src/Codegen.cpp index da4f0c1..c37302e 100644 --- a/compiler/src/Codegen.cpp +++ b/compiler/src/Codegen.cpp @@ -1,33 +1,33 @@ /** * @file Codegen.cpp - * @brief 寄存器码生成(12.8,切片 2:+ 全局 / I/Q/M) + * @brief 寄存器码生成(12.8,切片 3:+ NOT、短路 AND/OR) * @author * @date 2026-08-21 * * @details 设计说明(详见 Doc/compiler/寄存器码.md): - * - 切片 1:PROGRAM 标量变量帧(r0 起按声明序)、字面量 LOADK、 - * 变量间 MOVE、RET;空 MAIN 出映像。 - * - 切片 2:+ 全局数据区(声明序、对齐 BOOL 1B / INT 2B / TIME 8B、 - * 初值入数据段);读全局 LOAD_GLOBAL / io.input 绑定用 LOAD_I; - * 写全局 STORE_GLOBAL / io.output 绑定用 STORE_Q;写 io.input → 拒绝。 - * 其余构造报 codegen error(后续切片展开) - * - 常量统一进常量表(BOOL/INT/TIME),const_id = 首次出现序 - * - 指令 slot = 数据区字节偏移;符号表 address 保持逻辑槽号(12.6 冻结) + * - 切片 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 + off,见 Doc/isa/指令与映像.md) + * - 其余构造报 codegen error(后续切片展开) * * 函数清单: * - put_le32 / put_le64 小端写入映像缓冲 * - align_up 按 2 的幂宽度向上对齐 * - width_of 类型名 → 槽宽(bool 1 / int 2 / time 8) * - Builder::Builder (构造)存工程/源文件/链接结果/输出,收集 io 绑定分类 - * - Builder::run 逐 POU 建函数 → 布局数据区 → 拼映像 + * - Builder::run 布局数据区 → 逐 POU 建函数 → 拼映像 * - Builder::fail 组装 "codegen error: " 返回 false * - find_pou 按名查 POU AST * - build_function 编译一个 POU 的语句体(帧寄存器分配) - * - compile_stmt 语句编译(切片 2:仅赋值,左值可帧寄存器或全局) - * - compile_expr 表达式编译到寄存器(字面量 / 变量读,含全局 LOAD_*) - * - store_target 赋值左值:帧寄存器直写或 STORE_* 到全局槽 + * - begin_stmt / alloc_temp 临时寄存器:语句内递增、语句结束复用基址 + * - compile_stmt 语句编译(切片 3:仅赋值,左值可帧寄存器或全局) + * - store_target 赋值左值:STORE_* 到全局槽 + * - compile_expr 表达式编译到寄存器(字面量/变量读/NOT/短路 AND/OR) * - load_op / store_op 按 io 绑定选读取/写入操作码 * - global_offset 逻辑槽号 → 数据区字节偏移 + * - patch_jump 回填跳转偏移(相对下一条指令) * - layout_data 布局全局数据区(对齐 + 初值字节) * - init_of 查全局声明的初值(AST) * - const_id 取常量表 id(无则追加) @@ -282,8 +282,8 @@ namespace { /** * @brief 表达式编译到目标寄存器 - * @details 字面量 → LOADK;帧变量 → MOVE; - * 全局/外部 → LOAD_GLOBAL(io.input 绑定用 LOAD_I) + * @details 字面量 → LOADK;帧变量 → MOVE;全局/外部 → LOAD_*(io.input 用 LOAD_I); + * NOT → 求值后 NOT rd, t;AND/OR → 短路跳转(JF/JT 跳过右侧) * @param f 当前函数 * @param e 表达式 AST * @param rd 目标寄存器 @@ -314,7 +314,46 @@ namespace { } return fail("no register or slot for '" + e.name + "'"); } - return fail("expression not supported in slice 2"); + if (e.kind == ExprKind::Not) { + const uint8_t t = alloc_temp(f); + if (!compile_expr(f, *e.operand, t)) { + return false; + } + f.code.push_back(isa::enc_rr(isa::Op::NOT, rd, t)); + return true; + } + if (e.kind == ExprKind::And || e.kind == ExprKind::Or) { + // 短路:左侧结果在 rd;AND 为假 / OR 为真时跳过右侧 + if (!compile_expr(f, *e.lhs, rd)) { + return false; + } + const isa::Op jop = (e.kind == ExprKind::And) ? isa::Op::JF : isa::Op::JT; + const size_t jmp_idx = f.code.size(); + f.code.push_back(isa::enc_jc(jop, rd, 0)); // 偏移留洞,稍后回填 + const uint8_t t = alloc_temp(f); + if (!compile_expr(f, *e.rhs, t)) { + return false; + } + f.code.push_back(isa::enc_rr(isa::Op::MOVE, rd, t)); + patch_jump(f, jmp_idx, f.code.size()); + return true; + } + return fail("expression not supported in slice 3"); + } + + /** + * @brief 回填跳转偏移(相对下一条指令) + * @param f 当前函数 + * @param idx 跳转指令下标(占位) + * @param target_idx 目标指令下标 + */ + void patch_jump(FuncCtx& f, size_t idx, size_t target_idx) { + const int16_t off = static_cast( + static_cast(target_idx) - (static_cast(idx) + 1)); + const isa::Instr w = f.code[idx]; + f.code[idx] = isa::pack(isa::op(w), isa::rd(w), + static_cast(static_cast(off) & 0xFFu), + static_cast((static_cast(off) >> 8) & 0xFFu)); } /** diff --git a/tests/src/codegen_test.cpp b/tests/src/codegen_test.cpp index 820db67..464a232 100644 --- a/tests/src/codegen_test.cpp +++ b/tests/src/codegen_test.cpp @@ -279,6 +279,74 @@ static bool test_write_input() { return true; } +// ---- 7. 用例 03:短路 AND / OR 必须编跳转 ---- + +static bool test_case03() { + std::vector img; + std::string err; + CHECK(compile_case("03_short_circuit", &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); // a b x + 1 临时 + CHECK(r.code_len == 9); + + char buf[64]; + const uint8_t* base = v.code_bytes(); + const uint32_t* ins = reinterpret_cast(base); + // x := a AND b:MOVE r2,r0 / JF r2,+2 / MOVE r3,r1 / MOVE r2,r3 + isa::disasm(ins[0], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r2, r0") == 0); + isa::disasm(ins[1], buf, sizeof buf); + CHECK(std::strcmp(buf, "JF r2, +2") == 0); + isa::disasm(ins[2], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r3, r1") == 0); + isa::disasm(ins[3], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r2, r3") == 0); + // x := a OR b:MOVE r2,r0 / JT r2,+2 / MOVE r3,r1 / MOVE r2,r3 + isa::disasm(ins[4], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r2, r0") == 0); + isa::disasm(ins[5], buf, sizeof buf); + CHECK(std::strcmp(buf, "JT r2, +2") == 0); + isa::disasm(ins[6], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r3, r1") == 0); + isa::disasm(ins[7], buf, sizeof buf); + CHECK(std::strcmp(buf, "MOVE r2, r3") == 0); + isa::disasm(ins[8], buf, sizeof buf); + CHECK(std::strcmp(buf, "RET") == 0); + return true; +} + +// ---- 8. NOT ---- + +static bool test_not() { + std::vector img; + std::string err; + const char* main_st = + "PROGRAM MAIN\nVAR\n a, x : BOOL;\nEND_VAR\n" + " x := NOT a;\n" + "END_PROGRAM\n"; + CHECK(compile_src("not", "", "", main_st, &img, &err)); + + const isa::ImageView v = isa::ImageView::from(img); + CHECK(v.ok()); + const isa::FuncRow r = v.func_row(0); + CHECK(r.nregs == 3); // a x + 1 临时 + CHECK(r.code_len == 3); + + 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, "MOVE r2, r0") == 0); // 临时 ← a + isa::disasm(ins[1], buf, sizeof buf); + CHECK(std::strcmp(buf, "NOT r1, r2") == 0); // x := NOT 临时 + isa::disasm(ins[2], buf, sizeof buf); + CHECK(std::strcmp(buf, "RET") == 0); + return true; +} + int main() { if (!test_case01()) return 1; if (!test_case02()) return 1; @@ -286,6 +354,8 @@ int main() { if (!test_io_ops()) return 1; if (!test_layout()) return 1; if (!test_write_input()) return 1; + if (!test_case03()) return 1; + if (!test_not()) return 1; std::printf("codegen_test: %d checks passed\n", g_checks); return 0; }