[clang] e5df9cc - [CodeGen] Treat ObjC `__unsafe_unretained` and class types as trivial
Akira Hatanaka via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 11 11:22:15 PST 2022
Author: Akira Hatanaka
Date: 2022-01-11T11:18:24-08:00
New Revision: e5df9cc098b554ebb066792e40cbde6feddc57bc
URL: https://github.com/llvm/llvm-project/commit/e5df9cc098b554ebb066792e40cbde6feddc57bc
DIFF: https://github.com/llvm/llvm-project/commit/e5df9cc098b554ebb066792e40cbde6feddc57bc.diff
LOG: [CodeGen] Treat ObjC `__unsafe_unretained` and class types as trivial
when generating copy/dispose helper functions
Analyze the block captures just once before generating copy/dispose
block helper functions and honor the inert `__unsafe_unretained`
qualifier. This refactor fixes a bug where captures of ObjC
`__unsafe_unretained` and class types were needlessly retained/released
by the copy/dispose helper functions.
Differential Revision: https://reviews.llvm.org/D116948
Added:
Modified:
clang/lib/CodeGen/CGBlocks.cpp
clang/lib/CodeGen/CGBlocks.h
clang/lib/CodeGen/CGObjCMac.cpp
clang/test/CodeGenObjC/arc-blocks.m
clang/test/CodeGenObjC/blocks.m
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp
index 7bb6dbb8a8aca..1f1de3df857c9 100644
--- a/clang/lib/CodeGen/CGBlocks.cpp
+++ b/clang/lib/CodeGen/CGBlocks.cpp
@@ -33,10 +33,10 @@ using namespace clang;
using namespace CodeGen;
CGBlockInfo::CGBlockInfo(const BlockDecl *block, StringRef name)
- : Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false),
- HasCXXObject(false), UsesStret(false), HasCapturedVariableLayout(false),
- CapturesNonExternalType(false), LocalAddress(Address::invalid()),
- StructureType(nullptr), Block(block) {
+ : Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false),
+ NoEscape(false), HasCXXObject(false), UsesStret(false),
+ HasCapturedVariableLayout(false), CapturesNonExternalType(false),
+ LocalAddress(Address::invalid()), StructureType(nullptr), Block(block) {
// Skip asm prefix, if any. 'name' is usually taken directly from
// the mangled name of the enclosing function.
@@ -66,17 +66,6 @@ static llvm::Constant *buildDisposeHelper(CodeGenModule &CGM,
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,
- NonTrivialCStruct,
- 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 {
@@ -110,11 +99,7 @@ enum class CaptureStrKind {
} // end anonymous namespace
-static void findBlockCapturedManagedEntities(
- const CGBlockInfo &BlockInfo, const LangOptions &LangOpts,
- SmallVectorImpl<BlockCaptureManagedEntity> &ManagedCaptures);
-
-static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
+static std::string getBlockCaptureStr(const CGBlockInfo::Capture &Cap,
CaptureStrKind StrKind,
CharUnits BlockAlignment,
CodeGenModule &CGM);
@@ -124,34 +109,33 @@ static std::string getBlockDescriptorName(const CGBlockInfo &BlockInfo,
std::string Name = "__block_descriptor_";
Name += llvm::to_string(BlockInfo.BlockSize.getQuantity()) + "_";
- if (BlockInfo.needsCopyDisposeHelpers()) {
+ if (BlockInfo.NeedsCopyDispose) {
if (CGM.getLangOpts().Exceptions)
Name += "e";
if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions)
Name += "a";
Name += llvm::to_string(BlockInfo.BlockAlign.getQuantity()) + "_";
- SmallVector<BlockCaptureManagedEntity, 4> ManagedCaptures;
- findBlockCapturedManagedEntities(BlockInfo, CGM.getContext().getLangOpts(),
- ManagedCaptures);
+ for (auto &Cap : BlockInfo.SortedCaptures) {
+ if (Cap.isConstantOrTrivial())
+ continue;
- for (const BlockCaptureManagedEntity &E : ManagedCaptures) {
- Name += llvm::to_string(E.Capture->getOffset().getQuantity());
+ Name += llvm::to_string(Cap.getOffset().getQuantity());
- if (E.CopyKind == E.DisposeKind) {
+ if (Cap.CopyKind == Cap.DisposeKind) {
// If CopyKind and DisposeKind are the same, merge the capture
// information.
- assert(E.CopyKind != BlockCaptureEntityKind::None &&
+ assert(Cap.CopyKind != BlockCaptureEntityKind::None &&
"shouldn't see BlockCaptureManagedEntity that is None");
- Name += getBlockCaptureStr(E, CaptureStrKind::Merged,
+ Name += getBlockCaptureStr(Cap, CaptureStrKind::Merged,
BlockInfo.BlockAlign, CGM);
} else {
// If CopyKind and DisposeKind are not the same, which can happen when
// either Kind is None or the captured object is a __strong block,
// concatenate the copy and dispose strings.
- Name += getBlockCaptureStr(E, CaptureStrKind::CopyHelper,
+ Name += getBlockCaptureStr(Cap, CaptureStrKind::CopyHelper,
BlockInfo.BlockAlign, CGM);
- Name += getBlockCaptureStr(E, CaptureStrKind::DisposeHelper,
+ Name += getBlockCaptureStr(Cap, CaptureStrKind::DisposeHelper,
BlockInfo.BlockAlign, CGM);
}
}
@@ -223,7 +207,7 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM,
// Optional copy/dispose helpers.
bool hasInternalHelper = false;
- if (blockInfo.needsCopyDisposeHelpers()) {
+ if (blockInfo.NeedsCopyDispose) {
// copy_func_helper_decl
llvm::Constant *copyHelper = buildCopyHelper(CGM, blockInfo);
elements.add(copyHelper);
@@ -340,17 +324,21 @@ namespace {
struct BlockLayoutChunk {
CharUnits Alignment;
CharUnits Size;
- Qualifiers::ObjCLifetime Lifetime;
const BlockDecl::Capture *Capture; // null for 'this'
llvm::Type *Type;
QualType FieldType;
+ BlockCaptureEntityKind CopyKind, DisposeKind;
+ BlockFieldFlags CopyFlags, DisposeFlags;
BlockLayoutChunk(CharUnits align, CharUnits size,
- Qualifiers::ObjCLifetime lifetime,
- const BlockDecl::Capture *capture,
- llvm::Type *type, QualType fieldType)
- : Alignment(align), Size(size), Lifetime(lifetime),
- Capture(capture), Type(type), FieldType(fieldType) {}
+ const BlockDecl::Capture *capture, llvm::Type *type,
+ QualType fieldType, BlockCaptureEntityKind CopyKind,
+ BlockFieldFlags CopyFlags,
+ BlockCaptureEntityKind DisposeKind,
+ BlockFieldFlags DisposeFlags)
+ : Alignment(align), Size(size), Capture(capture), Type(type),
+ FieldType(fieldType), CopyKind(CopyKind), DisposeKind(DisposeKind),
+ CopyFlags(CopyFlags), DisposeFlags(DisposeFlags) {}
/// Tell the block info that this chunk has the given field index.
void setIndex(CGBlockInfo &info, unsigned index, CharUnits offset) {
@@ -358,32 +346,93 @@ namespace {
info.CXXThisIndex = index;
info.CXXThisOffset = offset;
} else {
- auto C = CGBlockInfo::Capture::makeIndex(index, offset, FieldType);
- info.Captures.insert({Capture->getVariable(), C});
+ info.SortedCaptures.push_back(CGBlockInfo::Capture::makeIndex(
+ index, offset, FieldType, CopyKind, CopyFlags, DisposeKind,
+ DisposeFlags, Capture));
}
}
+
+ bool isTrivial() const {
+ return CopyKind == BlockCaptureEntityKind::None &&
+ DisposeKind == BlockCaptureEntityKind::None;
+ }
};
- /// Order by 1) all __strong together 2) next, all byfref together 3) next,
- /// all __weak together. Preserve descending alignment in all situations.
+ /// Order by 1) all __strong together 2) next, all block together 3) next,
+ /// all byref together 4) next, all __weak together. Preserve descending
+ /// alignment in all situations.
bool operator<(const BlockLayoutChunk &left, const BlockLayoutChunk &right) {
if (left.Alignment != right.Alignment)
return left.Alignment > right.Alignment;
auto getPrefOrder = [](const BlockLayoutChunk &chunk) {
- if (chunk.Capture && chunk.Capture->isByRef())
- return 1;
- if (chunk.Lifetime == Qualifiers::OCL_Strong)
+ switch (chunk.CopyKind) {
+ case BlockCaptureEntityKind::ARCStrong:
return 0;
- if (chunk.Lifetime == Qualifiers::OCL_Weak)
- return 2;
- return 3;
+ case BlockCaptureEntityKind::BlockObject:
+ switch (chunk.CopyFlags.getBitMask()) {
+ case BLOCK_FIELD_IS_OBJECT:
+ return 0;
+ case BLOCK_FIELD_IS_BLOCK:
+ return 1;
+ case BLOCK_FIELD_IS_BYREF:
+ return 2;
+ default:
+ break;
+ }
+ break;
+ case BlockCaptureEntityKind::ARCWeak:
+ return 3;
+ default:
+ break;
+ }
+ return 4;
};
return getPrefOrder(left) < getPrefOrder(right);
}
} // end anonymous namespace
+static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
+computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
+ const LangOptions &LangOpts);
+
+static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
+computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
+ const LangOptions &LangOpts);
+
+static void addBlockLayout(CharUnits align, CharUnits size,
+ const BlockDecl::Capture *capture, llvm::Type *type,
+ QualType fieldType,
+ SmallVectorImpl<BlockLayoutChunk> &Layout,
+ CGBlockInfo &Info, CodeGenModule &CGM) {
+ if (!capture) {
+ // 'this' capture.
+ Layout.push_back(BlockLayoutChunk(
+ align, size, capture, type, fieldType, BlockCaptureEntityKind::None,
+ BlockFieldFlags(), BlockCaptureEntityKind::None, BlockFieldFlags()));
+ return;
+ }
+
+ const LangOptions &LangOpts = CGM.getLangOpts();
+ BlockCaptureEntityKind CopyKind, DisposeKind;
+ BlockFieldFlags CopyFlags, DisposeFlags;
+
+ std::tie(CopyKind, CopyFlags) =
+ computeCopyInfoForBlockCapture(*capture, fieldType, LangOpts);
+ std::tie(DisposeKind, DisposeFlags) =
+ computeDestroyInfoForBlockCapture(*capture, fieldType, LangOpts);
+ Layout.push_back(BlockLayoutChunk(align, size, capture, type, fieldType,
+ CopyKind, CopyFlags, DisposeKind,
+ DisposeFlags));
+
+ if (Info.NoEscape)
+ return;
+
+ if (!Layout.back().isTrivial())
+ Info.NeedsCopyDispose = true;
+}
+
/// Determines if the given type is safe for constant capture in C++.
static bool isSafeForCXXConstantCapture(QualType type) {
const RecordType *recordType =
@@ -541,6 +590,9 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
CGM.getLangOpts().getGC() == LangOptions::NonGC)
info.HasCapturedVariableLayout = true;
+ if (block->doesNotEscape())
+ info.NoEscape = true;
+
// Collect the layout chunks.
SmallVector<BlockLayoutChunk, 16> layout;
layout.reserve(block->capturesCXXThis() +
@@ -560,9 +612,8 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
auto TInfo = CGM.getContext().getTypeInfoInChars(thisType);
maxFieldAlign = std::max(maxFieldAlign, TInfo.Align);
- layout.push_back(BlockLayoutChunk(TInfo.Align, TInfo.Width,
- Qualifiers::OCL_None,
- nullptr, llvmType, thisType));
+ addBlockLayout(TInfo.Align, TInfo.Width, nullptr, llvmType, thisType,
+ layout, info, CGM);
}
// Next, all the block captures.
@@ -570,9 +621,6 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
const VarDecl *variable = CI.getVariable();
if (CI.isEscapingByref()) {
- // We have to copy/dispose of the __block reference.
- info.NeedsCopyDispose = true;
-
// Just use void* instead of a pointer to the byref type.
CharUnits align = CGM.getPointerAlign();
maxFieldAlign = std::max(maxFieldAlign, align);
@@ -581,72 +629,28 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
// the capture field type should always match.
assert(CGF && getCaptureFieldType(*CGF, CI) == variable->getType() &&
"capture type
diff ers from the variable type");
- layout.push_back(BlockLayoutChunk(align, CGM.getPointerSize(),
- Qualifiers::OCL_None, &CI,
- CGM.VoidPtrTy, variable->getType()));
+ addBlockLayout(align, CGM.getPointerSize(), &CI, CGM.VoidPtrTy,
+ variable->getType(), layout, info, CGM);
continue;
}
// Otherwise, build a layout chunk with the size and alignment of
// the declaration.
if (llvm::Constant *constant = tryCaptureAsConstant(CGM, CGF, variable)) {
- info.Captures[variable] = CGBlockInfo::Capture::makeConstant(constant);
+ info.SortedCaptures.push_back(
+ CGBlockInfo::Capture::makeConstant(constant, &CI));
continue;
}
QualType VT = getCaptureFieldType(*CGF, CI);
- // If we have a lifetime qualifier, honor it for capture purposes.
- // That includes *not* copying it if it's __unsafe_unretained.
- Qualifiers::ObjCLifetime lifetime = VT.getObjCLifetime();
- if (lifetime) {
- switch (lifetime) {
- case Qualifiers::OCL_None: llvm_unreachable("impossible");
- case Qualifiers::OCL_ExplicitNone:
- case Qualifiers::OCL_Autoreleasing:
- break;
-
- case Qualifiers::OCL_Strong:
- case Qualifiers::OCL_Weak:
- info.NeedsCopyDispose = true;
- }
-
- // Block pointers require copy/dispose. So do Objective-C pointers.
- } else if (VT->isObjCRetainableType()) {
- // But honor the inert __unsafe_unretained qualifier, which doesn't
- // actually make it into the type system.
- if (VT->isObjCInertUnsafeUnretainedType()) {
- lifetime = Qualifiers::OCL_ExplicitNone;
- } else {
- info.NeedsCopyDispose = true;
- // used for mrr below.
- lifetime = Qualifiers::OCL_Strong;
- }
-
- // So do types that require non-trivial copy construction.
- } else if (CI.hasCopyExpr()) {
- info.NeedsCopyDispose = true;
- info.HasCXXObject = true;
- if (!VT->getAsCXXRecordDecl()->isExternallyVisible())
- info.CapturesNonExternalType = true;
-
- // So do C structs that require non-trivial copy construction or
- // destruction.
- } else if (VT.isNonTrivialToPrimitiveCopy() == QualType::PCK_Struct ||
- VT.isDestructedType() == QualType::DK_nontrivial_c_struct) {
- info.NeedsCopyDispose = true;
-
- // And so do types with destructors.
- } else if (CGM.getLangOpts().CPlusPlus) {
- if (const CXXRecordDecl *record = VT->getAsCXXRecordDecl()) {
- if (!record->hasTrivialDestructor()) {
+ if (CGM.getLangOpts().CPlusPlus)
+ if (const CXXRecordDecl *record = VT->getAsCXXRecordDecl())
+ if (CI.hasCopyExpr() || !record->hasTrivialDestructor()) {
info.HasCXXObject = true;
- info.NeedsCopyDispose = true;
if (!record->isExternallyVisible())
info.CapturesNonExternalType = true;
}
- }
- }
CharUnits size = C.getTypeSizeInChars(VT);
CharUnits align = C.getDeclAlign(variable);
@@ -656,8 +660,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
llvm::Type *llvmType =
CGM.getTypes().ConvertTypeForMem(VT);
- layout.push_back(
- BlockLayoutChunk(align, size, lifetime, &CI, llvmType, VT));
+ addBlockLayout(align, size, &CI, llvmType, VT, layout, info, CGM);
}
// If that was everything, we're done here.
@@ -665,6 +668,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
info.StructureType =
llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
info.CanBeGlobal = true;
+ info.buildCaptureMap();
return;
}
@@ -718,6 +722,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
// ...until we get to the alignment of the maximum field.
if (endAlign >= maxFieldAlign) {
+ ++li;
break;
}
}
@@ -770,6 +775,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
endAlign = getLowBit(blockSize);
}
+ info.buildCaptureMap();
info.StructureType =
llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
}
@@ -826,7 +832,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// If the block is non-escaping, set field 'isa 'to NSConcreteGlobalBlock
// and set the BLOCK_IS_GLOBAL bit of field 'flags'. Copying a non-escaping
// block just returns the original block and releasing it is a no-op.
- llvm::Constant *blockISA = blockInfo.getBlockDecl()->doesNotEscape()
+ llvm::Constant *blockISA = blockInfo.NoEscape
? CGM.getNSConcreteGlobalBlock()
: CGM.getNSConcreteStackBlock();
isa = llvm::ConstantExpr::getBitCast(blockISA, VoidPtrTy);
@@ -838,13 +844,13 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
flags = BLOCK_HAS_SIGNATURE;
if (blockInfo.HasCapturedVariableLayout)
flags |= BLOCK_HAS_EXTENDED_LAYOUT;
- if (blockInfo.needsCopyDisposeHelpers())
+ if (blockInfo.NeedsCopyDispose)
flags |= BLOCK_HAS_COPY_DISPOSE;
if (blockInfo.HasCXXObject)
flags |= BLOCK_HAS_CXX_OBJ;
if (blockInfo.UsesStret)
flags |= BLOCK_USE_STRET;
- if (blockInfo.getBlockDecl()->doesNotEscape())
+ if (blockInfo.NoEscape)
flags |= BLOCK_IS_NOESCAPE | BLOCK_IS_GLOBAL;
}
@@ -1033,7 +1039,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
}
// Push a cleanup for the capture if necessary.
- if (!blockInfo.NeedsCopyDispose)
+ if (!blockInfo.NoEscape && !blockInfo.NeedsCopyDispose)
continue;
// Ignore __block captures; there's nothing special in the on-stack block
@@ -1654,6 +1660,11 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
// For all other types, the memcpy is fine.
return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags());
+ // Honor the inert __unsafe_unretained qualifier, which doesn't actually
+ // make it into the type system.
+ if (T->isObjCInertUnsafeUnretainedType())
+ return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags());
+
// Special rules for ARC captures:
Qualifiers QS = T.getQualifiers();
@@ -1669,34 +1680,6 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
llvm_unreachable("after exhaustive PrimitiveCopyKind switch");
}
-static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
-computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
- const LangOptions &LangOpts);
-
-/// 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) {
- for (const auto &CI : BlockInfo.getBlockDecl()->captures()) {
- const VarDecl *Variable = CI.getVariable();
- const CGBlockInfo::Capture &Capture = BlockInfo.getCapture(Variable);
- if (Capture.isConstant())
- continue;
-
- QualType VT = Capture.fieldType();
- auto CopyInfo = computeCopyInfoForBlockCapture(CI, VT, LangOpts);
- auto DisposeInfo = computeDestroyInfoForBlockCapture(CI, VT, LangOpts);
- if (CopyInfo.first != BlockCaptureEntityKind::None ||
- DisposeInfo.first != BlockCaptureEntityKind::None)
- ManagedCaptures.emplace_back(CopyInfo.first, DisposeInfo.first,
- CopyInfo.second, DisposeInfo.second, CI,
- Capture);
- }
-
- // Sort the captures by offset.
- llvm::sort(ManagedCaptures);
-}
-
namespace {
/// Release a __block variable.
struct CallBlockRelease final : EHScopeStack::Cleanup {
@@ -1732,13 +1715,13 @@ bool CodeGenFunction::cxxDestructorCanThrow(QualType T) {
}
// Return a string that has the information about a capture.
-static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
+static std::string getBlockCaptureStr(const CGBlockInfo::Capture &Cap,
CaptureStrKind StrKind,
CharUnits BlockAlignment,
CodeGenModule &CGM) {
std::string Str;
ASTContext &Ctx = CGM.getContext();
- const BlockDecl::Capture &CI = *E.CI;
+ const BlockDecl::Capture &CI = *Cap.Cap;
QualType CaptureTy = CI.getVariable()->getType();
BlockCaptureEntityKind Kind;
@@ -1747,15 +1730,16 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
// CaptureStrKind::Merged should be passed only when the operations and the
// flags are the same for copy and dispose.
assert((StrKind != CaptureStrKind::Merged ||
- (E.CopyKind == E.DisposeKind && E.CopyFlags == E.DisposeFlags)) &&
+ (Cap.CopyKind == Cap.DisposeKind &&
+ Cap.CopyFlags == Cap.DisposeFlags)) &&
"
diff erent operations and flags");
if (StrKind == CaptureStrKind::DisposeHelper) {
- Kind = E.DisposeKind;
- Flags = E.DisposeFlags;
+ Kind = Cap.DisposeKind;
+ Flags = Cap.DisposeFlags;
} else {
- Kind = E.CopyKind;
- Flags = E.CopyFlags;
+ Kind = Cap.CopyKind;
+ Flags = Cap.CopyFlags;
}
switch (Kind) {
@@ -1803,8 +1787,7 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
}
case BlockCaptureEntityKind::NonTrivialCStruct: {
bool IsVolatile = CaptureTy.isVolatileQualified();
- CharUnits Alignment =
- BlockAlignment.alignmentAtOffset(E.Capture->getOffset());
+ CharUnits Alignment = BlockAlignment.alignmentAtOffset(Cap.getOffset());
Str += "n";
std::string FuncStr;
@@ -1829,7 +1812,7 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
}
static std::string getCopyDestroyHelperFuncName(
- const SmallVectorImpl<BlockCaptureManagedEntity> &Captures,
+ const SmallVectorImpl<CGBlockInfo::Capture> &Captures,
CharUnits BlockAlignment, CaptureStrKind StrKind, CodeGenModule &CGM) {
assert((StrKind == CaptureStrKind::CopyHelper ||
StrKind == CaptureStrKind::DisposeHelper) &&
@@ -1843,9 +1826,11 @@ static std::string getCopyDestroyHelperFuncName(
Name += "a";
Name += llvm::to_string(BlockAlignment.getQuantity()) + "_";
- for (const BlockCaptureManagedEntity &E : Captures) {
- Name += llvm::to_string(E.Capture->getOffset().getQuantity());
- Name += getBlockCaptureStr(E, StrKind, BlockAlignment, CGM);
+ for (auto &Cap : Captures) {
+ if (Cap.isConstantOrTrivial())
+ continue;
+ Name += llvm::to_string(Cap.getOffset().getQuantity());
+ Name += getBlockCaptureStr(Cap, StrKind, BlockAlignment, CGM);
}
return Name;
@@ -1916,11 +1901,9 @@ static void setBlockHelperAttributesVisibility(bool CapturesNonExternalType,
/// the contents of an individual __block variable to the heap.
llvm::Constant *
CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
- SmallVector<BlockCaptureManagedEntity, 4> CopiedCaptures;
- findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures);
- std::string FuncName =
- getCopyDestroyHelperFuncName(CopiedCaptures, blockInfo.BlockAlign,
- CaptureStrKind::CopyHelper, CGM);
+ std::string FuncName = getCopyDestroyHelperFuncName(
+ blockInfo.SortedCaptures, blockInfo.BlockAlign,
+ CaptureStrKind::CopyHelper, CGM);
if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName))
return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy);
@@ -1967,17 +1950,19 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
dst = Address(Builder.CreateLoad(dst), blockInfo.BlockAlign);
dst = Builder.CreateBitCast(dst, structPtrTy, "block.dest");
- for (const auto &CopiedCapture : CopiedCaptures) {
- const BlockDecl::Capture &CI = *CopiedCapture.CI;
- const CGBlockInfo::Capture &capture = *CopiedCapture.Capture;
+ for (auto &capture : blockInfo.SortedCaptures) {
+ if (capture.isConstantOrTrivial())
+ continue;
+
+ const BlockDecl::Capture &CI = *capture.Cap;
QualType captureType = CI.getVariable()->getType();
- BlockFieldFlags flags = CopiedCapture.CopyFlags;
+ BlockFieldFlags flags = capture.CopyFlags;
unsigned index = capture.getIndex();
Address srcField = Builder.CreateStructGEP(src, index);
Address dstField = Builder.CreateStructGEP(dst, index);
- switch (CopiedCapture.CopyKind) {
+ switch (capture.CopyKind) {
case BlockCaptureEntityKind::CXXRecord:
// If there's an explicit copy expression, we do that.
assert(CI.getCopyExpr() && "copy expression for variable is missing");
@@ -2040,7 +2025,7 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
// Ensure that we destroy the copied object if an exception is thrown later
// in the helper function.
- pushCaptureCleanup(CopiedCapture.CopyKind, dstField, captureType, flags,
+ pushCaptureCleanup(capture.CopyKind, dstField, captureType, flags,
/*ForCopyHelper*/ true, CI.getVariable(), *this);
}
@@ -2085,8 +2070,10 @@ computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
BlockFieldFlags());
case QualType::DK_none: {
// Non-ARC captures are strong, and we need to use _Block_object_dispose.
+ // But honor the inert __unsafe_unretained qualifier, which doesn't actually
+ // make it into the type system.
if (T->isObjCRetainableType() && !T.getQualifiers().hasObjCLifetime() &&
- !LangOpts.ObjCAutoRefCount)
+ !LangOpts.ObjCAutoRefCount && !T->isObjCInertUnsafeUnretainedType())
return std::make_pair(BlockCaptureEntityKind::BlockObject,
getBlockFieldFlagsForObjCObjectPointer(CI, T));
// Otherwise, we have nothing to do.
@@ -2105,11 +2092,9 @@ computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
/// variable.
llvm::Constant *
CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
- SmallVector<BlockCaptureManagedEntity, 4> DestroyedCaptures;
- findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures);
- std::string FuncName =
- getCopyDestroyHelperFuncName(DestroyedCaptures, blockInfo.BlockAlign,
- CaptureStrKind::DisposeHelper, CGM);
+ std::string FuncName = getCopyDestroyHelperFuncName(
+ blockInfo.SortedCaptures, blockInfo.BlockAlign,
+ CaptureStrKind::DisposeHelper, CGM);
if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName))
return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy);
@@ -2153,14 +2138,16 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
CodeGenFunction::RunCleanupsScope cleanups(*this);
- for (const auto &DestroyedCapture : DestroyedCaptures) {
- const BlockDecl::Capture &CI = *DestroyedCapture.CI;
- const CGBlockInfo::Capture &capture = *DestroyedCapture.Capture;
- BlockFieldFlags flags = DestroyedCapture.DisposeFlags;
+ for (auto &capture : blockInfo.SortedCaptures) {
+ if (capture.isConstantOrTrivial())
+ continue;
+
+ const BlockDecl::Capture &CI = *capture.Cap;
+ BlockFieldFlags flags = capture.DisposeFlags;
Address srcField = Builder.CreateStructGEP(src, capture.getIndex());
- pushCaptureCleanup(DestroyedCapture.DisposeKind, srcField,
+ pushCaptureCleanup(capture.DisposeKind, srcField,
CI.getVariable()->getType(), flags,
/*ForCopyHelper*/ false, CI.getVariable(), *this);
}
diff --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h
index 552d720b0a1d9..e8857d98894f9 100644
--- a/clang/lib/CodeGen/CGBlocks.h
+++ b/clang/lib/CodeGen/CGBlocks.h
@@ -141,6 +141,17 @@ class BlockByrefInfo {
CharUnits FieldOffset;
};
+/// Represents a type of copy/destroy operation that should be performed for an
+/// entity that's captured by a block.
+enum class BlockCaptureEntityKind {
+ None,
+ CXXRecord, // Copy or destroy
+ ARCWeak,
+ ARCStrong,
+ NonTrivialCStruct,
+ BlockObject, // Assign or release
+};
+
/// CGBlockInfo - Information to generate a block literal.
class CGBlockInfo {
public:
@@ -190,20 +201,40 @@ class CGBlockInfo {
return FieldType;
}
- static Capture makeIndex(unsigned index, CharUnits offset,
- QualType FieldType) {
+ static Capture
+ makeIndex(unsigned index, CharUnits offset, QualType FieldType,
+ BlockCaptureEntityKind CopyKind, BlockFieldFlags CopyFlags,
+ BlockCaptureEntityKind DisposeKind, BlockFieldFlags DisposeFlags,
+ const BlockDecl::Capture *Cap) {
Capture v;
v.Data = (index << 1) | 1;
v.Offset = offset.getQuantity();
v.FieldType = FieldType;
+ v.CopyKind = CopyKind;
+ v.CopyFlags = CopyFlags;
+ v.DisposeKind = DisposeKind;
+ v.DisposeFlags = DisposeFlags;
+ v.Cap = Cap;
return v;
}
- static Capture makeConstant(llvm::Value *value) {
+ static Capture makeConstant(llvm::Value *value,
+ const BlockDecl::Capture *Cap) {
Capture v;
v.Data = reinterpret_cast<uintptr_t>(value);
+ v.Cap = Cap;
return v;
}
+
+ bool isConstantOrTrivial() const {
+ return CopyKind == BlockCaptureEntityKind::None &&
+ DisposeKind == BlockCaptureEntityKind::None;
+ }
+
+ BlockCaptureEntityKind CopyKind = BlockCaptureEntityKind::None,
+ DisposeKind = BlockCaptureEntityKind::None;
+ BlockFieldFlags CopyFlags, DisposeFlags;
+ const BlockDecl::Capture *Cap;
};
/// CanBeGlobal - True if the block can be global, i.e. it has
@@ -214,6 +245,9 @@ class CGBlockInfo {
/// dispose helper functions if the block were escaping.
bool NeedsCopyDispose : 1;
+ /// Indicates whether the block is non-escaping.
+ bool NoEscape : 1;
+
/// HasCXXObject - True if the block's custom copy/dispose functions
/// need to be run even in GC mode.
bool HasCXXObject : 1;
@@ -231,8 +265,11 @@ class CGBlockInfo {
/// functions.
bool CapturesNonExternalType : 1;
- /// The mapping of allocated indexes within the block.
- llvm::DenseMap<const VarDecl*, Capture> Captures;
+ /// Mapping from variables to pointers to captures in SortedCaptures.
+ llvm::DenseMap<const VarDecl *, Capture *> Captures;
+
+ /// The block's captures. Non-constant captures are sorted by their offsets.
+ llvm::SmallVector<Capture, 4> SortedCaptures;
Address LocalAddress;
llvm::StructType *StructureType;
@@ -256,14 +293,18 @@ class CGBlockInfo {
/// has been encountered.
CGBlockInfo *NextBlockInfo;
+ void buildCaptureMap() {
+ for (auto &C : SortedCaptures)
+ Captures[C.Cap->getVariable()] = &C;
+ }
+
const Capture &getCapture(const VarDecl *var) const {
return const_cast<CGBlockInfo*>(this)->getCapture(var);
}
Capture &getCapture(const VarDecl *var) {
- llvm::DenseMap<const VarDecl*, Capture>::iterator
- it = Captures.find(var);
+ auto it = Captures.find(var);
assert(it != Captures.end() && "no entry for variable!");
- return it->second;
+ return *it->second;
}
const BlockDecl *getBlockDecl() const { return Block; }
@@ -274,11 +315,6 @@ class CGBlockInfo {
}
CGBlockInfo(const BlockDecl *blockDecl, StringRef Name);
-
- // Indicates whether the block needs a custom copy or dispose function.
- bool needsCopyDisposeHelpers() const {
- return NeedsCopyDispose && !Block->doesNotEscape();
- }
};
} // end namespace CodeGen
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index d07524be127bb..a11a9ca7d8c0f 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -2935,8 +2935,7 @@ CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM,
std::string CGObjCCommonMac::getRCBlockLayoutStr(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
fillRunSkipBlockVars(CGM, blockInfo);
- return getBlockLayoutInfoString(RunSkipBlockVars,
- blockInfo.needsCopyDisposeHelpers());
+ return getBlockLayoutInfoString(RunSkipBlockVars, blockInfo.NeedsCopyDispose);
}
llvm::Constant *CGObjCCommonMac::BuildByrefLayout(CodeGen::CodeGenModule &CGM,
diff --git a/clang/test/CodeGenObjC/arc-blocks.m b/clang/test/CodeGenObjC/arc-blocks.m
index c939efdc2b5b7..1fb767a009b94 100644
--- a/clang/test/CodeGenObjC/arc-blocks.m
+++ b/clang/test/CodeGenObjC/arc-blocks.m
@@ -8,6 +8,10 @@
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP46:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i8* } { i64 0, i64 48, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @{{.*}}, i32 0, i32 0) }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP48:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32b to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
+// Check that no copy/dispose helpers are emitted for this block.
+
+// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP10:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8* } { i64 0, i64 40, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @{{.*}}, i32 0, i32 0) }, align 8
+
// This shouldn't crash.
void test0(id (^maker)(void)) {
maker();
@@ -769,5 +773,20 @@ void test23(id x, Test23 *t) {
[t m:123, ^{ (void)x; }];
}
+// CHECK-COMMON-LABEL: define internal void @"\01+[Test24 m]"(
+// CHECK-COMMON: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %{{.*}}, i32 0, i32 4
+// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, i8*, i8* }* @[[BLOCK_DESCRIPTOR_TMP10]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]],
+
+ at interface Test24
+ at property (class) void (^block)(void);
++(void)m;
+ at end
+
+ at implementation Test24
++(void)m {
+ self.block = ^{ (void)self; };
+}
+ at end
+
// CHECK: attributes [[NUW]] = { nounwind }
// CHECK-UNOPT: attributes [[NUW]] = { nounwind }
diff --git a/clang/test/CodeGenObjC/blocks.m b/clang/test/CodeGenObjC/blocks.m
index ab2e237a28dab..3373e947f2ced 100644
--- a/clang/test/CodeGenObjC/blocks.m
+++ b/clang/test/CodeGenObjC/blocks.m
@@ -1,5 +1,14 @@
// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fblocks -o - %s | FileCheck %s
+// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i32, i32 }
+
+// Check that there is only one capture (20o) in the copy/dispose function
+// names.
+
+// CHECK: @[[BLOCK_DESCRIPTOR0:.*]] = linkonce_odr hidden unnamed_addr constant { i32, i32, i8*, i8*, i8*, i32 } { i32 0, i32 28, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_4_20o to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_4_20o to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i32 512 },
+
+void (^gb0)(void);
+
// test1. All of this is somehow testing rdar://6676764
struct S {
void (^F)(struct S*);
@@ -132,3 +141,21 @@ void test4(void (^block)()) {
// CHECK-NEXT: [[T5:%.*]] = bitcast i8* [[T4]] to void (i8*, i32, i32, i32, i32)*
// CHECK-NEXT: call void [[T5]](i8* [[T3]], i32 0, i32 1, i32 2, i32 3)
// CHECK-NEXT: ret void
+
+void test5(A *a) {
+ __unsafe_unretained A *t = a;
+ gb0 = ^{ (void)a; (void)t; };
+}
+
+// CHECK-LABEL: define void @test5(
+// CHECK: %[[V0:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, {{.*}}*, {{.*}}* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, {{.*}}*, {{.*}}* }>* %{{.*}}, i32 0, i32 4
+// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i32, i32, i8*, i8*, i8*, i32 }* @[[BLOCK_DESCRIPTOR0]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[V0]],
+
+void test6(id a, long long b) {
+ void (^block)() = ^{ (void)b; (void)a; };
+}
+
+// Check that the block literal doesn't have two fields for capture 'a'.
+
+// CHECK-LABEL: define void @test6(
+// CHECK: alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i64 }>,
More information about the cfe-commits
mailing list