[DebugInfo] Add macro tracking support to DebugInfoFinder (#179931)

Extend `DebugInfoFinder` to collect and expose macro debug information
(`DIMacro` and `DIMacroFile` nodes).

Also update `ModuleDebugInfoPrinter` to display macro information
including the macro type, name, value, and source location.

-----

The motivation behind this PR is that `DebugInfoFinder` is key for the
[SPIRV-LLVM-Translator](https://github.com/KhronosGroup/SPIRV-LLVM-Translator)
and also for future support of debug info in the SPIRV backend in LLVM.

This new lookup of `DIMacro` with their `DIMacroFile` when available
simplifies the logic around the translation for this debug information.
This commit is contained in:
Manuel Carrasco 2026-02-06 15:33:04 +00:00 committed by GitHub
parent 52955f8b27
commit c8d8e5e210
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 108 additions and 0 deletions

View file

@ -127,13 +127,16 @@ private:
void processScope(DIScope *Scope);
void processType(DIType *DT);
void processImportedEntity(const DIImportedEntity *Import);
void processMacroNode(DIMacroNode *Macro, DIMacroFile *CurrentMacroFile);
bool addCompileUnit(DICompileUnit *CU);
bool addGlobalVariable(DIGlobalVariableExpression *DIG);
bool addScope(DIScope *Scope);
bool addSubprogram(DISubprogram *SP);
bool addType(DIType *DT);
bool addMacro(DIMacro *Macro, DIMacroFile *MacroFile);
public:
using DIMacroEntry = std::pair<DIMacro *, DIMacroFile *>;
using compile_unit_iterator =
SmallVectorImpl<DICompileUnit *>::const_iterator;
using subprogram_iterator = SmallVectorImpl<DISubprogram *>::const_iterator;
@ -141,6 +144,7 @@ public:
SmallVectorImpl<DIGlobalVariableExpression *>::const_iterator;
using type_iterator = SmallVectorImpl<DIType *>::const_iterator;
using scope_iterator = SmallVectorImpl<DIScope *>::const_iterator;
using macro_iterator = SmallVectorImpl<DIMacroEntry>::const_iterator;
iterator_range<compile_unit_iterator> compile_units() const { return CUs; }
@ -154,11 +158,14 @@ public:
iterator_range<scope_iterator> scopes() const { return Scopes; }
iterator_range<macro_iterator> macros() const { return Macros; }
unsigned compile_unit_count() const { return CUs.size(); }
unsigned global_variable_count() const { return GVs.size(); }
unsigned subprogram_count() const { return SPs.size(); }
unsigned type_count() const { return TYs.size(); }
unsigned scope_count() const { return Scopes.size(); }
unsigned macro_count() const { return Macros.size(); }
private:
SmallVector<DICompileUnit *, 8> CUs;
@ -166,6 +173,7 @@ private:
SmallVector<DIGlobalVariableExpression *, 8> GVs;
SmallVector<DIType *, 8> TYs;
SmallVector<DIScope *, 8> Scopes;
SmallVector<DIMacroEntry, 8> Macros;
SmallPtrSet<const MDNode *, 32> NodesSeen;
};

View file

@ -103,6 +103,31 @@ static void printModuleDebugInfo(raw_ostream &O, const Module *M,
}
O << '\n';
}
for (const auto &MacroEntry : Finder.macros()) {
const DIMacro *Macro = MacroEntry.first;
const DIMacroFile *MacroFile = MacroEntry.second;
O << "Macro: ";
auto MacroType = dwarf::MacinfoString(Macro->getMacinfoType());
if (!MacroType.empty())
O << MacroType;
else
O << "unknown-macinfo(" << Macro->getMacinfoType() << ")";
O << " '" << Macro->getName() << "'";
if (!Macro->getValue().empty())
O << " = '" << Macro->getValue() << "'";
if (MacroFile && MacroFile->getFile()) {
const DIFile *File = MacroFile->getFile();
printFile(O, File->getFilename(), File->getDirectory(),
MacroFile->getLine());
} else {
O << " at line " << Macro->getLine();
}
O << '\n';
}
}
ModuleDebugInfoPrinterPass::ModuleDebugInfoPrinterPass(raw_ostream &OS)

View file

@ -176,6 +176,7 @@ void DebugInfoFinder::reset() {
GVs.clear();
TYs.clear();
Scopes.clear();
Macros.clear();
NodesSeen.clear();
}
@ -212,6 +213,8 @@ void DebugInfoFinder::processCompileUnit(DICompileUnit *CU) {
processSubprogram(cast<DISubprogram>(RT));
for (auto *Import : CU->getImportedEntities())
processImportedEntity(Import);
for (auto *Macro : CU->getMacros())
processMacroNode(Macro, nullptr);
}
void DebugInfoFinder::processInstruction(const Module &M,
@ -275,6 +278,36 @@ void DebugInfoFinder::processImportedEntity(const DIImportedEntity *Import) {
processScope(M->getScope());
}
/// Process a macro debug info node (DIMacroNode).
///
/// A DIMacroNode is one of two types:
/// - DIMacro: A single macro definition. Add it to the Macros list along with
/// its containing DIMacroFile.
/// - DIMacroFile: A file containing macros. Recursively process all nested
/// macro nodes within it (avoiding duplicates by tracking visited nodes).
void DebugInfoFinder::processMacroNode(DIMacroNode *Macro,
DIMacroFile *CurrentMacroFile) {
if (!Macro)
return;
if (auto *M = dyn_cast<DIMacro>(Macro)) {
addMacro(M, CurrentMacroFile);
return;
}
auto *MF = dyn_cast<DIMacroFile>(Macro);
assert(MF &&
"Expected a DIMacroFile (it can't be any other type at this point)");
// Check if we've already seen this macro file to avoid infinite recursion
if (!NodesSeen.insert(MF).second)
return;
// Recursively process nested macros in the macro file
for (auto *Element : MF->getElements())
processMacroNode(Element, MF);
}
void DebugInfoFinder::processScope(DIScope *Scope) {
if (!Scope)
return;
@ -389,6 +422,17 @@ bool DebugInfoFinder::addScope(DIScope *Scope) {
return true;
}
bool DebugInfoFinder::addMacro(DIMacro *Macro, DIMacroFile *MacroFile) {
if (!Macro)
return false;
if (!NodesSeen.insert(Macro).second)
return false;
Macros.push_back(std::make_pair(Macro, MacroFile));
return true;
}
/// Recursively handle DILocations in followup metadata etc.
///
/// TODO: If for example a followup loop metadata would reference itself this

View file

@ -0,0 +1,31 @@
; RUN: opt -passes='print<module-debuginfo>' -disable-output 2>&1 < %s \
; RUN: | FileCheck %s
; Macro hierarchy graph:
; CompileUnit
; ├── MacroFile: ./def.c (!2)
; │ ├── Define Macro: 'SIZE' = '5' (!6)
; │ ├── Undef Macro: 'SIZE' (!7)
; │ └── MacroFile: ./def.nested.c (!4)
; │ └── Undef Macro: 'BAZ' (!9)
; └── Define Macro: 'BAZ' (!8)
; CHECK: Macro: DW_MACINFO_define 'SIZE' = '5' from ./def.c
; CHECK: Macro: DW_MACINFO_undef 'SIZE' from ./def.c
; CHECK: Macro: DW_MACINFO_undef 'BAZ' from ./def.nested.c
; CHECK: Macro: DW_MACINFO_define 'BAZ' at line 1
!llvm.module.flags = !{!0, !10}
!llvm.dbg.cu = !{!1}
!0 = !{i32 7, !"Dwarf Version", i32 0}
!1 = distinct !DICompileUnit(language: DW_LANG_OpenCL, file: !3, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, macros: !{!2, !8})
!2 = !DIMacroFile(file: !3, nodes: !{!6, !7, !4})
!3 = !DIFile(filename: "def.c", directory: ".")
!4 = !DIMacroFile(file: !5, nodes: !{!9})
!5 = !DIFile(filename: "def.nested.c", directory: ".")
!6 = !DIMacro(type: DW_MACINFO_define, line: 1, name: "SIZE", value: "5")
!7 = !DIMacro(type: DW_MACINFO_undef, line: 1, name: "SIZE")
!8 = !DIMacro(type: DW_MACINFO_define, line: 1, name: "BAZ")
!9 = !DIMacro(type: DW_MACINFO_undef, line: 1, name: "BAZ")
!10 = !{i32 2, !"Debug Info Version", i32 3}