Index: docs/Block-ABI-Apple.rst =================================================================== --- docs/Block-ABI-Apple.rst +++ docs/Block-ABI-Apple.rst @@ -61,6 +61,14 @@ .. code-block:: c enum { + // Set to true on blocks that have captures (and thus are not true + // global blocks) but are known not to escape for various other + // reasons. BLOCK_IS_NOESCAPE implies BLOCK_IS_GLOBAL since copying a + // non-escaping block returns the original block and releasing such a + // block is a no-op, which is exactly how global blocks are handled. + + BLOCK_IS_NOESCAPE = (1 << 23), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code BLOCK_IS_GLOBAL = (1 << 28), Index: lib/CodeGen/CGBlocks.h =================================================================== --- lib/CodeGen/CGBlocks.h +++ lib/CodeGen/CGBlocks.h @@ -54,6 +54,7 @@ }; enum BlockLiteralFlags { + BLOCK_IS_NOESCAPE = (1 << 23), BLOCK_HAS_COPY_DISPOSE = (1 << 25), BLOCK_HAS_CXX_OBJ = (1 << 26), BLOCK_IS_GLOBAL = (1 << 28), @@ -214,7 +215,8 @@ /// no non-constant captures. bool CanBeGlobal : 1; - /// True if the block needs a custom copy or dispose function. + /// True if the block has captures that would necessitate custom copy or + /// dispose helper functions if the block were escaping. bool NeedsCopyDispose : 1; /// HasCXXObject - True if the block's custom copy/dispose functions @@ -276,6 +278,11 @@ } 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 Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ lib/CodeGen/CGBlocks.cpp @@ -104,7 +104,7 @@ elements.addInt(ulong, blockInfo.BlockSize.getQuantity()); // Optional copy/dispose helpers. - if (blockInfo.NeedsCopyDispose) { + if (blockInfo.needsCopyDisposeHelpers()) { // copy_func_helper_decl elements.add(buildCopyHelper(CGM, blockInfo)); @@ -159,6 +159,7 @@ /// These are the flags (with corresponding bit number) that the /// compiler is actually supposed to know about. + /// 23. BLOCK_IS_NOESCAPE - indicates that the block is non-escaping /// 25. BLOCK_HAS_COPY_DISPOSE - indicates that the block /// descriptor provides copy and dispose helper functions /// 26. BLOCK_HAS_CXX_OBJ - indicates that there's a captured @@ -778,8 +779,13 @@ llvm::Constant *descriptor; BlockFlags flags; if (!IsOpenCL) { - isa = llvm::ConstantExpr::getBitCast(CGM.getNSConcreteStackBlock(), - VoidPtrTy); + // 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() + ? CGM.getNSConcreteGlobalBlock() + : CGM.getNSConcreteStackBlock(); + isa = llvm::ConstantExpr::getBitCast(blockISA, VoidPtrTy); // Build the block descriptor. descriptor = buildBlockDescriptor(CGM, blockInfo); @@ -788,12 +794,14 @@ flags = BLOCK_HAS_SIGNATURE; if (blockInfo.HasCapturedVariableLayout) flags |= BLOCK_HAS_EXTENDED_LAYOUT; - if (blockInfo.NeedsCopyDispose) + if (blockInfo.needsCopyDisposeHelpers()) flags |= BLOCK_HAS_COPY_DISPOSE; if (blockInfo.HasCXXObject) flags |= BLOCK_HAS_CXX_OBJ; if (blockInfo.UsesStret) flags |= BLOCK_USE_STRET; + if (blockInfo.getBlockDecl()->doesNotEscape()) + flags |= BLOCK_IS_NOESCAPE | BLOCK_IS_GLOBAL; } auto projectField = Index: test/CodeGenObjC/noescape.m =================================================================== --- test/CodeGenObjC/noescape.m +++ test/CodeGenObjC/noescape.m @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin -fblocks -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fblocks -emit-llvm -o - %s | FileCheck -check-prefix CHECK -check-prefix CHECK-NOARC %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fblocks -emit-llvm -fobjc-arc -o - %s | FileCheck -check-prefix CHECK -check-prefix CHECK-ARC %s typedef void (^BlockTy)(void); @@ -12,6 +13,12 @@ void noescapeFunc2(__attribute__((noescape)) id); void noescapeFunc3(__attribute__((noescape)) union U); +// Block descriptors of non-escaping blocks don't need pointers to copy/dispose +// 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-LABEL: define void @test0( // CHECK: call void @noescapeFunc0({{.*}}, {{.*}} nocapture {{.*}}) // CHECK: declare void @noescapeFunc0(i8*, {{.*}} nocapture) @@ -69,3 +76,41 @@ ^(int *__attribute__((noescape)) p0){}(p); b(p); } + +// If the block is non-escaping, set the BLOCK_IS_NOESCAPE and BLOCK_IS_GLOBAL +// bits of field 'flags' and set the 'isa' field to 'NSConcreteGlobalBlock'. + +// CHECK: define void @test6(i8* %{{.*}}, i8* %[[B:.*]]) +// CHECK: %{{.*}} = alloca i8*, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca i8*, align 8 +// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8 +// CHECK-NOARC: store i8* %[[B]], i8** %[[B_ADDR]], align 8 +// CHECK-ARC: store i8* null, i8** %[[B_ADDR]], align 8 +// CHECK-ARC: call void @objc_storeStrong(i8** %[[B_ADDR]], i8* %[[B]]) +// CHECK-ARC: %[[V0:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK]], i32 0, i32 5 +// CHECK: %[[BLOCK_ISA:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK]], i32 0, i32 0 +// CHECK: store i8* bitcast (i8** @_NSConcreteGlobalBlock to i8*), i8** %[[BLOCK_ISA]], align 8 +// CHECK: %[[BLOCK_FLAGS:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK]], i32 0, i32 1 +// CHECK: store i32 -796917760, i32* %[[BLOCK_FLAGS]], align 8 +// CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK]], i32 0, i32 4 +// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, i8*, i64 }* @[[BLOCK_DESCIPTOR_TMP_2]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 +// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK]], i32 0, i32 5 +// CHECK-NOARC: %[[V1:.*]] = load i8*, i8** %[[B_ADDR]], align 8 +// CHECK-NOARC: store i8* %[[V1]], i8** %[[BLOCK_CAPTURED]], align 8 +// CHECK-ARC: %[[V2:.*]] = load i8*, i8** %[[B_ADDR]], align 8 +// CHECK-ARC: %[[V3:.*]] = call i8* @objc_retain(i8* %[[V2]]) #3 +// CHECK-ARC: store i8* %[[V3]], i8** %[[BLOCK_CAPTURED]], align 8 +// CHECK: call void @noescapeFunc0( +// CHECK-ARC: call void @objc_storeStrong(i8** %[[V0]], i8* null) +// CHECK-ARC: call void @objc_storeStrong(i8** %[[B_ADDR]], i8* null) + +// Non-escaping blocks don't need copy/dispose helper functions. + +// CHECK-NOT: define internal void @__copy_helper_block_ +// CHECK-NOT: define internal void @__destroy_helper_block_ + +void func(id); + +void test6(id a, id b) { + noescapeFunc0(a, ^{ func(b); }); +}