1; RUN: opt -passes=objc-arc -S < %s | FileCheck %s 2 3target datalayout = "e-p:64:64:64" 4 5declare ptr @llvm.objc.retain(ptr) 6declare ptr @llvm.objc.autoreleaseReturnValue(ptr) 7declare ptr @llvm.objc.retainAutoreleasedReturnValue(ptr) 8declare ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr) 9declare void @opaque() 10declare void @llvm.lifetime.start(i64, ptr nocapture) 11declare void @llvm.lifetime.end(i64, ptr nocapture) 12 13; CHECK-LABEL: define ptr @elide_with_retainRV( 14; CHECK-NEXT: entry: 15; CHECK-NEXT: ret ptr %x 16define ptr @elide_with_retainRV(ptr %x) nounwind { 17entry: 18 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 19 %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind 20 ret ptr %c 21} 22 23; CHECK-LABEL: define ptr @elide_with_retainRV_bitcast( 24; CHECK-NEXT: entry: 25; CHECK-NEXT: ret ptr %x 26define ptr @elide_with_retainRV_bitcast(ptr %x) nounwind { 27entry: 28 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 29 %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %x) nounwind 30 ret ptr %d 31} 32 33; CHECK-LABEL: define ptr @elide_with_retainRV_phi( 34; CHECK-NOT: define 35; CHECK: phis: 36; CHECK-NEXT: phi ptr 37; CHECK-NEXT: ret ptr 38define ptr @elide_with_retainRV_phi(ptr %x) nounwind { 39entry: 40 br label %phis 41 42phis: 43 %a = phi ptr [ %x, %entry ] 44 %c = phi ptr [ %x, %entry ] 45 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %a) nounwind 46 %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %c) nounwind 47 ret ptr %d 48} 49 50; CHECK-LABEL: define ptr @elide_with_retainRV_splitByRetain( 51; CHECK-NEXT: entry: 52; CHECK-NEXT: %b = call ptr @llvm.objc.autorelease(ptr %x) 53; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %x) 54; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %b) 55define ptr @elide_with_retainRV_splitByRetain(ptr %x) nounwind { 56entry: 57 ; Cleanup is blocked by other ARC intrinsics for ease of implementation; we 58 ; only delay processing AutoreleaseRV until the very next ARC intrinsic. In 59 ; practice, it would be very strange for this to matter. 60 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 61 %c = call ptr @llvm.objc.retain(ptr %x) nounwind 62 %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind 63 ret ptr %d 64} 65 66; CHECK-LABEL: define ptr @elide_with_retainRV_splitByOpaque( 67; CHECK-NEXT: entry: 68; CHECK-NEXT: %b = call ptr @llvm.objc.autorelease(ptr %x) 69; CHECK-NEXT: call void @opaque() 70; CHECK-NEXT: %d = tail call ptr @llvm.objc.retain(ptr %b) 71; CHECK-NEXT: ret ptr %d 72define ptr @elide_with_retainRV_splitByOpaque(ptr %x) nounwind { 73entry: 74 ; Cleanup should get blocked by opaque calls. 75 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 76 call void @opaque() nounwind 77 %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind 78 ret ptr %d 79} 80 81; CHECK-LABEL: define ptr @elide_with_retainRV_splitByLifetime( 82; CHECK-NEXT: entry: 83; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %x) 84; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr %x) 85; CHECK-NEXT: ret ptr %x 86define ptr @elide_with_retainRV_splitByLifetime(ptr %x) nounwind { 87entry: 88 ; Cleanup should skip over lifetime intrinsics. 89 call void @llvm.lifetime.start(i64 8, ptr %x) 90 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 91 call void @llvm.lifetime.end(i64 8, ptr %x) 92 %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind 93 ret ptr %d 94} 95 96; CHECK-LABEL: define ptr @elide_with_retainRV_wrongArg( 97; CHECK-NEXT: entry: 98; CHECK-NEXT: call void @llvm.objc.release(ptr %x) 99; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %y) 100define ptr @elide_with_retainRV_wrongArg(ptr %x, ptr %y) nounwind { 101entry: 102 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 103 %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %y) nounwind 104 ret ptr %c 105} 106 107; CHECK-LABEL: define ptr @elide_with_retainRV_wrongBB( 108; CHECK-NEXT: entry: 109; CHECK-NEXT: call ptr @llvm.objc.autorelease(ptr %x) 110; CHECK-NEXT: br label %next 111; CHECK: next: 112; CHECK-NEXT: tail call ptr @llvm.objc.retain( 113; CHECK-NEXT: ret ptr 114define ptr @elide_with_retainRV_wrongBB(ptr %x) nounwind { 115entry: 116 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 117 br label %next 118 119next: 120 %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind 121 ret ptr %c 122} 123 124; CHECK-LABEL: define ptr @elide_with_retainRV_beforeAutoreleaseRV( 125; CHECK-NEXT: entry: 126; CHECK-NEXT: tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) 127; CHECK-NEXT: ret ptr %x 128define ptr @elide_with_retainRV_beforeAutoreleaseRV(ptr %x) nounwind { 129entry: 130 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 131 %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind 132 %d = call ptr @llvm.objc.autoreleaseReturnValue(ptr %c) nounwind 133 ret ptr %c 134} 135 136; CHECK-LABEL: define ptr @elide_with_retainRV_afterRetain( 137; CHECK-NEXT: entry: 138; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %x) 139; CHECK-NEXT: ret ptr %a 140define ptr @elide_with_retainRV_afterRetain(ptr %x) nounwind { 141entry: 142 %a = call ptr @llvm.objc.retain(ptr %x) nounwind 143 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %a) nounwind 144 %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind 145 ret ptr %c 146} 147 148; CHECK-LABEL: define ptr @elide_with_claimRV( 149; CHECK-NEXT: entry: 150; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) 151; CHECK-NEXT: ret ptr %x 152define ptr @elide_with_claimRV(ptr %x) nounwind { 153entry: 154 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 155 %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind 156 ret ptr %c 157} 158 159; CHECK-LABEL: define ptr @elide_with_claimRV_bitcast( 160; CHECK-NEXT: entry: 161; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) 162; CHECK-NEXT: ret ptr %x 163define ptr @elide_with_claimRV_bitcast(ptr %x) nounwind { 164entry: 165 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 166 %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %x) nounwind 167 ret ptr %d 168} 169 170; CHECK-LABEL: define ptr @elide_with_claimRV_phi( 171; CHECK-NOT: define 172; CHECK: phis: 173; CHECK-NEXT: %c = phi ptr 174; CHECK-NEXT: tail call void @llvm.objc.release(ptr %c) 175; CHECK-NEXT: ret ptr %c 176define ptr @elide_with_claimRV_phi(ptr %x) nounwind { 177entry: 178 br label %phis 179 180phis: 181 %a = phi ptr [ %x, %entry ] 182 %c = phi ptr [ %x, %entry ] 183 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %a) nounwind 184 %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %c) nounwind 185 ret ptr %d 186} 187 188; CHECK-LABEL: define ptr @elide_with_claimRV_splitByRetain( 189; CHECK-NEXT: entry: 190; CHECK-NEXT: %b = call ptr @llvm.objc.autorelease(ptr %x) 191; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %x) 192; CHECK-NEXT: tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) 193define ptr @elide_with_claimRV_splitByRetain(ptr %x) nounwind { 194entry: 195 ; Cleanup is blocked by other ARC intrinsics for ease of implementation; we 196 ; only delay processing AutoreleaseRV until the very next ARC intrinsic. In 197 ; practice, it would be very strange for this to matter. 198 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 199 %c = call ptr @llvm.objc.retain(ptr %x) nounwind 200 %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind 201 ret ptr %d 202} 203 204; CHECK-LABEL: define ptr @elide_with_claimRV_splitByOpaque( 205; CHECK-NEXT: entry: 206; CHECK-NEXT: %b = call ptr @llvm.objc.autorelease(ptr %x) 207; CHECK-NEXT: call void @opaque() 208; CHECK-NEXT: %d = tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) 209; CHECK-NEXT: ret ptr %d 210define ptr @elide_with_claimRV_splitByOpaque(ptr %x) nounwind { 211entry: 212 ; Cleanup should get blocked by opaque calls. 213 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 214 call void @opaque() nounwind 215 %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind 216 ret ptr %d 217} 218 219; CHECK-LABEL: define ptr @elide_with_claimRV_splitByLifetime( 220; CHECK-NEXT: entry: 221; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %x) 222; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr %x) 223; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) 224; CHECK-NEXT: ret ptr %x 225define ptr @elide_with_claimRV_splitByLifetime(ptr %x) nounwind { 226entry: 227 ; Cleanup should skip over lifetime intrinsics. 228 call void @llvm.lifetime.start(i64 8, ptr %x) 229 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 230 call void @llvm.lifetime.end(i64 8, ptr %x) 231 %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind 232 ret ptr %d 233} 234 235; CHECK-LABEL: define ptr @elide_with_claimRV_wrongArg( 236; CHECK-NEXT: entry: 237; CHECK-NEXT: call void @llvm.objc.release(ptr %x) 238; CHECK-NEXT: tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %y) 239define ptr @elide_with_claimRV_wrongArg(ptr %x, ptr %y) nounwind { 240entry: 241 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 242 %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %y) nounwind 243 ret ptr %c 244} 245 246; CHECK-LABEL: define ptr @elide_with_claimRV_wrongBB( 247; CHECK-NEXT: entry: 248; CHECK-NEXT: call ptr @llvm.objc.autorelease(ptr %x) 249; CHECK-NEXT: br label %next 250; CHECK: next: 251; CHECK-NEXT: tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue( 252; CHECK-NEXT: ret ptr 253define ptr @elide_with_claimRV_wrongBB(ptr %x) nounwind { 254entry: 255 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 256 br label %next 257 258next: 259 %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind 260 ret ptr %c 261} 262 263 264; CHECK-LABEL: define ptr @elide_with_claimRV_beforeAutoreleaseRV( 265; CHECK-NEXT: entry: 266; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) 267; CHECK-NEXT: tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) 268; CHECK-NEXT: ret ptr %x 269define ptr @elide_with_claimRV_beforeAutoreleaseRV(ptr %x) nounwind { 270entry: 271 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind 272 %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind 273 %d = call ptr @llvm.objc.autoreleaseReturnValue(ptr %c) nounwind 274 ret ptr %c 275} 276 277; CHECK-LABEL: define ptr @elide_with_claimRV_afterRetain( 278; CHECK-NEXT: entry: 279; CHECK-NEXT: ret ptr %x 280define ptr @elide_with_claimRV_afterRetain(ptr %x) nounwind { 281entry: 282 %a = call ptr @llvm.objc.retain(ptr %x) nounwind 283 %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %a) nounwind 284 %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind 285 ret ptr %c 286} 287