ahatanak created this revision. ahatanak added reviewers: rjmccall, arphaman. Herald added subscribers: dexonsmith, mgrang.
Currently, clang generates different copy or dispose helper functions for each block literal on the stack if the block has the possibility of being copied to the heap. This patch makes changes to merge equivalent copy and dispose helper functions and reduce code size. To enable merging equivalent copy/dispose functions, the types and offsets of the captured objects are encoded into the helper function name. This allows IRGen to check whether an equivalent helper function has already been emitted and reuse the function instead of generating a new helper function whenever a block is defined. In addition, the helper functions are marked as `linkonce_odr` to enable merging helper functions that have the same name across translation units and marked as `unnamed_addr` to enable the linker's deduplication pass to merge functions that have different names but the same content. rdar://problem/22950898 Repository: rC Clang https://reviews.llvm.org/D50152 Files: lib/CodeGen/CGBlocks.cpp lib/CodeGen/CGNonTrivialStruct.cpp lib/CodeGen/CodeGenFunction.cpp lib/CodeGen/CodeGenFunction.h test/CodeGen/blocks-1.c test/CodeGen/blocks.c test/CodeGen/sanitize-thread-no-checking-at-run-time.m test/CodeGenCXX/block-byref-cxx-objc.cpp test/CodeGenCXX/blocks.cpp test/CodeGenCXX/cxx-block-objects.cpp test/CodeGenObjC/arc-blocks.m test/CodeGenObjC/debug-info-block-helper.m test/CodeGenObjC/debug-info-blocks.m test/CodeGenObjC/mrc-weak.m test/CodeGenObjC/strong-in-c-struct.m test/CodeGenObjCXX/arc-blocks.mm test/CodeGenObjCXX/lambda-to-block.mm test/CodeGenObjCXX/mrc-weak.mm
Index: test/CodeGenObjCXX/mrc-weak.mm =================================================================== --- test/CodeGenObjCXX/mrc-weak.mm +++ test/CodeGenObjCXX/mrc-weak.mm @@ -119,10 +119,10 @@ // CHECK: call void @use_block // CHECK: call void @objc_destroyWeak -// CHECK-LABEL: define internal void @__copy_helper_block +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block // CHECK: @objc_copyWeak -// CHECK-LABEL: define internal void @__destroy_helper_block +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block // CHECK: @objc_destroyWeak void test8(void) { @@ -142,8 +142,8 @@ // CHECK: call void @objc_destroyWeak // CHECK-LABEL: define void @_Z14test9_baselinev() -// CHECK: define internal void @__copy_helper -// CHECK: define internal void @__destroy_helper +// CHECK: define linkonce_odr hidden void @__copy_helper +// CHECK: define linkonce_odr hidden void @__destroy_helper void test9_baseline(void) { Foo *p = get_object(); use_block(^{ [p run]; }); Index: test/CodeGenObjCXX/lambda-to-block.mm =================================================================== --- test/CodeGenObjCXX/lambda-to-block.mm +++ test/CodeGenObjCXX/lambda-to-block.mm @@ -12,7 +12,7 @@ void hasLambda(Copyable x) { takesBlock([x] () { }); } -// CHECK-LABEL: define internal void @__copy_helper_block_ +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ // CHECK: call void @"_ZZ9hasLambda8CopyableEN3$_0C1ERKS0_" // CHECK-LABEL: define internal void @"_ZZ9hasLambda8CopyableEN3$_0C2ERKS0_" // CHECK: call void @_ZN8CopyableC1ERKS_ Index: test/CodeGenObjCXX/arc-blocks.mm =================================================================== --- test/CodeGenObjCXX/arc-blocks.mm +++ test/CodeGenObjCXX/arc-blocks.mm @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -std=gnu++98 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-runtime-has-weak -fblocks -fobjc-arc -fexceptions -fobjc-arc-exceptions -o - %s | FileCheck -check-prefix CHECK %s // RUN: %clang_cc1 -std=gnu++98 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-runtime-has-weak -fblocks -fobjc-arc -fexceptions -fobjc-arc-exceptions -O1 -o - %s | FileCheck -check-prefix CHECK-O1 %s +// RUN: %clang_cc1 -std=gnu++98 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-runtime-has-weak -fblocks -fobjc-arc -o - %s | FileCheck -check-prefix CHECK-NOEXCP %s // CHECK: [[A:.*]] = type { i64, [10 x i8*] } // CHECK: %[[STRUCT_TEST1_S0:.*]] = type { i32 } @@ -55,10 +56,16 @@ // Check that copy/dispose helper functions are exception safe. -// CHECK-LABEL: define internal void @__copy_helper_block_( +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ea8_s_32_b8_40_w_48_c2S0_56_c2S0_60( // CHECK: %[[BLOCK_SOURCE:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* // CHECK: %[[BLOCK_DEST:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* +// CHECK: %[[V9:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 5 +// CHECK: %[[V10:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 5 +// CHECK: %[[BLOCKCOPY_SRC2:.*]] = load i8*, i8** %[[V9]], align 8 +// CHECK: store i8* null, i8** %[[V10]], align 8 +// CHECK: call void @objc_storeStrong(i8** %[[V10]], i8* %[[BLOCKCOPY_SRC2]]) + // CHECK: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 6 // CHECK: %[[V5:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 6 // CHECK: %[[BLOCKCOPY_SRC:.*]] = load i8*, i8** %[[V4]], align 8 @@ -69,12 +76,6 @@ // CHECK: %[[V8:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 7 // CHECK: call void @objc_copyWeak(i8** %[[V8]], i8** %[[V7]]) -// CHECK: %[[V9:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 5 -// CHECK: %[[V10:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 5 -// CHECK: %[[BLOCKCOPY_SRC2:.*]] = load i8*, i8** %[[V9]], align 8 -// CHECK: store i8* null, i8** %[[V10]], align 8 -// CHECK: call void @objc_storeStrong(i8** %[[V10]], i8* %[[BLOCKCOPY_SRC2]]) - // CHECK: %[[V11:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 8 // CHECK: %[[V12:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 8 // CHECK: invoke void @_ZN5test12S0C1ERKS0_(%[[STRUCT_TEST1_S0]]* %[[V12]], %[[STRUCT_TEST1_S0]]* dereferenceable(4) %[[V11]]) @@ -100,26 +101,27 @@ // CHECK: br label %[[EHCLEANUP]] // CHECK: [[EHCLEANUP]]: -// CHECK: call void @objc_storeStrong(i8** %[[V10]], i8* null) // CHECK: call void @objc_destroyWeak(i8** %[[V8]]) // CHECK: %[[V21:.*]] = load i8*, i8** %[[V5]], align 8 // CHECK: call void @_Block_object_dispose(i8* %[[V21]], i32 8) +// CHECK: call void @objc_storeStrong(i8** %[[V10]], i8* null) // CHECK: br label %[[EH_RESUME:.*]] // CHECK: [[EH_RESUME]]: // CHECK: resume { i8*, i32 } // CHECK: [[TERMINATE_LPAD]]: // CHECK: call void @__clang_call_terminate( -// CHECK-O1-LABEL: define internal void @__copy_helper_block_( +// CHECK-O1-LABEL: define linkonce_odr hidden void @__copy_helper_block_ea8_s_32_b8_40_w_48_c2S0_56_c2S0_60( // CHECK-O1: tail call void @objc_release({{.*}}) {{.*}} !clang.imprecise_release +// CHECK-NOEXCP: define linkonce_odr hidden void @__copy_helper_block_8_s_32_b8_40_w_48_c2S0_56_c2S0_60( -// CHECK: define internal void @__destroy_helper_block_( +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_ea8_s_32_b8_40_w_48_c2S0_56_c2S0_60( // CHECK: %[[BLOCK:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* +// CHECK: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 5 // CHECK: %[[V2:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 6 // CHECK: %[[V3:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 7 -// CHECK: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 5 // CHECK: %[[V5:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 8 // CHECK: %[[V6:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 9 // CHECK: invoke void @_ZN5test12S0D1Ev(%[[STRUCT_TEST1_S0]]* %[[V6]]) @@ -130,10 +132,10 @@ // CHECK: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD1:.*]] // CHECK: [[INVOKE_CONT2]]: -// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* null) // CHECK: call void @objc_destroyWeak(i8** %[[V3]]) // CHECK: %[[V7:.*]] = load i8*, i8** %[[V2]], align 8 // CHECK: call void @_Block_object_dispose(i8* %[[V7]], i32 8) +// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* null) // CHECK: ret void // CHECK: [[LPAD]]: @@ -147,21 +149,22 @@ // CHECK: br label %[[EHCLEANUP]] // CHECK: [[EHCLEANUP]]: -// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* null) // CHECK: call void @objc_destroyWeak(i8** %[[V3]]) // CHECK: %[[V14:.*]] = load i8*, i8** %[[V2]], align 8 // CHECK: call void @_Block_object_dispose(i8* %[[V14]], i32 8) +// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* null) // CHECK: br label %[[EH_RESUME]] // CHECK: [[EH_RESUME]]: // CHECK: resume { i8*, i32 } // CHECK: [[TERMINATE_LPAD]]: // CHECK: call void @__clang_call_terminate( -// CHECK-O1-LABEL: define internal void @__destroy_helper_block_( +// CHECK-O1-LABEL: define linkonce_odr hidden void @__destroy_helper_block_ea8_s_32_b8_40_w_48_c2S0_56_c2S0_60( // CHECK-O1: tail call void @objc_release({{.*}}) {{.*}} !clang.imprecise_release // CHECK-O1: tail call void @objc_release({{.*}}) {{.*}} !clang.imprecise_release +// CHECK-NOEXCP: define linkonce_odr hidden void @__destroy_helper_block_8_s_32_b8_40_w_48_c2S0_56_c2S0_60( struct S0 { S0(); Index: test/CodeGenObjC/strong-in-c-struct.m =================================================================== --- test/CodeGenObjC/strong-in-c-struct.m +++ test/CodeGenObjC/strong-in-c-struct.m @@ -419,11 +419,11 @@ // CHECK: call void @__destructor_8_s16( // CHECK: ret void -// CHECK: define internal void @__copy_helper_block_.1(i8*, i8*) +// CHECK: define linkonce_odr hidden void @__copy_helper_block_8_n13_8_8_t0w16_s16_32(i8*, i8*) // CHECK: call void @__copy_constructor_8_8_t0w16_s16( // CHECK: ret void -// CHECK: define internal void @__destroy_helper_block_.2( +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_8_n5_8_s16_32( // CHECK: call void @__destructor_8_s16( // CHECK: ret void Index: test/CodeGenObjC/mrc-weak.m =================================================================== --- test/CodeGenObjC/mrc-weak.m +++ test/CodeGenObjC/mrc-weak.m @@ -139,10 +139,10 @@ // CHECK: call void @use_block // CHECK: call void @objc_destroyWeak -// CHECK-LABEL: define internal void @__copy_helper_block +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block // CHECK: @objc_copyWeak -// CHECK-LABEL: define internal void @__destroy_helper_block +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block // CHECK: @objc_destroyWeak void test8(void) { @@ -162,26 +162,26 @@ // CHECK: call void @objc_destroyWeak // CHECK-LABEL: define void @test9_baseline() -// CHECK: define internal void @__copy_helper -// CHECK: define internal void @__destroy_helper +// CHECK: define linkonce_odr hidden void @__copy_helper +// CHECK: define linkonce_odr hidden void @__destroy_helper void test9_baseline(void) { Foo *p = get_object(); use_block(^{ [p run]; }); } // CHECK-LABEL: define void @test9() -// CHECK-NOT: define internal void @__copy_helper -// CHECK-NOT: define internal void @__destroy_helper +// CHECK-NOT: define linkonce_odr hidden void @__copy_helper +// CHECK-NOT: define linkonce_odr hidden void @__destroy_helper // CHECK: define void @test9_fin() void test9(void) { __unsafe_unretained Foo *p = get_object(); use_block(^{ [p run]; }); } void test9_fin() {} // CHECK-LABEL: define void @test10() -// CHECK-NOT: define internal void @__copy_helper -// CHECK-NOT: define internal void @__destroy_helper +// CHECK-NOT: define linkonce_odr hidden void @__copy_helper +// CHECK-NOT: define linkonce_odr hidden void @__destroy_helper // CHECK: define void @test10_fin() void test10(void) { typedef __unsafe_unretained Foo *UnsafeFooPtr; Index: test/CodeGenObjC/debug-info-blocks.m =================================================================== --- test/CodeGenObjC/debug-info-blocks.m +++ test/CodeGenObjC/debug-info-blocks.m @@ -59,9 +59,9 @@ if ((self = [super init])) { // CHECK-DAG: [[DBG_LINE]] = !DILocation(line: 0, scope: ![[COPY_SP:[0-9]+]]) // CHECK-DAG: [[COPY_LINE]] = !DILocation(line: [[@LINE+7]], scope: ![[COPY_SP:[0-9]+]]) - // CHECK-DAG: [[COPY_SP]] = distinct !DISubprogram(name: "__copy_helper_block_" + // CHECK-DAG: [[COPY_SP]] = distinct !DISubprogram(name: "__copy_helper_block_8_b3_32" // CHECK-DAG: [[DESTROY_LINE]] = !DILocation(line: [[@LINE+5]], scope: ![[DESTROY_SP:[0-9]+]]) - // CHECK-DAG: [[DESTROY_SP]] = distinct !DISubprogram(name: "__destroy_helper_block_" + // CHECK-DAG: [[DESTROY_SP]] = distinct !DISubprogram(name: "__destroy_helper_block_8_b3_32" // CHECK-DAG: !DILocalVariable(arg: 1, scope: ![[COPY_SP]], {{.*}}, flags: DIFlagArtificial) // CHECK-DAG: !DILocalVariable(arg: 2, scope: ![[COPY_SP]], {{.*}}, flags: DIFlagArtificial) // CHECK-DAG: !DILocalVariable(arg: 1, scope: ![[DESTROY_SP]], {{.*}}, flags: DIFlagArtificial) Index: test/CodeGenObjC/debug-info-block-helper.m =================================================================== --- test/CodeGenObjC/debug-info-block-helper.m +++ test/CodeGenObjC/debug-info-block-helper.m @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -emit-llvm -fblocks -debug-info-kind=limited -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 %s -o - | FileCheck %s extern void foo(void(^)(void)); -// CHECK: !DISubprogram(name: "__destroy_helper_block_" +// CHECK: !DISubprogram(name: "__destroy_helper_block_8_b3_32_b8_40_b8_48" @interface NSObject { struct objc_object *isa; Index: test/CodeGenObjC/arc-blocks.m =================================================================== --- test/CodeGenObjC/arc-blocks.m +++ test/CodeGenObjC/arc-blocks.m @@ -1,6 +1,17 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-UNOPT %s +// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } +// CHECK-UNOPT: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } +// CHECK: @[[BLOCK_DESCRIPTOR_TMP9:.*]] = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_b8_32, void (i8*)* @__destroy_helper_block_8_b8_32, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 16 }, align 8 +// CHECK-UNOPT: @{{.*}} = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_s_32, void (i8*)* @__destroy_helper_block_8_s_32, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK-UNOPT: @{{.*}} = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_s_32, void (i8*)* @__destroy_helper_block_8_s_32, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK-UNOPT: @{{.*}} = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_s_32, void (i8*)* @__destroy_helper_block_8_s_32, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK-UNOPT: @{{.*}} = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_s_32, void (i8*)* @__destroy_helper_block_8_s_32, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK-UNOPT: @[[BLOCK_DESCRIPTOR_TMP44:.*]] = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_s_32, void (i8*)* @__destroy_helper_block_8_s_32, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK: @[[BLOCK_DESCRIPTOR_TMP46:.*]] = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i8* } { i64 0, i64 48, void (i8*, i8*)* @__copy_helper_block_8_s_32, void (i8*)* @__destroy_helper_block_8_s_32, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @{{.*}}, i32 0, i32 0) }, align 8 +// CHECK: @[[BLOCK_DESCRIPTOR_TMP48:.*]] = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_b7_32, void (i8*)* @__destroy_helper_block_8_s_32, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.47, i32 0, i32 0), i64 256 }, align 8 + // This shouldn't crash. void test0(id (^maker)(void)) { maker(); @@ -43,7 +54,7 @@ extern void test2_helper(id (^)(void)); test2_helper(^{ return x; }); -// CHECK-LABEL: define internal void @__copy_helper_block_(i8*, i8*) #{{[0-9]+}} { +// CHECK: define linkonce_odr hidden void @__copy_helper_block_8_s_32(i8*, i8*) unnamed_addr #{{[0-9]+}} { // CHECK: [[T0:%.*]] = load i8*, i8** // CHECK-NEXT: [[SRC:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* // CHECK-NEXT: [[T0:%.*]] = load i8*, i8** @@ -53,7 +64,8 @@ // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) [[NUW]] // CHECK-NEXT: ret void -// CHECK-LABEL: define internal void @__destroy_helper_block_(i8*) #{{[0-9]+}} { + +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_8_s_32(i8*) unnamed_addr #{{[0-9]+}} { // CHECK: [[T0:%.*]] = load i8*, i8** // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[T1]], i32 0, i32 5 @@ -155,10 +167,10 @@ // CHECK-NEXT: call void @objc_release(i8* [[T0]]) // CHECK-NEXT: ret void - // CHECK-LABEL: define internal void @__copy_helper_block_.{{[0-9]+}}(i8*, i8*) #{{[0-9]+}} { + // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_8_b8_32(i8*, i8*) unnamed_addr #{{[0-9]+}} { // CHECK: call void @_Block_object_assign(i8* {{%.*}}, i8* {{%.*}}, i32 8) - // CHECK-LABEL: define internal void @__destroy_helper_block_.{{[0-9]+}}(i8*) #{{[0-9]+}} { + // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_b8_32(i8*) unnamed_addr #{{[0-9]+}} { // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) } @@ -211,6 +223,8 @@ // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[VAR]], i32 0, i32 6 // 0x42800000 - has signature, copy/dispose helpers, as well as BLOCK_HAS_EXTENDED_LAYOUT // CHECK: store i32 -1040187392, + // CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %{{.*}}, i32 0, i32 4 +// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 }* @[[BLOCK_DESCRIPTOR_TMP9]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* // CHECK-NEXT: store i8* [[T0]], i8** // CHECK: call void @test6_helper( @@ -236,14 +250,6 @@ // CHECK: [[SLOT:%.*]] = getelementptr inbounds {{.*}}, i32 0, i32 6 // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[SLOT]], i8* null) // CHECK-NEXT: ret void - - // CHECK-LABEL: define internal void @__copy_helper_block_.{{[0-9]+}}(i8*, i8*) #{{[0-9]+}} { - // 0x8 - FIELD_IS_BYREF (no FIELD_IS_WEAK because clang in control) - // CHECK: call void @_Block_object_assign(i8* {{%.*}}, i8* {{%.*}}, i32 8) - - // CHECK-LABEL: define internal void @__destroy_helper_block_.{{[0-9]+}}(i8*) #{{[0-9]+}} { - // 0x8 - FIELD_IS_BYREF (no FIELD_IS_WEAK because clang in control) - // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) } void test7(void) { @@ -276,12 +282,12 @@ // CHECK-NEXT: call void @objc_release(i8* [[T0]]) // CHECK: ret void - // CHECK-LABEL: define internal void @__copy_helper_block_.{{[0-9]+}}(i8*, i8*) #{{[0-9]+}} { + // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_8_w_32(i8*, i8*) unnamed_addr #{{[0-9]+}} { // CHECK: getelementptr // CHECK-NEXT: getelementptr // CHECK-NEXT: call void @objc_copyWeak( - // CHECK-LABEL: define internal void @__destroy_helper_block_.{{[0-9]+}}(i8*) #{{[0-9]+}} { + // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_w_32(i8*) unnamed_addr #{{[0-9]+}} { // CHECK: getelementptr // CHECK-NEXT: call void @objc_destroyWeak( } @@ -631,6 +637,8 @@ // CHECK-UNOPT-NEXT: store i8* null, i8** [[X]] // CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[X]], // CHECK-UNOPT-NEXT: [[SLOTREL:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-UNOPT: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 4 +// CHECK-UNOPT: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 }* @[[BLOCK_DESCRIPTOR_TMP44]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 // CHECK-UNOPT: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 // CHECK-UNOPT-NEXT: [[T0:%.*]] = load i8*, i8** [[X]], // CHECK-UNOPT-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) @@ -642,25 +650,6 @@ // CHECK-UNOPT-NEXT: ret void extern void test18_helper(id (^)(void)); test18_helper(^{ return x; }); - -// CHECK-UNOPT-LABEL: define internal void @__copy_helper_block_.{{[0-9]+}}(i8*, i8*) #{{[0-9]+}} { -// CHECK-UNOPT: [[T0:%.*]] = load i8*, i8** -// CHECK-UNOPT-NEXT: [[SRC:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* -// CHECK-UNOPT-NEXT: [[T0:%.*]] = load i8*, i8** -// CHECK-UNOPT-NEXT: [[DST:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* -// CHECK-UNOPT-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[SRC]], i32 0, i32 5 -// CHECK-UNOPT-NEXT: [[T1:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[DST]], i32 0, i32 5 -// CHECK-UNOPT-NEXT: [[T2:%.*]] = load i8*, i8** [[T0]] -// CHECK-UNOPT-NEXT: store i8* null, i8** [[T1]] -// CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[T1]], i8* [[T2]]) [[NUW]] -// CHECK-UNOPT-NEXT: ret void - -// CHECK-UNOPT-LABEL: define internal void @__destroy_helper_block_.{{[0-9]+}}(i8*) #{{[0-9]+}} { -// CHECK-UNOPT: [[T0:%.*]] = load i8*, i8** -// CHECK-UNOPT-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* -// CHECK-UNOPT-NEXT: [[T2:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[T1]], i32 0, i32 5 -// CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null) -// CHECK-UNOPT-NEXT: ret void } // Ensure that we don't emit helper code in copy/dispose routines for variables @@ -670,33 +659,12 @@ (^ { testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers(x, unsafeObject); })(); } -// CHECK-LABEL: testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers_block_invoke -// CHECK-UNOPT-LABEL: testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers_block_invoke +// CHECK-LABEL: define void @testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers +// %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>* %{{.*}}, i32 0, i32 4 +// store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i8* }* @[[BLOCK_DESCRIPTOR_TMP46]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 -// CHECK-UNOPT: @__copy_helper_block -// CHECK-UNOPT: alloca -// CHECK-UNOPT-NEXT: alloca -// CHECK-UNOPT-NEXT: store -// CHECK-UNOPT-NEXT: store -// CHECK-UNOPT-NEXT: load -// CHECK-UNOPT-NEXT: bitcast -// CHECK-UNOPT-NEXT: load -// CHECK-UNOPT-NEXT: bitcast -// CHECK-UNOPT-NEXT: getelementptr -// CHECK-UNOPT-NEXT: getelementptr -// CHECK-UNOPT-NEXT: load -// CHECK-UNOPT-NEXT: store -// CHECK-UNOPT-NEXT: call void @objc_storeStrong -// CHECK-UNOPT-NEXT: ret - -// CHECK-UNOPT: @__destroy_helper_block -// CHECK-UNOPT: alloca -// CHECK-UNOPT-NEXT: store -// CHECK-UNOPT-NEXT: load -// CHECK-UNOPT-NEXT: bitcast -// CHECK-UNOPT-NEXT: getelementptr -// CHECK-UNOPT-NEXT: call void @objc_storeStrong -// CHECK-UNOPT-NEXT: ret +// CHECK-LABEL: define internal void @__testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers_block_invoke +// CHECK-UNOPT-LABEL: define internal void @__testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers_block_invoke // rdar://13588325 void test19_sink(void (^)(int)); @@ -712,6 +680,8 @@ // Block setup. We skip most of this. Note the bare retain. // CHECK-NEXT: [[SLOTREL:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 4 +// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 }* @[[BLOCK_DESCRIPTOR_TMP48]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 // CHECK-NEXT: [[T0:%.*]] = load void ()*, void ()** [[B]], // CHECK-NEXT: [[T1:%.*]] = bitcast void ()* [[T0]] to i8* @@ -762,12 +732,6 @@ // CHECK-UNOPT: store i8* [[RETAINED]], i8** [[BLOCKCAPTURED]] // CHECK-UNOPT: call void @objc_storeStrong(i8** [[CAPTUREFIELD]], i8* null) -// 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; }); Index: test/CodeGenCXX/cxx-block-objects.cpp =================================================================== --- test/CodeGenCXX/cxx-block-objects.cpp +++ test/CodeGenCXX/cxx-block-objects.cpp @@ -25,9 +25,9 @@ return 0; } -// CHECK-LABEL: define internal void @__copy_helper_block_ +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ // CHECK: call void @_ZN1AC1ERKS_ -// CHECK-LABEL:define internal void @__destroy_helper_block_ +// CHECK-LABEL:define linkonce_odr hidden void @__destroy_helper_block_ // CHECK: call void @_ZN1AD1Ev Index: test/CodeGenCXX/blocks.cpp =================================================================== --- test/CodeGenCXX/blocks.cpp +++ test/CodeGenCXX/blocks.cpp @@ -252,3 +252,26 @@ }); } } + +namespace test10 { + // Check that 'v' is included in the copy helper function name to indicate + // the constructor taking a volatile parameter is called to copy the captured + // object. + + // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_8_cv1S_32( + // CHECK: call void @_ZN6test101SC1ERVKS0_( + // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_c1S_32( + + struct S { + int a; + S(); + S(const S &); + S(const volatile S &); + ~S(); + }; + + void test() { + volatile S x; + ^{ (void)x; }; + } +} Index: test/CodeGenCXX/block-byref-cxx-objc.cpp =================================================================== --- test/CodeGenCXX/block-byref-cxx-objc.cpp +++ test/CodeGenCXX/block-byref-cxx-objc.cpp @@ -1,28 +1,52 @@ -// RUN: %clang_cc1 %s -emit-llvm -triple %itanium_abi_triple -o - -fblocks | FileCheck %s +// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -triple %itanium_abi_triple -o - -fblocks -fexceptions | FileCheck %s // rdar://8594790 struct A { int x; A(const A &); A(); - ~A(); + ~A() noexcept(false); }; -int main() -{ - __block A BYREF_VAR; - ^{ BYREF_VAR.x = 1234; }; +struct B { + int x; + B(const B &); + B(); + ~B(); +}; + +int testA() { + __block A a0, a1; + ^{ a0.x = 1234; a1.x = 5678; }; return 0; } // CHECK-LABEL: define internal void @__Block_byref_object_copy_ // CHECK: call {{.*}} @_ZN1AC1ERKS_ // CHECK-LABEL: define internal void @__Block_byref_object_dispose_ // CHECK: call {{.*}} @_ZN1AD1Ev -// CHECK-LABEL: define internal void @__copy_helper_block_ -// CHECK: call {{.*}}void @_Block_object_assign -// CHECK-LABEL: define internal void @__destroy_helper_block_ -// CHECK: call {{.*}}void @_Block_object_dispose + +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_e8_b8t_32_b8t_40( +// CHECK: call void @_Block_object_assign( +// CHECK: invoke void @_Block_object_assign( + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_e8_b8_32_b8_40( +// CHECK: call {{.*}}void @_Block_object_dispose( +// CHECK: call void @_Block_object_dispose( + +int testB() { + __block B b0, b1; + ^{ b0.x = 1234; b1.x = 5678; }; + return 0; +} + +// CHECK-LABEL: define internal void @__Block_byref_object_copy_ +// CHECK: call {{.*}} @_ZN1BC1ERKS_ +// CHECK-LABEL: define internal void @__Block_byref_object_dispose_ +// CHECK: call {{.*}} @_ZN1BD1Ev + +// CHECK-NOT: define {{.*}} void @__copy_helper_block_ +// CHECK-NOT: define {{.*}} void @__destroy_helper_block_ // rdar://problem/11135650 namespace test1 { Index: test/CodeGen/sanitize-thread-no-checking-at-run-time.m =================================================================== --- test/CodeGen/sanitize-thread-no-checking-at-run-time.m +++ test/CodeGen/sanitize-thread-no-checking-at-run-time.m @@ -35,7 +35,7 @@ void test2(id x) { extern void test2_helper(id (^)(void)); test2_helper(^{ return x; }); -// TSAN: define internal void @__destroy_helper_block_(i8*) [[ATTR:#[0-9]+]] +// TSAN: define linkonce_odr hidden void @__destroy_helper_block_8_b3_32(i8*) unnamed_addr [[ATTR:#[0-9]+]] } // TSAN: attributes [[ATTR]] = { noinline nounwind {{.*}} "sanitize_thread_no_checking_at_run_time" {{.*}} } Index: test/CodeGen/blocks.c =================================================================== --- test/CodeGen/blocks.c +++ test/CodeGen/blocks.c @@ -1,4 +1,9 @@ // RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - -fblocks | FileCheck %s + +// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i32, i32 } + +// CHECK: @[[BLOCK_DESCRIPTOR_TMP21:.*]] = internal constant { i32, i32, void (i8*, i8*)*, void (i8*)*, i8*, i8* } { i32 0, i32 24, void (i8*, i8*)* @__copy_helper_block_4_b8_20, void (i8*)* @__destroy_helper_block_4_b8_20, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str, i32 0, i32 0), i8* null }, align 4 + void (^f)(void) = ^{}; // rdar://6768379 @@ -27,6 +32,32 @@ ^ { i = 1; }(); }; +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_4_b8_20(i8*, i8*) unnamed_addr +// CHECK: %[[_ADDR:.*]] = alloca i8*, align 4 +// CHECK-NEXT: %[[_ADDR1:.*]] = alloca i8*, align 4 +// CHECK-NEXT: store i8* %0, i8** %[[_ADDR]], align 4 +// CHECK-NEXT: store i8* %1, i8** %[[_ADDR1]], align 4 +// CHECK-NEXT: %[[V2:.*]] = load i8*, i8** %[[_ADDR1]], align 4 +// CHECK-NEXT: %[[BLOCK_SOURCE:.*]] = bitcast i8* %[[V2]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* +// CHECK-NEXT: %[[V3:.*]] = load i8*, i8** %[[_ADDR]], align 4 +// CHECK-NEXT: %[[BLOCK_DEST:.*]] = bitcast i8* %[[V3]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* +// CHECK-NEXT: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK_SOURCE]], i32 0, i32 5 +// CHECK-NEXT: %[[V5:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK_DEST]], i32 0, i32 5 +// CHECK-NEXT: %[[BLOCKCOPY_SRC:.*]] = load i8*, i8** %[[V4]], align 4 +// CHECK-NEXT: %[[V6:.*]] = bitcast i8** %[[V5]] to i8* +// CHECK-NEXT: call void @_Block_object_assign(i8* %[[V6]], i8* %[[BLOCKCOPY_SRC]], i32 8) +// CHECK-NEXT: ret void + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_4_b8_20(i8*) unnamed_addr +// CHECK: %[[_ADDR:.*]] = alloca i8*, align 4 +// CHECK-NEXT: store i8* %0, i8** %[[_ADDR]], align 4 +// CHECK-NEXT: %[[V1:.*]] = load i8*, i8** %[[_ADDR]], align 4 +// CHECK-NEXT: %[[BLOCK:.*]] = bitcast i8* %[[V1]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* +// CHECK-NEXT: %[[V2:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK]], i32 0, i32 5 +// CHECK-NEXT: %[[V3:.*]] = load i8*, i8** %[[V2]], align 4 +// CHECK-NEXT: call void @_Block_object_dispose(i8* %[[V3]], i32 8) +// CHECK-NEXT: ret void + typedef double ftype(double); // It's not clear that we *should* support this syntax, but until that decision // is made, we should support it properly and not crash. @@ -85,30 +116,8 @@ __block int i; (^ { i = x; })(); } -// CHECK-LABEL: testConstCaptureInCopyAndDestroyHelpers_block_invoke - -// CHECK: @__copy_helper_block -// CHECK: alloca -// CHECK-NEXT: alloca -// CHECK-NEXT: store -// CHECK-NEXT: store -// CHECK-NEXT: load -// CHECK-NEXT: bitcast -// CHECK-NEXT: load -// CHECK-NEXT: bitcast -// CHECK-NEXT: getelementptr -// CHECK-NEXT: getelementptr -// CHECK-NEXT: load -// CHECK-NEXT: bitcast -// CHECK-NEXT: call void @_Block_object_assign -// CHECK-NEXT: ret - -// CHECK: @__destroy_helper_block -// CHECK: alloca -// CHECK-NEXT: store -// CHECK-NEXT: load -// CHECK-NEXT: bitcast -// CHECK-NEXT: getelementptr -// CHECK-NEXT: load -// CHECK-NEXT: call void @_Block_object_dispose -// CHECK-NEXT: ret +// CHECK-LABEL: define void @testConstCaptureInCopyAndDestroyHelpers( +// CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %{{.*}}, i32 0, i32 4 +// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i32, i32, void (i8*, i8*)*, void (i8*)*, i8*, i8* }* @[[BLOCK_DESCRIPTOR_TMP21]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 4 + +// CHECK-LABEL: define internal void @__testConstCaptureInCopyAndDestroyHelpers_block_invoke Index: test/CodeGen/blocks-1.c =================================================================== --- test/CodeGen/blocks-1.c +++ test/CodeGen/blocks-1.c @@ -1,11 +1,11 @@ // RUN: %clang_cc1 %s -emit-llvm -o %t -fblocks -// RUN: grep "_Block_object_dispose" %t | count 17 -// RUN: grep "__copy_helper_block_" %t | count 14 -// RUN: grep "__destroy_helper_block_" %t | count 14 +// RUN: grep "_Block_object_dispose" %t | count 12 +// RUN: grep "__copy_helper_block_" %t | count 9 +// RUN: grep "__destroy_helper_block_" %t | count 9 // RUN: grep "__Block_byref_object_copy_" %t | count 2 // RUN: grep "__Block_byref_object_dispose_" %t | count 2 // RUN: grep "i32 135)" %t | count 2 -// RUN: grep "_Block_object_assign" %t | count 10 +// RUN: grep "_Block_object_assign" %t | count 5 int printf(const char *, ...); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1799,6 +1799,11 @@ void GenerateCode(GlobalDecl GD, llvm::Function *Fn, const CGFunctionInfo &FnInfo); + + /// Annotate the function with an attribute that disables TSan checking at + /// runtime. + void markAsIgnoreThreadCheckingAtRuntime(llvm::Function *Fn); + /// Emit code for the start of a function. /// \param Loc The location to be associated with the function. /// \param StartLoc The location of the function body. @@ -3602,6 +3607,19 @@ CXXDtorType Type, const CXXRecordDecl *RD); + // Return the copy constructor name with the prefix "__copy_constructor_" + // removed. + static std::string getNonTrivialCopyConstructorStr(QualType QT, + CharUnits Alignment, + bool IsVolatile, + ASTContext &Ctx); + + // Return the destructor name with the prefix "__destructor_" removed. + static std::string getNonTrivialDestructorStr(QualType QT, + CharUnits Alignment, + bool IsVolatile, + ASTContext &Ctx); + // These functions emit calls to the special functions of non-trivial C // structs. void defaultInitNonTrivialCStructVar(LValue Dst); Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -772,9 +772,11 @@ return false; } -static void markAsIgnoreThreadCheckingAtRuntime(llvm::Function *Fn) { - Fn->addFnAttr("sanitize_thread_no_checking_at_run_time"); - Fn->removeFnAttr(llvm::Attribute::SanitizeThread); +void CodeGenFunction::markAsIgnoreThreadCheckingAtRuntime(llvm::Function *Fn) { + if (SanOpts.has(SanitizerKind::Thread)) { + Fn->addFnAttr("sanitize_thread_no_checking_at_run_time"); + Fn->removeFnAttr(llvm::Attribute::SanitizeThread); + } } static bool matchesStlAllocatorFn(const Decl *D, const ASTContext &Ctx) { @@ -887,10 +889,6 @@ (OMD->getSelector().isUnarySelector() && II->isStr(".cxx_destruct"))) { markAsIgnoreThreadCheckingAtRuntime(Fn); } - } else if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) { - IdentifierInfo *II = FD->getIdentifier(); - if (II && II->isStr("__destroy_helper_block_")) - markAsIgnoreThreadCheckingAtRuntime(Fn); } } Index: lib/CodeGen/CGNonTrivialStruct.cpp =================================================================== --- lib/CodeGen/CGNonTrivialStruct.cpp +++ lib/CodeGen/CGNonTrivialStruct.cpp @@ -283,8 +283,9 @@ struct GenDestructorFuncName : GenUnaryFuncName<GenDestructorFuncName>, DestructedTypeVisitor<GenDestructorFuncName> { using Super = DestructedTypeVisitor<GenDestructorFuncName>; - GenDestructorFuncName(CharUnits DstAlignment, ASTContext &Ctx) - : GenUnaryFuncName<GenDestructorFuncName>("__destructor_", DstAlignment, + GenDestructorFuncName(const char *Prefix, CharUnits DstAlignment, + ASTContext &Ctx) + : GenUnaryFuncName<GenDestructorFuncName>(Prefix, DstAlignment, Ctx) {} void visitWithKind(QualType::DestructionKind DK, QualType FT, const FieldDecl *FD, CharUnits CurStructOffset) { @@ -824,11 +825,28 @@ IsVolatile, *this, std::array<Address, 1>({{DstPtr}})); } +std::string +CodeGenFunction::getNonTrivialCopyConstructorStr(QualType QT, + CharUnits Alignment, + bool IsVolatile, + ASTContext &Ctx) { + GenBinaryFuncName<false> GenName("", Alignment, Alignment, Ctx); + return GenName.getName(QT, IsVolatile); +} + +std::string +CodeGenFunction::getNonTrivialDestructorStr(QualType QT, CharUnits Alignment, + bool IsVolatile, ASTContext &Ctx) { + GenDestructorFuncName GenName("", Alignment, Ctx); + return GenName.getName(QT, IsVolatile); +} + void CodeGenFunction::callCStructDestructor(LValue Dst) { bool IsVolatile = Dst.isVolatile(); Address DstPtr = Dst.getAddress(); QualType QT = Dst.getType(); - GenDestructorFuncName GenName(DstPtr.getAlignment(), getContext()); + GenDestructorFuncName GenName("__destructor_", DstPtr.getAlignment(), + getContext()); std::string FuncName = GenName.getName(QT, IsVolatile); callSpecialFunction(GenDestructor(getContext()), FuncName, QT, IsVolatile, *this, std::array<Address, 1>({{DstPtr}})); Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ lib/CodeGen/CGBlocks.cpp @@ -25,6 +25,7 @@ #include "llvm/IR/CallSite.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Module.h" +#include "llvm/Support/ScopedPrinter.h" #include <algorithm> #include <cstdio> @@ -1498,13 +1499,17 @@ struct BlockCaptureManagedEntity { BlockCaptureEntityKind Kind; BlockFieldFlags Flags; - const BlockDecl::Capture &CI; - const CGBlockInfo::Capture &Capture; + const BlockDecl::Capture *CI; + const CGBlockInfo::Capture *Capture; BlockCaptureManagedEntity(BlockCaptureEntityKind Type, BlockFieldFlags Flags, const BlockDecl::Capture &CI, const CGBlockInfo::Capture &Capture) - : Kind(Type), Flags(Flags), CI(CI), Capture(Capture) {} + : Kind(Type), Flags(Flags), CI(&CI), Capture(&Capture) {} + + bool operator<(const BlockCaptureManagedEntity &Other) const { + return Capture->getOffset() < Other.Capture->getOffset(); + } }; } // end anonymous namespace @@ -1583,6 +1588,9 @@ if (Info.first != BlockCaptureEntityKind::None) ManagedCaptures.emplace_back(Info.first, Info.second, CI, Capture); } + + // Sort the captures by offset. + std::sort(ManagedCaptures.begin(), ManagedCaptures.end()); } namespace { @@ -1609,6 +1617,84 @@ }; } // end anonymous namespace +static std::string getCopyDestroyHelperFuncName( + const SmallVectorImpl<BlockCaptureManagedEntity> &Captures, + CharUnits BlockAlignment, bool IsCopyHelper, CodeGenModule &CGM) { + std::string Name = + IsCopyHelper ? "__copy_helper_block_" : "__destroy_helper_block_"; + if (CGM.getLangOpts().Exceptions) + Name += "e"; + if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions) + Name += "a"; + Name += llvm::to_string(BlockAlignment.getQuantity()); + + for (const BlockCaptureManagedEntity &E : Captures) { + const BlockDecl::Capture &CI = *E.CI; + BlockFieldFlags Flags = E.Flags; + QualType CaptureTy = CI.getVariable()->getType(); + Name += "_"; + + switch (E.Kind) { + case BlockCaptureEntityKind::CXXRecord: { + Name += "c"; + // Check if the copy constructor that will be called to copy the captured + // object takes a volatile parameter. + if (IsCopyHelper) { + Expr *CE = CI.getCopyExpr(); + if (auto *EC = dyn_cast<ExprWithCleanups>(CE)) + CE = EC->getSubExpr(); + unsigned Qs; + cast<CXXConstructExpr>(CE)->getConstructor()->isCopyConstructor(Qs); + if (Qs & Qualifiers::Volatile) + Name += "v"; + } + std::string Str = CaptureTy->getAsCXXRecordDecl()->getName(); + Name += llvm::to_string(Str.size()) + Str; + break; + } + case BlockCaptureEntityKind::ARCWeak: + Name += "w"; + break; + case BlockCaptureEntityKind::ARCStrong: + Name += "s"; + break; + case BlockCaptureEntityKind::BlockObject: { + Name += "b" + llvm::to_string(Flags.getBitMask()); + const VarDecl *Var = CI.getVariable(); + // Check if copy can throw. + if (IsCopyHelper && CI.isByRef() && + Var->getType()->getAsCXXRecordDecl() && + CGM.getContext().getBlockVarCopyInits(Var)) + Name += "t"; + break; + } + case BlockCaptureEntityKind::NonTrivialCStruct: { + bool IsVolatile = CaptureTy.isVolatileQualified(); + CharUnits Alignment = + BlockAlignment.alignmentAtOffset(E.Capture->getOffset()); + ASTContext &Ctx = CGM.getContext(); + + Name += "n"; + std::string Str; + if (IsCopyHelper) + Str = CodeGenFunction::getNonTrivialCopyConstructorStr( + CaptureTy, Alignment, IsVolatile, Ctx); + else + Str = CodeGenFunction::getNonTrivialDestructorStr( + CaptureTy, Alignment, IsVolatile, Ctx); + Name += llvm::to_string(Str.size()) + "_" + Str; + break; + } + case BlockCaptureEntityKind::None: + llvm_unreachable("unexpected block capture kind"); + } + + Name += "_" + llvm::to_string(E.Capture->getOffset().getQuantity()); + } + + return Name; +} + static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, Address Field, QualType CaptureType, BlockFieldFlags Flags, bool EHOnly, @@ -1653,6 +1739,16 @@ /// the contents of an individual __block variable to the heap. llvm::Constant * CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { + SmallVector<BlockCaptureManagedEntity, 4> CopiedCaptures; + findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures, + computeCopyInfoForBlockCapture); + std::string FuncName = + getCopyDestroyHelperFuncName(CopiedCaptures, blockInfo.BlockAlign, + /*IsCopyHelper*/ true, CGM); + + if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName)) + return Func; + ASTContext &C = getContext(); FunctionArgList args; @@ -1671,11 +1767,11 @@ llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI); llvm::Function *Fn = - llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, - "__copy_helper_block_", &CGM.getModule()); + llvm::Function::Create(LTy, llvm::GlobalValue::LinkOnceODRLinkage, + FuncName, &CGM.getModule()); IdentifierInfo *II - = &CGM.getContext().Idents.get("__copy_helper_block_"); + = &CGM.getContext().Idents.get(FuncName); FunctionDecl *FD = FunctionDecl::Create(C, C.getTranslationUnitDecl(), @@ -1685,7 +1781,10 @@ false, false); - CGM.SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); + Fn->setVisibility(llvm::GlobalValue::HiddenVisibility); + Fn->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + CGM.SetLLVMFunctionAttributes(nullptr, FI, Fn); + CGM.SetLLVMFunctionAttributesForDefinition(nullptr, Fn); StartFunction(FD, C.VoidTy, Fn, FI, args); ApplyDebugLocation NL{*this, blockInfo.getBlockExpr()->getLocStart()}; @@ -1699,13 +1798,9 @@ dst = Address(Builder.CreateLoad(dst), blockInfo.BlockAlign); dst = Builder.CreateBitCast(dst, structPtrTy, "block.dest"); - SmallVector<BlockCaptureManagedEntity, 4> CopiedCaptures; - findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures, - computeCopyInfoForBlockCapture); - for (const auto &CopiedCapture : CopiedCaptures) { - const BlockDecl::Capture &CI = CopiedCapture.CI; - const CGBlockInfo::Capture &capture = CopiedCapture.Capture; + const BlockDecl::Capture &CI = *CopiedCapture.CI; + const CGBlockInfo::Capture &capture = *CopiedCapture.Capture; QualType captureType = CI.getVariable()->getType(); BlockFieldFlags flags = CopiedCapture.Flags; @@ -1844,6 +1939,16 @@ /// variable. llvm::Constant * CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { + SmallVector<BlockCaptureManagedEntity, 4> DestroyedCaptures; + findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures, + computeDestroyInfoForBlockCapture); + std::string FuncName = + getCopyDestroyHelperFuncName(DestroyedCaptures, blockInfo.BlockAlign, + /*IsCopyHelper*/ false, CGM); + + if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName)) + return Func; + ASTContext &C = getContext(); FunctionArgList args; @@ -1859,21 +1964,26 @@ llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI); llvm::Function *Fn = - llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, - "__destroy_helper_block_", &CGM.getModule()); + llvm::Function::Create(LTy, llvm::GlobalValue::LinkOnceODRLinkage, + FuncName, &CGM.getModule()); IdentifierInfo *II - = &CGM.getContext().Idents.get("__destroy_helper_block_"); + = &CGM.getContext().Idents.get(FuncName); FunctionDecl *FD = FunctionDecl::Create(C, C.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, C.VoidTy, nullptr, SC_Static, false, false); - CGM.SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); + Fn->setVisibility(llvm::GlobalValue::HiddenVisibility); + Fn->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + CGM.SetLLVMFunctionAttributes(nullptr, FI, Fn); + CGM.SetLLVMFunctionAttributesForDefinition(nullptr, Fn); StartFunction(FD, C.VoidTy, Fn, FI, args); + markAsIgnoreThreadCheckingAtRuntime(Fn); + ApplyDebugLocation NL{*this, blockInfo.getBlockExpr()->getLocStart()}; llvm::Type *structPtrTy = blockInfo.StructureType->getPointerTo(); @@ -1884,13 +1994,9 @@ CodeGenFunction::RunCleanupsScope cleanups(*this); - SmallVector<BlockCaptureManagedEntity, 4> DestroyedCaptures; - findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures, - computeDestroyInfoForBlockCapture); - for (const auto &DestroyedCapture : DestroyedCaptures) { - const BlockDecl::Capture &CI = DestroyedCapture.CI; - const CGBlockInfo::Capture &capture = DestroyedCapture.Capture; + const BlockDecl::Capture &CI = *DestroyedCapture.CI; + const CGBlockInfo::Capture &capture = *DestroyedCapture.Capture; BlockFieldFlags flags = DestroyedCapture.Flags; Address srcField =
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits