r301667 - [CodeGen][ObjC] Don't retain captured Objective-C pointers at block

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 28 11:50:57 PDT 2017


Author: ahatanak
Date: Fri Apr 28 13:50:57 2017
New Revision: 301667

URL: http://llvm.org/viewvc/llvm-project?rev=301667&view=rev
Log:
[CodeGen][ObjC] Don't retain captured Objective-C pointers at block
creation that are const-qualified.

When a block captures an ObjC object pointer, clang retains the pointer
to prevent prematurely destroying the object the pointer points to
before the block is called or copied.

When the captured object pointer is const-qualified, we can avoid
emitting the retain/release pair since the pointer variable cannot be
modified in the scope in which the block literal is introduced.

For example:

void test(const id x) {
    callee(^{ (void)x; });
}

This patch implements that optimization.

rdar://problem/28894510

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

Modified:
    cfe/trunk/lib/CodeGen/CGBlocks.cpp
    cfe/trunk/lib/CodeGen/CGObjC.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/test/CodeGenObjC/arc-blocks.m
    cfe/trunk/test/CodeGenObjC/arc-foreach.m

Modified: cfe/trunk/lib/CodeGen/CGBlocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBlocks.cpp?rev=301667&r1=301666&r2=301667&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGBlocks.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGBlocks.cpp Fri Apr 28 13:50:57 2017
@@ -619,7 +619,13 @@ static void enterBlockScope(CodeGenFunct
 
     // Block captures count as local values and have imprecise semantics.
     // They also can't be arrays, so need to worry about that.
-    if (dtorKind == QualType::DK_objc_strong_lifetime) {
+    //
+    // For const-qualified captures, emit clang.arc.use to ensure the captured
+    // object doesn't get released while we are still depending on its validity
+    // within the block.
+    if (VT.isConstQualified() && VT.getObjCLifetime() == Qualifiers::OCL_Strong)
+      destroyer = CodeGenFunction::emitARCIntrinsicUse;
+    else if (dtorKind == QualType::DK_objc_strong_lifetime) {
       destroyer = CodeGenFunction::destroyARCStrongImprecise;
     } else {
       destroyer = CGF.getDestroyer(dtorKind);
@@ -866,6 +872,12 @@ llvm::Value *CodeGenFunction::EmitBlockL
     } else if (type->isReferenceType()) {
       Builder.CreateStore(src.getPointer(), blockField);
 
+    // If type is const-qualified, copy the value into the block field.
+    } else if (type.isConstQualified() &&
+               type.getObjCLifetime() == Qualifiers::OCL_Strong) {
+      llvm::Value *value = Builder.CreateLoad(src, "captured");
+      Builder.CreateStore(value, blockField);
+
     // If this is an ARC __strong block-pointer variable, don't do a
     // block copy.
     //

Modified: cfe/trunk/lib/CodeGen/CGObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGObjC.cpp?rev=301667&r1=301666&r2=301667&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGObjC.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGObjC.cpp Fri Apr 28 13:50:57 2017
@@ -2415,6 +2415,12 @@ void CodeGenFunction::destroyARCWeak(Cod
   CGF.EmitARCDestroyWeak(addr);
 }
 
+void CodeGenFunction::emitARCIntrinsicUse(CodeGenFunction &CGF, Address addr,
+                                          QualType type) {
+  llvm::Value *value = CGF.Builder.CreateLoad(addr);
+  CGF.EmitARCIntrinsicUse(value);
+}
+
 namespace {
   struct CallObjCAutoreleasePoolObject final : EHScopeStack::Cleanup {
     llvm::Value *Token;

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=301667&r1=301666&r2=301667&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Fri Apr 28 13:50:57 2017
@@ -3369,6 +3369,7 @@ public:
   static Destroyer destroyARCStrongImprecise;
   static Destroyer destroyARCStrongPrecise;
   static Destroyer destroyARCWeak;
+  static Destroyer emitARCIntrinsicUse;
 
   void EmitObjCAutoreleasePoolPop(llvm::Value *Ptr); 
   llvm::Value *EmitObjCAutoreleasePoolPush();

Modified: cfe/trunk/test/CodeGenObjC/arc-blocks.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/arc-blocks.m?rev=301667&r1=301666&r2=301667&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/arc-blocks.m (original)
+++ cfe/trunk/test/CodeGenObjC/arc-blocks.m Fri Apr 28 13:50:57 2017
@@ -298,15 +298,11 @@ void test7(void) {
 // CHECK:      [[D0:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
 // CHECK:      [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
 // CHECK-NEXT: [[T1:%.*]] = load [[TEST8]]*, [[TEST8]]** [[SELF]],
-// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST8]]* [[T1]] to i8*
-// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]])
-// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[TEST8]]*
-// CHECK-NEXT: store [[TEST8]]* [[T4]], [[TEST8]]** [[T0]]
+// CHECK-NEXT: store %0* [[T1]], %0** [[T0]]
 // CHECK-NEXT: bitcast [[BLOCK_T]]* [[BLOCK]] to
 // CHECK: call void @test8_helper(
-// CHECK-NEXT: [[T1:%.*]] = load [[TEST8]]*, [[TEST8]]** [[D0]]
-// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST8]]* [[T1]] to i8*
-// CHECK-NEXT: call void @objc_release(i8* [[T2]])
+// CHECK-NEXT: [[T2:%.*]] = load [[TEST8]]*, [[TEST8]]** [[D0]]
+// CHECK-NEXT: call void (...) @clang.arc.use([[TEST8]]* [[T2]])
 // CHECK: ret void
 
   extern void test8_helper(void (^)(void));
@@ -741,5 +737,31 @@ void test19(void (^b)(void)) {
 // CHECK-NEXT: ret void
 }
 
+// CHECK-LABEL: define void @test20(
+// CHECK: [[XADDR:%.*]] = alloca i8*
+// CHECK-NEXT: [[BLOCK:%.*]] = alloca <[[BLOCKTY:.*]]>
+// CHECK-NEXT: [[RETAINEDX:%.*]] = call i8* @objc_retain(i8* %{{.*}})
+// CHECK-NEXT: store i8* [[RETAINEDX]], i8** [[XADDR]]
+// CHECK-NEXT: [[CAPTUREFIELD:%.*]] = getelementptr inbounds <[[BLOCKTY]]>, <[[BLOCKTY]]>* [[BLOCK]], i32 0, i32 5
+// CHECK: [[BLOCKCAPTURED:%.*]] = getelementptr inbounds <[[BLOCKTY]]>, <[[BLOCKTY]]>* [[BLOCK]], i32 0, i32 5
+// CHECK: [[CAPTURED:%.*]] = load i8*, i8** [[XADDR]]
+// CHECK: store i8* [[CAPTURED]], i8** [[BLOCKCAPTURED]]
+// CHECK: [[CAPTURE:%.*]] = load i8*, i8** [[CAPTUREFIELD]]
+// CHECK-NEXT: call void (...) @clang.arc.use(i8* [[CAPTURE]])
+// CHECK-NEXT: [[X:%.*]] = load i8*, i8** [[XADDR]]
+// CHECK-NEXT: call void @objc_release(i8* [[X]])
+// CHECK-NEXT: ret void
+
+// CHECK-LABEL: define internal void @__copy_helper_block
+// CHECK: [[BLOCKSOURCE:%.*]] = bitcast i8* %{{.*}} to <[[BLOCKTY]]>*
+// CHECK: [[CAPTUREFIELD:%.*]] = getelementptr inbounds <[[BLOCKTY]]>, <[[BLOCKTY]]>* [[BLOCKSOURCE]], i32 0, i32 5
+// CHECK: [[BLOCKCOPYSRC:%.*]] = load i8*, i8** [[CAPTUREFIELD]]
+// CHECK: call i8* @objc_retain(i8* [[BLOCKCOPYSRC]])
+
+void test20_callee(void (^)());
+void test20(const id x) {
+  test20_callee(^{ (void)x; });
+}
+
 // CHECK: attributes [[NUW]] = { nounwind }
 // CHECK-UNOPT: attributes [[NUW]] = { nounwind }

Modified: cfe/trunk/test/CodeGenObjC/arc-foreach.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/arc-foreach.m?rev=301667&r1=301666&r2=301667&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/arc-foreach.m (original)
+++ cfe/trunk/test/CodeGenObjC/arc-foreach.m Fri Apr 28 13:50:57 2017
@@ -63,11 +63,11 @@ void test0(NSArray *array) {
 // CHECK-LP64:      [[D0:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
 // CHECK-LP64:      [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
 // CHECK-LP64-NEXT: [[T1:%.*]] = load i8*, i8** [[X]]
-// CHECK-LP64-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]])
-// CHECK-LP64-NEXT: store i8* [[T2]], i8** [[T0]]
-// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] 
-// CHECK-LP64: call void @use_block(
-// CHECK-LP64-NEXT: call void @objc_storeStrong(i8** [[D0]], i8* null)
+// CHECK-LP64-NEXT: store i8* [[T1]], i8** [[T0]]
+// CHECK-LP64-NEXT: [[BLOCK1:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]]
+// CHECK-LP64-NEXT: call void @use_block(void ()* [[BLOCK1]])
+// CHECK-LP64-NEXT: [[CAPTURE:%.*]] = load i8*, i8** [[D0]]
+// CHECK-LP64: call void (...) @clang.arc.use(i8* [[CAPTURE]])
 
 // CHECK-LP64:      [[T0:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_
 // CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[ARRAY_T]]* [[SAVED_ARRAY]] to i8*
@@ -200,13 +200,10 @@ NSArray *array4;
 // CHECK-LP64:         [[T0:%.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>* [[BLOCK]], i32 0, i32 5
 // CHECK-LP64:         [[BC:%.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>* [[BLOCK]], i32 0, i32 5
 // CHECK-LP64:         [[T1:%.*]] = load [[TY]]*, [[TY]]** [[SELF_ADDR]]
-// CHECK-LP64:         [[T2:%.*]] = bitcast [[TY]]* [[T1]] to i8*
-// CHECK-LP64:         [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]])
-// CHECK-LP64:         [[T4:%.*]] = bitcast i8* [[T3]] to [[TY]]*
-// CHECK-LP64:         store [[TY]]* [[T4]], [[TY]]** [[BC]]
+// CHECK-LP64:         store [[TY]]* [[T1]], [[TY]]** [[BC]]
 
-// CHECK-LP64:         [[T5:%.*]] = bitcast [[TY]]** [[T0]] to i8**
-// CHECK-LP64:         call void @objc_storeStrong(i8** [[T5]], i8* null)
+// CHECK-LP64:         [[T5:%.*]] = load [[TY]]*, [[TY]]** [[T0]]
+// CHECK-LP64:         call void (...) @clang.arc.use([[TY]]* [[T5]])
 // CHECK-LP64:         switch i32 {{%.*}}, label %[[UNREACHABLE:.*]] [
 // CHECK-LP64-NEXT:      i32 0, label %[[CLEANUP_CONT:.*]]
 // CHECK-LP64-NEXT:      i32 2, label %[[FORCOLL_END:.*]]




More information about the cfe-commits mailing list