# 寄存器码(V2) compiler 模块的代码生成。输入:`Project` + `Unit`(AST)+ `LinkResult`;输出:`.stb` 映像字节(`std::vector`)。全工程定案见 [`初步计划.md`](../初步计划.md) 12.8,指令/映像布局见 [`指令与映像.md`](../isa/指令与映像.md),槽表/值段格式见 [`stb文件格式.md`](../isa/stb文件格式.md)。 ## 做 / 不做 **做** - 寄存器分配:局部/变量固定寄存器,表达式临时值往后编号,`nregs` 写进函数头 - 跳转先留洞、后回填(字节单位,相对下一条);`AND` / `OR` **必须编成跳转**(短路),禁止两边都算 - 常量表(`LOADK`,8B 原始值无 tag,按 func 解释)、全局/实例槽(`LOAD` / `STORE` / `LOAD_OFF` / `STORE_OFF`)、FB 共享体基址访问(`LOAD_PTR` / `STORE_PTR`) - `CALL fn_id` 有界帧;用户 FB **共享体函数化**(r1 = 实例指针);内建 `CAL fb_id, 实例槽` - 拼装映像:V2 头 128B / 函数表 / 常量表 / 字节码 / 槽表 / 值段 + SHA-256 尾部 - `max_stack`:MAIN + FUNCTION 调用图 DAG 最坏路径 Σ(12 + nregs×8);递归 = 编译期报错 **不做(第一版)** - 图染色、SSA、生命周期分析、优化、异常表、闭包 - 函数指针、间接跳转、动态实例 ## 存储布局(V2 定案) | 数据 | 放哪 | 说明 | |---|---|---| | PROGRAM / FUNCTION 标量变量与临时 | **寄存器**(函数帧) | 每周期帧保留由 VM 负责(12.9) | | 全局(含 I/Q/M)与 FB 实例 | **值区**(紧凑变宽,1/2/4/8 对齐) | 槽表条目(8B:addr:u32 + 预留)指到值区偏移;状态跨周期持久 | - **槽表两级**:指令操作数 = **槽号**(u32)→ 槽表条目 → 值区字节偏移;`slot_addr(slot)` 由 VM/Image 提供 - 全局按 GVL 声明序编槽;**FB 实例 = 1 个槽表条目**,字段在实例基址上按偏移访问(字段偏移编译期按 1/2/4/8 对齐计算,字段类型 = machine.toml `[[fb]]` 表) - 字段访问 `fb.Q`:`LOAD_OFF rd, <实例槽>, <偏移>` / `STORE_OFF <实例槽>, <偏移>, rs`(16B SLOT+off 指令,调用点/输出读用);FB 共享体函数体内:`LOAD_PTR` / `STORE_PTR rd, r1, <偏移>`(16B RS+off,基址寄存器 r1 = 实例指针);I/Q/M 同普通槽(`LOAD` / `STORE`) - 函数帧寄存器:`FUNCTION` 结果固定 **r0**,`VAR_INPUT` 从 r1 起按声明序,`VAR` 续后;PROGRAM / FB 变量从 r0 起按声明序 ### 调用约定(v1 冻结,VM 12.9 按此实现) | 项 | 约定 | |---|---| | 结果寄存器 | **r0**(FUNCTION 结果写入处) | | 参数寄存器 | **r1..r7**(最多 7 个输入,按声明序) | | 变量 / 临时 | **全部从 r8 起**(调用约定区不受覆盖) | | `CALL` | VM 复制当前帧 **r0..r7 → 新帧 r0..r7**(全宽) | | `RET` | VM 复制当前帧 **r0..r7 → 调用方帧 r0..r7**(r0=结果,r1..r7 原样返回) | | 函数输入 | 函数体内**只读**(写输入 → codegen error) | - 调用点:实参求值到 r8+ 临时 → `MOVE r(1+i), t` → `CALL fn_id` → `MOVE rd, r0` - 所有 POU 的变量/临时统一 r8 起,故 r1..r7 在调用点可安全覆盖 ## 寄存器分配 - 局部变量固定占用前段(0..k-1) - 表达式临时从 k 起,**语句内递增、语句结束复用基址**(不分析生命周期) - `nregs` = 全函数临时峰值 + 1 ## 表达式与语句的指令模式 ```text 字面量 LOADK rd, const_id (func = 语言类型;REAL/LREAL 位模式按目标类型装载) 变量读 LOAD rd, slot (帧内直接 MOVE) 字段读 LOAD_OFF rd, <实例槽>, <偏移> 函数调用 <实参求值 → 参数寄存器> CALL fn_id MOVE rd, r0 NOT NOT rd, t AND/OR 短路(必须跳转): JF(AND)/JT(OR) rd, L_end MOVE rd, t L_end: 比较 CMP_xx rd, t1, t2 算术 ADD/SUB/MUL/DIV rd, t1, t2 负号 NEG rd, t IF JF t, L_else JMP L_end L_else: (ELSIF 逐级) JF t, L_next ... L_end: WHILE L_loop: JF t, L_end JMP L_loop L_end: FB 调用(用户,共享体): <每个实参求值 → 临时> STORE_OFF <实例槽>, <字段偏移>, 临时 LOADK r1, <实例值区地址> CALL (FB 函数体内:字段访问 = LOAD_PTR/STORE_PTR rd, r1, <字段偏移>) 内建 实参同左 → CAL , <实例槽> ``` - 常量统一进常量表(8B 原始值,位模式去重;BOOL / 整数 / 时间 / 浮点 / 日期),`const_id` = 首次出现序;`LOADK` 按 func 解释位模式 - 指令 func 位段 = 语言类型对应(`func_of_name`:宽度 × 符号 + F32/F64) ## 用户 FB 共享体(函数化,2026-08-26 实施) FB 体共享会与「绝对字段地址」冲突(不同实例基址不同),V1 内联;V2 改为**共享体函数化**: - FB 编译为**独立共享函数**(fn_id 收集序):`r1` = 实例指针(值区地址,隐藏参数/this),字段访问 `LOAD_PTR/STORE_PTR rd, r1, 字段偏移` - 调用点:实参 `STORE_OFF`(绝对槽)→ `LOADK r1, c(实例addr)` → `CALL fb_fn_id`(2 条指令替代整个展开体) - 嵌套(FB 函数内调 FUNCTION):实参区 r1..r7 会覆盖实例指针 → 调用点自动 `MOVE t, r1` / 调用后 `MOVE r1, t` 保护 - `VAR_TEMP` 落 FB 帧寄存器,帧每周期重建 → 天然每次调用重置(内联期的寄存器隔离 hack 退役) - 多实例共享同一函数体,状态经 r1 指针隔离(`count` 等 `VAR` 仍在实例字段) - FB 无递归、无动态实例(链接层保证),函数化安全 ## 映像拼装(V2) ```text 头 128B:magic / version=2 / model=STATOR2 / cycle_limit / dt_ms / 工程哈希 / entry_fn_id / n_globals / n_consts / n_funcs / 各段偏移 / n_slots / values_size / meta / max_stack 函数表:收集序 fn_id → { nregs, code_offset(相对字节码段), code_len } 常量表:const_id → { value:u64 }(无 tag) 字节码:各函数指令连续 槽表:每条目 8B(addr:u32 + 预留:u32) 值段:全局 + FB 实例紧凑变宽 尾部:SHA-256(覆盖头部到值段末尾) ``` - 用 `isa` 的 `enc_*` 编码指令(变长 4/8/16/32B,前缀分区) - 工程哈希用 12.3 的 `compute_project_hash`(FNV-1a 64) - `--dump-map` 可打印变量/槽映射(对照源码 ↔ 反汇编) ## 错误 稳定前缀 `codegen error`: ```text codegen error: register overflow (>256) in function 'f' codegen error: constant table overflow codegen error: no storage for 'x' codegen error: no instance 't' in 'MAIN' codegen error: recursive CALL/FB nesting too deep ``` ## 完成标准 1. `line1` 能写出映像(函数字节码 + 槽表/值段 + 工程哈希 + SHA-256),`StbView` 校验通过 2. 跳转回填无悬空(`--disasm` 抽查) 3. 用例 1~9、13、15、17、18、20 编译出映像;负例(10/11/12/14/16/19)仍在前层被拒 4. 全部构建 + `ctest` 无回归(14/14)