Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ lib/CodeGen/CGBlocks.cpp @@ -65,6 +65,59 @@ return CodeGenFunction(CGM).GenerateDestroyHelperFunction(blockInfo); } +namespace { + +// A struct containing the information needed to generate a block descriptor +// global variable and its name. +struct BlockDescriptorInfo { + // Size of the block. + unsigned Size; + + // Block copy and dispose helper functions. + llvm::Constant *CopyHelper = nullptr, *DisposeHelper = nullptr; + + // Type-encoding string. + std::string TypeAtEncoding; + + // Block layout string. Note that we cannot use the block layout's C literal + // string since it may contain a null byte, which is not allowed in function + // names. + std::string BlockLayoutStr; + + // The global variable that has the block layout information. + llvm::Constant *LayoutInfo; +}; + +const char CopyHelperPrefix[] = "__copy_helper_block_"; +const char DisposeHelperPrefix[] = "__destroy_helper_block_"; + +} + +static std::string getBlockDescriptorName(const BlockDescriptorInfo &DescInfo) { + std::string Name = "__block_descriptor"; + Name += llvm::to_string(DescInfo.Size); + + if (DescInfo.CopyHelper) { + // The name of the copy helper with the prefix removed. + StringRef CopyHelperStr = + DescInfo.CopyHelper->getOperand(0)->getName().substr( + StringRef(CopyHelperPrefix).size()); + Name += + "c" + llvm::to_string(CopyHelperStr.size()) + "_" + CopyHelperStr.str(); + // The name of the dispose helper with the prefix removed. + StringRef DisposeHelperStr = + DescInfo.DisposeHelper->getOperand(0)->getName().substr( + StringRef(DisposeHelperPrefix).size()); + Name += "d" + llvm::to_string(DisposeHelperStr.size()) + "_" + + DisposeHelperStr.str(); + } + + Name += "e" + llvm::to_string(DescInfo.TypeAtEncoding.size()) + "_" + + DescInfo.TypeAtEncoding; + Name += "l" + DescInfo.BlockLayoutStr; + 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. @@ -82,17 +135,55 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, const CGBlockInfo &blockInfo) { ASTContext &C = CGM.getContext(); + BlockDescriptorInfo descInfo; llvm::IntegerType *ulong = - cast(CGM.getTypes().ConvertType(C.UnsignedLongTy)); + cast(CGM.getTypes().ConvertType(C.UnsignedLongTy)); llvm::PointerType *i8p = nullptr; if (CGM.getLangOpts().OpenCL) - i8p = - llvm::Type::getInt8PtrTy( - CGM.getLLVMContext(), C.getTargetAddressSpace(LangAS::opencl_constant)); + i8p = llvm::Type::getInt8PtrTy( + CGM.getLLVMContext(), C.getTargetAddressSpace(LangAS::opencl_constant)); else i8p = CGM.VoidPtrTy; + descInfo.Size = blockInfo.BlockSize.getQuantity(); + + // Optional copy/dispose helpers. + if (blockInfo.needsCopyDisposeHelpers()) { + // copy_func_helper_decl + descInfo.CopyHelper = buildCopyHelper(CGM, blockInfo); + + // destroy_func_decl + descInfo.DisposeHelper = buildDisposeHelper(CGM, blockInfo); + } + + descInfo.TypeAtEncoding = + CGM.getContext().getObjCEncodingForBlock(blockInfo.getBlockExpr()); + + std::string descName; + + // GC layout. + if (C.getLangOpts().ObjC1) { + if (CGM.getLangOpts().getGC() != LangOptions::NonGC) + descInfo.LayoutInfo = + CGM.getObjCRuntime().BuildGCBlockLayout(CGM, blockInfo); + else { + descInfo.LayoutInfo = + CGM.getObjCRuntime().BuildRCBlockLayout(CGM, blockInfo, + descInfo.BlockLayoutStr); + + // Get the encoded block descriptor name. + descName = getBlockDescriptorName(descInfo); + + // If the block descriptor already exists, return it. + if (llvm::GlobalValue *desc = CGM.getModule().getNamedValue(descName)) + return llvm::ConstantExpr::getBitCast(desc, + CGM.getBlockDescriptorType()); + } + } else + descInfo.LayoutInfo = llvm::ConstantPointerNull::get(i8p); + + // Create a new block descriptor global variable. ConstantInitBuilder builder(CGM); auto elements = builder.beginStruct(); @@ -103,43 +194,54 @@ // FIXME: What is the right way to say this doesn't fit? We should give // a user diagnostic in that case. Better fix would be to change the // API to size_t. - elements.addInt(ulong, blockInfo.BlockSize.getQuantity()); + elements.addInt(ulong, descInfo.Size); // Optional copy/dispose helpers. + assert(((descInfo.CopyHelper && descInfo.DisposeHelper) || + (!descInfo.CopyHelper && !descInfo.DisposeHelper)) && + "both helpers exist or neither helpers exist"); if (blockInfo.needsCopyDisposeHelpers()) { - // copy_func_helper_decl - elements.add(buildCopyHelper(CGM, blockInfo)); - - // destroy_func_decl - elements.add(buildDisposeHelper(CGM, blockInfo)); + assert(descInfo.CopyHelper && "no copy helper"); + elements.add(descInfo.CopyHelper); + elements.add(descInfo.DisposeHelper); } // Signature. Mandatory ObjC-style method descriptor @encode sequence. - std::string typeAtEncoding = - CGM.getContext().getObjCEncodingForBlock(blockInfo.getBlockExpr()); elements.add(llvm::ConstantExpr::getBitCast( - CGM.GetAddrOfConstantCString(typeAtEncoding).getPointer(), i8p)); + CGM.GetAddrOfConstantCString(descInfo.TypeAtEncoding).getPointer(), i8p)); - // GC layout. - if (C.getLangOpts().ObjC1) { - if (CGM.getLangOpts().getGC() != LangOptions::NonGC) - elements.add(CGM.getObjCRuntime().BuildGCBlockLayout(CGM, blockInfo)); - else - elements.add(CGM.getObjCRuntime().BuildRCBlockLayout(CGM, blockInfo)); - } - else - elements.addNullPointer(i8p); + elements.add(descInfo.LayoutInfo); unsigned AddrSpace = 0; 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 (descInfo.CopyHelper) { + const auto *CopyHelper = + cast(descInfo.CopyHelper->getOperand(0)); + const auto *DisposeHelper = + cast(descInfo.DisposeHelper->getOperand(0)); + // If either the copy helper or the dispose helper has internal linkage, + // the block descriptor must have internal linkage too. + if (CopyHelper->getLinkage() == llvm::GlobalValue::InternalLinkage || + DisposeHelper->getLinkage() == llvm::GlobalValue::InternalLinkage) + linkage = llvm::GlobalValue::InternalLinkage; + else + linkage = llvm::GlobalValue::LinkOnceODRLinkage; + } 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); return llvm::ConstantExpr::getBitCast(global, CGM.getBlockDescriptorType()); } @@ -1664,7 +1766,7 @@ ItaniumMangleContext::create(Ctx, Ctx.getDiagnostics())); std::string Name = - IsCopyHelper ? "__copy_helper_block_" : "__destroy_helper_block_"; + IsCopyHelper ? CopyHelperPrefix : DisposeHelperPrefix; if (CGM.getLangOpts().Exceptions) Name += "e"; if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions) Index: lib/CodeGen/CGObjCGNU.cpp =================================================================== --- lib/CodeGen/CGObjCGNU.cpp +++ lib/CodeGen/CGObjCGNU.cpp @@ -655,7 +655,8 @@ return NULLPtr; } llvm::Constant *BuildRCBlockLayout(CodeGenModule &CGM, - const CGBlockInfo &blockInfo) override { + const CGBlockInfo &blockInfo, + std::string &layoutStr) override { return NULLPtr; } 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 @@ -1084,7 +1085,8 @@ llvm::Constant *BuildGCBlockLayout(CodeGen::CodeGenModule &CGM, const CGBlockInfo &blockInfo) override; llvm::Constant *BuildRCBlockLayout(CodeGen::CodeGenModule &CGM, - const CGBlockInfo &blockInfo) override; + const CGBlockInfo &blockInfo, + std::string &layoutStr) override; llvm::Constant *BuildByrefLayout(CodeGen::CodeGenModule &CGM, QualType T) override; @@ -2795,8 +2797,37 @@ return getConstantGEP(VMContext, Entry, 0, 0); } +static std::string getBlockLayoutInfoString( + const SmallVectorImpl &RunSkipBlockVars) { + std::string Str; + + for (const CGObjCCommonMac::RUN_SKIP &R : RunSkipBlockVars) { + Str += llvm::to_string(R.block_var_bytepos.getQuantity()); + Str += "s" + llvm::to_string(R.block_var_size.getQuantity()); + 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; + case CGObjCCommonMac::BLOCK_LAYOUT_UNRETAINED: + Str += "u"; + break; + default: + break; + } + } + + return Str; +} + llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, - const CGBlockInfo &blockInfo) { + const CGBlockInfo &blockInfo, + std::string &layoutStr) { assert(CGM.getLangOpts().getGC() == LangOptions::NonGC); RunSkipBlockVars.clear(); @@ -2845,6 +2876,8 @@ UpdateRunSkipBlockVars(CI.isByRef(), getBlockCaptureLifetime(type, false), fieldOffset, fieldSize); } + + layoutStr = getBlockLayoutInfoString(RunSkipBlockVars); return getBitmapBlockLayout(false); } Index: lib/CodeGen/CGObjCRuntime.h =================================================================== --- lib/CodeGen/CGObjCRuntime.h +++ lib/CodeGen/CGObjCRuntime.h @@ -277,7 +277,8 @@ virtual llvm::Constant *BuildGCBlockLayout(CodeGen::CodeGenModule &CGM, const CodeGen::CGBlockInfo &blockInfo) = 0; virtual llvm::Constant *BuildRCBlockLayout(CodeGen::CodeGenModule &CGM, - const CodeGen::CGBlockInfo &blockInfo) = 0; + const CodeGen::CGBlockInfo &blockInfo, + std::string &layoutStr) = 0; /// 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 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 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 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 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/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,7 @@ // 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 +// CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*]] = linkonce_odr hidden 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 {{.*}})