Files
Viper_Wiki/14-oo-refactor.md
T

12 KiB
Raw Blame History

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

注:表中 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 重构目标

  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. novirtual + 硬编码 SIZE(BUILDER_SIZE=32/pool.alloc(96))强耦合——方法化不改布局则无影响;若转 virtual 需全量重写
  2. 16 个跨模块 stub 访问器(value_get_ty/paramnode_get_ty…)是规避 TransPyC stub 限制的产物——方法化后可逐步消除(依赖编译器缺陷修复,非本方案范围)
  3. 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. 验收标准(最终)

  1. TPV bootstrap 编译通过(三重编译流程)
  2. 全部 Test 项目编译 + 运行通过
  3. IR 输出与基线一致(无行为变化)
  4. HandlesExprCall.vp 7043 行 → CallGen 各方法簇 < 1500 行
  5. 所有 Handles 调用不再六参连传(self 持状态)