diff options
author | Dan Gohman <gohman@apple.com> | 2012-01-13 00:39:07 +0000 |
---|---|---|
committer | Dan Gohman <gohman@apple.com> | 2012-01-13 00:39:07 +0000 |
commit | 79522dc569d3fb51b4a12cdacf24b82e5d5992d2 (patch) | |
tree | 155572c5b2633ce80ea31001c04c9aa3eae10e30 /test | |
parent | ddfda5cd1648c4cae12e6f62c3d86a36be3aefe4 (diff) | |
download | external_llvm-79522dc569d3fb51b4a12cdacf24b82e5d5992d2.zip external_llvm-79522dc569d3fb51b4a12cdacf24b82e5d5992d2.tar.gz external_llvm-79522dc569d3fb51b4a12cdacf24b82e5d5992d2.tar.bz2 |
Implement proper ObjC ARC objc_retainBlock "escape" analysis, so that
the optimizer doesn't eliminate objc_retainBlock calls which are needed
for their side effect of copying blocks onto the heap.
This implements rdar://10361249.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@148076 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test')
-rw-r--r-- | test/Transforms/ObjCARC/basic.ll | 19 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/retain-block.ll | 138 |
2 files changed, 155 insertions, 2 deletions
diff --git a/test/Transforms/ObjCARC/basic.ll b/test/Transforms/ObjCARC/basic.ll index 44c2602..552f4e0 100644 --- a/test/Transforms/ObjCARC/basic.ll +++ b/test/Transforms/ObjCARC/basic.ll @@ -786,7 +786,7 @@ C: @__block_holder_tmp_1 = external constant %block1 define void @test23() { entry: - %0 = call i8* @objc_retainBlock(i8* bitcast (%block1* @__block_holder_tmp_1 to i8*)) nounwind + %0 = call i8* @objc_retainBlock(i8* bitcast (%block1* @__block_holder_tmp_1 to i8*)) nounwind, !clang.arc.copy_on_escape !0 call void @bar(i32 ()* bitcast (%block1* @__block_holder_tmp_1 to i32 ()*)) call void @bar(i32 ()* bitcast (%block1* @__block_holder_tmp_1 to i32 ()*)) call void @objc_release(i8* bitcast (%block1* @__block_holder_tmp_1 to i8*)) nounwind @@ -801,13 +801,28 @@ entry: ; CHECK: } define void @test23b(i8* %p) { entry: - %0 = call i8* @objc_retainBlock(i8* %p) nounwind + %0 = call i8* @objc_retainBlock(i8* %p) nounwind, !clang.arc.copy_on_escape !0 call void @callee() call void @use_pointer(i8* %p) call void @objc_release(i8* %p) nounwind ret void } +; Don't optimize objc_retainBlock, because there's no copy_on_escape metadata. + +; CHECK: define void @test23c( +; CHECK: @objc_retainBlock +; CHECK: @objc_release +; CHECK: } +define void @test23c() { +entry: + %0 = call i8* @objc_retainBlock(i8* bitcast (%block1* @__block_holder_tmp_1 to i8*)) nounwind + call void @bar(i32 ()* bitcast (%block1* @__block_holder_tmp_1 to i32 ()*)) + call void @bar(i32 ()* bitcast (%block1* @__block_holder_tmp_1 to i32 ()*)) + call void @objc_release(i8* bitcast (%block1* @__block_holder_tmp_1 to i8*)) nounwind + ret void +} + ; Any call can decrement a retain count. ; CHECK: define void @test24( diff --git a/test/Transforms/ObjCARC/retain-block.ll b/test/Transforms/ObjCARC/retain-block.ll new file mode 100644 index 0000000..b3b62d3 --- /dev/null +++ b/test/Transforms/ObjCARC/retain-block.ll @@ -0,0 +1,138 @@ +; RUN: opt -objc-arc -S < %s | FileCheck %s + +target datalayout = "e-p:64:64:64" + +!0 = metadata !{} + +declare i8* @objc_retain(i8*) +declare void @callee(i8) +declare void @use_pointer(i8*) +declare void @objc_release(i8*) +declare i8* @objc_retainBlock(i8*) +declare i8* @objc_autorelease(i8*) + +; Basic retainBlock+release elimination. + +; CHECK: define void @test0(i8* %tmp) { +; CHECK-NOT: @objc +; CHECK: } +define void @test0(i8* %tmp) { +entry: + %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 + tail call void @use_pointer(i8* %tmp2) + tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 + ret void +} + +; Same as test0, but there's no copy_on_escape metadata, so there's no +; optimization possible. + +; CHECK: define void @test0_no_metadata(i8* %tmp) { +; CHECK: %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind +; CHECK: tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 +; CHECK: } +define void @test0_no_metadata(i8* %tmp) { +entry: + %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind + tail call void @use_pointer(i8* %tmp2) + tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 + ret void +} + +; Same as test0, but the pointer escapes, so there's no +; optimization possible. + +; CHECK: define void @test0_escape(i8* %tmp, i8** %z) { +; CHECK: %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 +; CHECK: tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 +; CHECK: } +define void @test0_escape(i8* %tmp, i8** %z) { +entry: + %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 + store i8* %tmp2, i8** %z + tail call void @use_pointer(i8* %tmp2) + tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 + ret void +} + +; Same as test0_escape, but there's no intervening call. + +; CHECK: define void @test0_just_escape(i8* %tmp, i8** %z) { +; CHECK: %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 +; CHECK: tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 +; CHECK: } +define void @test0_just_escape(i8* %tmp, i8** %z) { +entry: + %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 + store i8* %tmp2, i8** %z + tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 + ret void +} + +; Basic nested retainBlock+release elimination. + +; CHECK: define void @test1(i8* %tmp) { +; CHECK-NOT: @objc +; CHECK: tail call i8* @objc_retain(i8* %tmp) nounwind +; CHECK-NOT: @objc +; CHECK: tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 +; CHECK-NOT: @objc +; CHECK: } +define void @test1(i8* %tmp) { +entry: + %tmp1 = tail call i8* @objc_retain(i8* %tmp) nounwind + %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 + tail call void @use_pointer(i8* %tmp2) + tail call void @use_pointer(i8* %tmp2) + tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 + tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 + ret void +} + +; Same as test1, but there's no copy_on_escape metadata, so there's no +; retainBlock+release optimization possible. But we can still eliminate +; the outer retain+release. + +; CHECK: define void @test1_no_metadata(i8* %tmp) { +; CHECK-NEXT: entry: +; CHECK-NEXT: tail call i8* @objc_retainBlock(i8* %tmp) nounwind +; CHECK-NEXT: @use_pointer(i8* %tmp2) +; CHECK-NEXT: @use_pointer(i8* %tmp2) +; CHECK-NEXT: tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 +; CHECK-NOT: @objc +; CHECK: } +define void @test1_no_metadata(i8* %tmp) { +entry: + %tmp1 = tail call i8* @objc_retain(i8* %tmp) nounwind + %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind + tail call void @use_pointer(i8* %tmp2) + tail call void @use_pointer(i8* %tmp2) + tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 + tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 + ret void +} + +; Same as test1, but the pointer escapes, so there's no +; retainBlock+release optimization possible. But we can still eliminate +; the outer retain+release + +; CHECK: define void @test1_escape(i8* %tmp, i8** %z) { +; CHECK-NEXT: entry: +; CHECK-NEXT: %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 +; CHECK-NEXT: store i8* %tmp2, i8** %z +; CHECK-NEXT: @use_pointer(i8* %tmp2) +; CHECK-NEXT: @use_pointer(i8* %tmp2) +; CHECK-NEXT: tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 +; CHECK-NOT: @objc +; CHECK: } +define void @test1_escape(i8* %tmp, i8** %z) { +entry: + %tmp1 = tail call i8* @objc_retain(i8* %tmp) nounwind + %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 + store i8* %tmp2, i8** %z + tail call void @use_pointer(i8* %tmp2) + tail call void @use_pointer(i8* %tmp2) + tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 + tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 + ret void +} |