feat(tpc): 函数级 DWARF 调试元数据 + 合并 IR 元数据规整

DWARF: 新增 LLVMCG/DebugInfo.py(DebugInfoEmitter)——每模块一个 DIFile/DICompileUnit、每个 define 一个 DISubprogram 并在函数入口首条指令挂 DILocation,模块落盘前补 !llvm.dbg.cu 与 !llvm.module.flags(Debug Info Version 3)。开关:新增 --dwarf(默认关闭),dwarf_enabled = args.dwarf or args.asan,沿 Phase2Translator → _translate_file → Translator.DwarfEnabled → LlvmCodeGenerator.dwarf_enabled 下传。define 之外的 declare 不挂。include 库刻意不给 DI(其 .obj 进跨项目共享的 includes.binary 缓存,注入会污染缓存)

关键约束(踩过的坑,已在探针中实测):DICompileUnit 与 DISubprogram 必须 is_distinct=True;emissionKind 用 FullDebug 而非 Full;spFlags 用 DISPFlagDefinition;DISubprogram 必带 type=DISubroutineType,缺了 llc 会崩 0xC0000005;模块级必须有 !llvm.dbg.cu + Debug Info Version 3

IRMetadataFix: 新增 SanitizeIRMetadata,在 _llc_compile 入口统一规整 IR——BuildCombinedIR 是多模块 IR 文本直接拼接,--dwarf 后每个片段自带 !0..!N 与两份命名元数据,22383 行样本里 !0/!1 各重复 67 次导致 opt/llc 报 Metadata id is already used。实现:按块重编号 !N 并改写 !dbg/命名元数据引用、!llvm.dbg.cu 全量聚合、其它命名元数据全同去重;无元数据文件逐字节 no-op、幂等(1.7MB/0.30s)

顺带修 _AddWeakComdat:函数头正则跨行吞换行,--dwarf 把 !dbg 放到 define 行尾后 comdat 被插到 ! 附着之后,llc 报 expected { in function body(现场 52 个 define 中 36 个中招);改为插在附着之前,无 !dbg 时产物逐字节不变

已自证:llc/opt RC=0、行表含 .vp 文件名与行号、opt -passes=forceattrs,asan 与 llvm-link/always-inline 均不丢 !dbg。已知待办:llc 现报 inlinable function call ... must have a !dbg location ⇒ 需补语句/调用点级 !dbg(否则警告 ignoring invalid debug info 会丢弃整份 DI)
This commit is contained in:
TermiNexus committed 2026-09-16 19:29:51 +08:00
1 parent 033c0a7771
commit 43e7cdd2cc
9 files changed
+480 -27

No files matched your search

+23 -7
View File
@@ -1782,22 +1782,22 @@ class FunctionHandle(BaseHandle):
# 导致方法调用查找 ndarray.sum 而非 ndarray[double].sum。
if param_name in Gen.var_struct_class:
_existing_ps: str = Gen.var_struct_class[param_name]
# 指针占位名(rowptr / {sha1}.rowptr / rowptr[...])是模块限定
# 指针占位名(rawptr / {sha1}.rawptr / rawptr[...])是模块限定
# struct 联合(llvmlite.Function | t.CPtr)被 resolver 退化
# 的产物,必须允许 FindStructNameInAnnotation 短名修正
# (否则 func.IsVarArg → "属性在类 'rowptr' 中找不到")。
if not (_existing_ps == 'rowptr' or '.rowptr' in _existing_ps
or _existing_ps.startswith('rowptr[')):
# (否则 func.IsVarArg → "属性在类 'rawptr' 中找不到")。
if not (_existing_ps == 'rawptr' or '.rawptr' in _existing_ps
or _existing_ps.startswith('rawptr[')):
continue
found_struct: str | None = FindStructNameInAnnotation(arg.annotation, Gen.structs)
if found_struct:
Gen.var_struct_class[param_name] = found_struct
# 普通类注解形参(非 BinOp 联合):rowptr[X] / t.CPtr[X] / X / t.X
# 普通类注解形参(非 BinOp 联合):rawptr[X] / t.CPtr[X] / X / t.X
# 一律按"声明类"登记 var_struct_class。避免按 param.type.pointee 匹配
# 到泛型父特化(如 class Function(GSListNode[Function]) 的形参 func 被
# 登记成 GSListNode[Function],其 struct 仅含 Next → func.Blocks 访问
# fast-fail "属性 'Blocks' 在类 'GSListNode[Function]' 中找不到")。
# 泛型占位(rowptr[T])的 T 不在 Gen.structs,自动跳过、保留 pointee 匹配。
# 泛型占位(rawptr[T])的 T 不在 Gen.structs,自动跳过、保留 pointee 匹配。
for i, arg in enumerate(Node.args.args):
if arg.annotation is None:
continue
@@ -1815,7 +1815,7 @@ class FunctionHandle(BaseHandle):
elif isinstance(ann, ast.Subscript):
subv = ann.value
subname = getattr(subv, 'id', None) or getattr(subv, 'attr', '')
if subname in ('rowptr', 'CPtr', 'Ptr'):
if subname in ('rawptr', 'CPtr', 'Ptr'):
slc = ann.slice
if isinstance(slc, ast.Name):
target = slc.id
@@ -1943,6 +1943,22 @@ class FunctionHandle(BaseHandle):
except Exception:
pass
raise
# 函数级 DWARF 调试元数据(--dwarf / --asan 自动开启):为每个 define 建
# DISubprogram 挂 func.metadata["dbg"],并在入口块第一条指令挂 DILocation
# (line/column = 该 def 语句的位置),使 DWARF 行表至少有一条覆盖函数地址
# 区间的行。declare/extern 在此之前已 return,不会被挂 !dbg;未启用时
# _GetDebugInfo() 返回 None,不产生任何 IR 差异(零影响)。
_DbgInfo = Gen._GetDebugInfo()
if _DbgInfo is not None:
try:
_DbgLoc = _DbgInfo.AttachFunction(
func, getattr(Node, 'lineno', 0) or 0, getattr(Node, 'col_offset', 0) + 1)
if EntryBlock.instructions:
EntryBlock.instructions[0].metadata['dbg'] = _DbgLoc
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"发射调试元数据失败: {_e}", "Exception")
if not Gen.builder.block.is_terminated:
if 'naked' in fn_attrs:
Gen.builder.unreachable()