https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/149827
>From c094d29dd10161d62131a1de15562ce6024d5e2d Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 4 Aug 2025 14:20:45 +0100 Subject: [PATCH 1/8] [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 180c030d43c522a1e7763e688a2ced7f4b149fb7 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 4 Aug 2025 07:49:40 +0100 Subject: [PATCH 2/8] [ItaniumDemangle] Add getter for constructor/destructor variants This patch adds a way to retrieve the constructor/destructor variant from the Itanium demangle tree. This will be used by LLDB. --- libcxxabi/src/demangle/ItaniumDemangle.h | 2 ++ llvm/include/llvm/Demangle/Demangle.h | 3 +++ llvm/include/llvm/Demangle/ItaniumDemangle.h | 2 ++ llvm/lib/Demangle/ItaniumDemangle.cpp | 10 +++++++--- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h index 6f27da7b9cadf..7b3983bc89367 100644 --- a/libcxxabi/src/demangle/ItaniumDemangle.h +++ b/libcxxabi/src/demangle/ItaniumDemangle.h @@ -1766,6 +1766,8 @@ class CtorDtorName final : public Node { template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); } + int getVariant() const { return Variant; } + void printLeft(OutputBuffer &OB) const override { if (IsDtor) OB += "~"; diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h index d9b08b2d856dc..6af8fad9ceb86 100644 --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -127,6 +127,9 @@ struct ItaniumPartialDemangler { /// If this symbol describes a constructor or destructor. DEMANGLE_ABI bool isCtorOrDtor() const; + /// If this symbol describes a constructor or destructor. + std::optional<int> getCtorOrDtorVariant() const; + /// If this symbol describes a function. DEMANGLE_ABI bool isFunction() const; diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h index 62d427c3966bb..c0db02f8e7fef 100644 --- a/llvm/include/llvm/Demangle/ItaniumDemangle.h +++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -1766,6 +1766,8 @@ class CtorDtorName final : public Node { template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); } + int getVariant() const { return Variant; } + void printLeft(OutputBuffer &OB) const override { if (IsDtor) OB += "~"; diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp index 1009cc91ca12a..a5d7a5576fccf 100644 --- a/llvm/lib/Demangle/ItaniumDemangle.cpp +++ b/llvm/lib/Demangle/ItaniumDemangle.cpp @@ -560,13 +560,17 @@ bool ItaniumPartialDemangler::hasFunctionQualifiers() const { } bool ItaniumPartialDemangler::isCtorOrDtor() const { + return getCtorOrDtorVariant().has_value(); +} + +std::optional<int> ItaniumPartialDemangler::getCtorOrDtorVariant() const { const Node *N = static_cast<const Node *>(RootNode); while (N) { switch (N->getKind()) { default: - return false; + return std::nullopt; case Node::KCtorDtorName: - return true; + return static_cast<const CtorDtorName *>(N)->getVariant(); case Node::KAbiTagAttr: N = static_cast<const AbiTagAttr *>(N)->Base; @@ -588,7 +592,7 @@ bool ItaniumPartialDemangler::isCtorOrDtor() const { break; } } - return false; + return std::nullopt; } bool ItaniumPartialDemangler::isFunction() const { >From 07ca5c9690361f87583c028315a3499c1d2f079b Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 4 Aug 2025 14:21:16 +0100 Subject: [PATCH 3/8] [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 5fa2d53eeea2485c65215d5c5bbe0ccfc74f06f3 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 09:25:40 +0100 Subject: [PATCH 4/8] 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 afe5c2af5bb2b451ceefcaedf24422076bb7593c Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 09:37:25 +0100 Subject: [PATCH 5/8] 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 f8b885c47700e60c03c036ee02d708159eb83627 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 10:05:43 +0100 Subject: [PATCH 6/8] 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 41f5252ffa64f6b1bc724d9c757561dd14436b6b Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 10:06:22 +0100 Subject: [PATCH 7/8] 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 e182d96505e7b5a6d5e4a86a539cd9f9dbf5d0b8 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Fri, 8 Aug 2025 15:12:20 +0100 Subject: [PATCH 8/8] [DO-NOT-MERGE] Add unified linkage to structor declarations --- clang/include/clang/Basic/ABI.h | 10 ++++--- clang/lib/AST/ItaniumMangle.cpp | 6 ++++ clang/lib/AST/MicrosoftMangle.cpp | 2 ++ clang/lib/CodeGen/CGClass.cpp | 2 ++ clang/lib/CodeGen/CGDebugInfo.cpp | 22 +++++++++----- clang/lib/CodeGen/CodeGenModule.cpp | 4 ++- clang/lib/CodeGen/ItaniumCXXABI.cpp | 6 ++++ .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 30 +------------------ .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 17 +++++------ .../abi_tag_structors/TestAbiTagStructors.py | 2 +- .../TestExprDefinitionInDylib.py | 5 +--- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 8 ++--- 12 files changed, 54 insertions(+), 60 deletions(-) diff --git a/clang/include/clang/Basic/ABI.h b/clang/include/clang/Basic/ABI.h index 231bad799a42c..598d558a563ec 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 ctor. }; /// C++ destructor types. enum CXXDtorType { - Dtor_Deleting, ///< Deleting dtor - Dtor_Complete, ///< Complete object dtor - Dtor_Base, ///< Base object dtor - Dtor_Comdat ///< The COMDAT used for dtors + Dtor_Deleting, ///< Deleting dtor + Dtor_Complete, ///< Complete object dtor + Dtor_Base, ///< Base object dtor + Dtor_Comdat, ///< The COMDAT used for dtors + Dtor_Unified, ///< GCC-style unified dtor. }; } // end namespace clang diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 5233648d8f9c8..00a7f54cbe233 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -6056,6 +6056,9 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T, case Ctor_Base: Out << '2'; break; + case Ctor_Unified: + Out << '4'; + break; case Ctor_Comdat: Out << '5'; break; @@ -6083,6 +6086,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 e6ea0ada2e9ec..c784bf5a00c12 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -1494,6 +1494,8 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) { // <operator-name> ::= ?_E # vector deleting destructor // FIXME: Add a vector deleting dtor type. It goes in the vtable, so we need // it. + case Dtor_Unified: + llvm_unreachable("not expecting a unified dtor"); case Dtor_Comdat: llvm_unreachable("not expecting a COMDAT"); } diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 4a465e6526da0..ccd914007fdf0 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -1493,6 +1493,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 2b469f2e265b4..722bb17edbc0d 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -29,6 +29,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/VTableBuilder.h" +#include "clang/Basic/ABI.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" @@ -2177,22 +2178,29 @@ static bool isFunctionLocalClass(const CXXRecordDecl *RD) { llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction( const CXXMethodDecl *Method, llvm::DIFile *Unit, llvm::DIType *RecordTy) { - bool IsCtorOrDtor = - isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(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. + const bool ShouldAddLinkageName = + (!isa<CXXConstructorDecl>(Method) && !isa<CXXDestructorDecl>(Method)) || + CGM.getTarget().getCXXABI().isItaniumFamily(); + 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 (ShouldAddLinkageName && !isFunctionLocalClass(Method->getParent())) { + if (auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) + MethodLinkageName = + CGM.getMangledName(GlobalDecl(Ctor, CXXCtorType::Ctor_Unified)); + else if (auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) + MethodLinkageName = + CGM.getMangledName(GlobalDecl(Dtor, CXXDtorType::Dtor_Unified)); + else + MethodLinkageName = CGM.getMangledName(Method); + } // Get the location for the method. llvm::DIFile *MethodDefUnit = nullptr; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 834b1c067d84c..a9c65eb7ad2b7 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -37,6 +37,7 @@ #include "clang/AST/Mangle.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Basic/ABI.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Diagnostic.h" @@ -2150,7 +2151,8 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) { if (const auto *CD = dyn_cast<CXXConstructorDecl>(CanonicalGD.getDecl())) { if (!getTarget().getCXXABI().hasConstructorVariants()) { CXXCtorType OrigCtorType = GD.getCtorType(); - assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete); + assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete || + OrigCtorType == Ctor_Unified); if (OrigCtorType == Ctor_Base) CanonicalGD = GlobalDecl(CD, Ctor_Complete); } diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 5ffc1edb9986f..d933a45ca43c0 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -89,6 +89,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { case Dtor_Base: return false; + case Dtor_Unified: + llvm_unreachable("unexpected unified dtor"); + case Dtor_Comdat: llvm_unreachable("emitting dtor comdat as function?"); } @@ -102,6 +105,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { case Ctor_Base: return false; + case Ctor_Unified: + llvm_unreachable("unexpected unified ctor"); + case Ctor_CopyingClosure: case Ctor_DefaultClosure: llvm_unreachable("closure ctors in Itanium ABI?"); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 324d1c2b068ec..c795912b7bd7a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -251,36 +251,8 @@ 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 {}; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index cbd086c95620a..b4ec9d14b2afb 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2492,6 +2492,8 @@ static int ClangToItaniumCtorKind(clang::CXXCtorType kind) { return 1; case clang::CXXCtorType::Ctor_Base: return 2; + case clang::CXXCtorType::Ctor_Unified: + return 4; case clang::CXXCtorType::Ctor_CopyingClosure: case clang::CXXCtorType::Ctor_DefaultClosure: case clang::CXXCtorType::Ctor_Comdat: @@ -2507,6 +2509,8 @@ static int ClangToItaniumDtorKind(clang::CXXDtorType kind) { return 1; case clang::CXXDtorType::Dtor_Base: return 2; + case clang::CXXDtorType::Dtor_Unified: + return 4; case clang::CXXDtorType::Dtor_Comdat: llvm_unreachable("Unexpected destructor kind."); } @@ -2523,14 +2527,14 @@ GetItaniumCtorDtorVariant(llvm::StringRef discriminator) { return std::nullopt; if (is_ctor) { - if (structor_kind > clang::CXXCtorType::Ctor_DefaultClosure) + if (structor_kind > clang::CXXCtorType::Ctor_Unified) return std::nullopt; return ClangToItaniumCtorKind( static_cast<clang::CXXCtorType>(structor_kind)); } - if (structor_kind > clang::CXXDtorType::Dtor_Comdat) + if (structor_kind > clang::CXXDtorType::Dtor_Unified) return std::nullopt; return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind)); @@ -2542,12 +2546,8 @@ SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, 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::eFunctionNameTypeFull, lldb::eLanguageTypeUnknown); m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { @@ -2558,9 +2558,6 @@ SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, 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; 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/expr-definition-in-dylib/TestExprDefinitionInDylib.py b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py index 447cf7adfe14e..f5e181771a36e 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 @@ -34,7 +34,4 @@ 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("Bar()", result_type="Bar") diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index b03fac2d22a52..f0ff5f76275a6 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -1403,10 +1403,10 @@ 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() && + // 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. (DD->useAllLinkageNames() || DU->getAbstractScopeDIEs().lookup(SP))) addLinkageName(SPDie, LinkageName); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits