- README.md:依赖方向(STCompiler→compiler 不链 isa)、模块表、isa 归执行器侧、 数据流图(machine.toml + 型号/SHA) - isa/README.md:重写——只管指令定义(Types/Op/Instr/Encode),删 Image 部分 - 指令与映像.md:依赖图与 CMake(compiler 不链 isa) - 编译管线.md:管线首步加载 machine.toml、产物含型号/SHA - 初步计划.md:12.10/12.13 修订(--machine、依赖方向) - 扫描周期.md / 指令执行.md:12.9 步骤标注已落地、vm 读校验说明 - compiler/vm/executor README:依赖与边界同步
260 lines
10 KiB
Markdown
260 lines
10 KiB
Markdown
# 指令与映像
|
||
|
||
isa 模块规范:指令集 + 映像 + 定宽类型。不是编译器,不把 `.st` 编成映像。
|
||
|
||
`compiler` 写 `.stb`、`vm` 读 `.stb`,两边**各自实现**读写;`isa` 归执行器侧只管指令定义。指令 opcode / 类型 / FB 布局的登记见 [`指令配置.md`](指令配置.md)(`compiler/machine.toml`,与代码强校验)。
|
||
|
||
全工程定案见 [`初步计划.md`](../初步计划.md)。**类型、指令编码、映像布局以本文为准**,不要另写一份互相打架的表。头文件实现本文,不另当规范。
|
||
|
||
```text
|
||
STCompiler → compiler (编码查 machine.toml;自带 .stb 写)
|
||
BytecodeExecutor → vm → isa (isa 只管指令;vm 自带 .stb 读)
|
||
```
|
||
|
||
CMake 目标:`isa`(`STATIC`),无依赖;`vm` PUBLIC 链 `isa`。公开头:`isa/include/isa/`。
|
||
|
||
---
|
||
|
||
## 做 / 不做
|
||
|
||
**做**
|
||
|
||
- 操作码与 32 位指令布局
|
||
- 映像头与段(函数字节码、全局 / FB 槽、工程哈希)
|
||
- `BOOL` / `INT` / `TIME` 宽度与溢出规则
|
||
- 编解码、工程哈希、一条指令的文本转储(反汇编,给 STCompiler `--disasm` 与测试打印)
|
||
|
||
**不做**
|
||
|
||
- toml 字段、关键字表、符号表、AST
|
||
- 扫描周期循环、I/O 采样
|
||
- 短路语义(`AND`/`OR` 指令只组合已算好的值;短路由 `compiler` 编成跳转)
|
||
|
||
指令表可作为内部 IR 的文本形态,不必当主语言。
|
||
|
||
---
|
||
|
||
## 特性
|
||
|
||
| 项 | 第一版 |
|
||
|---|---|
|
||
| `BOOL` | `uint8_t`,只允许 0/1 |
|
||
| `INT` | `int16_t`(IEC `INT`) |
|
||
| `TIME` | `int64_t` 毫秒(有符号);与 `INT` 不混用 |
|
||
| 整数溢出 | **饱和**(夹到 `INT` 最小/最大) |
|
||
| 字节序 | 小端 |
|
||
| 指令宽度 | 32 bit,一种主布局 |
|
||
| 寄存器 | 下标 0..255;每函数实际个数写在函数头 `nregs` |
|
||
| 跳转 | 相对 offset,单位是**指令条数**,不是字节 |
|
||
| `CALL` | 立即数 `fn_id`(u16),无函数指针 |
|
||
|
||
### 调用约定(冻结,compiler 12.8 与 VM 12.9 共同遵守)
|
||
|
||
- 结果寄存器 **r0**;参数寄存器 **r1..r7**(最多 7 个输入,按声明序)
|
||
- 变量与表达式临时**全部从 r8 起**(调用约定区 r0..r7 不受用户数据占用)
|
||
- `CALL`:VM 复制当前帧 r0..r7 到新帧 r0..r7
|
||
- `RET`:VM 复制当前帧 r0..r7 回调用方帧 r0..r7(r0 = 结果;r1..r7 原样返回)
|
||
- 函数输入在函数体内只读(compiler 拒绝写入)
|
||
| 映像魔数 | `STSC`,版本 `1` |
|
||
| 工程哈希 | 源文件路径排序后对内容做 FNV-1a 64(非密码学) |
|
||
| I/O 绑定 | **不进映像**;编译期把 `var` 收成槽号,channel/bit 由 STCompiler 写进 sidecar(`.runtime.toml`),BytecodeExecutor 读 sidecar 采样 |
|
||
| 不做 | `REAL`、隐式宽化 |
|
||
|
||
---
|
||
|
||
## 类型
|
||
|
||
C++20 定宽:
|
||
|
||
- `BOOL`:`uint8_t`,0/1
|
||
- `INT`:`int16_t`;加减乘除溢出饱和
|
||
- `TIME`:`int64_t` 毫秒,有符号
|
||
|
||
`compiler` 与 `vm` 都按同一规则,不再另选溢出策略。
|
||
|
||
`TIME` 选 `int64_t` 的理由(冻结,不再反复问):
|
||
|
||
- 有符号:`TOF` 剩余时间、两个时间点相减会出负值,免去无符号边界处理
|
||
- 64 位:为将来 `DATE_AND_TIME`(毫秒时间戳)与 `TON` / `TOF` / `TP` 的 64 位定时器预留;毫秒量程约 2.9 亿年,扫描周期场景永不溢出
|
||
- 代价:槽位 8 字节对齐。**槽位规则**(12.9 方案 a):每槽 8 字节定宽,`BOOL` 用低 1 字节、`INT` 用低 2 字节(小端)、`TIME` 全 8 字节;槽号 × 8 = 数据偏移,无 padding。
|
||
|
||
---
|
||
|
||
## 指令
|
||
|
||
定宽 32 bit:
|
||
|
||
```text
|
||
[ op:8 | rd:8 | a:8 | b:8 ]
|
||
```
|
||
|
||
- 三寄存器:`a`/`b` 为 `ra`/`rb`(`MOVE`/`NOT` 只用 `rd`+`a`)
|
||
- `LOADK`:`a|b` 为 `const_id`(u16)
|
||
- `JMP` / `JT` / `JF`:`a|b` 为有符号相对 offset(指令条数),**相对下一条指令**:目标下标 = 当前下标 + 1 + off
|
||
- `CALL`:`a|b` 为 `fn_id`(u16)
|
||
|
||
操作码一次列全(`CMP_xx` 冻下面六种):
|
||
|
||
```text
|
||
MOVE rd, rs
|
||
LOADK rd, const_id
|
||
NOT rd, rs
|
||
AND/OR rd, ra, rb (* 已算好的值;短路用跳转 *)
|
||
ADD/SUB/MUL/DIV rd, ra, rb
|
||
CMP_EQ/NE/LT/LE/GT/GE rd, ra, rb
|
||
JMP offset
|
||
JT/JF r, offset
|
||
LOAD_I / STORE_Q / LOAD_M / STORE_M
|
||
LOAD_GLOBAL / STORE_GLOBAL
|
||
CAL_TON / CAL_TOF / CAL_TP / CAL_CTU / CAL_CTD / CAL_CTUD / CAL_R_TRIG / CAL_F_TRIG
|
||
CALL fn_id
|
||
RET
|
||
```
|
||
|
||
> 12.11 修订:内置功能块扩为 8 个(TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG),
|
||
> `CAL_*` 操作码**连续占 24..31**(原 CALL=27、RET=28 后移为 32、33)。
|
||
> 操作码数值即编码,本表为准。
|
||
|
||
局部 / `VAR` = 固定寄存器;表达式临时值往后编号;`nregs` 写进函数头。
|
||
`I`/`Q`/`M` 走映像指令,不是通用寄存器。
|
||
FB 实例固定布局,字段偏移编译期算死。
|
||
|
||
### 反汇编格式(disasm)
|
||
|
||
`disasm` 输出一条指令的一行文本,格式冻结:
|
||
|
||
| 形态 | 格式 | 示例 |
|
||
|---|---|---|
|
||
| 二寄存器 | `MOVE rd, rs` / `NOT rd, rs` | `MOVE r1, r2` |
|
||
| 三寄存器 | `OP rd, ra, rb`(逻辑 / 算术 / 比较) | `ADD r1, r2, r3` |
|
||
| 常量 | `LOADK rd, imm` | `LOADK r0, 3` |
|
||
| 无条件跳转 | `JMP off` | `JMP +4` |
|
||
| 条件跳转 | `JT rd, off` / `JF rd, off` | `JT r0, -1` |
|
||
| I/Q/M/全局 | `LOAD_I rd, slot` 等;STORE 同形 `STORE_Q rs, slot` | `LOAD_I r0, 2` |
|
||
| FB 内置 | `CAL_TON instance` 等 | `CAL_TON 0` |
|
||
| 调用 | `CALL fn_id` | `CALL 1` |
|
||
| 返回 | `RET` | `RET` |
|
||
| 未知操作码 | `??? 0x<hex>` | — |
|
||
|
||
跳转偏移带符号、恒显正负号(`%+d`);寄存器一律 `r<n>`;常量/槽号/`fn_id` 无符号十进制。
|
||
|
||
---
|
||
|
||
## 映像
|
||
|
||
魔数 `STSC`,版本 `1`。全部整数小端。文件 = 头 + 五个段 + **文件尾校验**,段序:常量表 → 函数表 → 字节码 → FB 布局 → 数据。偏移记在头里,段必须连续、偏移单调不减,末段终点不超过文件长度(文件尾在数据段之后)。
|
||
|
||
> 12.13 修订:头新增 **型号标识[32]**(原头 72 字节 → 104 字节,段偏移基准同步后移);
|
||
> 文件尾新增 **SHA-256[32]**(对文件尾之前全部内容计算,compiler 写侧 / vm 读侧各自实现)。
|
||
> 执行器读取时:型号标识与自身支持型号不匹配 → 报错;SHA-256 不匹配 → 报错(损坏/篡改)。
|
||
|
||
### 头(104 字节)
|
||
|
||
| 偏移 | 宽 | 字段 |
|
||
|---|---|---|
|
||
| 0 | 4 | 魔数 `STSC` |
|
||
| 4 | 4 | 版本 `1` |
|
||
| 8 | 4 | `cycle_limit` |
|
||
| 12 | 4 | `dt_ms` |
|
||
| 16 | 8 | 工程哈希(FNV-1a 64) |
|
||
| 24 | 4 | 入口 `entry_fn_id` |
|
||
| 28 | 4 | 全局槽数 `n_globals` |
|
||
| 32 | 4 | I 槽数 `n_i` |
|
||
| 36 | 4 | Q 槽数 `n_q` |
|
||
| 40 | 4 | M 槽数 `n_m` |
|
||
| 44 | 4 | 常量数 `n_consts` |
|
||
| 48 | 4 | 函数数 `n_funcs` |
|
||
| 52 | 4 | `offset_const` |
|
||
| 56 | 4 | `offset_funcs` |
|
||
| 60 | 4 | `offset_code` |
|
||
| 64 | 4 | `offset_fb` |
|
||
| 68 | 4 | `offset_data` |
|
||
| 72 | 32 | **型号标识**(定长 ASCII,含版本,如 `"STATOR" + "1"` 拼 32 字节补 `'\0'`) |
|
||
|
||
### 文件尾(SHA-256)
|
||
|
||
数据段之后追加 **32 字节 SHA-256**:对文件尾之前全部内容计算。执行器必须校验通过才运行;compiler 写入。
|
||
|
||
### 常量表一行(12 字节)
|
||
|
||
`[tag:u32][value:u64]`;`tag`:0=BOOL、1=INT、2=TIME。`value` 按 tag 解释,未用高位为 0。
|
||
|
||
### 函数表一行(12 字节)
|
||
|
||
`[nregs:u32][code_offset:u32][code_len:u32]`;行下标即 `fn_id`,`code_offset` 相对字节码段起点,`code_len` 为指令条数。
|
||
|
||
### 字节码段
|
||
|
||
指令按 `u32` 连续排;`CALL` 的 `fn_id` 是函数表行下标。
|
||
|
||
### FB 布局段
|
||
|
||
第一版为空;每类型一行 `[field_count:u32][field_count × (tag:u32, offset:u32)]`,offset 相对实例基址,12.8 落地 codegen 时冻精确表。
|
||
|
||
### 数据段
|
||
|
||
槽初值按序排:全局(`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`。
|
||
|
||
`[[io.input]]` / `[[io.output]]` 不进映像。
|
||
|
||
---
|
||
|
||
## 映像文件(`.stb`)与 sidecar
|
||
|
||
编译产物是单文件 `<name>.stb`:映像头 + 各段按本文布局连续排,魔数 `STSC`、版本 `1`、小端。`STCompiler` 写,`BytecodeExecutor` 读。
|
||
|
||
- `dt_ms` / `cycle_limit` 在映像头,执行器只认映像,不重复配置。
|
||
- I/O 绑定不进映像:`STCompiler` 另写 sidecar `<name>.runtime.toml`,`BytecodeExecutor` 读 sidecar 做采样与写回,不创造变量。
|
||
|
||
sidecar 格式(TOML 子集,冻结):
|
||
|
||
```toml
|
||
[[io.input]] # 或 [[io.output]]
|
||
var = "I0_0" # 已在 GVL 声明的名字
|
||
slot = 1 # 全局槽号(编译期已解析)
|
||
channel = 0
|
||
bit = 0
|
||
```
|
||
|
||
每行一个绑定;`var` 顺序不要求与映像槽序一致(`slot` 为准)。
|
||
|
||
---
|
||
|
||
## 目录与 CMake
|
||
|
||
```text
|
||
isa/
|
||
CMakeLists.txt
|
||
README.md # 代码目录入口,指向本文
|
||
include/isa/
|
||
Types.h # 宽度、饱和、槽值表示
|
||
Op.h # 操作码枚举 + 助记符
|
||
Instr.h # 32-bit 打包/拆字段(inline)
|
||
Image.h # 头、段、函数表/常量表/FB 布局
|
||
Encode.h # 编解码、哈希、反汇编一行
|
||
src/
|
||
Encode.cpp
|
||
Image.cpp
|
||
```
|
||
|
||
```cmake
|
||
add_library(isa STATIC src/Encode.cpp)
|
||
target_include_directories(isa PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||
```
|
||
|
||
`vm`:`target_link_libraries(vm PUBLIC isa)`;`compiler` 不链 `isa`。
|
||
|
||
---
|
||
|
||
## 实现顺序(对齐初步计划 12.0~12.1)
|
||
|
||
1. `Types.h`:三类型 + `sat_add` 等饱和函数(inline 即可)
|
||
2. `Op.h` + `Instr.h`:枚举与 32-bit 编解码
|
||
3. `Encode.cpp`:`encode` / `decode` / `disasm` 一条指令
|
||
4. `Image.h` + `Image.cpp`:头布局、FNV-1a、读写缓冲(先能写空映像 + 一条 `RET`)
|
||
5. 测试随后在 `tests/` 加编解码往返;本阶段以头文件和库能链为准
|
||
|
||
不在本模块做:VM `switch`、toml、关键字、符号表。
|