diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 98 |
2 files changed, 97 insertions, 5 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 6cfc5012f67..a4ae32e460c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15461,8 +15461,10 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, QualType::PrimitiveCopyKind PCK = FT.isNonTrivialToPrimitiveCopy(); if (PCK != QualType::PCK_Trivial && PCK != QualType::PCK_VolatileTrivial) Record->setNonTrivialToPrimitiveCopy(true); - if (FT.isDestructedType()) + if (FT.isDestructedType()) { Record->setNonTrivialToPrimitiveDestroy(true); + Record->setParamDestroyedInCallee(true); + } if (!FT.canPassInRegisters()) Record->setCanPassInRegisters(false); } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index a56869f6234..f8a3d9444aa 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -5791,12 +5791,21 @@ static void DefineImplicitSpecialMember(Sema &S, CXXMethodDecl *MD, } } -/// Determine whether a type is permitted to be passed or returned in -/// registers, per C++ [class.temporary]p3. -static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) { +/// 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) { if (D->isDependentType() || D->isInvalidDecl()) return false; + // Clang <= 4 used the pre-C++11 rule, which ignores move operations. + // The PS4 platform ABI follows the behavior of Clang 3.2. + if (CCK == TargetInfo::CCK_ClangABI4OrPS4) + return !D->hasNonTrivialDestructorForCall() && + !D->hasNonTrivialCopyConstructorForCall(); + // 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 @@ -5838,6 +5847,77 @@ static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) { return HasNonDeletedCopyOrMove; } +static bool computeCanPassInRegister(bool DestroyedInCallee, + const CXXRecordDecl *RD, + TargetInfo::CallingConvKind CCK, + Sema &S) { + if (RD->isDependentType() || RD->isInvalidDecl()) + return true; + + // The param cannot be passed in registers if CanPassInRegisters is already + // set to false. + if (!RD->canPassInRegisters()) + return false; + + if (CCK != TargetInfo::CCK_MicrosoftX86_64) + return DestroyedInCallee; + + 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 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(RD->getTypeForDecl()) <= 64) + return true; + return false; +} + /// \brief Perform semantic checks on a class definition that has been /// completing, introducing implicitly-declared members, checking for /// abstract types, etc. @@ -6001,7 +6081,17 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { checkClassLevelDLLAttribute(Record); - Record->setCanPassInRegisters(computeCanPassInRegisters(*this, Record)); + bool ClangABICompat4 = + Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4; + TargetInfo::CallingConvKind CCK = + Context.getTargetInfo().getCallingConvKind(ClangABICompat4); + bool DestroyedInCallee = paramCanBeDestroyedInCallee(*this, Record, CCK); + + if (Record->hasNonTrivialDestructor()) + Record->setParamDestroyedInCallee(DestroyedInCallee); + + Record->setCanPassInRegisters( + computeCanPassInRegister(DestroyedInCallee, Record, CCK, *this)); } /// Look up the special member function that would be called by a special |