# Debug: StmtGenMixin 拆分后 FuncTest 运行时崩溃 **Session ID**: stmtgen-split-crash **Status**: [OPEN] **Created**: 2026-08-17 ## 症状 - 拆分 StmtGenMixin.vp → 3 文件(顶层 763 / StmtGenDeclMixin 512 / StmtGenCifMixin 520)后 - PhaseC SUCCESS(FuncTest.exe 编译链接成功) - 运行时输出: ``` === FuncTest: 函数调用基准测试 === --- Test 1: basic function call --- PASS: add(10,20)=30 FAIL: add(10,20) expect 30 然后崩溃 ``` ## 回归窗口 - 拆分前(git restore 的原始 StmtGenMixin.vp 1715 行)FuncTest 应正常 - 拆分改动:类结构 StmtGenMixin → StmtGenDeclMixin → StmtGenCifMixin 三级继承;字段全留最底层 Cif;方法按功能域分 3 文件 ## 根因(已确认,证据充分) **崩溃根因:FuncGenMixin/ClassGenMixin 对"末句控制流"无条件生成 `unreachable`(追加89),而 llc O2 将 `br label %merge`(merge: unreachable)优化为 fall-through → 双分支执行。** - 证据链: 1. [FuncTest check 机器码](https://example.com):`testl %ecx; je fail; callq ok; (fall-through) callq fail` —— ok 后无跳转直接执行 fail → PASS+FAIL 双打印 + `int3` 崩溃 2. check IR:`br label %if.end.4` + `if.end.4: unreachable`(merge 可达却被判 unreachable) 3. [HandlesIf.vp L302-315]:分支**未 return 时 br 到 merge**(merge 可达);全 return 时不 br(不可达) 4. FuncGenMixin/ClassGenMixin 判定 `last_is_ctrl → build_unreachable` **不区分 merge 可达性** → bug - **与 StmtGenMixin 拆分逻辑无关**(HandlesIf/FuncGenMixin/ClassGenMixin 均未拆分);拆分暴露了该 bug(FuncTest check 是"末句 if + 无注解 + merge 可达"函数) ## 修复(追加90,7 文件) 1. `HandlesTranslator.vp`:Translator 加字段 `_ctrl_merge_reachable`(控制流 merge 可达性) 2. `HandlesIf.vp`:结尾按 then/else 分支 br 情况设置(无 else 恒 1;有 else 任一 br → 1;全 return → 0) 3. `HandlesWhile.vp`:cond_br 恒引用 end_bb → 设 1 4. `HandlesFor.vp`:7 个迭代器 end_bb 处设 1 5. `HandlesMatch.vp`:Handle 开头重置 0;br end_bb 处设 1 6. `FuncGenMixin.vp` L1140:`last_is_ctrl` 且 `_ctrl_merge_reachable == 0` 才 unreachable;可达 → 正常收尾(ConstZero/fail-fast) 7. `ClassGenMixin.vp` L4536:方法收尾路径同步 **验证要点**:check(merge 可达)→ ConstZero ret;get_struct_name(全 case return)→ unreachable 保持 ### 修正(追加90 v2:避免循环依赖) - **初版缺陷**:把 `_ctrl_merge_reachable` 放 Translator 实例字段 → HandlesMatch/If/While/For 方法体引用 `trans._ctrl_merge_reachable` 需 Translator 字段表,而 HandlesTranslator 依赖 HandlesMatch.NewMatchHandle → **HandlesMatch ↔ HandlesTranslator 循环依赖** → TPC 声明顺序死锁 → MatchHandle struct opaque、方法/工厂符号全缺失 → 连锁报错(1.LOG 证据) - **修正 v2**:改放 **HandlesBase 模块级全局** + `SetCtrlMergeReachable(val)` / `GetCtrlMergeReachable()`(无 struct 依赖,无循环;跨模块函数调用仅依赖 Phase1 stub) - HandlesTranslator.vp:字段回滚(已删) - HandlesBase.vp:加 `_ctrl_merge_reachable` + Set/Get 函数 - HandlesIf/While/For/Match:调用 `HandlesBase.SetCtrlMergeReachable(1/0)` - FuncGenMixin/ClassGenMixin:`HandlesBase.GetCtrlMergeReachable() == 0` + import HandlesBase - **已知残余风险**:模块级全局在多线程并行编译(Phase2Translator._parallel_translate_worker)下有竞争(_g_cif_eval_ok 先例同样存在且工作正常,暂接受) ### 真正的根因(1.LOG 定位,v3 修正) - **1.LOG L3:`HandlesMatch.vp: invalid syntax (line 976)`**——批量脚本 `_patch_ctrl.py` 的 replace 把 `HandlesBase.SetCtrlMergeReachable(1)` 误插到 `if nb_arr[ci]==0: br end_bb` 与 `else:` 之间 → **else 悬空语法错误** - **语法错误使 TPC 无法解析 HandlesMatch.vp** → MatchHandle struct opaque、Handle 方法无声明、NewMatchHandle 符号 undefined → 依赖它的 StmtGenCifMixin(NewMatchHandle 内 h.Trans = trans)/HandlesTranslator 连锁报错 - **v1(Translator 字段)/v2(HandlesBase 全局)都报同一 MatchHandle 错误的原因**:真正的障碍一直是语法错误,不是依赖方案 - **修正 v3**:删除 HandlesMatch.vp L975 误插行,if-else 结构恢复;其余 8 处 SetCtrlMergeReachable 位置已验证合法 ### 最终方案(v1 定稿,重建成功) - **v1/v2 的 MatchHandle 错误其实是 L975 语法错误**(误判为循环依赖) - **v3(HandlesBase 模块级全局)编译通过但 TPV 编译器运行时崩**(win32base Struct 收到全垃圾参数 → pool 悬空,插桩 DBG-ST 证实) - **定稿 v1**:`trans._ctrl_merge_reachable` 字段(线程安全,per-trans 无全局共享)+ 控制流翻译器写 + FuncGen/ClassGen 判定读 - **2026-08-17 用户验证:清缓存重建成功** ✓ - 待验证:FuncTest 运行(check 不再 PASS+FAIL 双打印)、68 测试回归 - **H1 跨模块继承字段布局错位**:Generator 组合布局中 StmtGenMixin 链字段块位置与各 Mixin 独立布局不一致 → self 字段访问偏移错误 → 函数表/状态错乱 - **H2 跨模块继承 struct 加载失败**:TPV 编译 Generator.vp 时从 vpi/stub 加载 StmtGenCifMixin 字段定义失败(opaque)→ 组合布局损坏 - **H3 self.method 沿 MRO 的 this 调整错误**:translate_stmt 调 Decl/Cif 方法时 this_adjust 偏移算错 → 方法内字段访问错乱 - **H4 模块级符号跨文件解析**:顶层/Decl 方法引用的模块级常量(ENUM_MAX 等)或符号解析失败降级 → 编译期静默错误 - **H5 编译链未清缓存**:tpv.exe 为旧产物(未含拆分逻辑)或新文件依赖顺序错误 → 生成代码错误 ## 证据计划 1. 确认用户验证方式(是否 --clean --rebuild-includes --clear-cache + Phase A 全量重建) 2. 确认拆分前 FuncTest 是否正常(回归基线) 3. 插桩定位崩溃点与字段偏移(TPV 侧或 FuncTest 侧) ## 状态日志 ### 2026-08-17 证据收集 2(TPV 自身产物 + asan 发现) - TPV 自身编译产物(output/0d5dfc518e9dbf1c.ll 4:54:31): - 三 class struct 布局完全一致:`{ i64, EnumEntry*, i32, i32 }` ✓ - translate_stmt 跨 Mixin 调用正确:`bitcast StmtGenMixin* → StmtGenDeclMixin*` + 调用 `9df7bd403cb4eb0a.StmtGenDeclMixin.translate_expr_stmt`(L4195-4196)✓ - **TPV 编译器自身(拆分后)编译正确** - **新证据:FuncTest.exe 是 ASan 插桩版**! - output/06/067f811511854c88.asan.ll 存在(4:54:45),内容是 **LLVM bitcode**(BC 头,非文本 IR) - FuncTest.exe 含 `__asan_init/__asan_report` 符号 - project.json linker flags **无 asan 运行时库**(只有 msvcrt/ucrt/pthread/mingwex/kernel32/user32) - **新假设 H6**:崩溃是 **--asan 编译模式**导致(main 插桩 + asan 运行时未正确初始化/链接),与 StmtGenMixin 拆分无关。拆分前 68 测试全过可能不带 --asan - 待确认:用户编译 FuncTest 的命令是否带 --asan;拆分前测试是否带 --asan - 分析 Test/FuncTest/.tpv_build/temp 的 .ll(4:46 产物): - `067f811511854c88.ll` L535:`call i32 @add(i32 10, i32 20)` ✓ 参数正确 - `748d0424e8976fa0.ll` L271-293(check):`br i1 %4, if.then.4/if.else.4` ✓ 条件跳转正确;ok/fail 各打印一次 ✓ - ok/fail 函数 L245-269:单次 printf ✓ - **结论:FuncTest 生成代码完全正常**。PASS+FAIL 双打印+崩溃非生成代码所致 - 倾向 H5:编译链产物混用(tpv.exe 未含拆分逻辑的旧产物/缓存复用);用户正在重编译 - 下一步:用户完成"清缓存 + Phase A 全量重建"后重跑 FuncTest 验证