diff --git a/llvm/lib/Transforms/Scalar/ObjCARC.cpp b/llvm/lib/Transforms/Scalar/ObjCARC.cpp index 56e6c4c95749..62c4694616f1 100644 --- a/llvm/lib/Transforms/Scalar/ObjCARC.cpp +++ b/llvm/lib/Transforms/Scalar/ObjCARC.cpp @@ -1963,6 +1963,7 @@ namespace { /// use here. enum DependenceKind { NeedsPositiveRetainCount, + AutoreleasePoolBoundary, CanChangeRetainCount, RetainAutoreleaseDep, ///< Blocks objc_retainAutorelease. RetainAutoreleaseRVDep, ///< Blocks objc_retainAutoreleaseReturnValue. @@ -1992,6 +1993,19 @@ Depends(DependenceKind Flavor, Instruction *Inst, const Value *Arg, } } + case AutoreleasePoolBoundary: { + InstructionClass Class = GetInstructionClass(Inst); + switch (Class) { + case IC_AutoreleasepoolPop: + case IC_AutoreleasepoolPush: + // These mark the end and begin of an autorelease pool scope. + return true; + default: + // Nothing else does this. + return false; + } + } + case CanChangeRetainCount: { InstructionClass Class = GetInstructionClass(Inst); switch (Class) { @@ -2009,6 +2023,7 @@ Depends(DependenceKind Flavor, Instruction *Inst, const Value *Arg, case RetainAutoreleaseDep: switch (GetBasicInstructionClass(Inst)) { case IC_AutoreleasepoolPop: + case IC_AutoreleasepoolPush: // Don't merge an objc_autorelease with an objc_retain inside a different // autoreleasepool scope. return true; @@ -2376,9 +2391,34 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) { // Check that there is nothing that cares about the reference // count between the call and the phi. - FindDependencies(NeedsPositiveRetainCount, Arg, - Inst->getParent(), Inst, - DependingInstructions, Visited, PA); + switch (Class) { + case IC_Retain: + case IC_RetainBlock: + // These can always be moved up. + break; + case IC_Release: + // These can't be moved across things that care about the retain count. + FindDependencies(NeedsPositiveRetainCount, Arg, + Inst->getParent(), Inst, + DependingInstructions, Visited, PA); + break; + case IC_Autorelease: + // These can't be moved across autorelease pool scope boundaries. + FindDependencies(AutoreleasePoolBoundary, Arg, + Inst->getParent(), Inst, + DependingInstructions, Visited, PA); + break; + case IC_RetainRV: + case IC_AutoreleaseRV: + // Don't move these; the RV optimization depends on the autoreleaseRV + // being tail called, and the retainRV being immediately after a call + // (which might still happen if we get lucky with codegen layout, but + // it's not worth taking the chance). + continue; + default: + llvm_unreachable("Invalid dependence flavor"); + } + if (DependingInstructions.size() == 1 && *DependingInstructions.begin() == PN) { Changed = true; diff --git a/llvm/test/Transforms/ObjCARC/basic.ll b/llvm/test/Transforms/ObjCARC/basic.ll index 08bd8c0601f3..ba2f778a28ec 100644 --- a/llvm/test/Transforms/ObjCARC/basic.ll +++ b/llvm/test/Transforms/ObjCARC/basic.ll @@ -3,10 +3,12 @@ target datalayout = "e-p:64:64:64" declare i8* @objc_retain(i8*) +declare i8* @objc_retainAutoreleasedReturnValue(i8*) declare void @objc_release(i8*) declare i8* @objc_autorelease(i8*) +declare i8* @objc_autoreleaseReturnValue(i8*) declare void @objc_autoreleasePoolPop(i8*) -declare void @objc_autoreleasePoolPush() +declare i8* @objc_autoreleasePoolPush() declare i8* @objc_retainBlock(i8*) declare i8* @objc_retainedObject(i8*) @@ -526,7 +528,7 @@ entry: define void @test13d(i8* %x, i64 %n) { entry: call i8* @objc_retain(i8* %x) nounwind - call void @objc_autoreleasePoolPush() + call i8* @objc_autoreleasePoolPush() call i8* @objc_retain(i8* %x) nounwind call void @use_pointer(i8* %x) call void @use_pointer(i8* %x) @@ -1400,7 +1402,7 @@ entry: ; CHECK-NEXT: call i8* @objc_autorelease(i8* %p) ; CHECK-NEXT: call void @use_pointer(i8* %p) ; CHECK-NEXT: call void @use_pointer(i8* %p) -; CHECK-NEXT: call void @objc_autoreleasePoolPush() +; CHECK-NEXT: call i8* @objc_autoreleasePoolPush() ; CHECK-NEXT: ret void ; CHECK-NEXT: } define void @test43b(i8* %p) { @@ -1410,7 +1412,7 @@ entry: call i8* @objc_retain(i8* %p) call void @use_pointer(i8* %p) call void @use_pointer(i8* %p) - call void @objc_autoreleasePoolPush() + call i8* @objc_autoreleasePoolPush() call void @objc_release(i8* %p) ret void } @@ -1797,6 +1799,78 @@ exit: ret void } +; Move an autorelease past a phi with a null. + +; CHECK: define i8* @test65( +; CHECK: if.then: +; CHECK: call i8* @objc_autorelease( +; CHECK: return: +; CHECK-NOT: @objc_autorelease +; CHECK: } +define i8* @test65(i1 %x) { +entry: + br i1 %x, label %return, label %if.then + +if.then: ; preds = %entry + %c = call i8* @returner() + %s = call i8* @objc_retainAutoreleasedReturnValue(i8* %c) nounwind + br label %return + +return: ; preds = %if.then, %entry + %retval = phi i8* [ %s, %if.then ], [ null, %entry ] + %q = call i8* @objc_autorelease(i8* %retval) nounwind + ret i8* %retval +} + +; Don't move an autorelease past an autorelease pool boundary. + +; CHECK: define i8* @test65b( +; CHECK: if.then: +; CHECK-NOT: @objc_autorelease +; CHECK: return: +; CHECK: call i8* @objc_autorelease( +; CHECK: } +define i8* @test65b(i1 %x) { +entry: + %t = call i8* @objc_autoreleasePoolPush() + br i1 %x, label %return, label %if.then + +if.then: ; preds = %entry + %c = call i8* @returner() + %s = call i8* @objc_retainAutoreleasedReturnValue(i8* %c) nounwind + br label %return + +return: ; preds = %if.then, %entry + %retval = phi i8* [ %s, %if.then ], [ null, %entry ] + call void @objc_autoreleasePoolPop(i8* %t) + %q = call i8* @objc_autorelease(i8* %retval) nounwind + ret i8* %retval +} + +; Don't move an autoreleaseReuturnValue, which would break +; the RV optimization. + +; CHECK: define i8* @test65c( +; CHECK: if.then: +; CHECK-NOT: @objc_autorelease +; CHECK: return: +; CHECK: call i8* @objc_autoreleaseReturnValue( +; CHECK: } +define i8* @test65c(i1 %x) { +entry: + br i1 %x, label %return, label %if.then + +if.then: ; preds = %entry + %c = call i8* @returner() + %s = call i8* @objc_retainAutoreleasedReturnValue(i8* %c) nounwind + br label %return + +return: ; preds = %if.then, %entry + %retval = phi i8* [ %s, %if.then ], [ null, %entry ] + %q = call i8* @objc_autoreleaseReturnValue(i8* %retval) nounwind + ret i8* %retval +} + declare void @bar(i32 ()*) ; A few real-world testcases.