mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-12 10:52:38 +00:00
Add support for attribute 'trivial_abi'.
The 'trivial_abi' attribute can be applied to a C++ class, struct, or union. It makes special functions of the annotated class (the destructor and copy/move constructors) to be trivial for the purpose of calls and, as a result, enables the annotated class or containing classes to be passed or returned using the C ABI for the underlying type. When a type that is considered trivial for the purpose of calls despite having a non-trivial destructor (which happens only when the class type or one of its subobjects is a 'trivial_abi' class) is passed to a function, the callee is responsible for destroying the object. For more background, see the discussions that took place on the mailing list: http://lists.llvm.org/pipermail/cfe-dev/2017-November/055955.html http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20180101/thread.html#214043 rdar://problem/35204524 Differential Revision: https://reviews.llvm.org/D41039 llvm-svn: 324269
This commit is contained in:
parent
02f6845095
commit
02914dc127
@ -1179,6 +1179,10 @@ public:
|
||||
const FunctionProtoType::ExceptionSpecInfo &ESI,
|
||||
bool AsWritten = false);
|
||||
|
||||
/// Determine whether a type is a class that should be detructed in the
|
||||
/// callee function.
|
||||
bool isParamDestroyedInCallee(QualType T) const;
|
||||
|
||||
/// \brief Return the uniqued reference to the type for a complex
|
||||
/// number with the specified element type.
|
||||
QualType getComplexType(QualType T) const;
|
||||
|
@ -1731,6 +1731,12 @@ private:
|
||||
unsigned HasWrittenPrototype : 1;
|
||||
unsigned IsDeleted : 1;
|
||||
unsigned IsTrivial : 1; // sunk from CXXMethodDecl
|
||||
|
||||
/// This flag indicates whether this function is trivial for the purpose of
|
||||
/// calls. This is meaningful only when this function is a copy/move
|
||||
/// constructor or a destructor.
|
||||
unsigned IsTrivialForCall : 1;
|
||||
|
||||
unsigned IsDefaulted : 1; // sunk from CXXMethoDecl
|
||||
unsigned IsExplicitlyDefaulted : 1; //sunk from CXXMethodDecl
|
||||
unsigned HasImplicitReturnZero : 1;
|
||||
@ -1845,7 +1851,8 @@ protected:
|
||||
IsInline(isInlineSpecified), IsInlineSpecified(isInlineSpecified),
|
||||
IsExplicitSpecified(false), IsVirtualAsWritten(false), IsPure(false),
|
||||
HasInheritedPrototype(false), HasWrittenPrototype(true),
|
||||
IsDeleted(false), IsTrivial(false), IsDefaulted(false),
|
||||
IsDeleted(false), IsTrivial(false), IsTrivialForCall(false),
|
||||
IsDefaulted(false),
|
||||
IsExplicitlyDefaulted(false), HasImplicitReturnZero(false),
|
||||
IsLateTemplateParsed(false), IsConstexpr(isConstexprSpecified),
|
||||
InstantiationIsPending(false), UsesSEHTry(false), HasSkippedBody(false),
|
||||
@ -2010,6 +2017,9 @@ public:
|
||||
bool isTrivial() const { return IsTrivial; }
|
||||
void setTrivial(bool IT) { IsTrivial = IT; }
|
||||
|
||||
bool isTrivialForCall() const { return IsTrivialForCall; }
|
||||
void setTrivialForCall(bool IT) { IsTrivialForCall = IT; }
|
||||
|
||||
/// Whether this function is defaulted per C++0x. Only valid for
|
||||
/// special member functions.
|
||||
bool isDefaulted() const { return IsDefaulted; }
|
||||
|
@ -437,6 +437,11 @@ class CXXRecordDecl : public RecordDecl {
|
||||
/// which have been declared but not yet defined.
|
||||
unsigned HasTrivialSpecialMembers : 6;
|
||||
|
||||
/// These bits keep track of the triviality of special functions for the
|
||||
/// purpose of calls. Only the bits corresponding to SMF_CopyConstructor,
|
||||
/// SMF_MoveConstructor, and SMF_Destructor are meaningful here.
|
||||
unsigned HasTrivialSpecialMembersForCall : 6;
|
||||
|
||||
/// \brief The declared special members of this class which are known to be
|
||||
/// non-trivial.
|
||||
///
|
||||
@ -445,6 +450,12 @@ class CXXRecordDecl : public RecordDecl {
|
||||
/// members which have not yet been declared.
|
||||
unsigned DeclaredNonTrivialSpecialMembers : 6;
|
||||
|
||||
/// These bits keep track of the declared special members that are
|
||||
/// non-trivial for the purpose of calls.
|
||||
/// Only the bits corresponding to SMF_CopyConstructor,
|
||||
/// SMF_MoveConstructor, and SMF_Destructor are meaningful here.
|
||||
unsigned DeclaredNonTrivialSpecialMembersForCall : 6;
|
||||
|
||||
/// \brief True when this class has a destructor with no semantic effect.
|
||||
unsigned HasIrrelevantDestructor : 1;
|
||||
|
||||
@ -1349,6 +1360,10 @@ public:
|
||||
return data().HasTrivialSpecialMembers & SMF_CopyConstructor;
|
||||
}
|
||||
|
||||
bool hasTrivialCopyConstructorForCall() const {
|
||||
return data().HasTrivialSpecialMembersForCall & SMF_CopyConstructor;
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has a non-trivial copy constructor
|
||||
/// (C++ [class.copy]p6, C++11 [class.copy]p12)
|
||||
bool hasNonTrivialCopyConstructor() const {
|
||||
@ -1356,6 +1371,12 @@ public:
|
||||
!hasTrivialCopyConstructor();
|
||||
}
|
||||
|
||||
bool hasNonTrivialCopyConstructorForCall() const {
|
||||
return (data().DeclaredNonTrivialSpecialMembersForCall &
|
||||
SMF_CopyConstructor) ||
|
||||
!hasTrivialCopyConstructorForCall();
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has a trivial move constructor
|
||||
/// (C++11 [class.copy]p12)
|
||||
bool hasTrivialMoveConstructor() const {
|
||||
@ -1363,6 +1384,11 @@ public:
|
||||
(data().HasTrivialSpecialMembers & SMF_MoveConstructor);
|
||||
}
|
||||
|
||||
bool hasTrivialMoveConstructorForCall() const {
|
||||
return hasMoveConstructor() &&
|
||||
(data().HasTrivialSpecialMembersForCall & SMF_MoveConstructor);
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has a non-trivial move constructor
|
||||
/// (C++11 [class.copy]p12)
|
||||
bool hasNonTrivialMoveConstructor() const {
|
||||
@ -1371,6 +1397,13 @@ public:
|
||||
!(data().HasTrivialSpecialMembers & SMF_MoveConstructor));
|
||||
}
|
||||
|
||||
bool hasNonTrivialMoveConstructorForCall() const {
|
||||
return (data().DeclaredNonTrivialSpecialMembersForCall &
|
||||
SMF_MoveConstructor) ||
|
||||
(needsImplicitMoveConstructor() &&
|
||||
!(data().HasTrivialSpecialMembersForCall & SMF_MoveConstructor));
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has a trivial copy assignment operator
|
||||
/// (C++ [class.copy]p11, C++11 [class.copy]p25)
|
||||
bool hasTrivialCopyAssignment() const {
|
||||
@ -1405,12 +1438,25 @@ public:
|
||||
return data().HasTrivialSpecialMembers & SMF_Destructor;
|
||||
}
|
||||
|
||||
bool hasTrivialDestructorForCall() const {
|
||||
return data().HasTrivialSpecialMembersForCall & SMF_Destructor;
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has a non-trivial destructor
|
||||
/// (C++ [class.dtor]p3)
|
||||
bool hasNonTrivialDestructor() const {
|
||||
return !(data().HasTrivialSpecialMembers & SMF_Destructor);
|
||||
}
|
||||
|
||||
bool hasNonTrivialDestructorForCall() const {
|
||||
return !(data().HasTrivialSpecialMembersForCall & SMF_Destructor);
|
||||
}
|
||||
|
||||
void setHasTrivialSpecialMemberForCall() {
|
||||
data().HasTrivialSpecialMembersForCall =
|
||||
(SMF_CopyConstructor | SMF_MoveConstructor | SMF_Destructor);
|
||||
}
|
||||
|
||||
/// \brief Determine whether declaring a const variable with this type is ok
|
||||
/// per core issue 253.
|
||||
bool allowConstDefaultInit() const {
|
||||
@ -1440,6 +1486,13 @@ public:
|
||||
data().CanPassInRegisters = CanPass;
|
||||
}
|
||||
|
||||
/// Determine whether the triviality for the purpose of calls for this class
|
||||
/// is overridden to be trivial because this class or the type of one of its
|
||||
/// subobjects has attribute "trivial_abi".
|
||||
bool hasTrivialABIOverride() const {
|
||||
return canPassInRegisters() && hasNonTrivialDestructor();
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has a non-literal or/ volatile type
|
||||
/// non-static data member or base class.
|
||||
bool hasNonLiteralTypeFieldsOrBases() const {
|
||||
@ -1797,6 +1850,8 @@ public:
|
||||
/// member function is now complete.
|
||||
void finishedDefaultedOrDeletedMember(CXXMethodDecl *MD);
|
||||
|
||||
void setTrivialForCallFlags(CXXMethodDecl *MD);
|
||||
|
||||
/// \brief Indicates that the definition of this class is now complete.
|
||||
void completeDefinition() override;
|
||||
|
||||
|
@ -808,6 +808,11 @@ public:
|
||||
/// Return true if this is a trivially copyable type (C++0x [basic.types]p9)
|
||||
bool isTriviallyCopyableType(const ASTContext &Context) const;
|
||||
|
||||
/// Determine whether this is a class whose triviality for the purpose of
|
||||
/// calls is overridden to be trivial because the class or the type of one of
|
||||
/// its subobjects has attribute "trivial_abi".
|
||||
bool hasTrivialABIOverride() const;
|
||||
|
||||
// Don't promise in the API that anything besides 'const' can be
|
||||
// easily added.
|
||||
|
||||
|
@ -1159,6 +1159,13 @@ def LayoutVersion : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
|
||||
let Documentation = [LayoutVersionDocs];
|
||||
}
|
||||
|
||||
def TrivialABI : InheritableAttr {
|
||||
let Spellings = [Clang<"trivial_abi">];
|
||||
let Subjects = SubjectList<[CXXRecord]>;
|
||||
let Documentation = [TrivialABIDocs];
|
||||
let LangOpts = [CPlusPlus];
|
||||
}
|
||||
|
||||
def MaxFieldAlignment : InheritableAttr {
|
||||
// This attribute has no spellings as it is only ever created implicitly.
|
||||
let Spellings = [];
|
||||
|
@ -2242,6 +2242,48 @@ It is only supported when using the Microsoft C++ ABI.
|
||||
}];
|
||||
}
|
||||
|
||||
def TrivialABIDocs : Documentation {
|
||||
let Category = DocCatVariable;
|
||||
let Content = [{
|
||||
The ``trivial_abi`` attribute can be applied to a C++ class, struct, or union.
|
||||
It instructs the compiler to pass and return the type using the C ABI for the
|
||||
underlying type when the type would otherwise be considered non-trivial for the
|
||||
purpose of calls.
|
||||
A class annotated with `trivial_abi` can have non-trivial destructors or copy/move constructors without automatically becoming non-trivial for the purposes of calls. For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// A is trivial for the purposes of calls because `trivial_abi` makes the
|
||||
// user-provided special functions trivial.
|
||||
struct __attribute__((trivial_abi)) A {
|
||||
~A();
|
||||
A(const A &);
|
||||
A(A &&);
|
||||
int x;
|
||||
};
|
||||
|
||||
// B's destructor and copy/move constructor are considered trivial for the
|
||||
// purpose of calls because A is trivial.
|
||||
struct B {
|
||||
A a;
|
||||
};
|
||||
|
||||
If a type is trivial for the purposes of calls, has a non-trivial destructor,
|
||||
and is passed as an argument by value, the convention is that the callee will
|
||||
destroy the object before returning.
|
||||
|
||||
Attribute ``trivial_abi`` has no effect in the following cases:
|
||||
|
||||
- The class directly declares a virtual base or virtual methods.
|
||||
- The class has a base class that is non-trivial for the purposes of calls.
|
||||
- The class has a non-static data member whose type is non-trivial for the
|
||||
purposes of calls, which includes:
|
||||
- classes that are non-trivial for the purposes of calls
|
||||
- __weak-qualified types in Objective-C++
|
||||
- arrays of any of the above
|
||||
}];
|
||||
}
|
||||
|
||||
def MSInheritanceDocs : Documentation {
|
||||
let Category = DocCatType;
|
||||
let Heading = "__single_inhertiance, __multiple_inheritance, __virtual_inheritance";
|
||||
|
@ -2881,6 +2881,9 @@ def err_base_specifier_attribute : Error<
|
||||
def err_invalid_attribute_on_virtual_function : Error<
|
||||
"%0 attribute cannot be applied to virtual functions">;
|
||||
|
||||
def ext_cannot_use_trivial_abi : ExtWarn<
|
||||
"'trivial_abi' cannot be applied to %0">, InGroup<IgnoredAttributes>;
|
||||
|
||||
// Availability attribute
|
||||
def warn_availability_unknown_platform : Warning<
|
||||
"unknown platform %0 in availability macro">, InGroup<Availability>;
|
||||
|
@ -2236,7 +2236,17 @@ public:
|
||||
|
||||
bool CheckNontrivialField(FieldDecl *FD);
|
||||
void DiagnoseNontrivial(const CXXRecordDecl *Record, CXXSpecialMember CSM);
|
||||
|
||||
enum TrivialABIHandling {
|
||||
/// The triviality of a method unaffected by "trivial_abi".
|
||||
TAH_IgnoreTrivialABI,
|
||||
|
||||
/// The triviality of a method affected by "trivial_abi".
|
||||
TAH_ConsiderTrivialABI
|
||||
};
|
||||
|
||||
bool SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
|
||||
TrivialABIHandling TAH = TAH_IgnoreTrivialABI,
|
||||
bool Diagnose = false);
|
||||
CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD);
|
||||
void ActOnLastBitfield(SourceLocation DeclStart,
|
||||
@ -5796,6 +5806,11 @@ public:
|
||||
SourceLocation BaseLoc);
|
||||
|
||||
void CheckCompletedCXXClass(CXXRecordDecl *Record);
|
||||
|
||||
/// Check that the C++ class annoated with "trivial_abi" satisfies all the
|
||||
/// conditions that are needed for the attribute to have an effect.
|
||||
void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD);
|
||||
|
||||
void ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
|
||||
Decl *TagDecl,
|
||||
SourceLocation LBrac,
|
||||
|
@ -2640,6 +2640,11 @@ void ASTContext::adjustExceptionSpec(
|
||||
}
|
||||
}
|
||||
|
||||
bool ASTContext::isParamDestroyedInCallee(QualType T) const {
|
||||
return getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee() ||
|
||||
T.hasTrivialABIOverride();
|
||||
}
|
||||
|
||||
/// getComplexType - Return the uniqued reference to the type for a complex
|
||||
/// number with the specified element type.
|
||||
QualType ASTContext::getComplexType(QualType T) const {
|
||||
|
@ -88,7 +88,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
|
||||
DefaultedMoveConstructorIsDeleted(false),
|
||||
DefaultedMoveAssignmentIsDeleted(false),
|
||||
DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
|
||||
DeclaredNonTrivialSpecialMembers(0), HasIrrelevantDestructor(true),
|
||||
HasTrivialSpecialMembersForCall(SMF_All),
|
||||
DeclaredNonTrivialSpecialMembers(0),
|
||||
DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true),
|
||||
HasConstexprNonCopyMoveConstructor(false),
|
||||
HasDefaultedDefaultConstructor(false),
|
||||
CanPassInRegisters(true),
|
||||
@ -281,6 +283,7 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
||||
// operator for a class X] is trivial [...] if:
|
||||
// -- class X has [...] no virtual base classes
|
||||
data().HasTrivialSpecialMembers &= SMF_Destructor;
|
||||
data().HasTrivialSpecialMembersForCall &= SMF_Destructor;
|
||||
|
||||
// C++0x [class]p7:
|
||||
// A standard-layout class is a class that: [...]
|
||||
@ -314,6 +317,10 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
||||
// subobject is trivial, and
|
||||
if (!BaseClassDecl->hasTrivialCopyConstructor())
|
||||
data().HasTrivialSpecialMembers &= ~SMF_CopyConstructor;
|
||||
|
||||
if (!BaseClassDecl->hasTrivialCopyConstructorForCall())
|
||||
data().HasTrivialSpecialMembersForCall &= ~SMF_CopyConstructor;
|
||||
|
||||
// If the base class doesn't have a simple move constructor, we'll eagerly
|
||||
// declare it and perform overload resolution to determine which function
|
||||
// it actually calls. If it does have a simple move constructor, this
|
||||
@ -321,6 +328,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
||||
if (!BaseClassDecl->hasTrivialMoveConstructor())
|
||||
data().HasTrivialSpecialMembers &= ~SMF_MoveConstructor;
|
||||
|
||||
if (!BaseClassDecl->hasTrivialMoveConstructorForCall())
|
||||
data().HasTrivialSpecialMembersForCall &= ~SMF_MoveConstructor;
|
||||
|
||||
// C++0x [class.copy]p27:
|
||||
// A copy/move assignment operator for class X is trivial if [...]
|
||||
// [...]
|
||||
@ -357,6 +367,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
||||
if (!BaseClassDecl->hasTrivialDestructor())
|
||||
data().HasTrivialSpecialMembers &= ~SMF_Destructor;
|
||||
|
||||
if (!BaseClassDecl->hasTrivialDestructorForCall())
|
||||
data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor;
|
||||
|
||||
if (!BaseClassDecl->hasIrrelevantDestructor())
|
||||
data().HasIrrelevantDestructor = false;
|
||||
|
||||
@ -539,6 +552,7 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
||||
// assignment operator for a class X] is trivial [...] if:
|
||||
// -- class X has no virtual functions [...]
|
||||
data().HasTrivialSpecialMembers &= SMF_Destructor;
|
||||
data().HasTrivialSpecialMembersForCall &= SMF_Destructor;
|
||||
|
||||
// C++0x [class]p7:
|
||||
// A standard-layout class is a class that: [...]
|
||||
@ -623,8 +637,10 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
||||
|
||||
// C++11 [class.dtor]p5:
|
||||
// A destructor is trivial if [...] the destructor is not virtual.
|
||||
if (DD->isVirtual())
|
||||
if (DD->isVirtual()) {
|
||||
data().HasTrivialSpecialMembers &= ~SMF_Destructor;
|
||||
data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle member functions.
|
||||
@ -670,16 +686,30 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
||||
// If this is the first declaration of a special member, we no longer have
|
||||
// an implicit trivial special member.
|
||||
data().HasTrivialSpecialMembers &=
|
||||
data().DeclaredSpecialMembers | ~SMKind;
|
||||
data().DeclaredSpecialMembers | ~SMKind;
|
||||
data().HasTrivialSpecialMembersForCall &=
|
||||
data().DeclaredSpecialMembers | ~SMKind;
|
||||
|
||||
if (!Method->isImplicit() && !Method->isUserProvided()) {
|
||||
// This method is user-declared but not user-provided. We can't work out
|
||||
// whether it's trivial yet (not until we get to the end of the class).
|
||||
// We'll handle this method in finishedDefaultedOrDeletedMember.
|
||||
} else if (Method->isTrivial())
|
||||
} else if (Method->isTrivial()) {
|
||||
data().HasTrivialSpecialMembers |= SMKind;
|
||||
else
|
||||
data().HasTrivialSpecialMembersForCall |= SMKind;
|
||||
} else if (Method->isTrivialForCall()) {
|
||||
data().HasTrivialSpecialMembersForCall |= SMKind;
|
||||
data().DeclaredNonTrivialSpecialMembers |= SMKind;
|
||||
} else {
|
||||
data().DeclaredNonTrivialSpecialMembers |= SMKind;
|
||||
// If this is a user-provided function, do not set
|
||||
// DeclaredNonTrivialSpecialMembersForCall here since we don't know
|
||||
// yet whether the method would be considered non-trivial for the
|
||||
// purpose of calls (attribute "trivial_abi" can be dropped from the
|
||||
// class later, which can change the special method's triviality).
|
||||
if (!Method->isUserProvided())
|
||||
data().DeclaredNonTrivialSpecialMembersForCall |= SMKind;
|
||||
}
|
||||
|
||||
// Note when we have declared a declared special member, and suppress the
|
||||
// implicit declaration of this special member.
|
||||
@ -772,6 +802,7 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
||||
struct DefinitionData &Data = data();
|
||||
Data.PlainOldData = false;
|
||||
Data.HasTrivialSpecialMembers = 0;
|
||||
Data.HasTrivialSpecialMembersForCall = 0;
|
||||
Data.HasIrrelevantDestructor = false;
|
||||
} else if (!Context.getLangOpts().ObjCAutoRefCount) {
|
||||
setHasObjectMember(true);
|
||||
@ -899,12 +930,19 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
||||
// member is trivial;
|
||||
if (!FieldRec->hasTrivialCopyConstructor())
|
||||
data().HasTrivialSpecialMembers &= ~SMF_CopyConstructor;
|
||||
|
||||
if (!FieldRec->hasTrivialCopyConstructorForCall())
|
||||
data().HasTrivialSpecialMembersForCall &= ~SMF_CopyConstructor;
|
||||
|
||||
// If the field doesn't have a simple move constructor, we'll eagerly
|
||||
// declare the move constructor for this class and we'll decide whether
|
||||
// it's trivial then.
|
||||
if (!FieldRec->hasTrivialMoveConstructor())
|
||||
data().HasTrivialSpecialMembers &= ~SMF_MoveConstructor;
|
||||
|
||||
if (!FieldRec->hasTrivialMoveConstructorForCall())
|
||||
data().HasTrivialSpecialMembersForCall &= ~SMF_MoveConstructor;
|
||||
|
||||
// C++0x [class.copy]p27:
|
||||
// A copy/move assignment operator for class X is trivial if [...]
|
||||
// [...]
|
||||
@ -921,6 +959,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
||||
|
||||
if (!FieldRec->hasTrivialDestructor())
|
||||
data().HasTrivialSpecialMembers &= ~SMF_Destructor;
|
||||
if (!FieldRec->hasTrivialDestructorForCall())
|
||||
data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor;
|
||||
if (!FieldRec->hasIrrelevantDestructor())
|
||||
data().HasIrrelevantDestructor = false;
|
||||
if (FieldRec->hasObjectMember())
|
||||
@ -1103,6 +1143,23 @@ void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
|
||||
data().DeclaredNonTrivialSpecialMembers |= SMKind;
|
||||
}
|
||||
|
||||
void CXXRecordDecl::setTrivialForCallFlags(CXXMethodDecl *D) {
|
||||
unsigned SMKind = 0;
|
||||
|
||||
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
|
||||
if (Constructor->isCopyConstructor())
|
||||
SMKind = SMF_CopyConstructor;
|
||||
else if (Constructor->isMoveConstructor())
|
||||
SMKind = SMF_MoveConstructor;
|
||||
} else if (isa<CXXDestructorDecl>(D))
|
||||
SMKind = SMF_Destructor;
|
||||
|
||||
if (D->isTrivialForCall())
|
||||
data().HasTrivialSpecialMembersForCall |= SMKind;
|
||||
else
|
||||
data().DeclaredNonTrivialSpecialMembersForCall |= SMKind;
|
||||
}
|
||||
|
||||
bool CXXRecordDecl::isCLike() const {
|
||||
if (getTagKind() == TTK_Class || getTagKind() == TTK_Interface ||
|
||||
!TemplateOrInstantiation.isNull())
|
||||
|
@ -2201,6 +2201,12 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QualType::hasTrivialABIOverride() const {
|
||||
if (const auto *RD = getTypePtr()->getAsCXXRecordDecl())
|
||||
return RD->hasTrivialABIOverride();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
|
||||
return !Context.getLangOpts().ObjCAutoRefCount &&
|
||||
Context.getLangOpts().ObjCWeak &&
|
||||
|
@ -3144,7 +3144,6 @@ static void emitWritebacks(CodeGenFunction &CGF,
|
||||
|
||||
static void deactivateArgCleanupsBeforeCall(CodeGenFunction &CGF,
|
||||
const CallArgList &CallArgs) {
|
||||
assert(CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee());
|
||||
ArrayRef<CallArgList::CallArgCleanup> Cleanups =
|
||||
CallArgs.getCleanupsToDeactivate();
|
||||
// Iterate in reverse to increase the likelihood of popping the cleanup.
|
||||
@ -3501,8 +3500,7 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
|
||||
// In the Microsoft C++ ABI, aggregate arguments are destructed by the callee.
|
||||
// However, we still have to push an EH-only cleanup in case we unwind before
|
||||
// we make it to the call.
|
||||
if (HasAggregateEvalKind &&
|
||||
CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
|
||||
if (HasAggregateEvalKind && getContext().isParamDestroyedInCallee(type)) {
|
||||
// If we're using inalloca, use the argument memory. Otherwise, use a
|
||||
// temporary.
|
||||
AggValueSlot Slot;
|
||||
@ -3514,7 +3512,8 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
|
||||
const CXXRecordDecl *RD = type->getAsCXXRecordDecl();
|
||||
bool DestroyedInCallee =
|
||||
RD && RD->hasNonTrivialDestructor() &&
|
||||
CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default;
|
||||
(CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default ||
|
||||
RD->hasTrivialABIOverride());
|
||||
if (DestroyedInCallee)
|
||||
Slot.setExternallyDestructed();
|
||||
|
||||
|
@ -1868,7 +1868,7 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
|
||||
// Don't push a cleanup in a thunk for a method that will also emit a
|
||||
// cleanup.
|
||||
if (!IsScalar && !CurFuncIsThunk &&
|
||||
getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
|
||||
getContext().isParamDestroyedInCallee(Ty)) {
|
||||
const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
|
||||
if (RD && RD->hasNonTrivialDestructor())
|
||||
pushDestroy(QualType::DK_cxx_destructor, DeclPtr, Ty);
|
||||
|
@ -68,8 +68,8 @@ public:
|
||||
if (CGM.getCodeGenOpts().getClangABICompat() <=
|
||||
CodeGenOptions::ClangABI::Ver4 ||
|
||||
CGM.getTriple().getOS() == llvm::Triple::PS4)
|
||||
return RD->hasNonTrivialDestructor() ||
|
||||
RD->hasNonTrivialCopyConstructor();
|
||||
return RD->hasNonTrivialDestructorForCall() ||
|
||||
RD->hasNonTrivialCopyConstructorForCall();
|
||||
return !canCopyArgument(RD);
|
||||
}
|
||||
|
||||
|
@ -820,19 +820,8 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
|
||||
return RAA_Default;
|
||||
|
||||
case llvm::Triple::x86_64:
|
||||
// If a class has a destructor, we'd really like to pass it indirectly
|
||||
// because it allows us to elide copies. Unfortunately, MSVC makes that
|
||||
// impossible for small types, which it will pass in a single register or
|
||||
// stack slot. Most objects with dtors are large-ish, so handle that early.
|
||||
// We can't call out all large objects as being indirect because there are
|
||||
// multiple x64 calling conventions and the C++ ABI code shouldn't dictate
|
||||
// how we pass large POD types.
|
||||
//
|
||||
// Note: This permits small classes with nontrivial destructors to be
|
||||
// passed in registers, which is non-conforming.
|
||||
if (RD->hasNonTrivialDestructor() &&
|
||||
getContext().getTypeSize(RD->getTypeForDecl()) > 64)
|
||||
return RAA_Indirect;
|
||||
bool CopyCtorIsTrivial = false, CopyCtorIsTrivialForCall = false;
|
||||
bool DtorIsTrivialForCall = false;
|
||||
|
||||
// If a class has at least one non-deleted, trivial copy constructor, it
|
||||
// is passed according to the C ABI. Otherwise, it is passed indirectly.
|
||||
@ -841,23 +830,49 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
|
||||
// passed in registers, so long as they *also* have a trivial copy ctor,
|
||||
// which is non-conforming.
|
||||
if (RD->needsImplicitCopyConstructor()) {
|
||||
// If the copy ctor has not yet been declared, we can read its triviality
|
||||
// off the AST.
|
||||
if (!RD->defaultedCopyConstructorIsDeleted() &&
|
||||
RD->hasTrivialCopyConstructor())
|
||||
return RAA_Default;
|
||||
if (!RD->defaultedCopyConstructorIsDeleted()) {
|
||||
if (RD->hasTrivialCopyConstructor())
|
||||
CopyCtorIsTrivial = true;
|
||||
if (RD->hasTrivialCopyConstructorForCall())
|
||||
CopyCtorIsTrivialForCall = true;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we need to find the copy constructor(s) and ask.
|
||||
for (const CXXConstructorDecl *CD : RD->ctors()) {
|
||||
if (CD->isCopyConstructor()) {
|
||||
// We had at least one nondeleted trivial copy ctor. Return directly.
|
||||
if (!CD->isDeleted() && CD->isTrivial())
|
||||
return RAA_Default;
|
||||
if (CD->isCopyConstructor() && !CD->isDeleted()) {
|
||||
if (CD->isTrivial())
|
||||
CopyCtorIsTrivial = true;
|
||||
if (CD->isTrivialForCall())
|
||||
CopyCtorIsTrivialForCall = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We have no trivial, non-deleted copy constructor.
|
||||
if (RD->needsImplicitDestructor()) {
|
||||
if (!RD->defaultedDestructorIsDeleted() &&
|
||||
RD->hasTrivialDestructorForCall())
|
||||
DtorIsTrivialForCall = true;
|
||||
} else if (const auto *D = RD->getDestructor()) {
|
||||
if (!D->isDeleted() && D->isTrivialForCall())
|
||||
DtorIsTrivialForCall = true;
|
||||
}
|
||||
|
||||
// If the copy ctor and dtor are both trivial-for-calls, pass direct.
|
||||
if (CopyCtorIsTrivialForCall && DtorIsTrivialForCall)
|
||||
return RAA_Default;
|
||||
|
||||
// If a class has a destructor, we'd really like to pass it indirectly
|
||||
// because it allows us to elide copies. Unfortunately, MSVC makes that
|
||||
// impossible for small types, which it will pass in a single register or
|
||||
// stack slot. Most objects with dtors are large-ish, so handle that early.
|
||||
// We can't call out all large objects as being indirect because there are
|
||||
// multiple x64 calling conventions and the C++ ABI code shouldn't dictate
|
||||
// how we pass large POD types.
|
||||
|
||||
// Note: This permits small classes with nontrivial destructors to be
|
||||
// passed in registers, which is non-conforming.
|
||||
if (CopyCtorIsTrivial &&
|
||||
getContext().getTypeSize(RD->getTypeForDecl()) <= 64)
|
||||
return RAA_Default;
|
||||
return RAA_Indirect;
|
||||
}
|
||||
|
||||
|
@ -10814,23 +10814,18 @@ bool Sema::CheckParmsForFunctionDef(ArrayRef<ParmVarDecl *> Parameters,
|
||||
// information is added for it.
|
||||
diagnoseArrayStarInParamType(*this, PType, Param->getLocation());
|
||||
|
||||
// MSVC destroys objects passed by value in the callee. Therefore a
|
||||
// function definition which takes such a parameter must be able to call the
|
||||
// object's destructor. However, we don't perform any direct access check
|
||||
// on the dtor.
|
||||
if (getLangOpts().CPlusPlus && Context.getTargetInfo()
|
||||
.getCXXABI()
|
||||
.areArgsDestroyedLeftToRightInCallee()) {
|
||||
if (!Param->isInvalidDecl()) {
|
||||
if (const RecordType *RT = Param->getType()->getAs<RecordType>()) {
|
||||
CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(RT->getDecl());
|
||||
if (!ClassDecl->isInvalidDecl() &&
|
||||
!ClassDecl->hasIrrelevantDestructor() &&
|
||||
!ClassDecl->isDependentContext()) {
|
||||
CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
|
||||
MarkFunctionReferenced(Param->getLocation(), Destructor);
|
||||
DiagnoseUseOfDecl(Destructor, Param->getLocation());
|
||||
}
|
||||
// If the parameter is a c++ class type and it has to be destructed in the
|
||||
// callee function, declare the destructor so that it can be called by the
|
||||
// callee function. Do not perfom any direct access check on the dtor here.
|
||||
if (!Param->isInvalidDecl()) {
|
||||
if (CXXRecordDecl *ClassDecl = Param->getType()->getAsCXXRecordDecl()) {
|
||||
if (!ClassDecl->isInvalidDecl() &&
|
||||
!ClassDecl->hasIrrelevantDestructor() &&
|
||||
!ClassDecl->isDependentContext() &&
|
||||
Context.isParamDestroyedInCallee(Param->getType())) {
|
||||
CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
|
||||
MarkFunctionReferenced(Param->getLocation(), Destructor);
|
||||
DiagnoseUseOfDecl(Destructor, Param->getLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6436,6 +6436,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
||||
case AttributeList::AT_LayoutVersion:
|
||||
handleLayoutVersion(S, D, Attr);
|
||||
break;
|
||||
case AttributeList::AT_TrivialABI:
|
||||
handleSimpleAttribute<TrivialABIAttr>(S, D, Attr);
|
||||
break;
|
||||
case AttributeList::AT_MSNoVTable:
|
||||
handleSimpleAttribute<MSNoVTableAttr>(S, D, Attr);
|
||||
break;
|
||||
|
@ -5780,20 +5780,20 @@ static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) {
|
||||
|
||||
if (D->needsImplicitCopyConstructor() &&
|
||||
!D->defaultedCopyConstructorIsDeleted()) {
|
||||
if (!D->hasTrivialCopyConstructor())
|
||||
if (!D->hasTrivialCopyConstructorForCall())
|
||||
return false;
|
||||
HasNonDeletedCopyOrMove = true;
|
||||
}
|
||||
|
||||
if (S.getLangOpts().CPlusPlus11 && D->needsImplicitMoveConstructor() &&
|
||||
!D->defaultedMoveConstructorIsDeleted()) {
|
||||
if (!D->hasTrivialMoveConstructor())
|
||||
if (!D->hasTrivialMoveConstructorForCall())
|
||||
return false;
|
||||
HasNonDeletedCopyOrMove = true;
|
||||
}
|
||||
|
||||
if (D->needsImplicitDestructor() && !D->defaultedDestructorIsDeleted() &&
|
||||
!D->hasTrivialDestructor())
|
||||
!D->hasTrivialDestructorForCall())
|
||||
return false;
|
||||
|
||||
for (const CXXMethodDecl *MD : D->methods()) {
|
||||
@ -5806,7 +5806,7 @@ static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) {
|
||||
else if (!isa<CXXDestructorDecl>(MD))
|
||||
continue;
|
||||
|
||||
if (!MD->isTrivial())
|
||||
if (!MD->isTrivialForCall())
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5890,6 +5890,13 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
|
||||
}
|
||||
}
|
||||
|
||||
// Set HasTrivialSpecialMemberForCall if the record has attribute
|
||||
// "trivial_abi".
|
||||
bool HasTrivialABI = Record->hasAttr<TrivialABIAttr>();
|
||||
|
||||
if (HasTrivialABI)
|
||||
Record->setHasTrivialSpecialMemberForCall();
|
||||
|
||||
bool HasMethodWithOverrideControl = false,
|
||||
HasOverridingMethodWithoutOverrideControl = false;
|
||||
if (!Record->isDependentType()) {
|
||||
@ -5912,12 +5919,23 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
|
||||
if (!M->isImplicit() && !M->isUserProvided()) {
|
||||
if (CSM != CXXInvalid) {
|
||||
M->setTrivial(SpecialMemberIsTrivial(M, CSM));
|
||||
|
||||
// Inform the class that we've finished declaring this member.
|
||||
Record->finishedDefaultedOrDeletedMember(M);
|
||||
M->setTrivialForCall(
|
||||
HasTrivialABI ||
|
||||
SpecialMemberIsTrivial(M, CSM, TAH_ConsiderTrivialABI));
|
||||
Record->setTrivialForCallFlags(M);
|
||||
}
|
||||
}
|
||||
|
||||
// Set triviality for the purpose of calls if this is a user-provided
|
||||
// copy/move constructor or destructor.
|
||||
if ((CSM == CXXCopyConstructor || CSM == CXXMoveConstructor ||
|
||||
CSM == CXXDestructor) && M->isUserProvided()) {
|
||||
M->setTrivialForCall(HasTrivialABI);
|
||||
Record->setTrivialForCallFlags(M);
|
||||
}
|
||||
|
||||
if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() &&
|
||||
M->hasAttr<DLLExportAttr>()) {
|
||||
if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) &&
|
||||
@ -7029,9 +7047,14 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM,
|
||||
///
|
||||
/// If \p Selected is not \c NULL, \c *Selected will be filled in with the
|
||||
/// member that was most likely to be intended to be trivial, if any.
|
||||
///
|
||||
/// If \p ForCall is true, look at CXXRecord::HasTrivialSpecialMembersForCall to
|
||||
/// determine whether the special member is trivial.
|
||||
static bool findTrivialSpecialMember(Sema &S, CXXRecordDecl *RD,
|
||||
Sema::CXXSpecialMember CSM, unsigned Quals,
|
||||
bool ConstRHS, CXXMethodDecl **Selected) {
|
||||
bool ConstRHS,
|
||||
Sema::TrivialABIHandling TAH,
|
||||
CXXMethodDecl **Selected) {
|
||||
if (Selected)
|
||||
*Selected = nullptr;
|
||||
|
||||
@ -7072,7 +7095,9 @@ static bool findTrivialSpecialMember(Sema &S, CXXRecordDecl *RD,
|
||||
// C++11 [class.dtor]p5:
|
||||
// A destructor is trivial if:
|
||||
// - all the direct [subobjects] have trivial destructors
|
||||
if (RD->hasTrivialDestructor())
|
||||
if (RD->hasTrivialDestructor() ||
|
||||
(TAH == Sema::TAH_ConsiderTrivialABI &&
|
||||
RD->hasTrivialDestructorForCall()))
|
||||
return true;
|
||||
|
||||
if (Selected) {
|
||||
@ -7087,7 +7112,9 @@ static bool findTrivialSpecialMember(Sema &S, CXXRecordDecl *RD,
|
||||
// C++11 [class.copy]p12:
|
||||
// A copy constructor is trivial if:
|
||||
// - the constructor selected to copy each direct [subobject] is trivial
|
||||
if (RD->hasTrivialCopyConstructor()) {
|
||||
if (RD->hasTrivialCopyConstructor() ||
|
||||
(TAH == Sema::TAH_ConsiderTrivialABI &&
|
||||
RD->hasTrivialCopyConstructorForCall())) {
|
||||
if (Quals == Qualifiers::Const)
|
||||
// We must either select the trivial copy constructor or reach an
|
||||
// ambiguity; no need to actually perform overload resolution.
|
||||
@ -7140,6 +7167,10 @@ static bool findTrivialSpecialMember(Sema &S, CXXRecordDecl *RD,
|
||||
// not supposed to!
|
||||
if (Selected)
|
||||
*Selected = SMOR.getMethod();
|
||||
|
||||
if (TAH == Sema::TAH_ConsiderTrivialABI &&
|
||||
(CSM == Sema::CXXCopyConstructor || CSM == Sema::CXXMoveConstructor))
|
||||
return SMOR.getMethod()->isTrivialForCall();
|
||||
return SMOR.getMethod()->isTrivial();
|
||||
}
|
||||
|
||||
@ -7178,14 +7209,14 @@ static bool checkTrivialSubobjectCall(Sema &S, SourceLocation SubobjLoc,
|
||||
QualType SubType, bool ConstRHS,
|
||||
Sema::CXXSpecialMember CSM,
|
||||
TrivialSubobjectKind Kind,
|
||||
bool Diagnose) {
|
||||
Sema::TrivialABIHandling TAH, bool Diagnose) {
|
||||
CXXRecordDecl *SubRD = SubType->getAsCXXRecordDecl();
|
||||
if (!SubRD)
|
||||
return true;
|
||||
|
||||
CXXMethodDecl *Selected;
|
||||
if (findTrivialSpecialMember(S, SubRD, CSM, SubType.getCVRQualifiers(),
|
||||
ConstRHS, Diagnose ? &Selected : nullptr))
|
||||
ConstRHS, TAH, Diagnose ? &Selected : nullptr))
|
||||
return true;
|
||||
|
||||
if (Diagnose) {
|
||||
@ -7215,7 +7246,8 @@ static bool checkTrivialSubobjectCall(Sema &S, SourceLocation SubobjLoc,
|
||||
<< Kind << SubType.getUnqualifiedType() << CSM;
|
||||
|
||||
// Explain why the defaulted or deleted special member isn't trivial.
|
||||
S.SpecialMemberIsTrivial(Selected, CSM, Diagnose);
|
||||
S.SpecialMemberIsTrivial(Selected, CSM, Sema::TAH_IgnoreTrivialABI,
|
||||
Diagnose);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7226,7 +7258,9 @@ static bool checkTrivialSubobjectCall(Sema &S, SourceLocation SubobjLoc,
|
||||
/// trivial.
|
||||
static bool checkTrivialClassMembers(Sema &S, CXXRecordDecl *RD,
|
||||
Sema::CXXSpecialMember CSM,
|
||||
bool ConstArg, bool Diagnose) {
|
||||
bool ConstArg,
|
||||
Sema::TrivialABIHandling TAH,
|
||||
bool Diagnose) {
|
||||
for (const auto *FI : RD->fields()) {
|
||||
if (FI->isInvalidDecl() || FI->isUnnamedBitfield())
|
||||
continue;
|
||||
@ -7236,7 +7270,7 @@ static bool checkTrivialClassMembers(Sema &S, CXXRecordDecl *RD,
|
||||
// Pretend anonymous struct or union members are members of this class.
|
||||
if (FI->isAnonymousStructOrUnion()) {
|
||||
if (!checkTrivialClassMembers(S, FieldType->getAsCXXRecordDecl(),
|
||||
CSM, ConstArg, Diagnose))
|
||||
CSM, ConstArg, TAH, Diagnose))
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
@ -7264,7 +7298,7 @@ static bool checkTrivialClassMembers(Sema &S, CXXRecordDecl *RD,
|
||||
|
||||
bool ConstRHS = ConstArg && !FI->isMutable();
|
||||
if (!checkTrivialSubobjectCall(S, FI->getLocation(), FieldType, ConstRHS,
|
||||
CSM, TSK_Field, Diagnose))
|
||||
CSM, TSK_Field, TAH, Diagnose))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -7278,14 +7312,15 @@ void Sema::DiagnoseNontrivial(const CXXRecordDecl *RD, CXXSpecialMember CSM) {
|
||||
|
||||
bool ConstArg = (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment);
|
||||
checkTrivialSubobjectCall(*this, RD->getLocation(), Ty, ConstArg, CSM,
|
||||
TSK_CompleteObject, /*Diagnose*/true);
|
||||
TSK_CompleteObject, TAH_IgnoreTrivialABI,
|
||||
/*Diagnose*/true);
|
||||
}
|
||||
|
||||
/// Determine whether a defaulted or deleted special member function is trivial,
|
||||
/// as specified in C++11 [class.ctor]p5, C++11 [class.copy]p12,
|
||||
/// C++11 [class.copy]p25, and C++11 [class.dtor]p5.
|
||||
bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
|
||||
bool Diagnose) {
|
||||
TrivialABIHandling TAH, bool Diagnose) {
|
||||
assert(!MD->isUserProvided() && CSM != CXXInvalid && "not special enough");
|
||||
|
||||
CXXRecordDecl *RD = MD->getParent();
|
||||
@ -7362,7 +7397,7 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
|
||||
// destructors]
|
||||
for (const auto &BI : RD->bases())
|
||||
if (!checkTrivialSubobjectCall(*this, BI.getLocStart(), BI.getType(),
|
||||
ConstArg, CSM, TSK_BaseClass, Diagnose))
|
||||
ConstArg, CSM, TSK_BaseClass, TAH, Diagnose))
|
||||
return false;
|
||||
|
||||
// C++11 [class.ctor]p5, C++11 [class.dtor]p5:
|
||||
@ -7377,7 +7412,7 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
|
||||
// -- for all of the non-static data members of its class that are of class
|
||||
// type (or array thereof), each such class has a trivial [default
|
||||
// constructor or destructor]
|
||||
if (!checkTrivialClassMembers(*this, RD, CSM, ConstArg, Diagnose))
|
||||
if (!checkTrivialClassMembers(*this, RD, CSM, ConstArg, TAH, Diagnose))
|
||||
return false;
|
||||
|
||||
// C++11 [class.dtor]p5:
|
||||
@ -7559,6 +7594,50 @@ void Sema::DiagnoseHiddenVirtualMethods(CXXMethodDecl *MD) {
|
||||
}
|
||||
}
|
||||
|
||||
void Sema::checkIllFormedTrivialABIStruct(CXXRecordDecl &RD) {
|
||||
auto PrintDiagAndRemoveAttr = [&]() {
|
||||
// No diagnostics if this is a template instantiation.
|
||||
if (!isTemplateInstantiation(RD.getTemplateSpecializationKind()))
|
||||
Diag(RD.getAttr<TrivialABIAttr>()->getLocation(),
|
||||
diag::ext_cannot_use_trivial_abi) << &RD;
|
||||
RD.dropAttr<TrivialABIAttr>();
|
||||
};
|
||||
|
||||
// Ill-formed if the struct has virtual functions.
|
||||
if (RD.isPolymorphic()) {
|
||||
PrintDiagAndRemoveAttr();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &B : RD.bases()) {
|
||||
// Ill-formed if the base class is non-trivial for the purpose of calls or a
|
||||
// virtual base.
|
||||
if ((!B.getType()->isDependentType() &&
|
||||
!B.getType()->getAsCXXRecordDecl()->canPassInRegisters()) ||
|
||||
B.isVirtual()) {
|
||||
PrintDiagAndRemoveAttr();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto *FD : RD.fields()) {
|
||||
// Ill-formed if the field is an ObjectiveC pointer or of a type that is
|
||||
// non-trivial for the purpose of calls.
|
||||
QualType FT = FD->getType();
|
||||
if (FT.getObjCLifetime() == Qualifiers::OCL_Weak) {
|
||||
PrintDiagAndRemoveAttr();
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *RT = FT->getBaseElementTypeUnsafe()->getAs<RecordType>())
|
||||
if (!RT->isDependentType() &&
|
||||
!cast<CXXRecordDecl>(RT->getDecl())->canPassInRegisters()) {
|
||||
PrintDiagAndRemoveAttr();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
|
||||
Decl *TagDecl,
|
||||
SourceLocation LBrac,
|
||||
@ -7577,12 +7656,17 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
|
||||
l->getName();
|
||||
}
|
||||
|
||||
// See if trivial_abi has to be dropped.
|
||||
auto *RD = dyn_cast<CXXRecordDecl>(TagDecl);
|
||||
if (RD && RD->hasAttr<TrivialABIAttr>())
|
||||
checkIllFormedTrivialABIStruct(*RD);
|
||||
|
||||
ActOnFields(S, RLoc, TagDecl, llvm::makeArrayRef(
|
||||
// strict aliasing violation!
|
||||
reinterpret_cast<Decl**>(FieldCollector->getCurFields()),
|
||||
FieldCollector->getCurNumFields()), LBrac, RBrac, AttrList);
|
||||
|
||||
CheckCompletedCXXClass(dyn_cast_or_null<CXXRecordDecl>(TagDecl));
|
||||
CheckCompletedCXXClass(RD);
|
||||
}
|
||||
|
||||
/// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared
|
||||
@ -10732,6 +10816,8 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
|
||||
// We don't need to use SpecialMemberIsTrivial here; triviality for
|
||||
// destructors is easy to compute.
|
||||
Destructor->setTrivial(ClassDecl->hasTrivialDestructor());
|
||||
Destructor->setTrivialForCall(ClassDecl->hasAttr<TrivialABIAttr>() ||
|
||||
ClassDecl->hasTrivialDestructorForCall());
|
||||
|
||||
// Note that we have declared this destructor.
|
||||
++ASTContext::NumImplicitDestructorsDeclared;
|
||||
@ -12035,9 +12121,16 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
|
||||
CopyConstructor->setParams(FromParam);
|
||||
|
||||
CopyConstructor->setTrivial(
|
||||
ClassDecl->needsOverloadResolutionForCopyConstructor()
|
||||
? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor)
|
||||
: ClassDecl->hasTrivialCopyConstructor());
|
||||
ClassDecl->needsOverloadResolutionForCopyConstructor()
|
||||
? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor)
|
||||
: ClassDecl->hasTrivialCopyConstructor());
|
||||
|
||||
CopyConstructor->setTrivialForCall(
|
||||
ClassDecl->hasAttr<TrivialABIAttr>() ||
|
||||
(ClassDecl->needsOverloadResolutionForCopyConstructor()
|
||||
? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor,
|
||||
TAH_ConsiderTrivialABI)
|
||||
: ClassDecl->hasTrivialCopyConstructorForCall()));
|
||||
|
||||
// Note that we have declared this constructor.
|
||||
++ASTContext::NumImplicitCopyConstructorsDeclared;
|
||||
@ -12158,9 +12251,16 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(
|
||||
MoveConstructor->setParams(FromParam);
|
||||
|
||||
MoveConstructor->setTrivial(
|
||||
ClassDecl->needsOverloadResolutionForMoveConstructor()
|
||||
? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor)
|
||||
: ClassDecl->hasTrivialMoveConstructor());
|
||||
ClassDecl->needsOverloadResolutionForMoveConstructor()
|
||||
? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor)
|
||||
: ClassDecl->hasTrivialMoveConstructor());
|
||||
|
||||
MoveConstructor->setTrivialForCall(
|
||||
ClassDecl->hasAttr<TrivialABIAttr>() ||
|
||||
(ClassDecl->needsOverloadResolutionForMoveConstructor()
|
||||
? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor,
|
||||
TAH_ConsiderTrivialABI)
|
||||
: ClassDecl->hasTrivialMoveConstructorForCall()));
|
||||
|
||||
// Note that we have declared this constructor.
|
||||
++ASTContext::NumImplicitMoveConstructorsDeclared;
|
||||
|
@ -2108,6 +2108,10 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
|
||||
}
|
||||
}
|
||||
|
||||
// See if trivial_abi has to be dropped.
|
||||
if (Instantiation && Instantiation->hasAttr<TrivialABIAttr>())
|
||||
checkIllFormedTrivialABIStruct(*Instantiation);
|
||||
|
||||
// Finish checking fields.
|
||||
ActOnFields(nullptr, Instantiation->getLocation(), Instantiation, Fields,
|
||||
SourceLocation(), SourceLocation(), nullptr);
|
||||
|
@ -7794,7 +7794,8 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
|
||||
diag::note_non_literal_user_provided_dtor :
|
||||
diag::note_non_literal_nontrivial_dtor) << RD;
|
||||
if (!Dtor->isUserProvided())
|
||||
SpecialMemberIsTrivial(Dtor, CXXDestructor, /*Diagnose*/true);
|
||||
SpecialMemberIsTrivial(Dtor, CXXDestructor, TAH_IgnoreTrivialABI,
|
||||
/*Diagnose*/true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -786,6 +786,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
|
||||
FD->HasWrittenPrototype = Record.readInt();
|
||||
FD->IsDeleted = Record.readInt();
|
||||
FD->IsTrivial = Record.readInt();
|
||||
FD->IsTrivialForCall = Record.readInt();
|
||||
FD->IsDefaulted = Record.readInt();
|
||||
FD->IsExplicitlyDefaulted = Record.readInt();
|
||||
FD->HasImplicitReturnZero = Record.readInt();
|
||||
@ -1577,7 +1578,9 @@ void ASTDeclReader::ReadCXXDefinitionData(
|
||||
Data.DefaultedMoveAssignmentIsDeleted = Record.readInt();
|
||||
Data.DefaultedDestructorIsDeleted = Record.readInt();
|
||||
Data.HasTrivialSpecialMembers = Record.readInt();
|
||||
Data.HasTrivialSpecialMembersForCall = Record.readInt();
|
||||
Data.DeclaredNonTrivialSpecialMembers = Record.readInt();
|
||||
Data.DeclaredNonTrivialSpecialMembersForCall = Record.readInt();
|
||||
Data.HasIrrelevantDestructor = Record.readInt();
|
||||
Data.HasConstexprNonCopyMoveConstructor = Record.readInt();
|
||||
Data.HasDefaultedDefaultConstructor = Record.readInt();
|
||||
@ -1715,7 +1718,9 @@ void ASTDeclReader::MergeDefinitionData(
|
||||
MATCH_FIELD(DefaultedMoveAssignmentIsDeleted)
|
||||
MATCH_FIELD(DefaultedDestructorIsDeleted)
|
||||
OR_FIELD(HasTrivialSpecialMembers)
|
||||
OR_FIELD(HasTrivialSpecialMembersForCall)
|
||||
OR_FIELD(DeclaredNonTrivialSpecialMembers)
|
||||
OR_FIELD(DeclaredNonTrivialSpecialMembersForCall)
|
||||
MATCH_FIELD(HasIrrelevantDestructor)
|
||||
OR_FIELD(HasConstexprNonCopyMoveConstructor)
|
||||
OR_FIELD(HasDefaultedDefaultConstructor)
|
||||
|
@ -6009,7 +6009,9 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
|
||||
Record->push_back(Data.DefaultedMoveAssignmentIsDeleted);
|
||||
Record->push_back(Data.DefaultedDestructorIsDeleted);
|
||||
Record->push_back(Data.HasTrivialSpecialMembers);
|
||||
Record->push_back(Data.HasTrivialSpecialMembersForCall);
|
||||
Record->push_back(Data.DeclaredNonTrivialSpecialMembers);
|
||||
Record->push_back(Data.DeclaredNonTrivialSpecialMembersForCall);
|
||||
Record->push_back(Data.HasIrrelevantDestructor);
|
||||
Record->push_back(Data.HasConstexprNonCopyMoveConstructor);
|
||||
Record->push_back(Data.HasDefaultedDefaultConstructor);
|
||||
|
@ -528,6 +528,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
|
||||
Record.push_back(D->HasWrittenPrototype);
|
||||
Record.push_back(D->IsDeleted);
|
||||
Record.push_back(D->IsTrivial);
|
||||
Record.push_back(D->IsTrivialForCall);
|
||||
Record.push_back(D->IsDefaulted);
|
||||
Record.push_back(D->IsExplicitlyDefaulted);
|
||||
Record.push_back(D->HasImplicitReturnZero);
|
||||
@ -2067,6 +2068,7 @@ void ASTWriter::WriteDeclAbbrevs() {
|
||||
Abv->Add(BitCodeAbbrevOp(1)); // HasWrittenProto
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Deleted
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Trivial
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // TrivialForCall
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Defaulted
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ExplicitlyDefaulted
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ImplicitReturnZero
|
||||
|
239
clang/test/CodeGenCXX/trivial_abi.cpp
Normal file
239
clang/test/CodeGenCXX/trivial_abi.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fcxx-exceptions -fexceptions -fclang-abi-compat=4.0 -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
// CHECK: %[[STRUCT_SMALL:.*]] = type { i32* }
|
||||
// CHECK: %[[STRUCT_LARGE:.*]] = type { i32*, [128 x i32] }
|
||||
// CHECK: %[[STRUCT_TRIVIAL:.*]] = type { i32 }
|
||||
// CHECK: %[[STRUCT_NONTRIVIAL:.*]] = type { i32 }
|
||||
|
||||
struct __attribute__((trivial_abi)) Small {
|
||||
int *p;
|
||||
Small();
|
||||
~Small();
|
||||
Small(const Small &) noexcept;
|
||||
Small &operator=(const Small &);
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) Large {
|
||||
int *p;
|
||||
int a[128];
|
||||
Large();
|
||||
~Large();
|
||||
Large(const Large &) noexcept;
|
||||
Large &operator=(const Large &);
|
||||
};
|
||||
|
||||
struct Trivial {
|
||||
int a;
|
||||
};
|
||||
|
||||
struct NonTrivial {
|
||||
NonTrivial();
|
||||
~NonTrivial();
|
||||
int a;
|
||||
};
|
||||
|
||||
struct HasTrivial {
|
||||
Small s;
|
||||
Trivial m;
|
||||
};
|
||||
|
||||
struct HasNonTrivial {
|
||||
Small s;
|
||||
NonTrivial m;
|
||||
};
|
||||
|
||||
// CHECK: define void @_Z14testParamSmall5Small(i64 %[[A_COERCE:.*]])
|
||||
// CHECK: %[[A:.*]] = alloca %[[STRUCT_SMALL]], align 8
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[A]], i32 0, i32 0
|
||||
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[A_COERCE]] to i32*
|
||||
// CHECK: store i32* %[[COERCE_VAL_IP]], i32** %[[COERCE_DIVE]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallD1Ev(%[[STRUCT_SMALL]]* %[[A]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
void testParamSmall(Small a) noexcept {
|
||||
}
|
||||
|
||||
// CHECK: define i64 @_Z15testReturnSmallv()
|
||||
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallC1Ev(%[[STRUCT_SMALL]]* %[[RETVAL]])
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[RETVAL]], i32 0, i32 0
|
||||
// CHECK: %[[V0:.*]] = load i32*, i32** %[[COERCE_DIVE]], align 8
|
||||
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i32* %[[V0]] to i64
|
||||
// CHECK: ret i64 %[[COERCE_VAL_PI]]
|
||||
// CHECK: }
|
||||
|
||||
Small testReturnSmall() {
|
||||
Small t;
|
||||
return t;
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z14testCallSmall0v()
|
||||
// CHECK: %[[T:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8
|
||||
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SMALL]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallC1Ev(%[[STRUCT_SMALL]]* %[[T]])
|
||||
// CHECK: %[[CALL1:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallC1ERKS_(%[[STRUCT_SMALL]]* %[[AGG_TMP]], %[[STRUCT_SMALL]]* dereferenceable(8) %[[T]])
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[AGG_TMP]], i32 0, i32 0
|
||||
// CHECK: %[[V0:.*]] = load i32*, i32** %[[COERCE_DIVE]], align 8
|
||||
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i32* %[[V0]] to i64
|
||||
// CHECK: call void @_Z14testParamSmall5Small(i64 %[[COERCE_VAL_PI]])
|
||||
// CHECK: %[[CALL2:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallD1Ev(%[[STRUCT_SMALL]]* %[[T]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
void testCallSmall0() {
|
||||
Small t;
|
||||
testParamSmall(t);
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z14testCallSmall1v()
|
||||
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call i64 @_Z15testReturnSmallv()
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[AGG_TMP]], i32 0, i32 0
|
||||
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to i32*
|
||||
// CHECK: store i32* %[[COERCE_VAL_IP]], i32** %[[COERCE_DIVE]], align 8
|
||||
// CHECK: %[[COERCE_DIVE1:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[AGG_TMP]], i32 0, i32 0
|
||||
// CHECK: %[[V0:.*]] = load i32*, i32** %[[COERCE_DIVE1]], align 8
|
||||
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i32* %[[V0]] to i64
|
||||
// CHECK: call void @_Z14testParamSmall5Small(i64 %[[COERCE_VAL_PI]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
void testCallSmall1() {
|
||||
testParamSmall(testReturnSmall());
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z16testIgnoredSmallv()
|
||||
// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call i64 @_Z15testReturnSmallv()
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[AGG_TMP_ENSURED]], i32 0, i32 0
|
||||
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to i32*
|
||||
// CHECK: store i32* %[[COERCE_VAL_IP]], i32** %[[COERCE_DIVE]], align 8
|
||||
// CHECK: %[[CALL1:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallD1Ev(%[[STRUCT_SMALL]]* %[[AGG_TMP_ENSURED]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
void testIgnoredSmall() {
|
||||
testReturnSmall();
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z14testParamLarge5Large(%[[STRUCT_LARGE:.*]]* %[[A:.*]])
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeD1Ev(%[[STRUCT_LARGE]]* %[[A]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
void testParamLarge(Large a) noexcept {
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z15testReturnLargev(%[[STRUCT_LARGE:.*]]* noalias sret %[[AGG_RESULT:.*]])
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeC1Ev(%[[STRUCT_LARGE]]* %[[AGG_RESULT]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
Large testReturnLarge() {
|
||||
Large t;
|
||||
return t;
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z14testCallLarge0v()
|
||||
// CHECK: %[[T:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8
|
||||
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeC1Ev(%[[STRUCT_LARGE]]* %[[T]])
|
||||
// CHECK: %[[CALL1:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeC1ERKS_(%[[STRUCT_LARGE]]* %[[AGG_TMP]], %[[STRUCT_LARGE]]* dereferenceable(520) %[[T]])
|
||||
// CHECK: call void @_Z14testParamLarge5Large(%[[STRUCT_LARGE]]* %[[AGG_TMP]])
|
||||
// CHECK: %[[CALL2:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeD1Ev(%[[STRUCT_LARGE]]* %[[T]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
void testCallLarge0() {
|
||||
Large t;
|
||||
testParamLarge(t);
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z14testCallLarge1v()
|
||||
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8
|
||||
// CHECK: call void @_Z15testReturnLargev(%[[STRUCT_LARGE]]* sret %[[AGG_TMP]])
|
||||
// CHECK: call void @_Z14testParamLarge5Large(%[[STRUCT_LARGE]]* %[[AGG_TMP]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
void testCallLarge1() {
|
||||
testParamLarge(testReturnLarge());
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z16testIgnoredLargev()
|
||||
// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8
|
||||
// CHECK: call void @_Z15testReturnLargev(%[[STRUCT_LARGE]]* sret %[[AGG_TMP_ENSURED]])
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeD1Ev(%[[STRUCT_LARGE]]* %[[AGG_TMP_ENSURED]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
void testIgnoredLarge() {
|
||||
testReturnLarge();
|
||||
}
|
||||
|
||||
// CHECK: define i64 @_Z20testReturnHasTrivialv()
|
||||
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_TRIVIAL:.*]], align 4
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_TRIVIAL]], %[[STRUCT_TRIVIAL]]* %[[RETVAL]], i32 0, i32 0
|
||||
// CHECK: %[[V0:.*]] = load i32, i32* %[[COERCE_DIVE]], align 4
|
||||
// CHECK: %[[COERCE_VAL_II:.*]] = zext i32 %[[V0]] to i64
|
||||
// CHECK: ret i64 %[[COERCE_VAL_II]]
|
||||
// CHECK: }
|
||||
|
||||
Trivial testReturnHasTrivial() {
|
||||
Trivial t;
|
||||
return t;
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z23testReturnHasNonTrivialv(%[[STRUCT_NONTRIVIAL:.*]]* noalias sret %[[AGG_RESULT:.*]])
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_NONTRIVIAL]]* @_ZN10NonTrivialC1Ev(%[[STRUCT_NONTRIVIAL]]* %[[AGG_RESULT]])
|
||||
// CHECK: ret void
|
||||
// CHECK: }
|
||||
|
||||
NonTrivial testReturnHasNonTrivial() {
|
||||
NonTrivial t;
|
||||
return t;
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z18testExceptionSmallv()
|
||||
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SMALL]], align 8
|
||||
// CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_SMALL]], align 8
|
||||
// CHECK: call %[[STRUCT_SMALL]]* @_ZN5SmallC1Ev(%[[STRUCT_SMALL]]* %[[AGG_TMP]])
|
||||
// CHECK: invoke %[[STRUCT_SMALL]]* @_ZN5SmallC1Ev(%[[STRUCT_SMALL]]* %[[AGG_TMP1]])
|
||||
|
||||
// CHECK: call void @_Z20calleeExceptionSmall5SmallS_(i64 %{{.*}}, i64 %{{.*}})
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
// CHECK: landingpad { i8*, i32 }
|
||||
// CHECK: call %[[STRUCT_SMALL]]* @_ZN5SmallD1Ev(%[[STRUCT_SMALL]]* %[[AGG_TMP]])
|
||||
// CHECK: br
|
||||
|
||||
// CHECK: resume { i8*, i32 }
|
||||
|
||||
void calleeExceptionSmall(Small, Small);
|
||||
|
||||
void testExceptionSmall() {
|
||||
calleeExceptionSmall(Small(), Small());
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z18testExceptionLargev()
|
||||
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE]], align 8
|
||||
// CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_LARGE]], align 8
|
||||
// CHECK: call %[[STRUCT_LARGE]]* @_ZN5LargeC1Ev(%[[STRUCT_LARGE]]* %[[AGG_TMP]])
|
||||
// CHECK: invoke %[[STRUCT_LARGE]]* @_ZN5LargeC1Ev(%[[STRUCT_LARGE]]* %[[AGG_TMP1]])
|
||||
|
||||
// CHECK: call void @_Z20calleeExceptionLarge5LargeS_(%[[STRUCT_LARGE]]* %[[AGG_TMP]], %[[STRUCT_LARGE]]* %[[AGG_TMP1]])
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
// CHECK: landingpad { i8*, i32 }
|
||||
// CHECK: call %[[STRUCT_LARGE]]* @_ZN5LargeD1Ev(%[[STRUCT_LARGE]]* %[[AGG_TMP]])
|
||||
// CHECK: br
|
||||
|
||||
// CHECK: resume { i8*, i32 }
|
||||
|
||||
void calleeExceptionLarge(Large, Large);
|
||||
|
||||
void testExceptionLarge() {
|
||||
calleeExceptionLarge(Large(), Large());
|
||||
}
|
104
clang/test/CodeGenObjCXX/trivial_abi.mm
Normal file
104
clang/test/CodeGenObjCXX/trivial_abi.mm
Normal file
@ -0,0 +1,104 @@
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -emit-llvm -o - %s | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -fclang-abi-compat=4.0 -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
// CHECK: %[[STRUCT_STRONGWEAK:.*]] = type { i8*, i8* }
|
||||
// CHECK: %[[STRUCT_STRONG:.*]] = type { i8* }
|
||||
// CHECK: %[[STRUCT_S:.*]] = type { i8* }
|
||||
|
||||
struct __attribute__((trivial_abi)) StrongWeak {
|
||||
id fstrong;
|
||||
__weak id fweak;
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) Strong {
|
||||
id fstrong;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct __attribute__((trivial_abi)) S {
|
||||
T a;
|
||||
};
|
||||
|
||||
// CHECK: define void @_Z19testParamStrongWeak10StrongWeak(%[[STRUCT_STRONGWEAK]]* %{{.*}})
|
||||
// CHECK-NOT: call
|
||||
// CHECK: ret void
|
||||
|
||||
void testParamStrongWeak(StrongWeak a) {
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z18testCallStrongWeakP10StrongWeak(%[[STRUCT_STRONGWEAK]]* %[[A:.*]])
|
||||
// CHECK: %[[A_ADDR:.*]] = alloca %[[STRUCT_STRONGWEAK]]*, align 8
|
||||
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONGWEAK]], align 8
|
||||
// CHECK: store %[[STRUCT_STRONGWEAK]]* %[[A]], %[[STRUCT_STRONGWEAK]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGWEAK]]*, %[[STRUCT_STRONGWEAK]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakC1ERKS_(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]], %[[STRUCT_STRONGWEAK]]* dereferenceable(16) %[[V0]])
|
||||
// CHECK: call void @_Z19testParamStrongWeak10StrongWeak(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]])
|
||||
// CHECK: %[[CALL1:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakD1Ev(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]])
|
||||
// CHECK: ret void
|
||||
|
||||
void testCallStrongWeak(StrongWeak *a) {
|
||||
testParamStrongWeak(*a);
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z20testReturnStrongWeakP10StrongWeak(%[[STRUCT_STRONGWEAK:.*]]* noalias sret %[[AGG_RESULT:.*]], %[[STRUCT_STRONGWEAK]]* %[[A:.*]])
|
||||
// CHECK: %[[A_ADDR:.*]] = alloca %[[STRUCT_STRONGWEAK]]*, align 8
|
||||
// CHECK: store %[[STRUCT_STRONGWEAK]]* %[[A]], %[[STRUCT_STRONGWEAK]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGWEAK]]*, %[[STRUCT_STRONGWEAK]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakC1ERKS_(%[[STRUCT_STRONGWEAK]]* %[[AGG_RESULT]], %[[STRUCT_STRONGWEAK]]* dereferenceable(16) %[[V0]])
|
||||
// CHECK: ret void
|
||||
|
||||
StrongWeak testReturnStrongWeak(StrongWeak *a) {
|
||||
return *a;
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z15testParamStrong6Strong(i64 %[[A_COERCE:.*]])
|
||||
// CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONG]], align 8
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[A]], i32 0, i32 0
|
||||
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[A_COERCE]] to i8*
|
||||
// CHECK: store i8* %[[COERCE_VAL_IP]], i8** %[[COERCE_DIVE]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_STRONG]]* @_ZN6StrongD1Ev(%[[STRUCT_STRONG]]* %[[A]])
|
||||
// CHECK: ret void
|
||||
|
||||
// CHECK: define linkonce_odr %[[STRUCT_STRONG]]* @_ZN6StrongD1Ev(
|
||||
|
||||
void testParamStrong(Strong a) {
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z14testCallStrongP6Strong(%[[STRUCT_STRONG]]* %[[A:.*]])
|
||||
// CHECK: %[[A_ADDR:.*]] = alloca %[[STRUCT_STRONG]]*, align 8
|
||||
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONG]], align 8
|
||||
// CHECK: store %[[STRUCT_STRONG]]* %[[A]], %[[STRUCT_STRONG]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONG]]*, %[[STRUCT_STRONG]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_STRONG]]* @_ZN6StrongC1ERKS_(%[[STRUCT_STRONG]]* %[[AGG_TMP]], %[[STRUCT_STRONG]]* dereferenceable(8) %[[V0]])
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[AGG_TMP]], i32 0, i32 0
|
||||
// CHECK: %[[V1:.*]] = load i8*, i8** %[[COERCE_DIVE]], align 8
|
||||
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i8* %[[V1]] to i64
|
||||
// CHECK: call void @_Z15testParamStrong6Strong(i64 %[[COERCE_VAL_PI]])
|
||||
// CHECK: ret void
|
||||
|
||||
void testCallStrong(Strong *a) {
|
||||
testParamStrong(*a);
|
||||
}
|
||||
|
||||
// CHECK: define i64 @_Z16testReturnStrongP6Strong(%[[STRUCT_STRONG]]* %[[A:.*]])
|
||||
// CHECK: entry:
|
||||
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_STRONG]], align 8
|
||||
// CHECK: %[[A_ADDR:.*]] = alloca %[[STRUCT_STRONG]]*, align 8
|
||||
// CHECK: store %[[STRUCT_STRONG]]* %[[A]], %[[STRUCT_STRONG]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONG]]*, %[[STRUCT_STRONG]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_STRONG]]* @_ZN6StrongC1ERKS_(%[[STRUCT_STRONG]]* %[[RETVAL]], %[[STRUCT_STRONG]]* dereferenceable(8) %[[V0]])
|
||||
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[RETVAL]], i32 0, i32 0
|
||||
// CHECK: %[[V1:.*]] = load i8*, i8** %[[COERCE_DIVE]], align 8
|
||||
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i8* %[[V1]] to i64
|
||||
// CHECK: ret i64 %[[COERCE_VAL_PI]]
|
||||
|
||||
Strong testReturnStrong(Strong *a) {
|
||||
return *a;
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z21testParamWeakTemplate1SIU6__weakP11objc_objectE(%[[STRUCT_S]]* %{{.*}})
|
||||
// CHECK-NOT: call
|
||||
// CHECK: ret void
|
||||
|
||||
void testParamWeakTemplate(S<__weak id> a) {
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
// The number of supported attributes should never go down!
|
||||
|
||||
// CHECK: #pragma clang attribute supports 66 attributes:
|
||||
// CHECK: #pragma clang attribute supports 67 attributes:
|
||||
// CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function)
|
||||
@ -66,6 +66,7 @@
|
||||
// CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local)
|
||||
// CHECK-NEXT: Target (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
|
||||
// CHECK-NEXT: TrivialABI (SubjectMatchRule_record)
|
||||
// CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType)
|
||||
// CHECK-NEXT: XRayInstrument (SubjectMatchRule_function, SubjectMatchRule_objc_method)
|
||||
// CHECK-NEXT: XRayLogArgs (SubjectMatchRule_function, SubjectMatchRule_objc_method)
|
||||
|
93
clang/test/SemaObjCXX/attr-trivial-abi.mm
Normal file
93
clang/test/SemaObjCXX/attr-trivial-abi.mm
Normal file
@ -0,0 +1,93 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -fobjc-runtime-has-weak -fobjc-weak -fobjc-arc -fsyntax-only -verify %s
|
||||
|
||||
void __attribute__((trivial_abi)) foo(); // expected-warning {{'trivial_abi' attribute only applies to classes}}
|
||||
|
||||
struct [[clang::trivial_abi]] S0 {
|
||||
int a;
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S1 {
|
||||
int a;
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S2 { // expected-warning {{'trivial_abi' cannot be applied to 'S2'}}
|
||||
__weak id a;
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S3 { // expected-warning {{'trivial_abi' cannot be applied to 'S3'}}
|
||||
virtual void m();
|
||||
};
|
||||
|
||||
struct S4 {
|
||||
int a;
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S5 : public virtual S4 { // expected-warning {{'trivial_abi' cannot be applied to 'S5'}}
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S9 : public S4 {
|
||||
};
|
||||
|
||||
struct S6 {
|
||||
__weak id a;
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S12 { // expected-warning {{'trivial_abi' cannot be applied to 'S12'}}
|
||||
__weak id a;
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S13 { // expected-warning {{'trivial_abi' cannot be applied to 'S13'}}
|
||||
__weak id a[2];
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S7 { // expected-warning {{'trivial_abi' cannot be applied to 'S7'}}
|
||||
S6 a;
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi)) S11 { // expected-warning {{'trivial_abi' cannot be applied to 'S11'}}
|
||||
S6 a[2];
|
||||
};
|
||||
|
||||
struct __attribute__((trivial_abi(1))) S8 { // expected-error {{'trivial_abi' attribute takes no arguments}}
|
||||
int a;
|
||||
};
|
||||
|
||||
// Do not warn when 'trivial_abi' is used to annotate a template class.
|
||||
template<class T>
|
||||
struct __attribute__((trivial_abi)) S10 {
|
||||
T p;
|
||||
};
|
||||
|
||||
S10<int *> p1;
|
||||
S10<__weak id> p2;
|
||||
|
||||
template<>
|
||||
struct __attribute__((trivial_abi)) S10<id> { // expected-warning {{'trivial_abi' cannot be applied to 'S10<id>'}}
|
||||
__weak id a;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct S14 {
|
||||
T a;
|
||||
__weak id b;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct __attribute__((trivial_abi)) S15 : S14<T> {
|
||||
};
|
||||
|
||||
S15<int> s15;
|
||||
|
||||
template<class T>
|
||||
struct __attribute__((trivial_abi)) S16 {
|
||||
S14<T> a;
|
||||
};
|
||||
|
||||
S16<int> s16;
|
||||
|
||||
template<class T>
|
||||
struct __attribute__((trivial_abi)) S17 { // expected-warning {{'trivial_abi' cannot be applied to 'S17'}}
|
||||
__weak id a;
|
||||
};
|
||||
|
||||
S17<int> s17;
|
Loading…
x
Reference in New Issue
Block a user