arphaman created this revision.
This patch refactors the code figures out which block captures need to be
copied/destroyed using special copy/destroy code.
This is a preparation patch for work on merging block copy/destroy routines.
Thanks for taking a look!
Repository:
rL LLVM
https://reviews.llvm.org/D30345
Files:
lib/CodeGen/CGBlocks.cpp
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 <typename T> 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<BlockCaptureCopyType>;
+
+using BlockCaptureDestroyInfo =
+ BlockCaptureCopyDestroyGenericInfo<BlockCaptureDestroyType>;
+
+} // 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<BlockCaptureCopyInfo> &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<BlockCaptureCopyInfo, 4> 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,13 +1576,15 @@
cast<llvm::Instruction>(dstField.getPointer())->eraseFromParent();
}
} else {
+ assert(CopiedCapture.Type == BlockCaptureCopyType::BlockObjectAssign);
srcValue = Builder.CreateBitCast(srcValue, VoidPtrTy);
llvm::Value *dstAddr =
Builder.CreateBitCast(dstField.getPointer(), VoidPtrTy);
llvm::Value *args[] = {
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<BlockCaptureDestroyInfo> &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<BlockCaptureDestroyInfo, 4> 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);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits