1 ; RUN: opt -objc-arc -S < %s | FileCheck %s 2 3 target datalayout = "e-p:64:64:64" 4 5 !0 = metadata !{} 6 7 declare i8* @objc_retain(i8*) 8 declare void @callee(i8) 9 declare void @use_pointer(i8*) 10 declare void @objc_release(i8*) 11 declare i8* @objc_retainBlock(i8*) 12 declare i8* @objc_autorelease(i8*) 13 14 ; Basic retainBlock+release elimination. 15 16 ; CHECK: define void @test0(i8* %tmp) { 17 ; CHECK-NOT: @objc 18 ; CHECK: } 19 define void @test0(i8* %tmp) { 20 entry: 21 %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 22 tail call void @use_pointer(i8* %tmp2) 23 tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 24 ret void 25 } 26 27 ; Same as test0, but there's no copy_on_escape metadata, so there's no 28 ; optimization possible. 29 30 ; CHECK: define void @test0_no_metadata(i8* %tmp) { 31 ; CHECK: %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) [[NUW:#[0-9]+]] 32 ; CHECK: tail call void @objc_release(i8* %tmp2) [[NUW]], !clang.imprecise_release !0 33 ; CHECK: } 34 define void @test0_no_metadata(i8* %tmp) { 35 entry: 36 %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind 37 tail call void @use_pointer(i8* %tmp2) 38 tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 39 ret void 40 } 41 42 ; Same as test0, but the pointer escapes, so there's no 43 ; optimization possible. 44 45 ; CHECK: define void @test0_escape(i8* %tmp, i8** %z) { 46 ; CHECK: %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) [[NUW]], !clang.arc.copy_on_escape !0 47 ; CHECK: tail call void @objc_release(i8* %tmp2) [[NUW]], !clang.imprecise_release !0 48 ; CHECK: } 49 define void @test0_escape(i8* %tmp, i8** %z) { 50 entry: 51 %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 52 store i8* %tmp2, i8** %z 53 tail call void @use_pointer(i8* %tmp2) 54 tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 55 ret void 56 } 57 58 ; Same as test0_escape, but there's no intervening call. 59 60 ; CHECK: define void @test0_just_escape(i8* %tmp, i8** %z) { 61 ; CHECK: %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) [[NUW]], !clang.arc.copy_on_escape !0 62 ; CHECK: tail call void @objc_release(i8* %tmp2) [[NUW]], !clang.imprecise_release !0 63 ; CHECK: } 64 define void @test0_just_escape(i8* %tmp, i8** %z) { 65 entry: 66 %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 67 store i8* %tmp2, i8** %z 68 tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 69 ret void 70 } 71 72 ; Basic nested retainBlock+release elimination. 73 74 ; CHECK: define void @test1(i8* %tmp) { 75 ; CHECK-NOT: @objc 76 ; CHECK: tail call i8* @objc_retain(i8* %tmp) [[NUW]] 77 ; CHECK-NOT: @objc 78 ; CHECK: tail call void @objc_release(i8* %tmp) [[NUW]], !clang.imprecise_release !0 79 ; CHECK-NOT: @objc 80 ; CHECK: } 81 define void @test1(i8* %tmp) { 82 entry: 83 %tmp1 = tail call i8* @objc_retain(i8* %tmp) nounwind 84 %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 85 tail call void @use_pointer(i8* %tmp2) 86 tail call void @use_pointer(i8* %tmp2) 87 tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 88 tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 89 ret void 90 } 91 92 ; Same as test1, but there's no copy_on_escape metadata, so there's no 93 ; retainBlock+release optimization possible. But we can still eliminate 94 ; the outer retain+release. 95 96 ; CHECK: define void @test1_no_metadata(i8* %tmp) { 97 ; CHECK-NEXT: entry: 98 ; CHECK-NEXT: tail call i8* @objc_retainBlock(i8* %tmp) [[NUW]] 99 ; CHECK-NEXT: @use_pointer(i8* %tmp2) 100 ; CHECK-NEXT: @use_pointer(i8* %tmp2) 101 ; CHECK-NEXT: tail call void @objc_release(i8* %tmp) [[NUW]], !clang.imprecise_release !0 102 ; CHECK-NOT: @objc 103 ; CHECK: } 104 define void @test1_no_metadata(i8* %tmp) { 105 entry: 106 %tmp1 = tail call i8* @objc_retain(i8* %tmp) nounwind 107 %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind 108 tail call void @use_pointer(i8* %tmp2) 109 tail call void @use_pointer(i8* %tmp2) 110 tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 111 tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 112 ret void 113 } 114 115 ; Same as test1, but the pointer escapes, so there's no 116 ; retainBlock+release optimization possible. But we can still eliminate 117 ; the outer retain+release 118 119 ; CHECK: define void @test1_escape(i8* %tmp, i8** %z) { 120 ; CHECK-NEXT: entry: 121 ; CHECK-NEXT: %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) [[NUW]], !clang.arc.copy_on_escape !0 122 ; CHECK-NEXT: store i8* %tmp2, i8** %z 123 ; CHECK-NEXT: @use_pointer(i8* %tmp2) 124 ; CHECK-NEXT: @use_pointer(i8* %tmp2) 125 ; CHECK-NEXT: tail call void @objc_release(i8* %tmp) [[NUW]], !clang.imprecise_release !0 126 ; CHECK-NOT: @objc 127 ; CHECK: } 128 define void @test1_escape(i8* %tmp, i8** %z) { 129 entry: 130 %tmp1 = tail call i8* @objc_retain(i8* %tmp) nounwind 131 %tmp2 = tail call i8* @objc_retainBlock(i8* %tmp) nounwind, !clang.arc.copy_on_escape !0 132 store i8* %tmp2, i8** %z 133 tail call void @use_pointer(i8* %tmp2) 134 tail call void @use_pointer(i8* %tmp2) 135 tail call void @objc_release(i8* %tmp2) nounwind, !clang.imprecise_release !0 136 tail call void @objc_release(i8* %tmp) nounwind, !clang.imprecise_release !0 137 ret void 138 } 139 140 ; CHECK: attributes [[NUW]] = { nounwind } 141