https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/149827
>From d55e41fa03d09b2ddfc9484c4a70a7d21ed9a994 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 18 Aug 2025 15:12:45 +0100 Subject: [PATCH 01/10] [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 630b95951cdcfa5f0b34be7ce2a209566e0c25a8 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 18 Aug 2025 15:14:40 +0100 Subject: [PATCH 02/10] [clang][DebugInfo] Emit unified (Itanium) mangled name to structor declarations --- clang/include/clang/Basic/ABI.h | 10 ++- clang/include/clang/Basic/DebugOptions.def | 6 ++ clang/include/clang/Driver/Options.td | 12 +++ clang/lib/AST/ItaniumMangle.cpp | 10 +++ clang/lib/AST/MicrosoftMangle.cpp | 2 + clang/lib/CodeGen/CGClass.cpp | 2 + clang/lib/CodeGen/CGDebugInfo.cpp | 35 ++++++-- clang/lib/CodeGen/CGDebugInfo.h | 4 + clang/lib/CodeGen/ItaniumCXXABI.cpp | 5 ++ clang/lib/CodeGen/MicrosoftCXXABI.cpp | 4 + clang/lib/Driver/ToolChains/Clang.cpp | 4 + clang/test/DebugInfo/CXX/artificial-arg.cpp | 3 +- .../CXX/debug-info-structor-linkage-names.cpp | 89 +++++++++++++++++++ 13 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 clang/test/DebugInfo/CXX/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/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 9cfb1bbcac5c3..f701c4d9e485f 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<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]>>, + 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, [], [], 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/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/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 994bdbdae860f..2ad19e9581c12 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<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). + // 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) { - bool IsCtorOrDtor = - isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(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 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/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"); } 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 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/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" >From 967b38d1f8c676aebaed352547b033b0070f9622 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 4 Aug 2025 14:20:45 +0100 Subject: [PATCH 03/10] [clang][Mangle] Inject structor type into mangled name when mangling for LLDB JIT expressions This patch adds special handling for `AsmLabel`s created by LLDB. LLDB uses `AsmLabel`s to encode information about a function declaration to make it easier to locate function symbols when JITing C++ expressions. For constructors/destructors LLDB doesn't know at the time of creating the `AsmLabelAttr` which structor variant the expression evaluator will need to call (this is decided when compiling the expression). So we make the Clang mangler inject this information into our custom label when we're JITting the expression. --- clang/lib/AST/Mangle.cpp | 33 +++++++++++++- clang/unittests/AST/DeclTest.cpp | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp index 0bfb51c11f0a5..1131477fa7200 100644 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -152,6 +152,33 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) { return shouldMangleCXXName(D); } +static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func:"; + +/// Given an LLDB function call label, this function prints the label +/// into \c Out, together with the structor type of \c GD (if the +/// decl is a constructor/destructor). LLDB knows how to handle mangled +/// names with this encoding. +/// +/// Example input label: +/// $__lldb_func::123:456:~Foo +/// +/// Example output: +/// $__lldb_func:D1:123:456:~Foo +/// +static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD, + llvm::raw_ostream &Out) { + assert(label.starts_with(g_lldb_func_call_label_prefix)); + + Out << g_lldb_func_call_label_prefix; + + if (llvm::isa<clang::CXXConstructorDecl>(GD.getDecl())) + Out << "C" << GD.getCtorType(); + else if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl())) + Out << "D" << GD.getDtorType(); + + Out << label.substr(g_lldb_func_call_label_prefix.size()); +} + void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { const ASTContext &ASTContext = getASTContext(); const NamedDecl *D = cast<NamedDecl>(GD.getDecl()); @@ -185,7 +212,11 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { if (!UserLabelPrefix.empty()) Out << '\01'; // LLVM IR Marker for __asm("foo") - Out << ALA->getLabel(); + if (ALA->getLabel().starts_with(g_lldb_func_call_label_prefix)) + emitLLDBAsmLabel(ALA->getLabel(), GD, Out); + else + Out << ALA->getLabel(); + return; } diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp index 6b443918ec137..4bd7886ef9b35 100644 --- a/clang/unittests/AST/DeclTest.cpp +++ b/clang/unittests/AST/DeclTest.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Mangle.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/ABI.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/TargetInfo.h" @@ -102,6 +103,80 @@ TEST(Decl, AsmLabelAttr) { "foo"); } +TEST(Decl, AsmLabelAttr_LLDB) { + StringRef Code = R"( + struct S { + void f() {} + S() = default; + ~S() = default; + }; + )"; + auto AST = + tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); + ASTContext &Ctx = AST->getASTContext(); + assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") && + "Expected target to have a global prefix"); + DiagnosticsEngine &Diags = AST->getDiagnostics(); + + const auto *DeclS = + selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx)); + + auto *DeclF = *DeclS->method_begin(); + auto *Ctor = *DeclS->ctor_begin(); + auto *Dtor = DeclS->getDestructor(); + + ASSERT_TRUE(DeclF); + ASSERT_TRUE(Ctor); + ASSERT_TRUE(Dtor); + + DeclF->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:_Z1fv")); + Ctor->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:S")); + Dtor->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:~S")); + + std::unique_ptr<ItaniumMangleContext> MC( + ItaniumMangleContext::create(Ctx, Diags)); + + { + std::string Mangled; + llvm::raw_string_ostream OS_Mangled(Mangled); + MC->mangleName(DeclF, OS_Mangled); + + ASSERT_EQ(Mangled, "\x01$__lldb_func::123:123:_Z1fv"); + }; + + { + std::string Mangled; + llvm::raw_string_ostream OS_Mangled(Mangled); + MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Complete), OS_Mangled); + + ASSERT_EQ(Mangled, "\x01$__lldb_func:C0:123:123:S"); + }; + + { + std::string Mangled; + llvm::raw_string_ostream OS_Mangled(Mangled); + MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Base), OS_Mangled); + + ASSERT_EQ(Mangled, "\x01$__lldb_func:C1:123:123:S"); + }; + + { + std::string Mangled; + llvm::raw_string_ostream OS_Mangled(Mangled); + MC->mangleName(GlobalDecl(Dtor, CXXDtorType::Dtor_Deleting), OS_Mangled); + + ASSERT_EQ(Mangled, "\x01$__lldb_func:D0:123:123:~S"); + }; + + { + std::string Mangled; + llvm::raw_string_ostream OS_Mangled(Mangled); + MC->mangleName(GlobalDecl(Dtor, CXXDtorType::Dtor_Base), OS_Mangled); + + ASSERT_EQ(Mangled, "\x01$__lldb_func:D2:123:123:~S"); + }; +} + TEST(Decl, MangleDependentSizedArray) { StringRef Code = R"( template <int ...N> >From 4539148d9e460e95627b847df3861f864b3f12b6 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Thu, 14 Aug 2025 10:14:24 +0100 Subject: [PATCH 04/10] [lldb][CPlusPlusLanguage] Expose ManglingSubstitutor as static helpers Allows us to use the mangling substitution facilities in CPlusPlusLanguage but also SymbolFileDWARF. --- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 322 ++++++++++-------- .../Language/CPlusPlus/CPlusPlusLanguage.h | 13 + 2 files changed, 201 insertions(+), 134 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index b4207439f5285..9d9880edd2454 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -604,126 +604,6 @@ bool CPlusPlusLanguage::ExtractContextAndIdentifier( return false; } -namespace { -class NodeAllocator { - llvm::BumpPtrAllocator Alloc; - -public: - void reset() { Alloc.Reset(); } - - template <typename T, typename... Args> T *makeNode(Args &&...args) { - return new (Alloc.Allocate(sizeof(T), alignof(T))) - T(std::forward<Args>(args)...); - } - - void *allocateNodeArray(size_t sz) { - return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, - alignof(llvm::itanium_demangle::Node *)); - } -}; - -template <typename Derived> -class ManglingSubstitutor - : public llvm::itanium_demangle::AbstractManglingParser<Derived, - NodeAllocator> { - using Base = - llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>; - -public: - ManglingSubstitutor() : Base(nullptr, nullptr) {} - - template <typename... Ts> - ConstString substitute(llvm::StringRef Mangled, Ts &&...Vals) { - this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...); - return substituteImpl(Mangled); - } - -protected: - void reset(llvm::StringRef Mangled) { - Base::reset(Mangled.begin(), Mangled.end()); - Written = Mangled.begin(); - Result.clear(); - Substituted = false; - } - - ConstString substituteImpl(llvm::StringRef Mangled) { - Log *log = GetLog(LLDBLog::Language); - if (this->parse() == nullptr) { - LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled); - return ConstString(); - } - if (!Substituted) - return ConstString(); - - // Append any trailing unmodified input. - appendUnchangedInput(); - LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result); - return ConstString(Result); - } - - void trySubstitute(llvm::StringRef From, llvm::StringRef To) { - if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From)) - return; - - // We found a match. Append unmodified input up to this point. - appendUnchangedInput(); - - // And then perform the replacement. - Result += To; - Written += From.size(); - Substituted = true; - } - -private: - /// Input character until which we have constructed the respective output - /// already. - const char *Written = ""; - - llvm::SmallString<128> Result; - - /// Whether we have performed any substitutions. - bool Substituted = false; - - const char *currentParserPos() const { return this->First; } - - void appendUnchangedInput() { - Result += - llvm::StringRef(Written, std::distance(Written, currentParserPos())); - Written = currentParserPos(); - } -}; - -/// Given a mangled function `Mangled`, replace all the primitive function type -/// arguments of `Search` with type `Replace`. -class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> { - llvm::StringRef Search; - llvm::StringRef Replace; - -public: - void reset(llvm::StringRef Mangled, llvm::StringRef Search, - llvm::StringRef Replace) { - ManglingSubstitutor::reset(Mangled); - this->Search = Search; - this->Replace = Replace; - } - - llvm::itanium_demangle::Node *parseType() { - trySubstitute(Search, Replace); - return ManglingSubstitutor::parseType(); - } -}; - -class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> { -public: - llvm::itanium_demangle::Node * - parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { - trySubstitute("C1", "C2"); - trySubstitute("D1", "D2"); - return ManglingSubstitutor::parseCtorDtorName(SoFar, State); - } -}; -} // namespace - std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings( const ConstString mangled_name) const { std::vector<ConstString> alternates; @@ -751,29 +631,49 @@ std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings( alternates.push_back(ConstString(fixed_scratch)); } - TypeSubstitutor TS; + auto *log = GetLog(LLDBLog::Language); + // `char` is implementation defined as either `signed` or `unsigned`. As a // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed // char, 'h'-unsigned char. If we're looking for symbols with a signed char // parameter, try finding matches which have the general case 'c'. - if (ConstString char_fixup = - TS.substitute(mangled_name.GetStringRef(), "a", "c")) - alternates.push_back(char_fixup); + if (auto char_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "a", "c")) { + // LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result); + if (*char_fixup_or_err) + alternates.push_back(*char_fixup_or_err); + } else + LLDB_LOG_ERROR(log, char_fixup_or_err.takeError(), + "Failed to substitute 'char' type mangling: {0}"); // long long parameter mangling 'x', may actually just be a long 'l' argument - if (ConstString long_fixup = - TS.substitute(mangled_name.GetStringRef(), "x", "l")) - alternates.push_back(long_fixup); + if (auto long_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "x", "l")) { + if (*long_fixup_or_err) + alternates.push_back(*long_fixup_or_err); + } else + LLDB_LOG_ERROR(log, long_fixup_or_err.takeError(), + "Failed to substitute 'long long' type mangling: {0}"); // unsigned long long parameter mangling 'y', may actually just be unsigned // long 'm' argument - if (ConstString ulong_fixup = - TS.substitute(mangled_name.GetStringRef(), "y", "m")) - alternates.push_back(ulong_fixup); - - if (ConstString ctor_fixup = - CtorDtorSubstitutor().substitute(mangled_name.GetStringRef())) - alternates.push_back(ctor_fixup); + if (auto ulong_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "y", "m")) { + if (*ulong_fixup_or_err) + alternates.push_back(*ulong_fixup_or_err); + } else + LLDB_LOG_ERROR( + log, ulong_fixup_or_err.takeError(), + "Failed to substitute 'unsigned long long' type mangling: {0}"); + + if (auto ctor_fixup_or_err = SubstituteStructorAliases_ItaniumMangle( + mangled_name.GetStringRef())) { + if (*ctor_fixup_or_err) { + alternates.push_back(*ctor_fixup_or_err); + } + } else + LLDB_LOG_ERROR(log, ctor_fixup_or_err.takeError(), + "Failed to substitute structor alias manglings: {0}"); return alternates; } @@ -2442,6 +2342,160 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable( } } +namespace { +class NodeAllocator { + llvm::BumpPtrAllocator Alloc; + +public: + void reset() { Alloc.Reset(); } + + template <typename T, typename... Args> T *makeNode(Args &&...args) { + return new (Alloc.Allocate(sizeof(T), alignof(T))) + T(std::forward<Args>(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, + alignof(llvm::itanium_demangle::Node *)); + } +}; + +template <typename Derived> +class ManglingSubstitutor + : public llvm::itanium_demangle::AbstractManglingParser<Derived, + NodeAllocator> { + using Base = + llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>; + +public: + ManglingSubstitutor() : Base(nullptr, nullptr) {} + + template <typename... Ts> + llvm::Expected<ConstString> substitute(llvm::StringRef Mangled, + Ts &&...Vals) { + this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...); + return substituteImpl(Mangled); + } + +protected: + void reset(llvm::StringRef Mangled) { + Base::reset(Mangled.begin(), Mangled.end()); + Written = Mangled.begin(); + Result.clear(); + Substituted = false; + } + + llvm::Expected<ConstString> substituteImpl(llvm::StringRef Mangled) { + if (this->parse() == nullptr) + return llvm::createStringError( + llvm::formatv("Failed to substitute mangling in {0}", Mangled)); + + if (!Substituted) + return ConstString(); + + // Append any trailing unmodified input. + appendUnchangedInput(); + return ConstString(Result); + } + + void trySubstitute(llvm::StringRef From, llvm::StringRef To) { + if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From)) + return; + + // We found a match. Append unmodified input up to this point. + appendUnchangedInput(); + + // And then perform the replacement. + Result += To; + Written += From.size(); + Substituted = true; + } + +private: + /// Input character until which we have constructed the respective output + /// already. + const char *Written = ""; + + llvm::SmallString<128> Result; + + /// Whether we have performed any substitutions. + bool Substituted = false; + + const char *currentParserPos() const { return this->First; } + + void appendUnchangedInput() { + Result += + llvm::StringRef(Written, std::distance(Written, currentParserPos())); + Written = currentParserPos(); + } +}; + +/// Given a mangled function `Mangled`, replace all the primitive function type +/// arguments of `Search` with type `Replace`. +class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + + llvm::itanium_demangle::Node *parseType() { + trySubstitute(Search, Replace); + return ManglingSubstitutor::parseType(); + } +}; + +class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + + void reset(llvm::StringRef Mangled) { ManglingSubstitutor::reset(Mangled); } + + llvm::itanium_demangle::Node * + parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { + if (!Search.empty() && !Replace.empty()) { + trySubstitute(Search, Replace); + } else { + trySubstitute("D1", "D2"); + trySubstitute("C1", "C2"); + } + return ManglingSubstitutor::parseCtorDtorName(SoFar, State); + } +}; +} // namespace + +llvm::Expected<ConstString> +CPlusPlusLanguage::SubstituteType_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to) { + return TypeSubstitutor().substitute(mangled_name, subst_from, subst_to); +} + +llvm::Expected<ConstString> CPlusPlusLanguage::SubstituteStructor_ItaniumMangle( + llvm::StringRef mangled_name, llvm::StringRef subst_from, + llvm::StringRef subst_to) { + return CtorDtorSubstitutor().substitute(mangled_name, subst_from, subst_to); +} + +llvm::Expected<ConstString> +CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle( + llvm::StringRef mangled_name) { + return CtorDtorSubstitutor().substitute(mangled_name); +} + #define LLDB_PROPERTIES_language_cplusplus #include "LanguageCPlusPlusProperties.inc" diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 4a30299dd2658..67b28bf202c3a 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -164,6 +164,19 @@ class CPlusPlusLanguage : public Language { ConstString FindBestAlternateFunctionMangledName( const Mangled mangled, const SymbolContext &sym_ctx) const override; + static llvm::Expected<ConstString> + SubstituteType_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to); + + static llvm::Expected<ConstString> + SubstituteStructor_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to); + + static llvm::Expected<ConstString> + SubstituteStructorAliases_ItaniumMangle(llvm::StringRef mangled_name); + llvm::StringRef GetInstanceVariableName() override { return "this"; } FormatEntity::Entry GetFunctionNameFormat() const override; >From 1a8b860e466010ac8aefe8c08ec143949031c12d Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 4 Aug 2025 14:21:16 +0100 Subject: [PATCH 05/10] [lldb][Expression] Encode structor variant into FunctionCallLabel This patch is an implementation of [this discussion](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7) about handling ABI-tagged structors during expression evaluation. **Motivation** LLDB encodes the mangled name of a `DW_TAG_subprogram` into `AsmLabel`s on function and method Clang AST nodes. This means that when calls to these functions get lowered into IR (when running JITted expressions), the address resolver can locate the appropriate symbol by mangled name (and it's guaranteed to find the symbol because we got the mangled name from debug-info, instead of letting Clang mangle it based on AST structure). However, we don't do this for `CXXConstructorDecl`s/`CXXDestructorDecl`s because these structor declarations in DWARF don't have a linkage name. This is because there can be multiple variants of a structor, each with a distinct mangling in the Itanium ABI. Each structor variant has its own definition `DW_TAG_subprogram`. So LLDB doesn't know which mangled name to put into the `AsmLabel`. Currently this means using an ABI-tagged constructor in LLDB expressions won't work (see [this RFC](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816) for concrete examples). **Proposed Solution** The `FunctionCallLabel` encoding that we put into `AsmLabel`s already supports stuffing more info about a DIE into it. So this patch extends the `FunctionCallLabel` to contain an optional discriminator (a sequence of bytes) which the `SymbolFileDWARF` plugin interprets as the constructor/destructor variant of that DIE. There's a few subtleties here: 1. At the point at which LLDB first constructs the label, it has no way of knowing (just by looking the debug-info declaration), which structor variant the expression evaluator is supposed to call. That's something that gets decided when compiling the expression. So we let the Clang mangler inject the correct structor variant into the `AsmLabel` during JITing. I adjusted the `AsmLabelAttr` mangling for this. An option would've been to create a new Clang attribute which behaved like an `AsmLabel` but with these special semantics for LLDB. My main concern there is that we'd have to adjust all the `AsmLabelAttr` checks around Clang to also now account for this new attribute. 2. The compiler is free to omit the `C1` variant of a constructor if the `C2` variant is sufficient. In that case it may alias `C1` to `C2`, leaving us with only the `C2` `DW_TAG_subprogram` in the object file. Linux is one of the platforms where this occurs. For those cases there's heuristic in `SymbolFileDWARF` where we pick `C2` if we asked for `C1` but it doesn't exist. This may not always be correct (if for some reason the compiler decided to drop `C1` for other reasons). --- lldb/include/lldb/Expression/Expression.h | 8 +- lldb/source/Expression/Expression.cpp | 29 ++-- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 47 +++++- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 137 ++++++++++++++++-- .../SymbolFile/DWARF/SymbolFileDWARF.h | 3 +- .../API/lang/cpp/abi_tag_structors/Makefile | 3 + .../abi_tag_structors/TestAbiTagStructors.py | 30 ++++ .../API/lang/cpp/abi_tag_structors/main.cpp | 38 +++++ lldb/unittests/Expression/ExpressionTest.cpp | 37 +++-- lldb/unittests/Symbol/TestTypeSystemClang.cpp | 14 +- 10 files changed, 291 insertions(+), 55 deletions(-) create mode 100644 lldb/test/API/lang/cpp/abi_tag_structors/Makefile create mode 100644 lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py create mode 100644 lldb/test/API/lang/cpp/abi_tag_structors/main.cpp diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h index 20067f469895b..847226167d584 100644 --- a/lldb/include/lldb/Expression/Expression.h +++ b/lldb/include/lldb/Expression/Expression.h @@ -103,11 +103,15 @@ class Expression { /// /// The format being: /// -/// <prefix>:<module uid>:<symbol uid>:<name> +/// <prefix>:<discriminator>:<module uid>:<symbol uid>:<name> /// /// The label string needs to stay valid for the entire lifetime /// of this object. struct FunctionCallLabel { + /// Arbitrary string which language plugins can interpret for their + /// own needs. + llvm::StringRef discriminator; + /// Unique identifier of the lldb_private::Module /// which contains the symbol identified by \c symbol_id. lldb::user_id_t module_id; @@ -133,7 +137,7 @@ struct FunctionCallLabel { /// /// The representation roundtrips through \c fromString: /// \code{.cpp} - /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov"; + /// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov"; /// FunctionCallLabel label = *fromString(label); /// /// assert (label.toString() == encoded); diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp index 796851ff15ca3..16ecb1d7deef8 100644 --- a/lldb/source/Expression/Expression.cpp +++ b/lldb/source/Expression/Expression.cpp @@ -34,10 +34,10 @@ Expression::Expression(ExecutionContextScope &exe_scope) llvm::Expected<FunctionCallLabel> lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) { - llvm::SmallVector<llvm::StringRef, 4> components; - label.split(components, ":", /*MaxSplit=*/3); + llvm::SmallVector<llvm::StringRef, 5> components; + label.split(components, ":", /*MaxSplit=*/4); - if (components.size() != 4) + if (components.size() != 5) return llvm::createStringError("malformed function call label."); if (components[0] != FunctionCallLabelPrefix) @@ -45,8 +45,10 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) { "expected function call label prefix '{0}' but found '{1}' instead.", FunctionCallLabelPrefix, components[0])); - llvm::StringRef module_label = components[1]; - llvm::StringRef die_label = components[2]; + llvm::StringRef discriminator = components[1]; + llvm::StringRef module_label = components[2]; + llvm::StringRef die_label = components[3]; + llvm::StringRef lookup_name = components[4]; lldb::user_id_t module_id = 0; if (!llvm::to_integer(module_label, module_id)) @@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) { return llvm::createStringError( llvm::formatv("failed to parse symbol ID from '{0}'.", die_label)); - return FunctionCallLabel{/*.module_id=*/module_id, + return FunctionCallLabel{/*.discriminator=*/discriminator, + /*.module_id=*/module_id, /*.symbol_id=*/die_id, - /*.lookup_name=*/components[3]}; + /*.lookup_name=*/lookup_name}; } std::string lldb_private::FunctionCallLabel::toString() const { - return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix, - module_id, symbol_id, lookup_name) + return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix, + discriminator, module_id, symbol_id, lookup_name) .str(); } void llvm::format_provider<FunctionCallLabel>::format( const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) { - OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, " - "lookup_name: {2} }", - label.module_id, label.symbol_id, label.lookup_name); + OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: " + "{1:x}, symbol_id: {2:x}, " + "lookup_name: {3} }", + label.discriminator, label.module_id, label.symbol_id, + label.lookup_name); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index a429ea848b7f7..cc04f1d7879ed 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -251,11 +251,52 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, return cv_quals; } +static const char *GetMangledOrStructorName(const DWARFDIE &die) { + const char *name = die.GetMangledName(/*substitute_name_allowed*/ false); + if (name) + return name; + + name = die.GetName(); + if (!name) + return nullptr; + + DWARFDIE parent = die.GetParent(); + if (!parent.IsStructUnionOrClass()) + return nullptr; + + const char *parent_name = parent.GetName(); + if (!parent_name) + return nullptr; + + // Constructor. + if (::strcmp(parent_name, name) == 0) + return name; + + // Destructor. + if (name[0] == '~' && ::strcmp(parent_name, name + 1) == 0) + return name; + + return nullptr; +} + static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) { - char const *name = die.GetMangledName(/*substitute_name_allowed*/ false); + char const *name = GetMangledOrStructorName(die); if (!name) return {}; + auto *cu = die.GetCU(); + if (!cu) + return {}; + + // FIXME: When resolving function call labels, we check that + // that the definition's DW_AT_specification points to the + // declaration that we encoded into the label here. But if the + // declaration came from a type-unit (and the definition from + // .debug_info), that check won't work. So for now, don't use + // function call labels for declaration DIEs from type-units. + if (cu->IsTypeUnit()) + return {}; + SymbolFileDWARF *dwarf = die.GetDWARF(); if (!dwarf) return {}; @@ -286,7 +327,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) { if (die_id == LLDB_INVALID_UID) return {}; - return FunctionCallLabel{/*module_id=*/module_id, + // Note, discriminator is added by Clang during mangling. + return FunctionCallLabel{/*discriminator=*/{}, + /*module_id=*/module_id, /*symbol_id=*/die_id, /*.lookup_name=*/name} .toString(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 9958af26379b9..30426a037eca9 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "SymbolFileDWARF.h" +#include "clang/Basic/ABI.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Support/Casting.h" @@ -78,6 +80,7 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" @@ -2483,28 +2486,132 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, return false; } -DWARFDIE -SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label) { +static int ClangToItaniumCtorKind(clang::CXXCtorType kind) { + switch (kind) { + case clang::CXXCtorType::Ctor_Complete: + return 1; + case clang::CXXCtorType::Ctor_Base: + return 2; + case clang::CXXCtorType::Ctor_CopyingClosure: + case clang::CXXCtorType::Ctor_DefaultClosure: + case clang::CXXCtorType::Ctor_Comdat: + llvm_unreachable("Unexpected constructor kind."); + } +} + +static int ClangToItaniumDtorKind(clang::CXXDtorType kind) { + switch (kind) { + case clang::CXXDtorType::Dtor_Deleting: + return 0; + case clang::CXXDtorType::Dtor_Complete: + return 1; + case clang::CXXDtorType::Dtor_Base: + return 2; + case clang::CXXDtorType::Dtor_Comdat: + llvm_unreachable("Unexpected destructor kind."); + } +} + +static std::optional<int> +GetItaniumCtorDtorVariant(llvm::StringRef discriminator) { + const bool is_ctor = discriminator.consume_front("C"); + if (!is_ctor && !discriminator.consume_front("D")) + return std::nullopt; + + uint64_t structor_kind; + if (!llvm::to_integer(discriminator, structor_kind)) + return std::nullopt; + + if (is_ctor) { + if (structor_kind > clang::CXXCtorType::Ctor_DefaultClosure) + return std::nullopt; + + return ClangToItaniumCtorKind( + static_cast<clang::CXXCtorType>(structor_kind)); + } + + if (structor_kind > clang::CXXDtorType::Dtor_Comdat) + return std::nullopt; + + return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind)); +} + +DWARFDIE SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, + const DWARFDIE &declaration) { DWARFDIE definition; + llvm::DenseMap<int, DWARFDIE> structor_variant_to_die; + + // eFunctionNameTypeFull for mangled name lookup. + // eFunctionNameTypeMethod is required for structor lookups (since we look + // those up by DW_AT_name). Module::LookupInfo info(ConstString(label.lookup_name), - lldb::eFunctionNameTypeFull, + lldb::eFunctionNameTypeFull | + lldb::eFunctionNameTypeMethod, lldb::eLanguageTypeUnknown); m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) return IterationAction::Continue; - // We don't check whether the specification DIE for this function - // corresponds to the declaration DIE because the declaration might be in - // a type-unit but the definition in the compile-unit (and it's - // specifcation would point to the declaration in the compile-unit). We - // rely on the mangled name within the module to be enough to find us the - // unique definition. - definition = entry; - return IterationAction::Stop; + auto spec = entry.GetAttributeValueAsReferenceDIE(DW_AT_specification); + if (!spec) + return IterationAction::Continue; + + if (spec != declaration) + return IterationAction::Continue; + + // We're not picking a specific structor variant DIE, so we're done here. + if (label.discriminator.empty()) { + definition = entry; + return IterationAction::Stop; + } + + const char *mangled = + entry.GetMangledName(/*substitute_name_allowed=*/false); + if (!mangled) + return IterationAction::Continue; + + llvm::ItaniumPartialDemangler D; + if (D.partialDemangle(mangled)) + return IterationAction::Continue; + + auto structor_variant = D.getCtorOrDtorVariant(); + if (!structor_variant) + return IterationAction::Continue; + + auto [_, inserted] = structor_variant_to_die.try_emplace(*structor_variant, + std::move(entry)); + assert(inserted); + + // The compiler may choose to alias the constructor variants + // (notably this happens on Linux), so we might not have a definition + // DIE for some structor variants. Hence we iterate over all variants + // and pick the most appropriate one out of those. + return IterationAction::Continue; }); - return definition; + if (definition.IsValid()) + return definition; + + auto label_variant = GetItaniumCtorDtorVariant(label.discriminator); + if (!label_variant) + return {}; + + auto it = structor_variant_to_die.find(*label_variant); + + // Found the exact variant. + if (it != structor_variant_to_die.end()) + return it->getSecond(); + + // We need a C1 constructor. If debug-info only contains a DIE for C2, + // assume C1 was aliased to C2. + if (!label.lookup_name.starts_with("~") && label_variant == 1) { + if (auto it = structor_variant_to_die.find(2); + it != structor_variant_to_die.end()) + return it->getSecond(); + } + + return {}; } llvm::Expected<SymbolContext> @@ -2519,11 +2626,9 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { // Label was created using a declaration DIE. Need to fetch the definition // to resolve the function call. if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) { - auto definition = FindFunctionDefinition(label); - if (!definition) + die = FindFunctionDefinition(label, die); + if (!die.IsValid()) return llvm::createStringError("failed to find definition DIE"); - - die = std::move(definition); } SymbolContextList sc_list; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index d7db8a3c0869f..6ba2592314c36 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -378,7 +378,8 @@ class SymbolFileDWARF : public SymbolFileCommon { /// SymbolFile. /// /// \returns A valid definition DIE on success. - DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label); + DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label, + const DWARFDIE &declaration); protected: SymbolFileDWARF(const SymbolFileDWARF &) = delete; diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/Makefile b/lldb/test/API/lang/cpp/abi_tag_structors/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/abi_tag_structors/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py new file mode 100644 index 0000000000000..b549736c1405d --- /dev/null +++ b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py @@ -0,0 +1,30 @@ +""" +Test that we can call structors/destructors +annotated (and thus mangled) with ABI tags. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class AbiTagStructorsTestCase(TestBase): + def test_abi_tag_lookup(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec("main.cpp", False) + ) + + self.expect_expr("Tagged()", result_type="Tagged") + self.expect_expr("t1 = t2", result_type="Tagged") + + self.expect("expr Tagged t3(t1)", error=False) + self.expect("expr t1.~Tagged()", error=False) + + # Calls to deleting and base object destructor variants (D0 and D2 in Itanium ABI) + self.expect_expr( + "struct D : public HasVirtualDtor {}; D d; d.func()", + result_type="int", + result_value="10", + ) diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp b/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp new file mode 100644 index 0000000000000..b7783ccc49460 --- /dev/null +++ b/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp @@ -0,0 +1,38 @@ +#include <cstdio> + +struct Tagged { + [[gnu::abi_tag("Default")]] Tagged() { std::puts(__func__); } + [[gnu::abi_tag("Copy")]] Tagged(const Tagged &) { std::puts(__func__); } + [[gnu::abi_tag("CopyAssign")]] Tagged &operator=(const Tagged &) { + std::puts(__func__); + return *this; + } + [[gnu::abi_tag("Dtor")]] ~Tagged() { std::puts(__func__); } +}; + +struct Base { + virtual ~Base() { std::puts(__func__); } + virtual int func() { return 5; } +}; + +struct HasVirtualDtor : public Base { + int func() override { return 10; } + + [[gnu::abi_tag("VirtualDtor")]] ~HasVirtualDtor() override { + std::puts(__func__); + } +}; + +int main() { + Tagged t1; + Tagged t2(t1); + t1 = t2; + + Base b; + HasVirtualDtor vdtor; + vdtor.func(); + + std::puts("Break here"); + + return 0; +} diff --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp index 12f6dd515fd11..ceb567c28ab99 100644 --- a/lldb/unittests/Expression/ExpressionTest.cpp +++ b/lldb/unittests/Expression/ExpressionTest.cpp @@ -23,15 +23,15 @@ struct LabelTestCase { static LabelTestCase g_label_test_cases[] = { // Failure modes - {"bar:0x0:0x0:_Z3foov", + {"bar:blah:0x0:0x0:_Z3foov", {}, {"expected function call label prefix '$__lldb_func' but found 'bar' " "instead."}}, - {"$__lldb_func :0x0:0x0:_Z3foov", + {"$__lldb_func :blah:0x0:0x0:_Z3foov", {}, {"expected function call label prefix '$__lldb_func' but found " "'$__lldb_func ' instead."}}, - {"$__lldb_funcc:0x0:0x0:_Z3foov", + {"$__lldb_funcc:blah:0x0:0x0:_Z3foov", {}, {"expected function call label prefix '$__lldb_func' but found " "'$__lldb_funcc' instead."}}, @@ -39,47 +39,52 @@ static LabelTestCase g_label_test_cases[] = { {"foo", {}, {"malformed function call label."}}, {"$__lldb_func", {}, {"malformed function call label."}}, {"$__lldb_func:", {}, {"malformed function call label."}}, - {"$__lldb_func:0x0:0x0", {}, {"malformed function call label."}}, - {"$__lldb_func:abc:0x0:_Z3foov", + {"$__lldb_func:blah", {}, {"malformed function call label."}}, + {"$__lldb_func:blah:0x0", {}, {"malformed function call label."}}, + {"$__lldb_func:111:0x0:0x0", {}, {"malformed function call label."}}, + {"$__lldb_func:111:abc:0x0:_Z3foov", {}, {"failed to parse module ID from 'abc'."}}, - {"$__lldb_func:-1:0x0:_Z3foov", + {"$__lldb_func:111:-1:0x0:_Z3foov", {}, {"failed to parse module ID from '-1'."}}, - {"$__lldb_func:0x0invalid:0x0:_Z3foov", + {"$__lldb_func:111:0x0invalid:0x0:_Z3foov", {}, {"failed to parse module ID from '0x0invalid'."}}, - {"$__lldb_func:0x0 :0x0:_Z3foov", + {"$__lldb_func:111:0x0 :0x0:_Z3foov", {}, {"failed to parse module ID from '0x0 '."}}, - {"$__lldb_func:0x0:abc:_Z3foov", + {"$__lldb_func:blah:0x0:abc:_Z3foov", {}, {"failed to parse symbol ID from 'abc'."}}, - {"$__lldb_func:0x5:-1:_Z3foov", + {"$__lldb_func:blah:0x5:-1:_Z3foov", {}, {"failed to parse symbol ID from '-1'."}}, - {"$__lldb_func:0x5:0x0invalid:_Z3foov", + {"$__lldb_func:blah:0x5:0x0invalid:_Z3foov", {}, {"failed to parse symbol ID from '0x0invalid'."}}, - {"$__lldb_func:0x5:0x0 :_Z3foov", + {"$__lldb_func:blah:0x5:0x0 :_Z3foov", {}, {"failed to parse symbol ID from '0x0 '."}}, - {"$__lldb_func:0x0:0x0:_Z3foov", + {"$__lldb_func:blah:0x0:0x0:_Z3foov", { + /*.discriminator=*/"blah", /*.module_id=*/0x0, /*.symbol_id=*/0x0, /*.lookup_name=*/"_Z3foov", }, {}}, - {"$__lldb_func:0x0:0x0:abc:def:::a", + {"$__lldb_func::0x0:0x0:abc:def:::a", { + /*.discriminator=*/"", /*.module_id=*/0x0, /*.symbol_id=*/0x0, /*.lookup_name=*/"abc:def:::a", }, {}}, - {"$__lldb_func:0xd2:0xf0:$__lldb_func", + {"$__lldb_func:0x45:0xd2:0xf0:$__lldb_func", { + /*.discriminator=*/"0x45", /*.module_id=*/0xd2, /*.symbol_id=*/0xf0, /*.lookup_name=*/"$__lldb_func", @@ -106,6 +111,7 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) { EXPECT_EQ(decoded_or_err->toString(), encoded); EXPECT_EQ(label_str, encoded); + EXPECT_EQ(decoded_or_err->discriminator, label.discriminator); EXPECT_EQ(decoded_or_err->module_id, label.module_id); EXPECT_EQ(decoded_or_err->symbol_id, label.symbol_id); EXPECT_EQ(decoded_or_err->lookup_name, label.lookup_name); @@ -113,6 +119,7 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) { auto roundtrip_or_err = FunctionCallLabel::fromString(label_str); EXPECT_THAT_EXPECTED(roundtrip_or_err, llvm::Succeeded()); + EXPECT_EQ(roundtrip_or_err->discriminator, label.discriminator); EXPECT_EQ(roundtrip_or_err->module_id, label.module_id); EXPECT_EQ(roundtrip_or_err->symbol_id, label.symbol_id); EXPECT_EQ(roundtrip_or_err->lookup_name, label.lookup_name); diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp index b993b82612497..f673cceae00dd 100644 --- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp +++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp @@ -1150,12 +1150,12 @@ TEST_F(TestTypeSystemClang, AsmLabel_CtorDtor) { is_explicit, is_attr_used, is_artificial); auto *ctor = m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), "S", /*asm_label=*/"$__lldb_func:0x0:0x0:S", + t.GetOpaqueQualType(), "S", /*asm_label=*/"$__lldb_func::0x0:0x0:S", function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); auto *dtor = m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), "~S", /*asm_label=*/"$__lldb_func:0x0:0x0:~S", + t.GetOpaqueQualType(), "~S", /*asm_label=*/"$__lldb_func::0x0:0x0:~S", function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); @@ -1181,11 +1181,11 @@ TEST_F(TestTypeSystemClang, AsmLabel_CtorDtor) { EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape( m_ast->DeclGetMangledName(ctor).GetStringRef()) .data(), - "$__lldb_func:0x0:0x0:S"); + "$__lldb_func:C0:0x0:0x0:S"); EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape( m_ast->DeclGetMangledName(dtor).GetStringRef()) .data(), - "$__lldb_func:0x0:0x0:~S"); + "$__lldb_func:D1:0x0:0x0:~S"); } struct AsmLabelTestCase { @@ -1215,10 +1215,10 @@ class TestTypeSystemClangAsmLabel }; static AsmLabelTestCase g_asm_label_test_cases[] = { - {/*mangled=*/"$__lldb_func:0x0:0x0:_Z3foov", + {/*mangled=*/"$__lldb_func::0x0:0x0:_Z3foov", /*expected=*/"_Z3foov"}, - {/*mangled=*/"$__lldb_func:0x0:0x0:foo", - /*expected=*/"$__lldb_func:0x0:0x0:foo"}, + {/*mangled=*/"$__lldb_func::0x0:0x0:foo", + /*expected=*/"$__lldb_func::0x0:0x0:foo"}, {/*mangled=*/"foo", /*expected=*/"foo"}, {/*mangled=*/"_Z3foov", >From 032d97aca6c1a4dd8e75f17b51f4b70ed385130b Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 09:25:40 +0100 Subject: [PATCH 06/10] fixup! remove type-unit special-case --- .../Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index cc04f1d7879ed..324d1c2b068ec 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -288,15 +288,6 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) { if (!cu) return {}; - // FIXME: When resolving function call labels, we check that - // that the definition's DW_AT_specification points to the - // declaration that we encoded into the label here. But if the - // declaration came from a type-unit (and the definition from - // .debug_info), that check won't work. So for now, don't use - // function call labels for declaration DIEs from type-units. - if (cu->IsTypeUnit()) - return {}; - SymbolFileDWARF *dwarf = die.GetDWARF(); if (!dwarf) return {}; >From ed8b0004c0bf911cafcf2d1eb2264dc0f919b023 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 09:37:25 +0100 Subject: [PATCH 07/10] fixup! clean up error handling --- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 26 ++++++++++++++----- .../SymbolFile/DWARF/SymbolFileDWARF.h | 5 ++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 30426a037eca9..2a715674fee4a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2536,8 +2536,9 @@ GetItaniumCtorDtorVariant(llvm::StringRef discriminator) { return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind)); } -DWARFDIE SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, - const DWARFDIE &declaration) { +llvm::Expected<DWARFDIE> +SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, + const DWARFDIE &declaration) { DWARFDIE definition; llvm::DenseMap<int, DWARFDIE> structor_variant_to_die; @@ -2571,6 +2572,8 @@ DWARFDIE SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, if (!mangled) return IterationAction::Continue; + // FIXME: we should make DWARF encode the structor variant instead of + // needing to re-demangle. llvm::ItaniumPartialDemangler D; if (D.partialDemangle(mangled)) return IterationAction::Continue; @@ -2595,7 +2598,9 @@ DWARFDIE SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, auto label_variant = GetItaniumCtorDtorVariant(label.discriminator); if (!label_variant) - return {}; + return llvm::createStringError( + llvm::formatv("failed to retrieve structor variant from label: {0}", + label.discriminator)); auto it = structor_variant_to_die.find(*label_variant); @@ -2605,13 +2610,16 @@ DWARFDIE SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, // We need a C1 constructor. If debug-info only contains a DIE for C2, // assume C1 was aliased to C2. + // + // FIXME: can DWARF encode this information for us? if (!label.lookup_name.starts_with("~") && label_variant == 1) { if (auto it = structor_variant_to_die.find(2); it != structor_variant_to_die.end()) return it->getSecond(); } - return {}; + return llvm::createStringError(llvm::formatv( + "failed to find structor variant DIE for label: {0}", label)); } llvm::Expected<SymbolContext> @@ -2626,9 +2634,13 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { // Label was created using a declaration DIE. Need to fetch the definition // to resolve the function call. if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) { - die = FindFunctionDefinition(label, die); - if (!die.IsValid()) - return llvm::createStringError("failed to find definition DIE"); + auto die_or_err = FindFunctionDefinition(label, die); + if (!die_or_err) + return llvm::joinErrors( + llvm::createStringError("failed to find definition DIE"), + die_or_err.takeError()); + + die = std::move(*die_or_err); } SymbolContextList sc_list; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 6ba2592314c36..e29ed1fc73902 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -378,8 +378,9 @@ class SymbolFileDWARF : public SymbolFileCommon { /// SymbolFile. /// /// \returns A valid definition DIE on success. - DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label, - const DWARFDIE &declaration); + llvm::Expected<DWARFDIE> + FindFunctionDefinition(const FunctionCallLabel &label, + const DWARFDIE &declaration); protected: SymbolFileDWARF(const SymbolFileDWARF &) = delete; >From b2c2ecd5de24bd4eca38bb6e20a1d8919333abac Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 10:05:43 +0100 Subject: [PATCH 08/10] fixup! more adjustments to error messages --- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 2a715674fee4a..cbd086c95620a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2619,7 +2619,7 @@ SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, } return llvm::createStringError(llvm::formatv( - "failed to find structor variant DIE for label: {0}", label)); + "failed to find DIE for structor variant: {0}", label.discriminator)); } llvm::Expected<SymbolContext> @@ -2637,7 +2637,7 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { auto die_or_err = FindFunctionDefinition(label, die); if (!die_or_err) return llvm::joinErrors( - llvm::createStringError("failed to find definition DIE"), + llvm::createStringError("failed to find definition DIE:"), die_or_err.takeError()); die = std::move(*die_or_err); >From b2563b4b6318d5db770a861bcec9ba02606096e1 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 10:06:22 +0100 Subject: [PATCH 09/10] fixup! add test for ctor declaration separate from definition --- .../cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py index 70d7fd096c7cf..447cf7adfe14e 100644 --- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py @@ -31,10 +31,10 @@ def test(self): ) self.expect_expr("f.method()", result_value="-72", result_type="int") + self.expect_expr("Foo()", result_type="Foo") # FIXME: mangled name lookup for ABI-tagged ctors fails because # the debug-info AST doesn't have ABI-tag information. self.expect( "expr Bar()", error=True, substrs=["error: Couldn't look up symbols"] - ) >From 50d79ee94e15617090b7827ed727e29fee51c326 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Thu, 21 Aug 2025 09:42:22 +0100 Subject: [PATCH 10/10] fixup! Unified structor mangling changes --- lldb/include/lldb/Symbol/SymbolFile.h | 4 +- lldb/source/Expression/IRExecutionUnit.cpp | 2 +- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 34 +--- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 155 ++++++++---------- .../SymbolFile/DWARF/SymbolFileDWARF.h | 2 +- .../DWARF/SymbolFileDWARFDebugMap.cpp | 4 +- .../DWARF/SymbolFileDWARFDebugMap.h | 2 +- .../abi_tag_structors/TestAbiTagStructors.py | 2 +- .../cpp/constructors/TestCppConstructors.py | 20 +-- .../TestExprDefinitionInDylib.py | 8 +- .../lang/cpp/expr-definition-in-dylib/lib.cpp | 4 + .../lang/cpp/expr-definition-in-dylib/lib.h | 7 +- .../cpp/expr-definition-in-dylib/main.cpp | 3 +- 13 files changed, 105 insertions(+), 142 deletions(-) diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index bbc615d9fdc38..a84f081b340eb 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -332,12 +332,12 @@ class SymbolFile : public PluginInterface { /// Resolves the function corresponding to the specified LLDB function /// call \c label. /// - /// \param[in] label The FunctionCallLabel to be resolved. + /// \param[in,out] label The FunctionCallLabel to be resolved. /// /// \returns An llvm::Error if the specified \c label couldn't be resolved. /// Returns the resolved function (as a SymbolContext) otherwise. virtual llvm::Expected<SymbolContext> - ResolveFunctionCallLabel(const FunctionCallLabel &label) { + ResolveFunctionCallLabel(FunctionCallLabel &label) { return llvm::createStringError("Not implemented"); } diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index d557084acb745..d3dd163edd034 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -778,7 +778,7 @@ class LoadAddressResolver { /// Returns address of the function referred to by the special function call /// label \c label. static llvm::Expected<lldb::addr_t> -ResolveFunctionCallLabel(const FunctionCallLabel &label, +ResolveFunctionCallLabel(FunctionCallLabel &label, const lldb_private::SymbolContext &sc, bool &symbol_was_missing_weak) { symbol_was_missing_weak = false; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 324d1c2b068ec..bc13e7875c4a6 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -251,43 +251,11 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, return cv_quals; } -static const char *GetMangledOrStructorName(const DWARFDIE &die) { - const char *name = die.GetMangledName(/*substitute_name_allowed*/ false); - if (name) - return name; - - name = die.GetName(); - if (!name) - return nullptr; - - DWARFDIE parent = die.GetParent(); - if (!parent.IsStructUnionOrClass()) - return nullptr; - - const char *parent_name = parent.GetName(); - if (!parent_name) - return nullptr; - - // Constructor. - if (::strcmp(parent_name, name) == 0) - return name; - - // Destructor. - if (name[0] == '~' && ::strcmp(parent_name, name + 1) == 0) - return name; - - return nullptr; -} - static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) { - char const *name = GetMangledOrStructorName(die); + const char *name = die.GetMangledName(/*substitute_name_allowed*/ false); if (!name) return {}; - auto *cu = die.GetCU(); - if (!cu) - return {}; - SymbolFileDWARF *dwarf = die.GetDWARF(); if (!dwarf) return {}; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index cbd086c95620a..4b0e81bb9079f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/Threading.h" @@ -24,6 +25,7 @@ #include "lldb/Core/Progress.h" #include "lldb/Core/Section.h" #include "lldb/Core/Value.h" +#include "lldb/Expression/Expression.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/RegularExpression.h" @@ -2486,12 +2488,14 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, return false; } -static int ClangToItaniumCtorKind(clang::CXXCtorType kind) { +static llvm::StringRef ClangToItaniumCtorKind(clang::CXXCtorType kind) { switch (kind) { case clang::CXXCtorType::Ctor_Complete: - return 1; + return "C1"; case clang::CXXCtorType::Ctor_Base: - return 2; + return "C2"; + case clang::CXXCtorType::Ctor_Unified: + return "C4"; case clang::CXXCtorType::Ctor_CopyingClosure: case clang::CXXCtorType::Ctor_DefaultClosure: case clang::CXXCtorType::Ctor_Comdat: @@ -2499,39 +2503,41 @@ static int ClangToItaniumCtorKind(clang::CXXCtorType kind) { } } -static int ClangToItaniumDtorKind(clang::CXXDtorType kind) { +static llvm::StringRef ClangToItaniumDtorKind(clang::CXXDtorType kind) { switch (kind) { case clang::CXXDtorType::Dtor_Deleting: - return 0; + return "D0"; case clang::CXXDtorType::Dtor_Complete: - return 1; + return "D1"; case clang::CXXDtorType::Dtor_Base: - return 2; + return "D2"; + case clang::CXXDtorType::Dtor_Unified: + return "D4"; case clang::CXXDtorType::Dtor_Comdat: llvm_unreachable("Unexpected destructor kind."); } } -static std::optional<int> +static llvm::StringRef GetItaniumCtorDtorVariant(llvm::StringRef discriminator) { const bool is_ctor = discriminator.consume_front("C"); if (!is_ctor && !discriminator.consume_front("D")) - return std::nullopt; + return {}; uint64_t structor_kind; if (!llvm::to_integer(discriminator, structor_kind)) - return std::nullopt; + return {}; if (is_ctor) { - if (structor_kind > clang::CXXCtorType::Ctor_DefaultClosure) - return std::nullopt; + if (structor_kind > clang::CXXCtorType::Ctor_Unified) + return {}; return ClangToItaniumCtorKind( static_cast<clang::CXXCtorType>(structor_kind)); } - if (structor_kind > clang::CXXDtorType::Dtor_Comdat) - return std::nullopt; + if (structor_kind > clang::CXXDtorType::Dtor_Unified) + return {}; return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind)); } @@ -2539,93 +2545,74 @@ GetItaniumCtorDtorVariant(llvm::StringRef discriminator) { llvm::Expected<DWARFDIE> SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, const DWARFDIE &declaration) { - DWARFDIE definition; - llvm::DenseMap<int, DWARFDIE> structor_variant_to_die; - - // eFunctionNameTypeFull for mangled name lookup. - // eFunctionNameTypeMethod is required for structor lookups (since we look - // those up by DW_AT_name). - Module::LookupInfo info(ConstString(label.lookup_name), - lldb::eFunctionNameTypeFull | - lldb::eFunctionNameTypeMethod, - lldb::eLanguageTypeUnknown); - - m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { - if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) - return IterationAction::Continue; + auto do_lookup = [this](llvm::StringRef lookup_name) -> DWARFDIE { + DWARFDIE found; + Module::LookupInfo info(ConstString(lookup_name), + lldb::eFunctionNameTypeFull, + lldb::eLanguageTypeUnknown); - auto spec = entry.GetAttributeValueAsReferenceDIE(DW_AT_specification); - if (!spec) - return IterationAction::Continue; - - if (spec != declaration) - return IterationAction::Continue; + m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { + if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) + return IterationAction::Continue; - // We're not picking a specific structor variant DIE, so we're done here. - if (label.discriminator.empty()) { - definition = entry; + found = entry; return IterationAction::Stop; - } - - const char *mangled = - entry.GetMangledName(/*substitute_name_allowed=*/false); - if (!mangled) - return IterationAction::Continue; - - // FIXME: we should make DWARF encode the structor variant instead of - // needing to re-demangle. - llvm::ItaniumPartialDemangler D; - if (D.partialDemangle(mangled)) - return IterationAction::Continue; + }); - auto structor_variant = D.getCtorOrDtorVariant(); - if (!structor_variant) - return IterationAction::Continue; - - auto [_, inserted] = structor_variant_to_die.try_emplace(*structor_variant, - std::move(entry)); - assert(inserted); - - // The compiler may choose to alias the constructor variants - // (notably this happens on Linux), so we might not have a definition - // DIE for some structor variants. Hence we iterate over all variants - // and pick the most appropriate one out of those. - return IterationAction::Continue; - }); + return found; + }; + DWARFDIE definition = do_lookup(label.lookup_name); if (definition.IsValid()) return definition; - auto label_variant = GetItaniumCtorDtorVariant(label.discriminator); - if (!label_variant) + // This is not a structor lookup. Nothing else to be done here. + if (label.discriminator.empty()) return llvm::createStringError( - llvm::formatv("failed to retrieve structor variant from label: {0}", - label.discriminator)); - - auto it = structor_variant_to_die.find(*label_variant); - - // Found the exact variant. - if (it != structor_variant_to_die.end()) - return it->getSecond(); + "no definition DIE found in this SymbolFile"); - // We need a C1 constructor. If debug-info only contains a DIE for C2, - // assume C1 was aliased to C2. + // We're doing a structor lookup. Maybe we didn't find the structor variant + // because the complete object structor was aliased to the base object + // structor. Try finding the alias instead. // - // FIXME: can DWARF encode this information for us? - if (!label.lookup_name.starts_with("~") && label_variant == 1) { - if (auto it = structor_variant_to_die.find(2); - it != structor_variant_to_die.end()) - return it->getSecond(); - } + // TODO: there are other reasons for why a subprogram definition might be + // missing. Ideally DWARF would tell us more details about which structor + // variant a DIE corresponds to and whether it's an alias. + auto subst_or_err = + CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle( + label.lookup_name); + if (!subst_or_err) + return subst_or_err.takeError(); + + definition = do_lookup(*subst_or_err); + + if (!definition.IsValid()) + return llvm::createStringError( + "failed to find definition DIE for structor alias in fallback lookup"); - return llvm::createStringError(llvm::formatv( - "failed to find DIE for structor variant: {0}", label.discriminator)); + return definition; } llvm::Expected<SymbolContext> -SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { +SymbolFileDWARF::ResolveFunctionCallLabel(FunctionCallLabel &label) { std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (!label.discriminator.empty()) { + llvm::StringRef from = label.discriminator[0] == 'C' ? "C4" : "D4"; + + llvm::StringRef variant = GetItaniumCtorDtorVariant(label.discriminator); + if (variant.empty()) + return llvm::createStringError( + "failed to get Itanium variant for discriminator"); + + auto subst_or_err = CPlusPlusLanguage::SubstituteStructor_ItaniumMangle( + label.lookup_name, from, variant); + if (subst_or_err && *subst_or_err) + label.lookup_name = subst_or_err->GetStringRef(); + else + llvm::consumeError(subst_or_err.takeError()); + } + DWARFDIE die = GetDIE(label.symbol_id); if (!die.IsValid()) return llvm::createStringError( diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index e29ed1fc73902..608130cd068ce 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -447,7 +447,7 @@ class SymbolFileDWARF : public SymbolFileCommon { DIEArray &&variable_dies); llvm::Expected<SymbolContext> - ResolveFunctionCallLabel(const FunctionCallLabel &label) override; + ResolveFunctionCallLabel(FunctionCallLabel &label) override; // Given a die_offset, figure out the symbol context representing that die. bool ResolveFunction(const DWARFDIE &die, bool include_inlines, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 9d7452a1988fa..8b8229a7020c5 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -1603,8 +1603,8 @@ void SymbolFileDWARFDebugMap::GetCompileOptions( }); } -llvm::Expected<SymbolContext> SymbolFileDWARFDebugMap::ResolveFunctionCallLabel( - const FunctionCallLabel &label) { +llvm::Expected<SymbolContext> +SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(FunctionCallLabel &label) { const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id); SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); if (!oso_dwarf) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index e1f1df23951c6..bce1ed2671af0 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -145,7 +145,7 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon { GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override; llvm::Expected<SymbolContext> - ResolveFunctionCallLabel(const FunctionCallLabel &label) override; + ResolveFunctionCallLabel(FunctionCallLabel &label) override; protected: enum { kHaveInitializedOSOs = (1 << 0), kNumFlags }; diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py index b549736c1405d..5e7c03f8e0dfe 100644 --- a/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py +++ b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py @@ -10,7 +10,7 @@ class AbiTagStructorsTestCase(TestBase): - def test_abi_tag_lookup(self): + def test(self): self.build() lldbutil.run_to_source_breakpoint( self, "Break here", lldb.SBFileSpec("main.cpp", False) diff --git a/lldb/test/API/lang/cpp/constructors/TestCppConstructors.py b/lldb/test/API/lang/cpp/constructors/TestCppConstructors.py index baf06e4c59fbb..3d957fe3c0ba9 100644 --- a/lldb/test/API/lang/cpp/constructors/TestCppConstructors.py +++ b/lldb/test/API/lang/cpp/constructors/TestCppConstructors.py @@ -39,16 +39,16 @@ def test_constructors(self): ) # FIXME: Calling deleted constructors should fail before linking. - self.expect( - "expr ClassWithDeletedCtor(1).value", - error=True, - substrs=["Couldn't look up symbols:"], - ) - self.expect( - "expr ClassWithDeletedDefaultCtor().value", - error=True, - substrs=["Couldn't look up symbols:", "function", "optimized out"], - ) + #self.expect( + # "expr ClassWithDeletedCtor(1).value", + # error=True, + # substrs=["Couldn't look up symbols:"], + #) + #self.expect( + # "expr ClassWithDeletedDefaultCtor().value", + # error=True, + # substrs=["Couldn't look up symbols:", "function", "optimized out"], + #) @skipIfWindows # Can't find operator new. @skipIfLinux # Fails on some Linux systems with SIGABRT. diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py index 447cf7adfe14e..b6998764b6922 100644 --- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py @@ -5,7 +5,6 @@ class ExprDefinitionInDylibTestCase(TestBase): - NO_DEBUG_INFO_TESTCASE = True @skipIfWindows def test(self): @@ -34,7 +33,6 @@ def test(self): self.expect_expr("Foo()", result_type="Foo") - # FIXME: mangled name lookup for ABI-tagged ctors fails because - # the debug-info AST doesn't have ABI-tag information. - self.expect( - "expr Bar()", error=True, substrs=["error: Couldn't look up symbols"] + self.expect_expr("Base()", result_type="Base") + + self.expect_expr("Bar()", result_type="Bar") diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp index 1a08817f5cda1..1ddd28021dffc 100644 --- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp @@ -11,3 +11,7 @@ Foo::~Foo() { std::puts(__func__); } Bar::Bar() { std::puts(__func__); } Bar::~Bar() { std::puts(__func__); } + +Base::Base() { std::puts(__func__); } + +Base::~Base() { std::puts(__func__); } diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h index 5ec227946cba0..be2206103c17e 100644 --- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h @@ -7,7 +7,12 @@ struct Foo { ~Foo(); }; -struct Bar { +struct Base { + [[gnu::abi_tag("BaseCtor")]] Base(); + [[gnu::abi_tag("BaseDtor")]] ~Base(); +}; + +struct Bar : public Base { [[gnu::abi_tag("Ctor")]] Bar(); [[gnu::abi_tag("Dtor")]] ~Bar(); }; diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp index 4d6bece21ecac..b2e8700257716 100644 --- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp @@ -2,6 +2,7 @@ int main() { Foo f; - Bar b; + Base b1; + Bar b2; return f.method(); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits