Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -1732,6 +1732,12 @@ 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; @@ -1842,7 +1848,8 @@ 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), @@ -2007,6 +2014,9 @@ 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; } Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -437,6 +437,11 @@ /// 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 @@ /// 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 @@ 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 @@ !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 @@ (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 @@ !(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 @@ 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 { @@ -1797,6 +1843,8 @@ /// 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; Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1155,6 +1155,12 @@ let Documentation = [LayoutVersionDocs]; } +def TrivialABI : InheritableAttr { + let Spellings = [Clang<"trivial_abi">]; + let Subjects = SubjectList<[CXXRecord]>; + let Documentation = [TrivialABIDocs]; +} + def MaxFieldAlignment : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly. let Spellings = []; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -2224,6 +2224,48 @@ }]; } +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"; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2881,6 +2881,9 @@ def err_invalid_attribute_on_virtual_function : Error< "%0 attribute cannot be applied to virtual functions">; +def warn_cannot_use_trivial_abi : Warning< + "'trivial_abi' cannot be applied to %0">, InGroup; + // Availability attribute def warn_availability_unknown_platform : Warning< "unknown platform %0 in availability macro">, InGroup; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2236,7 +2236,7 @@ bool CheckNontrivialField(FieldDecl *FD); void DiagnoseNontrivial(const CXXRecordDecl *Record, CXXSpecialMember CSM); bool SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM, - bool Diagnose = false); + bool ForCall = false, bool Diagnose = false); CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD); void ActOnLastBitfield(SourceLocation DeclStart, SmallVectorImpl &AllIvarDecls); Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -88,7 +88,9 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ if (!BaseClassDecl->hasTrivialDestructor()) data().HasTrivialSpecialMembers &= ~SMF_Destructor; + if (!BaseClassDecl->hasTrivialDestructorForCall()) + data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor; + if (!BaseClassDecl->hasIrrelevantDestructor()) data().HasIrrelevantDestructor = false; @@ -539,6 +552,7 @@ // 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 @@ // 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,27 @@ // 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 { 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 +799,7 @@ 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 +927,19 @@ // 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 +956,8 @@ 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 +1140,23 @@ data().DeclaredNonTrivialSpecialMembers |= SMKind; } +void CXXRecordDecl::setTrivialForCallFlags(CXXMethodDecl *D) { + unsigned SMKind = 0; + + if (CXXConstructorDecl *Constructor = dyn_cast(D)) { + if (Constructor->isCopyConstructor()) + SMKind = SMF_CopyConstructor; + else if (Constructor->isMoveConstructor()) + SMKind = SMF_MoveConstructor; + } else if (isa(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()) Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -3143,7 +3143,6 @@ static void deactivateArgCleanupsBeforeCall(CodeGenFunction &CGF, const CallArgList &CallArgs) { - assert(CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()); ArrayRef Cleanups = CallArgs.getCleanupsToDeactivate(); // Iterate in reverse to increase the likelihood of popping the cleanup. @@ -3496,12 +3495,17 @@ } bool HasAggregateEvalKind = hasAggregateEvaluationKind(type); + bool TypeDestructedInCallee = false; + if (const auto *RD = type->getAsCXXRecordDecl()) + TypeDestructedInCallee = + RD->canPassInRegisters() && RD->hasNonTrivialDestructor(); // 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 && + CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) || + TypeDestructedInCallee) { // If we're using inalloca, use the argument memory. Otherwise, use a // temporary. AggValueSlot Slot; @@ -3512,8 +3516,9 @@ const CXXRecordDecl *RD = type->getAsCXXRecordDecl(); bool DestroyedInCallee = - RD && RD->hasNonTrivialDestructor() && - CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default; + (RD && RD->hasNonTrivialDestructor() && + CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default) || + TypeDestructedInCallee; if (DestroyedInCallee) Slot.setExternallyDestructed(); Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -1800,7 +1800,6 @@ Address DeclPtr = Address::invalid(); bool DoStore = false; - bool IsScalar = hasScalarEvaluationKind(Ty); // If we already have a pointer to the argument, reuse the input pointer. if (Arg.isIndirect()) { DeclPtr = Arg.getIndirectAddress(); @@ -1809,16 +1808,6 @@ llvm::Type *IRTy = ConvertTypeForMem(Ty)->getPointerTo(AS); if (DeclPtr.getType() != IRTy) DeclPtr = Builder.CreateBitCast(DeclPtr, IRTy, D.getName()); - - // Push a destructor cleanup for this parameter if the ABI requires it. - // Don't push a cleanup in a thunk for a method that will also emit a - // cleanup. - if (!IsScalar && !CurFuncIsThunk && - getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) { - const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); - if (RD && RD->hasNonTrivialDestructor()) - pushDestroy(QualType::DK_cxx_destructor, DeclPtr, Ty); - } } else { // Otherwise, create a temporary to hold the value. DeclPtr = CreateMemTemp(Ty, getContext().getDeclAlign(&D), @@ -1826,6 +1815,24 @@ DoStore = true; } + // Push a destructor cleanup for this parameter if the ABI requires it. + // Don't push a cleanup in a thunk for a method that will also emit a + // cleanup. + bool IsScalar = hasScalarEvaluationKind(Ty); + bool TypeDestructedInCallee = false; + if (const auto *RD = Ty->getAsCXXRecordDecl()) + TypeDestructedInCallee = + RD->canPassInRegisters() && RD->hasNonTrivialDestructor(); + + if (!IsScalar && !CurFuncIsThunk && + ((Arg.isIndirect() && + getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) || + TypeDestructedInCallee)) { + const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); + if (RD && RD->hasNonTrivialDestructor()) + pushDestroy(QualType::DK_cxx_destructor, DeclPtr, Ty); + } + llvm::Value *ArgVal = (DoStore ? Arg.getDirectValue() : nullptr); LValue lv = MakeAddrLValue(DeclPtr, Ty); Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -68,8 +68,8 @@ 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); } Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -844,7 +844,7 @@ // If the copy ctor has not yet been declared, we can read its triviality // off the AST. if (!RD->defaultedCopyConstructorIsDeleted() && - RD->hasTrivialCopyConstructor()) + RD->hasTrivialCopyConstructorForCall()) return RAA_Default; } else { // Otherwise, we need to find the copy constructor(s) and ask. Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -6436,6 +6436,9 @@ case AttributeList::AT_LayoutVersion: handleLayoutVersion(S, D, Attr); break; + case AttributeList::AT_TrivialABI: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_MSNoVTable: handleSimpleAttribute(S, D, Attr); break; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -5768,20 +5768,20 @@ 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()) { @@ -5794,7 +5794,7 @@ else if (!isa(MD)) continue; - if (!MD->isTrivial()) + if (!MD->isTrivialForCall()) return false; } @@ -5878,6 +5878,13 @@ } } + // Set HasTrivialSpecialMemberForCall if the record has attribute + // "trivial_abi". + bool HasTrivialABI = Record->hasAttr(); + + if (HasTrivialABI) + Record->setHasTrivialSpecialMemberForCall(); + bool HasMethodWithOverrideControl = false, HasOverridingMethodWithoutOverrideControl = false; if (!Record->isDependentType()) { @@ -5900,12 +5907,22 @@ 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, true)); + 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()) { if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && @@ -7017,9 +7034,13 @@ /// /// 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, bool ForCall, + CXXMethodDecl **Selected) { if (Selected) *Selected = nullptr; @@ -7060,7 +7081,8 @@ // C++11 [class.dtor]p5: // A destructor is trivial if: // - all the direct [subobjects] have trivial destructors - if (RD->hasTrivialDestructor()) + if (RD->hasTrivialDestructor() || + (ForCall && RD->hasTrivialDestructorForCall())) return true; if (Selected) { @@ -7075,7 +7097,8 @@ // 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() || + (ForCall && 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. @@ -7128,6 +7151,10 @@ // not supposed to! if (Selected) *Selected = SMOR.getMethod(); + + if (ForCall && + (CSM == Sema::CXXCopyConstructor || CSM == Sema::CXXMoveConstructor)) + return SMOR.getMethod()->isTrivialForCall(); return SMOR.getMethod()->isTrivial(); } @@ -7165,7 +7192,7 @@ static bool checkTrivialSubobjectCall(Sema &S, SourceLocation SubobjLoc, QualType SubType, bool ConstRHS, Sema::CXXSpecialMember CSM, - TrivialSubobjectKind Kind, + TrivialSubobjectKind Kind, bool ForCall, bool Diagnose) { CXXRecordDecl *SubRD = SubType->getAsCXXRecordDecl(); if (!SubRD) @@ -7173,7 +7200,8 @@ CXXMethodDecl *Selected; if (findTrivialSpecialMember(S, SubRD, CSM, SubType.getCVRQualifiers(), - ConstRHS, Diagnose ? &Selected : nullptr)) + ConstRHS, ForCall, + Diagnose ? &Selected : nullptr)) return true; if (Diagnose) { @@ -7203,7 +7231,7 @@ << Kind << SubType.getUnqualifiedType() << CSM; // Explain why the defaulted or deleted special member isn't trivial. - S.SpecialMemberIsTrivial(Selected, CSM, Diagnose); + S.SpecialMemberIsTrivial(Selected, CSM, false, Diagnose); } } @@ -7214,7 +7242,8 @@ /// trivial. static bool checkTrivialClassMembers(Sema &S, CXXRecordDecl *RD, Sema::CXXSpecialMember CSM, - bool ConstArg, bool Diagnose) { + bool ConstArg, bool ForCall, + bool Diagnose) { for (const auto *FI : RD->fields()) { if (FI->isInvalidDecl() || FI->isUnnamedBitfield()) continue; @@ -7224,7 +7253,7 @@ // Pretend anonymous struct or union members are members of this class. if (FI->isAnonymousStructOrUnion()) { if (!checkTrivialClassMembers(S, FieldType->getAsCXXRecordDecl(), - CSM, ConstArg, Diagnose)) + CSM, ConstArg, ForCall, Diagnose)) return false; continue; } @@ -7252,7 +7281,7 @@ bool ConstRHS = ConstArg && !FI->isMutable(); if (!checkTrivialSubobjectCall(S, FI->getLocation(), FieldType, ConstRHS, - CSM, TSK_Field, Diagnose)) + CSM, TSK_Field, ForCall, Diagnose)) return false; } @@ -7266,14 +7295,14 @@ bool ConstArg = (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment); checkTrivialSubobjectCall(*this, RD->getLocation(), Ty, ConstArg, CSM, - TSK_CompleteObject, /*Diagnose*/true); + TSK_CompleteObject, false, /*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) { + bool ForCall, bool Diagnose) { assert(!MD->isUserProvided() && CSM != CXXInvalid && "not special enough"); CXXRecordDecl *RD = MD->getParent(); @@ -7350,7 +7379,8 @@ // destructors] for (const auto &BI : RD->bases()) if (!checkTrivialSubobjectCall(*this, BI.getLocStart(), BI.getType(), - ConstArg, CSM, TSK_BaseClass, Diagnose)) + ConstArg, CSM, TSK_BaseClass, ForCall, + Diagnose)) return false; // C++11 [class.ctor]p5, C++11 [class.dtor]p5: @@ -7365,7 +7395,7 @@ // -- 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, ForCall, Diagnose)) return false; // C++11 [class.dtor]p5: @@ -7547,6 +7577,47 @@ } } +static void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD, Sema &S) { + auto PrintDiagAndRemoveAttr = [&]() { + S.Diag(RD.getAttr()->getLocation(), + diag::warn_cannot_use_trivial_abi) << &RD; + RD.dropAttr(); + }; + + // Ill-formed if the struct has virtual functions. + if (RD.isPolymorphic()) { + PrintDiagAndRemoveAttr(); + return; + } + + for (const auto &B : RD.bases()) { + CXXRecordDecl *BaseClassDecl + = cast(B.getType()->getAs()->getDecl()); + // Ill-formed if the base class is non-trivial for the purpose of calls or a + // virtual base. + if (!BaseClassDecl->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()) + if (!cast(RT->getDecl())->canPassInRegisters()) { + PrintDiagAndRemoveAttr(); + return; + } + } +} + void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, Decl *TagDecl, SourceLocation LBrac, @@ -7565,12 +7636,17 @@ l->getName(); } + // See if trivial_abi has to be dropped. + auto *RD = dyn_cast_or_null(TagDecl); + if (RD && RD->hasAttr()) + checkIllFormedTrivialABIStruct(*RD, *this); + ActOnFields(S, RLoc, TagDecl, llvm::makeArrayRef( // strict aliasing violation! reinterpret_cast(FieldCollector->getCurFields()), FieldCollector->getCurNumFields()), LBrac, RBrac, AttrList); - CheckCompletedCXXClass(dyn_cast_or_null(TagDecl)); + CheckCompletedCXXClass(RD); } /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared @@ -10720,6 +10796,8 @@ // We don't need to use SpecialMemberIsTrivial here; triviality for // destructors is easy to compute. Destructor->setTrivial(ClassDecl->hasTrivialDestructor()); + Destructor->setTrivialForCall(ClassDecl->hasAttr() || + ClassDecl->hasTrivialDestructorForCall()); // Note that we have declared this destructor. ++ASTContext::NumImplicitDestructorsDeclared; @@ -12021,9 +12099,15 @@ CopyConstructor->setParams(FromParam); CopyConstructor->setTrivial( - ClassDecl->needsOverloadResolutionForCopyConstructor() - ? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor) - : ClassDecl->hasTrivialCopyConstructor()); + ClassDecl->needsOverloadResolutionForCopyConstructor() + ? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor) + : ClassDecl->hasTrivialCopyConstructor()); + + CopyConstructor->setTrivialForCall( + ClassDecl->hasAttr() || + (ClassDecl->needsOverloadResolutionForCopyConstructor() + ? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor, true) + : ClassDecl->hasTrivialCopyConstructorForCall())); // Note that we have declared this constructor. ++ASTContext::NumImplicitCopyConstructorsDeclared; @@ -12144,9 +12228,15 @@ MoveConstructor->setParams(FromParam); MoveConstructor->setTrivial( - ClassDecl->needsOverloadResolutionForMoveConstructor() - ? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor) - : ClassDecl->hasTrivialMoveConstructor()); + ClassDecl->needsOverloadResolutionForMoveConstructor() + ? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor) + : ClassDecl->hasTrivialMoveConstructor()); + + MoveConstructor->setTrivialForCall( + ClassDecl->hasAttr() || + (ClassDecl->needsOverloadResolutionForMoveConstructor() + ? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor, true) + : ClassDecl->hasTrivialMoveConstructorForCall())); // Note that we have declared this constructor. ++ASTContext::NumImplicitMoveConstructorsDeclared; Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -7790,7 +7790,7 @@ 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, false, /*Diagnose*/true); } return true; Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -786,6 +786,7 @@ 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(); @@ -1575,7 +1576,9 @@ 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(); @@ -1713,7 +1716,9 @@ 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) Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5986,7 +5986,9 @@ 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); Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -528,6 +528,7 @@ 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); @@ -2065,6 +2066,7 @@ 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 Index: test/CodeGenCXX/trivial_abi.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/trivial_abi.cpp @@ -0,0 +1,197 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -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 &); + Small &operator=(const Small &); +}; + +struct __attribute__((trivial_abi)) Large { + int *p; + int a[128]; + Large(); + ~Large(); + Large(const Large &); + 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) { +} + +// 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) { +} + +// 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; +} Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -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) Index: test/SemaObjCXX/attr-trivial-abi.mm =================================================================== --- /dev/null +++ test/SemaObjCXX/attr-trivial-abi.mm @@ -0,0 +1,55 @@ +// 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)) 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; +}; + +template +struct __attribute__((trivial_abi)) S10 { + T p; +}; + +S10 p1; + +// Do not warn when 'trivial_abi' is used to annotate a template class. +S10<__weak id> p2;