From d791e92b5fbd24eef539d5de738c41b835246539 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 19 Mar 2018 17:38:40 +0000 Subject: [PATCH] [ObjC] Allow declaring __weak pointer fields in C structs in ARC. This patch uses the infrastructure added in r326307 for enabling non-trivial fields to be declared in C structs to allow __weak fields in C structs in ARC. This recommits r327206, which was reverted because it caused module-enabled builders to fail. I discovered that the CXXRecordDecl::CanPassInRegisters flag wasn't being set correctly in some cases after I moved it to RecordDecl. Thanks to Eric Liu for helping me investigate the bug. rdar://problem/33599681 https://reviews.llvm.org/D44095 llvm-svn: 327870 --- clang/include/clang/AST/Decl.h | 18 ++ clang/include/clang/AST/DeclCXX.h | 18 -- clang/include/clang/AST/Type.h | 10 ++ clang/lib/AST/ASTImporter.cpp | 1 - clang/lib/AST/Decl.cpp | 3 +- clang/lib/AST/DeclCXX.cpp | 1 - clang/lib/AST/Type.cpp | 32 +++- clang/lib/CodeGen/CGBlocks.cpp | 7 +- clang/lib/CodeGen/CGNonTrivialStruct.cpp | 59 ++++++- clang/lib/CodeGen/CGObjC.cpp | 15 ++ clang/lib/CodeGen/CodeGenFunction.h | 2 + clang/lib/CodeGen/TargetInfo.cpp | 27 ++- clang/lib/Sema/SemaDecl.cpp | 8 +- clang/lib/Serialization/ASTReaderDecl.cpp | 4 +- clang/lib/Serialization/ASTWriter.cpp | 2 +- clang/lib/Serialization/ASTWriterDecl.cpp | 2 + .../CodeGenObjC/nontrivial-c-struct-exception.m | 31 +++- clang/test/CodeGenObjC/weak-in-c-struct.m | 193 +++++++++++++++++++++ clang/test/Modules/Inputs/module.map | 10 ++ clang/test/Modules/Inputs/template-nontrivial0.h | 13 ++ clang/test/Modules/Inputs/template-nontrivial1.h | 6 + clang/test/Modules/templates.mm | 10 ++ 22 files changed, 421 insertions(+), 51 deletions(-) create mode 100644 clang/test/CodeGenObjC/weak-in-c-struct.m create mode 100644 clang/test/Modules/Inputs/template-nontrivial0.h create mode 100644 clang/test/Modules/Inputs/template-nontrivial1.h diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index e141870..41b47ea 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3553,6 +3553,12 @@ class RecordDecl : public TagDecl { bool NonTrivialToPrimitiveCopy : 1; bool NonTrivialToPrimitiveDestroy : 1; + /// True if this class can be passed in a non-address-preserving fashion + /// (such as in registers). + /// This does not imply anything about how the ABI in use will actually + /// pass an object of this class. + bool CanPassInRegisters : 1; + protected: RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, @@ -3636,6 +3642,18 @@ public: NonTrivialToPrimitiveDestroy = V; } + /// Determine whether this class can be passed in registers. In C++ mode, + /// it must have at least one trivial, non-deleted copy or move constructor. + /// FIXME: This should be set as part of completeDefinition. + bool canPassInRegisters() const { + return CanPassInRegisters; + } + + /// Set that we can pass this RecordDecl in registers. + void setCanPassInRegisters(bool CanPass) { + CanPassInRegisters = CanPass; + } + /// \brief Determines whether this declaration represents the /// injected class name. /// diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index faa86e3..e9f6f9e 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -467,12 +467,6 @@ class CXXRecordDecl : public RecordDecl { /// constructor. unsigned HasDefaultedDefaultConstructor : 1; - /// \brief True if this class can be passed in a non-address-preserving - /// fashion (such as in registers) according to the C++ language rules. - /// This does not imply anything about how the ABI in use will actually - /// pass an object of this class. - unsigned CanPassInRegisters : 1; - /// \brief True if a defaulted default constructor for this class would /// be constexpr. unsigned DefaultedDefaultConstructorIsConstexpr : 1; @@ -1474,18 +1468,6 @@ public: return data().HasIrrelevantDestructor; } - /// \brief Determine whether this class has at least one trivial, non-deleted - /// copy or move constructor. - bool canPassInRegisters() const { - return data().CanPassInRegisters; - } - - /// \brief Set that we can pass this RecordDecl in registers. - // FIXME: This should be set as part of completeDefinition. - void setCanPassInRegisters(bool CanPass) { - data().CanPassInRegisters = CanPass; - } - /// 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". diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 0a5c086..bb8944a 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1097,6 +1097,10 @@ public: /// with the ARC __strong qualifier. PDIK_ARCStrong, + /// The type is an Objective-C retainable pointer type that is qualified + /// with the ARC __weak qualifier. + PDIK_ARCWeak, + /// The type is a struct containing a field whose type is not PCK_Trivial. PDIK_Struct }; @@ -1124,6 +1128,10 @@ public: /// with the ARC __strong qualifier. PCK_ARCStrong, + /// The type is an Objective-C retainable pointer type that is qualified + /// with the ARC __weak qualifier. + PCK_ARCWeak, + /// The type is a struct containing a field whose type is neither /// PCK_Trivial nor PCK_VolatileTrivial. /// Note that a C++ struct type does not necessarily match this; C++ copying @@ -1146,6 +1154,8 @@ public: /// source object is placed in an uninitialized state. PrimitiveCopyKind isNonTrivialToPrimitiveDestructiveMove() const; + bool canPassInRegisters() const; + enum DestructionKind { DK_none, DK_cxx_destructor, diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index ac262e7..6b9260d 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1105,7 +1105,6 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, = FromData.HasConstexprNonCopyMoveConstructor; ToData.HasDefaultedDefaultConstructor = FromData.HasDefaultedDefaultConstructor; - ToData.CanPassInRegisters = FromData.CanPassInRegisters; ToData.DefaultedDefaultConstructorIsConstexpr = FromData.DefaultedDefaultConstructorIsConstexpr; ToData.HasConstexprDefaultConstructor diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index b640dcc..35364d6 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3931,7 +3931,8 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C, HasObjectMember(false), HasVolatileMember(false), LoadedFieldsFromExternalStorage(false), NonTrivialToPrimitiveDefaultInitialize(false), - NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false) { + NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false), + CanPassInRegisters(true) { assert(classof(static_cast(this)) && "Invalid Kind!"); } diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 487386f..2718723 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -93,7 +93,6 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true), HasConstexprNonCopyMoveConstructor(false), HasDefaultedDefaultConstructor(false), - CanPassInRegisters(true), DefaultedDefaultConstructorIsConstexpr(true), HasConstexprDefaultConstructor(false), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 99f5b13..2985aac 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2214,11 +2214,14 @@ QualType::isNonTrivialToPrimitiveDefaultInitialize() const { if (RT->getDecl()->isNonTrivialToPrimitiveDefaultInitialize()) return PDIK_Struct; - Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime(); - if (Lifetime == Qualifiers::OCL_Strong) + switch (getQualifiers().getObjCLifetime()) { + case Qualifiers::OCL_Strong: return PDIK_ARCStrong; - - return PDIK_Trivial; + case Qualifiers::OCL_Weak: + return PDIK_ARCWeak; + default: + return PDIK_Trivial; + } } QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveCopy() const { @@ -2228,10 +2231,14 @@ QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveCopy() const { return PCK_Struct; Qualifiers Qs = getQualifiers(); - if (Qs.getObjCLifetime() == Qualifiers::OCL_Strong) + switch (Qs.getObjCLifetime()) { + case Qualifiers::OCL_Strong: return PCK_ARCStrong; - - return Qs.hasVolatile() ? PCK_VolatileTrivial : PCK_Trivial; + case Qualifiers::OCL_Weak: + return PCK_ARCWeak; + default: + return Qs.hasVolatile() ? PCK_VolatileTrivial : PCK_Trivial; + } } QualType::PrimitiveCopyKind @@ -2239,6 +2246,17 @@ QualType::isNonTrivialToPrimitiveDestructiveMove() const { return isNonTrivialToPrimitiveCopy(); } +bool QualType::canPassInRegisters() const { + if (const auto *RT = + getTypePtr()->getBaseElementTypeUnsafe()->getAs()) + return RT->getDecl()->canPassInRegisters(); + + if (getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak) + return false; + + return true; +} + bool Type::isLiteralType(const ASTContext &Ctx) const { if (isDependentType()) return false; diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index 663f1d0..1a69049 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -1525,6 +1525,9 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, case QualType::PCK_Struct: return std::make_pair(BlockCaptureEntityKind::NonTrivialCStruct, BlockFieldFlags()); + case QualType::PCK_ARCWeak: + // We need to register __weak direct captures with the runtime. + return std::make_pair(BlockCaptureEntityKind::ARCWeak, Flags); case QualType::PCK_ARCStrong: // We need to retain the copied value for __strong direct captures. // If it's a block pointer, we have to copy the block and assign that to @@ -1542,10 +1545,6 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, // Special rules for ARC captures: Qualifiers QS = T.getQualifiers(); - // We need to register __weak direct captures with the runtime. - if (QS.getObjCLifetime() == Qualifiers::OCL_Weak) - return std::make_pair(BlockCaptureEntityKind::ARCWeak, Flags); - // Non-ARC captures of retainable pointers are strong and // therefore require a call to _Block_object_assign. if (!QS.getObjCLifetime() && !LangOpts.ObjCAutoRefCount) diff --git a/clang/lib/CodeGen/CGNonTrivialStruct.cpp b/clang/lib/CodeGen/CGNonTrivialStruct.cpp index 3b0b173..731938a 100644 --- a/clang/lib/CodeGen/CGNonTrivialStruct.cpp +++ b/clang/lib/CodeGen/CGNonTrivialStruct.cpp @@ -77,6 +77,8 @@ struct DefaultInitializedTypeVisitor { switch (PDIK) { case QualType::PDIK_ARCStrong: return asDerived().visitARCStrong(FT, std::forward(Args)...); + case QualType::PDIK_ARCWeak: + return asDerived().visitARCWeak(FT, std::forward(Args)...); case QualType::PDIK_Struct: return asDerived().visitStruct(FT, std::forward(Args)...); case QualType::PDIK_Trivial: @@ -108,6 +110,8 @@ struct CopiedTypeVisitor { switch (PCK) { case QualType::PCK_ARCStrong: return asDerived().visitARCStrong(FT, std::forward(Args)...); + case QualType::PCK_ARCWeak: + return asDerived().visitARCWeak(FT, std::forward(Args)...); case QualType::PCK_Struct: return asDerived().visitStruct(FT, std::forward(Args)...); case QualType::PCK_Trivial: @@ -141,11 +145,6 @@ template struct StructVisitor { template void visitTrivial(Ts... Args) {} - template void visitARCWeak(Ts... Args) { - // FIXME: remove this when visitARCWeak is implemented in the subclasses. - llvm_unreachable("weak field is not expected"); - } - template void visitCXXDestructor(Ts... Args) { llvm_unreachable("field of a C++ struct type is not expected"); } @@ -245,6 +244,13 @@ template struct GenFuncNameBase { appendStr(getVolatileOffsetStr(FT.isVolatileQualified(), FieldOffset)); } + void visitARCWeak(QualType FT, const FieldDecl *FD, + CharUnits CurStructOffset) { + appendStr("_w"); + CharUnits FieldOffset = CurStructOffset + asDerived().getFieldOffset(FD); + appendStr(getVolatileOffsetStr(FT.isVolatileQualified(), FieldOffset)); + } + void visitStruct(QualType QT, const FieldDecl *FD, CharUnits CurStructOffset) { CharUnits FieldOffset = CurStructOffset + asDerived().getFieldOffset(FD); @@ -615,6 +621,12 @@ struct GenDestructor : StructVisitor, *CGF, getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD), QT); } + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + CGF->destroyARCWeak( + *CGF, getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD), QT); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructDestructor( @@ -636,6 +648,12 @@ struct GenDefaultInitialize getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD), QT); } + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + CGF->EmitNullInitialization( + getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD), QT); + } + template void visitArray(FieldKind FK, QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, std::array Addrs) { @@ -678,6 +696,14 @@ struct GenCopyConstructor : GenBinaryFunc { llvm::Value *Val = CGF->EmitARCRetain(QT, SrcVal); CGF->EmitStoreOfScalar(Val, CGF->MakeAddrLValue(Addrs[DstIdx], QT), true); } + + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + CGF->EmitARCCopyWeak(Addrs[DstIdx], Addrs[SrcIdx]); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructCopyConstructor(CGF->MakeAddrLValue(Addrs[DstIdx], FT), @@ -700,6 +726,14 @@ struct GenMoveConstructor : GenBinaryFunc { CGF->EmitStoreOfScalar(SrcVal, CGF->MakeAddrLValue(Addrs[DstIdx], QT), /* isInitialization */ true); } + + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + CGF->EmitARCMoveWeak(Addrs[DstIdx], Addrs[SrcIdx]); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructMoveConstructor(CGF->MakeAddrLValue(Addrs[DstIdx], FT), @@ -720,6 +754,14 @@ struct GenCopyAssignment : GenBinaryFunc { CGF->EmitARCStoreStrong(CGF->MakeAddrLValue(Addrs[DstIdx], QT), SrcVal, false); } + + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + CGF->emitARCCopyAssignWeak(QT, Addrs[DstIdx], Addrs[SrcIdx]); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructCopyAssignmentOperator( @@ -747,6 +789,13 @@ struct GenMoveAssignment : GenBinaryFunc { CGF->EmitARCRelease(DstVal, ARCImpreciseLifetime); } + void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array Addrs) { + Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + CGF->emitARCMoveAssignWeak(QT, Addrs[DstIdx], Addrs[SrcIdx]); + } + void callSpecialFunction(QualType FT, CharUnits Offset, std::array Addrs) { CGF->callCStructMoveAssignmentOperator( diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index e812ef3..6ece1d3 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -2307,6 +2307,21 @@ void CodeGenFunction::EmitARCCopyWeak(Address dst, Address src) { "objc_copyWeak"); } +void CodeGenFunction::emitARCCopyAssignWeak(QualType Ty, Address DstAddr, + Address SrcAddr) { + llvm::Value *Object = EmitARCLoadWeakRetained(SrcAddr); + Object = EmitObjCConsumeObject(Ty, Object); + EmitARCStoreWeak(DstAddr, Object, false); +} + +void CodeGenFunction::emitARCMoveAssignWeak(QualType Ty, Address DstAddr, + Address SrcAddr) { + llvm::Value *Object = EmitARCLoadWeakRetained(SrcAddr); + Object = EmitObjCConsumeObject(Ty, Object); + EmitARCStoreWeak(DstAddr, Object, false); + EmitARCDestroyWeak(SrcAddr); +} + /// Produce the code to do a objc_autoreleasepool_push. /// call i8* \@objc_autoreleasePoolPush(void) llvm::Value *CodeGenFunction::EmitObjCAutoreleasePoolPush() { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index fa65b1d..5852788 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3551,6 +3551,8 @@ public: llvm::Value *EmitARCLoadWeak(Address addr); llvm::Value *EmitARCLoadWeakRetained(Address addr); llvm::Value *EmitARCStoreWeak(Address addr, llvm::Value *value, bool ignored); + void emitARCCopyAssignWeak(QualType Ty, Address DstAddr, Address SrcAddr); + void emitARCMoveAssignWeak(QualType Ty, Address DstAddr, Address SrcAddr); void EmitARCCopyWeak(Address dst, Address src); void EmitARCMoveWeak(Address dst, Address src); llvm::Value *EmitARCRetainAutorelease(QualType type, llvm::Value *value); diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index aadbd30..939679e 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -140,8 +140,11 @@ bool SwiftABIInfo::isLegalVectorTypeForSwift(CharUnits vectorSize, static CGCXXABI::RecordArgABI getRecordArgABI(const RecordType *RT, CGCXXABI &CXXABI) { const CXXRecordDecl *RD = dyn_cast(RT->getDecl()); - if (!RD) + if (!RD) { + if (!RT->getDecl()->canPassInRegisters()) + return CGCXXABI::RAA_Indirect; return CGCXXABI::RAA_Default; + } return CXXABI.getRecordArgABI(RD); } @@ -153,6 +156,20 @@ static CGCXXABI::RecordArgABI getRecordArgABI(QualType T, return getRecordArgABI(RT, CXXABI); } +static bool classifyReturnType(const CGCXXABI &CXXABI, CGFunctionInfo &FI, + const ABIInfo &Info) { + QualType Ty = FI.getReturnType(); + + if (const auto *RT = Ty->getAs()) + if (!isa(RT->getDecl()) && + !RT->getDecl()->canPassInRegisters()) { + FI.getReturnInfo() = Info.getNaturalAlignIndirect(Ty); + return true; + } + + return CXXABI.classifyReturnType(FI); +} + /// Pass transparent unions as if they were the type of the first element. Sema /// should ensure that all elements of the union have the same "machine type". static QualType useFirstFieldIfTransparentUnion(QualType Ty) { @@ -1749,7 +1766,7 @@ void X86_32ABIInfo::computeInfo(CGFunctionInfo &FI) const { } else State.FreeRegs = DefaultNumRegisterParameters; - if (!getCXXABI().classifyReturnType(FI)) { + if (!::classifyReturnType(getCXXABI(), FI, *this)) { FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), State); } else if (FI.getReturnInfo().isIndirect()) { // The C++ ABI is not aware of register usage, so we have to check if the @@ -3545,7 +3562,7 @@ void X86_64ABIInfo::computeInfo(CGFunctionInfo &FI) const { unsigned FreeSSERegs = IsRegCall ? 16 : 8; unsigned NeededInt, NeededSSE; - if (!getCXXABI().classifyReturnType(FI)) { + if (!::classifyReturnType(getCXXABI(), FI, *this)) { if (IsRegCall && FI.getReturnType()->getTypePtr()->isRecordType() && !FI.getReturnType()->getTypePtr()->isUnionType()) { FI.getReturnInfo() = @@ -4895,7 +4912,7 @@ private: bool isIllegalVectorType(QualType Ty) const; void computeInfo(CGFunctionInfo &FI) const override { - if (!getCXXABI().classifyReturnType(FI)) + if (!::classifyReturnType(getCXXABI(), FI, *this)) FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); for (auto &it : FI.arguments()) @@ -5626,7 +5643,7 @@ void WindowsARMTargetCodeGenInfo::setTargetAttributes( } void ARMABIInfo::computeInfo(CGFunctionInfo &FI) const { - if (!getCXXABI().classifyReturnType(FI)) + if (!::classifyReturnType(getCXXABI(), FI, *this)) FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), FI.isVariadic()); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index ea97135..6319e4b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15266,7 +15266,6 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, // Get the type for the field. const Type *FDTy = FD->getType().getTypePtr(); - Qualifiers QS = FD->getType().getQualifiers(); if (!FD->isAnonymousStructOrUnion()) { // Remember all fields written by the user. @@ -15407,10 +15406,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, QualType T = Context.getObjCObjectPointerType(FD->getType()); FD->setType(T); } else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - Record && !ObjCFieldLifetimeErrReported && - ((!getLangOpts().CPlusPlus && - QS.getObjCLifetime() == Qualifiers::OCL_Weak) || - Record->isUnion())) { + Record && !ObjCFieldLifetimeErrReported && Record->isUnion()) { // It's an error in ARC or Weak if a field has lifetime. // We don't want to report this in a system header, though, // so we just make the field unavailable. @@ -15456,6 +15452,8 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, Record->setNonTrivialToPrimitiveCopy(true); if (FT.isDestructedType()) Record->setNonTrivialToPrimitiveDestroy(true); + if (!FT.canPassInRegisters()) + Record->setCanPassInRegisters(false); } if (Record && FD->getType().isVolatileQualified()) diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index ab99a17..733748c 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -742,6 +742,7 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) { RD->setNonTrivialToPrimitiveDefaultInitialize(Record.readInt()); RD->setNonTrivialToPrimitiveCopy(Record.readInt()); RD->setNonTrivialToPrimitiveDestroy(Record.readInt()); + RD->setCanPassInRegisters(Record.readInt()); return Redecl; } @@ -1587,7 +1588,6 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.HasIrrelevantDestructor = Record.readInt(); Data.HasConstexprNonCopyMoveConstructor = Record.readInt(); Data.HasDefaultedDefaultConstructor = Record.readInt(); - Data.CanPassInRegisters = Record.readInt(); Data.DefaultedDefaultConstructorIsConstexpr = Record.readInt(); Data.HasConstexprDefaultConstructor = Record.readInt(); Data.HasNonLiteralTypeFieldsOrBases = Record.readInt(); @@ -1727,7 +1727,6 @@ void ASTDeclReader::MergeDefinitionData( MATCH_FIELD(HasIrrelevantDestructor) OR_FIELD(HasConstexprNonCopyMoveConstructor) OR_FIELD(HasDefaultedDefaultConstructor) - MATCH_FIELD(CanPassInRegisters) MATCH_FIELD(DefaultedDefaultConstructorIsConstexpr) OR_FIELD(HasConstexprDefaultConstructor) MATCH_FIELD(HasNonLiteralTypeFieldsOrBases) @@ -4107,6 +4106,7 @@ void ASTDeclReader::UpdateDecl(Decl *D, bool HadRealDefinition = OldDD && (OldDD->Definition != RD || !Reader.PendingFakeDefinitionData.count(OldDD)); + RD->setCanPassInRegisters(Record.readInt()); ReadCXXRecordDefinition(RD, /*Update*/true); // Visible update is handled separately. diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index e63000c..a6412ed 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5193,6 +5193,7 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { case UPD_CXX_INSTANTIATED_CLASS_DEFINITION: { auto *RD = cast(D); UpdatedDeclContexts.insert(RD->getPrimaryContext()); + Record.push_back(RD->canPassInRegisters()); Record.AddCXXDefinitionData(RD); Record.AddOffset(WriteDeclContextLexicalBlock( *Context, const_cast(RD))); @@ -6020,7 +6021,6 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) { Record->push_back(Data.HasIrrelevantDestructor); Record->push_back(Data.HasConstexprNonCopyMoveConstructor); Record->push_back(Data.HasDefaultedDefaultConstructor); - Record->push_back(Data.CanPassInRegisters); Record->push_back(Data.DefaultedDefaultConstructorIsConstexpr); Record->push_back(Data.HasConstexprDefaultConstructor); Record->push_back(Data.HasNonLiteralTypeFieldsOrBases); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index b3886b6..6a42603 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -468,6 +468,7 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) { Record.push_back(D->isNonTrivialToPrimitiveDefaultInitialize()); Record.push_back(D->isNonTrivialToPrimitiveCopy()); Record.push_back(D->isNonTrivialToPrimitiveDestroy()); + Record.push_back(D->canPassInRegisters()); if (D->getDeclContext() == D->getLexicalDeclContext() && !D->hasAttrs() && @@ -1909,6 +1910,7 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNonTrivialToPrimitiveDestroy Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // canPassInRegisters // DC Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset diff --git a/clang/test/CodeGenObjC/nontrivial-c-struct-exception.m b/clang/test/CodeGenObjC/nontrivial-c-struct-exception.m index a926a6d..7db53bb 100644 --- a/clang/test/CodeGenObjC/nontrivial-c-struct-exception.m +++ b/clang/test/CodeGenObjC/nontrivial-c-struct-exception.m @@ -1,12 +1,18 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -fobjc-exceptions -fexceptions -fobjc-arc-exceptions -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -fobjc-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s // CHECK: %[[STRUCT_STRONG:.*]] = type { i32, i8* } +// CHECK: %[[STRUCT_WEAK:.*]] = type { i32, i8* } typedef struct { int i; id f1; } Strong; +typedef struct { + int i; + __weak id f1; +} Weak; + // CHECK: define void @testStrongException() // CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONG]], align 8 // CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_STRONG]], align 8 @@ -31,3 +37,26 @@ void calleeStrong(Strong, Strong); void testStrongException(void) { calleeStrong(genStrong(), genStrong()); } + +// CHECK: define void @testWeakException() +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_WEAK]], align 8 +// CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_WEAK]], align 8 +// CHECK: call void @genWeak(%[[STRUCT_WEAK]]* sret %[[AGG_TMP]]) +// CHECK: invoke void @genWeak(%[[STRUCT_WEAK]]* sret %[[AGG_TMP1]]) + +// CHECK: call void @calleeWeak(%[[STRUCT_WEAK]]* %[[AGG_TMP]], %[[STRUCT_WEAK]]* %[[AGG_TMP1]]) +// CHECK: ret void + +// CHECK: landingpad { i8*, i32 } +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_TMP]] to i8** +// CHECK: call void @__destructor_8_w8(i8** %[[V3]]) +// CHECK: br label + +// CHECK: resume + +Weak genWeak(void); +void calleeWeak(Weak, Weak); + +void testWeakException(void) { + calleeWeak(genWeak(), genWeak()); +} diff --git a/clang/test/CodeGenObjC/weak-in-c-struct.m b/clang/test/CodeGenObjC/weak-in-c-struct.m new file mode 100644 index 0000000..b0f4c08 --- /dev/null +++ b/clang/test/CodeGenObjC/weak-in-c-struct.m @@ -0,0 +1,193 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fblocks -fobjc-runtime=ios-11.0 -emit-llvm -o - %s | FileCheck -check-prefix=ARM64 -check-prefix=COMMON %s +// RUN: %clang_cc1 -triple thumbv7-apple-ios10 -fobjc-arc -fblocks -fobjc-runtime=ios-10.0 -emit-llvm -o - %s | FileCheck -check-prefix=COMMON %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13 -fobjc-arc -fblocks -fobjc-runtime=macosx-10.13.0 -emit-llvm -o - %s | FileCheck -check-prefix=COMMON %s +// RUN: %clang_cc1 -triple i386-apple-macosx10.13.0 -fobjc-arc -fblocks -fobjc-runtime=macosx-fragile-10.13.0 -emit-llvm -o - %s | FileCheck -check-prefix=COMMON %s + +typedef void (^BlockTy)(void); + +// COMMON: %[[STRUCT_WEAK:.*]] = type { i32, i8* } + +typedef struct { + int f0; + __weak id f1; +} Weak; + +Weak getWeak(void); +void calleeWeak(Weak); + +// ARM64: define void @test_constructor_destructor_Weak() +// ARM64: %[[T:.*]] = alloca %[[STRUCT_WEAK]], align 8 +// ARM64: %[[V0:.*]] = bitcast %[[STRUCT_WEAK]]* %[[T]] to i8** +// ARM64: call void @__default_constructor_8_w8(i8** %[[V0]]) +// ARM64: %[[V1:.*]] = bitcast %[[STRUCT_WEAK]]* %[[T]] to i8** +// ARM64: call void @__destructor_8_w8(i8** %[[V1]]) +// ARM64: ret void + +// ARM64: define linkonce_odr hidden void @__default_constructor_8_w8(i8** %[[DST:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 8 +// ARM64: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** +// ARM64: %[[V4:.*]] = bitcast i8** %[[V3]] to i8* +// ARM64: call void @llvm.memset.p0i8.i64(i8* align 8 %[[V4]], i8 0, i64 8, i1 false) + +// ARM64: define linkonce_odr hidden void @__destructor_8_w8(i8** %[[DST:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V2:.*]] = getelementptr inbounds i8, i8* %[[V1]], i64 8 +// ARM64: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** +// ARM64: call void @objc_destroyWeak(i8** %[[V3]]) + +void test_constructor_destructor_Weak(void) { + Weak t; +} + +// ARM64: define void @test_copy_constructor_Weak(%[[STRUCT_WEAK]]* %{{.*}}) +// ARM64: call void @__copy_constructor_8_8_t0w4_w8(i8** %{{.*}}, i8** %{{.*}}) +// ARM64: call void @__destructor_8_w8(i8** %{{.*}}) + +// ARM64: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_w8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V2:.*]] = bitcast i8** %[[V0]] to i32* +// ARM64: %[[V3:.*]] = bitcast i8** %[[V1]] to i32* +// ARM64: %[[V4:.*]] = load i32, i32* %[[V3]], align 8 +// ARM64: store i32 %[[V4]], i32* %[[V2]], align 8 +// ARM64: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// ARM64: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// ARM64: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// ARM64: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// ARM64: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// ARM64: call void @objc_copyWeak(i8** %[[V7]], i8** %[[V10]]) + +void test_copy_constructor_Weak(Weak *s) { + Weak t = *s; +} + +// ARM64: define void @test_copy_assignment_Weak(%[[STRUCT_WEAK]]* %{{.*}}, %[[STRUCT_WEAK]]* %{{.*}}) +// ARM64: call void @__copy_assignment_8_8_t0w4_w8(i8** %{{.*}}, i8** %{{.*}}) + +// ARM64: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_w8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V2:.*]] = bitcast i8** %[[V0]] to i32* +// ARM64: %[[V3:.*]] = bitcast i8** %[[V1]] to i32* +// ARM64: %[[V4:.*]] = load i32, i32* %[[V3]], align 8 +// ARM64: store i32 %[[V4]], i32* %[[V2]], align 8 +// ARM64: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// ARM64: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// ARM64: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// ARM64: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// ARM64: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// ARM64: %[[V11:.*]] = call i8* @objc_loadWeakRetained(i8** %[[V10]]) +// ARM64: %[[V12:.*]] = call i8* @objc_storeWeak(i8** %[[V7]], i8* %[[V11]]) +// ARM64: call void @objc_release(i8* %[[V11]]) + +void test_copy_assignment_Weak(Weak *d, Weak *s) { + *d = *s; +} + +// ARM64: define internal void @__Block_byref_object_copy_(i8*, i8*) +// ARM64: call void @__move_constructor_8_8_t0w4_w8(i8** %{{.*}}, i8** %{{.*}}) + +// ARM64: define linkonce_odr hidden void @__move_constructor_8_8_t0w4_w8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V2:.*]] = bitcast i8** %[[V0]] to i32* +// ARM64: %[[V3:.*]] = bitcast i8** %[[V1]] to i32* +// ARM64: %[[V4:.*]] = load i32, i32* %[[V3]], align 8 +// ARM64: store i32 %[[V4]], i32* %[[V2]], align 8 +// ARM64: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// ARM64: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// ARM64: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// ARM64: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// ARM64: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// ARM64: call void @objc_moveWeak(i8** %[[V7]], i8** %[[V10]]) + +void test_move_constructor_Weak(void) { + __block Weak t; + BlockTy b = ^{ (void)t; }; +} + +// ARM64: define void @test_move_assignment_Weak(%[[STRUCT_WEAK]]* %{{.*}}) +// ARM64: call void @__move_assignment_8_8_t0w4_w8(i8** %{{.*}}, i8** %{{.*}}) + +// ARM64: define linkonce_odr hidden void @__move_assignment_8_8_t0w4_w8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// ARM64: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// ARM64: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// ARM64: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// ARM64: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// ARM64: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// ARM64: %[[V2:.*]] = bitcast i8** %[[V0]] to i32* +// ARM64: %[[V3:.*]] = bitcast i8** %[[V1]] to i32* +// ARM64: %[[V4:.*]] = load i32, i32* %[[V3]], align 8 +// ARM64: store i32 %[[V4]], i32* %[[V2]], align 8 +// ARM64: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// ARM64: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// ARM64: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// ARM64: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// ARM64: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// ARM64: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// ARM64: %[[V11:.*]] = call i8* @objc_loadWeakRetained(i8** %[[V10]]) +// ARM64: %[[V12:.*]] = call i8* @objc_storeWeak(i8** %[[V7]], i8* %[[V11]]) +// ARM64: call void @objc_destroyWeak(i8** %[[V10]]) +// ARM64: call void @objc_release(i8* %[[V11]]) + +void test_move_assignment_Weak(Weak *p) { + *p = getWeak(); +} + +// COMMON: define void @test_parameter_Weak(%[[STRUCT_WEAK]]* %[[A:.*]]) +// COMMON: %[[V0:.*]] = bitcast %[[STRUCT_WEAK]]* %[[A]] to i8** +// COMMON: call void @__destructor_{{.*}}(i8** %[[V0]]) + +void test_parameter_Weak(Weak a) { +} + +// COMMON: define void @test_argument_Weak(%[[STRUCT_WEAK]]* %[[A:.*]]) +// COMMON: %[[A_ADDR:.*]] = alloca %[[STRUCT_WEAK]]* +// COMMON: %[[AGG_TMP:.*]] = alloca %[[STRUCT_WEAK]] +// COMMON: store %[[STRUCT_WEAK]]* %[[A]], %[[STRUCT_WEAK]]** %[[A_ADDR]] +// COMMON: %[[V0:.*]] = load %[[STRUCT_WEAK]]*, %[[STRUCT_WEAK]]** %[[A_ADDR]] +// COMMON: %[[V1:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_TMP]] to i8** +// COMMON: %[[V2:.*]] = bitcast %[[STRUCT_WEAK]]* %[[V0]] to i8** +// COMMON: call void @__copy_constructor_{{.*}}(i8** %[[V1]], i8** %[[V2]]) +// COMMON: call void @calleeWeak(%[[STRUCT_WEAK]]* %[[AGG_TMP]]) +// COMMON-NEXT: ret + +void test_argument_Weak(Weak *a) { + calleeWeak(*a); +} + +// COMMON: define void @test_return_Weak(%[[STRUCT_WEAK]]* noalias sret %[[AGG_RESULT:.*]], %[[STRUCT_WEAK]]* %[[A:.*]]) +// COMMON: %[[A_ADDR:.*]] = alloca %[[STRUCT_WEAK]]* +// COMMON: store %[[STRUCT_WEAK]]* %[[A]], %[[STRUCT_WEAK]]** %[[A_ADDR]] +// COMMON: %[[V0:.*]] = load %[[STRUCT_WEAK]]*, %[[STRUCT_WEAK]]** %[[A_ADDR]] +// COMMON: %[[V1:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_RESULT]] to i8** +// COMMON: %[[V2:.*]] = bitcast %[[STRUCT_WEAK]]* %[[V0]] to i8** +// COMMON: call void @__copy_constructor_{{.*}}(i8** %[[V1]], i8** %[[V2]]) +// COMMON: ret void + +Weak test_return_Weak(Weak *a) { + return *a; +} diff --git a/clang/test/Modules/Inputs/module.map b/clang/test/Modules/Inputs/module.map index 4788daa..93fe468 100644 --- a/clang/test/Modules/Inputs/module.map +++ b/clang/test/Modules/Inputs/module.map @@ -460,3 +460,13 @@ module innerstructredef { header "innerstructredef.h" } } + +module template_nontrivial0 { + header "template-nontrivial0.h" + export * +} + +module template_nontrivial1 { + header "template-nontrivial1.h" + export * +} diff --git a/clang/test/Modules/Inputs/template-nontrivial0.h b/clang/test/Modules/Inputs/template-nontrivial0.h new file mode 100644 index 0000000..cff080e --- /dev/null +++ b/clang/test/Modules/Inputs/template-nontrivial0.h @@ -0,0 +1,13 @@ +template +struct Class0 { + Class0(); + Class0(const Class0 &); + ~Class0(); + T *p; +}; + +struct S0 { + id x; +}; + +Class0 returnNonTrivial(); diff --git a/clang/test/Modules/Inputs/template-nontrivial1.h b/clang/test/Modules/Inputs/template-nontrivial1.h new file mode 100644 index 0000000..24136f0 --- /dev/null +++ b/clang/test/Modules/Inputs/template-nontrivial1.h @@ -0,0 +1,6 @@ +@import template_nontrivial0; + +struct S1 { + S1(); + Class0 a; +}; diff --git a/clang/test/Modules/templates.mm b/clang/test/Modules/templates.mm index 9c3f327..95e7a9c 100644 --- a/clang/test/Modules/templates.mm +++ b/clang/test/Modules/templates.mm @@ -122,3 +122,13 @@ void testWithAttributes() { static_assert(alignof(decltype(a)) == 2, ""); static_assert(alignof(decltype(b)) == 2, ""); } + +// Check that returnNonTrivial doesn't return Class0 directly in registers. + +// CHECK: declare void @_Z16returnNonTrivialv(%struct.Class0* sret) + +@import template_nontrivial0; +@import template_nontrivial1; + +S1::S1() : a(returnNonTrivial()) { +} -- 2.7.4