From df9f50d23c0510955c32840b1af4261b70b73347 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Fri, 23 Mar 2018 23:44:52 +0000 Subject: [PATCH] Add support for __attribute__(trivial_abi). , llvm-svn: 328389 --- lldb/packages/Python/lldbsuite/test/decorators.py | 13 ++++ .../lldbsuite/test/lang/cpp/trivial_abi/Makefile | 5 ++ .../test/lang/cpp/trivial_abi/TestTrivialABI.py | 75 ++++++++++++++++++++++ .../lldbsuite/test/lang/cpp/trivial_abi/main.cpp | 35 ++++++++++ .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 24 ++++++- 5 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/Makefile create mode 100644 lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/TestTrivialABI.py create mode 100644 lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/main.cpp diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py index 72c2ed7..228a8bd 100644 --- a/lldb/packages/Python/lldbsuite/test/decorators.py +++ b/lldb/packages/Python/lldbsuite/test/decorators.py @@ -652,6 +652,19 @@ def skipIfTargetAndroid(api_levels=None, archs=None): api_levels, archs)) +def skipUnlessSupportedTypeAttribute(attr): + """Decorate the item to skip test unless Clang supports type __attribute__(attr).""" + def compiler_doesnt_support_struct_attribute(self): + compiler_path = self.getCompiler() + compiler = os.path.basename(compiler_path) + f = tempfile.NamedTemporaryFile() + cmd = "echo 'struct __attribute__((%s)) Test {};' | %s -x c++ -c -o %s - ; exit" % (attr, compiler_path, f.name) + p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) + test_result = p.stderr.read() + if attr in test_result: + return "Compiler does not support attribute %s"%(attr) + return None + return skipTestIfFn(compiler_doesnt_support_struct_attribute) def skipUnlessThreadSanitizer(func): """Decorate the item to skip test unless Clang -fsanitize=thread is supported.""" diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/Makefile b/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/Makefile new file mode 100644 index 0000000..314f1cb --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/TestTrivialABI.py b/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/TestTrivialABI.py new file mode 100644 index 0000000..34ab00e --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/TestTrivialABI.py @@ -0,0 +1,75 @@ +""" +Test that we work properly with classes with the trivial_abi attribute +""" + +from __future__ import print_function + + +import os +import time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test import decorators + +class TestTrivialABI(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + NO_DEBUG_INFO_TESTCASE = True + + @decorators.skipUnlessSupportedTypeAttribute("trivial_abi") + def test_call_trivial(self): + """Test that we can print a variable & call a function with a trivial ABI class.""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.cpp") + self.expr_test(True) + + @decorators.skipUnlessSupportedTypeAttribute("trivial_abi") + @decorators.expectedFailureAll(bugnumber="llvm.org/pr36870") + def test_call_nontrivial(self): + """Test that we can print a variable & call a function on the same class w/o the trivial ABI marker.""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.cpp") + self.expr_test(False) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def check_value(self, test_var, ivar_value): + self.assertTrue(test_var.GetError().Success(), "Invalid valobj: %s"%(test_var.GetError().GetCString())) + ivar = test_var.GetChildMemberWithName("ivar") + self.assertTrue(test_var.GetError().Success(), "Failed to fetch ivar") + self.assertEqual(ivar_value, ivar.GetValueAsSigned(), "Got the right value for ivar") + + def check_frame(self, thread): + frame = thread.frames[0] + inVal_var = frame.FindVariable("inVal") + self.check_value(inVal_var, 10) + + options = lldb.SBExpressionOptions() + inVal_expr = frame.EvaluateExpression("inVal", options) + self.check_value(inVal_expr, 10) + + thread.StepOut() + outVal_ret = thread.GetStopReturnValue() + self.check_value(outVal_ret, 30) + + def expr_test(self, trivial): + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + "Set a breakpoint here", self.main_source_file) + + # Stop in a function that takes a trivial value, and try both frame var & expr to get its value: + if trivial: + self.check_frame(thread) + return + + # Now continue to the same thing without the trivial_abi and see if we get that right: + threads = lldbutil.continue_to_breakpoint(process, bkpt) + self.assertEqual(len(threads), 1, "Hit my breakpoint the second time.") + + self.check_frame(threads[0]) + + diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/main.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/main.cpp new file mode 100644 index 0000000..cdf593e --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/main.cpp @@ -0,0 +1,35 @@ +struct __attribute__((trivial_abi)) S_Trivial { + ~S_Trivial() {} + int ivar = 10; +}; + +S_Trivial takeTrivial(S_Trivial inVal) +{ + S_Trivial ret_val = inVal; + ret_val.ivar = 30; + return ret_val; // Set a breakpoint here +} + +struct S_NotTrivial { + ~S_NotTrivial() {} + int ivar = 10; +}; + +S_NotTrivial takeNotTrivial(S_NotTrivial inVal) +{ + S_NotTrivial ret_val = inVal; + ret_val.ivar = 30; + return ret_val; // Set a breakpoint here +} + +int +main() +{ + S_Trivial inVal, outVal; + outVal = takeTrivial(inVal); + + S_NotTrivial inNotVal, outNotVal; + outNotVal = takeNotTrivial(outNotVal); + + return 0; // Set another for return value +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 7a54f45..73ca83d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -573,6 +573,9 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, LanguageType class_language = eLanguageTypeUnknown; bool is_complete_objc_class = false; + size_t calling_convention + = llvm::dwarf::CallingConvention::DW_CC_normal; + // bool struct_is_class = false; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { @@ -628,7 +631,10 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, case DW_AT_APPLE_objc_complete_type: is_complete_objc_class = form_value.Signed(); break; - + case DW_AT_calling_convention: + calling_convention = form_value.Unsigned(); + break; + case DW_AT_allocated: case DW_AT_associated: case DW_AT_data_location: @@ -884,7 +890,7 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, class_language, &metadata); } } - + // Store a forward declaration to this class type in case any // parameters in any class methods need it for the clang // types for function prototypes. @@ -997,6 +1003,20 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); } } + + // If we made a clang type, set the trivial abi if applicable: + // We only do this for pass by value - which implies the Trivial ABI. + // There isn't a way to assert that something that would normally be + // pass by value is pass by reference, so we ignore that attribute if + // set. + if (calling_convention == llvm::dwarf::DW_CC_pass_by_value) { + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + if (record_decl) { + record_decl->setHasTrivialSpecialMemberForCall(); + } + } + } break; case DW_TAG_enumeration_type: { -- 2.7.4