1 ; RUN: opt -objc-arc-contract -S < %s | FileCheck %s 2 3 target datalayout = "e-p:64:64:64" 4 5 declare i8* @objc_retain(i8*) 6 declare void @objc_release(i8*) 7 declare void @use_pointer(i8*) 8 9 @x = external global i8* 10 11 ; CHECK: define void @test0( 12 ; CHECK: entry: 13 ; CHECK-NEXT: tail call void @objc_storeStrong(i8** @x, i8* %p) nounwind 14 ; CHECK-NEXT: ret void 15 define void @test0(i8* %p) { 16 entry: 17 %0 = tail call i8* @objc_retain(i8* %p) nounwind 18 %tmp = load i8** @x, align 8 19 store i8* %0, i8** @x, align 8 20 tail call void @objc_release(i8* %tmp) nounwind 21 ret void 22 } 23 24 ; Don't do this if the load is volatile. 25 26 ; CHECK: define void @test1(i8* %p) { 27 ; CHECK-NEXT: entry: 28 ; CHECK-NEXT: %0 = tail call i8* @objc_retain(i8* %p) nounwind 29 ; CHECK-NEXT: %tmp = load volatile i8** @x, align 8 30 ; CHECK-NEXT: store i8* %0, i8** @x, align 8 31 ; CHECK-NEXT: tail call void @objc_release(i8* %tmp) nounwind 32 ; CHECK-NEXT: ret void 33 ; CHECK-NEXT: } 34 define void @test1(i8* %p) { 35 entry: 36 %0 = tail call i8* @objc_retain(i8* %p) nounwind 37 %tmp = load volatile i8** @x, align 8 38 store i8* %0, i8** @x, align 8 39 tail call void @objc_release(i8* %tmp) nounwind 40 ret void 41 } 42 43 ; Don't do this if the store is volatile. 44 45 ; CHECK: define void @test2(i8* %p) { 46 ; CHECK-NEXT: entry: 47 ; CHECK-NEXT: %0 = tail call i8* @objc_retain(i8* %p) nounwind 48 ; CHECK-NEXT: %tmp = load i8** @x, align 8 49 ; CHECK-NEXT: store volatile i8* %0, i8** @x, align 8 50 ; CHECK-NEXT: tail call void @objc_release(i8* %tmp) nounwind 51 ; CHECK-NEXT: ret void 52 ; CHECK-NEXT: } 53 define void @test2(i8* %p) { 54 entry: 55 %0 = tail call i8* @objc_retain(i8* %p) nounwind 56 %tmp = load i8** @x, align 8 57 store volatile i8* %0, i8** @x, align 8 58 tail call void @objc_release(i8* %tmp) nounwind 59 ret void 60 } 61 62 ; Don't do this if there's a use of the old pointer value between the store 63 ; and the release. 64 65 ; CHECK: define void @test3(i8* %newValue) { 66 ; CHECK-NEXT: entry: 67 ; CHECK-NEXT: %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind 68 ; CHECK-NEXT: %x1 = load i8** @x, align 8 69 ; CHECK-NEXT: store i8* %x0, i8** @x, align 8 70 ; CHECK-NEXT: tail call void @use_pointer(i8* %x1), !clang.arc.no_objc_arc_exceptions !0 71 ; CHECK-NEXT: tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0 72 ; CHECK-NEXT: ret void 73 ; CHECK-NEXT: } 74 define void @test3(i8* %newValue) { 75 entry: 76 %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind 77 %x1 = load i8** @x, align 8 78 store i8* %newValue, i8** @x, align 8 79 tail call void @use_pointer(i8* %x1), !clang.arc.no_objc_arc_exceptions !0 80 tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0 81 ret void 82 } 83 84 ; Like test3, but with an icmp use instead of a call, for good measure. 85 86 ; CHECK: define i1 @test4(i8* %newValue, i8* %foo) { 87 ; CHECK-NEXT: entry: 88 ; CHECK-NEXT: %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind 89 ; CHECK-NEXT: %x1 = load i8** @x, align 8 90 ; CHECK-NEXT: store i8* %x0, i8** @x, align 8 91 ; CHECK-NEXT: %t = icmp eq i8* %x1, %foo 92 ; CHECK-NEXT: tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0 93 ; CHECK-NEXT: ret i1 %t 94 ; CHECK-NEXT: } 95 define i1 @test4(i8* %newValue, i8* %foo) { 96 entry: 97 %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind 98 %x1 = load i8** @x, align 8 99 store i8* %newValue, i8** @x, align 8 100 %t = icmp eq i8* %x1, %foo 101 tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0 102 ret i1 %t 103 } 104 105 ; Do form an objc_storeStrong here, because the use is before the store. 106 107 ; CHECK: define i1 @test5(i8* %newValue, i8* %foo) { 108 ; CHECK: %t = icmp eq i8* %x1, %foo 109 ; CHECK: tail call void @objc_storeStrong(i8** @x, i8* %newValue) nounwind 110 define i1 @test5(i8* %newValue, i8* %foo) { 111 entry: 112 %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind 113 %x1 = load i8** @x, align 8 114 %t = icmp eq i8* %x1, %foo 115 store i8* %newValue, i8** @x, align 8 116 tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0 117 ret i1 %t 118 } 119 120 ; Like test5, but the release is before the store. 121 122 ; CHECK: define i1 @test6(i8* %newValue, i8* %foo) { 123 ; CHECK: %t = icmp eq i8* %x1, %foo 124 ; CHECK: tail call void @objc_storeStrong(i8** @x, i8* %newValue) nounwind 125 define i1 @test6(i8* %newValue, i8* %foo) { 126 entry: 127 %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind 128 %x1 = load i8** @x, align 8 129 tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0 130 %t = icmp eq i8* %x1, %foo 131 store i8* %newValue, i8** @x, align 8 132 ret i1 %t 133 } 134 135 ; Like test0, but there's no store, so don't form an objc_storeStrong. 136 137 ; CHECK: define void @test7( 138 ; CHECK-NEXT: entry: 139 ; CHECK-NEXT: %0 = tail call i8* @objc_retain(i8* %p) nounwind 140 ; CHECK-NEXT: %tmp = load i8** @x, align 8 141 ; CHECK-NEXT: tail call void @objc_release(i8* %tmp) nounwind 142 ; CHECK-NEXT: ret void 143 ; CHECK-NEXT: } 144 define void @test7(i8* %p) { 145 entry: 146 %0 = tail call i8* @objc_retain(i8* %p) nounwind 147 %tmp = load i8** @x, align 8 148 tail call void @objc_release(i8* %tmp) nounwind 149 ret void 150 } 151 152 ; Like test0, but there's no retain, so don't form an objc_storeStrong. 153 154 ; CHECK: define void @test8( 155 ; CHECK-NEXT: entry: 156 ; CHECK-NEXT: %tmp = load i8** @x, align 8 157 ; CHECK-NEXT: store i8* %p, i8** @x, align 8 158 ; CHECK-NEXT: tail call void @objc_release(i8* %tmp) nounwind 159 ; CHECK-NEXT: ret void 160 ; CHECK-NEXT: } 161 define void @test8(i8* %p) { 162 entry: 163 %tmp = load i8** @x, align 8 164 store i8* %p, i8** @x, align 8 165 tail call void @objc_release(i8* %tmp) nounwind 166 ret void 167 } 168 169 !0 = metadata !{} 170