forked from OSchip/llvm-project
Add support for __attribute__(trivial_abi).
<rdar://problem/36035075>, <rdar://problem/36035039> llvm-svn: 328389
This commit is contained in:
parent
cd5e6a35bf
commit
df9f50d23c
|
@ -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."""
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
LEVEL = ../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -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])
|
||||
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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: {
|
||||
|
|
Loading…
Reference in New Issue