fix(traceback): 修复 Install 注入位置与 Phase1 声明缺失

traceback 不被用户代码 import,--clean/--clear-cache 后 Phase1 不生成其声明,main 入口解析不到 Install 的 sha1 前缀;改为 --traceback 时把 traceback 及其传递依赖强制纳入 Phase1。Install 调用改用函数体当前 builder 发射,避免另建 builder 插到块尾导致 call 落在 ret 之后、ASan opt 报非法 IR。同时统一 global_ctors 元素类型为 {i32, i8*, i8*} 以匹配 ASan 的 appending 全局。
This commit is contained in:
TermiNexus committed 2026-10-19 18:43:31 +08:00
1 parent d02e6f21e3
commit 4c0dfb222a
5 files changed
+27 -9

No files matched your search

+1 -1
View File
@@ -295,7 +295,7 @@ def main() -> None:
_vlog().error("阶段一需要指定源目录,不支持文件列表")
sys.exit(1)
all_include_dirs: list[str] = list(project_include_dirs) if project_include_dirs else []
gen: Phase1Generator = Phase1Generator(src, temp_dir, include_dirs=all_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout)
gen: Phase1Generator = Phase1Generator(src, temp_dir, include_dirs=all_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout, traceback_enabled=args.traceback)
gen.run()
save_sha1_map(temp_dir, gen.sha1_map)
+11 -1
View File
@@ -55,9 +55,11 @@ def _SaveToGlobalCache(Sha1: str, Ext: str, TempPath: str) -> None:
class Phase1Generator:
"""阶段一:从源文件生成声明接口"""
def __init__(self, src_root: str, temp_dir: str, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None) -> None:
def __init__(self, src_root: str, temp_dir: str, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None, traceback_enabled: bool = False) -> None:
self.src_root: str = os.path.abspath(src_root)
self.temp_dir: str = os.path.abspath(temp_dir)
# --traceback:强制把 traceback 模块纳入声明生成集合(见 _get_needed_include_files)
self.traceback_enabled: bool = traceback_enabled
self.include_dirs: list[str] = include_dirs or []
self.sha1_map: dict[str, str] = {}
self.include_py_map: dict[str, str] = {}
@@ -152,6 +154,14 @@ class Phase1Generator:
deps: set[str] = get_file_dependencies(src_path, self.src_root)
imported_from_src.update(deps)
# --traceback:traceback 模块不会被用户代码 import(入口处由编译器直接
# 调用 traceback.Install),若只按 import 收集,Phase1 不生成它的
# .vpi/.stub.ll —— Phase2 翻译 main 时解析不到 Install 的 sha1 前缀
# (--clean/--clear-cache 后必然失败),traceback 自身依赖的 include 声明
# 也会缺失。故此处强制纳入,走与普通 import 相同的传递依赖闭包。
if self.traceback_enabled:
imported_from_src.add('traceback')
# 检测 assert 使用,自动添加 testcheck 依赖:assert 译为
# testcheck.check(...),该 include 须纳入 .vpi/.stub 生成与后续编译链接,
# 否则断言所在模块解析不到 testcheck.check(跨模块符号缺失)。
+6 -1
View File
@@ -785,7 +785,8 @@ def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_
hotspot_sites_file: str | None = None,
hotspot_sites_all: bool = False,
hotspot_callees: list[str] | None = None,
dwarf_enabled: bool = False) -> tuple[list, list]:
dwarf_enabled: bool = False,
traceback_enabled: bool = False) -> tuple[list, list]:
"""批次子任务:worker 内创建一个 Phase2Translator + pickle.load 一次,顺序翻译批内全部文件。
batch 每项: (i, src_path, out_path, sha1, prebuilt_ModuleSha1Map)
@@ -813,6 +814,9 @@ def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_
trans.hotspot_sites_file = hotspot_sites_file
trans.hotspot_sites_all = hotspot_sites_all
trans.hotspot_callees = hotspot_callees
# Ctrl+C 调用栈(--traceback):worker 子进程无父进程属性,须显式透传
# (False=未启用,零影响);main 入口据此解析 Install 符号并注入调用
trans.traceback_enabled = traceback_enabled
symbol_table_bytes: bytes | None = None
if shared_sym_pickle_path and os.path.exists(shared_sym_pickle_path):
@@ -1766,6 +1770,7 @@ class Phase2Translator:
hotspot_sites_all=self.hotspot_sites_all,
hotspot_callees=self.hotspot_callees,
dwarf_enabled=self.dwarf_enabled,
traceback_enabled=self.traceback_enabled,
)
futures[future] = (bidx, rel_first, len(batch))
+5 -2
View File
@@ -2067,8 +2067,11 @@ class FunctionHandle(BaseHandle):
if _tb_install:
_install_fn = Gen._CreateOrReuseFuncDecl(
_tb_install, ir.FunctionType(ir.IntType(32), []))
_entry_block = func.blocks[0] if func.blocks else func.append_basic_block('entry')
ir.IRBuilder(_entry_block).call(_install_fn, [])
# 必须用函数体当前 builder 发射:它的插入锚点正好位于参数初始化之后、
# 函数体之前。若另建 IRBuilder(entry) 插到块尾,HandleBodyLlvm 之后仍
# 从自己的锚点继续插入 → Install call 被顶到 ret 之后,产生非法 IR
# (opt 报 "expected instruction opcode: }")。
Gen.builder.call(_install_fn, [])
try:
self.Trans.BodyHandler.HandleBodyLlvm(func_body)
except Exception as _body_exc:
+4 -4
View File
@@ -2571,13 +2571,13 @@ class BaseGenMixin:
raise RuntimeError(
'--traceback: 模块内已存在 @llvm.global_ctors,'
'无法注入 traceback 启动期构造器(不静默降级)')
elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p])
# 元素类型必须与 ASan 的 llvm.global_ctors({i32, ptr, ptr})一致,
# 否则 llvm-link 因 appending 全局类型不同而丢弃 traceback 条目。
elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, i8p, i8p])
arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1)
entry_val: ir.Constant = ir.Constant(elem_ty, [
ir.Constant(i32, 65535),
# 传函数引用字符串(非 Function 对象):与 _RegisterHotspotSitesViaCtor 同法,
# 否则 llvmlite 会以 str(Function) 渲染整个 define 体
ir.Constant(void_fnptr, ctor.get_reference()),
ir.Constant(i8p, ctor.get_reference()),
ir.Constant(i8p, None),
])
gv: ir.GlobalVariable = ir.GlobalVariable(self.module, arr_ty, name='llvm.global_ctors')