| ; RUN: opt -passes=objc-arc -S < %s | FileCheck %s |
| |
| target datalayout = "e-p:64:64:64" |
| |
| declare ptr @llvm.objc.retain(ptr) |
| declare ptr @llvm.objc.autoreleaseReturnValue(ptr) |
| declare ptr @llvm.objc.retainAutoreleasedReturnValue(ptr) |
| declare ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr) |
| declare void @opaque() |
| declare void @llvm.lifetime.start(i64, ptr nocapture) |
| declare void @llvm.lifetime.end(i64, ptr nocapture) |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_retainRV(ptr %x) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %c |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_bitcast( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_retainRV_bitcast(ptr %x) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %x) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_phi( |
| ; CHECK-NOT: define |
| ; CHECK: phis: |
| ; CHECK-NEXT: phi ptr |
| ; CHECK-NEXT: ret ptr |
| define ptr @elide_with_retainRV_phi(ptr %x) nounwind { |
| entry: |
| br label %phis |
| |
| phis: |
| %a = phi ptr [ %x, %entry ] |
| %c = phi ptr [ %x, %entry ] |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %a) nounwind |
| %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %c) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_splitByRetain( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: %b = call ptr @llvm.objc.autorelease(ptr %x) |
| ; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %x) |
| ; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %b) |
| define ptr @elide_with_retainRV_splitByRetain(ptr %x) nounwind { |
| entry: |
| ; Cleanup is blocked by other ARC intrinsics for ease of implementation; we |
| ; only delay processing AutoreleaseRV until the very next ARC intrinsic. In |
| ; practice, it would be very strange for this to matter. |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %c = call ptr @llvm.objc.retain(ptr %x) nounwind |
| %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_splitByOpaque( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: %b = call ptr @llvm.objc.autorelease(ptr %x) |
| ; CHECK-NEXT: call void @opaque() |
| ; CHECK-NEXT: %d = tail call ptr @llvm.objc.retain(ptr %b) |
| ; CHECK-NEXT: ret ptr %d |
| define ptr @elide_with_retainRV_splitByOpaque(ptr %x) nounwind { |
| entry: |
| ; Cleanup should get blocked by opaque calls. |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| call void @opaque() nounwind |
| %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_splitByLifetime( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %x) |
| ; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr %x) |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_retainRV_splitByLifetime(ptr %x) nounwind { |
| entry: |
| ; Cleanup should skip over lifetime intrinsics. |
| call void @llvm.lifetime.start(i64 8, ptr %x) |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| call void @llvm.lifetime.end(i64 8, ptr %x) |
| %d = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_wrongArg( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: call void @llvm.objc.release(ptr %x) |
| ; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %y) |
| define ptr @elide_with_retainRV_wrongArg(ptr %x, ptr %y) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %y) nounwind |
| ret ptr %c |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_wrongBB( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: call ptr @llvm.objc.autorelease(ptr %x) |
| ; CHECK-NEXT: br label %next |
| ; CHECK: next: |
| ; CHECK-NEXT: tail call ptr @llvm.objc.retain( |
| ; CHECK-NEXT: ret ptr |
| define ptr @elide_with_retainRV_wrongBB(ptr %x) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| br label %next |
| |
| next: |
| %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %c |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_beforeAutoreleaseRV( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_retainRV_beforeAutoreleaseRV(ptr %x) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind |
| %d = call ptr @llvm.objc.autoreleaseReturnValue(ptr %c) nounwind |
| ret ptr %c |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_retainRV_afterRetain( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %x) |
| ; CHECK-NEXT: ret ptr %a |
| define ptr @elide_with_retainRV_afterRetain(ptr %x) nounwind { |
| entry: |
| %a = call ptr @llvm.objc.retain(ptr %x) nounwind |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %a) nounwind |
| %c = call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %c |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_claimRV(ptr %x) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %c |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_bitcast( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_claimRV_bitcast(ptr %x) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %x) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_phi( |
| ; CHECK-NOT: define |
| ; CHECK: phis: |
| ; CHECK-NEXT: %c = phi ptr |
| ; CHECK-NEXT: tail call void @llvm.objc.release(ptr %c) |
| ; CHECK-NEXT: ret ptr %c |
| define ptr @elide_with_claimRV_phi(ptr %x) nounwind { |
| entry: |
| br label %phis |
| |
| phis: |
| %a = phi ptr [ %x, %entry ] |
| %c = phi ptr [ %x, %entry ] |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %a) nounwind |
| %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %c) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_splitByRetain( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: %b = call ptr @llvm.objc.autorelease(ptr %x) |
| ; CHECK-NEXT: tail call ptr @llvm.objc.retain(ptr %x) |
| ; CHECK-NEXT: tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) |
| define ptr @elide_with_claimRV_splitByRetain(ptr %x) nounwind { |
| entry: |
| ; Cleanup is blocked by other ARC intrinsics for ease of implementation; we |
| ; only delay processing AutoreleaseRV until the very next ARC intrinsic. In |
| ; practice, it would be very strange for this to matter. |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %c = call ptr @llvm.objc.retain(ptr %x) nounwind |
| %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_splitByOpaque( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: %b = call ptr @llvm.objc.autorelease(ptr %x) |
| ; CHECK-NEXT: call void @opaque() |
| ; CHECK-NEXT: %d = tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) |
| ; CHECK-NEXT: ret ptr %d |
| define ptr @elide_with_claimRV_splitByOpaque(ptr %x) nounwind { |
| entry: |
| ; Cleanup should get blocked by opaque calls. |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| call void @opaque() nounwind |
| %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_splitByLifetime( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %x) |
| ; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr %x) |
| ; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_claimRV_splitByLifetime(ptr %x) nounwind { |
| entry: |
| ; Cleanup should skip over lifetime intrinsics. |
| call void @llvm.lifetime.start(i64 8, ptr %x) |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| call void @llvm.lifetime.end(i64 8, ptr %x) |
| %d = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %d |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_wrongArg( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: call void @llvm.objc.release(ptr %x) |
| ; CHECK-NEXT: tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %y) |
| define ptr @elide_with_claimRV_wrongArg(ptr %x, ptr %y) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %y) nounwind |
| ret ptr %c |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_wrongBB( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: call ptr @llvm.objc.autorelease(ptr %x) |
| ; CHECK-NEXT: br label %next |
| ; CHECK: next: |
| ; CHECK-NEXT: tail call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue( |
| ; CHECK-NEXT: ret ptr |
| define ptr @elide_with_claimRV_wrongBB(ptr %x) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| br label %next |
| |
| next: |
| %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %c |
| } |
| |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_beforeAutoreleaseRV( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: tail call void @llvm.objc.release(ptr %x) |
| ; CHECK-NEXT: tail call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_claimRV_beforeAutoreleaseRV(ptr %x) nounwind { |
| entry: |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %x) nounwind |
| %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind |
| %d = call ptr @llvm.objc.autoreleaseReturnValue(ptr %c) nounwind |
| ret ptr %c |
| } |
| |
| ; CHECK-LABEL: define ptr @elide_with_claimRV_afterRetain( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: ret ptr %x |
| define ptr @elide_with_claimRV_afterRetain(ptr %x) nounwind { |
| entry: |
| %a = call ptr @llvm.objc.retain(ptr %x) nounwind |
| %b = call ptr @llvm.objc.autoreleaseReturnValue(ptr %a) nounwind |
| %c = call ptr @llvm.objc.unsafeClaimAutoreleasedReturnValue(ptr %b) nounwind |
| ret ptr %c |
| } |