12 KiB
TPV 对象化重构方案
基线:TPV
f63dcd7/ TransPyCe1cb560(对象化前最后稳定版本,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 |
注:表中 Handles.vp 行数为历史基线(TransPyV/App/lib/core/Handles 已删除);迁移去向:HandlesExprCall → Gen/Call(12 文件)、HandlesExpr → Gen/Expr、HandlesClassDef → Gen/Class、HandlesType → Gen/Type(位于 TransPyV/App/lib/core/Gen/)。*
1.2 重构目标
- 对象化:引入
Generator生成器对象(Mixin 组合),状态集中于self,与 TPC 的Translator + LlvmCodeGenerator架构对齐 - 膨胀治理:
translate_call7043 行 God Function 按职责拆散为CallGen的方法簇 - 不引入 TPC 代码:基于 TPC 设计理念 + Viper 约束自研;TPC 的 LLVMCG 仅作参考
- 每阶段可验证: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 演进阻力点
novirtual+ 硬编码 SIZE(BUILDER_SIZE=32/pool.alloc(96))强耦合——方法化不改布局则无影响;若转virtual需全量重写- 16 个跨模块 stub 访问器(
value_get_ty/paramnode_get_ty…)是规避 TransPyC stub 限制的产物——方法化后可逐步消除(依赖编译器缺陷修复,非本方案范围) LLVMType的pool.alloc(__sizeof__)+*p = v(旧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 # 入口:实际落地为组合:novirtual class Generator 持 46 个 GenMixin 子对象,非 Mixin 多继承
│ ├── 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 方法化(双轨过渡)
落地状态:未实施/已放弃,保留顶层函数调用(build_load → self.load 方法化未落地)。
4.1 原则
纯方法化平移,不改布局、不改语义:顶层函数 → 类方法,self 替代首参;类保持 novirtual + 硬编码 SIZE(不引入 vtable 继承)。
4.2 方法映射示例
# 现状(__builder.vp)
def build_load(builder, ty, ptr): ...
def build_gep_struct(builder, struct_ty, field_ty, ptr, field_idx): ...
# 目标(同文件,类方法)
novirtual 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 方法化
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)
# 采用 virtual(多态 Mixin 继承,与现有 ReturnHandle(HandlesBase.Mixin) 模式一致,
# 符合"多态类不用 novirtual"约定;数据类 llvmlite 仍为 novirtual)
virtual class Generator(BaseGenMixin, TypeGenMixin, StructGenMixin,
ExprGenMixin, CallGenMixin, FuncGenMixin,
ClassGenMixin, StmtGenMixin, ImportsGenMixin):
pass
注:本骨架为原始方案;实际落地为组合架构(novirtual class Generator 持 46 个 GenMixin 子对象),见 §3 图注。
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 签名瘦身规则
# 现状:六参连传
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
落地状态:未完全落地:pool/builder/mod/funcs 仍显式连传(CallGenMixin.translate_call),GenApi 保留模块级兼容壳。
5.4 CallGen 拆解(P4,膨胀治理重点)
translate_call 7043 行按分支簇拆为方法(纯移动,逻辑零改动):
| 方法簇 | 对应分支 |
|---|---|
_call_generic_ctor(node) |
泛型类构造 list[int](pool, n) |
_call_binop_cast(node) |
`(A |
_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. 风险与对策
| 风险 | 对策 |
|---|---|
novirtual + 硬编码 SIZE 布局 |
方法化不改布局;Generator 用 virtual(数据类保持 novirtual) |
| 跨模块 stub 访问器 | 方法化后保留访问器(消除依赖编译器缺陷修复,延后) |
| Viper 循环 import | 保持现有"延迟 import"模式 |
| 迁移引入行为差异 | 每阶段 .text.ll 基线 diff 校验 |
| REnum LLVMType 误转继承 | 明确保持 REnum 构造模式 |
| 工作量/回归风险 | 纯移动优先(P0-P5),语义重构仅 P6(可选) |
8. 验收标准(最终)
- TPV bootstrap 编译通过(三重编译流程)
- 全部 Test 项目编译 + 运行通过
- IR 输出与基线一致(无行为变化)
HandlesExprCall.vp7043 行 →CallGen各方法簇 < 1500 行- 所有 Handles 调用不再六参连传(
self持状态)