diff --git a/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/lib/Transforms/ObjCARC/ObjCARCOpts.cpp index 577a744f306..9da202eba95 100644 --- a/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -2344,6 +2344,14 @@ void ObjCARCOpt::OptimizeReturns(Function &F) { bool HasSafePathToCall = HasSafePathToPredecessorCall(Arg, Retain, DependingInstructions, Visited, PA); + + // Don't remove retainRV/autoreleaseRV pairs if the call isn't a tail call. + if (HasSafePathToCall && + GetBasicARCInstKind(Retain) == ARCInstKind::RetainRV && + GetBasicARCInstKind(Autorelease) == ARCInstKind::AutoreleaseRV && + !cast(*DependingInstructions.begin())->isTailCall()) + continue; + DependingInstructions.clear(); Visited.clear(); diff --git a/test/Transforms/ObjCARC/rv.ll b/test/Transforms/ObjCARC/rv.ll index 3d0d56ca0e5..0a1f4665afc 100644 --- a/test/Transforms/ObjCARC/rv.ll +++ b/test/Transforms/ObjCARC/rv.ll @@ -21,6 +21,8 @@ declare void @callee() declare void @callee_fnptr(void ()*) declare void @invokee() declare i8* @returner() +declare i8* @returner1(i8*) +declare i32 @__gxx_personality_v0(...) ; Test that retain+release elimination is suppressed when the ; retain is an objc_retainAutoreleasedReturnValue, since it's @@ -77,7 +79,7 @@ define void @test2() { ; CHECK-NEXT: ret i8* %call define i8* @test3() { entry: - %call = call i8* @returner() + %call = tail call i8* @returner() %0 = call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %call) nounwind %1 = call i8* @llvm.objc.autoreleaseReturnValue(i8* %0) nounwind ret i8* %1 @@ -387,6 +389,44 @@ bb3: ret i32* %retval } +; Don't eliminate the retainRV/autoreleaseRV pair if the call isn't a tail call. + +; CHECK-LABEL: define i8* @test28( +; CHECK: call i8* @returner() +; CHECK: call i8* @llvm.objc.retainAutoreleasedReturnValue( +; CHECK: call i8* @llvm.objc.autoreleaseReturnValue( +define i8* @test28() { +entry: + %call = call i8* @returner() + %0 = call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %call) nounwind + %1 = call i8* @llvm.objc.autoreleaseReturnValue(i8* %0) nounwind + ret i8* %1 +} + +; CHECK-LABEL: define i8* @test29( +; CHECK: call i8* @llvm.objc.retainAutoreleasedReturnValue( +; CHECK: call i8* @llvm.objc.autoreleaseReturnValue( + +define i8* @test29(i8* %k) local_unnamed_addr personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +entry: + %0 = tail call i8* @llvm.objc.retain(i8* %k) + %call = invoke i8* @returner1(i8* %k) + to label %invoke.cont unwind label %lpad + +invoke.cont: + %1 = bitcast i8* %call to i8* + %2 = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %1) + tail call void @llvm.objc.release(i8* %k), !clang.imprecise_release !0 + %3 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %1) + ret i8* %call + +lpad: + %4 = landingpad { i8*, i32 } + cleanup + tail call void @llvm.objc.release(i8* %k) #1, !clang.imprecise_release !0 + resume { i8*, i32 } %4 +} + !0 = !{} ; CHECK: attributes [[NUW]] = { nounwind }