diff --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h --- a/clang/lib/CodeGen/CGBlocks.h +++ b/clang/lib/CodeGen/CGBlocks.h @@ -141,6 +141,17 @@ 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 @@ 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(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 @@ /// 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 @@ /// functions. bool CapturesNonExternalType : 1; - /// The mapping of allocated indexes within the block. - llvm::DenseMap Captures; + /// Mapping from variables to pointers to captures in SortedCaptures. + llvm::DenseMap Captures; + + /// The block's captures. Non-constant captures are sorted by their offsets. + llvm::SmallVector SortedCaptures; Address LocalAddress; llvm::StructType *StructureType; @@ -256,14 +293,18 @@ /// 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(this)->getCapture(var); } Capture &getCapture(const VarDecl *var) { - llvm::DenseMap::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 @@ } 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/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -33,10 +33,10 @@ 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 @@ 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 @@ } // end anonymous namespace -static void findBlockCapturedManagedEntities( - const CGBlockInfo &BlockInfo, const LangOptions &LangOpts, - SmallVectorImpl &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 @@ 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 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 @@ // 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 @@ 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 @@ 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 +computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, + const LangOptions &LangOpts); + +static std::pair +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 &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 @@ CGM.getLangOpts().getGC() == LangOptions::NonGC) info.HasCapturedVariableLayout = true; + if (block->doesNotEscape()) + info.NoEscape = true; + // Collect the layout chunks. SmallVector layout; layout.reserve(block->capturesCXXThis() + @@ -560,9 +612,8 @@ 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 @@ 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 @@ // the capture field type should always match. assert(CGF && getCaptureFieldType(*CGF, CI) == variable->getType() && "capture type differs 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 @@ 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 @@ info.StructureType = llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true); info.CanBeGlobal = true; + info.buildCaptureMap(); return; } @@ -718,6 +722,7 @@ // ...until we get to the alignment of the maximum field. if (endAlign >= maxFieldAlign) { + ++li; break; } } @@ -770,6 +775,7 @@ endAlign = getLowBit(blockSize); } + info.buildCaptureMap(); info.StructureType = llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true); } @@ -826,7 +832,7 @@ // 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 @@ 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 @@ } // 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 @@ // 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 @@ llvm_unreachable("after exhaustive PrimitiveCopyKind switch"); } -static std::pair -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 &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 @@ } // 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 @@ // 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)) && "different 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 @@ } 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 getCopyDestroyHelperFuncName( - const SmallVectorImpl &Captures, + const SmallVectorImpl &Captures, CharUnits BlockAlignment, CaptureStrKind StrKind, CodeGenModule &CGM) { assert((StrKind == CaptureStrKind::CopyHelper || StrKind == CaptureStrKind::DisposeHelper) && @@ -1843,9 +1826,11 @@ 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 @@ /// the contents of an individual __block variable to the heap. llvm::Constant * CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { - SmallVector 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 @@ 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 @@ // 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 @@ 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 @@ /// variable. llvm::Constant * CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { - SmallVector 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::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/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -2935,8 +2935,7 @@ 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 --- 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 @@ [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-UNOPT: attributes [[NUW]] = { nounwind } diff --git a/clang/test/CodeGenObjC/blocks.m b/clang/test/CodeGenObjC/blocks.m --- 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 @@ // 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 }>,