From 2e00b98027b51ee5bfb88a4daea8b78bf9a01e62 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Sat, 8 Sep 2018 20:03:00 +0000 Subject: Distinguish `__block` variables that are captured by escaping blocks from those that aren't. This patch changes the way __block variables that aren't captured by escaping blocks are handled: - Since non-escaping blocks on the stack never get copied to the heap (see https://reviews.llvm.org/D49303), Sema shouldn't error out when the type of a non-escaping __block variable doesn't have an accessible copy constructor. - IRGen doesn't have to use the specialized byref structure (see https://clang.llvm.org/docs/Block-ABI-Apple.html#id8) for a non-escaping __block variable anymore. Instead IRGen can emit the variable as a normal variable and copy the reference to the block literal. Byref copy/dispose helpers aren't needed either. rdar://problem/39352313 Differential Revision: https://reviews.llvm.org/D51564 llvm-svn: 341754 --- clang/test/CodeGenObjC/arc-no-arc-exceptions.m | 1 + clang/test/CodeGenObjC/arc-unoptimized-byref-var.m | 1 + clang/test/CodeGenObjC/blocks-1.m | 49 +++++++++++--------- clang/test/CodeGenObjC/noescape.m | 52 +++++++++++++++++++++- 4 files changed, 82 insertions(+), 21 deletions(-) (limited to 'clang/test/CodeGenObjC') diff --git a/clang/test/CodeGenObjC/arc-no-arc-exceptions.m b/clang/test/CodeGenObjC/arc-no-arc-exceptions.m index 3338ad8477b..31d1dd53763 100644 --- a/clang/test/CodeGenObjC/arc-no-arc-exceptions.m +++ b/clang/test/CodeGenObjC/arc-no-arc-exceptions.m @@ -42,6 +42,7 @@ void NSLog(id, ...); void test2(void) { @autoreleasepool { __attribute__((__blocks__(byref))) int x; + ^{ (void)x; }; NSLog(@"Address of x outside of block: %p", &x); } } diff --git a/clang/test/CodeGenObjC/arc-unoptimized-byref-var.m b/clang/test/CodeGenObjC/arc-unoptimized-byref-var.m index 9d856d659af..ffc73a44d4e 100644 --- a/clang/test/CodeGenObjC/arc-unoptimized-byref-var.m +++ b/clang/test/CodeGenObjC/arc-unoptimized-byref-var.m @@ -3,6 +3,7 @@ void test19() { __block id x; + ^{ (void)x; }; // CHECK-UNOPT-LABEL: define internal void @__Block_byref_object_copy // CHECK-UNOPT: [[X:%.*]] = getelementptr inbounds [[BYREF_T:%.*]], [[BYREF_T:%.*]]* [[VAR:%.*]], i32 0, i32 6 // CHECK-UNOPT: [[X2:%.*]] = getelementptr inbounds [[BYREF_T:%.*]], [[BYREF_T:%.*]]* [[VAR1:%.*]], i32 0, i32 6 diff --git a/clang/test/CodeGenObjC/blocks-1.m b/clang/test/CodeGenObjC/blocks-1.m index 0d2c35096ae..cecb55d17a8 100644 --- a/clang/test/CodeGenObjC/blocks-1.m +++ b/clang/test/CodeGenObjC/blocks-1.m @@ -1,23 +1,31 @@ -// RUN: %clang_cc1 %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -// RUN: grep "_Block_object_dispose" %t | count 6 -// RUN: grep "__copy_helper_block_" %t | count 4 -// RUN: grep "__destroy_helper_block_" %t | count 4 -// RUN: grep "__Block_byref_object_copy_" %t | count 2 -// RUN: grep "__Block_byref_object_dispose_" %t | count 2 -// RUN: not grep "i32 135)" %t -// RUN: grep "_Block_object_assign" %t | count 4 -// RUN: grep "objc_read_weak" %t | count 2 -// RUN: grep "objc_assign_weak" %t | count 3 -// RUN: %clang_cc1 -x objective-c++ %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -// RUN: grep "_Block_object_dispose" %t | count 6 -// RUN: grep "__copy_helper_block_" %t | count 4 -// RUN: grep "__destroy_helper_block_" %t | count 4 -// RUN: grep "__Block_byref_object_copy_" %t | count 2 -// RUN: grep "__Block_byref_object_dispose_" %t | count 2 -// RUN: not grep "i32 135)" %t -// RUN: grep "_Block_object_assign" %t | count 4 -// RUN: grep "objc_read_weak" %t | count 2 -// RUN: grep "objc_assign_weak" %t | count 3 +// RUN: %clang_cc1 %s -emit-llvm -o - -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s --check-prefix=CHECK --check-prefix=OBJC +// RUN: %clang_cc1 -x objective-c++ %s -emit-llvm -o - -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s --check-prefix=CHECK --check-prefix=OBJCXX + +// OBJC-LABEL: define void @test1( +// OBJCXX-LABEL: define void @_Z5test1P12NSDictionary( + +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ +// CHECK: call void @_Block_object_assign( + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_ +// CHECK: call void @_Block_object_dispose( + +// OBJC-LABEL: define void @foo( +// OBJCXX-LABEL: define void @_Z3foov( +// CHECK: call i8* @objc_read_weak( +// CHECK: call i8* @objc_assign_weak( +// CHECK: call void @_Block_object_dispose( + +// OBJC-LABEL: define void @test2( +// OBJCXX-LABEL: define void @_Z5test2v( +// CHECK: call i8* @objc_assign_weak( +// CHECK: call void @_Block_object_dispose( + +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ +// CHECK: call void @_Block_object_assign( + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_ +// CHECK: call void @_Block_object_dispose( @interface NSDictionary @end @@ -30,6 +38,7 @@ void test1(NSDictionary * dict) { void foo() { __block __weak D *weakSelf; + ^{ (void)weakSelf; }; D *l; l = weakSelf; weakSelf = l; diff --git a/clang/test/CodeGenObjC/noescape.m b/clang/test/CodeGenObjC/noescape.m index b4c8bf7eaf2..c7624f24fb6 100644 --- a/clang/test/CodeGenObjC/noescape.m +++ b/clang/test/CodeGenObjC/noescape.m @@ -8,6 +8,7 @@ union U { long long *ll; } __attribute__((transparent_union)); +void escapingFunc0(BlockTy); void noescapeFunc0(id, __attribute__((noescape)) BlockTy); void noescapeFunc1(__attribute__((noescape)) int *); void noescapeFunc2(__attribute__((noescape)) id); @@ -21,6 +22,9 @@ void noescapeFunc3(__attribute__((noescape)) union U); // When the block is non-escaping, copy/dispose helpers aren't generated, so the // block layout string must include information about __strong captures. +// CHECK-NOARC: %[[STRUCT_BLOCK_BYREF_B0:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_B0]]*, i32, i32, i8*, %[[STRUCT_S0:.*]] } +// CHECK-ARC: %[[STRUCT_BLOCK_BYREF_B0:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_B0]]*, i32, i32, i8*, i8*, i8*, %[[STRUCT_S0:.*]] } +// CHECK: %[[STRUCT_S0]] = type { i8*, i8* } // CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*ls32l8"]] = linkonce_odr hidden unnamed_addr 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( @@ -102,7 +106,7 @@ void test5(BlockTy2 b, int *p) { // 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: %[[V3:.*]] = call i8* @objc_retain(i8* %[[V2]]) // CHECK-ARC: store i8* %[[V3]], i8** %[[BLOCK_CAPTURED]], align 8 // CHECK: call void @noescapeFunc0( // CHECK-ARC: call void @objc_storeStrong(i8** %[[V0]], i8* null) @@ -118,3 +122,49 @@ void func(id); void test6(id a, id b) { noescapeFunc0(a, ^{ func(b); }); } + +// We don't need either the byref helper functions or the byref structs for +// __block variables that are not captured by escaping blocks. + +// CHECK: define void @test7( +// CHECK: alloca i8*, align 8 +// CHECK: %[[B0:.*]] = alloca i8*, align 8 +// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>, 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: store i8** %[[B0]], i8*** %[[BLOCK_CAPTURED]], align 8 + +// CHECK-ARC-NOT: define internal void @__Block_byref_object_copy_ +// CHECK-ARC-NOT: define internal void @__Block_byref_object_dispose_ + +void test7() { + id a; + __block id b0; + noescapeFunc0(a, ^{ (void)b0; }); +} + +// __block variables captured by escaping blocks need byref helper functions. + +// CHECK: define void @test8( +// CHECK: %[[A:.*]] = alloca i8*, align 8 +// CHECK: %[[B0:.*]] = alloca %[[STRUCT_BLOCK_BYREF_B0]], align 8 +// CHECK: alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8 +// CHECK: %[[BLOCK1:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8 +// CHECK: %[[BLOCK_CAPTURED7:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK1]], i32 0, i32 5 +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_BLOCK_BYREF_B0]]* %[[B0]] to i8* +// CHECK: store i8* %[[V3]], i8** %[[BLOCK_CAPTURED7]], align 8 + +// CHECK-ARC: define internal void @__Block_byref_object_copy_ +// CHECK-ARC: define internal void @__Block_byref_object_dispose_ +// CHECK: define linkonce_odr hidden void @__copy_helper_block_ +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_ + +struct S0 { + id a, b; +}; + +void test8() { + id a; + __block struct S0 b0; + noescapeFunc0(a, ^{ (void)b0; }); + escapingFunc0(^{ (void)b0; }); +} -- cgit v1.2.3