12.8 切片 6:FUNCTION 与 CALL,冻结调用约定。

- 调用约定(冻结进 isa 规格与 12.8 文档):结果 r0、参数 r1..r7(最多 7 输入、函数内只读)、
  变量与临时全部从 r8 起;CALL/RET 时 VM 复制 r0..r7(12.9 实现)
- 调用点:实参求值到 r8+ 临时 → MOVE r(1+i) → CALL fn_id → MOVE rd, r0
- 函数名赋值 = 写 r0(结果);写函数输入 → codegen error
- codegen_test:134 断言(用例 13 两函数 CALL 序列;既有用例帧基址迁移 r8 起),ctest 9/9
This commit is contained in:
2026-08-21 11:48:33 +08:00
parent 31ba557627
commit 0a36e36b16
4 changed files with 213 additions and 72 deletions
+14
View File
@@ -30,6 +30,20 @@ compiler 模块的代码生成。输入:`Project` + `Unit`AST+ `LinkResu
- 字段访问 `fb.Q``LOAD_GLOBAL rd, <实例基址+字段偏移>`(编译期算死);I/Q/M 用 `LOAD_I` / `STORE_Q` / `LOAD_M` / `STORE_M` 同槽号 - 字段访问 `fb.Q``LOAD_GLOBAL rd, <实例基址+字段偏移>`(编译期算死);I/Q/M 用 `LOAD_I` / `STORE_Q` / `LOAD_M` / `STORE_M` 同槽号
- 函数帧寄存器:`FUNCTION` 结果固定 **r0**`VAR_INPUT` 从 r1 起按声明序,`VAR` 续后;PROGRAM / FB 变量从 r0 起按声明序 - 函数帧寄存器:`FUNCTION` 结果固定 **r0**`VAR_INPUT` 从 r1 起按声明序,`VAR` 续后;PROGRAM / FB 变量从 r0 起按声明序
### 调用约定(v1 冻结,VM 12.9 按此实现)
| 项 | 约定 |
|---|---|
| 结果寄存器 | **r0**FUNCTION 结果写入处) |
| 参数寄存器 | **r1..r7**(最多 7 个输入,按声明序) |
| 变量 / 临时 | **全部从 r8 起**(调用约定区不受覆盖) |
| `CALL` | VM 复制当前帧 **r0..r7 → 新帧 r0..r7** |
| `RET` | VM 复制当前帧 **r0..r7 → 调用方帧 r0..r7**(r0=结果,r1..r7 原样返回) |
| 函数输入 | 函数体内**只读**(写输入 → codegen error |
- 调用点:实参求值到 r8+ 临时 → `MOVE r(1+i), t``CALL fn_id``MOVE rd, r0`
- 所有 POU 的变量/临时统一 r8 起,故 r1..r7 在调用点可安全覆盖
## 寄存器分配 ## 寄存器分配
- 局部变量固定占用前段(0..k-1) - 局部变量固定占用前段(0..k-1)
+8
View File
@@ -47,6 +47,14 @@ CMake 目标:`isa``STATIC`),无依赖。公开头:`isa/include/isa/`
| 寄存器 | 下标 0..255;每函数实际个数写在函数头 `nregs` | | 寄存器 | 下标 0..255;每函数实际个数写在函数头 `nregs` |
| 跳转 | 相对 offset,单位是**指令条数**,不是字节 | | 跳转 | 相对 offset,单位是**指令条数**,不是字节 |
| `CALL` | 立即数 `fn_id`u16),无函数指针 | | `CALL` | 立即数 `fn_id`u16),无函数指针 |
### 调用约定(冻结,compiler 12.8 与 VM 12.9 共同遵守)
- 结果寄存器 **r0**;参数寄存器 **r1..r7**(最多 7 个输入,按声明序)
- 变量与表达式临时**全部从 r8 起**(调用约定区 r0..r7 不受用户数据占用)
- `CALL`:VM 复制当前帧 r0..r7 到新帧 r0..r7
- `RET`:VM 复制当前帧 r0..r7 回调用方帧 r0..r7r0 = 结果;r1..r7 原样返回)
- 函数输入在函数体内只读(compiler 拒绝写入)
| 映像魔数 | `STSC`,版本 `1` | | 映像魔数 | `STSC`,版本 `1` |
| 工程哈希 | 源文件路径排序后对内容做 FNV-1a 64(非密码学) | | 工程哈希 | 源文件路径排序后对内容做 FNV-1a 64(非密码学) |
| I/O 绑定 | **不进映像**;编译期把 `var` 收成槽号,channel/bit 由 STCompiler 写进 sidecar`.runtime.toml`),BytecodeExecutor 读 sidecar 采样 | | I/O 绑定 | **不进映像**;编译期把 `var` 收成槽号,channel/bit 由 STCompiler 写进 sidecar`.runtime.toml`),BytecodeExecutor 读 sidecar 采样 |
+86 -18
View File
@@ -1,15 +1,15 @@
/** /**
* @file Codegen.cpp * @file Codegen.cpp
* @brief 寄存器码生成(12.8,切片 5+ WHILE、四则运算 * @brief 寄存器码生成(12.8,切片 6+ FUNCTION 与 CALL
* @author * @author
* @date 2026-08-21 * @date 2026-08-21
* *
* @details 设计说明(详见 Doc/compiler/寄存器码.md): * @details 设计说明(详见 Doc/compiler/寄存器码.md):
* - 切片 1~4:帧/字面量/MOVE/RET;全局数据区;NOT 与短路 AND/ORCMP 与 IF。 * - 切片 1~5:帧/字面量/MOVE/RET;全局数据区;NOT 与短路 AND/ORCMP 与 IFWHILE 与四则
* - 切片 5WHILE 编成 * - 切片 6:调用约定(冻结)——结果 r0、参数 r1..r7(最多 7 输入、函数内只读)、
* L_loop: <cond→t> JF t, L_end <body> JMP L_loop L_end: * 变量与临时全部从 r8 起(调用约定区不受覆盖);
* (用例 5 循环体含自增,四则 ADD/SUB/MUL/DIV 同模式一并实现,用例 7 可测) * CALL 时 VM 复制 r0..r7 到新帧、RET 复制回调用方(r0=结果);
* - 跳转偏移相对下一条指令(目标 = 当前 + 1 + off,见 Doc/isa/指令与映像.md * 调用点:实参求值到 r8+ 临时 → MOVE r(1+i) → CALL fn_id → MOVE rd, r0
* - 其余构造报 codegen error(后续切片展开) * - 其余构造报 codegen error(后续切片展开)
* *
* 函数清单: * 函数清单:
@@ -19,13 +19,14 @@
* - 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 的语句体(帧寄存器分配 * - find_fn_id 按名查 fn_idPOU 收集序
* - build_function 编译一个 POUPROGRAM/FUNCTION;帧约定分配)
* - begin_stmt / alloc_temp 临时寄存器:语句内递增、语句结束复用基址 * - begin_stmt / alloc_temp 临时寄存器:语句内递增、语句结束复用基址
* - compile_stmt 语句编译(切片 5赋值 / IF / WHILE * - compile_stmt 语句编译(赋值 / IF / WHILE;函数输入只读
* - compile_if IF/ELSIF/ELSE:条件链 + 分支尾部 JMPJF/JMP 回填 * - compile_if IF/ELSIF/ELSE:条件链 + 分支尾部 JMPJF/JMP 回填
* - compile_while WHILEL_loop + 条件 JF + 体 + JMP 回环 * - compile_while WHILEL_loop + 条件 JF + 体 + JMP 回环
* - store_target 赋值左值:STORE_* 到全局槽 * - store_target 赋值左值:STORE_* 到全局槽
* - compile_expr 表达式编译到寄存器(含 CMP/算术/NOT/短路 AND/OR * - compile_expr 表达式编译到寄存器(含 CMP/算术/NOT/短路/CALL
* - cmp_op / arith_op 比较符/算术符 → 操作码 * - cmp_op / arith_op 比较符/算术符 → 操作码
* - load_op / store_op 按 io 绑定选读取/写入操作码 * - load_op / store_op 按 io 绑定选读取/写入操作码
* - global_offset 逻辑槽号 → 数据区字节偏移 * - global_offset 逻辑槽号 → 数据区字节偏移
@@ -176,9 +177,11 @@ namespace {
std::string name; std::string name;
std::vector<isa::Instr> code; // 字节码(函数表 code_offset 相对此段) std::vector<isa::Instr> code; // 字节码(函数表 code_offset 相对此段)
std::map<std::string, uint8_t> regs; // 变量名 → 帧寄存器 std::map<std::string, uint8_t> regs; // 变量名 → 帧寄存器
uint8_t nlocals = 0; // 变量 = 临时寄存器起始 uint8_t nlocals = 0; // 变量区终点 = 临时寄存器起始
uint8_t nregs = 0; // 峰值(变量 + 临时) uint8_t nregs = 0; // 峰值(变量 + 临时)
uint8_t temp_used = 0; // 本语句已用临时数(语句结束清零) uint8_t temp_used = 0; // 本语句已用临时数(语句结束清零)
bool is_function = false; // FUNCTION(结果 r0 / 输入只读)
std::string result_name; // FUNCTION 名(结果寄存器映射)
}; };
/** /**
@@ -203,31 +206,53 @@ namespace {
/** /**
* @brief 编译一个 POU * @brief 编译一个 POU
* @details 切片 1:仅 PROGRAM + 标量变量(FB 实例/函数留后续切片 * @details 切片 6:支持 PROGRAM 与 FUNCTIONFB 留切片 8
* 帧约定(见 Doc/compiler/寄存器码.md):结果 r0、输入 r1..r7(只读)、
* 变量与临时全部从 r8 起
* @param pou POU AST * @param pou POU AST
* @param f 输出函数编译态 * @param f 输出函数编译态
* @return true 成功;falseerr 已写) * @return true 成功;falseerr 已写)
*/ */
bool build_function(const POU& pou, FuncCtx* f) { bool build_function(const POU& pou, FuncCtx* f) {
if (pou.kind != PouKind::Program) { if (pou.kind == PouKind::FunctionBlock) {
return fail("only PROGRAM supported in slice 1 ('" + pou.name + "')"); return fail("FB not supported in slice 6 ('" + pou.name + "')");
} }
f->is_function = pou.kind == PouKind::Function;
f->result_name = pou.name;
if (f->is_function) {
f->regs[pou.name] = 0; // 结果寄存器 r0
uint8_t idx = 1;
for (const VarBlock& b : pou.blocks) {
if (b.section != VarSection::Input) {
continue;
}
for (const VarDecl& d : b.vars) {
f->regs[d.name] = idx++; // 输入 r1..r7
}
}
}
uint8_t r = 8; // 变量/临时基址(调用约定区 r0..r7 不占用)
for (const VarBlock& b : pou.blocks) { for (const VarBlock& b : pou.blocks) {
// External / Global 走数据区槽,不占帧寄存器 // External / Global 走数据区槽,不占帧寄存器
if (b.section == VarSection::External || b.section == VarSection::Global) { if (b.section == VarSection::External || b.section == VarSection::Global) {
continue; continue;
} }
// FUNCTION 输入已分配(r1..r7
if (f->is_function && b.section == VarSection::Input) {
continue;
}
for (const VarDecl& d : b.vars) { for (const VarDecl& d : b.vars) {
if (d.type.kind == TypeKind::FbUser || d.type.kind == TypeKind::FbBuiltin) { if (d.type.kind == TypeKind::FbUser || d.type.kind == TypeKind::FbBuiltin) {
return fail("FB instance not supported in slice 1 ('" + d.name + "')"); return fail("FB instance not supported in slice 6 ('" + d.name + "')");
} }
if (f->regs.size() > 255) { if (r > 255) {
return fail("register overflow in function '" + pou.name + "'"); return fail("register overflow in function '" + pou.name + "'");
} }
f->regs[d.name] = f->nregs++; f->regs[d.name] = r++;
} }
} }
f->nlocals = f->nregs; f->nlocals = r;
f->nregs = r;
for (const Stmt& st : pou.body) { for (const Stmt& st : pou.body) {
begin_stmt(*f); begin_stmt(*f);
if (!compile_stmt(*f, st)) { if (!compile_stmt(*f, st)) {
@@ -259,6 +284,10 @@ namespace {
// 全局 / 外部左值 → STORE_* 到数据区 // 全局 / 外部左值 → STORE_* 到数据区
return store_target(f, st.target, *st.value); return store_target(f, st.target, *st.value);
} }
// 函数输入只读(调用约定,见 Doc/compiler/寄存器码.md
if (f.is_function && it->second >= 1 && it->second <= 7) {
return fail("cannot write function input '" + st.target + "'");
}
const uint8_t rd = it->second; const uint8_t rd = it->second;
return compile_expr(f, *st.value, rd); return compile_expr(f, *st.value, rd);
} }
@@ -452,7 +481,46 @@ namespace {
patch_jump(f, jmp_idx, f.code.size()); patch_jump(f, jmp_idx, f.code.size());
return true; return true;
} }
return fail("expression not supported in slice 3"); if (e.kind == ExprKind::Call) {
// 调用约定:实参求值到 r8+ 临时 → MOVE 到 r1..r7 → CALL → 结果 r0
if (e.args.size() > 7) {
return fail("too many arguments (max 7)");
}
std::vector<uint8_t> args;
for (const auto& a : e.args) {
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *a, t)) {
return false;
}
args.push_back(t);
}
const int fn_id = find_fn_id(e.name);
if (fn_id < 0) {
return fail("no fn_id for '" + e.name + "'");
}
for (size_t i = 0; i < args.size(); ++i) {
f.code.push_back(
isa::enc_rr(isa::Op::MOVE, static_cast<uint8_t>(1 + i), args[i]));
}
f.code.push_back(isa::enc_call(static_cast<uint16_t>(fn_id)));
f.code.push_back(isa::enc_rr(isa::Op::MOVE, rd, 0));
return true;
}
return fail("expression not supported in slice 6");
}
/**
* @brief 按名查 fn_idPOU 收集序下标)
* @param name POU 名(小写)
* @return fn_id;未找到返回 -1
*/
int find_fn_id(const std::string& name) const {
for (size_t i = 0; i < link_.scopes.size(); ++i) {
if (link_.scopes[i].name == name) {
return static_cast<int>(i);
}
}
return -1;
} }
/** /**
+105 -54
View File
@@ -121,7 +121,7 @@ static bool test_case01() {
CHECK(v.header().project_hash != isa::kFnvBasis); CHECK(v.header().project_hash != isa::kFnvBasis);
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 0); CHECK(r.nregs == 8); // 帧基址(调用约定区)
CHECK(r.code_len == 1); CHECK(r.code_len == 1);
char buf[32]; char buf[32];
@@ -143,16 +143,16 @@ static bool test_case02() {
CHECK(v.header().n_consts == 2); // TRUE 与 FALSE CHECK(v.header().n_consts == 2); // TRUE 与 FALSE
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 2); // a, b CHECK(r.nregs == 10); // a, br8 起)
CHECK(r.code_len == 3); CHECK(r.code_len == 3);
// 从字节码段取指令(按函数行 code_offset 定位) // 从字节码段取指令(按函数行 code_offset 定位)
const uint8_t* base = v.code_bytes(); const uint8_t* base = v.code_bytes();
char buf[64]; char buf[64];
isa::disasm(reinterpret_cast<const uint32_t*>(base)[0], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r0, 0") == 0); // a := TRUE(常量 0 CHECK(std::strcmp(buf, "LOADK r8, 0") == 0); // a := TRUE(常量 0
isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r1, 1") == 0); // b := FALSE(常量 1 CHECK(std::strcmp(buf, "LOADK r9, 1") == 0); // b := FALSE(常量 1
isa::disasm(reinterpret_cast<const uint32_t*>(base)[2], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0); CHECK(std::strcmp(buf, "RET") == 0);
@@ -179,13 +179,13 @@ static bool test_case09() {
CHECK(v.data_bytes()[1] == 0); CHECK(v.data_bytes()[1] == 0);
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 1); // 仅局部 x CHECK(r.nregs == 9); // r8 起:仅局部 x
CHECK(r.code_len == 2); CHECK(r.code_len == 2);
char buf[64]; char buf[64];
const uint8_t* base = v.code_bytes(); const uint8_t* base = v.code_bytes();
isa::disasm(reinterpret_cast<const uint32_t*>(base)[0], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOAD_GLOBAL r0, 0") == 0); // x := G1(槽 0 偏移 0 CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 0") == 0); // x := G1(槽 0 偏移 0
isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0); CHECK(std::strcmp(buf, "RET") == 0);
return true; return true;
@@ -213,16 +213,16 @@ static bool test_io_ops() {
CHECK(v.header().n_globals == 2); CHECK(v.header().n_globals == 2);
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 2); // q + 1 临时 CHECK(r.nregs == 10); // r8 起:q + 1 临时
char buf[64]; char buf[64];
const uint8_t* base = v.code_bytes(); const uint8_t* base = v.code_bytes();
isa::disasm(reinterpret_cast<const uint32_t*>(base)[0], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOAD_I r0, 0") == 0); // q := I0_0io.input → LOAD_I CHECK(std::strcmp(buf, "LOAD_I r8, 0") == 0); // q := I0_0io.input → LOAD_I
isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r1, r0") == 0); // 临时 ← q CHECK(std::strcmp(buf, "MOVE r9, r8") == 0); // 临时 ← q
isa::disasm(reinterpret_cast<const uint32_t*>(base)[2], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "STORE_Q r1, 1") == 0); // Q0_0 := 临时(io.output → STORE_Q CHECK(std::strcmp(buf, "STORE_Q r9, 1") == 0); // Q0_0 := 临时(io.output → STORE_Q
isa::disasm(reinterpret_cast<const uint32_t*>(base)[3], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(base)[3], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0); CHECK(std::strcmp(buf, "RET") == 0);
return true; return true;
@@ -255,7 +255,7 @@ static bool test_layout() {
char buf[64]; char buf[64];
isa::disasm(reinterpret_cast<const uint32_t*>(v.code_bytes())[0], buf, sizeof buf); isa::disasm(reinterpret_cast<const uint32_t*>(v.code_bytes())[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOAD_GLOBAL r0, 2") == 0); // x := IINT 偏移 2 CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 2") == 0); // x := IINT 偏移 2
return true; return true;
} }
@@ -289,7 +289,7 @@ static bool test_case03() {
const isa::ImageView v = isa::ImageView::from(img); const isa::ImageView v = isa::ImageView::from(img);
CHECK(v.ok()); CHECK(v.ok());
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 4); // a b x + 1 临时 CHECK(r.nregs == 12); // r8 起:a b x + 1 临时
CHECK(r.code_len == 9); CHECK(r.code_len == 9);
char buf[64]; char buf[64];
@@ -297,22 +297,22 @@ static bool test_case03() {
const uint32_t* ins = reinterpret_cast<const uint32_t*>(base); 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 // x := a AND bMOVE r2,r0 / JF r2,+2 / MOVE r3,r1 / MOVE r2,r3
isa::disasm(ins[0], buf, sizeof buf); isa::disasm(ins[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r2, r0") == 0); CHECK(std::strcmp(buf, "MOVE r10, r8") == 0);
isa::disasm(ins[1], buf, sizeof buf); isa::disasm(ins[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "JF r2, +2") == 0); CHECK(std::strcmp(buf, "JF r10, +2") == 0);
isa::disasm(ins[2], buf, sizeof buf); isa::disasm(ins[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r3, r1") == 0); CHECK(std::strcmp(buf, "MOVE r11, r9") == 0);
isa::disasm(ins[3], buf, sizeof buf); isa::disasm(ins[3], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r2, r3") == 0); CHECK(std::strcmp(buf, "MOVE r10, r11") == 0);
// x := a OR bMOVE r2,r0 / JT r2,+2 / MOVE r3,r1 / MOVE r2,r3 // x := a OR bMOVE r2,r0 / JT r2,+2 / MOVE r3,r1 / MOVE r2,r3
isa::disasm(ins[4], buf, sizeof buf); isa::disasm(ins[4], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r2, r0") == 0); CHECK(std::strcmp(buf, "MOVE r10, r8") == 0);
isa::disasm(ins[5], buf, sizeof buf); isa::disasm(ins[5], buf, sizeof buf);
CHECK(std::strcmp(buf, "JT r2, +2") == 0); CHECK(std::strcmp(buf, "JT r10, +2") == 0);
isa::disasm(ins[6], buf, sizeof buf); isa::disasm(ins[6], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r3, r1") == 0); CHECK(std::strcmp(buf, "MOVE r11, r9") == 0);
isa::disasm(ins[7], buf, sizeof buf); isa::disasm(ins[7], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r2, r3") == 0); CHECK(std::strcmp(buf, "MOVE r10, r11") == 0);
isa::disasm(ins[8], buf, sizeof buf); isa::disasm(ins[8], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0); CHECK(std::strcmp(buf, "RET") == 0);
return true; return true;
@@ -332,16 +332,16 @@ static bool test_not() {
const isa::ImageView v = isa::ImageView::from(img); const isa::ImageView v = isa::ImageView::from(img);
CHECK(v.ok()); CHECK(v.ok());
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 3); // a x + 1 临时 CHECK(r.nregs == 11); // r8 起:a x + 1 临时
CHECK(r.code_len == 3); CHECK(r.code_len == 3);
char buf[64]; char buf[64];
const uint8_t* base = v.code_bytes(); const uint8_t* base = v.code_bytes();
const uint32_t* ins = reinterpret_cast<const uint32_t*>(base); const uint32_t* ins = reinterpret_cast<const uint32_t*>(base);
isa::disasm(ins[0], buf, sizeof buf); isa::disasm(ins[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r2, r0") == 0); // 临时 ← a CHECK(std::strcmp(buf, "MOVE r10, r8") == 0); // 临时 ← a
isa::disasm(ins[1], buf, sizeof buf); isa::disasm(ins[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "NOT r1, r2") == 0); // x := NOT 临时 CHECK(std::strcmp(buf, "NOT r9, r10") == 0); // x := NOT 临时
isa::disasm(ins[2], buf, sizeof buf); isa::disasm(ins[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0); CHECK(std::strcmp(buf, "RET") == 0);
return true; return true;
@@ -357,7 +357,7 @@ static bool test_case04() {
const isa::ImageView v = isa::ImageView::from(img); const isa::ImageView v = isa::ImageView::from(img);
CHECK(v.ok()); CHECK(v.ok());
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 5); // sel out + 3 临时CMP 目的 + 两操作数) CHECK(r.nregs == 13); // r8 起:sel out + 3 临时
CHECK(r.code_len == 14); CHECK(r.code_len == 14);
char buf[64]; char buf[64];
@@ -365,33 +365,33 @@ static bool test_case04() {
const uint32_t* ins = reinterpret_cast<const uint32_t*>(base); const uint32_t* ins = reinterpret_cast<const uint32_t*>(base);
// IF sel = 0 THENsel→r3、0→r4、CMP_EQ r2(常量序:0、10、1、20、30 // IF sel = 0 THENsel→r3、0→r4、CMP_EQ r2(常量序:0、10、1、20、30
isa::disasm(ins[0], buf, sizeof buf); isa::disasm(ins[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r3, r0") == 0); CHECK(std::strcmp(buf, "MOVE r11, r8") == 0);
isa::disasm(ins[1], buf, sizeof buf); isa::disasm(ins[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r4, 0") == 0); CHECK(std::strcmp(buf, "LOADK r12, 0") == 0);
isa::disasm(ins[2], buf, sizeof buf); isa::disasm(ins[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "CMP_EQ r2, r3, r4") == 0); CHECK(std::strcmp(buf, "CMP_EQ r10, r11, r12") == 0);
isa::disasm(ins[3], buf, sizeof buf); isa::disasm(ins[3], buf, sizeof buf);
CHECK(std::strcmp(buf, "JF r2, +2") == 0); // 假 → elsif[6] CHECK(std::strcmp(buf, "JF r10, +2") == 0); // 假 → elsif[6]
isa::disasm(ins[4], buf, sizeof buf); isa::disasm(ins[4], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r1, 1") == 0); // out := 10(常量 1 CHECK(std::strcmp(buf, "LOADK r9, 1") == 0); // out := 10(常量 1
isa::disasm(ins[5], buf, sizeof buf); isa::disasm(ins[5], buf, sizeof buf);
CHECK(std::strcmp(buf, "JMP +7") == 0); // → end[13] CHECK(std::strcmp(buf, "JMP +7") == 0); // → end[13]
// ELSIF sel = 1 // ELSIF sel = 1
isa::disasm(ins[6], buf, sizeof buf); isa::disasm(ins[6], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r3, r0") == 0); CHECK(std::strcmp(buf, "MOVE r11, r8") == 0);
isa::disasm(ins[7], buf, sizeof buf); isa::disasm(ins[7], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r4, 2") == 0); CHECK(std::strcmp(buf, "LOADK r12, 2") == 0);
isa::disasm(ins[8], buf, sizeof buf); isa::disasm(ins[8], buf, sizeof buf);
CHECK(std::strcmp(buf, "CMP_EQ r2, r3, r4") == 0); CHECK(std::strcmp(buf, "CMP_EQ r10, r11, r12") == 0);
isa::disasm(ins[9], buf, sizeof buf); isa::disasm(ins[9], buf, sizeof buf);
CHECK(std::strcmp(buf, "JF r2, +2") == 0); // 假 → else[12] CHECK(std::strcmp(buf, "JF r10, +2") == 0); // 假 → else[12]
isa::disasm(ins[10], buf, sizeof buf); isa::disasm(ins[10], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r1, 3") == 0); CHECK(std::strcmp(buf, "LOADK r9, 3") == 0);
isa::disasm(ins[11], buf, sizeof buf); isa::disasm(ins[11], buf, sizeof buf);
CHECK(std::strcmp(buf, "JMP +1") == 0); // → end[13] CHECK(std::strcmp(buf, "JMP +1") == 0); // → end[13]
// ELSE // ELSE
isa::disasm(ins[12], buf, sizeof buf); isa::disasm(ins[12], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r1, 4") == 0); CHECK(std::strcmp(buf, "LOADK r9, 4") == 0);
isa::disasm(ins[13], buf, sizeof buf); isa::disasm(ins[13], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0); CHECK(std::strcmp(buf, "RET") == 0);
return true; return true;
@@ -407,30 +407,30 @@ static bool test_case05() {
const isa::ImageView v = isa::ImageView::from(img); const isa::ImageView v = isa::ImageView::from(img);
CHECK(v.ok()); CHECK(v.ok());
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 4); // n + 3 临时(CMP 目的 + 两操作数) CHECK(r.nregs == 12); // r8 起:n + 3 临时(条件结果 r9 + 两操作数)
CHECK(r.code_len == 10); CHECK(r.code_len == 10);
char buf[64]; char buf[64];
const uint8_t* base = v.code_bytes(); const uint8_t* base = v.code_bytes();
const uint32_t* ins = reinterpret_cast<const uint32_t*>(base); const uint32_t* ins = reinterpret_cast<const uint32_t*>(base);
isa::disasm(ins[0], buf, sizeof buf); isa::disasm(ins[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r0, 0") == 0); // n := 0 CHECK(std::strcmp(buf, "LOADK r8, 0") == 0); // n := 0
// L_loop [1]n < 10 → r1 // L_loop [1]n < 10 → r9
isa::disasm(ins[1], buf, sizeof buf); isa::disasm(ins[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r2, r0") == 0); CHECK(std::strcmp(buf, "MOVE r10, r8") == 0);
isa::disasm(ins[2], buf, sizeof buf); isa::disasm(ins[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r3, 1") == 0); // 常量 1 = 10 CHECK(std::strcmp(buf, "LOADK r11, 1") == 0); // 常量 1 = 10
isa::disasm(ins[3], buf, sizeof buf); isa::disasm(ins[3], buf, sizeof buf);
CHECK(std::strcmp(buf, "CMP_LT r1, r2, r3") == 0); CHECK(std::strcmp(buf, "CMP_LT r9, r10, r11") == 0);
isa::disasm(ins[4], buf, sizeof buf); isa::disasm(ins[4], buf, sizeof buf);
CHECK(std::strcmp(buf, "JF r1, +4") == 0); // 假 → RET[9] CHECK(std::strcmp(buf, "JF r9, +4") == 0); // 假 → RET[9]
// n := n + 1(体语句临时复用 r1/r2 // n := n + 1(体语句临时复用 r9/r10
isa::disasm(ins[5], buf, sizeof buf); isa::disasm(ins[5], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r1, r0") == 0); CHECK(std::strcmp(buf, "MOVE r9, r8") == 0);
isa::disasm(ins[6], buf, sizeof buf); isa::disasm(ins[6], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r2, 2") == 0); // 常量 2 = 1 CHECK(std::strcmp(buf, "LOADK r10, 2") == 0); // 常量 2 = 1
isa::disasm(ins[7], buf, sizeof buf); isa::disasm(ins[7], buf, sizeof buf);
CHECK(std::strcmp(buf, "ADD r0, r1, r2") == 0); CHECK(std::strcmp(buf, "ADD r8, r9, r10") == 0);
isa::disasm(ins[8], buf, sizeof buf); isa::disasm(ins[8], buf, sizeof buf);
CHECK(std::strcmp(buf, "JMP -8") == 0); // 回 L_loop[1] CHECK(std::strcmp(buf, "JMP -8") == 0); // 回 L_loop[1]
isa::disasm(ins[9], buf, sizeof buf); isa::disasm(ins[9], buf, sizeof buf);
@@ -448,7 +448,7 @@ static bool test_case07() {
const isa::ImageView v = isa::ImageView::from(img); const isa::ImageView v = isa::ImageView::from(img);
CHECK(v.ok()); CHECK(v.ok());
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 6); // a b c eq + 2 临时(语句间复用) CHECK(r.nregs == 14); // r8 起:a b c eq + 2 临时
char buf[64]; char buf[64];
const uint8_t* base = v.code_bytes(); const uint8_t* base = v.code_bytes();
@@ -456,10 +456,10 @@ static bool test_case07() {
bool saw_mul = false, saw_div = false, saw_sub = false, saw_gt = false; bool saw_mul = false, saw_div = false, saw_sub = false, saw_gt = false;
for (uint32_t i = 0; i < r.code_len; ++i) { for (uint32_t i = 0; i < r.code_len; ++i) {
isa::disasm(ins[i], buf, sizeof buf); isa::disasm(ins[i], buf, sizeof buf);
if (std::strstr(buf, "MUL r0, ")) saw_mul = true; if (std::strstr(buf, "MUL r8, ")) saw_mul = true;
if (std::strstr(buf, "DIV r1, ")) saw_div = true; if (std::strstr(buf, "DIV r9, ")) saw_div = true;
if (std::strstr(buf, "SUB r2, ")) saw_sub = true; if (std::strstr(buf, "SUB r10, ")) saw_sub = true;
if (std::strstr(buf, "CMP_GT r3, ")) saw_gt = true; if (std::strstr(buf, "CMP_GT r11, ")) saw_gt = true;
} }
CHECK(saw_mul && saw_div && saw_sub && saw_gt); CHECK(saw_mul && saw_div && saw_sub && saw_gt);
return true; return true;
@@ -481,13 +481,63 @@ static bool test_case08() {
CHECK(c1.tag == isa::types::Time && c1.value == 1250); // T#1s250ms CHECK(c1.tag == isa::types::Time && c1.value == 1250); // T#1s250ms
const isa::FuncRow r = v.func_row(0); const isa::FuncRow r = v.func_row(0);
CHECK(r.nregs == 2); CHECK(r.nregs == 10); // r8 起
char buf[64]; char buf[64];
const uint32_t* ins = reinterpret_cast<const uint32_t*>(v.code_bytes()); const uint32_t* ins = reinterpret_cast<const uint32_t*>(v.code_bytes());
isa::disasm(ins[0], buf, sizeof buf); isa::disasm(ins[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r0, 0") == 0); CHECK(std::strcmp(buf, "LOADK r8, 0") == 0);
isa::disasm(ins[1], buf, sizeof buf); isa::disasm(ins[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r1, 1") == 0); CHECK(std::strcmp(buf, "LOADK r9, 1") == 0);
return true;
}
// ---- 13. 用例 13FUNCTION + CALL(调用约定 r0 结果 / r1.. 参数)----
static bool test_case13() {
std::vector<uint8_t> img;
std::string err;
CHECK(compile_case("13_function_call", &img, &err));
const isa::ImageView v = isa::ImageView::from(img);
CHECK(v.ok());
CHECK(v.header().n_funcs == 2);
CHECK(v.header().entry_fn_id == 1); // MAIN 是第二个 POU
char buf[64];
const uint8_t* base = v.code_bytes();
// Addfn_id 0):结果 r0、输入 r1(a) r2(b)、临时 r8/r9
const isa::FuncRow fa = v.func_row(0);
CHECK(fa.nregs == 10);
CHECK(fa.code_len == 4);
const uint32_t* ia = reinterpret_cast<const uint32_t*>(base);
isa::disasm(ia[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r8, r1") == 0); // t ← a
isa::disasm(ia[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r9, r2") == 0); // t ← b
isa::disasm(ia[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "ADD r0, r8, r9") == 0); // 结果 r0
isa::disasm(ia[3], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0);
// MAINfn_id 1):x 在 r8,实参临时 r9/r10
const isa::FuncRow fm = v.func_row(1);
CHECK(fm.nregs == 11);
CHECK(fm.code_len == 7);
const uint32_t* im = reinterpret_cast<const uint32_t*>(base) + fm.code_offset / 4;
isa::disasm(im[0], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r9, 0") == 0); // 3
isa::disasm(im[1], buf, sizeof buf);
CHECK(std::strcmp(buf, "LOADK r10, 1") == 0); // 4
isa::disasm(im[2], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r1, r9") == 0); // 参数 1 ← 3
isa::disasm(im[3], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r2, r10") == 0); // 参数 2 ← 4
isa::disasm(im[4], buf, sizeof buf);
CHECK(std::strcmp(buf, "CALL 0") == 0);
isa::disasm(im[5], buf, sizeof buf);
CHECK(std::strcmp(buf, "MOVE r8, r0") == 0); // x ← 结果
isa::disasm(im[6], buf, sizeof buf);
CHECK(std::strcmp(buf, "RET") == 0);
return true; return true;
} }
@@ -504,6 +554,7 @@ int main() {
if (!test_case05()) return 1; if (!test_case05()) return 1;
if (!test_case07()) return 1; if (!test_case07()) return 1;
if (!test_case08()) return 1; if (!test_case08()) return 1;
if (!test_case13()) 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;
} }