diff options
author | Akira Hatanaka <ahatanaka@apple.com> | 2018-02-28 07:15:55 +0000 |
---|---|---|
committer | Akira Hatanaka <ahatanaka@apple.com> | 2018-02-28 07:15:55 +0000 |
commit | 7275da0f2ee24336fe83cb7cfe2ba22f9cefc117 (patch) | |
tree | 9faf6e67f81d54afcaa45b6300b4c150070a2eb3 /clang/lib/CodeGen/CGBlocks.cpp | |
parent | ac799b05d42fee69a91bf35beb7f87c548827a42 (diff) | |
download | bcm5719-llvm-7275da0f2ee24336fe83cb7cfe2ba22f9cefc117.tar.gz bcm5719-llvm-7275da0f2ee24336fe83cb7cfe2ba22f9cefc117.zip |
[ObjC] Allow declaring __strong pointer fields in structs in Objective-C
ARC mode.
Declaring __strong pointer fields in structs was not allowed in
Objective-C ARC until now because that would make the struct non-trivial
to default-initialize, copy/move, and destroy, which is not something C
was designed to do. This patch lifts that restriction.
Special functions for non-trivial C structs are synthesized that are
needed to default-initialize, copy/move, and destroy the structs and
manage the ownership of the objects the __strong pointer fields point
to. Non-trivial structs passed to functions are destructed in the callee
function.
rdar://problem/33599681
Differential Revision: https://reviews.llvm.org/D41228
llvm-svn: 326307
Diffstat (limited to 'clang/lib/CodeGen/CGBlocks.cpp')
-rw-r--r-- | clang/lib/CodeGen/CGBlocks.cpp | 177 |
1 files changed, 122 insertions, 55 deletions
diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index bbef9292d2c..7bd61768758 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -477,6 +477,14 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF, info.NeedsCopyDispose = true; info.HasCXXObject = true; + // So do C structs that require non-trivial copy construction or + // destruction. + } else if (variable->getType().isNonTrivialToPrimitiveCopy() == + QualType::PCK_Struct || + variable->getType().isDestructedType() == + QualType::DK_nontrivial_c_struct) { + info.NeedsCopyDispose = true; + // And so do types with destructors. } else if (CGM.getLangOpts().CPlusPlus) { if (const CXXRecordDecl *record = @@ -1511,6 +1519,7 @@ enum class BlockCaptureEntityKind { CXXRecord, // Copy or destroy ARCWeak, ARCStrong, + NonTrivialCStruct, BlockObject, // Assign or release None }; @@ -1546,39 +1555,46 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, Flags |= BLOCK_FIELD_IS_WEAK; return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags); } - if (!T->isObjCRetainableType()) - // For all other types, the memcpy is fine. - return std::make_pair(BlockCaptureEntityKind::None, Flags); Flags = BLOCK_FIELD_IS_OBJECT; bool isBlockPointer = T->isBlockPointerType(); if (isBlockPointer) Flags = BLOCK_FIELD_IS_BLOCK; - // 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); - - // We need to retain the copied value for __strong direct captures. - if (QS.getObjCLifetime() == Qualifiers::OCL_Strong) { - // If it's a block pointer, we have to copy the block and - // assign that to the destination pointer, so we might as - // well use _Block_object_assign. Otherwise we can avoid that. + switch (T.isNonTrivialToPrimitiveCopy()) { + case QualType::PCK_Struct: + return std::make_pair(BlockCaptureEntityKind::NonTrivialCStruct, + BlockFieldFlags()); + 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 + // the destination pointer, so we might as well use _Block_object_assign. + // Otherwise we can avoid that. return std::make_pair(!isBlockPointer ? BlockCaptureEntityKind::ARCStrong : BlockCaptureEntityKind::BlockObject, Flags); - } + case QualType::PCK_Trivial: + case QualType::PCK_VolatileTrivial: { + if (!T->isObjCRetainableType()) + // For all other types, the memcpy is fine. + return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags()); - // Non-ARC captures of retainable pointers are strong and - // therefore require a call to _Block_object_assign. - if (!QS.getObjCLifetime() && !LangOpts.ObjCAutoRefCount) - return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags); + // Special rules for ARC captures: + Qualifiers QS = T.getQualifiers(); - // Otherwise the memcpy is fine. - return std::make_pair(BlockCaptureEntityKind::None, Flags); + // 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) + return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags); + + // Otherwise the memcpy is fine. + return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags()); + } + } } /// Find the set of block captures that need to be explicitly copied or destroy. @@ -1675,6 +1691,13 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { EmitSynthesizedCXXCopyCtor(dstField, srcField, CI.getCopyExpr()); } else if (CopiedCapture.Kind == BlockCaptureEntityKind::ARCWeak) { EmitARCCopyWeak(dstField, srcField); + // If this is a C struct that requires non-trivial copy construction, emit a + // call to its copy constructor. + } else if (CopiedCapture.Kind == + BlockCaptureEntityKind::NonTrivialCStruct) { + QualType varType = CI.getVariable()->getType(); + callCStructCopyConstructor(MakeAddrLValue(dstField, varType), + MakeAddrLValue(srcField, varType)); } else { llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src"); if (CopiedCapture.Kind == BlockCaptureEntityKind::ARCStrong) { @@ -1730,50 +1753,50 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { return llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy); } +static BlockFieldFlags +getBlockFieldFlagsForObjCObjectPointer(const BlockDecl::Capture &CI, + QualType T) { + BlockFieldFlags Flags = BLOCK_FIELD_IS_OBJECT; + if (T->isBlockPointerType()) + Flags = BLOCK_FIELD_IS_BLOCK; + return Flags; +} + static std::pair<BlockCaptureEntityKind, BlockFieldFlags> computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, const LangOptions &LangOpts) { - BlockFieldFlags Flags; if (CI.isByRef()) { - Flags = BLOCK_FIELD_IS_BYREF; + BlockFieldFlags Flags = BLOCK_FIELD_IS_BYREF; if (T.isObjCGCWeak()) Flags |= BLOCK_FIELD_IS_WEAK; return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags); } - if (const CXXRecordDecl *Record = T->getAsCXXRecordDecl()) { - if (Record->hasTrivialDestructor()) - return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags()); + switch (T.isDestructedType()) { + case QualType::DK_cxx_destructor: return std::make_pair(BlockCaptureEntityKind::CXXRecord, BlockFieldFlags()); + case QualType::DK_objc_strong_lifetime: + // Use objc_storeStrong for __strong direct captures; the + // dynamic tools really like it when we do this. + return std::make_pair(BlockCaptureEntityKind::ARCStrong, + getBlockFieldFlagsForObjCObjectPointer(CI, T)); + case QualType::DK_objc_weak_lifetime: + // Support __weak direct captures. + return std::make_pair(BlockCaptureEntityKind::ARCWeak, + getBlockFieldFlagsForObjCObjectPointer(CI, T)); + case QualType::DK_nontrivial_c_struct: + return std::make_pair(BlockCaptureEntityKind::NonTrivialCStruct, + BlockFieldFlags()); + case QualType::DK_none: { + // Non-ARC captures are strong, and we need to use _Block_object_dispose. + if (T->isObjCRetainableType() && !T.getQualifiers().hasObjCLifetime() && + !LangOpts.ObjCAutoRefCount) + return std::make_pair(BlockCaptureEntityKind::BlockObject, + getBlockFieldFlagsForObjCObjectPointer(CI, T)); + // Otherwise, we have nothing to do. + return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags()); + } } - - // Other types don't need to be destroy explicitly. - if (!T->isObjCRetainableType()) - return std::make_pair(BlockCaptureEntityKind::None, Flags); - - Flags = BLOCK_FIELD_IS_OBJECT; - if (T->isBlockPointerType()) - Flags = BLOCK_FIELD_IS_BLOCK; - - // Special rules for ARC captures. - Qualifiers QS = T.getQualifiers(); - - // Use objc_storeStrong for __strong direct captures; the - // dynamic tools really like it when we do this. - if (QS.getObjCLifetime() == Qualifiers::OCL_Strong) - return std::make_pair(BlockCaptureEntityKind::ARCStrong, Flags); - - // Support __weak direct captures. - if (QS.getObjCLifetime() == Qualifiers::OCL_Weak) - return std::make_pair(BlockCaptureEntityKind::ARCWeak, Flags); - - // Non-ARC captures are strong, and we need to use - // _Block_object_dispose. - if (!QS.hasObjCLifetime() && !LangOpts.ObjCAutoRefCount) - return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags); - - // Otherwise, we have nothing to do. - return std::make_pair(BlockCaptureEntityKind::None, Flags); } /// Generate the destroy-helper function for a block closure object: @@ -1851,6 +1874,13 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { } else if (DestroyedCapture.Kind == BlockCaptureEntityKind::ARCStrong) { EmitARCDestroyStrong(srcField, ARCImpreciseLifetime); + // If this is a C struct that requires non-trivial destruction, emit a call + // to its destructor. + } else if (DestroyedCapture.Kind == + BlockCaptureEntityKind::NonTrivialCStruct) { + QualType varType = CI.getVariable()->getType(); + pushDestroy(varType.isDestructedType(), srcField, varType); + // Otherwise we call _Block_object_dispose. It wouldn't be too // hard to just emit this as a cleanup if we wanted to make sure // that things were done in reverse. @@ -2018,6 +2048,36 @@ public: id.AddPointer(VarType.getCanonicalType().getAsOpaquePtr()); } }; + +/// Emits the copy/dispose helpers for a __block variable that is a non-trivial +/// C struct. +class NonTrivialCStructByrefHelpers final : public BlockByrefHelpers { + QualType VarType; + +public: + NonTrivialCStructByrefHelpers(CharUnits alignment, QualType type) + : BlockByrefHelpers(alignment), VarType(type) {} + + void emitCopy(CodeGenFunction &CGF, Address destField, + Address srcField) override { + CGF.callCStructMoveConstructor(CGF.MakeAddrLValue(destField, VarType), + CGF.MakeAddrLValue(srcField, VarType)); + } + + bool needsDispose() const override { + return VarType.isDestructedType(); + } + + void emitDispose(CodeGenFunction &CGF, Address field) override { + EHScopeStack::stable_iterator cleanupDepth = CGF.EHStack.stable_begin(); + CGF.pushDestroy(VarType.isDestructedType(), field, VarType); + CGF.PopCleanupBlocks(cleanupDepth); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const override { + id.AddPointer(VarType.getCanonicalType().getAsOpaquePtr()); + } +}; } // end anonymous namespace static llvm::Constant * @@ -2203,6 +2263,13 @@ CodeGenFunction::buildByrefHelpers(llvm::StructType &byrefType, CGM, byrefInfo, CXXByrefHelpers(valueAlignment, type, copyExpr)); } + // If type is a non-trivial C struct type that is non-trivial to + // destructly move or destroy, build the copy and dispose helpers. + if (type.isNonTrivialToPrimitiveDestructiveMove() == QualType::PCK_Struct || + type.isDestructedType() == QualType::DK_nontrivial_c_struct) + return ::buildByrefHelpers( + CGM, byrefInfo, NonTrivialCStructByrefHelpers(valueAlignment, type)); + // Otherwise, if we don't have a retainable type, there's nothing to do. // that the runtime does extra copies. if (!type->isObjCRetainableType()) return nullptr; |