Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ lib/CodeGen/CGBlocks.cpp @@ -1373,6 +1373,110 @@ return fn; } +namespace { + +/// Represents a type of copy operation that should be performed for an entity +/// that's captured by a block. +enum class BlockCaptureCopyType { + CXXCopyCtor, + ARCWeak, + ARCStrong, + BlockObjectAssign +}; + +/// Represents a type of destroy operation that should be performed on an +/// entity that's captured by a block. +enum class BlockCaptureDestroyType { + CXXDtor, + ARCWeak, + ARCStrong, + BlockRelease +}; + +template struct BlockCaptureCopyDestroyGenericInfo { + T Type; + BlockFieldFlags Flags; + const BlockDecl::Capture &CI; + const CGBlockInfo::Capture &Capture; + + BlockCaptureCopyDestroyGenericInfo(T Type, BlockFieldFlags Flags, + const BlockDecl::Capture &CI, + const CGBlockInfo::Capture &Capture) + : Type(Type), Flags(Flags), CI(CI), Capture(Capture) {} +}; + +using BlockCaptureCopyInfo = + BlockCaptureCopyDestroyGenericInfo; + +using BlockCaptureDestroyInfo = + BlockCaptureCopyDestroyGenericInfo; + +} // end anonymous namespace + +/// Find the set of block captures that need to be explicitly copied. +static void +computeBlockCopyEntries(const CGBlockInfo &BlockInfo, + const LangOptions &LangOpts, + SmallVectorImpl &CopiedCaptures) { + for (const auto &CI : BlockInfo.getBlockDecl()->captures()) { + const VarDecl *Variable = CI.getVariable(); + QualType Type = Variable->getType(); + const CGBlockInfo::Capture &Capture = BlockInfo.getCapture(Variable); + if (Capture.isConstant()) + continue; + + if (CI.getCopyExpr()) { + assert(!CI.isByRef()); + // don't bother computing flags + CopiedCaptures.emplace_back(BlockCaptureCopyType::CXXCopyCtor, + BlockFieldFlags(), CI, Capture); + continue; + } + BlockFieldFlags Flags; + BlockCaptureCopyType CopyType = BlockCaptureCopyType::BlockObjectAssign; + if (CI.isByRef()) { + Flags = BLOCK_FIELD_IS_BYREF; + if (Type.isObjCGCWeak()) + Flags |= BLOCK_FIELD_IS_WEAK; + } else if (Type->isObjCRetainableType()) { + Flags = BLOCK_FIELD_IS_OBJECT; + bool isBlockPointer = Type->isBlockPointerType(); + if (isBlockPointer) + Flags = BLOCK_FIELD_IS_BLOCK; + + // Special rules for ARC captures: + Qualifiers QS = Type.getQualifiers(); + + // We need to register __weak direct captures with the runtime. + if (QS.getObjCLifetime() == Qualifiers::OCL_Weak) { + CopyType = BlockCaptureCopyType::ARCWeak; + + // We need to retain the copied value for __strong direct captures. + } else 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. + if (!isBlockPointer) + CopyType = BlockCaptureCopyType::ARCStrong; + + // Non-ARC captures of retainable pointers are strong and + // therefore require a call to _Block_object_assign. + } else if (!QS.getObjCLifetime() && !LangOpts.ObjCAutoRefCount) { + // fall through + + // Otherwise the memcpy is fine. + } else { + continue; + } + + // For all other types, the memcpy is fine. + } else { + continue; + } + CopiedCaptures.emplace_back(CopyType, Flags, CI, Capture); + } +} + /// Generate the copy-helper function for a block closure object: /// static void block_copy_helper(block_t *dst, block_t *src); /// The runtime will have previously initialized 'dst' by doing a @@ -1431,78 +1535,27 @@ dst = Address(Builder.CreateLoad(dst), blockInfo.BlockAlign); dst = Builder.CreateBitCast(dst, structPtrTy, "block.dest"); - const BlockDecl *blockDecl = blockInfo.getBlockDecl(); - - for (const auto &CI : blockDecl->captures()) { - const VarDecl *variable = CI.getVariable(); - QualType type = variable->getType(); - - const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable); - if (capture.isConstant()) continue; - - const Expr *copyExpr = CI.getCopyExpr(); - BlockFieldFlags flags; - - bool useARCWeakCopy = false; - bool useARCStrongCopy = false; - - if (copyExpr) { - assert(!CI.isByRef()); - // don't bother computing flags - - } else if (CI.isByRef()) { - flags = BLOCK_FIELD_IS_BYREF; - if (type.isObjCGCWeak()) - flags |= BLOCK_FIELD_IS_WEAK; - - } else if (type->isObjCRetainableType()) { - flags = BLOCK_FIELD_IS_OBJECT; - bool isBlockPointer = type->isBlockPointerType(); - if (isBlockPointer) - flags = BLOCK_FIELD_IS_BLOCK; - - // Special rules for ARC captures: - Qualifiers qs = type.getQualifiers(); + SmallVector CopiedCaptures; + computeBlockCopyEntries(blockInfo, getLangOpts(), CopiedCaptures); - // We need to register __weak direct captures with the runtime. - if (qs.getObjCLifetime() == Qualifiers::OCL_Weak) { - useARCWeakCopy = true; - - // We need to retain the copied value for __strong direct captures. - } else 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. - if (!isBlockPointer) - useARCStrongCopy = true; - - // Non-ARC captures of retainable pointers are strong and - // therefore require a call to _Block_object_assign. - } else if (!qs.getObjCLifetime() && !getLangOpts().ObjCAutoRefCount) { - // fall through - - // Otherwise the memcpy is fine. - } else { - continue; - } - - // For all other types, the memcpy is fine. - } else { - continue; - } + for (const auto &CopiedCapture : CopiedCaptures) { + const BlockDecl::Capture &CI = CopiedCapture.CI; + const CGBlockInfo::Capture &capture = CopiedCapture.Capture; + BlockFieldFlags flags = CopiedCapture.Flags; unsigned index = capture.getIndex(); Address srcField = Builder.CreateStructGEP(src, index, capture.getOffset()); Address dstField = Builder.CreateStructGEP(dst, index, capture.getOffset()); // If there's an explicit copy expression, we do that. - if (copyExpr) { - EmitSynthesizedCXXCopyCtor(dstField, srcField, copyExpr); - } else if (useARCWeakCopy) { + if (CI.getCopyExpr()) { + assert(CopiedCapture.Type == BlockCaptureCopyType::CXXCopyCtor); + EmitSynthesizedCXXCopyCtor(dstField, srcField, CI.getCopyExpr()); + } else if (CopiedCapture.Type == BlockCaptureCopyType::ARCWeak) { EmitARCCopyWeak(dstField, srcField); } else { llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src"); - if (useARCStrongCopy) { + if (CopiedCapture.Type == BlockCaptureCopyType::ARCStrong) { // At -O0, store null into the destination field (so that the // storeStrong doesn't over-release) and then call storeStrong. // This is a workaround to not having an initStrong call. @@ -1523,6 +1576,7 @@ cast(dstField.getPointer())->eraseFromParent(); } } else { + assert(CopiedCapture.Type == BlockCaptureCopyType::BlockObjectAssign); srcValue = Builder.CreateBitCast(srcValue, VoidPtrTy); llvm::Value *dstAddr = Builder.CreateBitCast(dstField.getPointer(), VoidPtrTy); @@ -1530,6 +1584,7 @@ dstAddr, srcValue, llvm::ConstantInt::get(Int32Ty, flags.getBitMask()) }; + const VarDecl *variable = CI.getVariable(); bool copyCanThrow = false; if (CI.isByRef() && variable->getType()->getAsCXXRecordDecl()) { const Expr *copyExpr = @@ -1553,6 +1608,63 @@ return llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy); } +/// Find the set of block captures that need to be explicitly destroyed. +static void computeBlockDestroyEntries( + const CGBlockInfo &BlockInfo, const LangOptions &LangOpts, + SmallVectorImpl &DestroyedCaptures) { + for (const auto &CI : BlockInfo.getBlockDecl()->captures()) { + const VarDecl *Variable = CI.getVariable(); + QualType Type = Variable->getType(); + + const CGBlockInfo::Capture &Capture = BlockInfo.getCapture(Variable); + if (Capture.isConstant()) + continue; + + BlockFieldFlags Flags; + BlockCaptureDestroyType DestroyType = BlockCaptureDestroyType::BlockRelease; + + if (CI.isByRef()) { + Flags = BLOCK_FIELD_IS_BYREF; + if (Type.isObjCGCWeak()) + Flags |= BLOCK_FIELD_IS_WEAK; + } else if (const CXXRecordDecl *Record = Type->getAsCXXRecordDecl()) { + if (!Record->hasTrivialDestructor()) + DestroyedCaptures.emplace_back(BlockCaptureDestroyType::CXXDtor, + BlockFieldFlags(), CI, Capture); + continue; + } else if (Type->isObjCRetainableType()) { + Flags = BLOCK_FIELD_IS_OBJECT; + if (Type->isBlockPointerType()) + Flags = BLOCK_FIELD_IS_BLOCK; + + // Special rules for ARC captures. + Qualifiers QS = Type.getQualifiers(); + + // Use objc_storeStrong for __strong direct captures; the + // dynamic tools really like it when we do this. + if (QS.getObjCLifetime() == Qualifiers::OCL_Strong) { + DestroyType = BlockCaptureDestroyType::ARCStrong; + + // Support __weak direct captures. + } else if (QS.getObjCLifetime() == Qualifiers::OCL_Weak) { + DestroyType = BlockCaptureDestroyType::ARCWeak; + + // Non-ARC captures are strong, and we need to use + // _Block_object_dispose. + } else if (!QS.hasObjCLifetime() && !LangOpts.ObjCAutoRefCount) { + // fall through + + // Otherwise, we have nothing to do. + } else { + continue; + } + } else { + continue; + } + DestroyedCaptures.emplace_back(DestroyType, Flags, CI, Capture); + } +} + /// Generate the destroy-helper function for a block closure object: /// static void block_destroy_helper(block_t *theBlock); /// @@ -1602,79 +1714,38 @@ src = Address(Builder.CreateLoad(src), blockInfo.BlockAlign); src = Builder.CreateBitCast(src, structPtrTy, "block"); - const BlockDecl *blockDecl = blockInfo.getBlockDecl(); - CodeGenFunction::RunCleanupsScope cleanups(*this); - for (const auto &CI : blockDecl->captures()) { - const VarDecl *variable = CI.getVariable(); - QualType type = variable->getType(); - - const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable); - if (capture.isConstant()) continue; - - BlockFieldFlags flags; - const CXXDestructorDecl *dtor = nullptr; - - bool useARCWeakDestroy = false; - bool useARCStrongDestroy = false; - - if (CI.isByRef()) { - flags = BLOCK_FIELD_IS_BYREF; - if (type.isObjCGCWeak()) - flags |= BLOCK_FIELD_IS_WEAK; - } else if (const CXXRecordDecl *record = type->getAsCXXRecordDecl()) { - if (record->hasTrivialDestructor()) - continue; - dtor = record->getDestructor(); - } else if (type->isObjCRetainableType()) { - flags = BLOCK_FIELD_IS_OBJECT; - if (type->isBlockPointerType()) - flags = BLOCK_FIELD_IS_BLOCK; - - // Special rules for ARC captures. - Qualifiers qs = type.getQualifiers(); - - // Use objc_storeStrong for __strong direct captures; the - // dynamic tools really like it when we do this. - if (qs.getObjCLifetime() == Qualifiers::OCL_Strong) { - useARCStrongDestroy = true; - - // Support __weak direct captures. - } else if (qs.getObjCLifetime() == Qualifiers::OCL_Weak) { - useARCWeakDestroy = true; - - // Non-ARC captures are strong, and we need to use _Block_object_dispose. - } else if (!qs.hasObjCLifetime() && !getLangOpts().ObjCAutoRefCount) { - // fall through + SmallVector DestroyedCaptures; + computeBlockDestroyEntries(blockInfo, getLangOpts(), DestroyedCaptures); - // Otherwise, we have nothing to do. - } else { - continue; - } - } else { - continue; - } + for (const auto &DestroyedCapture : DestroyedCaptures) { + const BlockDecl::Capture &CI = DestroyedCapture.CI; + const CGBlockInfo::Capture &capture = DestroyedCapture.Capture; + BlockFieldFlags flags = DestroyedCapture.Flags; Address srcField = Builder.CreateStructGEP(src, capture.getIndex(), capture.getOffset()); - // If there's an explicit copy expression, we do that. - if (dtor) { - PushDestructorCleanup(dtor, srcField); + // If the captured record has a destructor then call it. + if (DestroyedCapture.Type == BlockCaptureDestroyType::CXXDtor) { + const auto *Dtor = + CI.getVariable()->getType()->getAsCXXRecordDecl()->getDestructor(); + PushDestructorCleanup(Dtor, srcField); - // If this is a __weak capture, emit the release directly. - } else if (useARCWeakDestroy) { + // If this is a __weak capture, emit the release directly. + } else if (DestroyedCapture.Type == BlockCaptureDestroyType::ARCWeak) { EmitARCDestroyWeak(srcField); // Destroy strong objects with a call if requested. - } else if (useARCStrongDestroy) { + } else if (DestroyedCapture.Type == BlockCaptureDestroyType::ARCStrong) { EmitARCDestroyStrong(srcField, ARCImpreciseLifetime); // 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. } else { + assert(DestroyedCapture.Type == BlockCaptureDestroyType::BlockRelease); llvm::Value *value = Builder.CreateLoad(srcField); value = Builder.CreateBitCast(value, VoidPtrTy); BuildBlockRelease(value, flags);