[clang] [llvm] [clang][DebugInfo] Emit unified (Itanium) mangled name to structor declarations (PR #154142)
Michael Buch via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 21 01:24:41 PDT 2025
https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/154142
>From ff0b6ac2d035ea3abb8506c02b92e07a17788142 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 18 Aug 2025 15:12:45 +0100
Subject: [PATCH 1/8] [llvm][DebugInfo] Support DW_AT_linkage_names that are
different between declaration and definition
(cherry picked from commit 62641a7cc6b439c747be0a9ae91b9b266d67816e)
---
llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 7 +-
.../structor-declaration-linkage-names.ll | 68 +++++++++++++++++++
2 files changed, 70 insertions(+), 5 deletions(-)
create mode 100644 llvm/test/DebugInfo/Generic/structor-declaration-linkage-names.ll
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index b03fac2d22a52..4904ad03199c7 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -1403,11 +1403,8 @@ bool DwarfUnit::applySubprogramDefinitionAttributes(const DISubprogram *SP,
// Add the linkage name if we have one and it isn't in the Decl.
StringRef LinkageName = SP->getLinkageName();
- assert(((LinkageName.empty() || DeclLinkageName.empty()) ||
- LinkageName == DeclLinkageName) &&
- "decl has a linkage name and it is different");
- if (DeclLinkageName.empty() &&
- // Always emit it for abstract subprograms.
+ // Always emit linkage name for abstract subprograms.
+ if (DeclLinkageName != LinkageName &&
(DD->useAllLinkageNames() || DU->getAbstractScopeDIEs().lookup(SP)))
addLinkageName(SPDie, LinkageName);
diff --git a/llvm/test/DebugInfo/Generic/structor-declaration-linkage-names.ll b/llvm/test/DebugInfo/Generic/structor-declaration-linkage-names.ll
new file mode 100644
index 0000000000000..9b1f2a5b2a186
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/structor-declaration-linkage-names.ll
@@ -0,0 +1,68 @@
+; RUN: %llc_dwarf < %s -filetype=obj | llvm-dwarfdump -debug-info - | FileCheck %s
+
+; Make sure we attach DW_AT_linkage_name on function declarations but only
+; attach it on definitions if the value is different than on the declaration.
+
+target triple = "arm64-apple-macosx"
+
+define void @_Z11SameLinkagev() !dbg !4 {
+entry:
+ ret void
+}
+
+; CHECK: DW_AT_linkage_name ("_Z11SameLinkagev")
+; CHECK: DW_AT_declaration (true)
+; CHECK-NOT: DW_AT_linkage_name ("_Z11SameLinkagev")
+
+define void @_Z11DiffLinkagev() !dbg !8 {
+entry:
+ ret void
+}
+
+; CHECK: DW_AT_linkage_name ("SomeName")
+; CHECK: DW_AT_declaration (true)
+; CHECK: DW_AT_linkage_name ("_Z11DiffLinkagev")
+
+define void @_Z15EmptyDefLinkagev() !dbg !10 {
+entry:
+ ret void
+}
+
+; CHECK: DW_AT_linkage_name ("_Z15EmptyDefLinkagev")
+; CHECK: DW_AT_declaration (true)
+; CHECK-NOT: DW_AT_linkage_name
+
+define void @_Z16EmptyDeclLinkagev() !dbg !12 {
+entry:
+ ret void
+}
+
+; CHECK: DW_AT_declaration (true)
+; CHECK: DW_AT_linkage_name ("_Z16EmptyDeclLinkagev")
+
+define void @_Z13EmptyLinkagesv() !dbg !14 {
+entry:
+ ret void
+}
+
+; CHECK-NOT: DW_AT_linkage_name
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+!1 = !DIFile(filename: "foo.cpp", directory: "/tmp")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = distinct !DISubprogram(name: "SameLinkage", linkageName: "_Z11SameLinkagev", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !7)
+!5 = !DISubroutineType(types: !6)
+!6 = !{null}
+!7 = !DISubprogram(name: "SameLinkage", linkageName: "_Z11SameLinkagev", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
+!8 = distinct !DISubprogram(name: "DiffLinkage", linkageName: "_Z11DiffLinkagev", scope: !1, file: !1, line: 5, type: !5, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !9)
+!9 = !DISubprogram(name: "DiffLinkage", linkageName: "SomeName", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
+!10 = distinct !DISubprogram(name: "EmptyDefLinkage", linkageName: "", scope: !1, file: !1, line: 5, type: !5, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !11)
+!11 = !DISubprogram(name: "EmptyDefLinkage", linkageName: "_Z15EmptyDefLinkagev", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
+!12 = distinct !DISubprogram(name: "EmptyDeclLinkage", linkageName: "_Z16EmptyDeclLinkagev", scope: !1, file: !1, line: 5, type: !5, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !13)
+!13 = !DISubprogram(name: "EmptyDeclLinkage", linkageName: "", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
+!14 = distinct !DISubprogram(name: "EmptyLinkages", linkageName: "", scope: !1, file: !1, line: 5, type: !5, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !15)
+!15 = !DISubprogram(name: "EmptyLinkages", linkageName: "", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
>From aa885ba539135e27e0d2ace6d9a40d3760ee5710 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 18 Aug 2025 15:14:40 +0100
Subject: [PATCH 2/8] [clang][DebugInfo] Emit unified (Itanium) mangled name to
structor declarations
---
clang/include/clang/Basic/ABI.h | 10 +--
clang/lib/AST/ItaniumMangle.cpp | 10 +++
clang/lib/AST/MicrosoftMangle.cpp | 2 +
clang/lib/CodeGen/CGDebugInfo.cpp | 32 +++++++--
clang/lib/CodeGen/CGDebugInfo.h | 4 ++
clang/lib/CodeGen/MicrosoftCXXABI.cpp | 4 ++
.../debug-info-structor-linkage-names.cpp | 72 +++++++++++++++++++
7 files changed, 124 insertions(+), 10 deletions(-)
create mode 100644 clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
diff --git a/clang/include/clang/Basic/ABI.h b/clang/include/clang/Basic/ABI.h
index 231bad799a42c..8279529c316cf 100644
--- a/clang/include/clang/Basic/ABI.h
+++ b/clang/include/clang/Basic/ABI.h
@@ -27,14 +27,16 @@ enum CXXCtorType {
Ctor_Comdat, ///< The COMDAT used for ctors
Ctor_CopyingClosure, ///< Copying closure variant of a ctor
Ctor_DefaultClosure, ///< Default closure variant of a ctor
+ Ctor_Unified, ///< GCC-style unified dtor
};
/// C++ destructor types.
enum CXXDtorType {
- Dtor_Deleting, ///< Deleting dtor
- Dtor_Complete, ///< Complete object dtor
- Dtor_Base, ///< Base object dtor
- Dtor_Comdat ///< The COMDAT used for dtors
+ Dtor_Deleting, ///< Deleting dtor
+ Dtor_Complete, ///< Complete object dtor
+ Dtor_Base, ///< Base object dtor
+ Dtor_Comdat, ///< The COMDAT used for dtors
+ Dtor_Unified, ///< GCC-style unified dtor
};
} // end namespace clang
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 112678fb2714a..f6ff9861508a2 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6029,6 +6029,8 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T,
// ::= CI2 <type> # base inheriting constructor
//
// In addition, C5 is a comdat name with C1 and C2 in it.
+ // C4 represents a ctor declaration and is used by debuggers to look up
+ // the various ctor variants.
Out << 'C';
if (InheritedFrom)
Out << 'I';
@@ -6039,6 +6041,9 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T,
case Ctor_Base:
Out << '2';
break;
+ case Ctor_Unified:
+ Out << '4';
+ break;
case Ctor_Comdat:
Out << '5';
break;
@@ -6056,6 +6061,8 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
// ::= D2 # base object destructor
//
// In addition, D5 is a comdat name with D1, D2 and, if virtual, D0 in it.
+ // D4 represents a dtor declaration and is used by debuggers to look up
+ // the various dtor variants.
switch (T) {
case Dtor_Deleting:
Out << "D0";
@@ -6066,6 +6073,9 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
case Dtor_Base:
Out << "D2";
break;
+ case Dtor_Unified:
+ Out << "D4";
+ break;
case Dtor_Comdat:
Out << "D5";
break;
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index fc79ab1de24ff..d214db76e419b 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1496,6 +1496,8 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
// it.
case Dtor_Comdat:
llvm_unreachable("not expecting a COMDAT");
+ case Dtor_Unified:
+ llvm_unreachable("not expecting a unified dtor type");
}
llvm_unreachable("Unsupported dtor type?");
}
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 994bdbdae860f..f32e1e8185337 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2177,24 +2177,44 @@ static bool isFunctionLocalClass(const CXXRecordDecl *RD) {
return false;
}
-llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
- const CXXMethodDecl *Method, llvm::DIFile *Unit, llvm::DIType *RecordTy) {
+llvm::StringRef
+CGDebugInfo::GetMethodLinkageName(const CXXMethodDecl *Method) const {
+ assert(Method);
+
bool IsCtorOrDtor =
isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method);
+ // In some ABIs (particularly Itanium) a single ctor/dtor
+ // corresponds to multiple functions. Attach a "unified"
+ // linkage name for those (which is the convention GCC uses).
+ // Otherwise, attach no linkage name.
+ if (IsCtorOrDtor && !CGM.getTarget().getCXXABI().hasConstructorVariants())
+ return {};
+
+ if (const auto *Ctor = llvm::dyn_cast<CXXConstructorDecl>(Method))
+ return CGM.getMangledName(GlobalDecl(Ctor, CXXCtorType::Ctor_Unified));
+
+ if (const auto *Dtor = llvm::dyn_cast<CXXDestructorDecl>(Method))
+ return CGM.getMangledName(GlobalDecl(Dtor, CXXDtorType::Dtor_Unified));
+
+ return CGM.getMangledName(Method);
+}
+
+llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
+ const CXXMethodDecl *Method, llvm::DIFile *Unit, llvm::DIType *RecordTy) {
+ assert(Method);
+
StringRef MethodName = getFunctionName(Method);
llvm::DISubroutineType *MethodTy = getOrCreateMethodType(Method, Unit);
- // Since a single ctor/dtor corresponds to multiple functions, it doesn't
- // make sense to give a single ctor/dtor a linkage name.
StringRef MethodLinkageName;
// FIXME: 'isFunctionLocalClass' seems like an arbitrary/unintentional
// property to use here. It may've been intended to model "is non-external
// type" but misses cases of non-function-local but non-external classes such
// as those in anonymous namespaces as well as the reverse - external types
// that are function local, such as those in (non-local) inline functions.
- if (!IsCtorOrDtor && !isFunctionLocalClass(Method->getParent()))
- MethodLinkageName = CGM.getMangledName(Method);
+ if (!isFunctionLocalClass(Method->getParent()))
+ MethodLinkageName = GetMethodLinkageName(Method);
// Get the location for the method.
llvm::DIFile *MethodDefUnit = nullptr;
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 497d3a6ab17b1..55c528031368d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -899,6 +899,10 @@ class CGDebugInfo {
std::memcpy(Data + A.size(), B.data(), B.size());
return StringRef(Data, A.size() + B.size());
}
+
+ /// If one exists, returns the linkage name of the specified \
+ /// (non-null) \c Method. Returns empty string otherwise.
+ llvm::StringRef GetMethodLinkageName(const CXXMethodDecl *Method) const;
};
/// A scoped helper to set the current debug location to the specified
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 88f0648660965..94190a149e859 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -77,6 +77,8 @@ class MicrosoftCXXABI : public CGCXXABI {
return false;
case Dtor_Comdat: llvm_unreachable("emitting dtor comdat as function?");
+ case Dtor_Unified:
+ llvm_unreachable("unexpected unified dtor type");
}
llvm_unreachable("bad dtor kind");
}
@@ -1417,6 +1419,8 @@ llvm::GlobalValue::LinkageTypes MicrosoftCXXABI::getCXXDestructorLinkage(
// and are emitted everywhere they are used. They are internal if the class
// is internal.
return llvm::GlobalValue::LinkOnceODRLinkage;
+ case Dtor_Unified:
+ llvm_unreachable("MS C++ ABI does not support unified dtors");
case Dtor_Comdat:
llvm_unreachable("MS C++ ABI does not support comdat dtors");
}
diff --git a/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp b/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
new file mode 100644
index 0000000000000..88b4ee75d4565
--- /dev/null
+++ b/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
@@ -0,0 +1,72 @@
+// Tests that we emit unified constructor/destructor linkage names
+// for ABIs that support it.
+
+// RUN: %clang_cc1 -triple aarch64-apple-macosx -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s --check-prefixes=CHECK,ITANIUM
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s --check-prefixes=CHECK,MSABI
+
+struct Base {
+ Base(int x);
+ ~Base();
+};
+
+Base::Base(int x) {}
+Base::~Base() {}
+
+// Check that we emit unified ctor/dtor (C4/D4) on Itanium but not for MS-ABI.
+
+// CHECK: ![[BASE_CTOR_DECL:[0-9]+]] = !DISubprogram(name: "Base"
+// MSABI-NOT: linkageName:
+// ITANIUM-SAME: linkageName: "_ZN4BaseC4Ei"
+// CHECK-SAME: spFlags: 0
+
+// CHECK: ![[BASE_DTOR_DECL:[0-9]+]] = !DISubprogram(name: "~Base"
+// MSABI-NOT: linkageName:
+// ITANIUM-SAME: linkageName: "_ZN4BaseD4Ev"
+// CHECK-SAME: spFlags: 0
+
+// Check that the ctor/dtor definitions have linkage names that aren't
+// the ones on the declaration.
+
+// CHECK: !DISubprogram(name: "Base"
+// MSABI-SAME: linkageName:
+// ITANIUM-SAME: linkageName: "_ZN4BaseC2Ei"
+// CHECK-SAME: spFlags: DISPFlagDefinition
+// CHECK-SAME: declaration: ![[BASE_CTOR_DECL]]
+
+// ITANIUM: !DISubprogram(name: "Base"
+// ITANIUM-SAME: linkageName: "_ZN4BaseC1Ei"
+// ITANIUM-SAME: spFlags: DISPFlagDefinition
+// ITANIUM-SAME: declaration: ![[BASE_CTOR_DECL]]
+
+// CHECK: !DISubprogram(name: "~Base"
+// MSABI-SAME: linkageName:
+// ITANIUM-SAME: linkageName: "_ZN4BaseD2Ev"
+// CHECK-SAME: spFlags: DISPFlagDefinition
+// CHECK-SAME: declaration: ![[BASE_DTOR_DECL]]
+
+// ITANIUM: !DISubprogram(name: "~Base"
+// ITANIUM-SAME: linkageName: "_ZN4BaseD1Ev"
+// ITANIUM-SAME: spFlags: DISPFlagDefinition
+// ITANIUM-SAME: declaration: ![[BASE_DTOR_DECL]]
+
+struct Derived : public Base {
+ using Base::Base;
+} d(5);
+
+// CHECK: !DISubprogram(name: "Base"
+// MSABI-SAME: linkageName:
+// ITANIUM-SAME: linkageName: "_ZN7DerivedCI14BaseEi"
+// CHECK-SAME: spFlags: {{.*}}DISPFlagDefinition
+// CHECK-SAME: declaration: ![[BASE_INHERIT_CTOR_DECL:[0-9]+]]
+
+// CHECK: [[BASE_INHERIT_CTOR_DECL]] = !DISubprogram(name: "Base"
+// MSABI-NOT: linkageName:
+// ITANIUM-SAME: linkageName: "_ZN7DerivedCI44BaseEi"
+// CHECK-SAME spFlags: 0
+
+// ITANIUM: !DISubprogram(name: "Base"
+// ITANIUM-SAME: linkageName: "_ZN7DerivedCI24BaseEi"
+// ITANIUM-SAME: spFlags: DISPFlagDefinition
+// ITANIUM-SAME: declaration: ![[BASE_INHERIT_CTOR_DECL:[0-9]+]]
+
+// MSABI: !DISubprogram(name: "~Derived"
>From c37afa19131206f1bd71b933ad2337a61291bb60 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 18 Aug 2025 17:07:42 +0100
Subject: [PATCH 3/8] fixup! switch-statement warnings
---
clang/lib/CodeGen/CGClass.cpp | 2 ++
clang/lib/CodeGen/ItaniumCXXABI.cpp | 5 +++++
2 files changed, 7 insertions(+)
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index e9a92ae0f01cb..ba587943384a6 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1502,6 +1502,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
// we'd introduce *two* handler blocks. In the Microsoft ABI, we
// always delegate because we might not have a definition in this TU.
switch (DtorType) {
+ case Dtor_Unified:
+ llvm_unreachable("not expecting a unified dtor");
case Dtor_Comdat: llvm_unreachable("not expecting a COMDAT");
case Dtor_Deleting: llvm_unreachable("already handled deleting case");
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 4ed3775f156c9..4e6907ba68acf 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -91,6 +91,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
case Dtor_Comdat:
llvm_unreachable("emitting dtor comdat as function?");
+ case Dtor_Unified:
+ llvm_unreachable("emitting unified dtor as function?");
}
llvm_unreachable("bad dtor kind");
}
@@ -108,6 +110,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
case Ctor_Comdat:
llvm_unreachable("emitting ctor comdat as function?");
+
+ case Ctor_Unified:
+ llvm_unreachable("emitting unified ctor as function?");
}
llvm_unreachable("bad dtor kind");
}
>From bfed7f8160b5bf7a6805a20a2df42c9cbe37b1a2 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 20 Aug 2025 10:19:45 +0100
Subject: [PATCH 4/8] fixup! add clang driver option
---
clang/include/clang/Basic/DebugOptions.def | 5 ++++-
clang/include/clang/Driver/Options.td | 9 +++++++++
clang/lib/CodeGen/CGDebugInfo.cpp | 5 ++++-
clang/lib/Driver/ToolChains/Clang.cpp | 4 ++++
.../debug-info-structor-linkage-names.cpp | 20 +++++++++++++++++--
5 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def
index c6e736e92744c..c2fb705662c0c 100644
--- a/clang/include/clang/Basic/DebugOptions.def
+++ b/clang/include/clang/Basic/DebugOptions.def
@@ -125,8 +125,11 @@ DEBUGOPT(DebugNameTable, 2, 0, Compatible)
/// Whether to use DWARF base address specifiers in .debug_ranges.
DEBUGOPT(DebugRangesBaseAddress, 1, 0, Compatible)
+/// Whether to add linkage names to constructor/destructor declarations.
+DEBUGOPT(DebugStructorDeclLinkageNames, 1, 0, Benign)
+
/// Whether to embed source in DWARF debug line section.
-DEBUGOPT(EmbedSource, 1, 0, Compatible)
+DEBUGOPT(EmbedSource, 1, 1, Compatible)
#undef DEBUGOPT
#undef ENUM_DEBUGOPT
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 9cfb1bbcac5c3..ef0097f8ba334 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4767,6 +4767,15 @@ def gembed_source : Flag<["-"], "gembed-source">, Group<g_flags_Group>,
def gno_embed_source : Flag<["-"], "gno-embed-source">, Group<g_flags_Group>,
Flags<[NoXarchOption]>,
HelpText<"Restore the default behavior of not embedding source text in DWARF debug sections">;
+defm structor_decl_linkage_names
+ : BoolGOption<"structor-decl-linkage-names",
+ CodeGenOpts<"DebugStructorDeclLinkageNames">, DefaultTrue,
+ NegFlag<SetFalse>,
+ PosFlag<SetTrue, [], [],
+ "Attach linkage names to C++ constructor/destructor "
+ "declarations in DWARF."
+ "Implies -g.">,
+ BothFlags<[], [ClangOption, CLOption, CC1Option]>>;
defm key_instructions : BoolGOption<"key-instructions",
CodeGenOpts<"DebugKeyInstructions">, DefaultFalse,
NegFlag<SetFalse>, PosFlag<SetTrue, [], [],
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index f32e1e8185337..2ad19e9581c12 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2181,9 +2181,12 @@ llvm::StringRef
CGDebugInfo::GetMethodLinkageName(const CXXMethodDecl *Method) const {
assert(Method);
- bool IsCtorOrDtor =
+ const bool IsCtorOrDtor =
isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method);
+ if (IsCtorOrDtor && !CGM.getCodeGenOpts().DebugStructorDeclLinkageNames)
+ return {};
+
// In some ABIs (particularly Itanium) a single ctor/dtor
// corresponds to multiple functions. Attach a "unified"
// linkage name for those (which is the convention GCC uses).
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 29b7180df5cb5..10a00f268884e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4594,6 +4594,10 @@ renderDebugOptions(const ToolChain &TC, const Driver &D, const llvm::Triple &T,
options::OPT_gno_key_instructions, false))
CmdArgs.push_back("-gkey-instructions");
+ if (!Args.hasFlag(options::OPT_gstructor_decl_linkage_names,
+ options::OPT_gno_structor_decl_linkage_names, true))
+ CmdArgs.push_back("-gno-structor-decl-linkage-names");
+
if (EmitCodeView) {
CmdArgs.push_back("-gcodeview");
diff --git a/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp b/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
index 88b4ee75d4565..72b0bf39e0bbb 100644
--- a/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
@@ -1,8 +1,21 @@
// Tests that we emit unified constructor/destructor linkage names
// for ABIs that support it.
-// RUN: %clang_cc1 -triple aarch64-apple-macosx -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s --check-prefixes=CHECK,ITANIUM
-// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s --check-prefixes=CHECK,MSABI
+// Check that -gstructor-decl-linkage-names is the default.
+// RUN: %clang_cc1 -triple aarch64-apple-macosx -emit-llvm -debug-info-kind=standalone \
+// RUN: %s -o - | FileCheck %s --check-prefixes=CHECK,ITANIUM
+//
+// Check with -gstructor-decl-linkage-names.
+// RUN: %clang_cc1 -triple aarch64-apple-macosx -emit-llvm -debug-info-kind=standalone \
+// RUN: -gstructor-decl-linkage-names %s -o - | FileCheck %s --check-prefixes=CHECK,ITANIUM
+//
+// Check with -gno-structor-decl-linkage-names.
+// RUN: %clang_cc1 -triple aarch64-apple-macosx -emit-llvm -debug-info-kind=standalone \
+// RUN: -gno-structor-decl-linkage-names %s -o - | FileCheck %s --check-prefixes=CHECK,DISABLE
+//
+// Check ABI without structor variants.
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -debug-info-kind=standalone \
+// RUN: -gstructor-decl-linkage-names %s -o - | FileCheck %s --check-prefixes=CHECK,MSABI
struct Base {
Base(int x);
@@ -16,11 +29,13 @@ Base::~Base() {}
// CHECK: ![[BASE_CTOR_DECL:[0-9]+]] = !DISubprogram(name: "Base"
// MSABI-NOT: linkageName:
+// DISABLE-NOT: linkageName:
// ITANIUM-SAME: linkageName: "_ZN4BaseC4Ei"
// CHECK-SAME: spFlags: 0
// CHECK: ![[BASE_DTOR_DECL:[0-9]+]] = !DISubprogram(name: "~Base"
// MSABI-NOT: linkageName:
+// DISABLE-NOT: linkageName:
// ITANIUM-SAME: linkageName: "_ZN4BaseD4Ev"
// CHECK-SAME: spFlags: 0
@@ -61,6 +76,7 @@ struct Derived : public Base {
// CHECK: [[BASE_INHERIT_CTOR_DECL]] = !DISubprogram(name: "Base"
// MSABI-NOT: linkageName:
+// DISABLE-NOT: linkageName:
// ITANIUM-SAME: linkageName: "_ZN7DerivedCI44BaseEi"
// CHECK-SAME spFlags: 0
>From f53976c15c0fd69a102aa7876059f1e4ca93d862 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 20 Aug 2025 14:23:06 +0100
Subject: [PATCH 5/8] fixup! fix test
---
clang/test/CodeGenCXX/debug-info-artificial-arg.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/test/CodeGenCXX/debug-info-artificial-arg.cpp b/clang/test/CodeGenCXX/debug-info-artificial-arg.cpp
index a0cf131f83e15..21b8d047b3456 100644
--- a/clang/test/CodeGenCXX/debug-info-artificial-arg.cpp
+++ b/clang/test/CodeGenCXX/debug-info-artificial-arg.cpp
@@ -25,7 +25,8 @@ int main(int argc, char **argv) {
// CHECK: ![[CLASSTYPE:.*]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "A",
// CHECK-SAME: identifier: "_ZTS1A"
// CHECK: ![[ARTARG:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[CLASSTYPE]],{{.*}} DIFlagArtificial
-// CHECK: !DISubprogram(name: "A", scope: ![[CLASSTYPE]]
+// CHECK: !DISubprogram(name: "A"
+// CHECK-SAME: scope: ![[CLASSTYPE]]
// CHECK-SAME: line: 12
// CHECK-SAME: DIFlagPublic
// CHECK: !DISubroutineType(types: [[FUNCTYPE:![0-9]*]])
>From c3c3ccdfe332831b7b35583e082e11d624f79aba Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 20 Aug 2025 14:25:26 +0100
Subject: [PATCH 6/8] fixup! fix test
---
clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp b/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
index 72b0bf39e0bbb..b7aac198c5180 100644
--- a/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structor-linkage-names.cpp
@@ -85,4 +85,5 @@ struct Derived : public Base {
// ITANIUM-SAME: spFlags: DISPFlagDefinition
// ITANIUM-SAME: declaration: ![[BASE_INHERIT_CTOR_DECL:[0-9]+]]
-// MSABI: !DISubprogram(name: "~Derived"
+// MSABI: !DISubprogram(name: "~Derived"
+// DISABLE: !DISubprogram(name: "~Derived"
>From 6292ed376eaca0cd115f983f419e91cdbf94eaf2 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 21 Aug 2025 08:54:57 +0100
Subject: [PATCH 7/8] fixup! add comment
---
clang/include/clang/Basic/DebugOptions.def | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def
index c2fb705662c0c..a768b12fa4e0d 100644
--- a/clang/include/clang/Basic/DebugOptions.def
+++ b/clang/include/clang/Basic/DebugOptions.def
@@ -126,10 +126,13 @@ DEBUGOPT(DebugNameTable, 2, 0, Compatible)
DEBUGOPT(DebugRangesBaseAddress, 1, 0, Compatible)
/// Whether to add linkage names to constructor/destructor declarations.
+/// This is an escape hatch for cases where attaching the additional linkage
+/// names would increase debug-info size (particularly the .debug_str section)
+/// too much.
DEBUGOPT(DebugStructorDeclLinkageNames, 1, 0, Benign)
/// Whether to embed source in DWARF debug line section.
-DEBUGOPT(EmbedSource, 1, 1, Compatible)
+DEBUGOPT(EmbedSource, 1, 0, Compatible)
#undef DEBUGOPT
#undef ENUM_DEBUGOPT
>From a9e9151e73fa63c90e7f774664320a193b80f14b Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 21 Aug 2025 09:22:33 +0100
Subject: [PATCH 8/8] fixup! add DocBrief
---
clang/include/clang/Driver/Options.td | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index ef0097f8ba334..f701c4d9e485f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4775,7 +4775,10 @@ defm structor_decl_linkage_names
"Attach linkage names to C++ constructor/destructor "
"declarations in DWARF."
"Implies -g.">,
- BothFlags<[], [ClangOption, CLOption, CC1Option]>>;
+ BothFlags<[], [ClangOption, CLOption, CC1Option]>>,
+ DocBrief<[{On some ABIs (e.g., Itanium), constructors and destructors may have multiple variants. Historically, when generating DWARF, Clang did not attach ``DW_AT_linkage_name``s to structor DIEs because there were multiple possible manglings (depending on the structor variant) that could be used. With ``-gstructor-decl-linkage-names``, for ABIs with structor variants, we attach a "unified" mangled name to structor declarations DIEs which debuggers can use to look up all the definitions for a structor declaration. E.g., a "unified" mangled name ``_ZN3FooC4Ev`` may have multiple definitions associated with it such as ``_ZN3FooC1Ev`` and ``_ZN3FooC2Ev``.
+
+Enabling this flag results in a better interactive debugging experience (both GDB and LLDB have support for understanding these "unified" linkage names). However, it comes with a significant increase in debug-info size (particularly the `.debug_str` section). As an escape hatch, users can disable this feature using ``-gno-structor-decl-linkage-names``.}]>;
defm key_instructions : BoolGOption<"key-instructions",
CodeGenOpts<"DebugKeyInstructions">, DefaultFalse,
NegFlag<SetFalse>, PosFlag<SetTrue, [], [],
More information about the llvm-commits
mailing list