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
+86 -18
View File
@@ -1,15 +1,15 @@
/**
* @file Codegen.cpp
* @brief 寄存器码生成(12.8,切片 5+ WHILE、四则运算
* @brief 寄存器码生成(12.8,切片 6+ FUNCTION 与 CALL
* @author
* @date 2026-08-21
*
* @details 设计说明(详见 Doc/compiler/寄存器码.md):
* - 切片 1~4:帧/字面量/MOVE/RET;全局数据区;NOT 与短路 AND/ORCMP 与 IF。
* - 切片 5WHILE 编成
* 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
* - 切片 1~5:帧/字面量/MOVE/RET;全局数据区;NOT 与短路 AND/ORCMP 与 IFWHILE 与四则
* - 切片 6:调用约定(冻结)——结果 r0、参数 r1..r7(最多 7 输入、函数内只读)、
* 变量与临时全部从 r8 起(调用约定区不受覆盖);
* CALL 时 VM 复制 r0..r7 到新帧、RET 复制回调用方(r0=结果);
* 调用点:实参求值到 r8+ 临时 → MOVE r(1+i) → CALL fn_id → MOVE rd, r0
* - 其余构造报 codegen error(后续切片展开)
*
* 函数清单:
@@ -19,13 +19,14 @@
* - Builder::run 布局数据区 → 逐 POU 建函数 → 拼映像
* - Builder::fail 组装 "codegen error: <msg>" 返回 false
* - find_pou 按名查 POU AST
* - build_function 编译一个 POU 的语句体(帧寄存器分配
* - find_fn_id 按名查 fn_idPOU 收集序
* - build_function 编译一个 POUPROGRAM/FUNCTION;帧约定分配)
* - begin_stmt / alloc_temp 临时寄存器:语句内递增、语句结束复用基址
* - compile_stmt 语句编译(切片 5赋值 / IF / WHILE
* - compile_stmt 语句编译(赋值 / IF / WHILE;函数输入只读
* - compile_if IF/ELSIF/ELSE:条件链 + 分支尾部 JMPJF/JMP 回填
* - compile_while WHILEL_loop + 条件 JF + 体 + JMP 回环
* - store_target 赋值左值:STORE_* 到全局槽
* - compile_expr 表达式编译到寄存器(含 CMP/算术/NOT/短路 AND/OR
* - compile_expr 表达式编译到寄存器(含 CMP/算术/NOT/短路/CALL
* - cmp_op / arith_op 比较符/算术符 → 操作码
* - load_op / store_op 按 io 绑定选读取/写入操作码
* - global_offset 逻辑槽号 → 数据区字节偏移
@@ -176,9 +177,11 @@ namespace {
std::string name;
std::vector<isa::Instr> code; // 字节码(函数表 code_offset 相对此段)
std::map<std::string, uint8_t> regs; // 变量名 → 帧寄存器
uint8_t nlocals = 0; // 变量 = 临时寄存器起始
uint8_t nlocals = 0; // 变量区终点 = 临时寄存器起始
uint8_t nregs = 0; // 峰值(变量 + 临时)
uint8_t temp_used = 0; // 本语句已用临时数(语句结束清零)
bool is_function = false; // FUNCTION(结果 r0 / 输入只读)
std::string result_name; // FUNCTION 名(结果寄存器映射)
};
/**
@@ -203,31 +206,53 @@ namespace {
/**
* @brief 编译一个 POU
* @details 切片 1:仅 PROGRAM + 标量变量(FB 实例/函数留后续切片
* @details 切片 6:支持 PROGRAM 与 FUNCTIONFB 留切片 8
* 帧约定(见 Doc/compiler/寄存器码.md):结果 r0、输入 r1..r7(只读)、
* 变量与临时全部从 r8 起
* @param pou POU AST
* @param f 输出函数编译态
* @return true 成功;falseerr 已写)
*/
bool build_function(const POU& pou, FuncCtx* f) {
if (pou.kind != PouKind::Program) {
return fail("only PROGRAM supported in slice 1 ('" + pou.name + "')");
if (pou.kind == PouKind::FunctionBlock) {
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) {
// External / Global 走数据区槽,不占帧寄存器
if (b.section == VarSection::External || b.section == VarSection::Global) {
continue;
}
// FUNCTION 输入已分配(r1..r7
if (f->is_function && b.section == VarSection::Input) {
continue;
}
for (const VarDecl& d : b.vars) {
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 + "'");
}
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) {
begin_stmt(*f);
if (!compile_stmt(*f, st)) {
@@ -259,6 +284,10 @@ namespace {
// 全局 / 外部左值 → STORE_* 到数据区
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;
return compile_expr(f, *st.value, rd);
}
@@ -452,7 +481,46 @@ namespace {
patch_jump(f, jmp_idx, f.code.size());
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;
}
/**