forked from OSchip/llvm-project
[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
This commit is contained in:
parent
75b08cce47
commit
e5df9cc098
|
@ -33,10 +33,10 @@ using namespace clang;
|
||||||
using namespace CodeGen;
|
using namespace CodeGen;
|
||||||
|
|
||||||
CGBlockInfo::CGBlockInfo(const BlockDecl *block, StringRef name)
|
CGBlockInfo::CGBlockInfo(const BlockDecl *block, StringRef name)
|
||||||
: Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false),
|
: Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false),
|
||||||
HasCXXObject(false), UsesStret(false), HasCapturedVariableLayout(false),
|
NoEscape(false), HasCXXObject(false), UsesStret(false),
|
||||||
CapturesNonExternalType(false), LocalAddress(Address::invalid()),
|
HasCapturedVariableLayout(false), CapturesNonExternalType(false),
|
||||||
StructureType(nullptr), Block(block) {
|
LocalAddress(Address::invalid()), StructureType(nullptr), Block(block) {
|
||||||
|
|
||||||
// Skip asm prefix, if any. 'name' is usually taken directly from
|
// Skip asm prefix, if any. 'name' is usually taken directly from
|
||||||
// the mangled name of the enclosing function.
|
// the mangled name of the enclosing function.
|
||||||
|
@ -66,17 +66,6 @@ static llvm::Constant *buildDisposeHelper(CodeGenModule &CGM,
|
||||||
|
|
||||||
namespace {
|
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
|
/// Represents a captured entity that requires extra operations in order for
|
||||||
/// this entity to be copied or destroyed correctly.
|
/// this entity to be copied or destroyed correctly.
|
||||||
struct BlockCaptureManagedEntity {
|
struct BlockCaptureManagedEntity {
|
||||||
|
@ -110,11 +99,7 @@ enum class CaptureStrKind {
|
||||||
|
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
static void findBlockCapturedManagedEntities(
|
static std::string getBlockCaptureStr(const CGBlockInfo::Capture &Cap,
|
||||||
const CGBlockInfo &BlockInfo, const LangOptions &LangOpts,
|
|
||||||
SmallVectorImpl<BlockCaptureManagedEntity> &ManagedCaptures);
|
|
||||||
|
|
||||||
static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
|
|
||||||
CaptureStrKind StrKind,
|
CaptureStrKind StrKind,
|
||||||
CharUnits BlockAlignment,
|
CharUnits BlockAlignment,
|
||||||
CodeGenModule &CGM);
|
CodeGenModule &CGM);
|
||||||
|
@ -124,34 +109,33 @@ static std::string getBlockDescriptorName(const CGBlockInfo &BlockInfo,
|
||||||
std::string Name = "__block_descriptor_";
|
std::string Name = "__block_descriptor_";
|
||||||
Name += llvm::to_string(BlockInfo.BlockSize.getQuantity()) + "_";
|
Name += llvm::to_string(BlockInfo.BlockSize.getQuantity()) + "_";
|
||||||
|
|
||||||
if (BlockInfo.needsCopyDisposeHelpers()) {
|
if (BlockInfo.NeedsCopyDispose) {
|
||||||
if (CGM.getLangOpts().Exceptions)
|
if (CGM.getLangOpts().Exceptions)
|
||||||
Name += "e";
|
Name += "e";
|
||||||
if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions)
|
if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions)
|
||||||
Name += "a";
|
Name += "a";
|
||||||
Name += llvm::to_string(BlockInfo.BlockAlign.getQuantity()) + "_";
|
Name += llvm::to_string(BlockInfo.BlockAlign.getQuantity()) + "_";
|
||||||
|
|
||||||
SmallVector<BlockCaptureManagedEntity, 4> ManagedCaptures;
|
for (auto &Cap : BlockInfo.SortedCaptures) {
|
||||||
findBlockCapturedManagedEntities(BlockInfo, CGM.getContext().getLangOpts(),
|
if (Cap.isConstantOrTrivial())
|
||||||
ManagedCaptures);
|
continue;
|
||||||
|
|
||||||
for (const BlockCaptureManagedEntity &E : ManagedCaptures) {
|
Name += llvm::to_string(Cap.getOffset().getQuantity());
|
||||||
Name += llvm::to_string(E.Capture->getOffset().getQuantity());
|
|
||||||
|
|
||||||
if (E.CopyKind == E.DisposeKind) {
|
if (Cap.CopyKind == Cap.DisposeKind) {
|
||||||
// If CopyKind and DisposeKind are the same, merge the capture
|
// If CopyKind and DisposeKind are the same, merge the capture
|
||||||
// information.
|
// information.
|
||||||
assert(E.CopyKind != BlockCaptureEntityKind::None &&
|
assert(Cap.CopyKind != BlockCaptureEntityKind::None &&
|
||||||
"shouldn't see BlockCaptureManagedEntity that is None");
|
"shouldn't see BlockCaptureManagedEntity that is None");
|
||||||
Name += getBlockCaptureStr(E, CaptureStrKind::Merged,
|
Name += getBlockCaptureStr(Cap, CaptureStrKind::Merged,
|
||||||
BlockInfo.BlockAlign, CGM);
|
BlockInfo.BlockAlign, CGM);
|
||||||
} else {
|
} else {
|
||||||
// If CopyKind and DisposeKind are not the same, which can happen when
|
// If CopyKind and DisposeKind are not the same, which can happen when
|
||||||
// either Kind is None or the captured object is a __strong block,
|
// either Kind is None or the captured object is a __strong block,
|
||||||
// concatenate the copy and dispose strings.
|
// concatenate the copy and dispose strings.
|
||||||
Name += getBlockCaptureStr(E, CaptureStrKind::CopyHelper,
|
Name += getBlockCaptureStr(Cap, CaptureStrKind::CopyHelper,
|
||||||
BlockInfo.BlockAlign, CGM);
|
BlockInfo.BlockAlign, CGM);
|
||||||
Name += getBlockCaptureStr(E, CaptureStrKind::DisposeHelper,
|
Name += getBlockCaptureStr(Cap, CaptureStrKind::DisposeHelper,
|
||||||
BlockInfo.BlockAlign, CGM);
|
BlockInfo.BlockAlign, CGM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +207,7 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM,
|
||||||
|
|
||||||
// Optional copy/dispose helpers.
|
// Optional copy/dispose helpers.
|
||||||
bool hasInternalHelper = false;
|
bool hasInternalHelper = false;
|
||||||
if (blockInfo.needsCopyDisposeHelpers()) {
|
if (blockInfo.NeedsCopyDispose) {
|
||||||
// copy_func_helper_decl
|
// copy_func_helper_decl
|
||||||
llvm::Constant *copyHelper = buildCopyHelper(CGM, blockInfo);
|
llvm::Constant *copyHelper = buildCopyHelper(CGM, blockInfo);
|
||||||
elements.add(copyHelper);
|
elements.add(copyHelper);
|
||||||
|
@ -340,17 +324,21 @@ namespace {
|
||||||
struct BlockLayoutChunk {
|
struct BlockLayoutChunk {
|
||||||
CharUnits Alignment;
|
CharUnits Alignment;
|
||||||
CharUnits Size;
|
CharUnits Size;
|
||||||
Qualifiers::ObjCLifetime Lifetime;
|
|
||||||
const BlockDecl::Capture *Capture; // null for 'this'
|
const BlockDecl::Capture *Capture; // null for 'this'
|
||||||
llvm::Type *Type;
|
llvm::Type *Type;
|
||||||
QualType FieldType;
|
QualType FieldType;
|
||||||
|
BlockCaptureEntityKind CopyKind, DisposeKind;
|
||||||
|
BlockFieldFlags CopyFlags, DisposeFlags;
|
||||||
|
|
||||||
BlockLayoutChunk(CharUnits align, CharUnits size,
|
BlockLayoutChunk(CharUnits align, CharUnits size,
|
||||||
Qualifiers::ObjCLifetime lifetime,
|
const BlockDecl::Capture *capture, llvm::Type *type,
|
||||||
const BlockDecl::Capture *capture,
|
QualType fieldType, BlockCaptureEntityKind CopyKind,
|
||||||
llvm::Type *type, QualType fieldType)
|
BlockFieldFlags CopyFlags,
|
||||||
: Alignment(align), Size(size), Lifetime(lifetime),
|
BlockCaptureEntityKind DisposeKind,
|
||||||
Capture(capture), Type(type), FieldType(fieldType) {}
|
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.
|
/// Tell the block info that this chunk has the given field index.
|
||||||
void setIndex(CGBlockInfo &info, unsigned index, CharUnits offset) {
|
void setIndex(CGBlockInfo &info, unsigned index, CharUnits offset) {
|
||||||
|
@ -358,32 +346,93 @@ namespace {
|
||||||
info.CXXThisIndex = index;
|
info.CXXThisIndex = index;
|
||||||
info.CXXThisOffset = offset;
|
info.CXXThisOffset = offset;
|
||||||
} else {
|
} else {
|
||||||
auto C = CGBlockInfo::Capture::makeIndex(index, offset, FieldType);
|
info.SortedCaptures.push_back(CGBlockInfo::Capture::makeIndex(
|
||||||
info.Captures.insert({Capture->getVariable(), C});
|
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,
|
/// Order by 1) all __strong together 2) next, all block together 3) next,
|
||||||
/// all __weak together. Preserve descending alignment in all situations.
|
/// all byref together 4) next, all __weak together. Preserve descending
|
||||||
|
/// alignment in all situations.
|
||||||
bool operator<(const BlockLayoutChunk &left, const BlockLayoutChunk &right) {
|
bool operator<(const BlockLayoutChunk &left, const BlockLayoutChunk &right) {
|
||||||
if (left.Alignment != right.Alignment)
|
if (left.Alignment != right.Alignment)
|
||||||
return left.Alignment > right.Alignment;
|
return left.Alignment > right.Alignment;
|
||||||
|
|
||||||
auto getPrefOrder = [](const BlockLayoutChunk &chunk) {
|
auto getPrefOrder = [](const BlockLayoutChunk &chunk) {
|
||||||
if (chunk.Capture && chunk.Capture->isByRef())
|
switch (chunk.CopyKind) {
|
||||||
return 1;
|
case BlockCaptureEntityKind::ARCStrong:
|
||||||
if (chunk.Lifetime == Qualifiers::OCL_Strong)
|
|
||||||
return 0;
|
return 0;
|
||||||
if (chunk.Lifetime == Qualifiers::OCL_Weak)
|
case BlockCaptureEntityKind::BlockObject:
|
||||||
return 2;
|
switch (chunk.CopyFlags.getBitMask()) {
|
||||||
return 3;
|
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);
|
return getPrefOrder(left) < getPrefOrder(right);
|
||||||
}
|
}
|
||||||
} // end anonymous namespace
|
} // 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++.
|
/// Determines if the given type is safe for constant capture in C++.
|
||||||
static bool isSafeForCXXConstantCapture(QualType type) {
|
static bool isSafeForCXXConstantCapture(QualType type) {
|
||||||
const RecordType *recordType =
|
const RecordType *recordType =
|
||||||
|
@ -541,6 +590,9 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
|
||||||
CGM.getLangOpts().getGC() == LangOptions::NonGC)
|
CGM.getLangOpts().getGC() == LangOptions::NonGC)
|
||||||
info.HasCapturedVariableLayout = true;
|
info.HasCapturedVariableLayout = true;
|
||||||
|
|
||||||
|
if (block->doesNotEscape())
|
||||||
|
info.NoEscape = true;
|
||||||
|
|
||||||
// Collect the layout chunks.
|
// Collect the layout chunks.
|
||||||
SmallVector<BlockLayoutChunk, 16> layout;
|
SmallVector<BlockLayoutChunk, 16> layout;
|
||||||
layout.reserve(block->capturesCXXThis() +
|
layout.reserve(block->capturesCXXThis() +
|
||||||
|
@ -560,9 +612,8 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
|
||||||
auto TInfo = CGM.getContext().getTypeInfoInChars(thisType);
|
auto TInfo = CGM.getContext().getTypeInfoInChars(thisType);
|
||||||
maxFieldAlign = std::max(maxFieldAlign, TInfo.Align);
|
maxFieldAlign = std::max(maxFieldAlign, TInfo.Align);
|
||||||
|
|
||||||
layout.push_back(BlockLayoutChunk(TInfo.Align, TInfo.Width,
|
addBlockLayout(TInfo.Align, TInfo.Width, nullptr, llvmType, thisType,
|
||||||
Qualifiers::OCL_None,
|
layout, info, CGM);
|
||||||
nullptr, llvmType, thisType));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, all the block captures.
|
// Next, all the block captures.
|
||||||
|
@ -570,9 +621,6 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
|
||||||
const VarDecl *variable = CI.getVariable();
|
const VarDecl *variable = CI.getVariable();
|
||||||
|
|
||||||
if (CI.isEscapingByref()) {
|
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.
|
// Just use void* instead of a pointer to the byref type.
|
||||||
CharUnits align = CGM.getPointerAlign();
|
CharUnits align = CGM.getPointerAlign();
|
||||||
maxFieldAlign = std::max(maxFieldAlign, align);
|
maxFieldAlign = std::max(maxFieldAlign, align);
|
||||||
|
@ -581,72 +629,28 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
|
||||||
// the capture field type should always match.
|
// the capture field type should always match.
|
||||||
assert(CGF && getCaptureFieldType(*CGF, CI) == variable->getType() &&
|
assert(CGF && getCaptureFieldType(*CGF, CI) == variable->getType() &&
|
||||||
"capture type differs from the variable type");
|
"capture type differs from the variable type");
|
||||||
layout.push_back(BlockLayoutChunk(align, CGM.getPointerSize(),
|
addBlockLayout(align, CGM.getPointerSize(), &CI, CGM.VoidPtrTy,
|
||||||
Qualifiers::OCL_None, &CI,
|
variable->getType(), layout, info, CGM);
|
||||||
CGM.VoidPtrTy, variable->getType()));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, build a layout chunk with the size and alignment of
|
// Otherwise, build a layout chunk with the size and alignment of
|
||||||
// the declaration.
|
// the declaration.
|
||||||
if (llvm::Constant *constant = tryCaptureAsConstant(CGM, CGF, variable)) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QualType VT = getCaptureFieldType(*CGF, CI);
|
QualType VT = getCaptureFieldType(*CGF, CI);
|
||||||
|
|
||||||
// If we have a lifetime qualifier, honor it for capture purposes.
|
if (CGM.getLangOpts().CPlusPlus)
|
||||||
// That includes *not* copying it if it's __unsafe_unretained.
|
if (const CXXRecordDecl *record = VT->getAsCXXRecordDecl())
|
||||||
Qualifiers::ObjCLifetime lifetime = VT.getObjCLifetime();
|
if (CI.hasCopyExpr() || !record->hasTrivialDestructor()) {
|
||||||
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()) {
|
|
||||||
info.HasCXXObject = true;
|
info.HasCXXObject = true;
|
||||||
info.NeedsCopyDispose = true;
|
|
||||||
if (!record->isExternallyVisible())
|
if (!record->isExternallyVisible())
|
||||||
info.CapturesNonExternalType = true;
|
info.CapturesNonExternalType = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CharUnits size = C.getTypeSizeInChars(VT);
|
CharUnits size = C.getTypeSizeInChars(VT);
|
||||||
CharUnits align = C.getDeclAlign(variable);
|
CharUnits align = C.getDeclAlign(variable);
|
||||||
|
@ -656,8 +660,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
|
||||||
llvm::Type *llvmType =
|
llvm::Type *llvmType =
|
||||||
CGM.getTypes().ConvertTypeForMem(VT);
|
CGM.getTypes().ConvertTypeForMem(VT);
|
||||||
|
|
||||||
layout.push_back(
|
addBlockLayout(align, size, &CI, llvmType, VT, layout, info, CGM);
|
||||||
BlockLayoutChunk(align, size, lifetime, &CI, llvmType, VT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If that was everything, we're done here.
|
// If that was everything, we're done here.
|
||||||
|
@ -665,6 +668,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
|
||||||
info.StructureType =
|
info.StructureType =
|
||||||
llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
|
llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
|
||||||
info.CanBeGlobal = true;
|
info.CanBeGlobal = true;
|
||||||
|
info.buildCaptureMap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,6 +722,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
|
||||||
|
|
||||||
// ...until we get to the alignment of the maximum field.
|
// ...until we get to the alignment of the maximum field.
|
||||||
if (endAlign >= maxFieldAlign) {
|
if (endAlign >= maxFieldAlign) {
|
||||||
|
++li;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -770,6 +775,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
|
||||||
endAlign = getLowBit(blockSize);
|
endAlign = getLowBit(blockSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.buildCaptureMap();
|
||||||
info.StructureType =
|
info.StructureType =
|
||||||
llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
|
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
|
// 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
|
// 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.
|
// 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.getNSConcreteGlobalBlock()
|
||||||
: CGM.getNSConcreteStackBlock();
|
: CGM.getNSConcreteStackBlock();
|
||||||
isa = llvm::ConstantExpr::getBitCast(blockISA, VoidPtrTy);
|
isa = llvm::ConstantExpr::getBitCast(blockISA, VoidPtrTy);
|
||||||
|
@ -838,13 +844,13 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
|
||||||
flags = BLOCK_HAS_SIGNATURE;
|
flags = BLOCK_HAS_SIGNATURE;
|
||||||
if (blockInfo.HasCapturedVariableLayout)
|
if (blockInfo.HasCapturedVariableLayout)
|
||||||
flags |= BLOCK_HAS_EXTENDED_LAYOUT;
|
flags |= BLOCK_HAS_EXTENDED_LAYOUT;
|
||||||
if (blockInfo.needsCopyDisposeHelpers())
|
if (blockInfo.NeedsCopyDispose)
|
||||||
flags |= BLOCK_HAS_COPY_DISPOSE;
|
flags |= BLOCK_HAS_COPY_DISPOSE;
|
||||||
if (blockInfo.HasCXXObject)
|
if (blockInfo.HasCXXObject)
|
||||||
flags |= BLOCK_HAS_CXX_OBJ;
|
flags |= BLOCK_HAS_CXX_OBJ;
|
||||||
if (blockInfo.UsesStret)
|
if (blockInfo.UsesStret)
|
||||||
flags |= BLOCK_USE_STRET;
|
flags |= BLOCK_USE_STRET;
|
||||||
if (blockInfo.getBlockDecl()->doesNotEscape())
|
if (blockInfo.NoEscape)
|
||||||
flags |= BLOCK_IS_NOESCAPE | BLOCK_IS_GLOBAL;
|
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.
|
// Push a cleanup for the capture if necessary.
|
||||||
if (!blockInfo.NeedsCopyDispose)
|
if (!blockInfo.NoEscape && !blockInfo.NeedsCopyDispose)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Ignore __block captures; there's nothing special in the on-stack block
|
// 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.
|
// For all other types, the memcpy is fine.
|
||||||
return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags());
|
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:
|
// Special rules for ARC captures:
|
||||||
Qualifiers QS = T.getQualifiers();
|
Qualifiers QS = T.getQualifiers();
|
||||||
|
|
||||||
|
@ -1669,34 +1680,6 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
|
||||||
llvm_unreachable("after exhaustive PrimitiveCopyKind switch");
|
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 {
|
namespace {
|
||||||
/// Release a __block variable.
|
/// Release a __block variable.
|
||||||
struct CallBlockRelease final : EHScopeStack::Cleanup {
|
struct CallBlockRelease final : EHScopeStack::Cleanup {
|
||||||
|
@ -1732,13 +1715,13 @@ bool CodeGenFunction::cxxDestructorCanThrow(QualType T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a string that has the information about a capture.
|
// 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,
|
CaptureStrKind StrKind,
|
||||||
CharUnits BlockAlignment,
|
CharUnits BlockAlignment,
|
||||||
CodeGenModule &CGM) {
|
CodeGenModule &CGM) {
|
||||||
std::string Str;
|
std::string Str;
|
||||||
ASTContext &Ctx = CGM.getContext();
|
ASTContext &Ctx = CGM.getContext();
|
||||||
const BlockDecl::Capture &CI = *E.CI;
|
const BlockDecl::Capture &CI = *Cap.Cap;
|
||||||
QualType CaptureTy = CI.getVariable()->getType();
|
QualType CaptureTy = CI.getVariable()->getType();
|
||||||
|
|
||||||
BlockCaptureEntityKind Kind;
|
BlockCaptureEntityKind Kind;
|
||||||
|
@ -1747,15 +1730,16 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
|
||||||
// CaptureStrKind::Merged should be passed only when the operations and the
|
// CaptureStrKind::Merged should be passed only when the operations and the
|
||||||
// flags are the same for copy and dispose.
|
// flags are the same for copy and dispose.
|
||||||
assert((StrKind != CaptureStrKind::Merged ||
|
assert((StrKind != CaptureStrKind::Merged ||
|
||||||
(E.CopyKind == E.DisposeKind && E.CopyFlags == E.DisposeFlags)) &&
|
(Cap.CopyKind == Cap.DisposeKind &&
|
||||||
|
Cap.CopyFlags == Cap.DisposeFlags)) &&
|
||||||
"different operations and flags");
|
"different operations and flags");
|
||||||
|
|
||||||
if (StrKind == CaptureStrKind::DisposeHelper) {
|
if (StrKind == CaptureStrKind::DisposeHelper) {
|
||||||
Kind = E.DisposeKind;
|
Kind = Cap.DisposeKind;
|
||||||
Flags = E.DisposeFlags;
|
Flags = Cap.DisposeFlags;
|
||||||
} else {
|
} else {
|
||||||
Kind = E.CopyKind;
|
Kind = Cap.CopyKind;
|
||||||
Flags = E.CopyFlags;
|
Flags = Cap.CopyFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (Kind) {
|
switch (Kind) {
|
||||||
|
@ -1803,8 +1787,7 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
|
||||||
}
|
}
|
||||||
case BlockCaptureEntityKind::NonTrivialCStruct: {
|
case BlockCaptureEntityKind::NonTrivialCStruct: {
|
||||||
bool IsVolatile = CaptureTy.isVolatileQualified();
|
bool IsVolatile = CaptureTy.isVolatileQualified();
|
||||||
CharUnits Alignment =
|
CharUnits Alignment = BlockAlignment.alignmentAtOffset(Cap.getOffset());
|
||||||
BlockAlignment.alignmentAtOffset(E.Capture->getOffset());
|
|
||||||
|
|
||||||
Str += "n";
|
Str += "n";
|
||||||
std::string FuncStr;
|
std::string FuncStr;
|
||||||
|
@ -1829,7 +1812,7 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string getCopyDestroyHelperFuncName(
|
static std::string getCopyDestroyHelperFuncName(
|
||||||
const SmallVectorImpl<BlockCaptureManagedEntity> &Captures,
|
const SmallVectorImpl<CGBlockInfo::Capture> &Captures,
|
||||||
CharUnits BlockAlignment, CaptureStrKind StrKind, CodeGenModule &CGM) {
|
CharUnits BlockAlignment, CaptureStrKind StrKind, CodeGenModule &CGM) {
|
||||||
assert((StrKind == CaptureStrKind::CopyHelper ||
|
assert((StrKind == CaptureStrKind::CopyHelper ||
|
||||||
StrKind == CaptureStrKind::DisposeHelper) &&
|
StrKind == CaptureStrKind::DisposeHelper) &&
|
||||||
|
@ -1843,9 +1826,11 @@ static std::string getCopyDestroyHelperFuncName(
|
||||||
Name += "a";
|
Name += "a";
|
||||||
Name += llvm::to_string(BlockAlignment.getQuantity()) + "_";
|
Name += llvm::to_string(BlockAlignment.getQuantity()) + "_";
|
||||||
|
|
||||||
for (const BlockCaptureManagedEntity &E : Captures) {
|
for (auto &Cap : Captures) {
|
||||||
Name += llvm::to_string(E.Capture->getOffset().getQuantity());
|
if (Cap.isConstantOrTrivial())
|
||||||
Name += getBlockCaptureStr(E, StrKind, BlockAlignment, CGM);
|
continue;
|
||||||
|
Name += llvm::to_string(Cap.getOffset().getQuantity());
|
||||||
|
Name += getBlockCaptureStr(Cap, StrKind, BlockAlignment, CGM);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Name;
|
return Name;
|
||||||
|
@ -1916,11 +1901,9 @@ static void setBlockHelperAttributesVisibility(bool CapturesNonExternalType,
|
||||||
/// the contents of an individual __block variable to the heap.
|
/// the contents of an individual __block variable to the heap.
|
||||||
llvm::Constant *
|
llvm::Constant *
|
||||||
CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
|
CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
|
||||||
SmallVector<BlockCaptureManagedEntity, 4> CopiedCaptures;
|
std::string FuncName = getCopyDestroyHelperFuncName(
|
||||||
findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures);
|
blockInfo.SortedCaptures, blockInfo.BlockAlign,
|
||||||
std::string FuncName =
|
CaptureStrKind::CopyHelper, CGM);
|
||||||
getCopyDestroyHelperFuncName(CopiedCaptures, blockInfo.BlockAlign,
|
|
||||||
CaptureStrKind::CopyHelper, CGM);
|
|
||||||
|
|
||||||
if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName))
|
if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName))
|
||||||
return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy);
|
return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy);
|
||||||
|
@ -1967,17 +1950,19 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
|
||||||
dst = Address(Builder.CreateLoad(dst), blockInfo.BlockAlign);
|
dst = Address(Builder.CreateLoad(dst), blockInfo.BlockAlign);
|
||||||
dst = Builder.CreateBitCast(dst, structPtrTy, "block.dest");
|
dst = Builder.CreateBitCast(dst, structPtrTy, "block.dest");
|
||||||
|
|
||||||
for (const auto &CopiedCapture : CopiedCaptures) {
|
for (auto &capture : blockInfo.SortedCaptures) {
|
||||||
const BlockDecl::Capture &CI = *CopiedCapture.CI;
|
if (capture.isConstantOrTrivial())
|
||||||
const CGBlockInfo::Capture &capture = *CopiedCapture.Capture;
|
continue;
|
||||||
|
|
||||||
|
const BlockDecl::Capture &CI = *capture.Cap;
|
||||||
QualType captureType = CI.getVariable()->getType();
|
QualType captureType = CI.getVariable()->getType();
|
||||||
BlockFieldFlags flags = CopiedCapture.CopyFlags;
|
BlockFieldFlags flags = capture.CopyFlags;
|
||||||
|
|
||||||
unsigned index = capture.getIndex();
|
unsigned index = capture.getIndex();
|
||||||
Address srcField = Builder.CreateStructGEP(src, index);
|
Address srcField = Builder.CreateStructGEP(src, index);
|
||||||
Address dstField = Builder.CreateStructGEP(dst, index);
|
Address dstField = Builder.CreateStructGEP(dst, index);
|
||||||
|
|
||||||
switch (CopiedCapture.CopyKind) {
|
switch (capture.CopyKind) {
|
||||||
case BlockCaptureEntityKind::CXXRecord:
|
case BlockCaptureEntityKind::CXXRecord:
|
||||||
// If there's an explicit copy expression, we do that.
|
// If there's an explicit copy expression, we do that.
|
||||||
assert(CI.getCopyExpr() && "copy expression for variable is missing");
|
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
|
// Ensure that we destroy the copied object if an exception is thrown later
|
||||||
// in the helper function.
|
// in the helper function.
|
||||||
pushCaptureCleanup(CopiedCapture.CopyKind, dstField, captureType, flags,
|
pushCaptureCleanup(capture.CopyKind, dstField, captureType, flags,
|
||||||
/*ForCopyHelper*/ true, CI.getVariable(), *this);
|
/*ForCopyHelper*/ true, CI.getVariable(), *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2085,8 +2070,10 @@ computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
|
||||||
BlockFieldFlags());
|
BlockFieldFlags());
|
||||||
case QualType::DK_none: {
|
case QualType::DK_none: {
|
||||||
// Non-ARC captures are strong, and we need to use _Block_object_dispose.
|
// 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() &&
|
if (T->isObjCRetainableType() && !T.getQualifiers().hasObjCLifetime() &&
|
||||||
!LangOpts.ObjCAutoRefCount)
|
!LangOpts.ObjCAutoRefCount && !T->isObjCInertUnsafeUnretainedType())
|
||||||
return std::make_pair(BlockCaptureEntityKind::BlockObject,
|
return std::make_pair(BlockCaptureEntityKind::BlockObject,
|
||||||
getBlockFieldFlagsForObjCObjectPointer(CI, T));
|
getBlockFieldFlagsForObjCObjectPointer(CI, T));
|
||||||
// Otherwise, we have nothing to do.
|
// Otherwise, we have nothing to do.
|
||||||
|
@ -2105,11 +2092,9 @@ computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
|
||||||
/// variable.
|
/// variable.
|
||||||
llvm::Constant *
|
llvm::Constant *
|
||||||
CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
|
CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
|
||||||
SmallVector<BlockCaptureManagedEntity, 4> DestroyedCaptures;
|
std::string FuncName = getCopyDestroyHelperFuncName(
|
||||||
findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures);
|
blockInfo.SortedCaptures, blockInfo.BlockAlign,
|
||||||
std::string FuncName =
|
CaptureStrKind::DisposeHelper, CGM);
|
||||||
getCopyDestroyHelperFuncName(DestroyedCaptures, blockInfo.BlockAlign,
|
|
||||||
CaptureStrKind::DisposeHelper, CGM);
|
|
||||||
|
|
||||||
if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName))
|
if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName))
|
||||||
return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy);
|
return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy);
|
||||||
|
@ -2153,14 +2138,16 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
|
||||||
|
|
||||||
CodeGenFunction::RunCleanupsScope cleanups(*this);
|
CodeGenFunction::RunCleanupsScope cleanups(*this);
|
||||||
|
|
||||||
for (const auto &DestroyedCapture : DestroyedCaptures) {
|
for (auto &capture : blockInfo.SortedCaptures) {
|
||||||
const BlockDecl::Capture &CI = *DestroyedCapture.CI;
|
if (capture.isConstantOrTrivial())
|
||||||
const CGBlockInfo::Capture &capture = *DestroyedCapture.Capture;
|
continue;
|
||||||
BlockFieldFlags flags = DestroyedCapture.DisposeFlags;
|
|
||||||
|
const BlockDecl::Capture &CI = *capture.Cap;
|
||||||
|
BlockFieldFlags flags = capture.DisposeFlags;
|
||||||
|
|
||||||
Address srcField = Builder.CreateStructGEP(src, capture.getIndex());
|
Address srcField = Builder.CreateStructGEP(src, capture.getIndex());
|
||||||
|
|
||||||
pushCaptureCleanup(DestroyedCapture.DisposeKind, srcField,
|
pushCaptureCleanup(capture.DisposeKind, srcField,
|
||||||
CI.getVariable()->getType(), flags,
|
CI.getVariable()->getType(), flags,
|
||||||
/*ForCopyHelper*/ false, CI.getVariable(), *this);
|
/*ForCopyHelper*/ false, CI.getVariable(), *this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,17 @@ public:
|
||||||
CharUnits FieldOffset;
|
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.
|
/// CGBlockInfo - Information to generate a block literal.
|
||||||
class CGBlockInfo {
|
class CGBlockInfo {
|
||||||
public:
|
public:
|
||||||
|
@ -190,20 +201,40 @@ public:
|
||||||
return FieldType;
|
return FieldType;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Capture makeIndex(unsigned index, CharUnits offset,
|
static Capture
|
||||||
QualType FieldType) {
|
makeIndex(unsigned index, CharUnits offset, QualType FieldType,
|
||||||
|
BlockCaptureEntityKind CopyKind, BlockFieldFlags CopyFlags,
|
||||||
|
BlockCaptureEntityKind DisposeKind, BlockFieldFlags DisposeFlags,
|
||||||
|
const BlockDecl::Capture *Cap) {
|
||||||
Capture v;
|
Capture v;
|
||||||
v.Data = (index << 1) | 1;
|
v.Data = (index << 1) | 1;
|
||||||
v.Offset = offset.getQuantity();
|
v.Offset = offset.getQuantity();
|
||||||
v.FieldType = FieldType;
|
v.FieldType = FieldType;
|
||||||
|
v.CopyKind = CopyKind;
|
||||||
|
v.CopyFlags = CopyFlags;
|
||||||
|
v.DisposeKind = DisposeKind;
|
||||||
|
v.DisposeFlags = DisposeFlags;
|
||||||
|
v.Cap = Cap;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Capture makeConstant(llvm::Value *value) {
|
static Capture makeConstant(llvm::Value *value,
|
||||||
|
const BlockDecl::Capture *Cap) {
|
||||||
Capture v;
|
Capture v;
|
||||||
v.Data = reinterpret_cast<uintptr_t>(value);
|
v.Data = reinterpret_cast<uintptr_t>(value);
|
||||||
|
v.Cap = Cap;
|
||||||
return v;
|
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
|
/// CanBeGlobal - True if the block can be global, i.e. it has
|
||||||
|
@ -214,6 +245,9 @@ public:
|
||||||
/// dispose helper functions if the block were escaping.
|
/// dispose helper functions if the block were escaping.
|
||||||
bool NeedsCopyDispose : 1;
|
bool NeedsCopyDispose : 1;
|
||||||
|
|
||||||
|
/// Indicates whether the block is non-escaping.
|
||||||
|
bool NoEscape : 1;
|
||||||
|
|
||||||
/// HasCXXObject - True if the block's custom copy/dispose functions
|
/// HasCXXObject - True if the block's custom copy/dispose functions
|
||||||
/// need to be run even in GC mode.
|
/// need to be run even in GC mode.
|
||||||
bool HasCXXObject : 1;
|
bool HasCXXObject : 1;
|
||||||
|
@ -231,8 +265,11 @@ public:
|
||||||
/// functions.
|
/// functions.
|
||||||
bool CapturesNonExternalType : 1;
|
bool CapturesNonExternalType : 1;
|
||||||
|
|
||||||
/// The mapping of allocated indexes within the block.
|
/// Mapping from variables to pointers to captures in SortedCaptures.
|
||||||
llvm::DenseMap<const VarDecl*, Capture> Captures;
|
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;
|
Address LocalAddress;
|
||||||
llvm::StructType *StructureType;
|
llvm::StructType *StructureType;
|
||||||
|
@ -256,14 +293,18 @@ public:
|
||||||
/// has been encountered.
|
/// has been encountered.
|
||||||
CGBlockInfo *NextBlockInfo;
|
CGBlockInfo *NextBlockInfo;
|
||||||
|
|
||||||
|
void buildCaptureMap() {
|
||||||
|
for (auto &C : SortedCaptures)
|
||||||
|
Captures[C.Cap->getVariable()] = &C;
|
||||||
|
}
|
||||||
|
|
||||||
const Capture &getCapture(const VarDecl *var) const {
|
const Capture &getCapture(const VarDecl *var) const {
|
||||||
return const_cast<CGBlockInfo*>(this)->getCapture(var);
|
return const_cast<CGBlockInfo*>(this)->getCapture(var);
|
||||||
}
|
}
|
||||||
Capture &getCapture(const VarDecl *var) {
|
Capture &getCapture(const VarDecl *var) {
|
||||||
llvm::DenseMap<const VarDecl*, Capture>::iterator
|
auto it = Captures.find(var);
|
||||||
it = Captures.find(var);
|
|
||||||
assert(it != Captures.end() && "no entry for variable!");
|
assert(it != Captures.end() && "no entry for variable!");
|
||||||
return it->second;
|
return *it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BlockDecl *getBlockDecl() const { return Block; }
|
const BlockDecl *getBlockDecl() const { return Block; }
|
||||||
|
@ -274,11 +315,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
CGBlockInfo(const BlockDecl *blockDecl, StringRef Name);
|
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
|
} // end namespace CodeGen
|
||||||
|
|
|
@ -2935,8 +2935,7 @@ CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM,
|
||||||
std::string CGObjCCommonMac::getRCBlockLayoutStr(CodeGenModule &CGM,
|
std::string CGObjCCommonMac::getRCBlockLayoutStr(CodeGenModule &CGM,
|
||||||
const CGBlockInfo &blockInfo) {
|
const CGBlockInfo &blockInfo) {
|
||||||
fillRunSkipBlockVars(CGM, blockInfo);
|
fillRunSkipBlockVars(CGM, blockInfo);
|
||||||
return getBlockLayoutInfoString(RunSkipBlockVars,
|
return getBlockLayoutInfoString(RunSkipBlockVars, blockInfo.NeedsCopyDispose);
|
||||||
blockInfo.needsCopyDisposeHelpers());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Constant *CGObjCCommonMac::BuildByrefLayout(CodeGen::CodeGenModule &CGM,
|
llvm::Constant *CGObjCCommonMac::BuildByrefLayout(CodeGen::CodeGenModule &CGM,
|
||||||
|
|
|
@ -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_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-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.
|
// This shouldn't crash.
|
||||||
void test0(id (^maker)(void)) {
|
void test0(id (^maker)(void)) {
|
||||||
maker();
|
maker();
|
||||||
|
@ -769,5 +773,20 @@ void test23(id x, Test23 *t) {
|
||||||
[t m:123, ^{ (void)x; }];
|
[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]],
|
||||||
|
|
||||||
|
@interface Test24
|
||||||
|
@property (class) void (^block)(void);
|
||||||
|
+(void)m;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation Test24
|
||||||
|
+(void)m {
|
||||||
|
self.block = ^{ (void)self; };
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
// CHECK: attributes [[NUW]] = { nounwind }
|
// CHECK: attributes [[NUW]] = { nounwind }
|
||||||
// CHECK-UNOPT: attributes [[NUW]] = { nounwind }
|
// CHECK-UNOPT: attributes [[NUW]] = { nounwind }
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fblocks -o - %s | FileCheck %s
|
// 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
|
// test1. All of this is somehow testing rdar://6676764
|
||||||
struct S {
|
struct S {
|
||||||
void (^F)(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: [[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: call void [[T5]](i8* [[T3]], i32 0, i32 1, i32 2, i32 3)
|
||||||
// CHECK-NEXT: ret void
|
// 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 }>,
|
||||||
|
|
Loading…
Reference in New Issue