[llvm] CodeGen: Fix CodeView crashes with empty llvm.dbg.cu (PR #163286)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 3 22:16:16 PST 2025


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/163286

>From 668173cfa0a405e225942b1cbc1d780bac61a5ae Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Tue, 14 Oct 2025 07:48:32 +0900
Subject: [PATCH 1/3] CodeGen: Fix CodeView crashes with empty llvm.dbg.cu

---
 llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 21 +++++++++----
 .../X86/codeview-empty-dbg-cu-crash.ll        | 30 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll

diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index e57ed24a45065..6cbed50e14ea9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -625,10 +625,13 @@ void CodeViewDebug::beginModule(Module *M) {
   if (Asm->hasDebugInfo()) {
     Node = *M->debug_compile_units_begin();
   } else {
+    auto DebugCompileUnits = MMI->getModule()->debug_compile_units();
+    if (DebugCompileUnits.empty())
+      return;
+
     // When emitting only compiler information, we may have only NoDebug CUs,
     // which would be skipped by debug_compile_units_begin.
-    NamedMDNode *CUs = MMI->getModule()->getNamedMetadata("llvm.dbg.cu");
-    Node = *CUs->operands().begin();
+    Node = *DebugCompileUnits.begin();
   }
   const auto *CU = cast<DICompileUnit>(Node);
   DISourceLanguageName Lang = CU->getSourceLanguage();
@@ -900,8 +903,11 @@ void CodeViewDebug::emitCompilerInformation() {
   OS.AddComment("CPUType");
   OS.emitInt16(static_cast<uint64_t>(TheCPU));
 
-  NamedMDNode *CUs = MMI->getModule()->getNamedMetadata("llvm.dbg.cu");
-  const MDNode *Node = *CUs->operands().begin();
+  auto CUs = MMI->getModule()->debug_compile_units();
+  if (CUs.empty())
+    return;
+
+  const MDNode *Node = *CUs.begin();
   const auto *CU = cast<DICompileUnit>(Node);
 
   StringRef CompilerVersion = CU->getProducer();
@@ -948,8 +954,11 @@ void CodeViewDebug::emitBuildInfo() {
   // not clear if the compiler path should refer to the executable for the
   // frontend or the backend. Leave it blank for now.
   TypeIndex BuildInfoArgs[BuildInfoRecord::MaxArgs] = {};
-  NamedMDNode *CUs = MMI->getModule()->getNamedMetadata("llvm.dbg.cu");
-  const MDNode *Node = *CUs->operands().begin(); // FIXME: Multiple CUs.
+  auto CUs = MMI->getModule()->debug_compile_units();
+  if (CUs.empty())
+    return;
+
+  const MDNode *Node = *CUs.begin(); // FIXME: Multiple CUs.
   const auto *CU = cast<DICompileUnit>(Node);
   const DIFile *MainSourceFile = CU->getFile();
   BuildInfoArgs[BuildInfoRecord::CurrentDirectory] =
diff --git a/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll b/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll
new file mode 100644
index 0000000000000..395b99e7f90b6
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll
@@ -0,0 +1,30 @@
+; RUN: llc -mtriple=x86_64-pc-windows-msvc < %s | FileCheck %s
+
+; CHECK: .file	"<stdin>"
+; CHECK-NEXT: .section	.debug$S,"dr"
+; CHECK-NEXT: .p2align	2, 0x0
+; CHECK-NEXT: .long	4                               # Debug section magic
+; CHECK-NEXT: .long	241
+; CHECK-NEXT: .long	.Ltmp1-.Ltmp0                   # Subsection size
+; CHECK-NEXT: .Ltmp0:
+; CHECK-NEXT: .short	.Ltmp3-.Ltmp2                   # Record length
+; CHECK-NEXT: .Ltmp2:
+; CHECK-NEXT: .short	4353                            # Record kind: S_OBJNAME
+; CHECK-NEXT: .long	0                               # Signature
+; CHECK-NEXT: .byte	0                               # Object name
+; CHECK-NEXT: .p2align	2, 0x0
+; CHECK-NEXT: .Ltmp3:
+; CHECK-NEXT: .short	.Ltmp5-.Ltmp4                   # Record length
+; CHECK-NEXT: .Ltmp4:
+; CHECK-NEXT: .short	4412                            # Record kind: S_COMPILE3
+; CHECK-NEXT: .long	3                               # Flags and language
+; CHECK-NEXT: .short	208                             # CPUType
+; CHECK-NEXT: .Ltmp1:
+; CHECK-NEXT: .p2align	2, 0x0
+; CHECK-NEXT: .cv_filechecksums                       # File index to string table offset subsection
+; CHECK-NEXT: .cv_stringtable                         # String table
+
+!llvm.dbg.cu = !{}
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}

>From 7b86e0c2215ffd2336734d429706ff307b3bb5a9 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Thu, 30 Oct 2025 21:22:42 -0700
Subject: [PATCH 2/3] Emit 0 instead of incomplete information

---
 llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 20 +++++++++----------
 .../X86/codeview-empty-dbg-cu-crash.ll        |  7 +++++++
 2 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index 6cbed50e14ea9..e64ad6a5aa4cc 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -903,18 +903,18 @@ void CodeViewDebug::emitCompilerInformation() {
   OS.AddComment("CPUType");
   OS.emitInt16(static_cast<uint64_t>(TheCPU));
 
+  StringRef CompilerVersion = "0";
   auto CUs = MMI->getModule()->debug_compile_units();
-  if (CUs.empty())
-    return;
-
-  const MDNode *Node = *CUs.begin();
-  const auto *CU = cast<DICompileUnit>(Node);
+  if (!CUs.empty()) {
+    const MDNode *Node = *CUs.begin();
+    const auto *CU = cast<DICompileUnit>(Node);
 
-  StringRef CompilerVersion = CU->getProducer();
-  Version FrontVer = parseVersion(CompilerVersion);
-  OS.AddComment("Frontend version");
-  for (int N : FrontVer.Part) {
-    OS.emitInt16(N);
+    CompilerVersion = CU->getProducer();
+    Version FrontVer = parseVersion(CompilerVersion);
+    OS.AddComment("Frontend version");
+    for (int N : FrontVer.Part) {
+      OS.emitInt16(N);
+    }
   }
 
   // Some Microsoft tools, like Binscope, expect a backend version number of at
diff --git a/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll b/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll
index 395b99e7f90b6..b40b48cb0bd40 100644
--- a/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll
+++ b/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll
@@ -19,6 +19,13 @@
 ; CHECK-NEXT: .short	4412                            # Record kind: S_COMPILE3
 ; CHECK-NEXT: .long	3                               # Flags and language
 ; CHECK-NEXT: .short	208                             # CPUType
+; CHECK-NEXT: .short	22000                           # Backend version
+; CHECK-NEXT: .short	0
+; CHECK-NEXT: .short	0
+; CHECK-NEXT: .short	0
+; CHECK-NEXT: .asciz	"0"                             # Null-terminated compiler version string
+; CHECK-NEXT: .p2align	2, 0x0
+; CHECK-NEXT: .Ltmp5:
 ; CHECK-NEXT: .Ltmp1:
 ; CHECK-NEXT: .p2align	2, 0x0
 ; CHECK-NEXT: .cv_filechecksums                       # File index to string table offset subsection

>From 9c5ab4d6ab812a31fb349f85b3754bda7a909874 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Mon, 3 Nov 2025 21:17:57 -0800
Subject: [PATCH 3/3] Redo this. The compile unit iterator has the unhelpful
 behavior of skipping entries that are there.

---
 llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 38 +++++++++----------
 .../X86/codeview-empty-dbg-cu-crash.ll        |  6 ++-
 2 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index e64ad6a5aa4cc..5379f5f659245 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -625,13 +625,15 @@ void CodeViewDebug::beginModule(Module *M) {
   if (Asm->hasDebugInfo()) {
     Node = *M->debug_compile_units_begin();
   } else {
-    auto DebugCompileUnits = MMI->getModule()->debug_compile_units();
-    if (DebugCompileUnits.empty())
-      return;
-
     // When emitting only compiler information, we may have only NoDebug CUs,
     // which would be skipped by debug_compile_units_begin.
-    Node = *DebugCompileUnits.begin();
+    NamedMDNode *CUs = MMI->getModule()->getNamedMetadata("llvm.dbg.cu");
+    if (CUs->operands().empty()) {
+      Asm = nullptr;
+      return;
+    }
+
+    Node = *CUs->operands().begin();
   }
   const auto *CU = cast<DICompileUnit>(Node);
   DISourceLanguageName Lang = CU->getSourceLanguage();
@@ -903,18 +905,19 @@ void CodeViewDebug::emitCompilerInformation() {
   OS.AddComment("CPUType");
   OS.emitInt16(static_cast<uint64_t>(TheCPU));
 
+  NamedMDNode *CUs = MMI->getModule()->getNamedMetadata("llvm.dbg.cu");
+
   StringRef CompilerVersion = "0";
-  auto CUs = MMI->getModule()->debug_compile_units();
-  if (!CUs.empty()) {
-    const MDNode *Node = *CUs.begin();
+  if (!CUs->operands().empty()) {
+    const MDNode *Node = *CUs->operands().begin();
     const auto *CU = cast<DICompileUnit>(Node);
-
     CompilerVersion = CU->getProducer();
-    Version FrontVer = parseVersion(CompilerVersion);
-    OS.AddComment("Frontend version");
-    for (int N : FrontVer.Part) {
-      OS.emitInt16(N);
-    }
+  }
+
+  Version FrontVer = parseVersion(CompilerVersion);
+  OS.AddComment("Frontend version");
+  for (int N : FrontVer.Part) {
+    OS.emitInt16(N);
   }
 
   // Some Microsoft tools, like Binscope, expect a backend version number of at
@@ -954,11 +957,8 @@ void CodeViewDebug::emitBuildInfo() {
   // not clear if the compiler path should refer to the executable for the
   // frontend or the backend. Leave it blank for now.
   TypeIndex BuildInfoArgs[BuildInfoRecord::MaxArgs] = {};
-  auto CUs = MMI->getModule()->debug_compile_units();
-  if (CUs.empty())
-    return;
-
-  const MDNode *Node = *CUs.begin(); // FIXME: Multiple CUs.
+  NamedMDNode *CUs = MMI->getModule()->getNamedMetadata("llvm.dbg.cu");
+  const MDNode *Node = *CUs->operands().begin(); // FIXME: Multiple CUs.
   const auto *CU = cast<DICompileUnit>(Node);
   const DIFile *MainSourceFile = CU->getFile();
   BuildInfoArgs[BuildInfoRecord::CurrentDirectory] =
diff --git a/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll b/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll
index b40b48cb0bd40..51435b10fdc2a 100644
--- a/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll
+++ b/llvm/test/DebugInfo/X86/codeview-empty-dbg-cu-crash.ll
@@ -19,6 +19,10 @@
 ; CHECK-NEXT: .short	4412                            # Record kind: S_COMPILE3
 ; CHECK-NEXT: .long	3                               # Flags and language
 ; CHECK-NEXT: .short	208                             # CPUType
+; CHECK-NEXT: .short	0                               # Frontend version
+; CHECK-NEXT: .short	0
+; CHECK-NEXT: .short	0
+; CHECK-NEXT: .short	0
 ; CHECK-NEXT: .short	22000                           # Backend version
 ; CHECK-NEXT: .short	0
 ; CHECK-NEXT: .short	0
@@ -28,8 +32,6 @@
 ; CHECK-NEXT: .Ltmp5:
 ; CHECK-NEXT: .Ltmp1:
 ; CHECK-NEXT: .p2align	2, 0x0
-; CHECK-NEXT: .cv_filechecksums                       # File index to string table offset subsection
-; CHECK-NEXT: .cv_stringtable                         # String table
 
 !llvm.dbg.cu = !{}
 !llvm.module.flags = !{!0}



More information about the llvm-commits mailing list