Files
TransPyC/lib/core/LLVMCG/DebugInfo.py
T
TermiNexus 43e7cdd2cc 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)
2026-09-16 19:29:51 +08:00

98 lines
4.1 KiB
Python
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.
from __future__ import annotations
import llvmlite.ir as ir
class DebugInfoEmitter:
"""函数级 DWARF 调试元数据发射器(llvmlite 文本 IR)。
每个 Module 一份 DICompileUnit + DIFile;每个 define 一份 DISubprogram
(is_distinct=True)挂在 func.metadata["dbg"] 上;函数入口第一条指令挂
DILocation(line=该函数 def 行,column=该行 col),使 LLVM 行表至少有一条
覆盖函数地址区间的行 —— ASan / llvm-symbolizer 反查函数内任意地址即可得到
"<file>.vp:函数起始行"。
仅在 --dwarf(或 --asan 自动开启)时被实例化;把 Module 落成 .ll 文本之前
必须调用 Finalize() 补模块级 !llvm.dbg.cu 与 !llvm.module.flags
(Debug Info Version = 3),否则 llc 会丢弃调试信息或报无效 DI 警告。
"""
def __init__(self, module: ir.Module, source_path: str) -> None:
self.Module: ir.Module = module
normalized: str = (source_path or '').replace('\\', '/')
head: str
sep: int = normalized.rfind('/')
if sep < 0:
head, self.FileName = '', normalized
else:
head, self.FileName = normalized[:sep], normalized[sep + 1:]
self.Directory: str = head
self._File: ir.DIValue | None = None
self._CompileUnit: ir.DIValue | None = None
self._SubroutineType: ir.DIValue | None = None
self.Finalized: bool = False
def _GetFile(self) -> ir.DIValue:
if self._File is None:
self._File = self.Module.add_debug_info("DIFile", {
"filename": self.FileName,
"directory": self.Directory,
})
return self._File
def _GetCompileUnit(self) -> ir.DIValue:
if self._CompileUnit is None:
# emissionKind 必须是 DIToken("FullDebug")(写成 "Full" llc 报无效 DI)
self._CompileUnit = self.Module.add_debug_info("DICompileUnit", {
"language": ir.DIToken("DW_LANG_C99"),
"file": self._GetFile(),
"producer": "TPC",
"isOptimized": False,
"runtimeVersion": 0,
"emissionKind": ir.DIToken("FullDebug"),
}, is_distinct=True)
return self._CompileUnit
def _GetSubroutineType(self) -> ir.DIValue:
if self._SubroutineType is None:
# DISubprogram 必须带 type,缺失会让 llc 崩溃(0xC0000005)
self._SubroutineType = self.Module.add_debug_info("DISubroutineType", {
"types": self.Module.add_metadata([None]),
})
return self._SubroutineType
def AttachFunction(self, func: ir.Function, Line: int, Column: int) -> ir.DIValue:
"""为 define(有函数体)建 DISubprogram 并返回其入口 DILocation。
仅对 define 调用;declare/extern 没有函数体,不得挂 !dbg。
"""
sp: ir.DIValue = self.Module.add_debug_info("DISubprogram", {
"name": func.name,
"scope": self._GetFile(),
"file": self._GetFile(),
"type": self._GetSubroutineType(),
"line": Line,
"scopeLine": Line,
"unit": self._GetCompileUnit(),
"spFlags": ir.DIToken("DISPFlagDefinition"),
}, is_distinct=True)
func.metadata["dbg"] = sp
return self.Module.add_debug_info("DILocation", {
"line": Line,
"column": Column,
"scope": sp,
})
def Finalize(self) -> None:
"""落 .ll 文本之前补模块级 named metadata;无任何函数时不做(no-op)。"""
if self.Finalized or self._CompileUnit is None:
return
self.Module.add_named_metadata("llvm.dbg.cu", self._CompileUnit)
flags: ir.MDValue = self.Module.add_metadata([
ir.Constant(ir.IntType(32), 2),
ir.MetaDataString(self.Module, "Debug Info Version"),
ir.Constant(ir.IntType(32), 3),
])
self.Module.add_named_metadata("llvm.module.flags", flags)
self.Finalized = True