Skip to content

Commit

Permalink
[CodeGen][Blocks] Refactor capture handling in code that generates
Browse files Browse the repository at this point in the history
block copy/destroy routines

This is a preparation commit for work on merging unique block copy/destroy
helper functions.

rdar://22950898

Differential Revision: https://reviews.llvm.org/D30345

llvm-svn: 297023
  • Loading branch information
hyp committed Mar 6, 2017
1 parent 921cab3 commit e08e5bc
Showing 1 changed file with 173 additions and 118 deletions.
291 changes: 173 additions & 118 deletions clang/lib/CodeGen/CGBlocks.cpp
Expand Up @@ -1373,6 +1373,103 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD,
return fn;
}

namespace {

/// Represents a type of copy/destroy operation that should be performed for an
/// entity that's captured by a block.
enum class BlockCaptureEntityKind {
CXXRecord, // Copy or destroy
ARCWeak,
ARCStrong,
BlockObject, // Assign or release
None
};

/// Represents a captured entity that requires extra operations in order for
/// this entity to be copied or destroyed correctly.
struct BlockCaptureManagedEntity {
BlockCaptureEntityKind Kind;
BlockFieldFlags Flags;
const BlockDecl::Capture &CI;
const CGBlockInfo::Capture &Capture;

BlockCaptureManagedEntity(BlockCaptureEntityKind Type, BlockFieldFlags Flags,
const BlockDecl::Capture &CI,
const CGBlockInfo::Capture &Capture)
: Kind(Type), Flags(Flags), CI(CI), Capture(Capture) {}
};

} // end anonymous namespace

static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
const LangOptions &LangOpts) {
if (CI.getCopyExpr()) {
assert(!CI.isByRef());
// don't bother computing flags
return std::make_pair(BlockCaptureEntityKind::CXXRecord, BlockFieldFlags());
}
BlockFieldFlags Flags;
if (CI.isByRef()) {
Flags = BLOCK_FIELD_IS_BYREF;
if (T.isObjCGCWeak())
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.
return std::make_pair(!isBlockPointer ? BlockCaptureEntityKind::ARCStrong
: BlockCaptureEntityKind::BlockObject,
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, Flags);
}

/// Find the set of block captures that need to be explicitly copied or destroy.
static void findBlockCapturedManagedEntities(
const CGBlockInfo &BlockInfo, const LangOptions &LangOpts,
SmallVectorImpl<BlockCaptureManagedEntity> &ManagedCaptures,
llvm::function_ref<std::pair<BlockCaptureEntityKind, BlockFieldFlags>(
const BlockDecl::Capture &, QualType, const LangOptions &)>
Predicate) {
for (const auto &CI : BlockInfo.getBlockDecl()->captures()) {
const VarDecl *Variable = CI.getVariable();
const CGBlockInfo::Capture &Capture = BlockInfo.getCapture(Variable);
if (Capture.isConstant())
continue;

auto Info = Predicate(CI, Variable->getType(), LangOpts);
if (Info.first != BlockCaptureEntityKind::None)
ManagedCaptures.emplace_back(Info.first, Info.second, 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
Expand Down Expand Up @@ -1431,78 +1528,28 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
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;
SmallVector<BlockCaptureManagedEntity, 4> CopiedCaptures;
findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures,
computeCopyInfoForBlockCapture);

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();

// 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.Kind == BlockCaptureEntityKind::CXXRecord);
EmitSynthesizedCXXCopyCtor(dstField, srcField, CI.getCopyExpr());
} else if (CopiedCapture.Kind == BlockCaptureEntityKind::ARCWeak) {
EmitARCCopyWeak(dstField, srcField);
} else {
llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src");
if (useARCStrongCopy) {
if (CopiedCapture.Kind == BlockCaptureEntityKind::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.
Expand All @@ -1523,13 +1570,15 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
cast<llvm::Instruction>(dstField.getPointer())->eraseFromParent();
}
} else {
assert(CopiedCapture.Kind == BlockCaptureEntityKind::BlockObject);
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 =
Expand All @@ -1553,6 +1602,52 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
return llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy);
}

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;
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());
return std::make_pair(BlockCaptureEntityKind::CXXRecord, 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:
/// static void block_destroy_helper(block_t *theBlock);
///
Expand Down Expand Up @@ -1602,79 +1697,39 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
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;
SmallVector<BlockCaptureManagedEntity, 4> DestroyedCaptures;
findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures,
computeDestroyInfoForBlockCapture);

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

// 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.Kind == BlockCaptureEntityKind::CXXRecord) {
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.Kind == BlockCaptureEntityKind::ARCWeak) {
EmitARCDestroyWeak(srcField);

// Destroy strong objects with a call if requested.
} else if (useARCStrongDestroy) {
} else if (DestroyedCapture.Kind == BlockCaptureEntityKind::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.Kind == BlockCaptureEntityKind::BlockObject);
llvm::Value *value = Builder.CreateLoad(srcField);
value = Builder.CreateBitCast(value, VoidPtrTy);
BuildBlockRelease(value, flags);
Expand Down

0 comments on commit e08e5bc

Please sign in to comment.