summaryrefslogtreecommitdiff
path: root/clang/test/CodeGenCXX/blocks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/test/CodeGenCXX/blocks.cpp')
-rw-r--r--clang/test/CodeGenCXX/blocks.cpp228
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>();
+}