forked from OSchip/llvm-project
[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
This commit is contained in:
parent
aac23d7881
commit
fcbe17c6be
|
@ -3559,6 +3559,11 @@ class RecordDecl : public TagDecl {
|
|||
/// pass an object of this class.
|
||||
bool CanPassInRegisters : 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.
|
||||
bool ParamDestroyedInCallee : 1;
|
||||
|
||||
protected:
|
||||
RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC,
|
||||
SourceLocation StartLoc, SourceLocation IdLoc,
|
||||
|
@ -3654,6 +3659,14 @@ public:
|
|||
CanPassInRegisters = CanPass;
|
||||
}
|
||||
|
||||
bool isParamDestroyedInCallee() const {
|
||||
return ParamDestroyedInCallee;
|
||||
}
|
||||
|
||||
void setParamDestroyedInCallee(bool V) {
|
||||
ParamDestroyedInCallee = V;
|
||||
}
|
||||
|
||||
/// \brief Determines whether this declaration represents the
|
||||
/// injected class name.
|
||||
///
|
||||
|
|
|
@ -1468,13 +1468,6 @@ public:
|
|||
return data().HasIrrelevantDestructor;
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
|
|
@ -808,11 +808,6 @@ public:
|
|||
/// 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.
|
||||
|
||||
|
|
|
@ -284,6 +284,10 @@ LANGOPT(XRayAlwaysEmitCustomEvents, 1, 0,
|
|||
BENIGN_LANGOPT(AllowEditorPlaceholders, 1, 0,
|
||||
"allow editor placeholders in source")
|
||||
|
||||
ENUM_LANGOPT(ClangABICompat, ClangABI, 4, ClangABI::Latest,
|
||||
"version of Clang that we should attempt to be ABI-compatible "
|
||||
"with")
|
||||
|
||||
#undef LANGOPT
|
||||
#undef COMPATIBLE_LANGOPT
|
||||
#undef BENIGN_LANGOPT
|
||||
|
|
|
@ -102,6 +102,23 @@ public:
|
|||
MSVC2015 = 19
|
||||
};
|
||||
|
||||
/// Clang versions with different platform ABI conformance.
|
||||
enum class ClangABI {
|
||||
/// Attempt to be ABI-compatible with code generated by Clang 3.8.x
|
||||
/// (SVN r257626). This causes <1 x long long> to be passed in an
|
||||
/// integer register instead of an SSE register on x64_64.
|
||||
Ver3_8,
|
||||
|
||||
/// Attempt to be ABI-compatible with code generated by Clang 4.0.x
|
||||
/// (SVN r291814). This causes move operations to be ignored when
|
||||
/// determining whether a class type can be passed or returned directly.
|
||||
Ver4,
|
||||
|
||||
/// Conform to the underlying platform's C and C++ ABIs as closely
|
||||
/// as we can.
|
||||
Latest
|
||||
};
|
||||
|
||||
enum FPContractModeKind {
|
||||
// Form fused FP ops only where result will not be affected.
|
||||
FPC_Off,
|
||||
|
|
|
@ -1053,6 +1053,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
enum CallingConvKind {
|
||||
CCK_Default,
|
||||
CCK_ClangABI4OrPS4,
|
||||
CCK_MicrosoftX86_64
|
||||
};
|
||||
|
||||
virtual CallingConvKind getCallingConvKind(bool ClangABICompat4) const;
|
||||
|
||||
/// Controls if __builtin_longjmp / __builtin_setjmp can be lowered to
|
||||
/// llvm.eh.sjlj.longjmp / llvm.eh.sjlj.setjmp.
|
||||
virtual bool hasSjLjLowering() const {
|
||||
|
|
|
@ -138,9 +138,6 @@ ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 2, Legacy)
|
|||
CODEGENOPT(OmitLeafFramePointer , 1, 0) ///< Set when -momit-leaf-frame-pointer is
|
||||
///< enabled.
|
||||
|
||||
/// A version of Clang that we should attempt to be ABI-compatible with.
|
||||
ENUM_CODEGENOPT(ClangABICompat, ClangABI, 4, ClangABI::Latest)
|
||||
|
||||
VALUE_CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified.
|
||||
VALUE_CODEGENOPT(OptimizeSize, 2, 0) ///< If -Os (==1) or -Oz (==2) is specified.
|
||||
|
||||
|
|
|
@ -2643,9 +2643,11 @@ void ASTContext::adjustExceptionSpec(
|
|||
}
|
||||
|
||||
bool ASTContext::isParamDestroyedInCallee(QualType T) const {
|
||||
return getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee() ||
|
||||
T.hasTrivialABIOverride() ||
|
||||
T.isDestructedType() == QualType::DK_nontrivial_c_struct;
|
||||
if (getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee())
|
||||
return true;
|
||||
if (const auto *RT = T->getBaseElementTypeUnsafe()->getAs<RecordType>())
|
||||
return RT->getDecl()->isParamDestroyedInCallee();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// getComplexType - Return the uniqued reference to the type for a complex
|
||||
|
|
|
@ -3951,7 +3951,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C,
|
|||
LoadedFieldsFromExternalStorage(false),
|
||||
NonTrivialToPrimitiveDefaultInitialize(false),
|
||||
NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false),
|
||||
CanPassInRegisters(true) {
|
||||
CanPassInRegisters(true), ParamDestroyedInCallee(false) {
|
||||
assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
|
||||
}
|
||||
|
||||
|
|
|
@ -801,7 +801,17 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
struct DefinitionData &Data = data();
|
||||
Data.PlainOldData = false;
|
||||
Data.HasTrivialSpecialMembers = 0;
|
||||
Data.HasTrivialSpecialMembersForCall = 0;
|
||||
|
||||
// __strong or __weak fields do not make special functions non-trivial
|
||||
// for the purpose of calls.
|
||||
Qualifiers::ObjCLifetime LT = T.getQualifiers().getObjCLifetime();
|
||||
if (LT != Qualifiers::OCL_Strong && LT != Qualifiers::OCL_Weak)
|
||||
data().HasTrivialSpecialMembersForCall = 0;
|
||||
|
||||
// Structs with __weak fields should never be passed directly.
|
||||
if (LT == Qualifiers::OCL_Weak)
|
||||
setCanPassInRegisters(false);
|
||||
|
||||
Data.HasIrrelevantDestructor = false;
|
||||
} else if (!Context.getLangOpts().ObjCAutoRefCount) {
|
||||
setHasObjectMember(true);
|
||||
|
|
|
@ -2195,12 +2195,6 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool QualType::hasTrivialABIOverride() const {
|
||||
if (const auto *RD = getTypePtr()->getAsCXXRecordDecl())
|
||||
return RD->hasTrivialABIOverride();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
|
||||
return !Context.getLangOpts().ObjCAutoRefCount &&
|
||||
Context.getLangOpts().ObjCWeak &&
|
||||
|
|
|
@ -357,6 +357,14 @@ bool TargetInfo::initFeatureMap(
|
|||
return true;
|
||||
}
|
||||
|
||||
TargetInfo::CallingConvKind
|
||||
TargetInfo::getCallingConvKind(bool ClangABICompat4) const {
|
||||
if (getCXXABI() != TargetCXXABI::Microsoft &&
|
||||
(ClangABICompat4 || getTriple().getOS() == llvm::Triple::PS4))
|
||||
return CCK_ClangABI4OrPS4;
|
||||
return CCK_Default;
|
||||
}
|
||||
|
||||
LangAS TargetInfo::getOpenCLTypeAddrSpace(OpenCLTypeKind TK) const {
|
||||
switch (TK) {
|
||||
case OCLTK_Image:
|
||||
|
|
|
@ -728,6 +728,11 @@ public:
|
|||
Builder.defineMacro("_M_X64", "100");
|
||||
Builder.defineMacro("_M_AMD64", "100");
|
||||
}
|
||||
|
||||
TargetInfo::CallingConvKind
|
||||
getCallingConvKind(bool ClangABICompat4) const override {
|
||||
return CCK_MicrosoftX86_64;
|
||||
}
|
||||
};
|
||||
|
||||
// x86-64 MinGW target
|
||||
|
|
|
@ -3540,24 +3540,13 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
|
|||
else
|
||||
Slot = CreateAggTemp(type, "agg.tmp");
|
||||
|
||||
bool DestroyedInCallee = true, NeedsEHCleanup = true;
|
||||
if (const auto *RD = type->getAsCXXRecordDecl()) {
|
||||
DestroyedInCallee =
|
||||
RD && RD->hasNonTrivialDestructor() &&
|
||||
(CGM.getCXXABI().getRecordArgABI(RD) != CGCXXABI::RAA_Default ||
|
||||
RD->hasTrivialABIOverride());
|
||||
} else {
|
||||
NeedsEHCleanup = needsEHCleanup(type.isDestructedType());
|
||||
}
|
||||
|
||||
if (DestroyedInCallee)
|
||||
Slot.setExternallyDestructed();
|
||||
Slot.setExternallyDestructed();
|
||||
|
||||
EmitAggExpr(E, Slot);
|
||||
RValue RV = Slot.asRValue();
|
||||
args.add(RV, type);
|
||||
|
||||
if (DestroyedInCallee && NeedsEHCleanup) {
|
||||
if (type->getAsCXXRecordDecl() || needsEHCleanup(type.isDestructedType())) {
|
||||
// Create a no-op GEP between the placeholder and the cleanup so we can
|
||||
// RAUW it successfully. It also serves as a marker of the first
|
||||
// instruction where the cleanup is active.
|
||||
|
|
|
@ -63,13 +63,6 @@ public:
|
|||
bool classifyReturnType(CGFunctionInfo &FI) const override;
|
||||
|
||||
bool passClassIndirect(const CXXRecordDecl *RD) const {
|
||||
// Clang <= 4 used the pre-C++11 rule, which ignores move operations.
|
||||
// The PS4 platform ABI follows the behavior of Clang 3.2.
|
||||
if (CGM.getCodeGenOpts().getClangABICompat() <=
|
||||
CodeGenOptions::ClangABI::Ver4 ||
|
||||
CGM.getTriple().getOS() == llvm::Triple::PS4)
|
||||
return RD->hasNonTrivialDestructorForCall() ||
|
||||
RD->hasNonTrivialCopyConstructorForCall();
|
||||
return !canCopyArgument(RD);
|
||||
}
|
||||
|
||||
|
|
|
@ -829,60 +829,7 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
|
|||
return RAA_Default;
|
||||
|
||||
case llvm::Triple::x86_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 (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 RAA_Default;
|
||||
|
||||
// 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 &&
|
||||
getContext().getTypeSize(RD->getTypeForDecl()) <= 64)
|
||||
return RAA_Default;
|
||||
return RAA_Indirect;
|
||||
return !canCopyArgument(RD) ? RAA_Indirect : RAA_Default;
|
||||
}
|
||||
|
||||
llvm_unreachable("invalid enum");
|
||||
|
|
|
@ -2131,8 +2131,8 @@ class X86_64ABIInfo : public SwiftABIInfo {
|
|||
/// classify it as INTEGER (for compatibility with older clang compilers).
|
||||
bool classifyIntegerMMXAsSSE() const {
|
||||
// Clang <= 3.8 did not do this.
|
||||
if (getCodeGenOpts().getClangABICompat() <=
|
||||
CodeGenOptions::ClangABI::Ver3_8)
|
||||
if (getContext().getLangOpts().getClangABICompat() <=
|
||||
LangOptions::ClangABI::Ver3_8)
|
||||
return false;
|
||||
|
||||
const llvm::Triple &Triple = getTarget().getTriple();
|
||||
|
|
|
@ -633,33 +633,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
|||
if (!Opts.ProfileInstrumentUsePath.empty())
|
||||
setPGOUseInstrumentor(Opts, Opts.ProfileInstrumentUsePath);
|
||||
|
||||
if (Arg *A = Args.getLastArg(OPT_fclang_abi_compat_EQ)) {
|
||||
Opts.setClangABICompat(CodeGenOptions::ClangABI::Latest);
|
||||
|
||||
StringRef Ver = A->getValue();
|
||||
std::pair<StringRef, StringRef> VerParts = Ver.split('.');
|
||||
unsigned Major, Minor = 0;
|
||||
|
||||
// Check the version number is valid: either 3.x (0 <= x <= 9) or
|
||||
// y or y.0 (4 <= y <= current version).
|
||||
if (!VerParts.first.startswith("0") &&
|
||||
!VerParts.first.getAsInteger(10, Major) &&
|
||||
3 <= Major && Major <= CLANG_VERSION_MAJOR &&
|
||||
(Major == 3 ? VerParts.second.size() == 1 &&
|
||||
!VerParts.second.getAsInteger(10, Minor)
|
||||
: VerParts.first.size() == Ver.size() ||
|
||||
VerParts.second == "0")) {
|
||||
// Got a valid version number.
|
||||
if (Major == 3 && Minor <= 8)
|
||||
Opts.setClangABICompat(CodeGenOptions::ClangABI::Ver3_8);
|
||||
else if (Major <= 4)
|
||||
Opts.setClangABICompat(CodeGenOptions::ClangABI::Ver4);
|
||||
} else if (Ver != "latest") {
|
||||
Diags.Report(diag::err_drv_invalid_value)
|
||||
<< A->getAsString(Args) << A->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
Opts.CoverageMapping =
|
||||
Args.hasFlag(OPT_fcoverage_mapping, OPT_fno_coverage_mapping, false);
|
||||
Opts.DumpCoverageMapping = Args.hasArg(OPT_dump_coverage_mapping);
|
||||
|
@ -2670,6 +2643,33 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
|
|||
|
||||
// -fallow-editor-placeholders
|
||||
Opts.AllowEditorPlaceholders = Args.hasArg(OPT_fallow_editor_placeholders);
|
||||
|
||||
if (Arg *A = Args.getLastArg(OPT_fclang_abi_compat_EQ)) {
|
||||
Opts.setClangABICompat(LangOptions::ClangABI::Latest);
|
||||
|
||||
StringRef Ver = A->getValue();
|
||||
std::pair<StringRef, StringRef> VerParts = Ver.split('.');
|
||||
unsigned Major, Minor = 0;
|
||||
|
||||
// Check the version number is valid: either 3.x (0 <= x <= 9) or
|
||||
// y or y.0 (4 <= y <= current version).
|
||||
if (!VerParts.first.startswith("0") &&
|
||||
!VerParts.first.getAsInteger(10, Major) &&
|
||||
3 <= Major && Major <= CLANG_VERSION_MAJOR &&
|
||||
(Major == 3 ? VerParts.second.size() == 1 &&
|
||||
!VerParts.second.getAsInteger(10, Minor)
|
||||
: VerParts.first.size() == Ver.size() ||
|
||||
VerParts.second == "0")) {
|
||||
// Got a valid version number.
|
||||
if (Major == 3 && Minor <= 8)
|
||||
Opts.setClangABICompat(LangOptions::ClangABI::Ver3_8);
|
||||
else if (Major <= 4)
|
||||
Opts.setClangABICompat(LangOptions::ClangABI::Ver4);
|
||||
} else if (Ver != "latest") {
|
||||
Diags.Report(diag::err_drv_invalid_value)
|
||||
<< A->getAsString(Args) << A->getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -743,6 +743,7 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) {
|
|||
RD->setNonTrivialToPrimitiveCopy(Record.readInt());
|
||||
RD->setNonTrivialToPrimitiveDestroy(Record.readInt());
|
||||
RD->setCanPassInRegisters(Record.readInt());
|
||||
RD->setParamDestroyedInCallee(Record.readInt());
|
||||
return Redecl;
|
||||
}
|
||||
|
||||
|
@ -4109,6 +4110,7 @@ void ASTDeclReader::UpdateDecl(Decl *D,
|
|||
OldDD && (OldDD->Definition != RD ||
|
||||
!Reader.PendingFakeDefinitionData.count(OldDD));
|
||||
RD->setCanPassInRegisters(Record.readInt());
|
||||
RD->setParamDestroyedInCallee(Record.readInt());
|
||||
ReadCXXRecordDefinition(RD, /*Update*/true);
|
||||
|
||||
// Visible update is handled separately.
|
||||
|
|
|
@ -5194,6 +5194,7 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
|
|||
auto *RD = cast<CXXRecordDecl>(D);
|
||||
UpdatedDeclContexts.insert(RD->getPrimaryContext());
|
||||
Record.push_back(RD->canPassInRegisters());
|
||||
Record.push_back(RD->isParamDestroyedInCallee());
|
||||
Record.AddCXXDefinitionData(RD);
|
||||
Record.AddOffset(WriteDeclContextLexicalBlock(
|
||||
*Context, const_cast<CXXRecordDecl *>(RD)));
|
||||
|
|
|
@ -470,6 +470,7 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) {
|
|||
Record.push_back(D->isNonTrivialToPrimitiveCopy());
|
||||
Record.push_back(D->isNonTrivialToPrimitiveDestroy());
|
||||
Record.push_back(D->canPassInRegisters());
|
||||
Record.push_back(D->isParamDestroyedInCallee());
|
||||
|
||||
if (D->getDeclContext() == D->getLexicalDeclContext() &&
|
||||
!D->hasAttrs() &&
|
||||
|
@ -1912,6 +1913,8 @@ void ASTWriter::WriteDeclAbbrevs() {
|
|||
// isNonTrivialToPrimitiveDestroy
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // canPassInRegisters
|
||||
// isParamDestroyedInCallee
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
|
||||
|
||||
// DC
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset
|
||||
|
|
|
@ -172,12 +172,9 @@ void small_arg_with_dtor(SmallWithDtor s) {}
|
|||
void call_small_arg_with_dtor() {
|
||||
small_arg_with_dtor(SmallWithDtor());
|
||||
}
|
||||
// The temporary is copied, so it's destroyed in the caller as well as the
|
||||
// callee.
|
||||
// WIN64-LABEL: define dso_local void @"?call_small_arg_with_dtor@@YAXXZ"()
|
||||
// WIN64: call %struct.SmallWithDtor* @"??0SmallWithDtor@@QEAA@XZ"
|
||||
// WIN64: call void @"?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i32 %{{.*}})
|
||||
// WIN64: call void @"??1SmallWithDtor@@QEAA@XZ"
|
||||
// WIN64: ret void
|
||||
|
||||
// Test that references aren't destroyed in the callee.
|
||||
|
|
|
@ -31,6 +31,8 @@ void test_ObjCMember_copy_construct_destruct(ObjCMember m1) {
|
|||
void test_ObjCMember_copy_assign(ObjCMember m1, ObjCMember m2) {
|
||||
// CHECK: {{call.*_ZN10ObjCMemberaSERKS_}}
|
||||
m1 = m2;
|
||||
// CHECK-NEXT: call void @_ZN10ObjCMemberD1Ev(
|
||||
// CHECK-NEXT: call void @_ZN10ObjCMemberD1Ev(
|
||||
// CHECK-NEXT: ret void
|
||||
}
|
||||
|
||||
|
@ -58,6 +60,8 @@ void test_ObjCArrayMember_copy_construct_destruct(ObjCArrayMember m1) {
|
|||
void test_ObjCArrayMember_copy_assign(ObjCArrayMember m1, ObjCArrayMember m2) {
|
||||
// CHECK: {{call.*@_ZN15ObjCArrayMemberaSERKS_}}
|
||||
m1 = m2;
|
||||
// CHECK-NEXT: call void @_ZN15ObjCArrayMemberD1Ev(
|
||||
// CHECK-NEXT: call void @_ZN15ObjCArrayMemberD1Ev(
|
||||
// CHECK-NEXT: ret void
|
||||
}
|
||||
|
||||
|
@ -79,7 +83,8 @@ void test_ObjCBlockMember_default_construct_destruct() {
|
|||
void test_ObjCBlockMember_copy_construct_destruct(ObjCBlockMember m1) {
|
||||
// CHECK: call void @_ZN15ObjCBlockMemberC1ERKS_
|
||||
ObjCBlockMember m2 = m1;
|
||||
// CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev
|
||||
// CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev(
|
||||
// CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev(
|
||||
// CHECK-NEXT: ret void
|
||||
}
|
||||
|
||||
|
@ -87,6 +92,8 @@ void test_ObjCBlockMember_copy_construct_destruct(ObjCBlockMember m1) {
|
|||
void test_ObjCBlockMember_copy_assign(ObjCBlockMember m1, ObjCBlockMember m2) {
|
||||
// CHECK: {{call.*_ZN15ObjCBlockMemberaSERKS_}}
|
||||
m1 = m2;
|
||||
// CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev(
|
||||
// CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev(
|
||||
// CHECK-NEXT: ret void
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +1,60 @@
|
|||
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -emit-llvm -o - %s | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -fclang-abi-compat=4.0 -emit-llvm -o - %s | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -emit-llvm -o - -DTRIVIALABI %s | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fobjc-arc -fobjc-weak -fobjc-runtime-has-weak -fclang-abi-compat=4.0 -emit-llvm -o - -DTRIVIALABI %s | FileCheck %s
|
||||
|
||||
// Check that structs consisting solely of __strong or __weak pointer fields are
|
||||
// destructed in the callee function and structs consisting solely of __strong
|
||||
// pointer fields are passed directly.
|
||||
|
||||
// CHECK: %[[STRUCT_STRONGWEAK:.*]] = type { i8*, i8* }
|
||||
// CHECK: %[[STRUCT_STRONG:.*]] = type { i8* }
|
||||
// CHECK: %[[STRUCT_S:.*]] = type { i8* }
|
||||
// CHECK: %[[STRUCT_CONTAINSNONTRIVIAL:.*]] = type { %{{.*}}, i8* }
|
||||
|
||||
#ifdef TRIVIALABI
|
||||
struct __attribute__((trivial_abi)) StrongWeak {
|
||||
#else
|
||||
struct StrongWeak {
|
||||
#endif
|
||||
id fstrong;
|
||||
__weak id fweak;
|
||||
};
|
||||
|
||||
#ifdef TRIVIALABI
|
||||
struct __attribute__((trivial_abi)) Strong {
|
||||
#else
|
||||
struct Strong {
|
||||
#endif
|
||||
id fstrong;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
#ifdef TRIVIALABI
|
||||
struct __attribute__((trivial_abi)) S {
|
||||
#else
|
||||
struct S {
|
||||
#endif
|
||||
T a;
|
||||
};
|
||||
|
||||
struct NonTrivial {
|
||||
NonTrivial();
|
||||
NonTrivial(const NonTrivial &);
|
||||
~NonTrivial();
|
||||
int *a;
|
||||
};
|
||||
|
||||
// This struct is not passed directly nor destructed in the callee because f0
|
||||
// has type NonTrivial.
|
||||
struct ContainsNonTrivial {
|
||||
NonTrivial f0;
|
||||
id f1;
|
||||
};
|
||||
|
||||
// CHECK: define void @_Z19testParamStrongWeak10StrongWeak(%[[STRUCT_STRONGWEAK]]* %{{.*}})
|
||||
// CHECK-NOT: call
|
||||
// CHECK: ret void
|
||||
// CHECK: call %struct.StrongWeak* @_ZN10StrongWeakD1Ev(
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void testParamStrongWeak(StrongWeak a) {
|
||||
}
|
||||
|
@ -33,7 +66,7 @@ void testParamStrongWeak(StrongWeak a) {
|
|||
// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGWEAK]]*, %[[STRUCT_STRONGWEAK]]** %[[A_ADDR]], align 8
|
||||
// CHECK: %[[CALL:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakC1ERKS_(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]], %[[STRUCT_STRONGWEAK]]* dereferenceable(16) %[[V0]])
|
||||
// CHECK: call void @_Z19testParamStrongWeak10StrongWeak(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]])
|
||||
// CHECK: %[[CALL1:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakD1Ev(%[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]])
|
||||
// CHECK-NOT: call
|
||||
// CHECK: ret void
|
||||
|
||||
void testCallStrongWeak(StrongWeak *a) {
|
||||
|
@ -96,8 +129,23 @@ Strong testReturnStrong(Strong *a) {
|
|||
}
|
||||
|
||||
// CHECK: define void @_Z21testParamWeakTemplate1SIU6__weakP11objc_objectE(%[[STRUCT_S]]* %{{.*}})
|
||||
// CHECK-NOT: call
|
||||
// CHECK: ret void
|
||||
// CHECK: call %struct.S* @_ZN1SIU6__weakP11objc_objectED1Ev(
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void testParamWeakTemplate(S<__weak id> a) {
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z27testParamContainsNonTrivial18ContainsNonTrivial(%[[STRUCT_CONTAINSNONTRIVIAL]]* %{{.*}})
|
||||
// CHECK-NOT: call
|
||||
// CHECK: ret void
|
||||
|
||||
void testParamContainsNonTrivial(ContainsNonTrivial a) {
|
||||
}
|
||||
|
||||
// CHECK: define void @_Z26testCallContainsNonTrivialP18ContainsNonTrivial(
|
||||
// CHECK: call void @_Z27testParamContainsNonTrivial18ContainsNonTrivial(%[[STRUCT_CONTAINSNONTRIVIAL]]* %{{.*}})
|
||||
// CHECK: call %struct.ContainsNonTrivial* @_ZN18ContainsNonTrivialD1Ev(%[[STRUCT_CONTAINSNONTRIVIAL]]* %{{.*}})
|
||||
|
||||
void testCallContainsNonTrivial(ContainsNonTrivial *a) {
|
||||
testParamContainsNonTrivial(*a);
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -std=c++1z -fobjc-arc -o - %s | FileCheck %s
|
||||
|
||||
struct S0 {
|
||||
~S0();
|
||||
id f;
|
||||
};
|
||||
|
||||
struct S1 {
|
||||
S1();
|
||||
~S1();
|
||||
S1(S0);
|
||||
id f;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue