12.9 步骤 0:数据段改 8 字节定宽槽(方案 a)。

- 指令与映像.md / 寄存器码.md:slot 语义 = 槽号(数据偏移 = 槽号 × 8),取消变宽对齐与 global_offset 映射
- Codegen.cpp:删除 width_of/align_up/global_offsets_;layout_data/layout_fb_instances 按槽号布局(字段 = 槽序号)
- stb文件格式.md:line1 真实字节更新(224B、56B 数据段、starter 槽 4/5/6)
- codegen_test:断言更新(槽号化 + 数据区字节数),189 断言 9/9 全绿
This commit is contained in:
2026-08-21 20:01:21 +08:00
parent cd417cd27b
commit ce948451e0
5 changed files with 78 additions and 117 deletions
+37 -79
View File
@@ -7,15 +7,14 @@
* @details 设计说明(详见 Doc/compiler/寄存器码.md):
* - 切片 1~6:帧/字面量/MOVE/RET;全局数据区;短路 AND/ORCMP 与 IFWHILE 与四则;
* FUNCTION 与 CALL(调用约定 r0/r1..r7/r8+)。
* - 切片 7:FB 实例 → 数据区实例块(跨周期持久);字段地址 = 实例基 + 字段偏移
* - 切片 7:FB 实例 → 数据区实例块(跨周期持久);字段槽号 = 实例基 + 字段序号
* (编译期算死);字段读写走 LOAD_GLOBAL / STORE_GLOBAL
* 内建 TON/TOF/CTU → 实参写字段后 CAL_* <实例偏移>
* 内建 TON/TOF/CTU → 实参写字段后 CAL_* <实例基槽>
* 用户 FB → 调用点内联展开(实参写字段 → FB 体以实例字段为变量编译)
* - 其余构造报 codegen error
* - 方案 a(12.9 前置):数据段**每槽 8 字节定宽**,指令 slot = 槽号(无映射)
*
* 函数清单:
* - put_le32 / put_le64 小端写入映像缓冲
* - align_up / width_of 对齐与槽宽
* - Builder::Builder (构造)存工程/源文件/链接结果/输出,收集 io 绑定分类
* - Builder::run 布局数据区(全局 + 实例)→ 逐 POU 建函数 → 拼映像
* - Builder::fail 组装 "codegen error: <msg>" 返回 false
@@ -29,9 +28,10 @@
* - store_target 赋值左值:STORE_* 到全局槽(或内联模式实例字段)
* - compile_expr 表达式编译到寄存器(含 Field 字段读)
* - cmp_op / arith_op / load_op / store_op 操作码选择
* - global_offset / instance_of 槽号 → 偏移 / 实例 → 字段地址
* - global_offset 槽号恒等(方案 a 无映射)
* - instance_of 实例 → 字段槽号表
* - patch_jump 回填跳转偏移(相对下一条指令)
* - layout_data / layout_fb_instances / init_of 数据区布局
* - layout_data / layout_fb_instances / init_of 数据区布局8 字节定宽槽)
* - const_id 取常量表 id(无则追加)
* - assemble_image 拼头 + 常量表 + 函数表 + 字节码 + 数据段
* - codegen_project 对外入口
@@ -68,38 +68,6 @@ namespace {
}
}
/**
* @brief 按 2 的幂宽度向上对齐
* @param v 当前偏移
* @param w 对齐宽度(1/2/8
* @return 对齐后的偏移
*/
uint32_t align_up(uint32_t v, uint32_t w) {
return (v + w - 1) & ~(w - 1);
}
/**
* @brief 类型名 → 槽宽(对齐规则见 Doc/isa/指令与映像.md
* @param name "bool" / "int" / "time"
* @return 1 / 2 / 8;未知返回 1
*/
uint32_t width_of(const std::string& name) {
if (name == "int") return 2;
if (name == "time") return 8;
return 1; // bool 及未知
}
/**
* @brief TypeKind → 槽宽(FB 字段布局用)
* @param k 类型种类
* @return 1 / 2 / 8;未知返回 1
*/
uint32_t width_of(TypeKind k) {
if (k == TypeKind::Int) return 2;
if (k == TypeKind::Time) return 8;
return 1;
}
/**
* @brief 代码生成器(切片 1)
*/
@@ -715,58 +683,56 @@ namespace {
}
/**
* @brief 逻辑槽号 → 数据区字节偏移(layout_data 先行
* @param slot 全局逻辑槽号(符号表 address
* @return 数据区字节偏移(u16 可容
* @brief 逻辑槽号(符号表 address)即指令 slot(方案 a:8 字节定宽,无映射
* @param slot 全局逻辑槽号
* @return 同值(u16 可容,layout_data 已校验
*/
uint16_t global_offset(uint32_t slot) const {
return static_cast<uint16_t>(global_offsets_[slot]);
return static_cast<uint16_t>(slot);
}
/**
* @brief 布局全局数据区:对齐 + 初值字节
* @details 槽 = 声明序12.6);对齐规则 BOOL 1B / INT 2B / TIME 8B
* 起点对齐自身宽度;初值取自 GVL 声明的 has_init(无则 0
* @return true 成功;false(槽溢出等,err 已写
* @brief 布局全局数据区:每槽 8 字节定宽(方案 a
* @details 槽 = 符号表 address声明序12.6);
* BOOL 用低 1 字节、INT 用低 2 字节(小端)、TIME 全 8 字节;
* 初值取自 GVL 声明的 has_init(无则 0
* @return true 成功;false(槽号越界,err 已写)
*/
bool layout_data() {
uint32_t cur = 0;
for (const Symbol& s : link_.globals) {
const uint32_t w = width_of(s.type_name);
cur = align_up(cur, w);
if (cur + w > 0xFFFF) {
if (s.address > 0xFFFF) {
return fail("data area exceeds slot range");
}
global_offsets_.push_back(cur);
bool has_init = false;
int64_t init = 0;
init_of(s.name, &has_init, &init);
data_.resize(cur + w, 0);
if (s.type_name == "bool") {
data_[cur] = static_cast<uint8_t>(has_init ? init : 0);
} else if (s.type_name == "int") {
const uint16_t v = static_cast<uint16_t>(has_init ? init : 0);
data_[cur] = static_cast<uint8_t>(v & 0xFFu);
data_[cur + 1] = static_cast<uint8_t>((v >> 8) & 0xFFu);
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 v = static_cast<uint64_t>(has_init ? init : 0);
const uint64_t tv = static_cast<uint64_t>(v);
for (int i = 0; i < 8; ++i) {
data_[cur + i] = static_cast<uint8_t>((v >> (8 * i)) & 0xFFu);
data_[off + i] = static_cast<uint8_t>((tv >> (8 * i)) & 0xFFu);
}
} else { // bool
data_[off] = v ? 1 : 0;
}
cur += w;
}
return true;
}
/**
* @brief 布局 FB 实例块:数据区全局之后,按 POU 收集序 / 实例声明序
* @details 每实例:字段按布局段序对齐排布(BOOL 1 / INT 2 / TIME 8),
* 字段地址 = 实例基 + 字段偏移;实例块间 8 字节对齐,块内容初值 0
* @details 每实例:字段按布局段序各占一槽(8 字节定宽),
* 字段地址 = 实例基 + 字段序号;实例基槽 = 累计槽数
* @return true 成功;falseerr 已写)
*/
bool layout_fb_instances() {
uint32_t cur = align_up(static_cast<uint32_t>(data_.size()), 8);
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) {
@@ -777,27 +743,20 @@ namespace {
if (lay == sc.fb_instances.end()) {
return fail("no layout for instance '" + s.name + "'");
}
cur = align_up(cur, 8);
InstFields inst;
inst.base = cur;
inst.base = cur; // 实例基槽(CAL_* 操作数)
inst.type_name = s.type_name;
uint32_t off = 0;
for (const FbField& fd : lay->second.fields) {
const uint32_t w = width_of(fd.type);
off = align_up(off, w);
inst.field_addr[fd.name] = cur + off;
off += w;
for (size_t i = 0; i < lay->second.fields.size(); ++i) {
inst.field_addr[lay->second.fields[i].name] = cur + i;
}
cur += align_up(off, 8);
cur += static_cast<uint32_t>(lay->second.fields.size());
instances_[sc.name + "/" + s.name] = inst;
any = true;
}
}
// 仅在有实例时把数据区补到实例区终点(无实例则保持全局区原样)
if (any) {
while (data_.size() < cur) {
data_.push_back(0);
}
data_.resize(static_cast<size_t>(cur) * 8, 0);
}
return true;
}
@@ -945,9 +904,8 @@ namespace {
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<uint32_t> global_offsets_; // 逻辑槽号 → 数据区字节偏移
std::vector<uint8_t> data_; // 数据区(全局初值 + FB 实例块)
std::map<std::string, InstFields> instances_; // "POU/实例名" → 字段地址
std::vector<uint8_t> data_; // 数据区(8 字节定宽槽:全局 + FB 实例块)
std::map<std::string, InstFields> instances_; // "POU/实例名" → 字段槽号
const InstFields* inline_fields_ = nullptr; // 内联 FB 体字段上下文(可空)
};