]> granicus.if.org Git - clang/commitdiff
[CodeGen][ObjC] Don't retain captured Objective-C pointers at block
authorAkira Hatanaka <ahatanaka@apple.com>
Fri, 28 Apr 2017 18:50:57 +0000 (18:50 +0000)
committerAkira Hatanaka <ahatanaka@apple.com>
Fri, 28 Apr 2017 18:50:57 +0000 (18:50 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@301667 91177308-0d34-0410-b5e6-96231b3b80d8

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

index 1a57b3e6608dccd76315a363cba9ee403b51bbc0..791a57e61f530c91ae28b0ed29a8e6dc26a52392 100644 (file)
@@ -619,7 +619,13 @@ static void enterBlockScope(CodeGenFunction &CGF, BlockDecl *block) {
 
     // 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::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
     } 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.
     //
index 76e7df861f740e9afc3bb42b594046c3199e57c2..f4fbab3c2b8326bc54828f37357deda2a6d8e87a 100644 (file)
@@ -2415,6 +2415,12 @@ void CodeGenFunction::destroyARCWeak(CodeGenFunction &CGF,
   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;
index 1ded824ba5b02de90c273c3c8412fa8304b8b925..f9bc4a1bc2db57c66e0b726190b3bb575ec8d5e6 100644 (file)
@@ -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();
index 4bb618c3a415d84722d599f0a0d7d15bf3a123f0..69cd7bb297b2751a2bfa0cfdda57e979a7b1f86f 100644 (file)
@@ -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 }
index db150e88a59f59f0572713af82d53dc11b7907d5..e34445704e3745eb660e9c224e8098e30f707bda 100644 (file)
@@ -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:.*]]