/// This is true if this struct ends with a flexible
/// array member (e.g. int X[]) or if this union contains a struct that does.
/// If so, this cannot be contained in arrays or other structs as a member.
- bool HasFlexibleArrayMember : 1;
+ unsigned HasFlexibleArrayMember : 1;
/// Whether this is the type of an anonymous struct or union.
- bool AnonymousStructOrUnion : 1;
+ unsigned AnonymousStructOrUnion : 1;
/// This is true if this struct has at least one member
/// containing an Objective-C object pointer type.
- bool HasObjectMember : 1;
+ unsigned HasObjectMember : 1;
/// This is true if struct has at least one member of
/// 'volatile' type.
- bool HasVolatileMember : 1;
+ unsigned HasVolatileMember : 1;
/// Whether the field declarations of this record have been loaded
/// from external storage. To avoid unnecessary deserialization of
/// methods/nested types we allow deserialization of just the fields
/// when needed.
- mutable bool LoadedFieldsFromExternalStorage : 1;
+ mutable unsigned LoadedFieldsFromExternalStorage : 1;
/// Basic properties of non-trivial C structs.
- bool NonTrivialToPrimitiveDefaultInitialize : 1;
- bool NonTrivialToPrimitiveCopy : 1;
- bool NonTrivialToPrimitiveDestroy : 1;
+ unsigned NonTrivialToPrimitiveDefaultInitialize : 1;
+ unsigned NonTrivialToPrimitiveCopy : 1;
+ unsigned NonTrivialToPrimitiveDestroy : 1;
- /// Indicates whether this struct is destroyed in the callee. This flag is
- /// meaningless when Microsoft ABI is used since parameters are always
- /// destroyed in the callee.
+ /// Indicates whether this struct is destroyed in the callee.
///
/// Please note that MSVC won't merge adjacent bitfields if they don't have
/// the same type.
- uint8_t ParamDestroyedInCallee : 1;
+ unsigned ParamDestroyedInCallee : 1;
/// Represents the way this type is passed to a function.
- uint8_t ArgPassingRestrictions : 2;
+ unsigned ArgPassingRestrictions : 2;
protected:
RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC,
}
}
-/// Determine whether a type would be destructed in the callee if it had a
-/// non-trivial destructor. The rules here are based on C++ [class.temporary]p3,
-/// which determines whether a struct can be passed to or returned from
-/// functions in registers.
-static bool paramCanBeDestroyedInCallee(Sema &S, CXXRecordDecl *D,
- TargetInfo::CallingConvKind CCK) {
+/// Determine whether a type is permitted to be passed or returned in
+/// registers, per C++ [class.temporary]p3.
+static bool canPassInRegisters(Sema &S, CXXRecordDecl *D,
+ TargetInfo::CallingConvKind CCK) {
if (D->isDependentType() || D->isInvalidDecl())
return false;
return !D->hasNonTrivialDestructorForCall() &&
!D->hasNonTrivialCopyConstructorForCall();
+ if (CCK == TargetInfo::CCK_MicrosoftX86_64) {
+ 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.
+ //
+ // Note: This permits classes with non-trivial copy or move ctors to be
+ // passed in registers, so long as they *also* have a trivial copy ctor,
+ // which is non-conforming.
+ if (D->needsImplicitCopyConstructor()) {
+ if (!D->defaultedCopyConstructorIsDeleted()) {
+ if (D->hasTrivialCopyConstructor())
+ CopyCtorIsTrivial = true;
+ if (D->hasTrivialCopyConstructorForCall())
+ CopyCtorIsTrivialForCall = true;
+ }
+ } else {
+ for (const CXXConstructorDecl *CD : D->ctors()) {
+ if (CD->isCopyConstructor() && !CD->isDeleted()) {
+ if (CD->isTrivial())
+ CopyCtorIsTrivial = true;
+ if (CD->isTrivialForCall())
+ CopyCtorIsTrivialForCall = true;
+ }
+ }
+ }
+
+ if (D->needsImplicitDestructor()) {
+ if (!D->defaultedDestructorIsDeleted() &&
+ D->hasTrivialDestructorForCall())
+ DtorIsTrivialForCall = true;
+ } else if (const auto *DD = D->getDestructor()) {
+ if (!DD->isDeleted() && DD->isTrivialForCall())
+ DtorIsTrivialForCall = true;
+ }
+
+ // If the copy ctor and dtor are both trivial-for-calls, pass direct.
+ if (CopyCtorIsTrivialForCall && DtorIsTrivialForCall)
+ return true;
+
+ // 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 &&
+ S.getASTContext().getTypeSize(D->getTypeForDecl()) <= 64)
+ return true;
+ return false;
+ }
+
// Per C++ [class.temporary]p3, the relevant condition is:
// each copy constructor, move constructor, and destructor of X is
// either trivial or deleted, and X has at least one non-deleted copy
return HasNonDeletedCopyOrMove;
}
-static RecordDecl::ArgPassingKind
-computeArgPassingRestrictions(bool DestroyedInCallee, const CXXRecordDecl *RD,
- TargetInfo::CallingConvKind CCK, Sema &S) {
- if (RD->isDependentType() || RD->isInvalidDecl())
- return RecordDecl::APK_CanPassInRegs;
-
- // The param cannot be passed in registers if ArgPassingRestrictions is set to
- // APK_CanNeverPassInRegs.
- if (RD->getArgPassingRestrictions() == RecordDecl::APK_CanNeverPassInRegs)
- return RecordDecl::APK_CanNeverPassInRegs;
-
- if (CCK != TargetInfo::CCK_MicrosoftX86_64)
- return DestroyedInCallee ? RecordDecl::APK_CanPassInRegs
- : RecordDecl::APK_CannotPassInRegs;
-
- 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.
- //
- // Note: This permits classes with non-trivial copy or move ctors to be
- // passed in registers, so long as they *also* have a trivial copy ctor,
- // which is non-conforming.
- if (RD->needsImplicitCopyConstructor()) {
- if (!RD->defaultedCopyConstructorIsDeleted()) {
- if (RD->hasTrivialCopyConstructor())
- CopyCtorIsTrivial = true;
- if (RD->hasTrivialCopyConstructorForCall())
- CopyCtorIsTrivialForCall = true;
- }
- } else {
- for (const CXXConstructorDecl *CD : RD->ctors()) {
- if (CD->isCopyConstructor() && !CD->isDeleted()) {
- if (CD->isTrivial())
- CopyCtorIsTrivial = true;
- if (CD->isTrivialForCall())
- CopyCtorIsTrivialForCall = true;
- }
- }
- }
-
- 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 RecordDecl::APK_CanPassInRegs;
-
- // 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 &&
- S.getASTContext().getTypeSize(RD->getTypeForDecl()) <= 64)
- return RecordDecl::APK_CanPassInRegs;
- return RecordDecl::APK_CannotPassInRegs;
-}
-
/// Perform semantic checks on a class definition that has been
/// completing, introducing implicitly-declared members, checking for
/// abstract types, etc.
Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4;
TargetInfo::CallingConvKind CCK =
Context.getTargetInfo().getCallingConvKind(ClangABICompat4);
- bool DestroyedInCallee = paramCanBeDestroyedInCallee(*this, Record, CCK);
+ bool CanPass = canPassInRegisters(*this, Record, CCK);
- if (Record->hasNonTrivialDestructor())
- Record->setParamDestroyedInCallee(DestroyedInCallee);
-
- Record->setArgPassingRestrictions(
- computeArgPassingRestrictions(DestroyedInCallee, Record, CCK, *this));
+ // Do not change ArgPassingRestrictions if it has already been set to
+ // APK_CanNeverPassInRegs.
+ if (Record->getArgPassingRestrictions() != RecordDecl::APK_CanNeverPassInRegs)
+ Record->setArgPassingRestrictions(CanPass
+ ? RecordDecl::APK_CanPassInRegs
+ : RecordDecl::APK_CannotPassInRegs);
+
+ // If canPassInRegisters returns true despite the record having a non-trivial
+ // destructor, the record is destructed in the callee. This happens only when
+ // the record or one of its subobjects has a field annotated with trivial_abi
+ // or a field qualified with ObjC __strong/__weak.
+ if (Context.getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee())
+ Record->setParamDestroyedInCallee(true);
+ else if (Record->hasNonTrivialDestructor())
+ Record->setParamDestroyedInCallee(CanPass);
}
/// Look up the special member function that would be called by a special