Files
Interpreter/compiler/src/Codegen.cpp
T
Admin e187ed7b03 阶段 B 步骤 5:类型元数据配置化(TypeInfo)。
- TypeInfo.h/cpp:TypeMeta(配置别名 + 内建基元解析),查询大小写不敏感;
  提供 width/is_signed/is_float/tag/has_range/value_in_range
- Codegen:layout_data 初值按 type_meta 的基元宽度/浮点写(删除手写宽度分支);
  const_id 的 tag 改查 type_meta
- machine_test:+10 断言(BOOL 1B/range[0,1]/tag0、INT 2B 有符号、TIME 8B、大小写不敏感、未定义空)
- 验收:line1 224 字节 hash 0xc3fe4ae74ad45900 不变、ctest 12/12
2026-08-21 22:16:48 +08:00

799 lines
32 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 代码生成器
*/
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:
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----
Instr E_rr(const char* name, uint8_t rd, uint8_t rs) {
return pack(opc(name), rd, rs, 0);
}
Instr E_rrr(const char* name, uint8_t rd, uint8_t ra, uint8_t rb) {
return pack(opc(name), rd, ra, rb);
}
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));
}
Instr E_slot(const char* name, uint8_t rd, uint16_t slot) {
return E_imm(name, rd, slot);
}
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));
}
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));
}
Instr E_call(uint16_t fn_id) { return E_imm("CALL", 0, fn_id); }
Instr E_ret() { return pack(opc("RET"), 0, 0, 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;
}
// 每函数的编译态
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 实例:字段名 → 数据区槽号
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; }
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;
}
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);
}
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);
}
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 = 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;
}
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;
}
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;
}
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");
}
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;
}
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 已强校验存在)----
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";
}
}
const char* load_name(const std::string& name) const {
return io_input_.count(name) ? "LOAD_I" : "LOAD_GLOBAL";
}
const char* store_name(const std::string& name) const {
return io_output_.count(name) ? "STORE_Q" : "STORE_GLOBAL";
}
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;
}
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;
}
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;
}
/**
* @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);
}
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());
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);
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];
}
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<ConstEntry> consts_;
std::map<std::string, bool> io_input_;
std::map<std::string, bool> io_output_;
std::vector<uint8_t> data_;
std::map<std::string, InstFields> instances_;
const InstFields* inline_fields_ = nullptr;
};
} // namespace
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