12.8 切片 3:NOT 与短路 AND/OR(编跳转)。

- 短路模式:<lhs→rd> JF(AND)/JT(OR) rd, L_end <rhs→t> MOVE rd, t L_end:
- patch_jump 回填偏移;约定相对下一条指令(目标 = 当前 + 1 + off),冻结进 isa 规格
- codegen_test:71 断言(用例 03 跳转序列 JF/JT +2、NOT 序列),ctest 9/9
This commit is contained in:
2026-08-21 11:40:53 +08:00
parent ac411e6a4a
commit e7dee50cde
3 changed files with 126 additions and 17 deletions
+1 -1
View File
@@ -82,7 +82,7 @@ C++20 定宽:
- 三寄存器:`a`/`b``ra`/`rb``MOVE`/`NOT` 只用 `rd`+`a` - 三寄存器:`a`/`b``ra`/`rb``MOVE`/`NOT` 只用 `rd`+`a`
- `LOADK``a|b``const_id`u16 - `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 - `CALL``a|b``fn_id`u16
操作码一次列全(`CMP_xx` 冻下面六种): 操作码一次列全(`CMP_xx` 冻下面六种):
+55 -16
View File
@@ -1,33 +1,33 @@
/** /**
* @file Codegen.cpp * @file Codegen.cpp
* @brief 寄存器码生成(12.8,切片 2+ 全局 / I/Q/M * @brief 寄存器码生成(12.8,切片 3+ NOT、短路 AND/OR
* @author * @author
* @date 2026-08-21 * @date 2026-08-21
* *
* @details 设计说明(详见 Doc/compiler/寄存器码.md): * @details 设计说明(详见 Doc/compiler/寄存器码.md):
* - 切片 1PROGRAM 标量变量帧r0 起按声明序)、字面量 LOADK、 * - 切片 1:PROGRAM 标量变量帧、字面量 LOADK、变量 MOVE、RET。
* 变量间 MOVE、RET;空 MAIN 出映像 * - 切片 2:全局数据区(对齐 + 初值)、LOAD_I/STORE_Q/LOAD_GLOBAL/STORE_GLOBAL
* - 切片 2:+ 全局数据区(声明序、对齐 BOOL 1B / INT 2B / TIME 8B、 * - 切片 3NOTenc_rr);AND/OR **短路编跳转**(禁止两边都算):
* 初值入数据段);读全局 LOAD_GLOBAL / io.input 绑定用 LOAD_I * <lhs → rd> JF(AND)/JT(OR) rd, L_end <rhs → t> MOVE rd, t L_end:
* 写全局 STORE_GLOBAL / io.output 绑定用 STORE_Q;写 io.input → 拒绝。 * 跳转偏移相对下一条指令(目标 = 当前 + 1 + off,见 Doc/isa/指令与映像.md
* 其余构造报 codegen error(后续切片展开) * - 其余构造报 codegen error(后续切片展开)
* - 常量统一进常量表(BOOL/INT/TIME),const_id = 首次出现序
* - 指令 slot = 数据区字节偏移;符号表 address 保持逻辑槽号(12.6 冻结)
* *
* 函数清单: * 函数清单:
* - put_le32 / put_le64 小端写入映像缓冲 * - put_le32 / put_le64 小端写入映像缓冲
* - align_up 按 2 的幂宽度向上对齐 * - align_up 按 2 的幂宽度向上对齐
* - width_of 类型名 → 槽宽(bool 1 / int 2 / time 8 * - width_of 类型名 → 槽宽(bool 1 / int 2 / time 8
* - Builder::Builder (构造)存工程/源文件/链接结果/输出,收集 io 绑定分类 * - Builder::Builder (构造)存工程/源文件/链接结果/输出,收集 io 绑定分类
* - Builder::run 逐 POU 建函数 → 布局数据区 → 拼映像 * - Builder::run 布局数据区 → 逐 POU 建函数 → 拼映像
* - Builder::fail 组装 "codegen error: <msg>" 返回 false * - Builder::fail 组装 "codegen error: <msg>" 返回 false
* - find_pou 按名查 POU AST * - find_pou 按名查 POU AST
* - build_function 编译一个 POU 的语句体(帧寄存器分配) * - build_function 编译一个 POU 的语句体(帧寄存器分配)
* - compile_stmt 语句编译(切片 2:仅赋值,左值可帧寄存器或全局) * - begin_stmt / alloc_temp 临时寄存器:语句内递增、语句结束复用基址
* - compile_expr 表达式编译到寄存器(字面量 / 变量读,含全局 LOAD_* * - compile_stmt 语句编译(切片 3:仅赋值,左值可帧寄存器或全局
* - store_target 赋值左值:帧寄存器直写或 STORE_* 到全局槽 * - store_target 赋值左值:STORE_* 到全局槽
* - compile_expr 表达式编译到寄存器(字面量/变量读/NOT/短路 AND/OR
* - load_op / store_op 按 io 绑定选读取/写入操作码 * - load_op / store_op 按 io 绑定选读取/写入操作码
* - global_offset 逻辑槽号 → 数据区字节偏移 * - global_offset 逻辑槽号 → 数据区字节偏移
* - patch_jump 回填跳转偏移(相对下一条指令)
* - layout_data 布局全局数据区(对齐 + 初值字节) * - layout_data 布局全局数据区(对齐 + 初值字节)
* - init_of 查全局声明的初值(AST) * - init_of 查全局声明的初值(AST)
* - const_id 取常量表 id(无则追加) * - const_id 取常量表 id(无则追加)
@@ -282,8 +282,8 @@ namespace {
/** /**
* @brief 表达式编译到目标寄存器 * @brief 表达式编译到目标寄存器
* @details 字面量 → LOADK;帧变量 → MOVE * @details 字面量 → LOADK;帧变量 → MOVE全局/外部 → LOAD_*io.input 用 LOAD_I);
* 全局/外部 → LOAD_GLOBALio.input 绑定用 LOAD_I * NOT → 求值后 NOT rd, tAND/OR → 短路跳转(JF/JT 跳过右侧
* @param f 当前函数 * @param f 当前函数
* @param e 表达式 AST * @param e 表达式 AST
* @param rd 目标寄存器 * @param rd 目标寄存器
@@ -314,7 +314,46 @@ namespace {
} }
return fail("no register or slot for '" + e.name + "'"); 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<int16_t>(
static_cast<int64_t>(target_idx) - (static_cast<int64_t>(idx) + 1));
const isa::Instr w = f.code[idx];
f.code[idx] = isa::pack(isa::op(w), isa::rd(w),
static_cast<uint8_t>(static_cast<uint16_t>(off) & 0xFFu),
static_cast<uint8_t>((static_cast<uint16_t>(off) >> 8) & 0xFFu));
} }
/** /**
+70
View File
@@ -279,6 +279,74 @@ static bool test_write_input() {
return true; return true;
} }
// ---- 7. 用例 03:短路 AND / OR 必须编跳转 ----
static bool test_case03() {
std::vector<uint8_t> 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<const uint32_t*>(base);
// x := a AND bMOVE 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 bMOVE 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<uint8_t> 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<const uint32_t*>(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() { int main() {
if (!test_case01()) return 1; if (!test_case01()) return 1;
if (!test_case02()) return 1; if (!test_case02()) return 1;
@@ -286,6 +354,8 @@ int main() {
if (!test_io_ops()) return 1; if (!test_io_ops()) return 1;
if (!test_layout()) return 1; if (!test_layout()) return 1;
if (!test_write_input()) 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); std::printf("codegen_test: %d checks passed\n", g_checks);
return 0; return 0;
} }