; RUN: opt -objc-arc-contract -S < %s | FileCheck %s ; RUN: opt -passes=objc-arc-contract -S < %s | FileCheck %s target datalayout = "e-p:64:64:64" declare i8* @llvm.objc.retain(i8*) declare void @llvm.objc.release(i8*) declare i8* @llvm.objc.autorelease(i8*) declare i8* @llvm.objc.autoreleaseReturnValue(i8*) declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*) declare void @use_pointer(i8*) declare i8* @returner() declare void @callee() ; CHECK-LABEL: define void @test0( ; CHECK: call void @use_pointer(i8* %0) ; CHECK: } define void @test0(i8* %x) nounwind { entry: %0 = call i8* @llvm.objc.retain(i8* %x) nounwind call void @use_pointer(i8* %x) ret void } ; CHECK-LABEL: define void @test1( ; CHECK: call void @use_pointer(i8* %0) ; CHECK: } define void @test1(i8* %x) nounwind { entry: %0 = call i8* @llvm.objc.autorelease(i8* %x) nounwind call void @use_pointer(i8* %x) ret void } ; Merge objc_retain and objc_autorelease into objc_retainAutorelease. ; CHECK-LABEL: define void @test2( ; CHECK: tail call i8* @llvm.objc.retainAutorelease(i8* %x) [[NUW:#[0-9]+]] ; CHECK: } define void @test2(i8* %x) nounwind { entry: %0 = tail call i8* @llvm.objc.retain(i8* %x) nounwind call i8* @llvm.objc.autorelease(i8* %0) nounwind call void @use_pointer(i8* %x) ret void } ; Same as test2 but the value is returned. Do an RV optimization. ; CHECK-LABEL: define i8* @test2b( ; CHECK: tail call i8* @llvm.objc.retainAutoreleaseReturnValue(i8* %x) [[NUW]] ; CHECK: } define i8* @test2b(i8* %x) nounwind { entry: %0 = tail call i8* @llvm.objc.retain(i8* %x) nounwind tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %0) nounwind ret i8* %x } ; Merge a retain,autorelease pair around a call. ; CHECK-LABEL: define void @test3( ; CHECK: tail call i8* @llvm.objc.retainAutorelease(i8* %x) [[NUW]] ; CHECK: @use_pointer(i8* %0) ; CHECK: } define void @test3(i8* %x, i64 %n) { entry: tail call i8* @llvm.objc.retain(i8* %x) nounwind call void @use_pointer(i8* %x) call i8* @llvm.objc.autorelease(i8* %x) nounwind ret void } ; Trivial retain,autorelease pair with intervening call, but it's post-dominated ; by another release. The retain and autorelease can be merged. ; CHECK-LABEL: define void @test4( ; CHECK-NEXT: entry: ; CHECK-NEXT: @llvm.objc.retainAutorelease(i8* %x) [[NUW]] ; CHECK-NEXT: @use_pointer ; CHECK-NEXT: @llvm.objc.release ; CHECK-NEXT: ret void ; CHECK-NEXT: } define void @test4(i8* %x, i64 %n) { entry: tail call i8* @llvm.objc.retain(i8* %x) nounwind call void @use_pointer(i8* %x) call i8* @llvm.objc.autorelease(i8* %x) nounwind tail call void @llvm.objc.release(i8* %x) nounwind ret void } ; Don't merge retain and autorelease if they're not control-equivalent. ; CHECK-LABEL: define void @test5( ; CHECK: tail call i8* @llvm.objc.retain(i8* %p) [[NUW]] ; CHECK: true: ; CHECK: call i8* @llvm.objc.autorelease(i8* %0) [[NUW]] ; CHECK: } define void @test5(i8* %p, i1 %a) { entry: tail call i8* @llvm.objc.retain(i8* %p) nounwind br i1 %a, label %true, label %false true: call i8* @llvm.objc.autorelease(i8* %p) nounwind call void @use_pointer(i8* %p) ret void false: ret void } ; Don't eliminate objc_retainAutoreleasedReturnValue by merging it into ; an objc_autorelease. ; TODO? Merge objc_retainAutoreleasedReturnValue and objc_autorelease into ; objc_retainAutoreleasedReturnValueAutorelease and merge ; objc_retainAutoreleasedReturnValue and objc_autoreleaseReturnValue ; into objc_retainAutoreleasedReturnValueAutoreleaseReturnValue? ; Those entrypoints don't exist yet though. ; CHECK-LABEL: define i8* @test6( ; CHECK: call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p) [[NUW]] ; CHECK: %t = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %1) [[NUW]] ; CHECK: } define i8* @test6() { %p = call i8* @returner() tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p) nounwind %t = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %p) nounwind call void @use_pointer(i8* %t) ret i8* %t } ; Don't spoil the RV optimization. ; CHECK: define i8* @test7(i8* %p) ; CHECK: tail call i8* @llvm.objc.retain(i8* %p) ; CHECK: call void @use_pointer(i8* %1) ; CHECK: tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %1) ; CHECK: ret i8* %2 ; CHECK-NEXT: } define i8* @test7(i8* %p) { %1 = tail call i8* @llvm.objc.retain(i8* %p) call void @use_pointer(i8* %p) %2 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %p) ret i8* %p } ; Do the return value substitution for PHI nodes too. ; CHECK-LABEL: define i8* @test8( ; CHECK: %retval = phi i8* [ %p, %if.then ], [ null, %entry ] ; CHECK: } define i8* @test8(i1 %x, i8* %c) { entry: br i1 %x, label %return, label %if.then if.then: ; preds = %entry %p = call i8* @llvm.objc.retain(i8* %c) nounwind br label %return return: ; preds = %if.then, %entry %retval = phi i8* [ %c, %if.then ], [ null, %entry ] ret i8* %retval } ; Kill calls to @llvm.objc.clang.arc.use(...) ; CHECK-LABEL: define void @test9( ; CHECK-NOT: clang.arc.use ; CHECK: } define void @test9(i8* %a, i8* %b) { call void (...) @llvm.objc.clang.arc.use(i8* %a, i8* %b) nounwind ret void } ; Turn objc_retain into objc_retainAutoreleasedReturnValue if its operand ; is a return value. ; CHECK: define void @test10() ; CHECK: tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %p) define void @test10() { %p = call i8* @returner() tail call i8* @llvm.objc.retain(i8* %p) nounwind ret void } ; Convert objc_retain to objc_retainAutoreleasedReturnValue if its ; argument is a return value. ; CHECK-LABEL: define void @test11( ; CHECK-NEXT: %y = call i8* @returner() ; CHECK-NEXT: tail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %y) [[NUW]] ; CHECK-NEXT: ret void define void @test11() { %y = call i8* @returner() tail call i8* @llvm.objc.retain(i8* %y) nounwind ret void } ; Don't convert objc_retain to objc_retainAutoreleasedReturnValue if its ; argument is not a return value. ; CHECK-LABEL: define void @test12( ; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %y) [[NUW]] ; CHECK-NEXT: ret void ; CHECK-NEXT: } define void @test12(i8* %y) { tail call i8* @llvm.objc.retain(i8* %y) nounwind ret void } ; Don't Convert objc_retain to objc_retainAutoreleasedReturnValue if it ; isn't next to the call providing its return value. ; CHECK-LABEL: define void @test13( ; CHECK-NEXT: %y = call i8* @returner() ; CHECK-NEXT: call void @callee() ; CHECK-NEXT: tail call i8* @llvm.objc.retain(i8* %y) [[NUW]] ; CHECK-NEXT: ret void ; CHECK-NEXT: } define void @test13() { %y = call i8* @returner() call void @callee() tail call i8* @llvm.objc.retain(i8* %y) nounwind ret void } ; CHECK-LABEL: define void @test14( ; CHECK-NOT: clang.arc.noop.use ; CHECK: ret void define void @test14(i8* %a, i8* %b) { call void (...) @llvm.objc.clang.arc.noop.use(i8* %a, i8* %b) nounwind ret void } declare void @llvm.objc.clang.arc.use(...) nounwind declare void @llvm.objc.clang.arc.noop.use(...) nounwind ; CHECK: attributes [[NUW]] = { nounwind }