[Lldb-commits] [lldb] 3256aa8 - [lldb] Add support for DW_AT_calling_convention to the DWARF parser

Raphael Isemann via lldb-commits lldb-commits at lists.llvm.org
Mon Oct 11 04:44:30 PDT 2021


Author: Raphael Isemann
Date: 2021-10-11T13:44:10+02:00
New Revision: 3256aa8fe6fd785df12cc39f95e55bef0bc371c2

URL: https://github.com/llvm/llvm-project/commit/3256aa8fe6fd785df12cc39f95e55bef0bc371c2
DIFF: https://github.com/llvm/llvm-project/commit/3256aa8fe6fd785df12cc39f95e55bef0bc371c2.diff

LOG: [lldb] Add support for DW_AT_calling_convention to the DWARF parser

This adds support for parsing DW_AT_calling_convention in the DWARF parser.

The generic DWARF parsing code already support extracting this attribute from A
DIE and TypeSystemClang already offers a parameter to add a calling convention
to a function type (as the PDB parser supports calling convention parsing), so
this patch just converts the DWARF enum value to the Clang enum value and adds a
few tests.

There are two tests in this patch.:

* A unit test for the added DWARF parsing code that should run on all platforms.

* An API tests that covers the whole expression evaluation machinery by trying
to call functions with non-standard calling conventions. The specific subtests
are target specific as some calling conventions only work on e.g. win32 (or, if
they work on other platforms they only really have observable differences on a
specific target).  The tests are also highly compiler-specific, so if GCC or
Clang tell us that they don't support a specific calling convention then we just
skip the test.

Note that some calling conventions are supported by Clang but aren't implemented
in LLVM (e.g. `pascal`), so there we just test that if this ever gets
implemented in LLVM that LLDB works too. There are also some more tricky/obscure
conventions that are left out such as the different swift* conventions, some
planned Obj-C conventions (`Preserve*`), AAPCS* conventions (as the DWARF->Clang
conversion is ambiguous for AAPCS and APPCS-VFP) and conventions only used for
OpenCL etc.

Reviewed By: aprantl

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

Added: 
    lldb/test/API/lang/c/calling-conventions/Makefile
    lldb/test/API/lang/c/calling-conventions/TestCCallingConventions.py
    lldb/test/API/lang/c/calling-conventions/fastcall.c
    lldb/test/API/lang/c/calling-conventions/ms_abi.c
    lldb/test/API/lang/c/calling-conventions/pascal.c
    lldb/test/API/lang/c/calling-conventions/regcall.c
    lldb/test/API/lang/c/calling-conventions/stdcall.c
    lldb/test/API/lang/c/calling-conventions/sysv_abi.c
    lldb/test/API/lang/c/calling-conventions/vectorcall.c

Modified: 
    lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
    lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 1aa2c4f3b1ea4..c29b42dec08b2 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -876,6 +876,37 @@ TypeSP DWARFASTParserClang::ParseEnum(const SymbolContext &sc,
   return type_sp;
 }
 
+static clang::CallingConv
+ConvertDWARFCallingConventionToClang(const ParsedDWARFTypeAttributes &attrs) {
+  switch (attrs.calling_convention) {
+  case llvm::dwarf::DW_CC_normal:
+    return clang::CC_C;
+  case llvm::dwarf::DW_CC_BORLAND_stdcall:
+    return clang::CC_X86StdCall;
+  case llvm::dwarf::DW_CC_BORLAND_msfastcall:
+    return clang::CC_X86FastCall;
+  case llvm::dwarf::DW_CC_LLVM_vectorcall:
+    return clang::CC_X86VectorCall;
+  case llvm::dwarf::DW_CC_BORLAND_pascal:
+    return clang::CC_X86Pascal;
+  case llvm::dwarf::DW_CC_LLVM_Win64:
+    return clang::CC_Win64;
+  case llvm::dwarf::DW_CC_LLVM_X86_64SysV:
+    return clang::CC_X86_64SysV;
+  case llvm::dwarf::DW_CC_LLVM_X86RegCall:
+    return clang::CC_X86RegCall;
+  default:
+    break;
+  }
+
+  Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
+                                        DWARF_LOG_LOOKUPS));
+  LLDB_LOG(log, "Unsupported DW_AT_calling_convention value: {0}",
+           attrs.calling_convention);
+  // Use the default calling convention as a fallback.
+  return clang::CC_C;
+}
+
 TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
                            ParsedDWARFTypeAttributes &attrs) {
   Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
@@ -954,11 +985,14 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
     is_cxx_method = false;
   }
 
+  clang::CallingConv calling_convention =
+      ConvertDWARFCallingConventionToClang(attrs);
+
   // clang_type will get the function prototype clang type after this
   // call
   CompilerType clang_type = m_ast.CreateFunctionType(
       return_clang_type, function_param_types.data(),
-      function_param_types.size(), is_variadic, type_quals);
+      function_param_types.size(), is_variadic, type_quals, calling_convention);
 
   if (attrs.name) {
     bool type_handled = false;

diff  --git a/lldb/test/API/lang/c/calling-conventions/Makefile b/lldb/test/API/lang/c/calling-conventions/Makefile
new file mode 100644
index 0000000000000..22f1051530f87
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/Makefile
@@ -0,0 +1 @@
+include Makefile.rules

diff  --git a/lldb/test/API/lang/c/calling-conventions/TestCCallingConventions.py b/lldb/test/API/lang/c/calling-conventions/TestCCallingConventions.py
new file mode 100644
index 0000000000000..5156190bc071a
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/TestCCallingConventions.py
@@ -0,0 +1,78 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test_event.build_exception import BuildError
+
+class TestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def build_and_run(self, test_file):
+        """
+        Tries building the given test source and runs to the first breakpoint.
+        Returns false if the file fails to build due to an unsupported calling
+        convention on the current test target. Returns true if building and
+        running to the breakpoint succeeded.
+        """
+        try:
+            self.build(dictionary={
+                "C_SOURCES" : test_file,
+                "CFLAGS_EXTRAS" : "-Werror"
+            })
+        except BuildError as e:
+             # Test source failed to build. Check if it failed because the
+             # calling convention argument is unsupported/unknown in which case
+             # the test should be skipped.
+             error_msg = str(e)
+             # Clang gives an explicit error when a calling convention is
+             # not supported.
+             if "calling convention is not supported for this target" in error_msg:
+               return False
+             # GCC's has two 
diff erent generic warnings it can emit.
+             if "attribute ignored" in error_msg:
+               return False
+             if "attribute directive ignored " in error_msg:
+               return False
+             # We got a 
diff erent build error, so raise it again to fail the
+             # test.
+             raise
+        lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec(test_file))
+        return True
+
+    def test_regcall(self):
+        if not self.build_and_run("regcall.c"):
+            return
+        self.expect_expr("func(1, 2, 3, 4)", result_type="int", result_value="10")
+
+    def test_ms_abi(self):
+        if not self.build_and_run("ms_abi.c"):
+            return
+        self.expect_expr("func(1, 2, 3, 4)", result_type="int", result_value="10")
+
+    def test_stdcall(self):
+        if not self.build_and_run("stdcall.c"):
+            return
+        self.expect_expr("func(1, 2, 3, 4)", result_type="int", result_value="10")
+
+    def test_vectorcall(self):
+        if not self.build_and_run("vectorcall.c"):
+            return
+        self.expect_expr("func(1.0)", result_type="int", result_value="1")
+
+    def test_fastcall(self):
+        if not self.build_and_run("fastcall.c"):
+            return
+        self.expect_expr("func(1, 2, 3, 4)", result_type="int", result_value="10")
+
+    def test_pascal(self):
+        if not self.build_and_run("pascal.c"):
+            return
+        self.expect_expr("func(1, 2, 3, 4)", result_type="int", result_value="10")
+
+    def test_sysv_abi(self):
+        if not self.build_and_run("sysv_abi.c"):
+            return
+        self.expect_expr("func(1, 2, 3, 4)", result_type="int", result_value="10")

diff  --git a/lldb/test/API/lang/c/calling-conventions/fastcall.c b/lldb/test/API/lang/c/calling-conventions/fastcall.c
new file mode 100644
index 0000000000000..2a6937d7a9cc4
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/fastcall.c
@@ -0,0 +1,7 @@
+int __attribute__((fastcall)) func(int a, int b, int c, int d) {
+  return a + b + c + d;
+}
+
+int main() {
+  return func(1, 2, 3, 4); // break here
+}

diff  --git a/lldb/test/API/lang/c/calling-conventions/ms_abi.c b/lldb/test/API/lang/c/calling-conventions/ms_abi.c
new file mode 100644
index 0000000000000..6b40cbc5d3fac
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/ms_abi.c
@@ -0,0 +1,7 @@
+int __attribute__((ms_abi)) func(int a, int b, int c, int d) {
+  return a + b + c + d;
+}
+
+int main() {
+  return func(1, 2, 3, 4); // break here
+}

diff  --git a/lldb/test/API/lang/c/calling-conventions/pascal.c b/lldb/test/API/lang/c/calling-conventions/pascal.c
new file mode 100644
index 0000000000000..ed0e3b3735fac
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/pascal.c
@@ -0,0 +1,7 @@
+int __attribute__((pascal)) func(int a, int b, int c, int d) {
+  return a + b + c + d;
+}
+
+int main() {
+  return func(1, 2, 3, 4); // break here
+}

diff  --git a/lldb/test/API/lang/c/calling-conventions/regcall.c b/lldb/test/API/lang/c/calling-conventions/regcall.c
new file mode 100644
index 0000000000000..6a683a9b060a0
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/regcall.c
@@ -0,0 +1,7 @@
+int __attribute__((regcall)) func(int a, int b, int c, int d) {
+  return a + b + c + d;
+}
+
+int main() {
+  return func(1, 2, 3, 4); // break here
+}

diff  --git a/lldb/test/API/lang/c/calling-conventions/stdcall.c b/lldb/test/API/lang/c/calling-conventions/stdcall.c
new file mode 100644
index 0000000000000..82df064b1bd8e
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/stdcall.c
@@ -0,0 +1,7 @@
+int __attribute__((stdcall)) func(int a, int b, int c, int d) {
+  return a + b + c + d;
+}
+
+int main() {
+  return func(1, 2, 3, 4); // break here
+}

diff  --git a/lldb/test/API/lang/c/calling-conventions/sysv_abi.c b/lldb/test/API/lang/c/calling-conventions/sysv_abi.c
new file mode 100644
index 0000000000000..309b2624d0b6e
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/sysv_abi.c
@@ -0,0 +1,7 @@
+int __attribute__((sysv_abi)) func(int a, int b, int c, int d) {
+  return a + b + c + d;
+}
+
+int main() {
+  return func(1, 2, 3, 4); // break here
+}

diff  --git a/lldb/test/API/lang/c/calling-conventions/vectorcall.c b/lldb/test/API/lang/c/calling-conventions/vectorcall.c
new file mode 100644
index 0000000000000..5e0e1e599b6aa
--- /dev/null
+++ b/lldb/test/API/lang/c/calling-conventions/vectorcall.c
@@ -0,0 +1,7 @@
+int __attribute__((vectorcall)) func(double a) {
+  return (int)a;
+}
+
+int main() {
+  return func(1.0); // break here
+}

diff  --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
index 435da1f9643bd..a44c88a3d3b13 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
@@ -116,3 +116,159 @@ TEST_F(DWARFASTParserClangTests,
               testing::UnorderedElementsAre(decl_ctxs[0], decl_ctxs[3]));
 }
 
+TEST_F(DWARFASTParserClangTests, TestCallingConventionParsing) {
+  // Tests parsing DW_AT_calling_convention values.
+
+  // The DWARF below just declares a list of function types with
+  // DW_AT_calling_convention on them.
+  const char *yamldata = R"(
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS32
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_386
+DWARF:
+  debug_str:
+    - func1
+    - func2
+    - func3
+    - func4
+    - func5
+    - func6
+    - func7
+    - func8
+    - func9
+  debug_abbrev:
+    - ID:              0
+      Table:
+        - Code:            0x1
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+        - Code:            0x2
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+            - Attribute:       DW_AT_high_pc
+              Form:            DW_FORM_data4
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_calling_convention
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+  debug_info:
+    - Version:         4
+      AddrSize:        4
+      Entries:
+        - AbbrCode:        0x1
+          Values:
+            - Value:           0xC
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x0
+            - Value:           0x5
+            - Value:           0x00
+            - Value:           0xCB
+            - Value:           0x1
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x10
+            - Value:           0x5
+            - Value:           0x06
+            - Value:           0xB3
+            - Value:           0x1
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x20
+            - Value:           0x5
+            - Value:           0x0C
+            - Value:           0xB1
+            - Value:           0x1
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x30
+            - Value:           0x5
+            - Value:           0x12
+            - Value:           0xC0
+            - Value:           0x1
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x40
+            - Value:           0x5
+            - Value:           0x18
+            - Value:           0xB2
+            - Value:           0x1
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x50
+            - Value:           0x5
+            - Value:           0x1E
+            - Value:           0xC1
+            - Value:           0x1
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x60
+            - Value:           0x5
+            - Value:           0x24
+            - Value:           0xC2
+            - Value:           0x1
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x70
+            - Value:           0x5
+            - Value:           0x2a
+            - Value:           0xEE
+            - Value:           0x1
+        - AbbrCode:        0x2
+          Values:
+            - Value:           0x80
+            - Value:           0x5
+            - Value:           0x30
+            - Value:           0x01
+            - Value:           0x1
+        - AbbrCode:        0x0
+...
+)";
+  YAMLModuleTester t(yamldata);
+
+  DWARFUnit *unit = t.GetDwarfUnit();
+  ASSERT_NE(unit, nullptr);
+  const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
+  ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
+  DWARFDIE cu_die(unit, cu_entry);
+
+  TypeSystemClang ast_ctx("dummy ASTContext", HostInfoBase::GetTargetTriple());
+  DWARFASTParserClangStub ast_parser(ast_ctx);
+
+  std::vector<std::string> found_function_types;
+  // The DWARF above is just a list of functions. Parse all of them to
+  // extract the function types and their calling convention values.
+  for (DWARFDIE func : cu_die.children()) {
+    ASSERT_EQ(func.Tag(), DW_TAG_subprogram);
+    SymbolContext sc;
+    bool new_type = false;
+    lldb::TypeSP type = ast_parser.ParseTypeFromDWARF(sc, func, &new_type);
+    found_function_types.push_back(
+        type->GetForwardCompilerType().GetTypeName().AsCString());
+  }
+
+  // Compare the parsed function types against the expected list of types.
+  const std::vector<std::string> expected_function_types = {
+      "void () __attribute__((regcall))",
+      "void () __attribute__((fastcall))",
+      "void () __attribute__((stdcall))",
+      "void () __attribute__((vectorcall))",
+      "void () __attribute__((pascal))",
+      "void () __attribute__((ms_abi))",
+      "void () __attribute__((sysv_abi))",
+      "void ()", // invalid calling convention.
+      "void ()", // DW_CC_normal -> no attribute
+  };
+  ASSERT_EQ(found_function_types, expected_function_types);
+}


        


More information about the lldb-commits mailing list