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:
@@ -25,9 +25,9 @@ compiler 模块的代码生成。输入:`Project` + `Unit`(AST)+ `LinkResu
|
|||||||
| FB 实例(任意 POU 内声明) | **数据区实例块**(绝对地址) | 状态跨周期持久 |
|
| FB 实例(任意 POU 内声明) | **数据区实例块**(绝对地址) | 状态跨周期持久 |
|
||||||
| 全局(含 I/Q/M) | 数据区(声明序) | 12.6 已定槽号 |
|
| 全局(含 I/Q/M) | 数据区(声明序) | 12.6 已定槽号 |
|
||||||
|
|
||||||
- **数据区** = 全局块(声明序,按对齐规则:BOOL 1B / INT 2B / TIME 8B,起点对齐自身宽度)→ FB 实例块(按实例声明序,各块按类型布局大小、8 字节对齐)
|
- **数据区** = 全局块(声明序,**每槽 8 字节定宽**,见 [`指令与映像.md`](../isa/指令与映像.md))→ FB 实例块(按实例声明序,每字段一槽)
|
||||||
- **指令 slot = 数据区字节偏移**(u16);符号表 `address` 保持逻辑槽号(12.6 冻结),编译时做 槽号 → 偏移 映射
|
- **指令 slot = 槽号**(u16,0..65535);数据偏移 = 槽号 × 8;符号表 `address` 即指令 slot,无映射
|
||||||
- 字段访问 `fb.Q`:`LOAD_GLOBAL rd, <实例基址+字段偏移>`(编译期算死);I/Q/M 用 `LOAD_I` / `STORE_Q` / `LOAD_M` / `STORE_M` 同槽号
|
- 字段访问 `fb.Q`:`LOAD_GLOBAL rd, <实例基槽+字段序号>`(编译期算死);I/Q/M 用 `LOAD_I` / `STORE_Q` / `LOAD_M` / `STORE_M` 同槽号
|
||||||
- 函数帧寄存器:`FUNCTION` 结果固定 **r0**,`VAR_INPUT` 从 r1 起按声明序,`VAR` 续后;PROGRAM / FB 变量从 r0 起按声明序
|
- 函数帧寄存器:`FUNCTION` 结果固定 **r0**,`VAR_INPUT` 从 r1 起按声明序,`VAR` 续后;PROGRAM / FB 变量从 r0 起按声明序
|
||||||
|
|
||||||
### 调用约定(v1 冻结,VM 12.9 按此实现)
|
### 调用约定(v1 冻结,VM 12.9 按此实现)
|
||||||
|
|||||||
+19
-16
@@ -1,6 +1,6 @@
|
|||||||
# .stb 映像文件结构
|
# .stb 映像文件结构
|
||||||
|
|
||||||
编译产物 `.stb` 的**面向使用者**的结构说明。规范以 [`指令与映像.md`](指令与映像.md) 为准,本文用 `line1.stb`(184 字节)做真实拆解。
|
编译产物 `.stb` 的**面向使用者**的结构说明。规范以 [`指令与映像.md`](指令与映像.md) 为准,本文用 `line1.stb`(224 字节)做真实拆解。
|
||||||
|
|
||||||
## 概览
|
## 概览
|
||||||
|
|
||||||
@@ -74,15 +74,15 @@ line1(2 行):
|
|||||||
- `CALL` 的 `fn_id` = 函数表行下标
|
- `CALL` 的 `fn_id` = 函数表行下标
|
||||||
- 调用约定:结果 r0、参数 r1..r7、变量与临时 r8 起
|
- 调用约定:结果 r0、参数 r1..r7、变量与临时 r8 起
|
||||||
|
|
||||||
line1 `MAIN` 前 5 条(`0x60` 起):
|
line1 `MAIN` 前 5 条(`0x60` 起,slot 为 8 字节定宽槽号):
|
||||||
|
|
||||||
| 偏移 | 字节 | 反汇编 | 含义 |
|
| 偏移 | 字节 | 反汇编 | 含义 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 0x60 | `12 08 01 00` | `LOAD_I r8, 1` | I0_0(io.input 绑定 → LOAD_I) |
|
| 0x64 | `12 08 01 00` | `LOAD_I r8, 1` | I0_0(io.input 绑定 → LOAD_I,槽 1) |
|
||||||
| 0x64 | `17 08 08 00` | `STORE_GLOBAL r8, 8` | starter.start ← I0_0 |
|
| 0x68 | `17 08 04 00` | `STORE_GLOBAL r8, 4` | starter.start(槽 4)← I0_0 |
|
||||||
| 0x68 | `16 09 02 00` | `LOAD_GLOBAL r9, 2` | I0_1 |
|
| 0x6C | `16 09 02 00` | `LOAD_GLOBAL r9, 2` | I0_1(槽 2) |
|
||||||
| 0x6C | `17 09 09 00` | `STORE_GLOBAL r9, 9` | starter.stop ← I0_1 |
|
| 0x70 | `17 09 05 00` | `STORE_GLOBAL r9, 5` | starter.stop(槽 5)← I0_1 |
|
||||||
| 0x70 | `16 08 08 00` | `LOAD_GLOBAL r8, 8` | 内联体:读 starter.start |
|
| 0x74 | `16 08 04 00` | `LOAD_GLOBAL r8, 4` | 内联体:读 starter.start(槽 4) |
|
||||||
|
|
||||||
## FB 布局段(`offset_fb` 起)
|
## FB 布局段(`offset_fb` 起)
|
||||||
|
|
||||||
@@ -90,18 +90,21 @@ v1 为空(用户 FB 内联展开,字段偏移已算进数据段;内建 TON
|
|||||||
|
|
||||||
## 数据段(`offset_data` 起)
|
## 数据段(`offset_data` 起)
|
||||||
|
|
||||||
**全局块**(`n_globals` 个,声明序)→ **FB 实例块**(按 POU 收集序、实例声明序,块间 8 字节对齐)。
|
**每槽 8 字节定宽**(12.9 方案 a):`BOOL` 用低 1 字节、`INT` 用低 2 字节(小端)、`TIME` 全 8 字节。指令 `slot` 操作数即**槽号**,数据偏移 = 槽号 × 8。
|
||||||
|
|
||||||
槽宽与对齐:`BOOL` 1 字节 / `INT` 2 字节 / `TIME` 8 字节,每个槽起点对齐自身宽度。
|
槽序:**全局块**(`n_globals` 个,声明序)→ **FB 实例块**(POU 收集序、实例声明序,每字段一槽)。
|
||||||
|
|
||||||
line1 数据段(16 字节,`0xA8` 起):
|
line1 数据段(56 字节 = 7 槽 × 8B,`0xA8` 起):
|
||||||
|
|
||||||
| 偏移 | 内容 |
|
| 槽号 | 偏移 | 内容 |
|
||||||
|---|---|
|
|---|---|---|
|
||||||
| 0xA8..0xAB | 全局:EmergencyStop、I0_0、I0_1、Q0_0(BOOL,初值 0) |
|
| 0 | 0xA8 | EmergencyStop(BOOL,初值 0) |
|
||||||
| 0xAC..0xAF | 填充(实例块 8 对齐) |
|
| 1 | 0xB0 | I0_0(BOOL) |
|
||||||
| 0xB0..0xB2 | starter 实例:start、stop、q(BOOL) |
|
| 2 | 0xB8 | I0_1(BOOL) |
|
||||||
| 0xB3..0xB7 | 填充 |
|
| 3 | 0xC0 | Q0_0(BOOL) |
|
||||||
|
| 4 | 0xC8 | starter.start(BOOL) |
|
||||||
|
| 5 | 0xD0 | starter.stop(BOOL) |
|
||||||
|
| 6 | 0xD8 | starter.q(BOOL) |
|
||||||
|
|
||||||
## 用工具验证
|
## 用工具验证
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -76,7 +76,7 @@ C++20 定宽:
|
|||||||
|
|
||||||
- 有符号:`TOF` 剩余时间、两个时间点相减会出负值,免去无符号边界处理
|
- 有符号:`TOF` 剩余时间、两个时间点相减会出负值,免去无符号边界处理
|
||||||
- 64 位:为将来 `DATE_AND_TIME`(毫秒时间戳)与 `TON` / `TOF` / `TP` 的 64 位定时器预留;毫秒量程约 2.9 亿年,扫描周期场景永不溢出
|
- 64 位:为将来 `DATE_AND_TIME`(毫秒时间戳)与 `TON` / `TOF` / `TP` 的 64 位定时器预留;毫秒量程约 2.9 亿年,扫描周期场景永不溢出
|
||||||
- 代价:槽位 8 字节对齐。**槽位对齐规则**:`BOOL` 1 字节、`INT` 2 字节、`TIME` 8 字节;连续排列按类型对齐,需要时插 padding,偏移以本规则为准。
|
- 代价:槽位 8 字节对齐。**槽位规则**(12.9 方案 a):每槽 8 字节定宽,`BOOL` 用低 1 字节、`INT` 用低 2 字节(小端)、`TIME` 全 8 字节;槽号 × 8 = 数据偏移,无 padding。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ FB 实例固定布局,字段偏移编译期算死。
|
|||||||
|
|
||||||
### 数据段
|
### 数据段
|
||||||
|
|
||||||
槽初值按序排:全局(`n_globals`)→ I → Q → M。槽宽与对齐:`BOOL` 1 字节 / `INT` 2 字节 / `TIME` 8 字节,每个槽起点对齐到自身宽度(需要时插 padding)。
|
槽初值按序排:全局(`n_globals`,v1 不拆 I/Q/M)→ FB 实例块(每字段一槽)。**每槽 8 字节定宽**(12.9 方案 a):`BOOL` 用低 1 字节、`INT` 用低 2 字节(小端)、`TIME` 全 8 字节。指令 `slot` 操作数即**槽号**,数据偏移 = 槽号 × 8;符号表 `address` 与指令 slot 一致,无映射。
|
||||||
|
|
||||||
工程哈希:收录源文件路径排序后对内容做 **FNV-1a 64**,只保证回放槽位一致,不是密码学哈希。FNV-1a 64:basis `0xcbf29ce484222325`,prime `0x100000001b3`。
|
工程哈希:收录源文件路径排序后对内容做 **FNV-1a 64**,只保证回放槽位一致,不是密码学哈希。FNV-1a 64:basis `0xcbf29ce484222325`,prime `0x100000001b3`。
|
||||||
|
|
||||||
|
|||||||
+37
-79
@@ -7,15 +7,14 @@
|
|||||||
* @details 设计说明(详见 Doc/compiler/寄存器码.md):
|
* @details 设计说明(详见 Doc/compiler/寄存器码.md):
|
||||||
* - 切片 1~6:帧/字面量/MOVE/RET;全局数据区;短路 AND/OR;CMP 与 IF;WHILE 与四则;
|
* - 切片 1~6:帧/字面量/MOVE/RET;全局数据区;短路 AND/OR;CMP 与 IF;WHILE 与四则;
|
||||||
* FUNCTION 与 CALL(调用约定 r0/r1..r7/r8+)。
|
* FUNCTION 与 CALL(调用约定 r0/r1..r7/r8+)。
|
||||||
* - 切片 7:FB 实例 → 数据区实例块(跨周期持久);字段地址 = 实例基址 + 字段偏移
|
* - 切片 7:FB 实例 → 数据区实例块(跨周期持久);字段槽号 = 实例基槽 + 字段序号
|
||||||
* (编译期算死);字段读写走 LOAD_GLOBAL / STORE_GLOBAL;
|
* (编译期算死);字段读写走 LOAD_GLOBAL / STORE_GLOBAL;
|
||||||
* 内建 TON/TOF/CTU → 实参写字段后 CAL_* <实例偏移>;
|
* 内建 TON/TOF/CTU → 实参写字段后 CAL_* <实例基槽>;
|
||||||
* 用户 FB → 调用点内联展开(实参写字段 → FB 体以实例字段为变量编译)
|
* 用户 FB → 调用点内联展开(实参写字段 → FB 体以实例字段为变量编译)
|
||||||
* - 其余构造报 codegen error
|
* - 方案 a(12.9 前置):数据段**每槽 8 字节定宽**,指令 slot = 槽号(无映射)
|
||||||
*
|
*
|
||||||
* 函数清单:
|
* 函数清单:
|
||||||
* - put_le32 / put_le64 小端写入映像缓冲
|
* - put_le32 / put_le64 小端写入映像缓冲
|
||||||
* - align_up / width_of 对齐与槽宽
|
|
||||||
* - Builder::Builder (构造)存工程/源文件/链接结果/输出,收集 io 绑定分类
|
* - Builder::Builder (构造)存工程/源文件/链接结果/输出,收集 io 绑定分类
|
||||||
* - Builder::run 布局数据区(全局 + 实例)→ 逐 POU 建函数 → 拼映像
|
* - Builder::run 布局数据区(全局 + 实例)→ 逐 POU 建函数 → 拼映像
|
||||||
* - Builder::fail 组装 "codegen error: <msg>" 返回 false
|
* - Builder::fail 组装 "codegen error: <msg>" 返回 false
|
||||||
@@ -29,9 +28,10 @@
|
|||||||
* - store_target 赋值左值:STORE_* 到全局槽(或内联模式实例字段)
|
* - store_target 赋值左值:STORE_* 到全局槽(或内联模式实例字段)
|
||||||
* - compile_expr 表达式编译到寄存器(含 Field 字段读)
|
* - compile_expr 表达式编译到寄存器(含 Field 字段读)
|
||||||
* - cmp_op / arith_op / load_op / store_op 操作码选择
|
* - cmp_op / arith_op / load_op / store_op 操作码选择
|
||||||
* - global_offset / instance_of 槽号 → 偏移 / 实例 → 字段地址
|
* - global_offset 槽号恒等(方案 a 无映射)
|
||||||
|
* - instance_of 实例 → 字段槽号表
|
||||||
* - patch_jump 回填跳转偏移(相对下一条指令)
|
* - patch_jump 回填跳转偏移(相对下一条指令)
|
||||||
* - layout_data / layout_fb_instances / init_of 数据区布局
|
* - layout_data / layout_fb_instances / init_of 数据区布局(8 字节定宽槽)
|
||||||
* - const_id 取常量表 id(无则追加)
|
* - const_id 取常量表 id(无则追加)
|
||||||
* - assemble_image 拼头 + 常量表 + 函数表 + 字节码 + 数据段
|
* - assemble_image 拼头 + 常量表 + 函数表 + 字节码 + 数据段
|
||||||
* - codegen_project 对外入口
|
* - 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)
|
* @brief 代码生成器(切片 1)
|
||||||
*/
|
*/
|
||||||
@@ -715,58 +683,56 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 逻辑槽号 → 数据区字节偏移(layout_data 先行)
|
* @brief 逻辑槽号(符号表 address)即指令 slot(方案 a:8 字节定宽,无映射)
|
||||||
* @param slot 全局逻辑槽号(符号表 address)
|
* @param slot 全局逻辑槽号
|
||||||
* @return 数据区字节偏移(u16 可容)
|
* @return 同值(u16 可容,layout_data 已校验)
|
||||||
*/
|
*/
|
||||||
uint16_t global_offset(uint32_t slot) const {
|
uint16_t global_offset(uint32_t slot) const {
|
||||||
return static_cast<uint16_t>(global_offsets_[slot]);
|
return static_cast<uint16_t>(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 布局全局数据区:对齐 + 初值字节
|
* @brief 布局全局数据区:每槽 8 字节定宽(方案 a)
|
||||||
* @details 槽序 = 声明序(12.6);对齐规则 BOOL 1B / INT 2B / TIME 8B,
|
* @details 槽号 = 符号表 address(声明序,12.6);
|
||||||
* 起点对齐自身宽度;初值取自 GVL 声明的 has_init(无则 0)
|
* BOOL 用低 1 字节、INT 用低 2 字节(小端)、TIME 全 8 字节;
|
||||||
* @return true 成功;false(槽溢出等,err 已写)
|
* 初值取自 GVL 声明的 has_init(无则 0)
|
||||||
|
* @return true 成功;false(槽号越界,err 已写)
|
||||||
*/
|
*/
|
||||||
bool layout_data() {
|
bool layout_data() {
|
||||||
uint32_t cur = 0;
|
|
||||||
for (const Symbol& s : link_.globals) {
|
for (const Symbol& s : link_.globals) {
|
||||||
const uint32_t w = width_of(s.type_name);
|
if (s.address > 0xFFFF) {
|
||||||
cur = align_up(cur, w);
|
|
||||||
if (cur + w > 0xFFFF) {
|
|
||||||
return fail("data area exceeds slot range");
|
return fail("data area exceeds slot range");
|
||||||
}
|
}
|
||||||
global_offsets_.push_back(cur);
|
|
||||||
bool has_init = false;
|
bool has_init = false;
|
||||||
int64_t init = 0;
|
int64_t init = 0;
|
||||||
init_of(s.name, &has_init, &init);
|
init_of(s.name, &has_init, &init);
|
||||||
data_.resize(cur + w, 0);
|
const int64_t v = has_init ? init : 0;
|
||||||
if (s.type_name == "bool") {
|
const size_t off = static_cast<size_t>(s.address) * 8;
|
||||||
data_[cur] = static_cast<uint8_t>(has_init ? init : 0);
|
data_.resize(off + 8, 0);
|
||||||
} else if (s.type_name == "int") {
|
if (s.type_name == "int") {
|
||||||
const uint16_t v = static_cast<uint16_t>(has_init ? init : 0);
|
const uint16_t iv = static_cast<uint16_t>(v);
|
||||||
data_[cur] = static_cast<uint8_t>(v & 0xFFu);
|
data_[off] = static_cast<uint8_t>(iv & 0xFFu);
|
||||||
data_[cur + 1] = static_cast<uint8_t>((v >> 8) & 0xFFu);
|
data_[off + 1] = static_cast<uint8_t>((iv >> 8) & 0xFFu);
|
||||||
} else if (s.type_name == "time") {
|
} 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) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 布局 FB 实例块:数据区全局之后,按 POU 收集序 / 实例声明序
|
* @brief 布局 FB 实例块:数据区全局之后,按 POU 收集序 / 实例声明序
|
||||||
* @details 每实例:字段按布局段序对齐排布(BOOL 1 / INT 2 / TIME 8),
|
* @details 每实例:字段按布局段序各占一槽(8 字节定宽),
|
||||||
* 字段地址 = 实例基址 + 字段偏移;实例块间 8 字节对齐,块内容初值 0
|
* 字段地址 = 实例基槽 + 字段序号;实例基槽 = 累计槽数
|
||||||
* @return true 成功;false(err 已写)
|
* @return true 成功;false(err 已写)
|
||||||
*/
|
*/
|
||||||
bool layout_fb_instances() {
|
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;
|
bool any = false;
|
||||||
for (const LinkResult::PouScope& sc : link_.scopes) {
|
for (const LinkResult::PouScope& sc : link_.scopes) {
|
||||||
for (const Symbol& s : sc.syms) {
|
for (const Symbol& s : sc.syms) {
|
||||||
@@ -777,27 +743,20 @@ namespace {
|
|||||||
if (lay == sc.fb_instances.end()) {
|
if (lay == sc.fb_instances.end()) {
|
||||||
return fail("no layout for instance '" + s.name + "'");
|
return fail("no layout for instance '" + s.name + "'");
|
||||||
}
|
}
|
||||||
cur = align_up(cur, 8);
|
|
||||||
InstFields inst;
|
InstFields inst;
|
||||||
inst.base = cur;
|
inst.base = cur; // 实例基槽(CAL_* 操作数)
|
||||||
inst.type_name = s.type_name;
|
inst.type_name = s.type_name;
|
||||||
uint32_t off = 0;
|
for (size_t i = 0; i < lay->second.fields.size(); ++i) {
|
||||||
for (const FbField& fd : lay->second.fields) {
|
inst.field_addr[lay->second.fields[i].name] = cur + i;
|
||||||
const uint32_t w = width_of(fd.type);
|
|
||||||
off = align_up(off, w);
|
|
||||||
inst.field_addr[fd.name] = cur + off;
|
|
||||||
off += w;
|
|
||||||
}
|
}
|
||||||
cur += align_up(off, 8);
|
cur += static_cast<uint32_t>(lay->second.fields.size());
|
||||||
instances_[sc.name + "/" + s.name] = inst;
|
instances_[sc.name + "/" + s.name] = inst;
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 仅在有实例时把数据区补到实例区终点(无实例则保持全局区原样)
|
// 仅在有实例时把数据区补到实例区终点(无实例则保持全局区原样)
|
||||||
if (any) {
|
if (any) {
|
||||||
while (data_.size() < cur) {
|
data_.resize(static_cast<size_t>(cur) * 8, 0);
|
||||||
data_.push_back(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -945,9 +904,8 @@ namespace {
|
|||||||
std::vector<isa::ConstEntry> consts_;
|
std::vector<isa::ConstEntry> consts_;
|
||||||
std::map<std::string, bool> io_input_; // io.input 绑定名(小写)
|
std::map<std::string, bool> io_input_; // io.input 绑定名(小写)
|
||||||
std::map<std::string, bool> io_output_; // io.output 绑定名(小写)
|
std::map<std::string, bool> io_output_; // io.output 绑定名(小写)
|
||||||
std::vector<uint32_t> global_offsets_; // 逻辑槽号 → 数据区字节偏移
|
std::vector<uint8_t> data_; // 数据区(8 字节定宽槽:全局 + FB 实例块)
|
||||||
std::vector<uint8_t> data_; // 数据区(全局初值 + FB 实例块)
|
std::map<std::string, InstFields> instances_; // "POU/实例名" → 字段槽号
|
||||||
std::map<std::string, InstFields> instances_; // "POU/实例名" → 字段地址
|
|
||||||
const InstFields* inline_fields_ = nullptr; // 内联 FB 体字段上下文(可空)
|
const InstFields* inline_fields_ = nullptr; // 内联 FB 体字段上下文(可空)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+17
-17
@@ -174,7 +174,7 @@ static bool test_case09() {
|
|||||||
const isa::ImageView v = isa::ImageView::from(img);
|
const isa::ImageView v = isa::ImageView::from(img);
|
||||||
CHECK(v.ok());
|
CHECK(v.ok());
|
||||||
CHECK(v.header().n_globals == 1);
|
CHECK(v.header().n_globals == 1);
|
||||||
CHECK(v.data_len() == 2); // INT 2 字节
|
CHECK(v.data_len() == 8); // 8 字节定宽槽
|
||||||
CHECK(v.data_bytes()[0] == 5); // G1 初值 5(小端低位)
|
CHECK(v.data_bytes()[0] == 5); // G1 初值 5(小端低位)
|
||||||
CHECK(v.data_bytes()[1] == 0);
|
CHECK(v.data_bytes()[1] == 0);
|
||||||
|
|
||||||
@@ -248,14 +248,14 @@ static bool test_layout() {
|
|||||||
const isa::ImageView v = isa::ImageView::from(img);
|
const isa::ImageView v = isa::ImageView::from(img);
|
||||||
CHECK(v.ok());
|
CHECK(v.ok());
|
||||||
CHECK(v.header().n_globals == 3);
|
CHECK(v.header().n_globals == 3);
|
||||||
CHECK(v.data_len() == 16); // 0 + 2 + 8(对齐)= 16
|
CHECK(v.data_len() == 24); // 3 槽 × 8 字节定宽
|
||||||
CHECK(v.data_bytes()[0] == 0); // B 无初值
|
CHECK(v.data_bytes()[0] == 0); // B 无初值
|
||||||
CHECK(v.data_bytes()[2] == 0x2C && v.data_bytes()[3] == 0x01); // I = 300
|
CHECK(v.data_bytes()[8] == 0x2C && v.data_bytes()[9] == 0x01); // I = 300(槽 1)
|
||||||
CHECK(v.data_bytes()[8] == 10); // T = 10ms
|
CHECK(v.data_bytes()[16] == 10); // T = 10ms(槽 2)
|
||||||
|
|
||||||
char buf[64];
|
char buf[64];
|
||||||
isa::disasm(reinterpret_cast<const uint32_t*>(v.code_bytes())[0], buf, sizeof buf);
|
isa::disasm(reinterpret_cast<const uint32_t*>(v.code_bytes())[0], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 2") == 0); // x := I(INT 偏移 2)
|
CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 1") == 0); // x := I(槽 1)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,7 +552,7 @@ static bool test_case15() {
|
|||||||
CHECK(v.ok());
|
CHECK(v.ok());
|
||||||
CHECK(v.header().n_funcs == 2); // FB 占位 + MAIN
|
CHECK(v.header().n_funcs == 2); // FB 占位 + MAIN
|
||||||
CHECK(v.header().entry_fn_id == 1);
|
CHECK(v.header().entry_fn_id == 1);
|
||||||
CHECK(v.data_len() == 8); // 实例 3B(start@0 stop@1 q@2),8 对齐
|
CHECK(v.data_len() == 24); // 实例 3 槽 × 8 字节定宽
|
||||||
|
|
||||||
const isa::FuncRow fm = v.func_row(1);
|
const isa::FuncRow fm = v.func_row(1);
|
||||||
CHECK(fm.nregs == 12);
|
CHECK(fm.nregs == 12);
|
||||||
@@ -602,11 +602,11 @@ static bool test_case17() {
|
|||||||
isa::disasm(ins[2], buf, sizeof buf);
|
isa::disasm(ins[2], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "LOADK r10, 1") == 0);
|
CHECK(std::strcmp(buf, "LOADK r10, 1") == 0);
|
||||||
isa::disasm(ins[3], buf, sizeof buf);
|
isa::disasm(ins[3], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "STORE_GLOBAL r10, 8") == 0); // pt(TIME 8 对齐)
|
CHECK(std::strcmp(buf, "STORE_GLOBAL r10, 1") == 0); // pt(槽 1)
|
||||||
isa::disasm(ins[4], buf, sizeof buf);
|
isa::disasm(ins[4], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "CAL_TON 0") == 0);
|
CHECK(std::strcmp(buf, "CAL_TON 0") == 0);
|
||||||
isa::disasm(ins[5], buf, sizeof buf);
|
isa::disasm(ins[5], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 16") == 0); // t.Q
|
CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 2") == 0); // t.Q(槽 2)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,7 +619,7 @@ static bool test_case18() {
|
|||||||
|
|
||||||
const isa::ImageView v = isa::ImageView::from(img);
|
const isa::ImageView v = isa::ImageView::from(img);
|
||||||
CHECK(v.ok());
|
CHECK(v.ok());
|
||||||
CHECK(v.data_len() == 40); // tf 块 0..31(in@0 pt@8 q@16 et@24)、c 块 32..
|
CHECK(v.data_len() == 72); // tf 4 槽 + c 5 槽 = 9 槽 × 8
|
||||||
const isa::FuncRow r = v.func_row(0);
|
const isa::FuncRow r = v.func_row(0);
|
||||||
CHECK(r.nregs == 14);
|
CHECK(r.nregs == 14);
|
||||||
const uint32_t* ins = reinterpret_cast<const uint32_t*>(v.code_bytes());
|
const uint32_t* ins = reinterpret_cast<const uint32_t*>(v.code_bytes());
|
||||||
@@ -627,11 +627,11 @@ static bool test_case18() {
|
|||||||
isa::disasm(ins[4], buf, sizeof buf);
|
isa::disasm(ins[4], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "CAL_TOF 0") == 0);
|
CHECK(std::strcmp(buf, "CAL_TOF 0") == 0);
|
||||||
isa::disasm(ins[12], buf, sizeof buf);
|
isa::disasm(ins[12], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "CAL_CTU 32") == 0);
|
CHECK(std::strcmp(buf, "CAL_CTU 4") == 0);
|
||||||
isa::disasm(ins[13], buf, sizeof buf);
|
isa::disasm(ins[13], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "LOAD_GLOBAL r9, 36") == 0); // c.Q
|
CHECK(std::strcmp(buf, "LOAD_GLOBAL r9, 7") == 0); // c.Q(槽 7)
|
||||||
isa::disasm(ins[14], buf, sizeof buf);
|
isa::disasm(ins[14], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "LOAD_GLOBAL r10, 38") == 0); // c.CV(INT 2 对齐)
|
CHECK(std::strcmp(buf, "LOAD_GLOBAL r10, 8") == 0); // c.CV(槽 8)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,7 +660,7 @@ static bool test_line1() {
|
|||||||
CHECK(v.header().n_globals == 4);
|
CHECK(v.header().n_globals == 4);
|
||||||
CHECK(v.header().n_funcs == 2);
|
CHECK(v.header().n_funcs == 2);
|
||||||
CHECK(v.header().entry_fn_id == 1);
|
CHECK(v.header().entry_fn_id == 1);
|
||||||
CHECK(v.data_len() == 16); // 全局 4B + 实例块 8..10(8 对齐)
|
CHECK(v.data_len() == 56); // 7 槽 × 8 字节定宽
|
||||||
|
|
||||||
const isa::FuncRow fm = v.func_row(1);
|
const isa::FuncRow fm = v.func_row(1);
|
||||||
CHECK(fm.nregs == 13);
|
CHECK(fm.nregs == 13);
|
||||||
@@ -670,15 +670,15 @@ static bool test_line1() {
|
|||||||
isa::disasm(im[0], buf, sizeof buf);
|
isa::disasm(im[0], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "LOAD_I r8, 1") == 0); // I0_0(io.input)
|
CHECK(std::strcmp(buf, "LOAD_I r8, 1") == 0); // I0_0(io.input)
|
||||||
isa::disasm(im[1], buf, sizeof buf);
|
isa::disasm(im[1], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "STORE_GLOBAL r8, 8") == 0); // starter.start
|
CHECK(std::strcmp(buf, "STORE_GLOBAL r8, 4") == 0); // starter.start(槽 4)
|
||||||
isa::disasm(im[2], buf, sizeof buf);
|
isa::disasm(im[2], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "LOAD_GLOBAL r9, 2") == 0); // I0_1
|
CHECK(std::strcmp(buf, "LOAD_GLOBAL r9, 2") == 0); // I0_1
|
||||||
isa::disasm(im[3], buf, sizeof buf);
|
isa::disasm(im[3], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "STORE_GLOBAL r9, 9") == 0); // starter.stop
|
CHECK(std::strcmp(buf, "STORE_GLOBAL r9, 5") == 0); // starter.stop(槽 5)
|
||||||
isa::disasm(im[13], buf, sizeof buf);
|
isa::disasm(im[13], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "STORE_GLOBAL r8, 10") == 0); // starter.Q
|
CHECK(std::strcmp(buf, "STORE_GLOBAL r8, 6") == 0); // starter.Q(槽 6)
|
||||||
isa::disasm(im[14], buf, sizeof buf);
|
isa::disasm(im[14], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 10") == 0); // starter.Q 读回
|
CHECK(std::strcmp(buf, "LOAD_GLOBAL r8, 6") == 0); // starter.Q 读回
|
||||||
isa::disasm(im[15], buf, sizeof buf);
|
isa::disasm(im[15], buf, sizeof buf);
|
||||||
CHECK(std::strcmp(buf, "STORE_Q r8, 3") == 0); // Q0_0(io.output)
|
CHECK(std::strcmp(buf, "STORE_Q r8, 3") == 0); // Q0_0(io.output)
|
||||||
isa::disasm(im[16], buf, sizeof buf);
|
isa::disasm(im[16], buf, sizeof buf);
|
||||||
|
|||||||
Reference in New Issue
Block a user