From 633af6e8edce0afdc64357d56d52f77aaeeebede Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Tue, 14 Oct 2025 07:48:32 +0900 Subject: [PATCH 1/4] 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(Node); DISourceLanguageName Lang = CU->getSourceLanguage(); @@ -900,8 +903,11 @@ void CodeViewDebug::emitCompilerInformation() { OS.AddComment("CPUType"); OS.emitInt16(static_cast(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(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(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 "" +; 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 aadcb99d717a8c4e1f7b0574d9002666c3a23217 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Thu, 30 Oct 2025 21:22:42 -0700 Subject: [PATCH 2/4] 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(TheCPU)); + StringRef CompilerVersion = "0"; auto CUs = MMI->getModule()->debug_compile_units(); - if (CUs.empty()) - return; - - const MDNode *Node = *CUs.begin(); - const auto *CU = cast(Node); + if (!CUs.empty()) { + const MDNode *Node = *CUs.begin(); + const auto *CU = cast(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 e10eb45f29b66153bcd30bc66f69c4a92d9678e8 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Mon, 3 Nov 2025 21:17:57 -0800 Subject: [PATCH 3/4] 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(Node); DISourceLanguageName Lang = CU->getSourceLanguage(); @@ -903,18 +905,19 @@ void CodeViewDebug::emitCompilerInformation() { OS.AddComment("CPUType"); OS.emitInt16(static_cast(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(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(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} From f4ee03f83a9c434bd25035323e92e19ad1931d1b Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Tue, 11 Nov 2025 21:12:24 -0800 Subject: [PATCH 4/4] Store TheCU --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 17 ++++++----------- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h | 2 ++ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index 5379f5f659245..2ebccee6aa68c 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -632,11 +632,11 @@ void CodeViewDebug::beginModule(Module *M) { Asm = nullptr; return; } - Node = *CUs->operands().begin(); } - const auto *CU = cast(Node); - DISourceLanguageName Lang = CU->getSourceLanguage(); + + TheCU = cast(Node); + DISourceLanguageName Lang = TheCU->getSourceLanguage(); CurrentSourceLanguage = Lang.hasVersionedName() ? MapDWARFLanguageToCVLang( @@ -644,7 +644,7 @@ void CodeViewDebug::beginModule(Module *M) { : MapDWARFLanguageToCVLang( static_cast(Lang.getName())); if (!M->getCodeViewFlag() || - CU->getEmissionKind() == DICompileUnit::NoDebug) { + TheCU->getEmissionKind() == DICompileUnit::NoDebug) { Asm = nullptr; return; } @@ -905,14 +905,9 @@ void CodeViewDebug::emitCompilerInformation() { OS.AddComment("CPUType"); OS.emitInt16(static_cast(TheCPU)); - NamedMDNode *CUs = MMI->getModule()->getNamedMetadata("llvm.dbg.cu"); - StringRef CompilerVersion = "0"; - if (!CUs->operands().empty()) { - const MDNode *Node = *CUs->operands().begin(); - const auto *CU = cast(Node); - CompilerVersion = CU->getProducer(); - } + if (TheCU) + CompilerVersion = TheCU->getProducer(); Version FrontVer = parseVersion(CompilerVersion); OS.AddComment("Frontend version"); diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h index c2b878e52e1c3..7fd2cec8c74f2 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -98,6 +98,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase { /// The codeview CPU type used by the translation unit. codeview::CPUType TheCPU; + const DICompileUnit *TheCU = nullptr; + /// The AsmPrinter used for emitting compiler metadata. When only compiler /// info is being emitted, DebugHandlerBase::Asm may be null. AsmPrinter *CompilerInfoAsm = nullptr;