Files
Interpreter/compiler/src/Codegen.cpp
T
Admin 7b4961f7ed 阶段3-5:编译器验证——13 正例源码↔反汇编对照 + --dump-map
- STCompiler --dump-map:编译后打印变量/槽映射(函数局部 r 寄存器、
  全局 slot/off/type/func、实例 slot/off/fb/size/字段偏移)
- CodegenMap 输出结构(codegen_project 可选参数)
- 修复参数循环 bug(i+1<argc 漏掉末尾单参数选项)
- 结构自检(临时 stbcheck):13 用例寄存器/func/跳转/槽/长度全部通过
- 13 正例逐条对照全部通过,产出 Doc/compiler/用例反汇编对照.md
- 已知缺陷记录:LREAL 字面量装载按 REAL(TODO)
2026-08-26 09:47:24 +08:00

1091 lines
46 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file Codegen.cpp
* @brief 寄存器码生成(V2:配置驱动 + 变长指令;compiler 不依赖 isa
* @author
* @date 2026-08-21
*
* @details 设计说明(详见 Doc/compiler/寄存器码.md 与 Doc/isa/指令与映像.md V2):
* - 指令 prefix/op/func 全部来自 machine.tomlMachineConfig);发射经 Codec 位号
* - 数据模型:槽表(8B 引用 {addr,预留})→ 值段(变宽紧凑,1/2/4/8 对齐)
* - 常量表 8B 原始值(无 tag),LOADK 按 func 解释,位模式去重
* - 跳转回填字节单位(off32 相对下一条:目标 = 当前指令起点 + 指令长 + off)
* - 用户 FB 调用点内联展开(字段访问 = LOAD_OFF/STORE_OFF 实例槽 + 字段偏移)
* - 内建 FB → CAL fb_id, 实例槽(fb_id 查 [[fb]] 表)
* - max_stackMAIN + FUNCTION 调用图 DAG 最坏路径 Σ(12 + nregs×8);递归 = 编译期报错
*
* 函数清单:
* - Builder::Builder 构造;run 布局 → 逐 POU 建函数 → 拼映像
* - emit / emit0 指令发射(查 ConfigOpCodec 位号写字节)
* - E_rr/E_rrr/E_imm/E_slot/E_jmp/E_jc/E_call/E_cal/E_ret/E_slot_off
* - func_of_name 语言类型名 → func 位段值
* - compile_stmt/if/while/fb_call/fb_inline/store_target/compile_exprV1 语义保留)
* - patch_jump 字节化回填
* - layout_slots_values 槽表 + 值段布局(全局 + FB 实例,对齐 1/2/4/8
* - compute_max_stack 调用图 DAG 最长路径 + 环检测
* - assemble_image V2 头 128B + 段序拼装
*/
#include "compiler/Codegen.h"
#include <cctype>
#include <cstdio>
#include <cstring>
#include <functional>
#include <map>
#include <string>
#include <vector>
#include "compiler/Codec.h"
#include "compiler/MachineConfig.h"
#include "compiler/Stb.h"
#include "compiler/TypeInfo.h"
namespace compiler {
namespace {
/**
* @brief 代码生成器(V2 寄存器码)。
* @details 流程:布局槽表/值段 → 逐 POU 建函数 → 计算 max_stack → 拼映像。
* 寄存器分配:r0 结果、r1..r7 参数(调用约定区,输入只读)、r8+ 变量/临时;
* 跳转偏移字节单位相对下一条。失败统一经 fail() 写 err(前缀 "codegen error")。
*/
class Builder {
public:
Builder(const Project& proj, const std::vector<SourceUnit>& units,
const LinkResult& link, const MachineConfig& cfg,
std::vector<uint8_t>* image, std::string* err, CodegenMap* map)
: proj_(proj), units_(units), link_(link), cfg_(cfg), image_(image), err_(err),
map_(map) {
for (const IoBinding& b : proj_.io) {
std::string key = b.var;
for (char& ch : key) {
ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}
if (b.is_input) {
io_input_[key] = true;
} else {
io_output_[key] = true;
}
}
}
bool run() {
if (!layout_slots_values()) {
return false;
}
for (const LinkResult::PouScope& sc : link_.scopes) {
const POU* pou = find_pou(sc.name);
if (pou == nullptr) {
continue;
}
FuncCtx f;
f.name = sc.name;
f.pou_name = sc.name;
if (!build_function(*pou, &f)) {
return false;
}
funcs_.push_back(std::move(f));
}
if (!compute_max_stack()) {
return false;
}
return assemble_image();
}
private:
bool fail(const std::string& msg) {
if (err_) {
*err_ = "codegen error: " + msg;
}
return false;
}
/**
* @brief 查指令行(MachineConfig 强校验保证存在)。
* @param name 指令名(大写,如 "ADD"
* @return 配置行指针(找不到返回 nullptr,不应发生)
*/
const ConfigOp* opc(const char* name) const {
return cfg_.find_op(name);
}
// ---- 指令发射(V2Codec 位号写字节)----
/**
* @brief 单个函数的编译状态(V2)。
*/
struct FuncCtx {
std::string name; ///< 函数名
std::string pou_name; ///< 所属 POU 名(实例查找键)
std::vector<uint8_t> code; ///< 字节码(变长指令,字节流)
std::map<std::string, uint8_t> regs; ///< 变量名 → 帧寄存器
std::map<std::string, uint8_t> reg_func; ///< 变量名 → func(类型)
uint8_t nlocals = 0; ///< 变量区终点 = 临时寄存器起始
uint8_t nregs = 0; ///< 寄存器峰值
uint8_t temp_used = 0; ///< 本语句已用临时数
bool is_function = false; ///< FUNCTION(结果 r0 / 输入只读)
std::string result_name; ///< FUNCTION 名(结果寄存器映射)
std::vector<uint32_t> calls; ///< 本函数 CALL 的 fn_id 列表(max_stack 用)
};
/// 发射一条指令:追加 instr_len 字节到函数码流,返回起始字节偏移。
size_t emit(FuncCtx& f, const ConfigOp* op) {
const size_t start = f.code.size();
f.code.resize(start + kMaxInstrLen, 0);
set_byte0(f.code.data() + start, op->prefix, op->op);
if (op->func_mode == "none" || op->func_mode == "width" ||
op->func_mode == "uint") {
set_func(f.code.data() + start, 0);
}
f.code.resize(start + instr_len(f.code.data() + start));
return start;
}
/// 双寄存器指令(rd, rs)。
size_t E_rr(FuncCtx& f, const char* name, uint8_t rd, uint8_t rs) {
const size_t s = emit(f, opc(name));
set_rd(f.code.data() + s, rd);
set_rs1(f.code.data() + s, rs);
return s;
}
/// 三寄存器指令(rd, ra, rb)。
size_t E_rrr(FuncCtx& f, const char* name, uint8_t rd, uint8_t ra, uint8_t rb) {
const size_t s = emit(f, opc(name));
set_rd(f.code.data() + s, rd);
set_rs1(f.code.data() + s, ra);
set_rs2(f.code.data() + s, rb);
return s;
}
/// 立即数指令(rd, imm32)。
size_t E_imm(FuncCtx& f, const char* name, uint8_t rd, uint32_t imm) {
const size_t s = emit(f, opc(name));
set_rd(f.code.data() + s, rd);
set_imm32(f.code.data() + s, imm);
return s;
}
/// 槽号指令(rd, slot32)。
size_t E_slot(FuncCtx& f, const char* name, uint8_t rd, uint32_t slot) {
const size_t s = emit(f, opc(name));
set_rd(f.code.data() + s, rd);
set_slot32(f.code.data() + s, slot);
return s;
}
/// 无条件跳转(off 字节相对下一条)。
size_t E_jmp(FuncCtx& f, int32_t off) {
const size_t s = emit(f, opc("JMP"));
set_off32(f.code.data() + s, off);
return s;
}
/// 条件跳转(r 为真则跳;off 字节相对下一条,JC 形态 off@55..24)。
size_t E_jc(FuncCtx& f, const char* name, uint8_t r, int32_t off) {
const size_t s = emit(f, opc(name));
set_rd(f.code.data() + s, r);
set_off32_jc(f.code.data() + s, off);
return s;
}
/// CALL 指令(fn_id32)。
size_t E_call(FuncCtx& f, uint32_t fn_id) {
const size_t s = emit(f, opc("CALL"));
set_fn_id32(f.code.data() + s, fn_id);
return s;
}
/// RET 指令。
size_t E_ret(FuncCtx& f) {
const size_t s = emit(f, opc("RET"));
return s;
}
/// 字段访问(实例槽 + 字段偏移,16B SLOT+off)。
size_t E_slot_off(FuncCtx& f, const char* name, uint8_t rd, uint32_t slot,
int32_t off) {
const size_t s = emit(f, opc(name));
set_rd(f.code.data() + s, rd);
set_slot32(f.code.data() + s, slot);
set_off32_hi(f.code.data() + s, off);
return s;
}
/// 内建 FB 调用(fb_id16 + 实例槽32)。
size_t E_cal(FuncCtx& f, uint16_t fb_id, uint32_t slot) {
const size_t s = emit(f, opc("CAL"));
set_fb_id16(f.code.data() + s, fb_id);
set_cal_slot32(f.code.data() + s, slot);
return s;
}
/// 语言类型名 → func 位段值(大小写不敏感:符号表存小写,type 表存大写)。
uint8_t func_of_name(const std::string& type_name) const {
std::string key = type_name;
for (char& ch : key) {
ch = static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
}
const ConfigType* t = cfg_.find_type(key);
return t ? static_cast<uint8_t>(t->func) : 0;
}
const POU* find_pou(const std::string& name) const {
for (const SourceUnit& u : units_) {
for (const POU& p : u.ast.pous) {
if (p.name == name) {
return &p;
}
}
}
return nullptr;
}
/**
* @brief FB 实例布局(V2:槽表条目 + 值区偏移 + 字段偏移)。
*/
struct InstFields {
std::map<std::string, int32_t> field_off; ///< 字段名 → 值区内偏移
std::map<std::string, uint8_t> field_func; ///< 字段名 → func(类型)
uint32_t slot = 0; ///< 实例槽号(槽表条目)
std::string type_name; ///< 实例的 FB 类型名
};
void begin_stmt(FuncCtx& f) { f.temp_used = 0; }
uint8_t alloc_temp(FuncCtx& f) {
const uint8_t r = f.nlocals + f.temp_used;
++f.temp_used;
if (static_cast<uint16_t>(f.nlocals) + f.temp_used > f.nregs) {
f.nregs = f.nlocals + f.temp_used;
}
return r;
}
/// 变量名 → 语言类型名(局部声明;标量 = d.type.name 小写)。
std::string type_name_of_var(const POU& pou, const std::string& name) const {
for (const VarBlock& b : pou.blocks) {
for (const VarDecl& d : b.vars) {
if (d.name == name) {
return d.type.kind == TypeKind::Scalar ? d.type.name : "int";
}
}
}
return "int";
}
bool build_function(const POU& pou, FuncCtx* f) {
if (pou.kind == PouKind::FunctionBlock) {
f->nlocals = 8;
f->nregs = 8;
E_ret(*f);
return true;
}
f->is_function = pou.kind == PouKind::Function;
f->result_name = pou.name;
if (f->is_function) {
f->regs[pou.name] = 0;
f->reg_func[pou.name] = func_of_name("INT");
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++;
f->reg_func[d.name] = func_of_name(type_name_of_var(pou, d.name));
}
}
}
uint8_t r = 8;
for (const VarBlock& b : pou.blocks) {
if (b.section == VarSection::External || b.section == VarSection::Global) {
continue;
}
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) {
continue;
}
f->regs[d.name] = r;
f->reg_func[d.name] = func_of_name(type_name_of_var(pou, d.name));
++r;
}
}
f->nlocals = r;
f->nregs = r;
if (map_ != nullptr) {
std::map<std::string, std::string> vars;
for (const auto& kv : f->regs) {
std::string tn = "int";
const auto tf = f->reg_func.find(kv.first);
if (tf != f->reg_func.end()) {
tn = std::to_string(tf->second);
}
vars[kv.first] = "r" + std::to_string(kv.second) + " (func " + tn + ")";
}
map_->funcs.emplace_back(f->name, vars);
}
for (const Stmt& st : pou.body) {
begin_stmt(*f);
if (!compile_stmt(*f, st)) {
return false;
}
}
E_ret(*f);
return true;
}
bool compile_stmt(FuncCtx& f, const Stmt& st) {
if (st.kind == StmtKind::If) {
return compile_if(f, st);
}
if (st.kind == StmtKind::While) {
return compile_while(f, st);
}
if (st.kind == StmtKind::FbCall) {
return compile_fb_call(f, st);
}
if (st.kind != StmtKind::Assign) {
return fail("statement not supported");
}
if (inline_fields_) {
const auto fit = inline_fields_->field_off.find(st.target);
if (fit != inline_fields_->field_off.end()) {
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *st.value, t)) {
return false;
}
const size_t so = E_slot_off(f, "STORE_OFF", t, inline_fields_->slot,
fit->second);
const auto ff = inline_fields_->field_func.find(st.target);
if (ff != inline_fields_->field_func.end()) {
set_func(f.code.data() + so, ff->second);
}
return true;
}
}
const auto it = f.regs.find(st.target);
if (it == f.regs.end()) {
return store_target(f, st.target, *st.value);
}
if (f.is_function && it->second >= 1 && it->second <= 7) {
return fail("cannot write function input '" + st.target + "'");
}
return compile_expr(f, *st.value, it->second);
}
bool compile_fb_call(FuncCtx& f, const Stmt& st) {
const InstFields* inst = instance_of(f.pou_name, st.instance);
if (inst == nullptr) {
return fail("no instance '" + st.instance + "' in '" + f.pou_name + "'");
}
for (const FbArg& a : st.args) {
const auto it = inst->field_off.find(a.name);
if (it == inst->field_off.end()) {
return fail("unknown input '" + a.name + "' for '" + st.instance + "'");
}
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *a.value, t)) {
return false;
}
const size_t so = E_slot_off(f, "STORE_OFF", t, inst->slot, it->second);
const auto ff = inst->field_func.find(a.name);
if (ff != inst->field_func.end()) {
set_func(f.code.data() + so, ff->second);
}
}
// 内建 FBfb 表查 id
const ConfigFb* fb = cfg_.find_fb(uppercase_of(inst->type_name));
if (fb != nullptr) {
E_cal(f, static_cast<uint16_t>(fb->fb_id), inst->slot);
return true;
}
const POU* user_fb = find_pou(inst->type_name);
if (user_fb == nullptr) {
return fail("no FB type '" + inst->type_name + "'");
}
return compile_fb_inline(f, *user_fb, *inst);
}
static std::string uppercase_of(const std::string& s) {
std::string out = s;
for (char& ch : out) {
ch = static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
}
return out;
}
bool compile_fb_inline(FuncCtx& f, const POU& fb, const InstFields& inst) {
const InstFields* saved = inline_fields_;
inline_fields_ = &inst;
for (const Stmt& s : fb.body) {
begin_stmt(f);
if (!compile_stmt(f, s)) {
return false;
}
}
inline_fields_ = saved;
return true;
}
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 = E_jc(f, "JF", t, 0);
for (const Stmt& s : st.body) {
begin_stmt(f);
if (!compile_stmt(f, s)) {
return false;
}
}
const size_t jmp_idx = E_jmp(f, 0);
patch_jump(f, jmp_idx, loop);
patch_jump(f, jf_idx, f.code.size());
return true;
}
bool compile_if(FuncCtx& f, const Stmt& st) {
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;
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 = E_jc(f, "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) {
end_jmps.push_back(E_jmp(f, 0));
}
patch_jump(f, jf_idx, f.code.size());
}
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;
}
bool store_target(FuncCtx& f, const std::string& target, const Expr& value) {
const auto git = link_.global_index.find(target);
if (git == link_.global_index.end()) {
return fail("no storage for '" + target + "'");
}
if (io_input_.count(target)) {
return fail("cannot write to input '" + target + "'");
}
const uint8_t tmp = alloc_temp(f);
if (!compile_expr(f, value, tmp)) {
return false;
}
E_slot(f, "STORE", tmp, git->second);
return true;
}
bool compile_expr(FuncCtx& f, const Expr& e, uint8_t rd) {
if (e.kind == ExprKind::LitBool || e.kind == ExprKind::LitInt ||
e.kind == ExprKind::LitTime || e.kind == ExprKind::LitReal ||
e.kind == ExprKind::LitDate || e.kind == ExprKind::LitTod ||
e.kind == ExprKind::LitDt) {
const char* type_name = "INT";
int64_t value = e.int_value;
switch (e.kind) {
case ExprKind::LitBool: type_name = "BOOL"; break;
case ExprKind::LitInt: type_name = "INT"; break;
case ExprKind::LitTime: type_name = "TIME"; break;
case ExprKind::LitReal: {
// TODO(V2): 浮点字面量按目标类型装载(REAL/LREAL 位模式不同),
// 需 compile_expr 携带目标 func;当前按 REALfloat32)装载
type_name = "REAL";
float fv = static_cast<float>(e.double_value);
std::memcpy(&value, &fv, sizeof fv);
break;
}
case ExprKind::LitDate: type_name = "DATE"; break;
case ExprKind::LitTod: type_name = "TOD"; break;
case ExprKind::LitDt: type_name = "DT"; break;
default: break;
}
const size_t s = E_imm(f, "LOADK", rd, const_id(value));
set_func(f.code.data() + s, func_of_name(type_name));
return true;
}
if (e.kind == ExprKind::VarRef) {
if (inline_fields_) {
const auto fit = inline_fields_->field_off.find(e.name);
if (fit != inline_fields_->field_off.end()) {
const size_t lo = E_slot_off(f, "LOAD_OFF", rd,
inline_fields_->slot, fit->second);
const auto ff = inline_fields_->field_func.find(e.name);
if (ff != inline_fields_->field_func.end()) {
set_func(f.code.data() + lo, ff->second);
}
return true;
}
}
const auto sit = f.regs.find(e.name);
if (sit != f.regs.end()) {
const size_t s = E_rr(f, "MOVE", rd, sit->second);
const auto tf = f.reg_func.find(e.name);
set_func(f.code.data() + s,
tf != f.reg_func.end() ? tf->second : func_of_name("INT"));
return true;
}
const auto git = link_.global_index.find(e.name);
if (git != link_.global_index.end()) {
const size_t s = E_slot(f, "LOAD", rd, git->second);
set_func(f.code.data() + s, func_of_global(e.name));
return true;
}
return fail("no register or slot for '" + e.name + "'");
}
if (e.kind == ExprKind::Field) {
const InstFields* inst = instance_of(f.pou_name, e.name);
if (inst == nullptr) {
return fail("no instance '" + e.name + "' in '" + f.pou_name + "'");
}
const auto fit = inst->field_off.find(e.field);
if (fit == inst->field_off.end()) {
return fail("unknown field '" + e.field + "' for '" + e.name + "'");
}
const size_t lo = E_slot_off(f, "LOAD_OFF", rd, inst->slot, fit->second);
const auto ff = inst->field_func.find(e.field);
if (ff != inst->field_func.end()) {
set_func(f.code.data() + lo, ff->second);
}
return true;
}
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;
}
const size_t s = E_rrr(f, arith_name(e.kind), rd, l, r);
set_func(f.code.data() + s, expr_func(f, *e.lhs, *e.rhs));
return true;
}
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;
}
const size_t s = E_rrr(f, cmp_name(e.op), rd, l, r);
set_func(f.code.data() + s, expr_func(f, *e.lhs, *e.rhs));
return true;
}
if (e.kind == ExprKind::Not) {
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *e.operand, t)) {
return false;
}
E_rr(f, "NOT", rd, t);
return true;
}
if (e.kind == ExprKind::Neg) {
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *e.operand, t)) {
return false;
}
const size_t s = E_rr(f, "NEG", rd, t);
set_func(f.code.data() + s, expr_func(f, *e.operand, *e.operand));
return true;
}
if (e.kind == ExprKind::And || e.kind == ExprKind::Or) {
if (!compile_expr(f, *e.lhs, rd)) {
return false;
}
const char* jname = (e.kind == ExprKind::And) ? "JF" : "JT";
const size_t jmp_idx = E_jc(f, jname, rd, 0);
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *e.rhs, t)) {
return false;
}
E_rr(f, "MOVE", rd, t);
patch_jump(f, jmp_idx, f.code.size());
return true;
}
if (e.kind == ExprKind::Call) {
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) {
E_rr(f, "MOVE", static_cast<uint8_t>(1 + i), args[i]);
}
f.calls.push_back(static_cast<uint32_t>(fn_id));
E_call(f, static_cast<uint32_t>(fn_id));
E_rr(f, "MOVE", rd, 0);
return true;
}
return fail("expression not supported");
}
/// 表达式类型 func(算术/比较用):VarRef 取 reg_func/全局;
/// 嵌套表达式左右递归;字面量按装载类型(LitInt→INT、LitReal→REAL 等)。
uint8_t expr_func(const FuncCtx& f, const Expr& l, const Expr& r) const {
if (l.kind == ExprKind::VarRef) {
const auto tf = f.reg_func.find(l.name);
if (tf != f.reg_func.end()) {
return tf->second;
}
return func_of_global(l.name);
}
if (l.kind == ExprKind::LitBool || l.kind == ExprKind::LitInt ||
l.kind == ExprKind::LitTime || l.kind == ExprKind::LitReal ||
l.kind == ExprKind::LitDate || l.kind == ExprKind::LitTod ||
l.kind == ExprKind::LitDt) {
// 左是字面量 → 取右操作数(Typecheck 已定型同型)
if (r.kind == ExprKind::VarRef) {
const auto tf = f.reg_func.find(r.name);
if (tf != f.reg_func.end()) {
return tf->second;
}
return func_of_global(r.name);
}
}
return func_of_name("INT");
}
/// 全局变量 → func(查链接符号类型)。
uint8_t func_of_global(const std::string& name) const {
for (const Symbol& s : link_.globals) {
if (s.name == name) {
return func_of_name(s.type_name);
}
}
return func_of_name("INT");
}
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;
}
/**
* @brief 回填跳转偏移(字节单位)。
* @param f 函数编译态
* @param idx 跳转指令起始字节偏移
* @param target_off 目标字节偏移
* @details off 相对下一条:目标 = 当前起点 + 指令长 + off。
*/
void patch_jump(FuncCtx& f, size_t idx, size_t target_off) {
const int32_t off = static_cast<int32_t>(
static_cast<int64_t>(target_off) -
(static_cast<int64_t>(idx) + static_cast<int64_t>(instr_len(f.code.data() + idx))));
// 按前缀选偏移字段:JMP010010off@47..16JT/JF010011off@55..24
const std::string prefix = prefix_of(f.code.data() + idx);
if (prefix == "010010") {
set_off32(f.code.data() + idx, off);
} else {
set_off32_jc(f.code.data() + idx, off);
}
}
static const char* arith_name(ExprKind k) {
switch (k) {
case ExprKind::Add: return "ADD";
case ExprKind::Sub: return "SUB";
case ExprKind::Mul: return "MUL";
case ExprKind::Div: return "DIV";
default: return "ADD";
}
}
static const char* cmp_name(BinOp op) {
switch (op) {
case BinOp::Eq: return "CMP_EQ";
case BinOp::Ne: return "CMP_NE";
case BinOp::Lt: return "CMP_LT";
case BinOp::Le: return "CMP_LE";
case BinOp::Gt: return "CMP_GT";
case BinOp::Ge: return "CMP_GE";
default: return "CMP_EQ";
}
}
// ---- V2 布局:槽表 → 值段 ----
/**
* @brief 布局槽表与值段。
* @details 槽表条目:全局(声明序,每变量 1 条)+ FB 实例(每实例 1 条,
* 指向值区块起点)。值段:全局按类型 1/2/4/8 对齐 + 实例字段按
* 字段类型对齐连续排。初值写值段(实例字段 0)。
*/
bool layout_slots_values() {
uint32_t cur_addr = 0; // 值段当前偏移
for (const Symbol& s : link_.globals) {
const TypeMeta tm = type_meta(cfg_, s.type_name);
if (!tm) {
return fail("no type in machine.toml for '" + s.type_name + "'");
}
cur_addr = align_up(cur_addr, tm.width());
slots_.push_back(cur_addr);
values_.resize(cur_addr + tm.width(), 0);
bool has_init = false;
int64_t init = 0;
init_of(s.name, &has_init, &init);
const int64_t v = has_init ? init : 0;
write_value(values_, cur_addr, v, tm.width());
global_slot_[s.name] = static_cast<uint32_t>(slots_.size() - 1);
if (map_ != nullptr) {
map_->globals[s.name] = "slot=" + std::to_string(slots_.size() - 1) +
" off=" + std::to_string(cur_addr) +
" type=" + s.type_name +
" func=" + std::to_string(tm.func());
}
cur_addr += tm.width();
}
for (const LinkResult::PouScope& sc : link_.scopes) {
for (const Symbol& s : sc.syms) {
if (s.kind != SymbolKind::FbInstance) {
continue;
}
const auto lay = sc.fb_instances.find(s.name);
if (lay == sc.fb_instances.end()) {
return fail("no layout for instance '" + s.name + "'");
}
InstFields inst;
inst.slot = static_cast<uint32_t>(slots_.size());
inst.type_name = s.type_name;
int32_t off = 0;
for (const FbField& fd : lay->second.fields) {
const TypeMeta tm = type_meta(cfg_, fd.type_name);
if (!tm) {
return fail("no type for fb field '" + fd.type_name + "'");
}
off = static_cast<int32_t>(align_up(off, tm.width()));
inst.field_off[fd.name] = off;
inst.field_func[fd.name] = static_cast<uint8_t>(tm.func());
off += static_cast<int32_t>(tm.width());
}
// 实例块对齐到 max(字段对齐)
uint32_t max_align = 1;
for (const FbField& fd : lay->second.fields) {
const TypeMeta tm = type_meta(cfg_, fd.type_name);
max_align = max_align > tm.width() ? max_align : tm.width();
}
cur_addr = align_up(cur_addr, max_align);
slots_.push_back(cur_addr);
values_.resize(cur_addr + static_cast<uint32_t>(off), 0);
instances_[sc.name + "/" + s.name] = inst;
if (map_ != nullptr) {
std::string desc = "slot=" + std::to_string(inst.slot) +
" off=" + std::to_string(cur_addr) +
" fb=" + inst.type_name +
" size=" + std::to_string(off) + " ";
for (const auto& fo : inst.field_off) {
desc += fo.first + "@" + std::to_string(fo.second) + " ";
}
map_->instances[sc.name + "/" + s.name] = desc;
}
cur_addr += static_cast<uint32_t>(off);
}
}
return true;
}
static uint32_t align_up(uint32_t v, uint32_t a) {
if (a <= 1) {
return v;
}
return (v + a - 1) / a * a;
}
/// 按宽度写小端值(1/2/4/8 字节)。
static void write_value(std::vector<uint8_t>& buf, size_t off, int64_t v,
uint32_t width) {
for (uint32_t i = 0; i < width; ++i) {
buf[off + i] = static_cast<uint8_t>((static_cast<uint64_t>(v) >> (8 * i)) & 0xFFu);
}
}
const InstFields* instance_of(const std::string& pou,
const std::string& name) const {
const auto it = instances_.find(pou + "/" + name);
return it == instances_.end() ? nullptr : &it->second;
}
void init_of(const std::string& name, bool* has_init, int64_t* init) const {
for (const SourceUnit& u : units_) {
for (const VarBlock& b : u.ast.globals) {
for (const VarDecl& d : b.vars) {
if (d.name == name) {
*has_init = d.has_init;
*init = d.init_value;
return;
}
}
}
}
*has_init = false;
*init = 0;
}
/// 常量表:8B 原始值位模式去重(无 tag;LOADK 按 func 解释)。
uint16_t const_id(int64_t value) {
const uint64_t v = static_cast<uint64_t>(value);
for (size_t i = 0; i < consts_.size(); ++i) {
if (consts_[i] == v) {
return static_cast<uint16_t>(i);
}
}
consts_.push_back(v);
return static_cast<uint16_t>(consts_.size() - 1);
}
/**
* @brief max_stackMAIN + FUNCTION 调用图 DAG 最坏路径 Σ(12 + nregs×8)。
* @return true 成功;false(调用图有环 → "recursive call"
*/
bool compute_max_stack() {
const size_t n = funcs_.size();
// 邻接表:fn_id → 被调 fn_id 列表(权重 = 帧需求)
std::vector<std::vector<uint32_t>> edges(n);
for (size_t i = 0; i < n; ++i) {
for (const uint32_t c : funcs_[i].calls) {
if (c < n) {
edges[i].push_back(c);
}
}
}
// 环检测(DFS 三色)——防御(链接层已拒递归)
std::vector<int> color(n, 0);
std::vector<bool> in_stack(n, false);
std::function<bool(size_t)> dfs_cycle = [&](size_t u) -> bool {
color[u] = 1;
in_stack[u] = true;
for (const uint32_t v : edges[u]) {
if (color[v] == 0) {
if (dfs_cycle(v)) return true;
} else if (in_stack[v]) {
return true;
}
}
in_stack[u] = false;
return false;
};
for (size_t i = 0; i < n; ++i) {
if (color[i] == 0 && dfs_cycle(i)) {
return fail("recursive call detected");
}
}
// 最长路径(按字节权重)——DAG 上记忆化
std::vector<int64_t> memo(n, -1);
std::function<int64_t(size_t)> best = [&](size_t u) -> int64_t {
if (memo[u] >= 0) {
return memo[u];
}
int64_t b = 0;
for (const uint32_t v : edges[u]) {
const int64_t cand = 12 + static_cast<int64_t>(funcs_[v].nregs) * 8 +
best(v);
if (cand > b) {
b = cand;
}
}
memo[u] = b;
return b;
};
int64_t total = 0;
for (size_t i = 0; i < n; ++i) {
const int64_t need = 12 + static_cast<int64_t>(funcs_[i].nregs) * 8 + best(i);
if (need > total) {
total = need;
}
}
max_stack_ = static_cast<uint32_t>(total);
return true;
}
bool assemble_image() {
// 段布局(V2 头 128B + 常量表 8B + 函数表 12B + 字节码 + 槽表 + 值段 + SHA)
const uint32_t off_const = static_cast<uint32_t>(kHeaderSize);
const uint32_t off_funcs =
off_const + static_cast<uint32_t>(consts_.size()) * kConstEntrySize;
uint32_t off_code =
off_funcs + static_cast<uint32_t>(funcs_.size()) * kFuncRowSize;
uint32_t code_total = 0;
for (const FuncCtx& f : funcs_) {
code_total += static_cast<uint32_t>(f.code.size());
}
const uint32_t off_slots = align_up(off_code + code_total, 16);
const uint32_t off_values =
off_slots + static_cast<uint32_t>(slots_.size()) * kSlotRowSize;
const uint32_t off_end = off_values + static_cast<uint32_t>(values_.size()) +
static_cast<uint32_t>(kSha256Size);
std::vector<uint8_t>& b = *image_;
b.assign(off_end, 0);
put_le32(b, kOffMagic, kMagic);
put_le32(b, kOffVersion, kVersion);
put_le32(b, kOffCycleLimit, proj_.cycle_limit);
put_le32(b, kOffDtMs, proj_.dt_ms);
uint64_t hash = kFnvBasis;
if (!compute_project_hash(proj_, &hash, err_)) {
return false;
}
put_le64(b, kOffProjectHash, hash);
uint32_t entry = 0;
for (size_t i = 0; i < funcs_.size(); ++i) {
if (funcs_[i].name == "main") {
entry = static_cast<uint32_t>(i);
}
}
put_le32(b, kOffEntryFnId, entry);
put_le32(b, kOffNGlobals, static_cast<uint32_t>(link_.globals.size()));
put_le32(b, kOffNI, 0);
put_le32(b, kOffNQ, 0);
put_le32(b, kOffNM, 0);
put_le32(b, kOffNConsts, static_cast<uint32_t>(consts_.size()));
put_le32(b, kOffNFuncs, static_cast<uint32_t>(funcs_.size()));
put_le32(b, kOffConst, off_const);
put_le32(b, kOffFuncs, off_funcs);
put_le32(b, kOffCode, off_code);
put_le32(b, kOffSlots, off_slots);
put_le32(b, kOffValues, off_values);
put_le32(b, kOffNSlots, static_cast<uint32_t>(slots_.size()));
put_le32(b, kOffValuesSize, static_cast<uint32_t>(values_.size()));
put_le32(b, kOffMeta, 0);
put_le32(b, kOffMaxStack, max_stack_);
char mid[kModelIdSize];
fill_model_id(cfg_.model_name(), cfg_.version(), mid);
for (size_t i = 0; i < kModelIdSize; ++i) {
b[kOffModelId + i] = static_cast<uint8_t>(mid[i]);
}
// 常量表(8B 原始值)
for (size_t i = 0; i < consts_.size(); ++i) {
put_le64(b, off_const + i * kConstEntrySize, consts_[i]);
}
// 函数表(nregs/code_offset/code_len 字节)+ 字节码
uint32_t acc = 0;
for (size_t i = 0; i < funcs_.size(); ++i) {
const size_t o = off_funcs + i * kFuncRowSize;
put_le32(b, o + kFuncNregsOff, funcs_[i].nregs);
put_le32(b, o + kFuncCodeOff, acc);
put_le32(b, o + kFuncLenOff, static_cast<uint32_t>(funcs_[i].code.size()));
for (size_t j = 0; j < funcs_[i].code.size(); ++j) {
b[off_code + acc + j] = funcs_[i].code[j];
}
acc += static_cast<uint32_t>(funcs_[i].code.size());
}
// 槽表(addr:u32 + 预留:u32
for (size_t i = 0; i < slots_.size(); ++i) {
put_le32(b, off_slots + i * kSlotRowSize + kSlotAddrOff, slots_[i]);
put_le32(b, off_slots + i * kSlotRowSize + kSlotPadOff, 0);
}
// 值段
for (size_t i = 0; i < values_.size(); ++i) {
b[off_values + i] = values_[i];
}
// SHA-256 文件尾(对文件尾之前全部内容)
const size_t content_len = off_values + values_.size();
uint8_t digest[kSha256Size];
sha256(&b[0], content_len, digest);
for (size_t i = 0; i < kSha256Size; ++i) {
b[content_len + i] = digest[i];
}
return true;
}
static void put_le32(std::vector<uint8_t>& b, size_t off, uint32_t v) {
b[off + 0] = static_cast<uint8_t>(v & 0xFFu);
b[off + 1] = static_cast<uint8_t>((v >> 8) & 0xFFu);
b[off + 2] = static_cast<uint8_t>((v >> 16) & 0xFFu);
b[off + 3] = static_cast<uint8_t>((v >> 24) & 0xFFu);
}
static void put_le64(std::vector<uint8_t>& b, size_t off, uint64_t v) {
for (int i = 0; i < 8; ++i) {
b[off + i] = static_cast<uint8_t>((v >> (8 * i)) & 0xFFu);
}
}
// ---- 成员 ----
const Project& proj_;
const std::vector<SourceUnit>& units_;
const LinkResult& link_;
const MachineConfig& cfg_;
std::vector<uint8_t>* image_;
std::string* err_;
std::vector<FuncCtx> funcs_;
std::vector<uint64_t> consts_; ///< 常量表(8B 原始值,位模式去重)
std::map<std::string, bool> io_input_;
std::map<std::string, bool> io_output_;
std::vector<uint32_t> slots_; ///< 槽表 addr 列表
std::vector<uint8_t> values_; ///< 值段字节
std::map<std::string, uint32_t> global_slot_; ///< 全局名 → 槽号
std::map<std::string, InstFields> instances_; ///< "POU名/实例名" → 实例布局
const InstFields* inline_fields_ = nullptr; ///< 内联 FB 字段表
uint32_t max_stack_ = 0; ///< 栈区字节数
CodegenMap* map_ = nullptr; ///< 变量/槽映射输出(可空)
};
} // namespace
/**
* @brief 编译工程为 .stb 映像字节(对外入口,V2)。
* @param proj 工程定义
* @param units 全部源文件的 AST
* @param link 链接结果
* @param cfg 机器定义(machine.toml V2
* @param image 输出映像字节
* @param err 错误输出;可为 nullptr
* @return true 成功;falseerr 前缀 "codegen error"
*/
bool codegen_project(const Project& proj, const std::vector<SourceUnit>& units,
const LinkResult& link, const MachineConfig& cfg,
std::vector<uint8_t>* image, std::string* err, CodegenMap* map) {
Builder b(proj, units, link, cfg, image, err, map);
return b.run();
}
} // namespace compiler