-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[clang][DebugInfo] Emit unified (Itanium) mangled name to structor declarations #154142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[clang][DebugInfo] Emit unified (Itanium) mangled name to structor declarations #154142
Conversation
@llvm/pr-subscribers-clang-driver @llvm/pr-subscribers-debuginfo Author: Michael Buch (Michael137) ChangesDepends on #154137 This patch is motivated by #149827, where we plan on using mangled names on structor declarations to find the exact structor definition that LLDB's expression evaluator should call. Given a This patch aligns Clang with GCC's DWARF output and allows us to implement the same lookup scheme in LLDB. Full diff: https://github.com/llvm/llvm-project/pull/154142.diff 9 Files Affected:
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 a7380a19e3607..484f1b12f3588 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6014,6 +6014,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';
@@ -6024,6 +6026,9 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T,
case Ctor_Base:
Out << '2';
break;
+ case Ctor_Unified:
+ Out << '4';
+ break;
case Ctor_Comdat:
Out << '5';
break;
@@ -6041,6 +6046,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";
@@ -6051,6 +6058,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 241c7c35fcc83..fe188de81b20f 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"
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)
|
@llvm/pr-subscribers-clang Author: Michael Buch (Michael137) ChangesDepends on #154137 This patch is motivated by #149827, where we plan on using mangled names on structor declarations to find the exact structor definition that LLDB's expression evaluator should call. Given a This patch aligns Clang with GCC's DWARF output and allows us to implement the same lookup scheme in LLDB. Full diff: https://github.com/llvm/llvm-project/pull/154142.diff 9 Files Affected:
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 a7380a19e3607..484f1b12f3588 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6014,6 +6014,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';
@@ -6024,6 +6026,9 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T,
case Ctor_Base:
Out << '2';
break;
+ case Ctor_Unified:
+ Out << '4';
+ break;
case Ctor_Comdat:
Out << '5';
break;
@@ -6041,6 +6046,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";
@@ -6051,6 +6058,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 241c7c35fcc83..fe188de81b20f 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"
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)
|
I provided some debug-info size numbers in #149827 but I'll regenerate them with this PR because the implementation changed slightly. |
a430cef
to
cf9f73b
Compare
Here is the debug-info increase when I run bloaty on all the Clang/LLDB/LLVM object files:
Google used an earlier version of this patch to check how their binaries are affected. The Also CCing @jmorse as a heads up for Sony in case you have any concerns. |
Test failure is an XPASS in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for adding the flag! I think this could've turned into a big fire drill for us if we had detected a 15% regression in .debug_str size during internal qualification later. 😥
"Attach linkage names to C++ constructor/destructor " | ||
"declarations in DWARF." | ||
"Implies -g.">, | ||
BothFlags<[], [ClangOption, CLOption, CC1Option]>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add DocBrief here to expand on what this flag does, see this example:
https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-frecord-command-line
This seems like a good place to mention that enabling the flag causes a significant increase (estimated 15%) in generated debug_str object size, that it's experimental, and that it enables a better interactive debugging experience, particularly in LLDB, because it enables calling constructors and destructors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added!
/// Whether to embed source in DWARF debug line section. | ||
DEBUGOPT(EmbedSource, 1, 0, Compatible) | ||
DEBUGOPT(EmbedSource, 1, 1, Compatible) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops this must've been by accident
a9e9151
to
7491738
Compare
Similar thanks for the heads up, The change broadly LGTMs, although this isn't a zone of debug-info that I'm really familiar with. |
…een declaration and definition (cherry picked from commit 62641a7)
d6cb62e
to
91da739
Compare
Depends on #154137
This patch is motivated by #149827, where we plan on using mangled names on structor declarations to find the exact structor definition that LLDB's expression evaluator should call.
Given a
DW_TAG_subprogram
for a function declaration, the most convenient way for a debugger to find the corresponding definition is to use theDW_AT_linkage_name
(i.e., the mangled name). However, we currently can't do that for constructors/destructors because Clang doesn't attach linkage names to them. This is because, depending on ABI, there can be multiple definitions for a single constructor/destructor declaration. The way GCC works around this is by producing aC4
/D4
"unified" mangling for structor declarations (see godbolt). GDB uses this to locate the relevant definitions.This patch aligns Clang with GCC's DWARF output and allows us to implement the same lookup scheme in LLDB.