forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			310 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C++
		
	
	
	
| // RUN: %clang_cc1 %s -fblocks -triple x86_64-apple-darwin -emit-llvm -o - | FileCheck %s
 | |
| 
 | |
| // CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 }
 | |
| // CHECK: @[[BLOCK_DESCRIPTOR22:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i8* } { i64 0, i64 36, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* null }, align 8
 | |
| 
 | |
| namespace test0 {
 | |
|   // CHECK-LABEL: define void @_ZN5test04testEi(
 | |
|   // CHECK: define internal void @___ZN5test04testEi_block_invoke{{.*}}(
 | |
|   // CHECK: define internal void @___ZN5test04testEi_block_invoke_2{{.*}}(
 | |
|   void test(int x) {
 | |
|     ^{ ^{ (void) x; }; };
 | |
|   }
 | |
| }
 | |
| 
 | |
| extern void (^out)();
 | |
| 
 | |
| namespace test1 {
 | |
|   // Capturing const objects doesn't require a local block.
 | |
|   // CHECK-LABEL: define void @_ZN5test15test1Ev()
 | |
|   // CHECK:   store void ()* bitcast ({{.*}} @__block_literal_global{{.*}} to void ()*), void ()** @out
 | |
|   void test1() {
 | |
|     const int NumHorsemen = 4;
 | |
|     out = ^{ (void) NumHorsemen; };
 | |
|   }
 | |
| 
 | |
|   // That applies to structs too...
 | |
|   // CHECK-LABEL: define void @_ZN5test15test2Ev()
 | |
|   // CHECK:   store void ()* bitcast ({{.*}} @__block_literal_global{{.*}} to void ()*), void ()** @out
 | |
|   struct loc { double x, y; };
 | |
|   void test2() {
 | |
|     const loc target = { 5, 6 };
 | |
|     out = ^{ (void) target; };
 | |
|   }
 | |
| 
 | |
|   // ...unless they have mutable fields...
 | |
|   // CHECK-LABEL: define void @_ZN5test15test3Ev()
 | |
|   // CHECK:   [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
 | |
|   // CHECK:   [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to void ()*
 | |
|   // CHECK:   store void ()* [[T0]], void ()** @out
 | |
|   struct mut { mutable int x; };
 | |
|   void test3() {
 | |
|     const mut obj = { 5 };
 | |
|     out = ^{ (void) obj; };
 | |
|   }
 | |
| 
 | |
|   // ...or non-trivial destructors...
 | |
|   // CHECK-LABEL: define void @_ZN5test15test4Ev()
 | |
|   // CHECK:   [[OBJ:%.*]] = alloca
 | |
|   // CHECK:   [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
 | |
|   // CHECK:   [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to void ()*
 | |
|   // CHECK:   store void ()* [[T0]], void ()** @out
 | |
|   struct scope { int x; ~scope(); };
 | |
|   void test4() {
 | |
|     const scope obj = { 5 };
 | |
|     out = ^{ (void) obj; };
 | |
|   }
 | |
| 
 | |
|   // ...or non-trivial copy constructors, but it's not clear how to do
 | |
|   // that and still have a constant initializer in '03.
 | |
| }
 | |
| 
 | |
| namespace test2 {
 | |
|   struct A {
 | |
|     A();
 | |
|     A(const A &);
 | |
|     ~A();
 | |
|   };
 | |
| 
 | |
|   struct B {
 | |
|     B();
 | |
|     B(const B &);
 | |
|     ~B();
 | |
|   };
 | |
| 
 | |
|   // CHECK-LABEL: define void @_ZN5test24testEv()
 | |
|   void test() {
 | |
|     __block A a;
 | |
|     __block B b;
 | |
|     ^{ (void)a; (void)b; };
 | |
|   }
 | |
| 
 | |
|   // CHECK-LABEL: define internal void @__Block_byref_object_copy
 | |
|   // CHECK: call void @_ZN5test21AC1ERKS0_(
 | |
| 
 | |
|   // CHECK-LABEL: define internal void @__Block_byref_object_dispose
 | |
|   // CHECK: call void @_ZN5test21AD1Ev(
 | |
| 
 | |
|   // CHECK-LABEL: define internal void @__Block_byref_object_copy
 | |
|   // CHECK: call void @_ZN5test21BC1ERKS0_(
 | |
| 
 | |
|   // CHECK-LABEL: define internal void @__Block_byref_object_dispose
 | |
|   // CHECK: call void @_ZN5test21BD1Ev(
 | |
| }
 | |
| 
 | |
| // rdar://problem/9334739
 | |
| // Make sure we mark destructors for parameters captured in blocks.
 | |
| namespace test3 {
 | |
|   struct A {
 | |
|     A(const A&);
 | |
|     ~A();
 | |
|   };
 | |
| 
 | |
|   struct B : A {
 | |
|   };
 | |
| 
 | |
|   void test(B b) {
 | |
|     extern void consume(void(^)());
 | |
|     consume(^{ (void) b; });
 | |
|   }
 | |
| }
 | |
| 
 | |
| // rdar://problem/9971485
 | |
| namespace test4 {
 | |
|   struct A {
 | |
|     A();
 | |
|     ~A();
 | |
|   };
 | |
| 
 | |
|   void foo(A a);
 | |
| 
 | |
|   void test() {
 | |
|     extern void consume(void(^)());
 | |
|     consume(^{ return foo(A()); });
 | |
|   }
 | |
|   // CHECK-LABEL: define void @_ZN5test44testEv()
 | |
|   // CHECK-LABEL: define internal void @___ZN5test44testEv_block_invoke
 | |
|   // CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align 1
 | |
|   // CHECK-NEXT: store i8* [[BLOCKDESC:%.*]], i8** {{.*}}, align 8
 | |
|   // CHECK-NEXT: bitcast i8* [[BLOCKDESC]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]* }>*
 | |
|   // CHECK:      call void @_ZN5test41AC1Ev([[A]]* [[TMP]])
 | |
|   // CHECK-NEXT: call void @_ZN5test43fooENS_1AE([[A]]* [[TMP]])
 | |
|   // CHECK-NEXT: call void @_ZN5test41AD1Ev([[A]]* [[TMP]])
 | |
|   // CHECK-NEXT: ret void
 | |
| }
 | |
| 
 | |
| namespace test5 {
 | |
|   struct A {
 | |
|     unsigned afield;
 | |
|     A();
 | |
|     A(const A&);
 | |
|     ~A();
 | |
|     void foo() const;
 | |
|   };
 | |
| 
 | |
|   void doWithBlock(void(^)());
 | |
| 
 | |
|   void test(bool cond) {
 | |
|     A x;
 | |
|     void (^b)() = (cond ? ^{ x.foo(); } : (void(^)()) 0);
 | |
|     doWithBlock(b);
 | |
|   }
 | |
| 
 | |
|   // CHECK-LABEL:    define void @_ZN5test54testEb(
 | |
|   // CHECK:      [[COND:%.*]] = alloca i8
 | |
|   // CHECK-NEXT: [[X:%.*]] = alloca [[A:%.*]], align 4
 | |
|   // CHECK-NEXT: [[B:%.*]] = alloca void ()*, align 8
 | |
|   // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align 8
 | |
|   // CHECK-NEXT: [[CLEANUP_ACTIVE:%.*]] = alloca i1
 | |
|   // CHECK-NEXT: [[T0:%.*]] = zext i1
 | |
|   // CHECK-NEXT: store i8 [[T0]], i8* [[COND]], align 1
 | |
|   // CHECK-NEXT: call void @_ZN5test51AC1Ev([[A]]* [[X]])
 | |
|   // CHECK-NEXT: [[CLEANUP_ADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
 | |
|   // CHECK-NEXT: [[T0:%.*]] = load i8, i8* [[COND]], align 1
 | |
|   // CHECK-NEXT: [[T1:%.*]] = trunc i8 [[T0]] to i1
 | |
|   // CHECK-NEXT: store i1 false, i1* [[CLEANUP_ACTIVE]]
 | |
|   // CHECK-NEXT: br i1 [[T1]],
 | |
| 
 | |
|   // CHECK-NOT:  br
 | |
|   // CHECK:      [[CAPTURE:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
 | |
|   // CHECK-NEXT: call void @_ZN5test51AC1ERKS0_([[A]]* [[CAPTURE]], [[A]]* dereferenceable({{[0-9]+}}) [[X]])
 | |
|   // CHECK-NEXT: store i1 true, i1* [[CLEANUP_ACTIVE]]
 | |
|   // CHECK-NEXT: bitcast [[BLOCK_T]]* [[BLOCK]] to void ()*
 | |
|   // CHECK-NEXT: br label
 | |
|   // CHECK:      br label
 | |
|   // CHECK:      phi
 | |
|   // CHECK-NEXT: store
 | |
|   // CHECK-NEXT: load
 | |
|   // CHECK-NEXT: call void @_ZN5test511doWithBlockEU13block_pointerFvvE(
 | |
|   // CHECK-NEXT: [[T0:%.*]] = load i1, i1* [[CLEANUP_ACTIVE]]
 | |
|   // CHECK-NEXT: br i1 [[T0]]
 | |
|   // CHECK:      call void @_ZN5test51AD1Ev([[A]]* [[CLEANUP_ADDR]])
 | |
|   // CHECK-NEXT: br label
 | |
|   // CHECK:      call void @_ZN5test51AD1Ev([[A]]* [[X]])
 | |
|   // CHECK-NEXT: ret void
 | |
| }
 | |
| 
 | |
| namespace test6 {
 | |
|   struct A {
 | |
|     A();
 | |
|     ~A();
 | |
|   };
 | |
| 
 | |
|   void foo(const A &, void (^)());
 | |
|   void bar();
 | |
| 
 | |
|   void test() {
 | |
|     // Make sure that the temporary cleanup isn't somehow captured
 | |
|     // within the block.
 | |
|     foo(A(), ^{ bar(); });
 | |
|     bar();
 | |
|   }
 | |
| 
 | |
|   // CHECK-LABEL:    define void @_ZN5test64testEv()
 | |
|   // CHECK:      [[TEMP:%.*]] = alloca [[A:%.*]], align 1
 | |
|   // CHECK-NEXT: call void @_ZN5test61AC1Ev([[A]]* [[TEMP]])
 | |
|   // CHECK-NEXT: call void @_ZN5test63fooERKNS_1AEU13block_pointerFvvE(
 | |
|   // CHECK-NEXT: call void @_ZN5test61AD1Ev([[A]]* [[TEMP]])
 | |
|   // CHECK-NEXT: call void @_ZN5test63barEv()
 | |
|   // CHECK-NEXT: ret void
 | |
| }
 | |
| 
 | |
| namespace test7 {
 | |
|   int f() {
 | |
|     static int n;
 | |
|     int *const p = &n;
 | |
|     return ^{ return *p; }();
 | |
|   }
 | |
| }
 | |
| 
 | |
| namespace test8 {
 | |
|   // <rdar://problem/10832617>: failure to capture this after skipping rebuild
 | |
|   // of the 'this' pointer.
 | |
|   struct X {
 | |
|     int x;
 | |
| 
 | |
|     template<typename T>
 | |
|     int foo() {
 | |
|       return ^ { return x; }();
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   template int X::foo<int>();
 | |
| }
 | |
| 
 | |
| // rdar://13459289
 | |
| namespace test9 {
 | |
|   struct B {
 | |
|     void *p;
 | |
|     B();
 | |
|     B(const B&);
 | |
|     ~B();
 | |
|   };
 | |
| 
 | |
|   void use_block(void (^)());
 | |
|   void use_block_2(void (^)(), const B &a);
 | |
| 
 | |
|   // Ensuring that creating a non-trivial capture copy expression
 | |
|   // doesn't end up stealing the block registration for the block we
 | |
|   // just parsed.  That block must have captures or else it won't
 | |
|   // force registration.  Must occur within a block for some reason.
 | |
|   void test() {
 | |
|     B x;
 | |
|     use_block(^{
 | |
|         int y;
 | |
|         use_block_2(^{ (void)y; }, x);
 | |
|     });
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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_32c16_ZTSVN6test101BE(
 | |
|   // CHECK: call void @_ZN6test101BC1ERVKS0_(
 | |
|   // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_32c16_ZTSVN6test101BE(
 | |
|   // CHECK: call void @_ZN6test101BD1Ev(
 | |
| 
 | |
|   struct B {
 | |
|     int a;
 | |
|     B();
 | |
|     B(const B &);
 | |
|     B(const volatile B &);
 | |
|     ~B();
 | |
|   };
 | |
| 
 | |
|   void test() {
 | |
|     volatile B x;
 | |
|     ^{ (void)x; };
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Copy/dispose helper functions and block descriptors of blocks that capture
 | |
| // objects that are non-external and non-trivial have internal linkage.
 | |
| 
 | |
| // CHECK-LABEL: define internal void @_ZN12_GLOBAL__N_14testEv(
 | |
| // CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, i8*, i8*, i8*, i8* }* @[[BLOCK_DESCRIPTOR22]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %{{.*}}, align 8
 | |
| 
 | |
| // CHECK-LABEL: define internal void @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE(
 | |
| // CHECK-LABEL: define internal void @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE(
 | |
| 
 | |
| namespace {
 | |
|   struct B {
 | |
|     int a;
 | |
|     B();
 | |
|     B(const B &);
 | |
|     ~B();
 | |
|   };
 | |
| 
 | |
|   void test() {
 | |
|     B x;
 | |
|     ^{ (void)x; };
 | |
|   }
 | |
| }
 | |
| 
 | |
| void callTest() {
 | |
|   test();
 | |
| }
 |