Add support for __attribute__(trivial_abi).

<rdar://problem/36035075>, <rdar://problem/36035039>

llvm-svn: 328389
This commit is contained in:
Jim Ingham 2018-03-23 23:44:52 +00:00
parent cd5e6a35bf
commit df9f50d23c
5 changed files with 150 additions and 2 deletions

View File

@ -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."""

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

@ -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])

View File

@ -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
}

View File

@ -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: {