Skip to content

Commit

Permalink
Add support for attribute 'trivial_abi'.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ahatanaka committed Feb 5, 2018
1 parent 02f6845 commit 02914dc
Show file tree
Hide file tree
Showing 27 changed files with 853 additions and 81 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -1179,6 +1179,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
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;
Expand Down
12 changes: 11 additions & 1 deletion clang/include/clang/AST/Decl.h
Expand Up @@ -1731,6 +1731,12 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
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;
Expand Down Expand Up @@ -1845,7 +1851,8 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
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),
Expand Down Expand Up @@ -2010,6 +2017,9 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
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; }
Expand Down
55 changes: 55 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Expand Up @@ -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.
///
Expand All @@ -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;

Expand Down Expand Up @@ -1349,20 +1360,35 @@ class CXXRecordDecl : public RecordDecl {
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 {
return data().DeclaredNonTrivialSpecialMembers & SMF_CopyConstructor ||
!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 {
return hasMoveConstructor() &&
(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 {
Expand All @@ -1371,6 +1397,13 @@ class CXXRecordDecl : public RecordDecl {
!(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 {
Expand Down Expand Up @@ -1405,12 +1438,25 @@ class CXXRecordDecl : public RecordDecl {
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 {
Expand Down Expand Up @@ -1440,6 +1486,13 @@ class CXXRecordDecl : public RecordDecl {
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 {
Expand Down Expand Up @@ -1797,6 +1850,8 @@ class CXXRecordDecl : public RecordDecl {
/// 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;

Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/Type.h
Expand Up @@ -808,6 +808,11 @@ class QualType {
/// 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.

Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Expand Up @@ -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 = [];
Expand Down
42 changes: 42 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Expand Up @@ -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";
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -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>;
Expand Down
15 changes: 15 additions & 0 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -2236,7 +2236,17 @@ class Sema {

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,
Expand Down Expand Up @@ -5796,6 +5806,11 @@ class Sema {
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,
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Expand Up @@ -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 {
Expand Down

0 comments on commit 02914dc

Please sign in to comment.