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

Manuel Carrasco via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 5 06:29:08 PST 2026


https://github.com/mgcarrasco updated https://github.com/llvm/llvm-project/pull/179931

>From 3e240e0342cdb76dc5869b1817795a7c19e4215d Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Thu, 5 Feb 2026 06:03:52 -0600
Subject: [PATCH 1/4] [DebugInfo] Add macro tracking support to DebugInfoFinder

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.
---
 llvm/include/llvm/IR/DebugInfo.h              |  8 ++++
 llvm/lib/Analysis/ModuleDebugInfoPrinter.cpp  | 25 +++++++++++
 llvm/lib/IR/DebugInfo.cpp                     | 44 +++++++++++++++++++
 .../Generic/debuginfofinder-macros.ll         | 32 ++++++++++++++
 4 files changed, 109 insertions(+)
 create mode 100644 llvm/test/DebugInfo/Generic/debuginfofinder-macros.ll

diff --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h
index 922658927ad6a..cb8f6fa1b132b 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -127,13 +127,16 @@ class DebugInfoFinder {
   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 @@ class DebugInfoFinder {
       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 @@ class DebugInfoFinder {
 
   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 @@ class DebugInfoFinder {
   SmallVector<DIGlobalVariableExpression *, 8> GVs;
   SmallVector<DIType *, 8> TYs;
   SmallVector<DIScope *, 8> Scopes;
+  SmallVector<DIMacroEntry, 8> Macros;
   SmallPtrSet<const MDNode *, 32> NodesSeen;
 };
 
diff --git a/llvm/lib/Analysis/ModuleDebugInfoPrinter.cpp b/llvm/lib/Analysis/ModuleDebugInfoPrinter.cpp
index 9d53c37461ba8..06ead92201d77 100644
--- a/llvm/lib/Analysis/ModuleDebugInfoPrinter.cpp
+++ b/llvm/lib/Analysis/ModuleDebugInfoPrinter.cpp
@@ -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)
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index a357621f1e3b2..54309ac60ecef 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -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;
+  }
+
+  if (auto *MF = dyn_cast<DIMacroFile>(Macro)) {
+    // 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()) {
+      if (auto *ChildMacro = dyn_cast_or_null<DIMacroNode>(Element))
+        processMacroNode(ChildMacro, 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
diff --git a/llvm/test/DebugInfo/Generic/debuginfofinder-macros.ll b/llvm/test/DebugInfo/Generic/debuginfofinder-macros.ll
new file mode 100644
index 0000000000000..f29cc97a0c290
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/debuginfofinder-macros.ll
@@ -0,0 +1,32 @@
+; RUN: opt -passes='print<module-debuginfo>' -disable-output 2>&1 < %s \
+; RUN:   | FileCheck %s
+
+; Macro hierarchy graph:
+; CompileUnit
+;   ├── MacroFile: ./def.c (!3)
+;   │   ├── Define Macro: 'SIZE' = '5' (!7)
+;   │   ├── Undef Macro: 'SIZE' (!8)
+;   │   └── MacroFile: ./def.nested.c (!5)
+;   │       └── Undef Macro: 'BAZ' (!10)
+;   └── Define Macro: 'BAZ' (!9)
+
+; 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, !11}
+!llvm.dbg.cu = !{!1}
+
+!0 = !{i32 7, !"Dwarf Version", i32 0}
+!1 = distinct !DICompileUnit(language: DW_LANG_OpenCL, file: !2, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, macros: !{!3, !9})
+!2 = !DIFile(filename: "def.c", directory: "/tmp")
+!3 = !DIMacroFile(file: !4, nodes: !{!7, !8, !5})
+!4 = !DIFile(filename: "def.c", directory: ".")
+!5 = !DIMacroFile(file: !6, nodes: !{!10})
+!6 = !DIFile(filename: "def.nested.c", directory: ".")
+!7 = !DIMacro(type: DW_MACINFO_define, line: 1, name: "SIZE", value: "5")
+!8 = !DIMacro(type: DW_MACINFO_undef, line: 1, name: "SIZE")
+!9 = !DIMacro(type: DW_MACINFO_define, line: 1, name: "BAZ")
+!10 = !DIMacro(type: DW_MACINFO_undef, line: 1, name: "BAZ")
+!11 = !{i32 2, !"Debug Info Version", i32 3}

>From d3e5e9bfe92fe9f505b08cd28189a4491c6e7feb Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Thu, 5 Feb 2026 07:44:13 -0600
Subject: [PATCH 2/4] Simplify test.

---
 .../Generic/debuginfofinder-macros.ll         | 35 +++++++++----------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/llvm/test/DebugInfo/Generic/debuginfofinder-macros.ll b/llvm/test/DebugInfo/Generic/debuginfofinder-macros.ll
index f29cc97a0c290..25d7e8a204926 100644
--- a/llvm/test/DebugInfo/Generic/debuginfofinder-macros.ll
+++ b/llvm/test/DebugInfo/Generic/debuginfofinder-macros.ll
@@ -3,30 +3,29 @@
 
 ; Macro hierarchy graph:
 ; CompileUnit
-;   ├── MacroFile: ./def.c (!3)
-;   │   ├── Define Macro: 'SIZE' = '5' (!7)
-;   │   ├── Undef Macro: 'SIZE' (!8)
-;   │   └── MacroFile: ./def.nested.c (!5)
-;   │       └── Undef Macro: 'BAZ' (!10)
-;   └── Define Macro: 'BAZ' (!9)
+;   ├── 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, !11}
+!llvm.module.flags = !{!0, !10}
 !llvm.dbg.cu = !{!1}
 
 !0 = !{i32 7, !"Dwarf Version", i32 0}
-!1 = distinct !DICompileUnit(language: DW_LANG_OpenCL, file: !2, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, macros: !{!3, !9})
-!2 = !DIFile(filename: "def.c", directory: "/tmp")
-!3 = !DIMacroFile(file: !4, nodes: !{!7, !8, !5})
-!4 = !DIFile(filename: "def.c", directory: ".")
-!5 = !DIMacroFile(file: !6, nodes: !{!10})
-!6 = !DIFile(filename: "def.nested.c", directory: ".")
-!7 = !DIMacro(type: DW_MACINFO_define, line: 1, name: "SIZE", value: "5")
-!8 = !DIMacro(type: DW_MACINFO_undef, line: 1, name: "SIZE")
-!9 = !DIMacro(type: DW_MACINFO_define, line: 1, name: "BAZ")
-!10 = !DIMacro(type: DW_MACINFO_undef, line: 1, name: "BAZ")
-!11 = !{i32 2, !"Debug Info Version", i32 3}
+!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}

>From 7c7f2dceafec5a1a45a2b9d46a4e98ca0c98a249 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Thu, 5 Feb 2026 08:28:08 -0600
Subject: [PATCH 3/4] [review] switch to doxygen style.

---
 llvm/lib/IR/DebugInfo.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 54309ac60ecef..89da0ceb7b9e3 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -278,13 +278,13 @@ 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).
+/// 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)

>From 779ce1761f3acd1c5ab1ae56274d61c773b6835d Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Thu, 5 Feb 2026 08:28:32 -0600
Subject: [PATCH 4/4] [review] Reduce identation and remove unnecessary casts.

---
 llvm/lib/IR/DebugInfo.cpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 89da0ceb7b9e3..dcdcc702479f6 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -295,16 +295,16 @@ void DebugInfoFinder::processMacroNode(DIMacroNode *Macro,
     return;
   }
 
-  if (auto *MF = dyn_cast<DIMacroFile>(Macro)) {
-    // Check if we've already seen this macro file to avoid infinite recursion
-    if (!NodesSeen.insert(MF).second)
-      return;
+  auto *MF = dyn_cast<DIMacroFile>(Macro);
+  assert(MF && "Expected a DIMacroFile (it can't be any other type at this point)");
 
-    // Recursively process nested macros in the macro file
-    for (auto *Element : MF->getElements()) {
-      if (auto *ChildMacro = dyn_cast_or_null<DIMacroNode>(Element))
-        processMacroNode(ChildMacro, MF);
-    }
+  // 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);
   }
 }
 



More information about the llvm-commits mailing list