Home | History | Annotate | Download | only in ObjCARC
      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-LABEL: define void @test0(
     12 ; CHECK: entry:
     13 ; CHECK-NEXT: tail call void @objc_storeStrong(i8** @x, i8* %p) [[NUW:#[0-9]+]]
     14 ; CHECK-NEXT: ret void
     15 ; CHECK-NEXT: }
     16 define void @test0(i8* %p) {
     17 entry:
     18   %0 = tail call i8* @objc_retain(i8* %p) nounwind
     19   %tmp = load i8*, i8** @x, align 8
     20   store i8* %0, i8** @x, align 8
     21   tail call void @objc_release(i8* %tmp) nounwind
     22   ret void
     23 }
     24 
     25 ; Don't do this if the load is volatile.
     26 
     27 ; CHECK-LABEL: define void @test1(i8* %p) {
     28 ; CHECK-NEXT: entry:
     29 ; CHECK-NEXT:   %0 = tail call i8* @objc_retain(i8* %p) [[NUW]]
     30 ; CHECK-NEXT:   %tmp = load volatile i8*, i8** @x, align 8
     31 ; CHECK-NEXT:   store i8* %0, i8** @x, align 8
     32 ; CHECK-NEXT:   tail call void @objc_release(i8* %tmp) [[NUW]]
     33 ; CHECK-NEXT:   ret void
     34 ; CHECK-NEXT: }
     35 define void @test1(i8* %p) {
     36 entry:
     37   %0 = tail call i8* @objc_retain(i8* %p) nounwind
     38   %tmp = load volatile i8*, i8** @x, align 8
     39   store i8* %0, i8** @x, align 8
     40   tail call void @objc_release(i8* %tmp) nounwind
     41   ret void
     42 }
     43 
     44 ; Don't do this if the store is volatile.
     45 
     46 ; CHECK-LABEL: define void @test2(i8* %p) {
     47 ; CHECK-NEXT: entry:
     48 ; CHECK-NEXT:   %0 = tail call i8* @objc_retain(i8* %p) [[NUW]]
     49 ; CHECK-NEXT:   %tmp = load i8*, i8** @x, align 8
     50 ; CHECK-NEXT:   store volatile i8* %0, i8** @x, align 8
     51 ; CHECK-NEXT:   tail call void @objc_release(i8* %tmp) [[NUW]]
     52 ; CHECK-NEXT:   ret void
     53 ; CHECK-NEXT: }
     54 define void @test2(i8* %p) {
     55 entry:
     56   %0 = tail call i8* @objc_retain(i8* %p) nounwind
     57   %tmp = load i8*, i8** @x, align 8
     58   store volatile i8* %0, i8** @x, align 8
     59   tail call void @objc_release(i8* %tmp) nounwind
     60   ret void
     61 }
     62 
     63 ; Don't do this if there's a use of the old pointer value between the store
     64 ; and the release.
     65 
     66 ; CHECK-LABEL: define void @test3(i8* %newValue) {
     67 ; CHECK-NEXT:  entry:
     68 ; CHECK-NEXT:    %x0 = tail call i8* @objc_retain(i8* %newValue) [[NUW]]
     69 ; CHECK-NEXT:    %x1 = load i8*, i8** @x, align 8
     70 ; CHECK-NEXT:    store i8* %x0, i8** @x, align 8
     71 ; CHECK-NEXT:    tail call void @use_pointer(i8* %x1), !clang.arc.no_objc_arc_exceptions !0
     72 ; CHECK-NEXT:    tail call void @objc_release(i8* %x1) [[NUW]], !clang.imprecise_release !0
     73 ; CHECK-NEXT:    ret void
     74 ; CHECK-NEXT:  }
     75 define void @test3(i8* %newValue) {
     76 entry:
     77   %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
     78   %x1 = load i8*, i8** @x, align 8
     79   store i8* %newValue, i8** @x, align 8
     80   tail call void @use_pointer(i8* %x1), !clang.arc.no_objc_arc_exceptions !0
     81   tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0
     82   ret void
     83 }
     84 
     85 ; Like test3, but with an icmp use instead of a call, for good measure.
     86 
     87 ; CHECK-LABEL:  define i1 @test4(i8* %newValue, i8* %foo) {
     88 ; CHECK-NEXT:   entry:
     89 ; CHECK-NEXT:     %x0 = tail call i8* @objc_retain(i8* %newValue) [[NUW]]
     90 ; CHECK-NEXT:     %x1 = load i8*, i8** @x, align 8
     91 ; CHECK-NEXT:     store i8* %x0, i8** @x, align 8
     92 ; CHECK-NEXT:     %t = icmp eq i8* %x1, %foo
     93 ; CHECK-NEXT:     tail call void @objc_release(i8* %x1) [[NUW]], !clang.imprecise_release !0
     94 ; CHECK-NEXT:     ret i1 %t
     95 ; CHECK-NEXT:   }
     96 define i1 @test4(i8* %newValue, i8* %foo) {
     97 entry:
     98   %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
     99   %x1 = load i8*, i8** @x, align 8
    100   store i8* %newValue, i8** @x, align 8
    101   %t = icmp eq i8* %x1, %foo
    102   tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0
    103   ret i1 %t
    104 }
    105 
    106 ; Do form an objc_storeStrong here, because the use is before the store.
    107 
    108 ; CHECK-LABEL: define i1 @test5(i8* %newValue, i8* %foo) {
    109 ; CHECK: %t = icmp eq i8* %x1, %foo
    110 ; CHECK: tail call void @objc_storeStrong(i8** @x, i8* %newValue) [[NUW]]
    111 ; CHECK: }
    112 define i1 @test5(i8* %newValue, i8* %foo) {
    113 entry:
    114   %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
    115   %x1 = load i8*, i8** @x, align 8
    116   %t = icmp eq i8* %x1, %foo
    117   store i8* %newValue, i8** @x, align 8
    118   tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0
    119   ret i1 %t
    120 }
    121 
    122 ; Like test5, but the release is before the store.
    123 
    124 ; CHECK-LABEL: define i1 @test6(i8* %newValue, i8* %foo) {
    125 ; CHECK: %t = icmp eq i8* %x1, %foo
    126 ; CHECK: tail call void @objc_storeStrong(i8** @x, i8* %newValue) [[NUW]]
    127 ; CHECK: }
    128 define i1 @test6(i8* %newValue, i8* %foo) {
    129 entry:
    130   %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
    131   %x1 = load i8*, i8** @x, align 8
    132   tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0
    133   %t = icmp eq i8* %x1, %foo
    134   store i8* %newValue, i8** @x, align 8
    135   ret i1 %t
    136 }
    137 
    138 ; Like test0, but there's no store, so don't form an objc_storeStrong.
    139 
    140 ; CHECK-LABEL: define void @test7(
    141 ; CHECK-NEXT: entry:
    142 ; CHECK-NEXT:   %0 = tail call i8* @objc_retain(i8* %p) [[NUW]]
    143 ; CHECK-NEXT:   %tmp = load i8*, i8** @x, align 8
    144 ; CHECK-NEXT:   tail call void @objc_release(i8* %tmp) [[NUW]]
    145 ; CHECK-NEXT:   ret void
    146 ; CHECK-NEXT: }
    147 define void @test7(i8* %p) {
    148 entry:
    149   %0 = tail call i8* @objc_retain(i8* %p) nounwind
    150   %tmp = load i8*, i8** @x, align 8
    151   tail call void @objc_release(i8* %tmp) nounwind
    152   ret void
    153 }
    154 
    155 ; Like test0, but there's no retain, so don't form an objc_storeStrong.
    156 
    157 ; CHECK-LABEL: define void @test8(
    158 ; CHECK-NEXT: entry:
    159 ; CHECK-NEXT:   %tmp = load i8*, i8** @x, align 8
    160 ; CHECK-NEXT:   store i8* %p, i8** @x, align 8
    161 ; CHECK-NEXT:   tail call void @objc_release(i8* %tmp) [[NUW]]
    162 ; CHECK-NEXT:   ret void
    163 ; CHECK-NEXT: }
    164 define void @test8(i8* %p) {
    165 entry:
    166   %tmp = load i8*, i8** @x, align 8
    167   store i8* %p, i8** @x, align 8
    168   tail call void @objc_release(i8* %tmp) nounwind
    169   ret void
    170 }
    171 
    172 ; Make sure that we properly handle release that *may* release our new
    173 ; value in between the retain and the store. We need to be sure that
    174 ; this we can safely move the retain to the store. This specific test
    175 ; makes sure that we properly handled a release of an unrelated
    176 ; pointer.
    177 ;
    178 ; CHECK-LABEL: define i1 @test9(i8* %newValue, i8* %foo, i8* %unrelated_ptr) {
    179 ; CHECK-NOT: objc_storeStrong
    180 define i1 @test9(i8* %newValue, i8* %foo, i8* %unrelated_ptr) {
    181 entry:
    182   %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
    183   tail call void @objc_release(i8* %unrelated_ptr) nounwind, !clang.imprecise_release !0
    184   %x1 = load i8*, i8** @x, align 8
    185   tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0
    186   %t = icmp eq i8* %x1, %foo
    187   store i8* %newValue, i8** @x, align 8
    188   ret i1 %t  
    189 }
    190 
    191 ; Make sure that we don't perform the optimization when we just have a call.
    192 ;
    193 ; CHECK-LABEL: define i1 @test10(i8* %newValue, i8* %foo, i8* %unrelated_ptr) {
    194 ; CHECK-NOT: objc_storeStrong
    195 define i1 @test10(i8* %newValue, i8* %foo, i8* %unrelated_ptr) {
    196 entry:
    197   %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
    198   call void @use_pointer(i8* %unrelated_ptr)
    199   %x1 = load i8*, i8** @x, align 8
    200   tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0
    201   %t = icmp eq i8* %x1, %foo
    202   store i8* %newValue, i8** @x, align 8
    203   ret i1 %t
    204 }
    205 
    206 ; Make sure we form the store strong if the use in between the retain
    207 ; and the store does not touch reference counts.
    208 ; CHECK-LABEL: define i1 @test11(i8* %newValue, i8* %foo, i8* %unrelated_ptr) {
    209 ; CHECK: objc_storeStrong
    210 define i1 @test11(i8* %newValue, i8* %foo, i8* %unrelated_ptr) {
    211 entry:
    212   %x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
    213   %t = icmp eq i8* %newValue, %foo
    214   %x1 = load i8*, i8** @x, align 8
    215   tail call void @objc_release(i8* %x1) nounwind, !clang.imprecise_release !0
    216   store i8* %newValue, i8** @x, align 8
    217   ret i1 %t
    218 }
    219 
    220 !0 = !{}
    221 
    222 ; CHECK: attributes [[NUW]] = { nounwind }
    223