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/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def index c6e736e92744c..a768b12fa4e0d 100644 --- a/clang/include/clang/Basic/DebugOptions.def +++ b/clang/include/clang/Basic/DebugOptions.def @@ -125,6 +125,12 @@ 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. +/// 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, 0, Compatible) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 7313b360f521a..d5458d1ead136 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4767,6 +4767,18 @@ def gembed_source : Flag<["-"], "gembed-source">, Group, def gno_embed_source : Flag<["-"], "gno-embed-source">, 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, + PosFlag, + 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, PosFlag # 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/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 10e4543a6ab20..0ec6effe84b83 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -1499,6 +1499,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/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index c44fea3e6383d..3c1626e4f337e 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2177,24 +2177,47 @@ static bool isFunctionLocalClass(const CXXRecordDecl *RD) { return false; } +llvm::StringRef +CGDebugInfo::GetMethodLinkageName(const CXXMethodDecl *Method) const { + assert(Method); + + const bool IsCtorOrDtor = + isa(Method) || isa(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). + // Otherwise, attach no linkage name. + if (IsCtorOrDtor && !CGM.getTarget().getCXXABI().hasConstructorVariants()) + return {}; + + if (const auto *Ctor = llvm::dyn_cast(Method)) + return CGM.getMangledName(GlobalDecl(Ctor, CXXCtorType::Ctor_Unified)); + + if (const auto *Dtor = llvm::dyn_cast(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) { - bool IsCtorOrDtor = - isa(Method) || isa(Method); + 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 ff9c3cd2d1136..f86077369a42a 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/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 569fbe9e3bd3a..8d749c57d1436 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"); } 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/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 1b44090534e82..65a63e5dfafb4 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4595,6 +4595,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/DebugInfo/CXX/artificial-arg.cpp b/clang/test/DebugInfo/CXX/artificial-arg.cpp index a0cf131f83e15..21b8d047b3456 100644 --- a/clang/test/DebugInfo/CXX/artificial-arg.cpp +++ b/clang/test/DebugInfo/CXX/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]*]]) diff --git a/clang/test/DebugInfo/CXX/debug-info-structor-linkage-names.cpp b/clang/test/DebugInfo/CXX/debug-info-structor-linkage-names.cpp new file mode 100644 index 0000000000000..b7aac198c5180 --- /dev/null +++ b/clang/test/DebugInfo/CXX/debug-info-structor-linkage-names.cpp @@ -0,0 +1,89 @@ +// Tests that we emit unified constructor/destructor linkage names +// for ABIs that support it. + +// 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); + ~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: +// 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 + +// 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: +// DISABLE-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" +// DISABLE: !DISubprogram(name: "~Derived" diff --git a/clang/test/DebugInfo/ObjCXX/cyclic.mm b/clang/test/DebugInfo/ObjCXX/cyclic.mm index 2fb1611c904d0..a062b6ad50612 100644 --- a/clang/test/DebugInfo/ObjCXX/cyclic.mm +++ b/clang/test/DebugInfo/ObjCXX/cyclic.mm @@ -10,8 +10,9 @@ // CHECK-SAME: identifier: // CHECK: ![[BMEMBERS]] = !{![[BB:[0-9]+]]} B(struct A *); -// CHECK: ![[BB]] = !DISubprogram(name: "B", scope: ![[B]] -// CHECK-SAME: line: [[@LINE-2]], +// CHECK: ![[BB]] = !DISubprogram(name: "B", +// CHECK-SAME: scope: ![[B]] +// CHECK-SAME: line: [[@LINE-3]], // CHECK-SAME: type: ![[TY:[0-9]+]], // CHECK: ![[TY]] = !DISubroutineType(types: ![[ARGS:[0-9]+]]) // CHECK: ![[ARGS]] = !{null, ![[THIS:[0-9]+]], !{{[^,]+}}} 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)