Files
TransPyC/wiki/14-oo-refactor.md
T
TermiNexus fc93a28ec6 refactor(llvmlite): P0 对象化 - IRBuilder/LLVMModule 方法化(70 方法+一行委托兼容)
- IRBuilder 新增 62 个实例方法(load/store/gep/call/binop/cast 等), 顶层函数保留为一行委托, 调用方零改动
- LLVMModule 新增 8 个实例方法(add_function/add_global/add_named_type/PrintModule 等)
- 类字段/硬编码 SIZE/逻辑/IR 输出零变化, GetDiagnostics 无错误
- 新增 wiki/14-oo-refactor.md 对象化重构方案(P0-P6)
2026-08-09 22:54:01 +08:00

226 lines
12 KiB
Markdown
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.
# TPV 对象化重构方案
> 基线:TPV `f63dcd7` / TransPyC `e1cb560`(对象化前最后稳定版本,2026-08-09)
> 状态:方案讨论稿,未开始实施
## 1. 背景与目标
### 1.1 现状问题
TPV 编译器当前是"**数据对象 + 行为函数式**"架构:
- `includes/llvmlite/`:19 个数据结构类已对象化(`IRBuilder`/`LLVMModule`/`Value`/`LLVMType`…),但**所有操作是"首参传 self"的顶层函数**(`build_load(builder, ...)`、`module_add_function(mod, ...)`),与 C 风格 API 一致
- `TransPyV/App/lib/core/Handles/`:**纯函数模块**(`translate_call(pool, builder, mod, node, funcs, count, trans)` 六参连传),且存在严重膨胀:
| 文件 | 大小 | 行数 |
|---|---|---|
| HandlesExprCall.vp | 362KB | 7043 |
| HandlesExpr.vp | 181KB | 3715 |
| HandlesClassDef.vp | 172KB | 4010 |
| HandlesType.vp | 102KB | 2360 |
### 1.2 重构目标
1. **对象化**:引入 `Generator` 生成器对象(Mixin 组合),状态集中于 `self`,与 TPC 的 `Translator + LlvmCodeGenerator` 架构对齐
2. **膨胀治理**:`translate_call` 7043 行 God Function 按职责拆散为 `CallGen` 的方法簇
3. **不引入 TPC 代码**:基于 TPC 设计理念 + Viper 约束自研;TPC 的 LLVMCG 仅作参考
4. **每阶段可验证**:bootstrap 编译 + 全 Test 回归,IR 输出零变化
## 2. 现状盘点(已实测)
### 2.1 llvmlite 模块(`includes/llvmlite/`,7 文件)
| 文件 | 内容 | 对象化程度 |
|---|---|---|
| `__init__.vp` (422行) | 10 工厂函数 + `LLVMModuleCore`(27 个薄封装方法) | **已有方法化先例** |
| `__builder.vp` (1336行) | **60+ 个 `build_*` 顶层函数** + `IRBuilder`(4 字段:Pool/Func/CurBlock/Counter) | **IRBuilder 是天然状态对象,签名已按 self 优先** |
| `__types.vp` (748行) | 34 类型工厂/打印/深拷贝 + `LLVMType`(**REnum tagged union**)+ `LLVMTypeCore`(19 方法) | REnum 应保留,Core 是方法化先例 |
| `__module.vp` (565行) | 19 函数 + `LLVMModule`(12 字段容器,含 NamedType 链表) | 纯数据容器,可平移 |
| `__function.vp` (798行) | 35 函数 + `Line/BasicBlock/Param/Function`(GSListNode 链) | 纯数据结构 |
| `__values.vp` (230行) | 14 函数 + `Value`(5 字段) | 纯数据结构 |
| `__verify.vp` (399行) | 15 验证函数 + `VerifyResult` | 独立,最后迁移 |
**关键结论**:
- **IRBuilder 对象化零状态迁移成本**:4 字段已齐,60+ 顶层函数改方法只需 `self` 替代首参,签名一一对应
- **LLVMModule 对象化有先例**:`LLVMModuleCore`/`LLVMTypeCore` 已证明"方法化封装"可行(但仍为薄壳,核心 `module_add_named_type`/打印仍是顶层函数)
- **LLVMType 保持 REnum**:变体即"子类",不应转继承层次
### 2.2 演进阻力点
1. `@t.NoVTable` + 硬编码 SIZE(`BUILDER_SIZE=32`/`pool.alloc(96)`)强耦合——**方法化不改布局则无影响**;若转 `@t.CVTable` 需全量重写
2. 16 个跨模块 stub 访问器(`value_get_ty`/`paramnode_get_ty`…)是规避 TransPyC stub 限制的产物——方法化后可逐步消除(**依赖编译器缺陷修复,非本方案范围**)
3. `LLVMType` 的 `pool.alloc(__sizeof__)` + `c.DerefAs` 构造模式——保留为构造方法
## 3. 目标架构
```
includes/llvmlite/ # ① 方法化平移(P0)
├── __builder.vp # IRBuilder + 60 方法(build_load → self.load)
├── __module.vp # LLVMModule + 方法 + 命名类型注册
├── __types.vp # LLVMType(REnum) + 构造方法
├── __values.vp / __function.vp / __verify.vp
└── __init__.vp # 薄兼容层:旧顶层函数委托新方法(调用方零改动)
TransPyV/App/lib/core/
├── Gen/ # ② 生成器对象(P1-P6)
│ ├── Generator.vp # 入口:@t.CVTable,Mixin 组合,visit 分派
│ ├── BaseGenMixin.vp # 状态/Pool/Module/Builder/SymTab + 错误/trace
│ ├── TypeGenMixin.vp # 类型映射/注解解析(← HandlesType)
│ ├── StructGenMixin.vp # struct 注册/字段/继承/vtable(← HandlesStruct+ClassDef 布局)
│ ├── ExprGenMixin.vp # 表达式/二元/一元(← HandlesExpr+ExprOps)
│ ├── CallGenMixin.vp # 调用分派/方法/类型转换/c.*/stdarg(← HandlesExprCall 拆解)
│ ├── FuncGenMixin.vp # 函数/方法声明与翻译(← HandlesFunctions)
│ ├── ClassGenMixin.vp # 类定义/泛型特化(← HandlesClassDef)
│ ├── StmtGenMixin.vp # 语句(← Body/If/While/For/Assign/AnnAssign/Return/Match/Nonlocal)
│ └── ImportsGenMixin.vp # 导入(← HandlesImports)
└── Handles/ # ③ 迁移完成后:删除或保留为薄壳(P6 决定)
```
## 4. P0:llvmlite 方法化(双轨过渡)
### 4.1 原则
**纯方法化平移,不改布局、不改语义**:顶层函数 → 类方法,`self` 替代首参;类保持 `@t.NoVTable` + 硬编码 SIZE(不引入 vtable 继承)。
### 4.2 方法映射示例
```python
# 现状(__builder.vp)
def build_load(builder, ty, ptr): ...
def build_gep_struct(builder, struct_ty, field_ty, ptr, field_idx): ...
# 目标(同文件,类方法)
@t.NoVTable
class IRBuilder:
Pool, Func, CurBlock, Counter
def load(self, ty, ptr): ... # 原函数体,builder → self
def gep_struct(self, struct_ty, field_ty, ptr, field_idx): ...
# 兼容层(__init__.vp):旧函数保留为薄包装
def build_load(builder, ty, ptr):
return builder.load(ty, ptr)
```
### 4.3 覆盖清单(60+ 方法)
| 类别 | 方法 |
|---|---|
| 内存 | alloca / alloca_aligned / alloca_at_entry / load / store |
| 整数二元 | add/sub/mul/sdiv/udiv/srem/urem/and/or/xor/shl/lshr/ashr |
| 浮点 | fadd/fsub/fmul/fdiv/frem/fneg |
| 比较 | icmp / fcmp |
| 控制流 | br / cond_br / ret / ret_void / unreachable / switch / select |
| 调用 | call / call_indirect |
| 转换 | bitcast/sext/trunc/zext/ptrtoint/inttoptr/fpext/fptrunc/fp2si/si2fp/fp2ui/ui2fp |
| GEP | gep / gep_array / gep_struct |
| Phi/原子/杂项 | phi / atomicrmw / cmpxchg / va_arg / inline_asm |
### 4.4 Module 方法化
```python
def add_function(self, func): ...
def add_global(self, gv): ...
def add_named_type(self, ty): ...
def ensure_opaque_for_type(self, ty): ...
def set_target(self, triple): ...
def print_ir(self, mode): ...
```
### 4.5 验证
- bootstrap 编译 TPV(自举)通过
- 全 Test 回归:编译产物 `.text.ll` 与基线**逐字节一致**(兼容层保证行为不变)
- 兼容层在 P6 确认无调用方后删除
## 5. P1-P6:Generator 对象迁移
### 5.1 Generator 骨架(P1)
```python
# 采用 @t.CVTable(多态 Mixin 继承,与现有 ReturnHandle(HandlesBase.Mixin) 模式一致,
# 符合"多态类不用 NoVTable"约定;数据类 llvmlite 仍为 NoVTable)
@t.CVTable
class Generator(BaseGenMixin, TypeGenMixin, StructGenMixin,
ExprGenMixin, CallGenMixin, FuncGenMixin,
ClassGenMixin, StmtGenMixin, ImportsGenMixin):
pass
```
BaseGenMixin 状态字段(迁移自 HandlesTranslator):
`Pool` / `Module` / `Builder` / `SymTab` / `FuncTable` / `FuncCount` / `StructTable` / `VTableCache` / `ImportedModules` / `FromImports` / `ModuleSha1` / `CurFunc` / `CurBuilder` / `CurClassName` / 错误 trace 栈
### 5.2 Mixin 方法映射表(P2-P5)
| Mixin | 来源(现状函数) | 迁移后方法 |
|---|---|---|
| TypeGenMixin | `HandlesType.map_t_type` / `resolve_annotation_type` / CDefine 表 | `self.map_type(name)` / `self.resolve_annotation(node)` |
| StructGenMixin | `HandlesStruct.register_struct` / `find_struct` / `lookup_field`;`HandlesClassDef` 布局/vtable | `self.register_struct(...)` / `self.lookup_field(...)` / `self.gen_vtable(cls)` |
| ExprGenMixin | `HandlesExpr.translate_value` / `translate_name_value` / 属性访问;`HandlesExprOps.translate_binop` / `try_unary_operator_overload` | `self.visit_value(node)` / `self.visit_binop(node)` / `self.visit_attr(node)` |
| CallGenMixin | `HandlesExprCall.translate_call`(**拆解**)+ `_translate_method_call` / `_translate_t_type_cast` / `translate_c_addr` 等 | `self.visit_call(node)` → `self._call_method(...)` / `self._call_t_cast(...)` / `self._call_c_addr(...)` |
| FuncGenMixin | `HandlesFunctions.translate_function_def` / `_mangle_name` | `self.translate_func(node)` / `self.mangle_name(name)` |
| ClassGenMixin | `HandlesClassDef.translate_class_def` / `_specialize_generic_class` | `self.translate_class(node)` / `self.specialize_generic(...)` |
| StmtGenMixin | `HandlesBody.translate_stmt` + If/While/For/Assign/AnnAssign/Return/Match/Nonlocal 各 Handle | `self.visit_stmt(node)` |
| ImportsGenMixin | `HandlesImports` Import/ImportFrom | `self.import_module(...)` |
### 5.3 签名瘦身规则
```python
# 现状:六参连传
translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
# 目标:self 持状态
self.visit_call(node) # 内部用 self.Pool/self.Builder/self.Module/self.FuncTable
```
- 中间态:`self.visit_call(node, funcs_ptr, func_count)`(表引用局部化后去参)
- 迁移顺序**从叶子到根**(先无依赖者),每迁移一个 Mixin 立即 bootstrap
### 5.4 CallGen 拆解(P4,膨胀治理重点)
`translate_call` 7043 行按分支簇拆为方法(**纯移动,逻辑零改动**):
| 方法簇 | 对应分支 |
|---|---|
| `_call_generic_ctor(node)` | 泛型类构造 `list[int](pool, n)` |
| `_call_binop_cast(node)` | `(A|t.CPtr)(val)` BinOp 强转 |
| `_call_c_special(node)` | `c.Deref/Addr/Set/Load/Asm/LLVMIR/CIf/CIfdef` 系列 |
| `_call_stdarg(node)` | `va_arg/arg/va_start/va_end` |
| `_call_builtin_cast(node)` | `str/bytes/int/bool(x)` |
| `_call_t_cast(node)` | `t.CInt(x)` / `t.CVoid(x, CPtr)` / 裸名 typedef |
| `_call_class_sizeof(node)` | `ClassName.__sizeof__()` |
| `_call_method(node)` | `self.field.method(...)` / `(expr).method(...)` |
| `_call_plain(node)` | 普通函数/跨模块调用 |
## 6. 迁移计划总表
| 阶段 | 内容 | 产出 | 验证 |
|---|---|---|---|
| P0 | llvmlite 方法化 + 兼容层 | Builder/Module 方法化 | bootstrap + 全回归(IR 零差异) |
| P1 | Generator 骨架 + trans 字段迁入 | `Gen/Generator.vp` 空状态对象 | bootstrap |
| P2 | TypeGen / StructGen | 2 个 Mixin 文件 | bootstrap |
| P3 | ExprGen(含 ExprOps) | 1-2 个 Mixin | bootstrap |
| P4 | CallGen(拆 7043 行) | 9 个方法簇 | bootstrap |
| P5 | FuncGen / ClassGen / StmtGen / ImportsGen | 4 个 Mixin | bootstrap |
| P6 | 删除 Handles 壳 / 兼容层 / ast visitor(可选) | 收尾 | bootstrap + 全回归 |
**每阶段验收**:bootstrap 编译通过 → 全 Test 项目编译通过 → `.text.ll` 与基线 diff 为空(或仅行号差异)。
## 7. 风险与对策
| 风险 | 对策 |
|---|---|
| `@t.NoVTable` + 硬编码 SIZE 布局 | 方法化不改布局;Generator 用 `@t.CVTable`(数据类保持 NoVTable) |
| 跨模块 stub 访问器 | 方法化后保留访问器(消除依赖编译器缺陷修复,延后) |
| Viper 循环 import | 保持现有"延迟 import"模式 |
| 迁移引入行为差异 | 每阶段 `.text.ll` 基线 diff 校验 |
| REnum LLVMType 误转继承 | 明确保持 REnum 构造模式 |
| 工作量/回归风险 | 纯移动优先(P0-P5),语义重构仅 P6(可选) |
## 8. 验收标准(最终)
1. TPV bootstrap 编译通过(三重编译流程)
2. 全部 Test 项目编译 + 运行通过
3. IR 输出与基线一致(无行为变化)
4. `HandlesExprCall.vp` 7043 行 → `CallGen` 各方法簇 < 1500 行
5. 所有 Handles 调用不再六参连传(`self` 持状态)