12.8 切片 4:CMP_* 与 IF/ELSIF/ELSE 跳转。
- CMP_xx:两侧求值到临时后比较;IF 编成 <cond→t> JF t, L_next <body> JMP L_end 链 - 分支尾部 JMP 与条件 JF 双回填;无 else 时末分支 JF 直接落 L_end - codegen_test:89 断言(用例 04 完整跳转序列),ctest 9/9
This commit is contained in:
+97
-13
@@ -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 **短路编跳转**(禁止两边都算):
|
||||
* <lhs → rd> JF(AND)/JT(OR) rd, L_end <rhs → t> MOVE rd, t L_end:
|
||||
* - 切片 1~3:帧/字面量/MOVE/RET;全局数据区;NOT 与短路 AND/OR。
|
||||
* - 切片 4:比较 CMP_xx(两侧求值到临时);IF 编成
|
||||
* <cond→t> JF t, L_next <body> 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: <msg>" 返回 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 每分支:<cond→t> JF t, L_next → <body> → 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<std::pair<const Expr*, const std::vector<Stmt>*>> 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<size_t> 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<uint8_t>((static_cast<uint16_t>(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 变量名(小写)
|
||||
|
||||
Reference in New Issue
Block a user