diff options
Diffstat (limited to 'clang/test/CodeGenCXX/blocks.cpp')
-rw-r--r-- | clang/test/CodeGenCXX/blocks.cpp | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/clang/test/CodeGenCXX/blocks.cpp b/clang/test/CodeGenCXX/blocks.cpp new file mode 100644 index 0000000..eb54478 --- /dev/null +++ b/clang/test/CodeGenCXX/blocks.cpp @@ -0,0 +1,228 @@ +// RUN: %clang_cc1 %s -fblocks -triple x86_64-apple-darwin -emit-llvm -o - | FileCheck %s + +namespace test0 { + // CHECK: define void @_ZN5test04testEi( + // CHECK: define internal void @__test_block_invoke_{{.*}}( + // CHECK: define internal void @__block_global_{{.*}}( + void test(int x) { + ^{ ^{ (void) x; }; }; + } +} + +extern void (^out)(); + +namespace test1 { + // Capturing const objects doesn't require a local block. + // CHECK: 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: 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: 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: 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: define void @_ZN5test24testEv() + void test() { + __block A a; + __block B b; + } + + // CHECK: define internal void @__Block_byref_object_copy + // CHECK: call void @_ZN5test21AC1ERKS0_( + + // CHECK: define internal void @__Block_byref_object_dispose + // CHECK: call void @_ZN5test21AD1Ev( + + // CHECK: define internal void @__Block_byref_object_copy + // CHECK: call void @_ZN5test21BC1ERKS0_( + + // CHECK: 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: define void @_ZN5test44testEv() + // CHECK: define internal void @__test_block_invoke + // CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align 1 + // CHECK-NEXT: bitcast i8* + // CHECK-NEXT: 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: 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]], i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = load 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]], i32 0, i32 5 + // CHECK-NEXT: call void @_ZN5test51AC1ERKS0_([[A]]* [[CAPTURE]], [[A]]* [[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* [[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: 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>(); +} |