[llvm] [NVPTX] Fix for LTO dwarf emission when multiple CUs with a mix of DebugDirectivesOnly and NoDebug (PR #190371)

Daniel Donenfeld via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 29 09:06:01 PDT 2026


https://github.com/daniel-donenfeld updated https://github.com/llvm/llvm-project/pull/190371

>From 403de06976a5d6adb664120a79add38776a88357 Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Wed, 1 Apr 2026 13:32:54 +0000
Subject: [PATCH 1/3] Fix for LTO issue when multiple CU with mix of
 DebugDirectivesOnly and NoDebug

---
 llvm/include/llvm/IR/DebugInfoMetadata.h  |  1 +
 llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp |  6 ++-
 llvm/test/DebugInfo/NVPTX/no-debug-loc.ll | 46 +++++++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/DebugInfo/NVPTX/no-debug-loc.ll

diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 936029a33943d..6f3df51b0a9fb 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2156,6 +2156,7 @@ class DICompileUnit : public DIScope {
   DebugEmissionKind getEmissionKind() const {
     return (DebugEmissionKind)EmissionKind;
   }
+  bool isNoDebug() const { return EmissionKind == NoDebug; }
   bool isDebugDirectivesOnly() const {
     return EmissionKind == DebugDirectivesOnly;
   }
diff --git a/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp b/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
index 8b243f635abb6..34f6803276916 100644
--- a/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
@@ -377,7 +377,11 @@ void NVPTXAsmPrinter::emitFunctionEntryLabel() {
   // Emit initial .loc debug directive for correct relocation symbol data.
   if (const DISubprogram *SP = MF->getFunction().getSubprogram()) {
     assert(SP->getUnit());
-    if (!SP->getUnit()->isDebugDirectivesOnly())
+    // NoDebug and DebugDirectivesOnly do not require emitting the initial loc
+    // directive. NoDebug does not require any debug directives and the initial
+    // loc directive is not needed for DebugDirectivesOnly as it is redundant
+    // assuming this is a non-empty function.
+    if (!SP->getUnit()->isDebugDirectivesOnly() && !SP->getUnit()->isNoDebug())
       emitInitialRawDwarfLocDirective(*MF);
   }
 }
diff --git a/llvm/test/DebugInfo/NVPTX/no-debug-loc.ll b/llvm/test/DebugInfo/NVPTX/no-debug-loc.ll
new file mode 100644
index 0000000000000..8b4a4039cac6e
--- /dev/null
+++ b/llvm/test/DebugInfo/NVPTX/no-debug-loc.ll
@@ -0,0 +1,46 @@
+; RUN: llc < %s -mtriple=nvptx64-nvidia-cuda | FileCheck %s
+; RUN: %if ptxas %{ llc < %s -mtriple=nvptx64-nvidia-cuda | %ptxas-verify %}
+
+;; When a module contains multiple CUs where one is DebugDirectiveOnly and
+;; the rest are NoDebug, we would attempt to emit dwarf directives for the
+;; NoDebug CUs leading to an assertion. This test verifies that we only emit
+;; dwarf directives for the DebugDirectiveOnly CU.
+
+define i32 @foo(i32 %a, i32 %b) !dbg !5 {
+
+; CHECK:     .loc    [[FILE:[0-9]+]] 26 0          // debug_directives_only.cu:26:0
+; CHECK-NOT: .loc    [[FILE]]        26 0          // debug_directives_only.cu:26:0
+; CHECK:     .loc    [[FILE]]        40 22         // debug_directives_only.cu:40:22
+
+  %add = add i32 %b, %a, !dbg !8
+  ret i32 %add, !dbg !8
+}
+
+define i32 @bar(i32 %a, i32 %b) !dbg !40 {
+
+; CHECK-NOT: .loc
+
+  %add = add i32 %b, %a, !dbg !41
+  ret i32 %add, !dbg !41
+}
+
+; CHECK:     .file   [[FILE]] "/test/directory/debug_directives_only.cu"
+; CHECK-NOT: .file   {{[0-9]*}} "/test/directory/no_debug.cu"
+
+!llvm.dbg.cu = !{!0, !2}
+!nvvm.annotations = !{}
+!llvm.module.flags = !{!4}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: DebugDirectivesOnly)
+!1 = !DIFile(filename: "debug_directives_only.cu", directory: "/test/directory/")
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: NoDebug)
+!3 = !DIFile(filename: "no_debug.cu", directory: "/test/directory/")
+!4 = !{i32 1, !"Debug Info Version", i32 3}
+!5 = distinct !DISubprogram(name: "kernel", linkageName: "foo", scope: !1, file: !1, line: 123, type: !6, scopeLine: 26, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!6 = !DISubroutineType(types: !7)
+!7 = !{}
+!8 = !DILocation(line: 40, column: 22, scope: !31)
+!31 = distinct !DILexicalBlock(scope: !5, file: !1, line: 3, column: 17)
+!40 = distinct !DISubprogram(name: "kernel", linkageName: "bar", scope: !3, file: !3, line: 234, type: !6, scopeLine: 26, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
+!41 = !DILocation(line: 40, column: 22, scope: !42)
+!42 = distinct !DILexicalBlock(scope: !40, file: !3, line: 6, column: 8)

>From 896e2e42a7aa992b18bd3e522f825f1432588e13 Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Mon, 6 Apr 2026 19:16:20 +0000
Subject: [PATCH 2/3] Address PR feedback

---
 llvm/include/llvm/IR/DebugInfoMetadata.h  | 1 +
 llvm/test/DebugInfo/NVPTX/no-debug-loc.ll | 8 ++++----
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 6f3df51b0a9fb..8b16ffb2d7322 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2156,6 +2156,7 @@ class DICompileUnit : public DIScope {
   DebugEmissionKind getEmissionKind() const {
     return (DebugEmissionKind)EmissionKind;
   }
+  // Return true if this CU was compiled with debug info disabled
   bool isNoDebug() const { return EmissionKind == NoDebug; }
   bool isDebugDirectivesOnly() const {
     return EmissionKind == DebugDirectivesOnly;
diff --git a/llvm/test/DebugInfo/NVPTX/no-debug-loc.ll b/llvm/test/DebugInfo/NVPTX/no-debug-loc.ll
index 8b4a4039cac6e..3de22b0bedccc 100644
--- a/llvm/test/DebugInfo/NVPTX/no-debug-loc.ll
+++ b/llvm/test/DebugInfo/NVPTX/no-debug-loc.ll
@@ -1,13 +1,13 @@
 ; RUN: llc < %s -mtriple=nvptx64-nvidia-cuda | FileCheck %s
 ; RUN: %if ptxas %{ llc < %s -mtriple=nvptx64-nvidia-cuda | %ptxas-verify %}
 
-;; When a module contains multiple CUs where one is DebugDirectiveOnly and
+;; When a module contains multiple CUs where one is DebugDirectivesOnly and
 ;; the rest are NoDebug, we would attempt to emit dwarf directives for the
 ;; NoDebug CUs leading to an assertion. This test verifies that we only emit
-;; dwarf directives for the DebugDirectiveOnly CU.
+;; dwarf directives for the DebugDirectivesOnly CU.
 
 define i32 @foo(i32 %a, i32 %b) !dbg !5 {
-
+; CHECK-LABEL: foo
 ; CHECK:     .loc    [[FILE:[0-9]+]] 26 0          // debug_directives_only.cu:26:0
 ; CHECK-NOT: .loc    [[FILE]]        26 0          // debug_directives_only.cu:26:0
 ; CHECK:     .loc    [[FILE]]        40 22         // debug_directives_only.cu:40:22
@@ -17,7 +17,7 @@ define i32 @foo(i32 %a, i32 %b) !dbg !5 {
 }
 
 define i32 @bar(i32 %a, i32 %b) !dbg !40 {
-
+; CHECK-LABEL: bar
 ; CHECK-NOT: .loc
 
   %add = add i32 %b, %a, !dbg !41

>From 1137d39741b35e3f9172010787cea406590ed0de Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Wed, 29 Apr 2026 15:56:32 +0000
Subject: [PATCH 3/3] Address feedback to simplify the code for this check

---
 llvm/include/llvm/CodeGen/AsmPrinter.h     |  3 --
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 13 ---------
 llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp  | 34 ++++++++++++++++------
 3 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index ae09f2df3a47d..ff9d6d73ce146 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -360,9 +360,6 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
 
   void EmitToStreamer(MCStreamer &S, const MCInst &Inst);
 
-  /// Emits inital debug location directive.
-  void emitInitialRawDwarfLocDirective(const MachineFunction &MF);
-
   /// Return the current section we are emitting to.
   const MCSection *getCurrentSection() const;
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 76703b8ccc637..3397fd9aa36f4 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -506,19 +506,6 @@ void AsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) {
   S.emitInstruction(Inst, getSubtargetInfo());
 }
 
-void AsmPrinter::emitInitialRawDwarfLocDirective(const MachineFunction &MF) {
-  if (DD) {
-    assert(OutStreamer->hasRawTextSupport() &&
-           "Expected assembly output mode.");
-    // This is NVPTX specific and it's unclear why.
-    // PR51079: If we have code without debug information we need to give up.
-    DISubprogram *MFSP = MF.getFunction().getSubprogram();
-    if (!MFSP)
-      return;
-    (void)DD->emitInitialLocDirective(MF, /*CUID=*/0);
-  }
-}
-
 /// getCurrentSection() - Return the current section we are emitting to.
 const MCSection *AsmPrinter::getCurrentSection() const {
   return OutStreamer->getCurrentSectionOnly();
diff --git a/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp b/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
index 34f6803276916..7d75cd8cebd6a 100644
--- a/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
@@ -111,6 +111,30 @@ static StringRef getSamplerName(const Value &V) {
   return V.getName();
 }
 
+/// Emits initial debug location directive.
+static void emitInitialRawDwarfLocDirective(const MachineFunction &MF,
+                                            DwarfDebug *DD,
+                                            MCStreamer &OutStreamer) {
+  if (!DD)
+    return;
+
+  assert(OutStreamer.hasRawTextSupport() && "Expected assembly output mode.");
+  // This is NVPTX specific and it's unclear why.
+  // PR51079: If we have code without debug information we need to give up.
+  const DISubprogram *SP = MF.getFunction().getSubprogram();
+  if (!SP)
+    return;
+  assert(SP->getUnit());
+  // NoDebug and DebugDirectivesOnly do not require emitting the initial loc
+  // directive. NoDebug does not require any debug directives and the initial
+  // loc directive is not needed for DebugDirectivesOnly as it is redundant
+  // assuming this is a non-empty function.
+  if (SP->getUnit()->isDebugDirectivesOnly() || SP->getUnit()->isNoDebug())
+    return;
+
+  (void)DD->emitInitialLocDirective(MF, /*CUID=*/0);
+}
+
 /// discoverDependentGlobals - Return a set of GlobalVariables on which \p V
 /// depends.
 static void
@@ -375,15 +399,7 @@ void NVPTXAsmPrinter::emitFunctionEntryLabel() {
   setAndEmitFunctionVirtualRegisters(*MF);
   encodeDebugInfoRegisterNumbers(*MF);
   // Emit initial .loc debug directive for correct relocation symbol data.
-  if (const DISubprogram *SP = MF->getFunction().getSubprogram()) {
-    assert(SP->getUnit());
-    // NoDebug and DebugDirectivesOnly do not require emitting the initial loc
-    // directive. NoDebug does not require any debug directives and the initial
-    // loc directive is not needed for DebugDirectivesOnly as it is redundant
-    // assuming this is a non-empty function.
-    if (!SP->getUnit()->isDebugDirectivesOnly() && !SP->getUnit()->isNoDebug())
-      emitInitialRawDwarfLocDirective(*MF);
-  }
+  emitInitialRawDwarfLocDirective(*MF, getDwarfDebug(), *OutStreamer);
 }
 
 bool NVPTXAsmPrinter::runOnMachineFunction(MachineFunction &F) {



More information about the llvm-commits mailing list