diff --git a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules --- a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules +++ b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules @@ -387,8 +387,7 @@ endif ifeq (1,$(USE_LIBCPP)) - # TODO: Teach LLDB to handle ABI tags in libc++ namespaces. - CXXFLAGS += -DLLDB_USING_LIBCPP -D_LIBCPP_NO_ABI_TAG + CXXFLAGS += -DLLDB_USING_LIBCPP ifeq "$(OS)" "Android" # Nothing to do, this is already handled in # Android.rules. diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -143,6 +143,15 @@ // PluginInterface protocol llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +private: + void CollectAlternateFunctionNamesMicrosoft(std::vector &results, + ConstString name, + const SymbolContext &sc) const; + + void CollectAlternateFunctionNamesItanium(std::vector &results, + ConstString name, + const SymbolContext &sc) const; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -18,6 +18,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/ItaniumDemangle.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" @@ -514,6 +516,22 @@ return alternates; } +char const *printNode(const llvm::itanium_demangle::Node *RootNode) { + llvm::itanium_demangle::OutputBuffer OB; + if (!initializeOutputBuffer(nullptr, nullptr, OB, 128)) + return nullptr; + RootNode->print(OB); + OB += '\0'; + return OB.getBuffer(); +} + +char const *getFunctionName(llvm::itanium_demangle::Node const *root) { + auto *Name = + static_cast(root) + ->getName(); + return printNode(Name); +} + static ConstString FindBestAlternateFunctionMangledName( const Mangled mangled, const SymbolContext &sym_ctx) { ConstString demangled = mangled.GetDemangledName(); @@ -564,6 +582,69 @@ return ConstString(); } +ConstString +FindBestAlternateFunctionMangledName(llvm::itanium_demangle::Node const *node, + const SymbolContext &sym_ctx) { + if (!sym_ctx.module_sp) + return ConstString(); + + lldb_private::SymbolFile *sym_file = sym_ctx.module_sp->GetSymbolFile(); + if (!sym_file) + return ConstString(); + + std::vector alternates; + sym_file->GetMangledNamesForFunction(getFunctionName(node), alternates); + + using namespace llvm::itanium_demangle; + assert(node != nullptr); + assert(node->getKind() == Node::KFunctionEncoding); + + auto const *encoding = static_cast(node); + Qualifiers quals = encoding->getCVQuals(); + FunctionRefQual refQual = encoding->getRefQual(); + NodeArray params = encoding->getParams(); + auto const *attrs = encoding->getAttrs(); + + std::vector param_matches; + for (size_t i = 0; i < alternates.size(); i++) { + ConstString alternate_mangled_name = alternates[i]; + char const *alternate = alternate_mangled_name.AsCString(); + + ManglingParser alternate_parser( + alternate, alternate + std::strlen(alternate)); + if (auto const *alt_node = alternate_parser.parse()) { + assert(alt_node->getKind() == Node::KFunctionEncoding); + auto const *alt_encoding = + static_cast(alt_node); + Qualifiers alt_quals = alt_encoding->getCVQuals(); + FunctionRefQual alt_refQual = alt_encoding->getRefQual(); + NodeArray alt_params = alt_encoding->getParams(); + auto const *alt_attrs = alt_encoding->getAttrs(); + + if (params == alt_params) { + if (quals == alt_quals && refQual == alt_refQual) { + if ((attrs && alt_attrs && attrs->equals(alt_attrs)) || + (!attrs && !alt_attrs)) { + // Perfect match. Return it. + return alternate_mangled_name; + } + } + + // Not a perfect match, but count matching parameters + // as an approximate match. + param_matches.push_back(alternate_mangled_name); + } + } + } + + // No perfect match. Return one of the approximate + // matches as a best match + if (param_matches.size()) + return param_matches[0]; + + // No matches. + return ConstString(); +} static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) @@ -1411,27 +1492,80 @@ return file_path.contains("/usr/include/c++/"); } +void CPlusPlusLanguage::CollectAlternateFunctionNamesMicrosoft( + std::vector &results, ConstString name, + const SymbolContext &sc) const { + Mangled mangled(name); + if (SymbolNameFitsToLanguage(mangled)) { + if (ConstString best_alternate = + FindBestAlternateFunctionMangledName(mangled, sc)) { + results.push_back(best_alternate); + } + } + + std::vector alternates = + GenerateAlternateFunctionManglings(name); + results.insert(results.end(), alternates.begin(), alternates.end()); + + // As a last-ditch fallback, try the base name for C++ names. It's + // terrible, but the DWARF doesn't always encode "extern C" correctly. + ConstString basename = GetDemangledFunctionNameWithoutArguments(mangled); + results.push_back(basename); +} + +void CPlusPlusLanguage::CollectAlternateFunctionNamesItanium( + std::vector &results, ConstString name, + const SymbolContext &sc) const { + if (!name) + return; + + char const *mangled = name.GetCString(); + using namespace llvm::itanium_demangle; + ManglingParser parser(mangled, mangled + std::strlen(mangled)); + auto const *node = parser.parse(); + + if (node && SymbolNameFitsToLanguage(mangled)) { + if (ConstString best_alternate = + FindBestAlternateFunctionMangledName(node, sc)) { + results.push_back(best_alternate); + } + } + + std::vector alternates = + GenerateAlternateFunctionManglings(name); + results.insert(results.end(), alternates.begin(), alternates.end()); + + // As a last-ditch fallback, try the base name for C++ names. It's + // terrible, but the DWARF doesn't always encode "extern C" correctly. + if (node) { + ConstString basename(getFunctionName(node)); + results.push_back(basename); + } +} + void CPlusPlusLanguage::CollectAlternateFunctionNames( std::vector &results, const std::vector &mangled_names, const SymbolContext &sc) const { for (const ConstString &name : mangled_names) { - Mangled mangled(name); - if (SymbolNameFitsToLanguage(mangled)) { - if (ConstString best_alternate = - FindBestAlternateFunctionMangledName(mangled, sc)) { - results.push_back(best_alternate); + auto scheme = Mangled::GetManglingScheme(name.GetStringRef()); + switch (scheme) { + case Mangled::ManglingScheme::eManglingSchemeMSVC: + CollectAlternateFunctionNamesMicrosoft(results, name, sc); + break; + case Mangled::ManglingScheme::eManglingSchemeItanium: + CollectAlternateFunctionNamesItanium(results, name, sc); + break; + case Mangled::ManglingScheme::eManglingSchemeNone: + if (name.GetStringRef().startswith("__Z")) { + CollectAlternateFunctionNamesItanium(results, name, sc); + break; } + LLVM_FALLTHROUGH; + case Mangled::ManglingScheme::eManglingSchemeRustV0: + case Mangled::ManglingScheme::eManglingSchemeD: + llvm_unreachable( + "Trying to handle non-C++ mangling scheme from a C++ plugin"); } - - std::vector alternates = - GenerateAlternateFunctionManglings(name); - results.insert(results.end(), alternates.begin(), alternates.end()); - - // As a last-ditch fallback, try the base name for C++ names. It's - // terrible, but the DWARF doesn't always encode "extern C" correctly. - ConstString basename = - GetDemangledFunctionNameWithoutArguments(mangled); - results.push_back(basename); } } diff --git a/lldb/test/API/lang/cpp/abi_tag_lookup/Makefile b/lldb/test/API/lang/cpp/abi_tag_lookup/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/abi_tag_lookup/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/abi_tag_lookup/TestAbiTagLookup.py b/lldb/test/API/lang/cpp/abi_tag_lookup/TestAbiTagLookup.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/abi_tag_lookup/TestAbiTagLookup.py @@ -0,0 +1,55 @@ +""" +Test that we can call functions and use types +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 AbiTagLookupTestCase(TestBase): + + @skipIfWindows + @expectedFailureAll(debug_info=["dwarf", "gmodules", "dwo"]) + def test_abi_tag_lookup(self): + self.build() + lldbutil.run_to_source_breakpoint(self, 'Break here', + lldb.SBFileSpec("main.cpp", False)) + + # Qualified/unqualified lookup to templates in namespace + self.expect_expr("operator<(b1, b2)", result_type="bool", result_value="true") + self.expect_expr("A::operator<(b1, b2)", result_type="bool", result_value="true") + self.expect_expr("b1 < b2", result_type="bool", result_value="true") + + # Qualified/unqualified lookup to templates with ABI tags in namespace + self.expect_expr("operator>(b1, b2)", result_type="bool", result_value="true") + self.expect_expr("A::operator>(b1, b2)", result_type="bool", result_value="true") + self.expect_expr("b1 > b2", result_type="bool", result_value="true") + + # Call non-operator templates with ABI tags + self.expect_expr("A::withAbiTagInNS(1, 1)", result_type="int", result_value="1") + + self.expect_expr("A::withAbiTagInNS(1.0, 1.0)", result_type="int", result_value="2") + self.expect_expr("withAbiTagInNS(b1, b2)", result_type="int", result_value="2") + self.expect_expr("A::withAbiTagInNS(b1, b2)", result_type="int", result_value="2") + + self.expect_expr("withAbiTag(b1, b2)", result_type="int", result_value="3") + + # Structures with ABI tags + self.expect_expr("t.Value()", result_type="const int", result_value="4") + self.expect_expr("tt.Value()", result_type="const int", result_value="5") + + self.expect_expr("Tagged{.mem = 6}", result_type="Tagged", + result_children=[ValueCheck(name="mem", value="6")]) + + # Inline namespaces with ABI tags + self.expect_expr("v1::withImplicitTag(Simple{.mem = 6})", result_type="int", result_value="6") + self.expect_expr("withImplicitTag(Simple{.mem = 6})", result_type="int", result_value="6") + + # FIXME: this currently fails because CPlusPlusLanguage::FindBestAlternateFunctionMangledName + # requires at least the parameters of the symbols it compares to match. However, + # the IR we generate doesn't include ABI tags on the parameter. To fix this we need + # to add ABI tag attributes to the decls that DWARFASTParserClang generates. + self.expect("expr v1::withImplicitTag(Tagged{.mem = 6})", error=True, substrs=["error: Couldn't lookup symbols:"]) + self.expect("expr withImplicitTag(Tagged{.mem = 6})", error=True, substrs=["error: Couldn't lookup symbols:"]) diff --git a/lldb/test/API/lang/cpp/abi_tag_lookup/main.cpp b/lldb/test/API/lang/cpp/abi_tag_lookup/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/abi_tag_lookup/main.cpp @@ -0,0 +1,69 @@ +#include + +namespace A { +template bool operator<(const T &, const T &) { return true; } + +template +[[gnu::abi_tag("Two", "Tags")]] bool operator>(const T &, const T &) { + return true; +} + +template +[[gnu::abi_tag("OneTag")]] bool operator==(const T &, const T &) { + return true; +} + +[[gnu::abi_tag("Foo")]] int withAbiTagInNS(const int &, const int &) { + return 1; +} + +template +[[gnu::abi_tag("Bar")]] int withAbiTagInNS(const T &, const T &) { + return 2; +} + +struct B {}; +} // namespace A + +template +[[gnu::abi_tag("Baz")]] int withAbiTag(const T &, const T &) { + return 3; +} + +struct Simple { + int mem; +}; + +struct [[gnu::abi_tag("Qux")]] Tagged { + int mem; + + int const &Value() const { return mem; } +}; + +template struct [[gnu::abi_tag("Quux", "Quuux")]] TaggedTemplate { + T mem; + + T const &Value() const { return mem; } +}; + +// clang-format off +inline namespace [[gnu::abi_tag("Inline", "NS")]] v1 { +template int withImplicitTag(T const &t) { return t.mem; } +} // namespace +// clang-format on + +int main() { + A::B b1; + A::B b2; + Tagged t{.mem = 4}; + TaggedTemplate tt{.mem = 5}; + + int result = (b1 < b2) + (b1 > b2) + (b1 == b2) + withAbiTag(b1, b2) + + A::withAbiTagInNS(1.0, 2.0) + withAbiTagInNS(b1, b2) + + A::withAbiTagInNS(1, 2) + withImplicitTag(Tagged{.mem = 6}) + + withImplicitTag(Simple{.mem = 6}) + t.Value() + tt.Value(); + + std::puts("Break here"); + + return result; +}