r337580 - [CodeGen][ObjC] Make copying and disposing of a non-escaping block

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 20 10:10:32 PDT 2018


Author: ahatanak
Date: Fri Jul 20 10:10:32 2018
New Revision: 337580

URL: http://llvm.org/viewvc/llvm-project?rev=337580&view=rev
Log:
[CodeGen][ObjC] Make copying and disposing of a non-escaping block
no-ops.

A non-escaping block on the stack will never be called after its
lifetime ends, so it doesn't have to be copied to the heap. To prevent
a non-escaping block from being copied to the heap, this patch sets
field 'isa' of the block object to NSConcreteGlobalBlock and sets the
BLOCK_IS_GLOBAL bit of field 'flags', which causes the runtime to treat
the block as if it were a global block (calling _Block_copy on the block
just returns the original block and calling _Block_release is a no-op).

Also, a new flag bit 'BLOCK_IS_NOESCAPE' is added, which allows the
runtime or tools to distinguish between true global blocks and
non-escaping blocks.

rdar://problem/39352313

Differential Revision: https://reviews.llvm.org/D49303

Modified:
    cfe/trunk/docs/Block-ABI-Apple.rst
    cfe/trunk/lib/CodeGen/CGBlocks.cpp
    cfe/trunk/lib/CodeGen/CGBlocks.h
    cfe/trunk/test/CodeGenObjC/noescape.m

Modified: cfe/trunk/docs/Block-ABI-Apple.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/Block-ABI-Apple.rst?rev=337580&r1=337579&r2=337580&view=diff
==============================================================================
--- cfe/trunk/docs/Block-ABI-Apple.rst (original)
+++ cfe/trunk/docs/Block-ABI-Apple.rst Fri Jul 20 10:10:32 2018
@@ -61,6 +61,14 @@ The following flags bits are in use thus
 .. 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. For backward compatiblity with old runtimes, whenever
+        // BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. 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),

Modified: cfe/trunk/lib/CodeGen/CGBlocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBlocks.cpp?rev=337580&r1=337579&r2=337580&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGBlocks.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGBlocks.cpp Fri Jul 20 10:10:32 2018
@@ -104,7 +104,7 @@ static llvm::Constant *buildBlockDescrip
   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 @@ static llvm::Constant *buildBlockDescrip
 
     /// 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::Value *CodeGenFunction::EmitBlockL
   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 @@ llvm::Value *CodeGenFunction::EmitBlockL
     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 =

Modified: cfe/trunk/lib/CodeGen/CGBlocks.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBlocks.h?rev=337580&r1=337579&r2=337580&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGBlocks.h (original)
+++ cfe/trunk/lib/CodeGen/CGBlocks.h Fri Jul 20 10:10:32 2018
@@ -54,6 +54,7 @@ enum BlockByrefFlags {
 };
 
 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 @@ public:
   /// 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 @@ public:
   }
 
   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

Modified: cfe/trunk/test/CodeGenObjC/noescape.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/noescape.m?rev=337580&r1=337579&r2=337580&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/noescape.m (original)
+++ cfe/trunk/test/CodeGenObjC/noescape.m Fri Jul 20 10:10:32 2018
@@ -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 noescapeFunc1(__attribute__((noesca
 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 @@ void test5(BlockTy2 b, int *p) {
   ^(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); });
+}




More information about the cfe-commits mailing list