Files
Interpreter/compiler/src/Codegen.cpp
T
Admin 26256a36b9 扩充内置功能块为 8 个:TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG。
- isa:CAL_* 操作码连续占 24..31(CALL/RET 后移为 32/33,kOpCount=34);disasm 支持 5 个新操作码
- 词法/语法/链接:新 8 个关键字与冻结布局(TP=in/pt/q/et、CTD=cd/ld/pv/q/cv、
  CTUD=cu/cd/r/lu/pv/qu/qd/cv、R_TRIG/F_TRIG=clk/q)
- VM:do_cal 全 8 个语义(TP 脉冲、CTD 递减、CTUD 双向+装载、R_TRIG 上升沿、F_TRIG 下降沿);
  edge_prev_ 每槽 2 字节存边沿上次输入;exec_one 分发补 5 个新操作码
- 验证:TP 30ms 脉冲 2 周期、CTD 装载+递减到 q=1、CTUD cu/cd 双向、R/F_TRIG 交替;
  ctest 11/11 全绿
- 文档:指令与映像/词法/初步计划/符号表与链接/指令执行/扫描周期/使用说明 同步
2026-08-21 20:38:27 +08:00

936 lines
39 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 寄存器码生成(12.8,切片 7:+ FB 实例、内联展开、CAL_*)
* @author
* @date 2026-08-21
*
* @details 设计说明(详见 Doc/compiler/寄存器码.md):
* - 切片 1~6:帧/字面量/MOVE/RET;全局数据区;短路 AND/ORCMP 与 IFWHILE 与四则;
* FUNCTION 与 CALL(调用约定 r0/r1..r7/r8+)。
* - 切片 7:FB 实例 → 数据区实例块(跨周期持久);字段槽号 = 实例基槽 + 字段序号
* (编译期算死);字段读写走 LOAD_GLOBAL / STORE_GLOBAL
* 内建 TON/TOF/CTU → 实参写字段后 CAL_* <实例基槽>
* 用户 FB → 调用点内联展开(实参写字段 → FB 体以实例字段为变量编译)
* - 方案 a(12.9 前置):数据段**每槽 8 字节定宽**,指令 slot = 槽号(无映射)
*
* 函数清单:
* - put_le32 / put_le64 小端写入映像缓冲
* - Builder::Builder (构造)存工程/源文件/链接结果/输出,收集 io 绑定分类
* - Builder::run 布局数据区(全局 + 实例)→ 逐 POU 建函数 → 拼映像
* - Builder::fail 组装 "codegen error: <msg>" 返回 false
* - find_pou / find_fn_id 按名查 POU AST / fn_id
* - build_function 编译一个 POUPROGRAM/FUNCTION/FB 骨架;帧约定分配)
* - begin_stmt / alloc_temp 临时寄存器:语句内递增、语句结束复用基址
* - compile_stmt 语句编译(赋值 / IF / WHILE / FB 调用)
* - compile_if / compile_while IF 链 / WHILE 回环
* - compile_fb_call FB 调用:内建 CAL_* 或用户内联展开
* - compile_fb_inline 内联编译用户 FB 体(实例字段为变量)
* - store_target 赋值左值:STORE_* 到全局槽(或内联模式实例字段)
* - compile_expr 表达式编译到寄存器(含 Field 字段读)
* - cmp_op / arith_op / load_op / store_op 操作码选择
* - global_offset 槽号恒等(方案 a 无映射)
* - instance_of 实例 → 字段槽号表
* - patch_jump 回填跳转偏移(相对下一条指令)
* - layout_data / layout_fb_instances / init_of 数据区布局(8 字节定宽槽)
* - const_id 取常量表 id(无则追加)
* - assemble_image 拼头 + 常量表 + 函数表 + 字节码 + 数据段
* - codegen_project 对外入口
*/
#include "compiler/Codegen.h"
#include <cctype>
#include <cstdio>
#include <map>
#include <string>
#include <vector>
#include "isa/Encode.h"
#include "isa/Image.h"
#include "isa/Instr.h"
#include "isa/Types.h"
namespace compiler {
namespace {
// ---- 小端写入(Image.cpp 内部实现不可见,这里自带最小版)----
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);
}
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);
}
}
/**
* @brief 代码生成器(切片 1)
*/
class Builder {
public:
/**
* @brief 构造生成器
* @param proj 工程定义(cycle_limit / dt_ms / 哈希用)
* @param units 全部源文件的 AST
* @param link 链接结果(POU 顺序 / 符号)
* @param image 输出映像字节
* @param err 错误输出;可为 nullptr(静默)
*/
Builder(const Project& proj, const std::vector<SourceUnit>& units,
const LinkResult& link, std::vector<uint8_t>* image, std::string* err)
: proj_(proj), units_(units), link_(link), image_(image), err_(err) {
// io 绑定分类(名已折小写;不创造变量,只影响操作码选择)
for (const isa::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 建函数 → 拼映像
* @details 偏移先定(函数编译引用 global_offset / instance_of
* @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 组装错误消息并返回 false
* @param msg 错误描述(不含前缀)
* @return 恒 false
*/
bool fail(const std::string& msg) {
if (err_) {
*err_ = "codegen error: " + msg;
}
return false;
}
/**
* @brief 按名查 POU AST
* @param name POU 名(小写)
* @return POU 指针;未找到返回 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;
}
// 每函数的编译态
struct FuncCtx {
std::string name;
std::string pou_name; // 所属 POU 名(实例查找键)
std::vector<isa::Instr> code; // 字节码(函数表 code_offset 相对此段)
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 类型名(内建 / 用户)
};
/**
* @brief 语句开始:临时寄存器基址复用
* @param f 当前函数
*/
void begin_stmt(FuncCtx& f) { f.temp_used = 0; }
/**
* @brief 分配一个临时寄存器(语句内递增)
* @param f 当前函数
* @return 临时寄存器号(可能更新 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 起
* @param pou POU AST
* @param f 输出函数编译态
* @return true 成功;falseerr 已写)
*/
bool build_function(const POU& pou, FuncCtx* f) {
if (pou.kind == PouKind::FunctionBlock) {
// FB 内联展开(见 compile_fb_call),此处只占 fn_id
f->nlocals = 8;
f->nregs = 8;
f->code.push_back(isa::enc_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) {
// 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) {
// FB 实例走数据区(layout_fb_instances),不占帧寄存器
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(isa::enc_ret());
return true;
}
/**
* @brief 编译一条语句(切片 4:赋值 / IF)
* @param f 当前函数
* @param st 语句 AST
* @return true 成功;falseerr 已写)
*/
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 in slice 7");
}
// 内联 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(isa::enc_slot(isa::Op::STORE_GLOBAL, t, fit->second));
return true;
}
}
const auto it = f.regs.find(st.target);
if (it == f.regs.end()) {
// 全局 / 外部左值 → 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);
}
/**
* @brief 编译 FB 调用
* @details 实参求值到临时 → STORE_GLOBAL 到实例字段;
* 内建 TON/TOF/CTU → CAL_* <实例偏移>
* 用户 FB → compile_fb_inline 内联展开
* @param f 当前函数
* @param st FbCall 语句 AST
* @return true 成功;falseerr 已写)
*/
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(isa::enc_slot(isa::Op::STORE_GLOBAL, t, it->second));
}
if (inst->type_name == "ton" || inst->type_name == "tof" ||
inst->type_name == "tp" || inst->type_name == "ctu" ||
inst->type_name == "ctd" || inst->type_name == "ctud" ||
inst->type_name == "r_trig" || inst->type_name == "f_trig") {
const isa::Op op =
inst->type_name == "ton" ? isa::Op::CAL_TON
: inst->type_name == "tof" ? isa::Op::CAL_TOF
: inst->type_name == "tp" ? isa::Op::CAL_TP
: inst->type_name == "ctu" ? isa::Op::CAL_CTU
: inst->type_name == "ctd" ? isa::Op::CAL_CTD
: inst->type_name == "ctud" ? isa::Op::CAL_CTUD
: inst->type_name == "r_trig" ? isa::Op::CAL_R_TRIG
: isa::Op::CAL_F_TRIG;
f.code.push_back(isa::enc_slot(op, 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);
}
/**
* @brief 内联编译用户 FB 体
* @details 切到实例字段上下文(VarRef/赋值左值映射到字段地址);
* 表达式临时与调用方共用 r8+;字段读写全走 LOAD/STORE_GLOBAL
* @param f 当前函数
* @param fb FB 类型 POU
* @param inst 本实例字段表
* @return true 成功;falseerr 已写)
*/
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
* @details L_loop: <cond→t> JF t, L_end <body> JMP L_loop L_end:
* 条件 JF 与回环 JMP 双回填
* @param f 当前函数
* @param st WHILE 语句 AST
* @return true 成功;falseerr 已写)
*/
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(isa::enc_jc(isa::Op::JF, t, 0)); // 假 → L_end
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(isa::enc_jmp(0)); // 回环
patch_jump(f, jmp_idx, loop);
patch_jump(f, jf_idx, f.code.size());
return true;
}
/**
* @brief 编译 IF / ELSIF / ELSE
* @details 每分支:<cond→t> JF t, L_next → <body> → JMP L_end
* 最后一个分支无 else 时 JF 直接落 L_end(不补 JMP
* @param f 当前函数
* @param st IF 语句 AST
* @return true 成功;falseerr 已写)
*/
bool compile_if(FuncCtx& f, const Stmt& st) {
// 条件链:IF 体 + ELSIF 各体;ELSE 体可选
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; // 各分支尾部 JMP(回填到 L_end)
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(isa::enc_jc(isa::Op::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(isa::enc_jmp(0)); // 分支尾部 → L_end
end_jmps.push_back(jmp_idx);
}
patch_jump(f, jf_idx, f.code.size()); // 回填 JF 到下一分支起点
}
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 赋值左值:帧寄存器直写或 STORE_* 到全局槽
* @param f 当前函数
* @param target 左值名(全局 / 外部)
* @param value 右值表达式
* @return true 成功;falseerr 已写)
*/
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;
}
const uint16_t slot = global_offset(git->second);
f.code.push_back(isa::enc_slot(store_op(target), tmp, slot));
return true;
}
/**
* @brief 表达式编译到目标寄存器
* @details 字面量 → LOADK;帧变量 → MOVE;全局/外部 → LOAD_*io.input 用 LOAD_I);
* NOT → 求值后 NOT rd, tAND/OR → 短路跳转(JF/JT 跳过右侧)
* @param f 当前函数
* @param e 表达式 AST
* @param rd 目标寄存器
* @return true 成功;falseerr 已写)
*/
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 isa::types::TypeTag tag =
e.kind == ExprKind::LitBool ? isa::types::Bool
: e.kind == ExprKind::LitInt ? isa::types::Int
: isa::types::Time;
f.code.push_back(
isa::enc_imm(isa::Op::LOADK, rd, const_id(tag, e.int_value)));
return true;
}
if (e.kind == ExprKind::VarRef) {
// 内联 FB 体内:字段优先(实例字段为变量)
if (inline_fields_) {
const auto fit = inline_fields_->field_addr.find(e.name);
if (fit != inline_fields_->field_addr.end()) {
f.code.push_back(isa::enc_slot(isa::Op::LOAD_GLOBAL, rd, fit->second));
return true;
}
}
const auto sit = f.regs.find(e.name);
if (sit != f.regs.end()) {
f.code.push_back(isa::enc_rr(isa::Op::MOVE, rd, sit->second));
return true;
}
const auto git = link_.global_index.find(e.name);
if (git != link_.global_index.end()) {
const uint16_t slot = global_offset(git->second);
f.code.push_back(isa::enc_slot(load_op(e.name), rd, slot));
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(isa::enc_slot(isa::Op::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(isa::enc_rrr(arith_op(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(isa::enc_rrr(cmp_op(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(isa::enc_rr(isa::Op::NOT, rd, t));
return true;
}
if (e.kind == ExprKind::And || e.kind == ExprKind::Or) {
// 短路:左侧结果在 rd;AND 为假 / OR 为真时跳过右侧
if (!compile_expr(f, *e.lhs, rd)) {
return false;
}
const isa::Op jop = (e.kind == ExprKind::And) ? isa::Op::JF : isa::Op::JT;
const size_t jmp_idx = f.code.size();
f.code.push_back(isa::enc_jc(jop, rd, 0)); // 偏移留洞,稍后回填
const uint8_t t = alloc_temp(f);
if (!compile_expr(f, *e.rhs, t)) {
return false;
}
f.code.push_back(isa::enc_rr(isa::Op::MOVE, rd, t));
patch_jump(f, jmp_idx, f.code.size());
return true;
}
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;
}
/**
* @brief 回填跳转偏移(相对下一条指令)
* @param f 当前函数
* @param idx 跳转指令下标(占位)
* @param target_idx 目标指令下标
*/
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 isa::Instr w = f.code[idx];
f.code[idx] = isa::pack(isa::op(w), isa::rd(w),
static_cast<uint8_t>(static_cast<uint16_t>(off) & 0xFFu),
static_cast<uint8_t>((static_cast<uint16_t>(off) >> 8) & 0xFFu));
}
/**
* @brief 算术运算 → 操作码
* @param k 表达式种类(Add/Sub/Mul/Div
* @return 对应操作码
*/
isa::Op arith_op(ExprKind k) const {
switch (k) {
case ExprKind::Add: return isa::Op::ADD;
case ExprKind::Sub: return isa::Op::SUB;
case ExprKind::Mul: return isa::Op::MUL;
case ExprKind::Div: return isa::Op::DIV;
default: return isa::Op::ADD;
}
}
/**
* @brief 比较符 → CMP_xx 操作码
* @param op 比较运算
* @return 对应操作码(CMP_EQ / CMP_NE / CMP_LT / CMP_LE / CMP_GT / CMP_GE
*/
isa::Op cmp_op(BinOp op) const {
switch (op) {
case BinOp::Eq: return isa::Op::CMP_EQ;
case BinOp::Ne: return isa::Op::CMP_NE;
case BinOp::Lt: return isa::Op::CMP_LT;
case BinOp::Le: return isa::Op::CMP_LE;
case BinOp::Gt: return isa::Op::CMP_GT;
case BinOp::Ge: return isa::Op::CMP_GE;
default: return isa::Op::CMP_EQ; // Add/Sub/Mul/Div 不经此函数
}
}
/**
* @brief 按 io 绑定选读取操作码
* @param name 变量名(小写)
* @return io.input 绑定 → LOAD_I;否则 LOAD_GLOBAL
*/
isa::Op load_op(const std::string& name) const {
return io_input_.count(name) ? isa::Op::LOAD_I : isa::Op::LOAD_GLOBAL;
}
/**
* @brief 按 io 绑定选写入操作码
* @param name 变量名(小写)
* @return io.output 绑定 → STORE_Q;否则 STORE_GLOBAL
*/
isa::Op store_op(const std::string& name) const {
return io_output_.count(name) ? isa::Op::STORE_Q : isa::Op::STORE_GLOBAL;
}
/**
* @brief 逻辑槽号(符号表 address)即指令 slot(方案 a:8 字节定宽,无映射)
* @param slot 全局逻辑槽号
* @return 同值(u16 可容,layout_data 已校验)
*/
uint16_t global_offset(uint32_t slot) const {
return static_cast<uint16_t>(slot);
}
/**
* @brief 布局全局数据区:每槽 8 字节定宽(方案 a)
* @details 槽号 = 符号表 address(声明序,12.6);
* BOOL 用低 1 字节、INT 用低 2 字节(小端)、TIME 全 8 字节;
* 初值取自 GVL 声明的 has_init(无则 0
* @return true 成功;false(槽号越界,err 已写)
*/
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);
if (s.type_name == "int") {
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 (s.type_name == "time") {
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 { // bool
data_[off] = v ? 1 : 0;
}
}
return true;
}
/**
* @brief 布局 FB 实例块:数据区全局之后,按 POU 收集序 / 实例声明序
* @details 每实例:字段按布局段序各占一槽(8 字节定宽),
* 字段地址 = 实例基槽 + 字段序号;实例基槽 = 累计槽数
* @return true 成功;falseerr 已写)
*/
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; // 实例基槽(CAL_* 操作数)
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;
}
/**
* @brief 按 POU + 实例名查实例字段表
* @param pou POU 名
* @param name 实例名
* @return 实例字段表指针;未找到返回 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 查全局声明的初值(AST;gvl 文件顶层段,声明序 = 槽号序)
* @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 tag 类型标记(BOOL/INT/TIME
* @param value 常量值
* @return const_idu16
*/
uint16_t const_id(isa::types::TypeTag tag, int64_t value) {
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 拼映像:头 + 常量表 + 函数表 + 字节码 + 数据段
* @details 段序:const → funcs → code → fb(空)→ data
* 数据段放全局初值(layout_data 已生成字节)
* @return true 成功;falseerr 已写)
*/
bool assemble_image() {
const uint32_t off_const = isa::kHeaderSize;
const uint32_t off_funcs = off_const +
static_cast<uint32_t>(consts_.size()) * isa::kConstEntrySize;
uint32_t off_code = off_funcs +
static_cast<uint32_t>(funcs_.size()) * isa::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, isa::kMagic);
put_le32(b, 4, isa::kVersion);
put_le32(b, 8, proj_.cycle_limit);
put_le32(b, 12, proj_.dt_ms);
uint64_t hash = isa::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())); // n_globals
put_le32(b, 32, 0); // n_i
put_le32(b, 36, 0); // n_q
put_le32(b, 40, 0); // n_m
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); // offset_fb(空,与数据段起点相同)
put_le32(b, 68, off_data); // offset_data
for (size_t i = 0; i < consts_.size(); ++i) {
const size_t o = off_const + i * isa::kConstEntrySize;
put_le32(b, o, static_cast<uint32_t>(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 * isa::kFuncRowSize;
put_le32(b, o, f.nregs);
put_le32(b, o + 8, static_cast<uint32_t>(f.code.size()));
for (const isa::Instr in : f.code) {
put_le32(b, c, in);
c += 4;
}
}
// code_offset 回填(相对字节码段起点)
uint32_t acc = 0;
for (size_t i = 0; i < funcs_.size(); ++i) {
const size_t o = off_funcs + i * isa::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;
}
// ---- 成员 ----
const Project& proj_;
const std::vector<SourceUnit>& units_;
const LinkResult& link_;
std::vector<uint8_t>* image_;
std::string* err_;
std::vector<FuncCtx> funcs_;
std::vector<isa::ConstEntry> consts_;
std::map<std::string, bool> io_input_; // io.input 绑定名(小写)
std::map<std::string, bool> io_output_; // io.output 绑定名(小写)
std::vector<uint8_t> data_; // 数据区(8 字节定宽槽:全局 + FB 实例块)
std::map<std::string, InstFields> instances_; // "POU/实例名" → 字段槽号
const InstFields* inline_fields_ = nullptr; // 内联 FB 体字段上下文(可空)
};
} // namespace
/**
* @brief 编译工程为 .stb 映像字节(对外入口)
* @param proj 工程定义
* @param units 全部源文件的 AST
* @param link 链接结果(在类型检查成功后调用)
* @param image 输出映像字节
* @param err 错误输出;可为 nullptr(静默)
* @return true 成功;false 失败(err 前缀 "codegen error"
*/
bool codegen_project(const Project& proj, const std::vector<SourceUnit>& units,
const LinkResult& link, std::vector<uint8_t>* image,
std::string* err) {
Builder b(proj, units, link, image, err);
return b.run();
}
} // namespace compiler