[CodeGen][COFF] Always emit CodeView compiler info on Windows targets (#142970)

MSVC always emits minimal CodeView metadata with compiler information,
even when debug info is otherwise disabled. Other tools may rely on this
metadata being present. For example, linkers use it to determine whether
hotpatching is enabled for the object file.
This commit is contained in:
Jacek Caban
2025-06-13 13:48:29 -07:00
committed by GitHub
parent 60d000496b
commit be5c96bfac
19 changed files with 219 additions and 47 deletions

View File

@@ -414,6 +414,11 @@ CodeGenModule::CodeGenModule(ASTContext &C,
CodeGenOpts.CoverageNotesFile.size() ||
CodeGenOpts.CoverageDataFile.size())
DebugInfo.reset(new CGDebugInfo(*this));
else if (getTriple().isOSWindows())
// On Windows targets, we want to emit compiler info even if debug info is
// otherwise disabled. Use a temporary CGDebugInfo instance to emit only
// basic compiler metadata.
CGDebugInfo(*this);
Block.GlobalUniqueCount = 0;
@@ -1051,7 +1056,7 @@ void CodeGenModule::Release() {
"StrictVTablePointersRequirement",
llvm::MDNode::get(VMContext, Ops));
}
if (getModuleDebugInfo())
if (getModuleDebugInfo() || getTriple().isOSWindows())
// We support a single version in the linked module. The LLVM
// parser will drop debug info with a different version number
// (and warn about it, too).

View File

@@ -0,0 +1,8 @@
// REQUIRES: x86-registered-target
// RUN: %clang --target=x86_64-windows -g -S -emit-llvm -o - %s | FileCheck %s
// RUN: %clang --target=x86_64-windows -S -emit-llvm -o - %s | FileCheck %s
int main (void) {
return 0;
}
// CHECK: i32 2, !"Debug Info Version", i32 3}

View File

@@ -1,3 +1,4 @@
// REQUIRES: !system-windows
// RUN: %clang -g -S -emit-llvm -o - %s | FileCheck %s
// RUN: %clang -S -emit-llvm -o - %s | FileCheck %s --check-prefix=NO_DEBUG
int main (void) {

View File

@@ -0,0 +1,37 @@
// REQUIRES: x86-registered-target
// Check that CodeView compiler version is emitted even when debug info is otherwise disabled.
// RUN: %clang --target=i686-pc-windows-msvc -S -emit-llvm %s -o - | FileCheck --check-prefix=IR %s
// IR: !llvm.dbg.cu = !{!0}
// IR: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version {{.*}}", isOptimized: false, runtimeVersion: 0, emissionKind: NoDebug, splitDebugInlining: false, nameTableKind: None)
// RUN: %clang --target=i686-pc-windows-msvc -c %s -o %t.o
// RUN: llvm-readobj --codeview %t.o | FileCheck %s
// CHECK: CodeViewDebugInfo [
// CHECK-NEXT: Section: .debug$S (4)
// CHECK-NEXT: Magic: 0x4
// CHECK-NEXT: Subsection [
// CHECK-NEXT: SubSectionType: Symbols (0xF1)
// CHECK-NEXT: SubSectionSize:
// CHECK-NEXT: ObjNameSym {
// CHECK-NEXT: Kind: S_OBJNAME (0x1101)
// CHECK-NEXT: Signature: 0x0
// CHECK-NEXT: ObjectName:
// CHECK-NEXT: }
// CHECK-NEXT: Compile3Sym {
// CHECK-NEXT: Kind: S_COMPILE3 (0x113C)
// CHECK-NEXT: Language: Cpp (0x1)
// CHECK-NEXT: Flags [ (0x0)
// CHECK-NEXT: ]
// CHECK-NEXT: Machine: Pentium3 (0x7)
// CHECK-NEXT: FrontendVersion:
// CHECK-NEXT: BackendVersion:
// CHECK-NEXT: VersionName: clang version
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: ]
int main() {
return 0;
}

View File

@@ -11,12 +11,9 @@
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=HOTPATCH
// HOTPATCH: S_COMPILE3 [size = [[#]]]
// HOTPATCH: flags = hot patchable
///
/// Unfortunately we need /Z7, Clang does not systematically generate S_COMPILE3.
///
//
// RUN: %clang_cl --target=aarch64-pc-windows-msvc /c -o %t.obj -- %s
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=NO-HOTPATCH
// NO-HOTPATCH-NOT: flags = hot patchable
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=HOTPATCH
int main() {
return 0;

View File

@@ -11,12 +11,9 @@
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=HOTPATCH
// HOTPATCH: S_COMPILE3 [size = [[#]]]
// HOTPATCH: flags = hot patchable
///
/// Unfortunately we need /Z7, Clang does not systematically generate S_COMPILE3.
///
//
// RUN: %clang_cl --target=arm-pc-windows-msvc /c -o %t.obj -- %s
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=NO-HOTPATCH
// NO-HOTPATCH-NOT: flags = hot patchable
// RUN: llvm-pdbutil dump -symbols %t.obj | FileCheck %s --check-prefix=HOTPATCH
int main() {
return 0;

View File

@@ -1,6 +1,6 @@
// RUN: env SDKROOT="/" %clang -emit-llvm -S -o %t1.ll -x c - < %s
// RUN: env SDKROOT="/" %clang -emit-llvm -S -o - -x c - < %s | grep -v DIFile > %t1.ll
// RUN: env SDKROOT="/" %clang -emit-ast -o %t.ast %s
// RUN: env SDKROOT="/" %clang -emit-llvm -S -o %t2.ll -x ast - < %t.ast
// RUN: env SDKROOT="/" %clang -emit-llvm -S -o - -x ast - < %t.ast | grep -v DIFile > %t2.ll
// RUN: diff %t1.ll %t2.ll
int main(void) {

View File

@@ -1,6 +1,6 @@
// RUN: env SDKROOT="/" %clang -Wno-error=return-type -emit-llvm -S -o %t1.ll -x c++ - < %s
// RUN: env SDKROOT="/" %clang -Wno-error=return-type -emit-llvm -S -o - -x c++ - < %s | grep -v DIFile > %t1.ll
// RUN: env SDKROOT="/" %clang -Wno-error=return-type -fno-delayed-template-parsing -emit-ast -o %t.ast %s
// RUN: env SDKROOT="/" %clang -Wno-error=return-type -emit-llvm -S -o %t2.ll -x ast - < %t.ast
// RUN: env SDKROOT="/" %clang -Wno-error=return-type -emit-llvm -S -o - -x ast - < %t.ast | grep -v DIFile > %t2.ll
// RUN: diff %t1.ll %t2.ll
// http://llvm.org/bugs/show_bug.cgi?id=15377

View File

@@ -565,8 +565,11 @@ bool AsmPrinter::doInitialization(Module &M) {
if (MAI->doesSupportDebugInformation()) {
bool EmitCodeView = M.getCodeViewFlag();
if (EmitCodeView &&
(TM.getTargetTriple().isOSWindows() || TM.getTargetTriple().isUEFI()))
// On Windows targets, emit minimal CodeView compiler info even when debug
// info is disabled.
if ((TM.getTargetTriple().isOSWindows() &&
M.getNamedMetadata("llvm.dbg.cu")) ||
(TM.getTargetTriple().isUEFI() && EmitCodeView))
Handlers.push_back(std::make_unique<CodeViewDebug>(this));
if (!EmitCodeView || M.getDwarfVersion()) {
if (hasDebugInfo()) {

View File

@@ -125,6 +125,8 @@ static CPUType mapArchToCVCPUType(Triple::ArchType Type) {
return CPUType::ARM64;
case Triple::ArchType::mipsel:
return CPUType::MIPS;
case Triple::ArchType::UnknownArch:
return CPUType::Unknown;
default:
report_fatal_error("target architecture doesn't map to a CodeView CPUType");
}
@@ -611,21 +613,33 @@ static SourceLanguage MapDWLangToCVLang(unsigned DWLang) {
}
void CodeViewDebug::beginModule(Module *M) {
// If module doesn't have named metadata anchors or COFF debug section
// is not available, skip any debug info related stuff.
if (!Asm->hasDebugInfo() ||
!Asm->getObjFileLowering().getCOFFDebugSymbolsSection()) {
// If COFF debug section is not available, skip any debug info related stuff.
if (!Asm->getObjFileLowering().getCOFFDebugSymbolsSection()) {
Asm = nullptr;
return;
}
CompilerInfoAsm = Asm;
TheCPU = mapArchToCVCPUType(M->getTargetTriple().getArch());
// Get the current source language.
const MDNode *Node = *M->debug_compile_units_begin();
const MDNode *Node;
if (Asm->hasDebugInfo()) {
Node = *M->debug_compile_units_begin();
} else {
// 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();
}
const auto *CU = cast<DICompileUnit>(Node);
CurrentSourceLanguage = MapDWLangToCVLang(CU->getSourceLanguage());
if (!M->getCodeViewFlag() ||
CU->getEmissionKind() == DICompileUnit::NoDebug) {
Asm = nullptr;
return;
}
collectGlobalVariableInfo();
@@ -636,7 +650,7 @@ void CodeViewDebug::beginModule(Module *M) {
}
void CodeViewDebug::endModule() {
if (!Asm || !Asm->hasDebugInfo())
if (!CompilerInfoAsm)
return;
// The COFF .debug$S section consists of several subsections, each starting
@@ -652,6 +666,8 @@ void CodeViewDebug::endModule() {
emitObjName();
emitCompilerInformation();
endCVSubsection(CompilerInfo);
if (!Asm)
return;
emitInlineeLinesSubsection();
@@ -788,7 +804,7 @@ void CodeViewDebug::emitTypeGlobalHashes() {
void CodeViewDebug::emitObjName() {
MCSymbol *CompilerEnd = beginSymbolRecord(SymbolKind::S_OBJNAME);
StringRef PathRef(Asm->TM.Options.ObjectFilenameForDebug);
StringRef PathRef(CompilerInfoAsm->TM.Options.ObjectFilenameForDebug);
llvm::SmallString<256> PathStore(PathRef);
if (PathRef.empty() || PathRef == "-") {
@@ -846,7 +862,7 @@ void CodeViewDebug::emitCompilerInformation() {
}
using ArchType = llvm::Triple::ArchType;
ArchType Arch = MMI->getModule()->getTargetTriple().getArch();
if (Asm->TM.Options.Hotpatch || Arch == ArchType::thumb ||
if (CompilerInfoAsm->TM.Options.Hotpatch || Arch == ArchType::thumb ||
Arch == ArchType::aarch64) {
Flags |= static_cast<uint32_t>(CompileSym3Flags::HotPatch);
}
@@ -1015,7 +1031,7 @@ void CodeViewDebug::switchToDebugSectionForSymbol(const MCSymbol *GVSym) {
const MCSymbol *KeySym = GVSec ? GVSec->getCOMDATSymbol() : nullptr;
MCSectionCOFF *DebugSec = cast<MCSectionCOFF>(
Asm->getObjFileLowering().getCOFFDebugSymbolsSection());
CompilerInfoAsm->getObjFileLowering().getCOFFDebugSymbolsSection());
DebugSec = OS.getContext().getAssociativeCOFFSection(DebugSec, KeySym);
OS.switchSection(DebugSec);

View File

@@ -98,6 +98,10 @@ private:
/// The codeview CPU type used by the translation unit.
codeview::CPUType TheCPU;
/// The AsmPrinter used for emitting compiler metadata. When only compiler
/// info is being emitted, DebugHandlerBase::Asm may be null.
AsmPrinter *CompilerInfoAsm = nullptr;
static LocalVarDef createDefRangeMem(uint16_t CVRegister, int Offset);
/// Similar to DbgVariable in DwarfDebug, but not dwarf-specific.

View File

@@ -1,8 +1,8 @@
; RUN: llc -O2 -o %t1.o < %s
; RUN: llc -O2 -o %t2.o < %s
; RUN: llc -O2 -o %t3.o < %s
; RUN: llc -O2 -o %t4.o < %s
; RUN: llc -O2 -o %t5.o < %s
; RUN: llc -O2 < %s > %t1.o
; RUN: llc -O2 < %s > %t2.o
; RUN: llc -O2 < %s > %t3.o
; RUN: llc -O2 < %s > %t4.o
; RUN: llc -O2 < %s > %t5.o
; RUN: cmp %t1.o %t2.o
; RUN: cmp %t1.o %t3.o
; RUN: cmp %t1.o %t4.o

View File

@@ -43,6 +43,33 @@
; DWO-4: 0x00000000: Compile Unit: {{.*}} version = 0x0004, abbr_offset
; DWO-4: 0x0000000b: DW_TAG_compile_unit
; Check that basic CodeView compiler info is emitted even when the DWARF debug format is used.
; RUN: llc -dwarf-version=4 \
; RUN: -filetype=obj -O0 -mtriple=x86_64-unknown-windows-msvc < %s \
; RUN: | llvm-readobj --codeview - | FileCheck %s --check-prefix=CODEVIEW
; CODEVIEW: CodeViewDebugInfo [
; CODEVIEW-NEXT: Section: .debug$S (4)
; CODEVIEW-NEXT: Magic: 0x4
; CODEVIEW-NEXT: Subsection [
; CODEVIEW-NEXT: SubSectionType: Symbols (0xF1)
; CODEVIEW-NEXT: SubSectionSize: 0x90
; CODEVIEW-NEXT: ObjNameSym {
; CODEVIEW-NEXT: Kind: S_OBJNAME (0x1101)
; CODEVIEW-NEXT: Signature: 0x0
; CODEVIEW-NEXT: ObjectName:
; CODEVIEW-NEXT: }
; CODEVIEW-NEXT: Compile3Sym {
; CODEVIEW-NEXT: Kind: S_COMPILE3 (0x113C)
; CODEVIEW-NEXT: Language: Cpp (0x1)
; CODEVIEW-NEXT: Flags [ (0x0)
; CODEVIEW-NEXT: ]
; CODEVIEW-NEXT: Machine: X64 (0xD0)
; CODEVIEW-NEXT: FrontendVersion: 17.0.0.0
; CODEVIEW-NEXT: BackendVersion:
; CODEVIEW-NEXT: VersionName: clang version 17.0.0
; CODEVIEW-NEXT: }
; CODEVIEW-NEXT: ]
; CODEVIEW-NEXT: ]
; ModuleID = 't.cpp'
source_filename = "t.cpp"

View File

@@ -0,0 +1,38 @@
; RUN: llc -filetype=obj -o - < %s | llvm-readobj --codeview - | FileCheck %s
; Check that basic CodeView compiler info is emitted even when the CodeView flag is not set.
; CHECK-NOT: CodeViewTypes
; CHECK: CodeViewDebugInfo [
; CHECK-NEXT: Section: .debug$S (4)
; CHECK-NEXT: Magic: 0x4
; CHECK-NEXT: Subsection [
; CHECK-NEXT: SubSectionType: Symbols (0xF1)
; CHECK-NEXT: SubSectionSize: 0x2C
; CHECK-NEXT: ObjNameSym {
; CHECK-NEXT: Kind: S_OBJNAME (0x1101)
; CHECK-NEXT: Signature: 0x0
; CHECK-NEXT: ObjectName:
; CHECK-NEXT: }
; CHECK-NEXT: Compile3Sym {
; CHECK-NEXT: Kind: S_COMPILE3 (0x113C)
; CHECK-NEXT: Language: C (0x0)
; CHECK-NEXT: Flags [ (0x0)
; CHECK-NEXT: ]
; CHECK-NEXT: Machine: X64 (0xD0)
; CHECK-NEXT: FrontendVersion:
; CHECK-NEXT: BackendVersion:
; CHECK-NEXT: VersionName: clang
; CHECK-NEXT: }
; CHECK-NEXT: ]
; CHECK-NEXT: ]
source_filename = "empty"
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.0.24215"
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "clang", emissionKind: NoDebug)
!1 = !DIFile(filename: "empty", directory: "path/to")
!2 = !{i32 2, !"Debug Info Version", i32 3}

View File

@@ -1,8 +1,30 @@
; RUN: llc -filetype=obj -o - < %s | llvm-readobj --codeview - | FileCheck %s
; Check that debug info isn't emitted for CodeView with emissionKind NoDebug
; Check that only basic compiler info is emitted for CodeView with emissionKind NoDebug
; CHECK-NOT: CodeViewTypes
; CHECK-NOT: CodeViewDebugInfo
; CHECK-NOT: CodeViewTypes
; CHECK: CodeViewDebugInfo [
; CHECK-NEXT: Section: .debug$S (4)
; CHECK-NEXT: Magic: 0x4
; CHECK-NEXT: Subsection [
; CHECK-NEXT: SubSectionType: Symbols (0xF1)
; CHECK-NEXT: SubSectionSize: 0x2C
; CHECK-NEXT: ObjNameSym {
; CHECK-NEXT: Kind: S_OBJNAME (0x1101)
; CHECK-NEXT: Signature: 0x0
; CHECK-NEXT: ObjectName:
; CHECK-NEXT: }
; CHECK-NEXT: Compile3Sym {
; CHECK-NEXT: Kind: S_COMPILE3 (0x113C)
; CHECK-NEXT: Language: C (0x0)
; CHECK-NEXT: Flags [ (0x0)
; CHECK-NEXT: ]
; CHECK-NEXT: Machine: X64 (0xD0)
; CHECK-NEXT: FrontendVersion:
; CHECK-NEXT: BackendVersion:
; CHECK-NEXT: VersionName: clang
; CHECK-NEXT: }
; CHECK-NEXT: ]
; CHECK-NEXT: ]
source_filename = "empty"
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"

View File

@@ -107,11 +107,11 @@ source_filename = "test/DebugInfo/X86/fission-cu.ll"
; For COFF we should have this set of relocations for the debug info section
;
; OBJ: .debug_info
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_abbrev (6)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_line (26)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_str (10)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_str (10)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_addr (20)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_abbrev (8)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_line (28)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_str (12)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_str (12)
; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_addr (22)
; OBJ-NEXT: }
; HDR-NOT: .debug_aranges

View File

@@ -27,13 +27,14 @@ source_filename = "test/DebugInfo/X86/fission-cu.ll"
; OBJ-NEXT: 0 .text
; OBJ-NEXT: 1 .data
; OBJ-NEXT: 2 .bss
; OBJ-NEXT: 3 .debug_abbrev
; OBJ-NEXT: 4 .debug_info
; OBJ-NEXT: 5 .debug_str
; OBJ-NEXT: 6 .debug_addr
; OBJ-NEXT: 7 .debug_pubnames
; OBJ-NEXT: 8 .debug_pubtypes
; OBJ-NEXT: 9 .debug_line
; OBJ-NEXT: 3 .debug$S
; OBJ-NEXT: 4 .debug_abbrev
; OBJ-NEXT: 5 .debug_info
; OBJ-NEXT: 6 .debug_str
; OBJ-NEXT: 7 .debug_addr
; OBJ-NEXT: 8 .debug_pubnames
; OBJ-NEXT: 9 .debug_pubtypes
; OBJ-NEXT: 10 .debug_line
; OBJ: .debug_abbrev
; OBJ: .debug_info

View File

@@ -0,0 +1,16 @@
; RUN: llc -filetype=obj -o - < %s | llvm-readobj --codeview - | FileCheck %s
; Check that compiler info is not emitted when CodeView flag is not specified
; CHECK-NOT: CodeViewTypes
; CHECK-NOT: CodeViewDebugInfo
source_filename = "empty"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-uefi"
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "clang", emissionKind: NoDebug)
!1 = !DIFile(filename: "empty", directory: "path/to")
!2 = !{i32 2, !"Debug Info Version", i32 3}

View File

@@ -18,7 +18,7 @@
; CHECK: .loc 1 4 15
; CHECK: .loc 1 5 1
; CHECK-NOT: .section .{{debug.*}}
; CHECK-NOT: .section .{{debug_.*}}
; Function Attrs: nounwind uwtable
define void @f2() #0 !dbg !4 {