From 4c0dfb222aa4bf8229c914cf5f53c2e9676d5717 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 19 Oct 2026 18:43:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(traceback):=20=E4=BF=AE=E5=A4=8D=20Install?= =?UTF-8?q?=20=E6=B3=A8=E5=85=A5=E4=BD=8D=E7=BD=AE=E4=B8=8E=20Phase1=20?= =?UTF-8?q?=E5=A3=B0=E6=98=8E=E7=BC=BA=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 全局。 --- lib/Projectrans/Config.py | 2 +- lib/Projectrans/Phase1Generator.py | 12 +++++++++++- lib/Projectrans/Phase2Translator.py | 7 ++++++- lib/core/Handles/HandlesFunctions.py | 7 +++++-- lib/core/LLVMCG/BaseGen.py | 8 ++++---- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/Projectrans/Config.py b/lib/Projectrans/Config.py index 45c89de..6582c27 100644 --- a/lib/Projectrans/Config.py +++ b/lib/Projectrans/Config.py @@ -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) diff --git a/lib/Projectrans/Phase1Generator.py b/lib/Projectrans/Phase1Generator.py index cd52c57..78a8f34 100644 --- a/lib/Projectrans/Phase1Generator.py +++ b/lib/Projectrans/Phase1Generator.py @@ -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(跨模块符号缺失)。 diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index fc29e3e..faed9af 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -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)) diff --git a/lib/core/Handles/HandlesFunctions.py b/lib/core/Handles/HandlesFunctions.py index 2ca5a56..31f885f 100644 --- a/lib/core/Handles/HandlesFunctions.py +++ b/lib/core/Handles/HandlesFunctions.py @@ -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: diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index b567353..235c295 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -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')