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:
+1
-1
@@ -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` 冻下面六种):
|
||||
|
||||
+55
-16
@@ -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 **短路编跳转**(禁止两边都算):
|
||||
* <lhs → rd> JF(AND)/JT(OR) rd, L_end <rhs → t> 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: <msg>" 返回 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<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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -279,6 +279,74 @@ static bool test_write_input() {
|
||||
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 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<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() {
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user