Index: lib/CodeGen/CGBlocks.h =================================================================== --- lib/CodeGen/CGBlocks.h +++ lib/CodeGen/CGBlocks.h @@ -132,6 +132,9 @@ friend bool operator&(BlockFieldFlags l, BlockFieldFlags r) { return (l.flags & r.flags); } + bool operator==(BlockFieldFlags Other) const { + return flags == Other.flags; + } }; inline BlockFieldFlags operator|(BlockFieldFlag_t l, BlockFieldFlag_t r) { return BlockFieldFlags(l) | BlockFieldFlags(r); Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ lib/CodeGen/CGBlocks.cpp @@ -65,6 +65,107 @@ return CodeGenFunction(CGM).GenerateDestroyHelperFunction(blockInfo); } +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 { + BlockCaptureEntityKind CopyKind, DisposeKind; + BlockFieldFlags CopyFlags, DisposeFlags; + const BlockDecl::Capture *CI; + const CGBlockInfo::Capture *Capture; + + BlockCaptureManagedEntity(BlockCaptureEntityKind CopyType, + BlockCaptureEntityKind DisposeType, + BlockFieldFlags CopyFlags, + BlockFieldFlags DisposeFlags, + const BlockDecl::Capture &CI, + const CGBlockInfo::Capture &Capture) + : CopyKind(CopyType), DisposeKind(DisposeType), CopyFlags(CopyFlags), + DisposeFlags(DisposeFlags), CI(&CI), Capture(&Capture) {} + + bool operator<(const BlockCaptureManagedEntity &Other) const { + return Capture->getOffset() < Other.Capture->getOffset(); + } +}; + +enum class CaptureStrKind { + // String for the copy helper. + CopyHelper, + // String for the dispose helper. + DisposeHelper, + // Merge the strings for the copy helper and dispose helper. + Merged +}; + +} // end anonymous namespace + +static void findBlockCapturedManagedEntities( + const CGBlockInfo &BlockInfo, const LangOptions &LangOpts, + SmallVectorImpl &ManagedCaptures); + +static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E, + CaptureStrKind StrKind, + CharUnits BlockAlignment, + CodeGenModule &CGM); + +static std::string getBlockDescriptorName(const CGBlockInfo &BlockInfo, + CodeGenModule &CGM) { + std::string Name = "__block_descriptor_"; + Name += llvm::to_string(BlockInfo.BlockSize.getQuantity()) + "_"; + + if (BlockInfo.needsCopyDisposeHelpers()) { + 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 (const BlockCaptureManagedEntity &E : ManagedCaptures) { + Name += llvm::to_string(E.Capture->getOffset().getQuantity()); + + if (E.CopyKind == E.DisposeKind) { + // If CopyKind and DisposeKind are the same, merge the capture + // information. + assert(E.CopyKind != BlockCaptureEntityKind::None && + "shouldn't see BlockCaptureManagedEntity that is None"); + Name += getBlockCaptureStr(E, 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, + BlockInfo.BlockAlign, CGM); + Name += getBlockCaptureStr(E, CaptureStrKind::DisposeHelper, + BlockInfo.BlockAlign, CGM); + } + } + Name += "_"; + } + + std::string TypeAtEncoding = + CGM.getContext().getObjCEncodingForBlock(BlockInfo.getBlockExpr()); + Name += "e" + llvm::to_string(TypeAtEncoding.size()) + "_" + TypeAtEncoding; + Name += "l" + CGM.getObjCRuntime().getRCBlockLayoutStr(CGM, BlockInfo); + return Name; +} + /// buildBlockDescriptor - Build the block descriptor meta-data for a block. /// buildBlockDescriptor is accessed from 5th field of the Block_literal /// meta-data and contains stationary information about the block literal. @@ -93,6 +194,19 @@ else i8p = CGM.VoidPtrTy; + std::string descName; + + // If an equivalent block descriptor global variable exists, return it. + if (C.getLangOpts().ObjC1 && + CGM.getLangOpts().getGC() == LangOptions::NonGC) { + descName = getBlockDescriptorName(blockInfo, CGM); + if (llvm::GlobalValue *desc = CGM.getModule().getNamedValue(descName)) + return llvm::ConstantExpr::getBitCast(desc, + CGM.getBlockDescriptorType()); + } + + // If there isn't an equivalent block descriptor global variable, create a new + // one. ConstantInitBuilder builder(CGM); auto elements = builder.beginStruct(); @@ -106,12 +220,20 @@ elements.addInt(ulong, blockInfo.BlockSize.getQuantity()); // Optional copy/dispose helpers. + bool hasInternalHelper = false; if (blockInfo.needsCopyDisposeHelpers()) { // copy_func_helper_decl - elements.add(buildCopyHelper(CGM, blockInfo)); + llvm::Constant *copyHelper = buildCopyHelper(CGM, blockInfo); + elements.add(copyHelper); // destroy_func_decl - elements.add(buildDisposeHelper(CGM, blockInfo)); + llvm::Constant *disposeHelper = buildDisposeHelper(CGM, blockInfo); + elements.add(disposeHelper); + + if (cast(copyHelper->getOperand(0))->hasInternalLinkage() || + cast(disposeHelper->getOperand(0)) + ->hasInternalLinkage()) + hasInternalHelper = true; } // Signature. Mandatory ObjC-style method descriptor @encode sequence. @@ -134,12 +256,26 @@ if (C.getLangOpts().OpenCL) AddrSpace = C.getTargetAddressSpace(LangAS::opencl_constant); + llvm::GlobalValue::LinkageTypes linkage; + if (descName.empty()) { + linkage = llvm::GlobalValue::InternalLinkage; + descName = "__block_descriptor_tmp"; + } else if (hasInternalHelper) { + // If either the copy helper or the dispose helper has internal linkage, + // the block descriptor must have internal linkage too. + linkage = llvm::GlobalValue::InternalLinkage; + } else { + linkage = llvm::GlobalValue::LinkOnceODRLinkage; + } + llvm::GlobalVariable *global = - elements.finishAndCreateGlobal("__block_descriptor_tmp", - CGM.getPointerAlign(), - /*constant*/ true, - llvm::GlobalValue::InternalLinkage, - AddrSpace); + elements.finishAndCreateGlobal(descName, CGM.getPointerAlign(), + /*constant*/ true, linkage, AddrSpace); + + if (linkage == llvm::GlobalValue::LinkOnceODRLinkage) { + global->setVisibility(llvm::GlobalValue::HiddenVisibility); + global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + } return llvm::ConstantExpr::getBitCast(global, CGM.getBlockDescriptorType()); } @@ -1510,39 +1646,6 @@ return fn; } -namespace { - -/// Represents a type of copy/destroy operation that should be performed for an -/// entity that's captured by a block. -enum class BlockCaptureEntityKind { - CXXRecord, // Copy or destroy - ARCWeak, - ARCStrong, - 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 { - BlockCaptureEntityKind Kind; - BlockFieldFlags Flags; - const BlockDecl::Capture *CI; - const CGBlockInfo::Capture *Capture; - - BlockCaptureManagedEntity(BlockCaptureEntityKind Type, BlockFieldFlags Flags, - const BlockDecl::Capture &CI, - const CGBlockInfo::Capture &Capture) - : Kind(Type), Flags(Flags), CI(&CI), Capture(&Capture) {} - - bool operator<(const BlockCaptureManagedEntity &Other) const { - return Capture->getOffset() < Other.Capture->getOffset(); - } -}; - -} // end anonymous namespace - static std::pair computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, const LangOptions &LangOpts) { @@ -1600,22 +1703,29 @@ 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, - llvm::function_ref( - const BlockDecl::Capture &, QualType, const LangOptions &)> - Predicate) { + 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; - auto Info = Predicate(CI, Variable->getType(), LangOpts); - if (Info.first != BlockCaptureEntityKind::None) - ManagedCaptures.emplace_back(Info.first, Info.second, CI, Capture); + auto CopyInfo = + computeCopyInfoForBlockCapture(CI, Variable->getType(), LangOpts); + auto DisposeInfo = + computeDestroyInfoForBlockCapture(CI, Variable->getType(), 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. @@ -1656,15 +1766,114 @@ return false; } -static std::string getCopyDestroyHelperFuncName( - const SmallVectorImpl &Captures, - CharUnits BlockAlignment, bool IsCopyHelper, CodeGenModule &CGM) { +// Return a string that has the information about a capture. +static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E, + CaptureStrKind StrKind, + CharUnits BlockAlignment, + CodeGenModule &CGM) { + std::string Str; ASTContext &Ctx = CGM.getContext(); std::unique_ptr MC( ItaniumMangleContext::create(Ctx, Ctx.getDiagnostics())); + const BlockDecl::Capture &CI = *E.CI; + QualType CaptureTy = CI.getVariable()->getType(); - std::string Name = - IsCopyHelper ? "__copy_helper_block_" : "__destroy_helper_block_"; + BlockCaptureEntityKind Kind; + BlockFieldFlags Flags; + + // 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)) && + "different operations and flags"); + + if (StrKind == CaptureStrKind::DisposeHelper) { + Kind = E.DisposeKind; + Flags = E.DisposeFlags; + } else { + Kind = E.CopyKind; + Flags = E.CopyFlags; + } + + switch (Kind) { + case BlockCaptureEntityKind::CXXRecord: { + Str += "c"; + SmallString<256> TyStr; + llvm::raw_svector_ostream Out(TyStr); + MC->mangleTypeName(CaptureTy, Out); + Str += llvm::to_string(TyStr.size()) + TyStr.c_str(); + break; + } + case BlockCaptureEntityKind::ARCWeak: + Str += "w"; + break; + case BlockCaptureEntityKind::ARCStrong: + Str += "s"; + break; + case BlockCaptureEntityKind::BlockObject: { + const VarDecl *Var = CI.getVariable(); + unsigned F = Flags.getBitMask(); + if (F & BLOCK_FIELD_IS_BYREF) { + Str += "r"; + if (F & BLOCK_FIELD_IS_WEAK) + Str += "w"; + else { + // If CaptureStrKind::Merged is passed, check both the copy expression + // and the destructor. + if (StrKind != CaptureStrKind::DisposeHelper) { + if (Ctx.getBlockVarCopyInit(Var).canThrow()) + Str += "c"; + } + if (StrKind != CaptureStrKind::CopyHelper) { + if (CodeGenFunction::cxxDestructorCanThrow(CaptureTy)) + Str += "d"; + } + } + } else { + assert((F & BLOCK_FIELD_IS_OBJECT) && "unexpected flag value"); + if (F == BLOCK_FIELD_IS_BLOCK) + Str += "b"; + else + Str += "o"; + } + break; + } + case BlockCaptureEntityKind::NonTrivialCStruct: { + bool IsVolatile = CaptureTy.isVolatileQualified(); + CharUnits Alignment = + BlockAlignment.alignmentAtOffset(E.Capture->getOffset()); + + Str += "n"; + std::string FuncStr; + if (StrKind == CaptureStrKind::DisposeHelper) + FuncStr = CodeGenFunction::getNonTrivialDestructorStr( + CaptureTy, Alignment, IsVolatile, Ctx); + else + // If CaptureStrKind::Merged is passed, use the copy constructor string. + // It has all the information that the destructor string has. + FuncStr = CodeGenFunction::getNonTrivialCopyConstructorStr( + CaptureTy, Alignment, IsVolatile, Ctx); + // The underscore is necessary here because non-trivial copy constructor + // and destructor strings can start with a number. + Str += llvm::to_string(FuncStr.size()) + "_" + FuncStr; + break; + } + case BlockCaptureEntityKind::None: + break; + } + + return Str; +} + +static std::string getCopyDestroyHelperFuncName( + const SmallVectorImpl &Captures, + CharUnits BlockAlignment, CaptureStrKind StrKind, CodeGenModule &CGM) { + assert((StrKind == CaptureStrKind::CopyHelper || + StrKind == CaptureStrKind::DisposeHelper) && + "unexpected CaptureStrKind"); + std::string Name = StrKind == CaptureStrKind::CopyHelper + ? "__copy_helper_block_" + : "__destroy_helper_block_"; if (CGM.getLangOpts().Exceptions) Name += "e"; if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions) @@ -1672,72 +1881,8 @@ Name += llvm::to_string(BlockAlignment.getQuantity()) + "_"; for (const BlockCaptureManagedEntity &E : Captures) { - const BlockDecl::Capture &CI = *E.CI; - BlockFieldFlags Flags = E.Flags; - QualType CaptureTy = CI.getVariable()->getType(); Name += llvm::to_string(E.Capture->getOffset().getQuantity()); - - switch (E.Kind) { - case BlockCaptureEntityKind::CXXRecord: { - Name += "c"; - SmallString<256> Str; - llvm::raw_svector_ostream Out(Str); - MC->mangleTypeName(CaptureTy, Out); - Name += llvm::to_string(Str.size()) + Str.c_str(); - break; - } - case BlockCaptureEntityKind::ARCWeak: - Name += "w"; - break; - case BlockCaptureEntityKind::ARCStrong: - Name += "s"; - break; - case BlockCaptureEntityKind::BlockObject: { - const VarDecl *Var = CI.getVariable(); - unsigned F = Flags.getBitMask(); - if (F & BLOCK_FIELD_IS_BYREF) { - Name += "r"; - if (F & BLOCK_FIELD_IS_WEAK) - Name += "w"; - else { - if (IsCopyHelper) { - if (Ctx.getBlockVarCopyInit(Var).canThrow()) - Name += "c"; - } else { - if (CodeGenFunction::cxxDestructorCanThrow(CaptureTy)) - Name += "d"; - } - } - } else { - assert((F & BLOCK_FIELD_IS_OBJECT) && "unexpected flag value"); - if (F == BLOCK_FIELD_IS_BLOCK) - Name += "b"; - else - Name += "o"; - } - break; - } - case BlockCaptureEntityKind::NonTrivialCStruct: { - bool IsVolatile = CaptureTy.isVolatileQualified(); - CharUnits Alignment = - BlockAlignment.alignmentAtOffset(E.Capture->getOffset()); - - Name += "n"; - std::string Str; - if (IsCopyHelper) - Str = CodeGenFunction::getNonTrivialCopyConstructorStr( - CaptureTy, Alignment, IsVolatile, Ctx); - else - Str = CodeGenFunction::getNonTrivialDestructorStr(CaptureTy, Alignment, - IsVolatile, Ctx); - // The underscore is necessary here because non-trivial copy constructor - // and destructor strings can start with a number. - Name += llvm::to_string(Str.size()) + "_" + Str; - break; - } - case BlockCaptureEntityKind::None: - llvm_unreachable("unexpected block capture kind"); - } + Name += getBlockCaptureStr(E, StrKind, BlockAlignment, CGM); } return Name; @@ -1781,7 +1926,7 @@ break; } case BlockCaptureEntityKind::None: - llvm_unreachable("unexpected BlockCaptureEntityKind"); + break; } } @@ -1809,11 +1954,10 @@ llvm::Constant * CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { SmallVector CopiedCaptures; - findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures, - computeCopyInfoForBlockCapture); + findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures); std::string FuncName = getCopyDestroyHelperFuncName(CopiedCaptures, blockInfo.BlockAlign, - /*IsCopyHelper*/ true, CGM); + CaptureStrKind::CopyHelper, CGM); if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName)) return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy); @@ -1868,13 +2012,13 @@ const BlockDecl::Capture &CI = *CopiedCapture.CI; const CGBlockInfo::Capture &capture = *CopiedCapture.Capture; QualType captureType = CI.getVariable()->getType(); - BlockFieldFlags flags = CopiedCapture.Flags; + BlockFieldFlags flags = CopiedCapture.CopyFlags; unsigned index = capture.getIndex(); Address srcField = Builder.CreateStructGEP(src, index, capture.getOffset()); Address dstField = Builder.CreateStructGEP(dst, index, capture.getOffset()); - switch (CopiedCapture.Kind) { + switch (CopiedCapture.CopyKind) { case BlockCaptureEntityKind::CXXRecord: // If there's an explicit copy expression, we do that. assert(CI.getCopyExpr() && "copy expression for variable is missing"); @@ -1932,12 +2076,12 @@ break; } case BlockCaptureEntityKind::None: - llvm_unreachable("unexpected BlockCaptureEntityKind"); + continue; } // Ensure that we destroy the copied object if an exception is thrown later // in the helper function. - pushCaptureCleanup(CopiedCapture.Kind, dstField, captureType, flags, + pushCaptureCleanup(CopiedCapture.CopyKind, dstField, captureType, flags, /*ForCopyHelper*/ true, CI.getVariable(), *this); } @@ -2003,11 +2147,10 @@ llvm::Constant * CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { SmallVector DestroyedCaptures; - findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures, - computeDestroyInfoForBlockCapture); + findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures); std::string FuncName = getCopyDestroyHelperFuncName(DestroyedCaptures, blockInfo.BlockAlign, - /*IsCopyHelper*/ false, CGM); + CaptureStrKind::DisposeHelper, CGM); if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName)) return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy); @@ -2057,12 +2200,12 @@ for (const auto &DestroyedCapture : DestroyedCaptures) { const BlockDecl::Capture &CI = *DestroyedCapture.CI; const CGBlockInfo::Capture &capture = *DestroyedCapture.Capture; - BlockFieldFlags flags = DestroyedCapture.Flags; + BlockFieldFlags flags = DestroyedCapture.DisposeFlags; Address srcField = Builder.CreateStructGEP(src, capture.getIndex(), capture.getOffset()); - pushCaptureCleanup(DestroyedCapture.Kind, srcField, + pushCaptureCleanup(DestroyedCapture.DisposeKind, srcField, CI.getVariable()->getType(), flags, /*ForCopyHelper*/ false, CI.getVariable(), *this); } Index: lib/CodeGen/CGObjCMac.cpp =================================================================== --- lib/CodeGen/CGObjCMac.cpp +++ lib/CodeGen/CGObjCMac.cpp @@ -37,6 +37,7 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" #include @@ -1085,9 +1086,14 @@ const CGBlockInfo &blockInfo) override; llvm::Constant *BuildRCBlockLayout(CodeGen::CodeGenModule &CGM, const CGBlockInfo &blockInfo) override; + std::string getRCBlockLayoutStr(CodeGen::CodeGenModule &CGM, + const CGBlockInfo &blockInfo) override; llvm::Constant *BuildByrefLayout(CodeGen::CodeGenModule &CGM, QualType T) override; + +private: + void fillRunSkipBlockVars(CodeGenModule &CGM, const CGBlockInfo &blockInfo); }; namespace { @@ -2795,8 +2801,44 @@ return getConstantGEP(VMContext, Entry, 0, 0); } -llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, - const CGBlockInfo &blockInfo) { +static std::string getBlockLayoutInfoString( + const SmallVectorImpl &RunSkipBlockVars, + bool HasCopyDisposeHelpers) { + std::string Str; + for (const CGObjCCommonMac::RUN_SKIP &R : RunSkipBlockVars) { + if (R.opcode == CGObjCCommonMac::BLOCK_LAYOUT_UNRETAINED) { + // Copy/dispose helpers don't have any information about + // __unsafe_unretained captures, so unconditionally concatenate a string. + Str += "u"; + } else if (HasCopyDisposeHelpers) { + // Information about __strong, __weak, or byref captures has already been + // encoded into the names of the copy/dispose helpers. We have to add a + // string here only when the copy/dispose helpers aren't generated (which + // happens when the block is non-escaping). + continue; + } else { + switch (R.opcode) { + case CGObjCCommonMac::BLOCK_LAYOUT_STRONG: + Str += "s"; + break; + case CGObjCCommonMac::BLOCK_LAYOUT_BYREF: + Str += "r"; + break; + case CGObjCCommonMac::BLOCK_LAYOUT_WEAK: + Str += "w"; + break; + default: + continue; + } + } + Str += llvm::to_string(R.block_var_bytepos.getQuantity()); + Str += "l" + llvm::to_string(R.block_var_size.getQuantity()); + } + return Str; +} + +void CGObjCCommonMac::fillRunSkipBlockVars(CodeGenModule &CGM, + const CGBlockInfo &blockInfo) { assert(CGM.getLangOpts().getGC() == LangOptions::NonGC); RunSkipBlockVars.clear(); @@ -2845,9 +2887,22 @@ UpdateRunSkipBlockVars(CI.isByRef(), getBlockCaptureLifetime(type, false), fieldOffset, fieldSize); } +} + +llvm::Constant * +CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, + const CGBlockInfo &blockInfo) { + fillRunSkipBlockVars(CGM, blockInfo); return getBitmapBlockLayout(false); } +std::string CGObjCCommonMac::getRCBlockLayoutStr(CodeGenModule &CGM, + const CGBlockInfo &blockInfo) { + fillRunSkipBlockVars(CGM, blockInfo); + return getBlockLayoutInfoString(RunSkipBlockVars, + blockInfo.needsCopyDisposeHelpers()); +} + llvm::Constant *CGObjCCommonMac::BuildByrefLayout(CodeGen::CodeGenModule &CGM, QualType T) { assert(CGM.getLangOpts().getGC() == LangOptions::NonGC); Index: lib/CodeGen/CGObjCRuntime.h =================================================================== --- lib/CodeGen/CGObjCRuntime.h +++ lib/CodeGen/CGObjCRuntime.h @@ -278,6 +278,10 @@ const CodeGen::CGBlockInfo &blockInfo) = 0; virtual llvm::Constant *BuildRCBlockLayout(CodeGen::CodeGenModule &CGM, const CodeGen::CGBlockInfo &blockInfo) = 0; + virtual std::string getRCBlockLayoutStr(CodeGen::CodeGenModule &CGM, + const CGBlockInfo &blockInfo) { + return {}; + } /// Returns an i8* which points to the byref layout information. virtual llvm::Constant *BuildByrefLayout(CodeGen::CodeGenModule &CGM, Index: test/CodeGenCXX/blocks.cpp =================================================================== --- test/CodeGenCXX/blocks.cpp +++ test/CodeGenCXX/blocks.cpp @@ -1,5 +1,8 @@ // RUN: %clang_cc1 %s -fblocks -triple x86_64-apple-darwin -emit-llvm -o - | FileCheck %s +// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } +// CHECK: @[[BLOCK_DESCRIPTOR22:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i8* } { i64 0, i64 36, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* null }, align 8 + namespace test0 { // CHECK-LABEL: define void @_ZN5test04testEi( // CHECK: define internal void @___ZN5test04testEi_block_invoke{{.*}}( @@ -122,7 +125,7 @@ // CHECK-LABEL: define internal void @___ZN5test44testEv_block_invoke // CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align 1 // CHECK-NEXT: store i8* [[BLOCKDESC:%.*]], i8** {{.*}}, align 8 - // CHECK-NEXT: bitcast i8* [[BLOCKDESC]] to <{ i8*, i32, i32, i8*, %struct.__block_descriptor* }>* + // CHECK-NEXT: bitcast i8* [[BLOCKDESC]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]* }>* // CHECK: call void @_ZN5test41AC1Ev([[A]]* [[TMP]]) // CHECK-NEXT: call void @_ZN5test43fooENS_1AE([[A]]* [[TMP]]) // CHECK-NEXT: call void @_ZN5test41AD1Ev([[A]]* [[TMP]]) @@ -277,8 +280,11 @@ } } -// Copy/dispose helper functions that capture objects of non-external types -// should have internal linkage. +// Copy/dispose helper functions and block descriptors of blocks that capture +// objects that are non-external and non-trivial have internal linkage. + +// CHECK-LABEL: define internal void @_ZN12_GLOBAL__N_14testEv( +// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, i8*, i8*, i8*, i8* }* @[[BLOCK_DESCRIPTOR22]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %{{.*}}, align 8 // CHECK-LABEL: define internal void @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE( // CHECK-LABEL: define internal void @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE( Index: test/CodeGenObjC/arc-blocks.m =================================================================== --- test/CodeGenObjC/arc-blocks.m +++ test/CodeGenObjC/arc-blocks.m @@ -2,15 +2,10 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-UNOPT -check-prefix=CHECK-COMMON %s // CHECK-COMMON: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } -// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32r to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32r to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 16 }, align 8 -// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP9:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32r to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32r to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 16 }, align 8 -// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, 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), i64 256 }, align 8 -// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, 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), i64 256 }, align 8 -// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, 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), i64 256 }, align 8 -// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, 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), i64 256 }, align 8 -// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP44:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, 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), i64 256 }, align 8 -// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP46:.*]] = internal 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:.*]] = internal 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]* @.str.47, i32 0, i32 0), i64 256 }, align 8 +// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP44:.*]] = 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_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), i64 256 }, align 8 +// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP9:.*]] = 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_32r to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32r to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 16 }, 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 // This shouldn't crash. void test0(id (^maker)(void)) { Index: test/CodeGenObjC/arc-captured-32bit-block-var-layout-2.m =================================================================== --- test/CodeGenObjC/arc-captured-32bit-block-var-layout-2.m +++ test/CodeGenObjC/arc-captured-32bit-block-var-layout-2.m @@ -30,7 +30,8 @@ void (^block4)() = ^{ printf("%c %#lx", ch, fourByte); NSLog(@"%@", strong); }; // Test5 - // CHECK: Inline block variable layout: 0x0100, BL_STRONG:1, BL_OPERATOR:0 + // Nothing gets printed here since the descriptor of this block is merged with + // the descriptor of Test3's block. void (^block5)() = ^{ NSLog(@"%@", strong); printf("%c %#llx", ch, eightByte); }; // Test6 Index: test/CodeGenObjC/fragile-arc.m =================================================================== --- test/CodeGenObjC/fragile-arc.m +++ test/CodeGenObjC/fragile-arc.m @@ -126,13 +126,13 @@ extern void useBlock(void (^block)(void)); // 256 == 0x100 == starts with 1 strong -// GLOBALS: @__block_descriptor_tmp{{.*}} = internal constant {{.*}}, i32 256 } +// GLOBALS: @"__block_descriptor{{.*}} = linkonce_odr hidden {{.*}}, i32 256 } void testBlockLayoutStrong(id x) { useBlock(^{ (void) x; }); } // 1 == 0x001 == starts with 1 weak -// GLOBALS: @__block_descriptor_tmp{{.*}} = internal constant {{.*}}, i32 1 } +// GLOBALS: @"__block_descriptor{{.*}} = linkonce_odr hidden {{.*}}, i32 1 } void testBlockLayoutWeak(__weak id x) { useBlock(^{ (void) x; }); } Index: test/CodeGenObjC/noescape.m =================================================================== --- test/CodeGenObjC/noescape.m +++ test/CodeGenObjC/noescape.m @@ -17,7 +17,11 @@ // helper functions. // CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } -// CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*]] = internal constant { i64, i64, i8*, i64 } { i64 0, i64 40, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 + +// When the block is non-escaping, copy/dispose helpers aren't generated, so the +// block layout string must include information about __strong captures. + +// CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*ls32l8"]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i64 } { i64 0, i64 40, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 // CHECK-LABEL: define void @test0( // CHECK: call void @noescapeFunc0({{.*}}, {{.*}} nocapture {{.*}})