12.8 切片 5:WHILE 回环与四则运算。
- WHILE 编成 L_loop: <cond→t> JF t, L_end <body> 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
This commit is contained in:
@@ -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 编成
|
||||
* <cond→t> JF t, L_next <body> 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: <cond→t> JF t, L_end <body> 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: <cond→t> JF t, L_end <body> 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 每分支:<cond→t> JF t, L_next → <body> → 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<uint8_t>((static_cast<uint16_t>(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 比较运算
|
||||
|
||||
@@ -397,6 +397,100 @@ static bool test_case04() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 10. 用例 05:WHILE 回环(JF 出口 + JMP 回填)----
|
||||
|
||||
static bool test_case05() {
|
||||
std::vector<uint8_t> 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<const uint32_t*>(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<uint8_t> 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<const uint32_t*>(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<uint8_t> 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<const uint32_t*>(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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user