[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