diff options
author | Akira Hatanaka <ahatanaka@apple.com> | 2018-03-28 21:13:14 +0000 |
---|---|---|
committer | Akira Hatanaka <ahatanaka@apple.com> | 2018-03-28 21:13:14 +0000 |
commit | fcbe17c6be86c87031822c1c01d8b43b6d142695 (patch) | |
tree | 701edbfeeae78c64ed7133dead94509fd4abcc04 /clang/lib/Sema/SemaDeclCXX.cpp | |
parent | aac23d78814d45039c0a8b3dbf204d5664df854c (diff) | |
download | bcm5719-llvm-fcbe17c6be86c87031822c1c01d8b43b6d142695.tar.gz bcm5719-llvm-fcbe17c6be86c87031822c1c01d8b43b6d142695.zip |
[ObjC++] Make parameter passing and function return compatible with ObjC
ObjC and ObjC++ pass non-trivial structs in a way that is incompatible
with each other. For example:
typedef struct {
id f0;
__weak id f1;
} S;
// this code is compiled in c++.
extern "C" {
void foo(S s);
}
void caller() {
// the caller passes the parameter indirectly and destructs it.
foo(S());
}
// this function is compiled in c.
// 'a' is passed directly and is destructed in the callee.
void foo(S a) {
}
This patch fixes the incompatibility by passing and returning structs
with __strong or weak fields using the C ABI in C++ mode. __strong and
__weak fields in a struct do not cause the struct to be destructed in
the caller and __strong fields do not cause the struct to be passed
indirectly.
Also, this patch fixes the microsoft ABI bug mentioned here:
https://reviews.llvm.org/D41039?id=128767#inline-364710
rdar://problem/38887866
Differential Revision: https://reviews.llvm.org/D44908
llvm-svn: 328731
Diffstat (limited to 'clang/lib/Sema/SemaDeclCXX.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 98 |
1 files changed, 94 insertions, 4 deletions
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 |