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 反查函数内任意地址即可得到 ".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