[Lldb-commits] [lldb] 88d3c1d - [lldb][ClangExpression] Add asm() label to all FunctionDecls we create from DWARF

Michael Buch via lldb-commits lldb-commits at lists.llvm.org
Wed Aug 17 01:03:04 PDT 2022


Author: Michael Buch
Date: 2022-08-17T09:02:43+01:00
New Revision: 88d3c1db453189c3c76b5b83e4d47b2c7f4adf1f

URL: https://github.com/llvm/llvm-project/commit/88d3c1db453189c3c76b5b83e4d47b2c7f4adf1f
DIFF: https://github.com/llvm/llvm-project/commit/88d3c1db453189c3c76b5b83e4d47b2c7f4adf1f.diff

LOG: [lldb][ClangExpression] Add asm() label to all FunctionDecls we create from DWARF

When resolving symbols during IR execution, lldb makes a last effort attempt
to resolve external symbols from object files by approximate name matching.
It currently uses `CPlusPlusNameParser` to parse the demangled function name
and arguments for the unresolved symbol and its candidates. However, this
hand-rolled C++ parser doesn’t support ABI tags which, depending on the demangler,
get demangled into `[abi:tag]`. This lack of parsing support causes lldb to never
consider a candidate mangled function name that has ABI tags.

The issue reproduces by calling an ABI-tagged template function from the
expression evaluator. This is particularly problematic with the recent
addition of ABI tags to numerous libcxx APIs.

The issue stems from the fact that `clang::CodeGen` emits function
function calls using the mangled name inferred from the `FunctionDecl`
LLDB constructs from DWARF. Debug info often lacks information for
us to construct a perfect FunctionDecl resulting in subtle mangled
name inaccuracies.

This patch side-steps the problem of inaccurate `FunctionDecl`s by
attaching an `asm()` label to each `FunctionDecl` LLDB creates from DWARF.
`clang::CodeGen` consults this label to get the mangled name as one of
the first courses of action when emitting a function call.

LLDB already does this for C++ member functions as of
[675767a5910d2ec77ef8b51c78fe312cf9022896](https://reviews.llvm.org/D40283)

**Testing**

* Added API tests

Differential Revision: https://reviews.llvm.org/D131974

Added: 
    lldb/test/API/lang/cpp/abi_tag_lookup/Makefile
    lldb/test/API/lang/cpp/abi_tag_lookup/TestAbiTagLookup.py
    lldb/test/API/lang/cpp/abi_tag_lookup/main.cpp

Modified: 
    lldb/packages/Python/lldbsuite/test/make/Makefile.rules
    lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
index 52b800b1eb36d..b065a43cb1934 100644
--- a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
+++ b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
@@ -387,8 +387,7 @@ ifeq (1,$(USE_LIBSTDCPP))
 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/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 20eb49ddb45cc..fda68f289a397 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -1248,6 +1248,20 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
         lldbassert(function_decl);
 
         if (function_decl) {
+          // Attach an asm(<mangled_name>) label to the FunctionDecl.
+          // This ensures that clang::CodeGen emits function calls
+          // using symbols that are mangled according to the DW_AT_linkage_name.
+          // If we didn't do this, the external symbols wouldn't exactly
+          // match the mangled name LLDB knows about and the IRExecutionUnit
+          // would have to fall back to searching object files for
+          // approximately matching function names. The motivating
+          // example is generating calls to ABI-tagged template functions.
+          // This is done separately for member functions in
+          // AddMethodToCXXRecordType.
+          if (attrs.mangled_name && attrs.storage == clang::SC_Extern)
+            function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
+                m_ast.getASTContext(), attrs.mangled_name, /*literal=*/false));
+
           LinkDeclContextToDIE(function_decl, die);
 
           if (!function_param_decls.empty()) {

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
index 0000000000000..99998b20bcb05
--- /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
index 0000000000000..1a1b8692f430d
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_lookup/TestAbiTagLookup.py
@@ -0,0 +1,52 @@
+"""
+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")
+        self.expect_expr("withAbiTag(0, 0)", 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")
+
+        self.expect_expr("v1::withImplicitTag(Tagged{.mem = 6})", result_type="int", result_value="6")
+        self.expect_expr("withImplicitTag(Tagged{.mem = 6})", result_type="int", result_value="6")

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
index 0000000000000..4c1ff688df0cd
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_lookup/main.cpp
@@ -0,0 +1,71 @@
+#include <cstdio>
+
+namespace A {
+template <typename T> bool operator<(const T &, const T &) { return true; }
+
+template <typename T>
+[[gnu::abi_tag("Two", "Tags")]] bool operator>(const T &, const T &) {
+  return true;
+}
+
+template <typename T>
+[[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 <typename T>
+[[gnu::abi_tag("Bar")]] int withAbiTagInNS(const T &, const T &) {
+  return 2;
+}
+
+struct B {};
+} // namespace A
+
+template <typename T>
+[[gnu::abi_tag("Baz")]] int withAbiTag(const T &, const T &) {
+  return 3;
+}
+
+[[gnu::abi_tag("Baz")]] int withAbiTag(const int &, const int &) { return -3; }
+
+struct Simple {
+  int mem;
+};
+
+struct [[gnu::abi_tag("Qux")]] Tagged {
+  int mem;
+
+  int const &Value() const { return mem; }
+};
+
+template <typename T> 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 <typename T> 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<int> 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;
+}


        


More information about the lldb-commits mailing list