Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -3559,6 +3559,11 @@ /// 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 @@ CanPassInRegisters = CanPass; } + bool isParamDestroyedInCallee() const { + return ParamDestroyedInCallee; + } + + void setParamDestroyedInCallee(bool V) { + ParamDestroyedInCallee = V; + } + /// \brief Determines whether this declaration represents the /// injected class name. /// Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -467,6 +467,12 @@ /// constructor. unsigned HasDefaultedDefaultConstructor : 1; + /// Indicates that this class has a non-trivial primitive type (e.g., ObjC + /// __weak pointer) that forces the class to be passed indirectly. This flag + /// is used in CodeGen only when Microsoft ABI is used or clang ABI version + /// is 4.0 or older. + unsigned ForcePassIndirectly : 1; + /// \brief True if a defaulted default constructor for this class would /// be constexpr. unsigned DefaultedDefaultConstructorIsConstexpr : 1; @@ -1468,11 +1474,12 @@ 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(); + bool forcePassIndirectly() const { + return data().ForcePassIndirectly; + } + + void setForcePassIndirectly(bool Force) { + data().ForcePassIndirectly = Force; } /// \brief Determine whether this class has a non-literal or/ volatile type Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -808,11 +808,6 @@ /// 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. Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -2643,9 +2643,11 @@ } 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()) + return RT->getDecl()->isParamDestroyedInCallee(); + return false; } /// getComplexType - Return the uniqued reference to the type for a complex Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3932,7 +3932,7 @@ LoadedFieldsFromExternalStorage(false), NonTrivialToPrimitiveDefaultInitialize(false), NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false), - CanPassInRegisters(true) { + CanPassInRegisters(true), ParamDestroyedInCallee(false) { assert(classof(static_cast(this)) && "Invalid Kind!"); } Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -92,7 +92,7 @@ DeclaredNonTrivialSpecialMembers(0), DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true), HasConstexprNonCopyMoveConstructor(false), - HasDefaultedDefaultConstructor(false), + HasDefaultedDefaultConstructor(false), ForcePassIndirectly(false), DefaultedDefaultConstructorIsConstexpr(true), HasConstexprDefaultConstructor(false), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), @@ -801,7 +801,17 @@ 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) + setForcePassIndirectly(true); + Data.HasIrrelevantDestructor = false; } else if (!Context.getLangOpts().ObjCAutoRefCount) { setHasObjectMember(true); Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2195,12 +2195,6 @@ 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 && Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -3540,24 +3540,13 @@ 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. Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -69,7 +69,8 @@ CodeGenOptions::ClangABI::Ver4 || CGM.getTriple().getOS() == llvm::Triple::PS4) return RD->hasNonTrivialDestructorForCall() || - RD->hasNonTrivialCopyConstructorForCall(); + RD->hasNonTrivialCopyConstructorForCall() || + RD->forcePassIndirectly(); return !canCopyArgument(RD); } Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -827,6 +827,9 @@ return RAA_Default; case llvm::Triple::x86_64: + if (RD->forcePassIndirectly()) + return RAA_Indirect; + bool CopyCtorIsTrivial = false, CopyCtorIsTrivialForCall = false; bool DtorIsTrivialForCall = false; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -15450,8 +15450,10 @@ 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); } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -5791,9 +5791,11 @@ } } -/// 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) { if (D->isDependentType() || D->isInvalidDecl()) return false; @@ -6001,7 +6003,13 @@ checkClassLevelDLLAttribute(Record); - Record->setCanPassInRegisters(computeCanPassInRegisters(*this, Record)); + bool DestroyedInCallee = paramCanBeDestroyedInCallee(*this, Record); + + if (Record->hasNonTrivialDestructor()) + Record->setParamDestroyedInCallee(DestroyedInCallee); + + Record->setCanPassInRegisters(DestroyedInCallee && + !Record->forcePassIndirectly()); } /// Look up the special member function that would be called by a special Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -743,6 +743,7 @@ RD->setNonTrivialToPrimitiveCopy(Record.readInt()); RD->setNonTrivialToPrimitiveDestroy(Record.readInt()); RD->setCanPassInRegisters(Record.readInt()); + RD->setParamDestroyedInCallee(Record.readInt()); return Redecl; } @@ -1588,6 +1589,7 @@ Data.HasIrrelevantDestructor = Record.readInt(); Data.HasConstexprNonCopyMoveConstructor = Record.readInt(); Data.HasDefaultedDefaultConstructor = Record.readInt(); + Data.ForcePassIndirectly = Record.readInt(); Data.DefaultedDefaultConstructorIsConstexpr = Record.readInt(); Data.HasConstexprDefaultConstructor = Record.readInt(); Data.HasNonLiteralTypeFieldsOrBases = Record.readInt(); @@ -1727,6 +1729,7 @@ MATCH_FIELD(HasIrrelevantDestructor) OR_FIELD(HasConstexprNonCopyMoveConstructor) OR_FIELD(HasDefaultedDefaultConstructor) + MATCH_FIELD(ForcePassIndirectly) MATCH_FIELD(DefaultedDefaultConstructorIsConstexpr) OR_FIELD(HasConstexprDefaultConstructor) MATCH_FIELD(HasNonLiteralTypeFieldsOrBases) @@ -4109,6 +4112,7 @@ 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. Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5194,6 +5194,7 @@ auto *RD = cast(D); UpdatedDeclContexts.insert(RD->getPrimaryContext()); Record.push_back(RD->canPassInRegisters()); + Record.push_back(RD->isParamDestroyedInCallee()); Record.AddCXXDefinitionData(RD); Record.AddOffset(WriteDeclContextLexicalBlock( *Context, const_cast(RD))); @@ -6021,6 +6022,7 @@ Record->push_back(Data.HasIrrelevantDestructor); Record->push_back(Data.HasConstexprNonCopyMoveConstructor); Record->push_back(Data.HasDefaultedDefaultConstructor); + Record->push_back(Data.ForcePassIndirectly); Record->push_back(Data.DefaultedDefaultConstructorIsConstexpr); Record->push_back(Data.HasConstexprDefaultConstructor); Record->push_back(Data.HasNonLiteralTypeFieldsOrBases); Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -469,6 +469,7 @@ 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() && @@ -1911,6 +1912,8 @@ // 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 Index: test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp =================================================================== --- test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp +++ test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp @@ -172,12 +172,9 @@ 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. Index: test/CodeGenObjCXX/arc-special-member-functions.mm =================================================================== --- test/CodeGenObjCXX/arc-special-member-functions.mm +++ test/CodeGenObjCXX/arc-special-member-functions.mm @@ -31,6 +31,8 @@ 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_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_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_assign(ObjCBlockMember m1, ObjCBlockMember m2) { // CHECK: {{call.*_ZN15ObjCBlockMemberaSERKS_}} m1 = m2; + // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev( + // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev( // CHECK-NEXT: ret void } Index: test/CodeGenObjCXX/objc-struct-cxx-abi.mm =================================================================== --- test/CodeGenObjCXX/objc-struct-cxx-abi.mm +++ test/CodeGenObjCXX/objc-struct-cxx-abi.mm @@ -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 +#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 @@ // 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 @@ } // CHECK: define void @_Z21testParamWeakTemplate1SIU6__weakP11objc_objectE(%[[STRUCT_S]]* %{{.*}}) +// 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 testParamWeakTemplate(S<__weak id> a) { +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); } Index: test/CodeGenObjCXX/property-dot-copy-elision.mm =================================================================== --- test/CodeGenObjCXX/property-dot-copy-elision.mm +++ test/CodeGenObjCXX/property-dot-copy-elision.mm @@ -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; };