Files
TransPyC/debug-stmtgen-split-crash.md
T
2026-08-22 02:35:30 +08:00

102 lines
7.7 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.
# 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 验证