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)
98 lines
4.1 KiB
Python
98 lines
4.1 KiB
Python
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
|