From 0e6cfa4e8a86bd9f35d69f44aae209918475e44d Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 17 Aug 2018 15:46:07 +0000 Subject: [PATCH] [CodeGen] Merge identical block descriptor global variables. Currently, clang generates a new block descriptor global variable for each new block literal. This commit merges block descriptors that are identical inside and across translation units using the same approach taken in r339438. To enable merging identical block descriptors, the size and signature of the block and information about the captures are encoded into the name of the block descriptor variable. Also, the block descriptor variable is marked as linkonce_odr and unnamed_addr. rdar://problem/42640703 Differential Revision: https://reviews.llvm.org/D50783 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@340041 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGBlocks.cpp | 403 ++++++++++++------ lib/CodeGen/CGBlocks.h | 3 + lib/CodeGen/CGObjCMac.cpp | 59 ++- lib/CodeGen/CGObjCRuntime.h | 4 + test/CodeGenCXX/blocks.cpp | 12 +- test/CodeGenObjC/arc-blocks.m | 13 +- .../arc-captured-32bit-block-var-layout-2.m | 3 +- test/CodeGenObjC/fragile-arc.m | 4 +- test/CodeGenObjC/noescape.m | 6 +- 9 files changed, 359 insertions(+), 148 deletions(-) diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index c5066f4829..8b8b6ca66f 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -65,6 +65,107 @@ static llvm::Constant *buildDisposeHelper(CodeGenModule &CGM, 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 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, 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 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, 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 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, 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 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, return fn; } -namespace { - -/// Represents a type of copy/destroy operation that should be performed for an -/// entity that's captured by a block. -enum class BlockCaptureEntityKind { - CXXRecord, // Copy or destroy - ARCWeak, - ARCStrong, - 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 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, 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 @@ bool CodeGenFunction::cxxDestructorCanThrow(QualType T) { 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 @@ static std::string getCopyDestroyHelperFuncName( 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 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, break; } case BlockCaptureEntityKind::None: - llvm_unreachable("unexpected BlockCaptureEntityKind"); + break; } } @@ -1809,11 +1954,10 @@ static void setBlockHelperAttributesVisibility(bool CapturesNonExternalType, 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 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { 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 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { 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 @@ computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, 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 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { 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); } diff --git a/lib/CodeGen/CGBlocks.h b/lib/CodeGen/CGBlocks.h index 402acea70e..c802948af0 100644 --- a/lib/CodeGen/CGBlocks.h +++ b/lib/CodeGen/CGBlocks.h @@ -132,6 +132,9 @@ public: 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); diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 2b54e7bd67..92e442d331 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/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 @@ public: 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 @@ llvm::Constant *CGObjCCommonMac::getBitmapBlockLayout(bool ComputeByrefLayout) { 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 @@ llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, 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); diff --git a/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h index 78d5aba412..fa16c198ad 100644 --- a/lib/CodeGen/CGObjCRuntime.h +++ b/lib/CodeGen/CGObjCRuntime.h @@ -278,6 +278,10 @@ public: 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, diff --git a/test/CodeGenCXX/blocks.cpp b/test/CodeGenCXX/blocks.cpp index 7955b00f6f..32b1dd82dd 100644 --- a/test/CodeGenCXX/blocks.cpp +++ b/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 @@ namespace test4 { // 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 @@ namespace test10 { } } -// 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( diff --git a/test/CodeGenObjC/arc-blocks.m b/test/CodeGenObjC/arc-blocks.m index 602b772c22..89317ab113 100644 --- a/test/CodeGenObjC/arc-blocks.m +++ b/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)) { diff --git a/test/CodeGenObjC/arc-captured-32bit-block-var-layout-2.m b/test/CodeGenObjC/arc-captured-32bit-block-var-layout-2.m index 10feda938d..e944e35ed1 100644 --- a/test/CodeGenObjC/arc-captured-32bit-block-var-layout-2.m +++ b/test/CodeGenObjC/arc-captured-32bit-block-var-layout-2.m @@ -30,7 +30,8 @@ int main() { 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 diff --git a/test/CodeGenObjC/fragile-arc.m b/test/CodeGenObjC/fragile-arc.m index 2bf813d461..98a60133e2 100644 --- a/test/CodeGenObjC/fragile-arc.m +++ b/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; }); } diff --git a/test/CodeGenObjC/noescape.m b/test/CodeGenObjC/noescape.m index 56e1e51e5d..b4c8bf7eaf 100644 --- a/test/CodeGenObjC/noescape.m +++ b/test/CodeGenObjC/noescape.m @@ -17,7 +17,11 @@ void noescapeFunc3(__attribute__((noescape)) union U); // 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 {{.*}}) -- 2.40.0