Files
Interpreter/compiler/src/Codegen.cpp
T

975 lines
41 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 寄存器码生成(配置驱动;compiler 不依赖 isa
* @author
* @date 2026-08-21
*
* @details 设计说明(详见 Doc/compiler/寄存器码.md 与 Doc/isa/指令配置.md):
* - 指令 opcode / 类型 tag / FB 布局全部来自 machine.tomlMachineConfig
* - 帧/字面量/MOVE/RET;全局数据区;短路 AND/ORCMP 与 IFWHILE 与四则;
* FUNCTION 与 CALL(调用约定 r0/r1..r7/r8+
* - FB 实例 → 数据区实例块(跨周期持久);字段槽号 = 实例基槽 + 字段序号
* - 用户 FB 调用点内联展开;内建 FB → CAL_* <实例基槽>
* - 数据段每槽 8 字节定宽,指令 slot = 槽号
*
* 函数清单:
* - Builder::Builder (构造)存工程/源文件/链接结果/机器配置/输出,收集 io 绑定
* - Builder::run 布局数据区 → 逐 POU 建函数 → 拼映像
* - Builder::fail / opc 错误 / 按名查配置 opcode
* - E_rr / E_rrr / E_imm / E_slot / E_jmp / E_jc / E_call / E_ret 指令发射(配置 opcode
* - find_pou / find_fn_id 按名查 POU AST / fn_id
* - build_function 编译一个 POU(帧约定分配;FB 占位)
* - begin_stmt / alloc_temp 临时寄存器管理
* - compile_stmt 语句编译(赋值 / IF / WHILE / FB 调用)
* - compile_if / compile_while / compile_fb_call / compile_fb_inline
* - store_target / compile_expr 左值存储 / 表达式编译
* - arith_name / cmp_name / load_name / store_name 操作码名选择
* - patch_jump 回填跳转偏移(相对下一条)
* - layout_data / layout_fb_instances / instance_of / init_of
* - const_id 取常量表 id(tag 来自配置类型表)
* - assemble_image 拼头 + 常量表 + 函数表 + 字节码 + 数据段
* - codegen_project 对外入口
*/
#include "compiler/Codegen.h"
#include <cctype>
#include <cstdio>
#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 代码生成器(寄存器码)。
* @details 流程:布局数据区 → 布局 FB 实例 → 逐 POU 建函数 → 拼映像。
* 寄存器分配:r0 结果、r1..r7 参数(调用约定区,输入只读)、r8+ 变量/临时;
* 跳转偏移相对下一条(目标 = 当前 + 1 + off)。失败统一经 fail() 写
* err(前缀 "codegen error")。
*/
class Builder {
public:
/**
* @brief 构造生成器
* @param proj 工程定义(cycle_limit / dt_ms / 哈希用)
* @param units 全部源文件的 AST
* @param link 链接结果(POU 顺序 / 符号)
* @param cfg 机器定义(machine.toml 强校验后;指令 opcode / 类型 tag / FB 布局)
* @param image 输出映像字节
* @param err 错误输出;可为 nullptr(静默)
*/
Builder(const Project& proj, const std::vector<SourceUnit>& units,
const LinkResult& link, const MachineConfig& cfg,
std::vector<uint8_t>* image, std::string* err)
: proj_(proj), units_(units), link_(link), cfg_(cfg), image_(image), err_(err) {
// io 绑定分类(名已折小写;不创造变量,只影响操作码选择)
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;
}
}
}
/**
* @brief 布局数据区 → 布局 FB 实例 → 逐 POU 建函数 → 拼映像
* @return true 成功;falseerr 已写,前缀 "codegen error"
*/
bool run() {
if (!layout_data()) {
return false;
}
if (!layout_fb_instances()) {
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));
}
return assemble_image();
}
private:
/**
* @brief 记录错误并返回失败。
* @param msg 错误消息(自动加前缀 "codegen error: "
* @return 恒 false(便于 return fail(...) 连写)
*/
bool fail(const std::string& msg) {
if (err_) {
*err_ = "codegen error: " + msg;
}
return false;
}
/**
* @brief 按名查配置 opcodeMachineConfig 强校验保证存在)
* @param name 指令名(大写,如 "ADD"
* @return opcode(找不到返回 0,不应发生)
*/
uint8_t opc(const char* name) const {
const ConfigOp* op = cfg_.find_op(name);
return op ? static_cast<uint8_t>(op->opcode) : 0;
}
// ---- 指令发射(配置 opcode----
/// 双寄存器指令(rd, rs)。
Instr E_rr(const char* name, uint8_t rd, uint8_t rs) {
return pack(opc(name), rd, rs, 0);
}
/// 三寄存器指令(rd, ra, rb)。
Instr E_rrr(const char* name, uint8_t rd, uint8_t ra, uint8_t rb) {
return pack(opc(name), rd, ra, rb);
}
/// 立即数指令(imm 拆低/高字节入字段)。
Instr E_imm(const char* name, uint8_t rd, uint16_t imm) {
return pack(opc(name), rd, static_cast<uint8_t>(imm & 0xFFu),
static_cast<uint8_t>((imm >> 8) & 0xFFu));
}
/// 槽号指令(slot 复用 imm 字段,u16)。
Instr E_slot(const char* name, uint8_t rd, uint16_t slot) {
return E_imm(name, rd, slot);
}
/// 无条件跳转(off 相对下一条指令)。
Instr E_jmp(int16_t off) {
const uint16_t u = static_cast<uint16_t>(off);
return pack(opc("JMP"), 0, static_cast<uint8_t>(u & 0xFFu),
static_cast<uint8_t>((u >> 8) & 0xFFu));
}
/// 条件跳转(寄存器 r 为真则跳;off 相对下一条指令)。
Instr E_jc(const char* name, uint8_t r, int16_t off) {
const uint16_t u = static_cast<uint16_t>(off);
return pack(opc(name), r, static_cast<uint8_t>(u & 0xFFu),
static_cast<uint8_t>((u >> 8) & 0xFFu));
}
/// CALL 指令(fn_id 为函数表下标)。
Instr E_call(uint16_t fn_id) { return E_imm("CALL", 0, fn_id); }
/// RET 指令。
Instr E_ret() { return pack(opc("RET"), 0, 0, 0); }
/**
* @brief 按名查 POU AST。
* @param name POU 名
* @return 找到返回指针;否则 nullptr
*/
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 单个函数的编译状态。
* @details 寄存器分配(调用约定):结果 r0、输入 r1..r7、变量与临时 r8+。
*/
struct FuncCtx {
std::string name; ///< 函数名(函数表行用)
std::string pou_name; ///< 所属 POU 名(实例查找键)
std::vector<Instr> code; ///< 字节码
std::map<std::string, uint8_t> regs; ///< 变量名 → 帧寄存器
uint8_t nlocals = 0; ///< 变量区终点 = 临时寄存器起始
uint8_t nregs = 0; ///< 寄存器峰值(变量 + 临时)
uint8_t temp_used = 0; ///< 本语句已用临时数(语句结束清零)
bool is_function = false; ///< FUNCTION(结果 r0 / 输入只读)
std::string result_name; ///< FUNCTION 名(结果寄存器映射)
};
// FB 实例:字段名 → 数据区槽号
/**
* @brief FB 实例布局。
* @details 字段槽号 = 实例基槽 + 字段序号(跨周期持久)。
*/
struct InstFields {
std::map<std::string, uint32_t> field_addr; ///< 字段名 → 数据区槽号
uint32_t base = 0; ///< 实例基槽
std::string type_name; ///< 实例的 FB 类型名(内建 / 用户)
};
/// 语句开始:清零临时寄存器计数。
void begin_stmt(FuncCtx& f) { f.temp_used = 0; }
/**
* @brief 分配一个临时寄存器。
* @param f 函数编译态
* @return 临时寄存器号(nlocals + 已用数;随用抬高 nregs 峰值)
*/
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;
}
/**
* @brief 编译一个 POU
* @details 支持 PROGRAM / FUNCTION / FUNCTION_BLOCK
* FB 不生成业务字节码(调用点内联),只出空函数占位保持 fn_id 一致;
* 帧约定(见 Doc/compiler/寄存器码.md):结果 r0、输入 r1..r7(只读)、
* 变量与临时全部从 r8 起
*/
bool build_function(const POU& pou, FuncCtx* f) {
if (pou.kind == PouKind::FunctionBlock) {
f->nlocals = 8;
f->nregs = 8;
f->code.push_back(E_ret());
return true;
}
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) {
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->nlocals = r;
f->nregs = r;
for (const Stmt& st : pou.body) {
begin_stmt(*f);
if (!compile_stmt(*f, st)) {
return false;
}
}
f->code.push_back(E_ret());
return true;
}
/**
* @brief 编译一条语句。
* @param f 函数编译态
* @param st 语句 AST
* @return true 成功;falseerr 已写)
* @details 支持 IF / WHILE / FB 调用 / 赋值;其他语句报
* "statement not supported"。内联 FB 体内左值可能是实例字段
* (走 STORE_GLOBAL);写 FUNCTION 输入报 "cannot write function input"。
*/
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");
}
// 内联 FB 体内:左值可能是实例字段(STORE_GLOBAL
if (inline_fields_) {
const auto fit = inline_fields_->field_addr.find(st.target);
if (fit != inline_fields_->field_addr.end()) {
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *st.value, t)) {
return false;
}
f.code.push_back(E_slot("STORE_GLOBAL", t, fit->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);
}
/**
* @brief 编译 FB 调用语句。
* @param f 函数编译态
* @param st FB 调用语句 AST
* @return true 成功;falseerr 已写)
* @details 实参逐项编译后 STORE_GLOBAL 写实例字段;内建 FB
* 发 CAL_<TYPE> <实例基槽>;用户 FB 调用点内联展开。
* 错误:"no instance" / "unknown input" / "no FB type"。
*/
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_addr.find(a.name);
if (it == inst->field_addr.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;
}
f.code.push_back(E_slot("STORE_GLOBAL", t, it->second));
}
// 内建 FB:配置里必须有对应 op 行(强校验保证)→ 直接查配置 opcode
const std::string& tn = inst->type_name;
if (cfg_.find_op("CAL_" + uppercase_of(tn)) != nullptr &&
(tn == "ton" || tn == "tof" || tn == "tp" || tn == "ctu" ||
tn == "ctd" || tn == "ctud" || tn == "r_trig" || tn == "f_trig")) {
const std::string opname = "CAL_" + uppercase_of(tn);
f.code.push_back(E_slot(opname.c_str(), 0, inst->base));
return true;
}
const POU* fb = find_pou(inst->type_name);
if (fb == nullptr) {
return fail("no FB type '" + inst->type_name + "'");
}
return compile_fb_inline(f, *fb, *inst);
}
/// 转大写(内建 FB 名 → 操作码名)。
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;
}
/**
* @brief 用户 FB 调用点内联展开。
* @param f 函数编译态
* @param fb 用户 FB 的 POU AST
* @param inst 实例布局
* @return true 成功;falseerr 已写)
* @details 临时置 inline_fields_ 使体内变量引用改查实例字段
* (编译结束后恢复)。
*/
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;
}
/**
* @brief 编译 WHILE 循环。
* @param f 函数编译态
* @param st WHILE 语句 AST
* @return true 成功;falseerr 已写)
* @details 结构:条件 → JF 跳出 → 体 → JMP 回条件;偏移经
* patch_jump 回填(相对下一条)。
*/
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(E_jc("JF", t, 0));
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(E_jmp(0));
patch_jump(f, jmp_idx, loop);
patch_jump(f, jf_idx, f.code.size());
return true;
}
/**
* @brief 编译 IF / ELSIF / ELSE。
* @param f 函数编译态
* @param st IF 语句 AST
* @return true 成功;falseerr 已写)
* @details 每条分支:条件 → JF 跳下一条 → 体;分支间 JMP 跳
* 公共结束点;偏移经 patch_jump 回填。
*/
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 = f.code.size();
f.code.push_back(E_jc("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(E_jmp(0));
end_jmps.push_back(jmp_idx);
}
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;
}
/**
* @brief 编译左值存储(全局槽)。
* @param f 函数编译态
* @param target 目标名
* @param value 表达式
* @return true 成功;falseerr 已写)
* @details 目标槽号在 link_.global_index 中查;I/O 输入禁止写
* "cannot write to input");存储操作码经 store_name 选择
* I/O 输出 STORE_Q,其余 STORE_GLOBAL)。
* 错误:"no storage for ..." / "cannot write to input ..."。
*/
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;
}
f.code.push_back(E_slot(store_name(target), tmp, static_cast<uint16_t>(git->second)));
return true;
}
/**
* @brief 编译表达式到目标寄存器。
* @param f 函数编译态
* @param e 表达式 AST
* @param rd 目标寄存器号
* @return true 成功;falseerr 已写)
* @details 支持:字面量(LOADK,tag 来自配置类型表)、变量/字段引用、
* 四则、比较(cmp_name)、NOT、短路 AND/ORJF/JT)、函数调用
* (实参 MOVE 到 r1..r7 后 CALL,结果 MOVE 回 rd)。
* 错误:"no register or slot" / "no instance" / "unknown field" /
* "too many arguments (max 7)" / "no fn_id" / "expression not supported"。
*/
bool compile_expr(FuncCtx& f, const Expr& e, uint8_t rd) {
if (e.kind == ExprKind::LitBool || e.kind == ExprKind::LitInt ||
e.kind == ExprKind::LitTime) {
const char* type_name = e.kind == ExprKind::LitBool ? "BOOL"
: e.kind == ExprKind::LitInt ? "INT"
: "TIME";
f.code.push_back(E_imm("LOADK", rd, const_id(type_name, e.int_value)));
return true;
}
if (e.kind == ExprKind::VarRef) {
if (inline_fields_) {
const auto fit = inline_fields_->field_addr.find(e.name);
if (fit != inline_fields_->field_addr.end()) {
f.code.push_back(E_slot("LOAD_GLOBAL", rd, fit->second));
return true;
}
}
const auto sit = f.regs.find(e.name);
if (sit != f.regs.end()) {
f.code.push_back(E_rr("MOVE", rd, sit->second));
return true;
}
const auto git = link_.global_index.find(e.name);
if (git != link_.global_index.end()) {
f.code.push_back(
E_slot(load_name(e.name), rd, static_cast<uint16_t>(git->second)));
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_addr.find(e.field);
if (fit == inst->field_addr.end()) {
return fail("unknown field '" + e.field + "' for '" + e.name + "'");
}
f.code.push_back(E_slot("LOAD_GLOBAL", rd, fit->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;
}
f.code.push_back(E_rrr(arith_name(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)) {
return false;
}
const uint8_t r = alloc_temp(f);
if (!compile_expr(f, *e.rhs, r)) {
return false;
}
f.code.push_back(E_rrr(cmp_name(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)) {
return false;
}
f.code.push_back(E_rr("NOT", rd, t));
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 = f.code.size();
f.code.push_back(E_jc(jname, rd, 0));
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *e.rhs, t)) {
return false;
}
f.code.push_back(E_rr("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) {
f.code.push_back(E_rr("MOVE", static_cast<uint8_t>(1 + i), args[i]));
}
f.code.push_back(E_call(static_cast<uint16_t>(fn_id)));
f.code.push_back(E_rr("MOVE", rd, 0));
return true;
}
return fail("expression not supported");
}
/**
* @brief 按名查 fn_id(函数表下标)。
* @param name 函数名
* @return 找到返回下标;否则 -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;
}
/**
* @brief 回填跳转偏移。
* @param f 函数编译态
* @param idx 跳转指令下标
* @param target_idx 目标指令下标
* @details 偏移相对下一条:目标 = 当前 + 1 + offoff 为
* int16,重写指令的低 16 位)。
*/
void patch_jump(FuncCtx& f, size_t idx, size_t target_idx) {
const int16_t off = static_cast<int16_t>(
static_cast<int64_t>(target_idx) - (static_cast<int64_t>(idx) + 1));
const Instr w = f.code[idx];
const uint16_t u = static_cast<uint16_t>(off);
f.code[idx] = pack(op_of(w), rd_of(w), static_cast<uint8_t>(u & 0xFFu),
static_cast<uint8_t>((u >> 8) & 0xFFu));
}
// ---- 操作码名(MachineConfig 已强校验存在)----
/// 四则运算操作码名(Add→"ADD" 等;未知回退 "ADD")。
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";
}
}
/// 比较操作码名(Eq→"CMP_EQ" 等;未知回退 "CMP_EQ")。
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";
}
}
/// 加载操作码选择:I/O 输入走 LOAD_I,其余 LOAD_GLOBAL。
const char* load_name(const std::string& name) const {
return io_input_.count(name) ? "LOAD_I" : "LOAD_GLOBAL";
}
/// 存储操作码选择:I/O 输出走 STORE_Q,其余 STORE_GLOBAL。
const char* store_name(const std::string& name) const {
return io_output_.count(name) ? "STORE_Q" : "STORE_GLOBAL";
}
/**
* @brief 布局全局数据区。
* @return true 成功;falseerr 已写)
* @details 每槽 8 字节定宽小端,槽号 × 8 定位;初值按类型元数据
* 写(宽度 2 写 u16、8 写 u64、否则写 0/1;别名先经 type_meta 解析)。
* 错误:"data area exceeds slot range" / "no type in machine.toml" /
* "float initializer not supported yet"。
*/
bool layout_data() {
for (const Symbol& s : link_.globals) {
if (s.address > 0xFFFF) {
return fail("data area exceeds slot range");
}
bool has_init = false;
int64_t init = 0;
init_of(s.name, &has_init, &init);
const int64_t v = has_init ? init : 0;
const size_t off = static_cast<size_t>(s.address) * 8;
data_.resize(off + 8, 0);
// 初值按类型元数据写(配置别名 → 基元宽度/浮点)
const TypeMeta tm = type_meta(cfg_, s.type_name);
if (!tm) {
return fail("no type in machine.toml for '" + s.type_name + "'");
}
if (tm.is_float()) {
return fail("float initializer not supported yet");
}
if (tm.width() == 2) {
const uint16_t iv = static_cast<uint16_t>(v);
data_[off] = static_cast<uint8_t>(iv & 0xFFu);
data_[off + 1] = static_cast<uint8_t>((iv >> 8) & 0xFFu);
} else if (tm.width() == 8) {
const uint64_t tv = static_cast<uint64_t>(v);
for (int i = 0; i < 8; ++i) {
data_[off + i] = static_cast<uint8_t>((tv >> (8 * i)) & 0xFFu);
}
} else {
data_[off] = v ? 1 : 0;
}
}
return true;
}
/**
* @brief 布局 FB 实例块(全局区之后顺序追加)。
* @return true 成功;falseerr 已写)
* @details 实例 key 为 "POU名/实例名";字段槽号 = 基槽 + 字段序号。
* 错误:"no layout for instance ..."。
*/
bool layout_fb_instances() {
uint32_t cur = static_cast<uint32_t>(data_.size() / 8);
bool any = false;
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.base = cur;
inst.type_name = s.type_name;
for (size_t i = 0; i < lay->second.fields.size(); ++i) {
inst.field_addr[lay->second.fields[i].name] = cur + i;
}
cur += static_cast<uint32_t>(lay->second.fields.size());
instances_[sc.name + "/" + s.name] = inst;
any = true;
}
}
if (any) {
data_.resize(static_cast<size_t>(cur) * 8, 0);
}
return true;
}
/// 按 "POU名/实例名" 查实例布局;找不到返回 nullptr。
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;
}
/**
* @brief 查全局变量初值(源文件 globals 段)。
* @param name 变量名
* @param has_init 输出:是否有初值
* @param init 输出:初值
*/
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;
}
/**
* @brief 取常量表 id(无则追加)
* @param type_name 语言类型名("BOOL"/"INT"/"TIME")→ 配置 tag(契约)
* @param value 常量值
* @return const_idu16
*/
uint16_t const_id(const char* type_name, int64_t value) {
const TypeMeta tm = type_meta(cfg_, type_name);
const uint32_t tag = tm ? tm.tag() : 0;
for (size_t i = 0; i < consts_.size(); ++i) {
if (consts_[i].tag == tag && consts_[i].value == static_cast<uint64_t>(value)) {
return static_cast<uint16_t>(i);
}
}
consts_.push_back({tag, static_cast<uint64_t>(value)});
return static_cast<uint16_t>(consts_.size() - 1);
}
/**
* @brief 拼装最终映像。
* @return true 成功;falseerr 已写)
* @details 布局:头(104) + 常量表 + 函数表 + 字节码 + 数据段 +
* SHA-256 文件尾;型号标识 @72;头内各段偏移(52..68)与函数表
* code_offset 小端写入;SHA-256 对文件尾之前全部内容计算。
*/
bool assemble_image() {
const uint32_t off_const = static_cast<uint32_t>(kHeaderSize);
const uint32_t off_funcs = off_const +
static_cast<uint32_t>(consts_.size()) * static_cast<uint32_t>(kConstEntrySize);
uint32_t off_code = off_funcs +
static_cast<uint32_t>(funcs_.size()) * static_cast<uint32_t>(kFuncRowSize);
uint32_t code_total = 0;
for (const FuncCtx& f : funcs_) {
code_total += static_cast<uint32_t>(f.code.size()) * 4;
}
const uint32_t off_data = off_code + code_total;
const uint32_t off_end = off_data + static_cast<uint32_t>(data_.size()) +
static_cast<uint32_t>(kSha256Size);
std::vector<uint8_t>& b = *image_;
b.assign(off_end, 0);
put_le32(b, 0, kMagic);
put_le32(b, 4, kVersion);
put_le32(b, 8, proj_.cycle_limit);
put_le32(b, 12, proj_.dt_ms);
uint64_t hash = kFnvBasis;
if (!compute_project_hash(proj_, &hash, err_)) {
return false;
}
put_le64(b, 16, 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, 24, entry);
put_le32(b, 28, static_cast<uint32_t>(link_.globals.size()));
put_le32(b, 32, 0);
put_le32(b, 36, 0);
put_le32(b, 40, 0);
put_le32(b, 44, static_cast<uint32_t>(consts_.size()));
put_le32(b, 48, static_cast<uint32_t>(funcs_.size()));
put_le32(b, 52, off_const);
put_le32(b, 56, off_funcs);
put_le32(b, 60, off_code);
put_le32(b, 64, off_data);
put_le32(b, 68, off_data);
// 型号标识[32](偏移 7212.13 修订)
char mid[kModelIdSize];
fill_model_id(cfg_.model_name(), cfg_.version(), mid);
for (size_t i = 0; i < kModelIdSize; ++i) {
b[72 + i] = static_cast<uint8_t>(mid[i]);
}
for (size_t i = 0; i < consts_.size(); ++i) {
const size_t o = off_const + i * kConstEntrySize;
put_le32(b, o, consts_[i].tag);
put_le64(b, o + 4, consts_[i].value);
}
uint32_t c = off_code;
for (size_t i = 0; i < funcs_.size(); ++i) {
const FuncCtx& f = funcs_[i];
const size_t o = off_funcs + i * kFuncRowSize;
put_le32(b, o, f.nregs);
put_le32(b, o + 8, static_cast<uint32_t>(f.code.size()));
for (const Instr in : f.code) {
put_le32(b, c, in);
c += 4;
}
}
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 + 4, acc);
acc += static_cast<uint32_t>(funcs_[i].code.size()) * 4;
}
for (size_t i = 0; i < data_.size(); ++i) {
b[off_data + i] = data_[i];
}
// 文件尾 SHA-256(对文件尾之前全部内容计算)
const size_t content_len = off_data + data_.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;
}
/// 小端写 32 位。
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);
}
/// 小端写 64 位。
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_; ///< 工程定义(cycle_limit / dt_ms / 哈希)
const std::vector<SourceUnit>& units_; ///< 全部源文件 AST
const LinkResult& link_; ///< 链接结果(POU 顺序 / 全局符号 / FB 布局)
const MachineConfig& cfg_; ///< 机器定义(opcode / tag / FB 布局)
std::vector<uint8_t>* image_; ///< 输出映像字节
std::string* err_; ///< 错误输出(可为 nullptr
std::vector<FuncCtx> funcs_; ///< 已编译函数
std::vector<ConstEntry> consts_; ///< 常量表(tag + value
std::map<std::string, bool> io_input_; ///< I/O 输入变量名集合(小写;只影响操作码选择)
std::map<std::string, bool> io_output_; ///< I/O 输出变量名集合(小写;只影响操作码选择)
std::vector<uint8_t> data_; ///< 全局数据区字节(每槽 8 字节定宽)
std::map<std::string, InstFields> instances_; ///< "POU名/实例名" → 实例布局
const InstFields* inline_fields_ = nullptr; ///< 内联 FB 字段表(非空 = 正在内联 FB 体)
};
} // namespace
/**
* @brief 编译工程为 .stb 映像字节(对外入口)。
* @param proj 工程定义
* @param units 全部源文件的 AST
* @param link 链接结果
* @param cfg 机器定义(machine.toml
* @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) {
Builder b(proj, units, link, cfg, image, err);
return b.run();
}
} // namespace compiler