diff options
-rw-r--r-- | lib/Transforms/ObjCARC/ObjCARCContract.cpp | 65 | ||||
-rw-r--r-- | lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 52 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/contract.ll | 55 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/move-and-merge-autorelease.ll | 2 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/rv.ll | 50 |
5 files changed, 120 insertions, 104 deletions
diff --git a/lib/Transforms/ObjCARC/ObjCARCContract.cpp b/lib/Transforms/ObjCARC/ObjCARCContract.cpp index b96c64fe81..c43f4f4a44 100644 --- a/lib/Transforms/ObjCARC/ObjCARCContract.cpp +++ b/lib/Transforms/ObjCARC/ObjCARCContract.cpp @@ -66,6 +66,8 @@ namespace { Constant *RetainAutoreleaseCallee; /// Declaration for objc_retainAutoreleaseReturnValue(). Constant *RetainAutoreleaseRVCallee; + /// Declaration for objc_retainAutoreleasedReturnValue(). + Constant *RetainRVCallee; /// The inline asm string to insert between calls and RetainRV calls to make /// the optimization work on targets which need it. @@ -77,9 +79,12 @@ namespace { SmallPtrSet<CallInst *, 8> StoreStrongCalls; Constant *getStoreStrongCallee(Module *M); + Constant *getRetainRVCallee(Module *M); Constant *getRetainAutoreleaseCallee(Module *M); Constant *getRetainAutoreleaseRVCallee(Module *M); + bool OptimizeRetainCall(Function &F, Instruction *Retain); + bool ContractAutorelease(Function &F, Instruction *Autorelease, InstructionClass Class, SmallPtrSet<Instruction *, 4> @@ -172,6 +177,57 @@ Constant *ObjCARCContract::getRetainAutoreleaseRVCallee(Module *M) { return RetainAutoreleaseRVCallee; } +Constant *ObjCARCContract::getRetainRVCallee(Module *M) { + if (!RetainRVCallee) { + LLVMContext &C = M->getContext(); + Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C)); + Type *Params[] = { I8X }; + FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false); + AttributeSet Attribute = + AttributeSet().addAttribute(M->getContext(), AttributeSet::FunctionIndex, + Attribute::NoUnwind); + RetainRVCallee = + M->getOrInsertFunction("objc_retainAutoreleasedReturnValue", FTy, + Attribute); + } + return RetainRVCallee; +} + +/// Turn objc_retain into objc_retainAutoreleasedReturnValue if the operand is a +/// return value. We do this late so we do not disrupt the dataflow analysis in +/// ObjCARCOpt. +bool +ObjCARCContract::OptimizeRetainCall(Function &F, Instruction *Retain) { + ImmutableCallSite CS(GetObjCArg(Retain)); + const Instruction *Call = CS.getInstruction(); + if (!Call) + return false; + if (Call->getParent() != Retain->getParent()) + return false; + + // Check that the call is next to the retain. + BasicBlock::const_iterator I = Call; + ++I; + while (IsNoopInstruction(I)) ++I; + if (&*I != Retain) + return false; + + // Turn it to an objc_retainAutoreleasedReturnValue. + Changed = true; + ++NumPeeps; + + DEBUG(dbgs() << "Transforming objc_retain => " + "objc_retainAutoreleasedReturnValue since the operand is a " + "return value.\nOld: "<< *Retain << "\n"); + + // We do not have to worry about tail calls/does not throw since + // retain/retainRV have the same properties. + cast<CallInst>(Retain)->setCalledFunction(getRetainRVCallee(F.getParent())); + + DEBUG(dbgs() << "New: " << *Retain << "\n"); + return true; +} + /// Merge an autorelease with a retain into a fused call. bool ObjCARCContract::ContractAutorelease(Function &F, Instruction *Autorelease, @@ -329,6 +385,7 @@ bool ObjCARCContract::doInitialization(Module &M) { StoreStrongCallee = 0; RetainAutoreleaseCallee = 0; RetainAutoreleaseRVCallee = 0; + RetainRVCallee = 0; // Initialize RetainRVMarker. RetainRVMarker = 0; @@ -380,7 +437,6 @@ bool ObjCARCContract::runOnFunction(Function &F) { // objc_retainBlock does not necessarily return its argument. InstructionClass Class = GetBasicInstructionClass(Inst); switch (Class) { - case IC_Retain: case IC_FusedRetainAutorelease: case IC_FusedRetainAutoreleaseRV: break; @@ -389,6 +445,13 @@ bool ObjCARCContract::runOnFunction(Function &F) { if (ContractAutorelease(F, Inst, Class, DependingInstructions, Visited)) continue; break; + case IC_Retain: + // Attempt to convert retains to retainrvs if they are next to function + // calls. + if (!OptimizeRetainCall(F, Inst)) + break; + // If we succeed in our optimization, fall through. + // FALLTHROUGH case IC_RetainRV: { // If we're compiling for a target which needs a special inline-asm // marker to do the retainAutoreleasedReturnValue optimization, diff --git a/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/lib/Transforms/ObjCARC/ObjCARCOpts.cpp index 54a701c978..d95c9e8334 100644 --- a/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -997,9 +997,6 @@ namespace { /// them. These are initialized lazily to avoid cluttering up the Module /// with unused declarations. - /// Declaration for ObjC runtime function - /// objc_retainAutoreleasedReturnValue. - Constant *RetainRVCallee; /// Declaration for ObjC runtime function objc_autoreleaseReturnValue. Constant *AutoreleaseRVCallee; /// Declaration for ObjC runtime function objc_release. @@ -1033,7 +1030,6 @@ namespace { unsigned ARCAnnotationProvenanceSourceMDKind; #endif // ARC_ANNOATIONS - Constant *getRetainRVCallee(Module *M); Constant *getAutoreleaseRVCallee(Module *M); Constant *getReleaseCallee(Module *M); Constant *getRetainCallee(Module *M); @@ -1042,7 +1038,6 @@ namespace { bool IsRetainBlockOptimizable(const Instruction *Inst); - void OptimizeRetainCall(Function &F, Instruction *Retain); bool OptimizeRetainRVCall(Function &F, Instruction *RetainRV); void OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV, InstructionClass &Class); @@ -1152,22 +1147,6 @@ bool ObjCARCOpt::IsRetainBlockOptimizable(const Instruction *Inst) { return true; } -Constant *ObjCARCOpt::getRetainRVCallee(Module *M) { - if (!RetainRVCallee) { - LLVMContext &C = M->getContext(); - Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C)); - Type *Params[] = { I8X }; - FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false); - AttributeSet Attribute = - AttributeSet().addAttribute(M->getContext(), AttributeSet::FunctionIndex, - Attribute::NoUnwind); - RetainRVCallee = - M->getOrInsertFunction("objc_retainAutoreleasedReturnValue", FTy, - Attribute); - } - return RetainRVCallee; -} - Constant *ObjCARCOpt::getAutoreleaseRVCallee(Module *M) { if (!AutoreleaseRVCallee) { LLVMContext &C = M->getContext(); @@ -1247,35 +1226,6 @@ Constant *ObjCARCOpt::getAutoreleaseCallee(Module *M) { return AutoreleaseCallee; } -/// Turn objc_retain into objc_retainAutoreleasedReturnValue if the operand is a -/// return value. -void -ObjCARCOpt::OptimizeRetainCall(Function &F, Instruction *Retain) { - ImmutableCallSite CS(GetObjCArg(Retain)); - const Instruction *Call = CS.getInstruction(); - if (!Call) return; - if (Call->getParent() != Retain->getParent()) return; - - // Check that the call is next to the retain. - BasicBlock::const_iterator I = Call; - ++I; - while (IsNoopInstruction(I)) ++I; - if (&*I != Retain) - return; - - // Turn it to an objc_retainAutoreleasedReturnValue.. - Changed = true; - ++NumPeeps; - - DEBUG(dbgs() << "Transforming objc_retain => " - "objc_retainAutoreleasedReturnValue since the operand is a " - "return value.\nOld: "<< *Retain << "\n"); - - cast<CallInst>(Retain)->setCalledFunction(getRetainRVCallee(F.getParent())); - - DEBUG(dbgs() << "New: " << *Retain << "\n"); -} - /// Turn objc_retainAutoreleasedReturnValue into objc_retain if the operand is /// not a return value. Or, if it can be paired with an /// objc_autoreleaseReturnValue, delete the pair and return true. @@ -1493,7 +1443,6 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) { // FALLTHROUGH case IC_Retain: ++NumRetainsBeforeOpt; - OptimizeRetainCall(F, Inst); break; case IC_RetainRV: if (OptimizeRetainRVCall(F, Inst)) @@ -3076,7 +3025,6 @@ bool ObjCARCOpt::doInitialization(Module &M) { // calls finalizers which can have arbitrary side effects. // These are initialized lazily. - RetainRVCallee = 0; AutoreleaseRVCallee = 0; ReleaseCallee = 0; RetainCallee = 0; diff --git a/test/Transforms/ObjCARC/contract.ll b/test/Transforms/ObjCARC/contract.ll index 77ad39a01c..3544f88552 100644 --- a/test/Transforms/ObjCARC/contract.ll +++ b/test/Transforms/ObjCARC/contract.ll @@ -10,6 +10,7 @@ declare i8* @objc_retainAutoreleasedReturnValue(i8*) declare void @use_pointer(i8*) declare i8* @returner() +declare void @callee() ; CHECK: define void @test0 ; CHECK: call void @use_pointer(i8* %0) @@ -172,6 +173,60 @@ define void @test9(i8* %a, i8* %b) { ret void } + +; Turn objc_retain into objc_retainAutoreleasedReturnValue if its operand +; is a return value. + +; CHECK: define void @test10() +; CHECK: tail call i8* @objc_retainAutoreleasedReturnValue(i8* %p) +define void @test10() { + %p = call i8* @returner() + tail call i8* @objc_retain(i8* %p) nounwind + ret void +} + +; Convert objc_retain to objc_retainAutoreleasedReturnValue if its +; argument is a return value. + +; CHECK: define void @test11( +; CHECK-NEXT: %y = call i8* @returner() +; CHECK-NEXT: tail call i8* @objc_retainAutoreleasedReturnValue(i8* %y) [[NUW]] +; CHECK-NEXT: ret void +define void @test11() { + %y = call i8* @returner() + tail call i8* @objc_retain(i8* %y) nounwind + ret void +} + +; Don't convert objc_retain to objc_retainAutoreleasedReturnValue if its +; argument is not a return value. + +; CHECK: define void @test12( +; CHECK-NEXT: tail call i8* @objc_retain(i8* %y) [[NUW]] +; CHECK-NEXT: ret void +; CHECK-NEXT: } +define void @test12(i8* %y) { + tail call i8* @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: define void @test13( +; CHECK-NEXT: %y = call i8* @returner() +; CHECK-NEXT: call void @callee() +; CHECK-NEXT: tail call i8* @objc_retain(i8* %y) [[NUW]] +; CHECK-NEXT: ret void +; CHECK-NEXT: } +define void @test13() { + %y = call i8* @returner() + call void @callee() + tail call i8* @objc_retain(i8* %y) nounwind + ret void +} + + declare void @clang.arc.use(...) nounwind ; CHECK: attributes [[NUW]] = { nounwind } diff --git a/test/Transforms/ObjCARC/move-and-merge-autorelease.ll b/test/Transforms/ObjCARC/move-and-merge-autorelease.ll index 8462c70a48..e5d2f07e45 100644 --- a/test/Transforms/ObjCARC/move-and-merge-autorelease.ll +++ b/test/Transforms/ObjCARC/move-and-merge-autorelease.ll @@ -1,4 +1,4 @@ -; RUN: opt -S -objc-arc < %s | FileCheck %s +; RUN: opt -S -objc-arc -objc-arc-contract < %s | FileCheck %s ; The optimizer should be able to move the autorelease past two phi nodes ; and fold it with the release in bb65. diff --git a/test/Transforms/ObjCARC/rv.ll b/test/Transforms/ObjCARC/rv.ll index 589c60f9f3..e857c9f41b 100644 --- a/test/Transforms/ObjCARC/rv.ll +++ b/test/Transforms/ObjCARC/rv.ll @@ -136,17 +136,6 @@ define i8* @test7b() { ret i8* %p } -; Turn objc_retain into objc_retainAutoreleasedReturnValue if its operand -; is a return value. - -; CHECK: define void @test8() -; CHECK: tail call i8* @objc_retainAutoreleasedReturnValue(i8* %p) -define void @test8() { - %p = call i8* @returner() - call i8* @objc_retain(i8* %p) - ret void -} - ; Don't apply the RV optimization to autorelease if there's no retain. ; CHECK: define i8* @test9(i8* %p) @@ -235,45 +224,6 @@ define void @test15() { ret void } -; Convert objc_retain to objc_retainAutoreleasedReturnValue if its -; argument is a return value. - -; CHECK: define void @test16( -; CHECK-NEXT: %y = call i8* @returner() -; CHECK-NEXT: tail call i8* @objc_retainAutoreleasedReturnValue(i8* %y) [[NUW]] -; CHECK-NEXT: ret void -define void @test16() { - %y = call i8* @returner() - call i8* @objc_retain(i8* %y) - ret void -} - -; Don't convert objc_retain to objc_retainAutoreleasedReturnValue if its -; argument is not a return value. - -; CHECK: define void @test17( -; CHECK-NEXT: tail call i8* @objc_retain(i8* %y) [[NUW]] -; CHECK-NEXT: ret void -define void @test17(i8* %y) { - call i8* @objc_retain(i8* %y) - ret void -} - -; Don't Convert objc_retain to objc_retainAutoreleasedReturnValue if it -; isn't next to the call providing its return value. - -; CHECK: define void @test18( -; CHECK-NEXT: %y = call i8* @returner() -; CHECK-NEXT: call void @callee() -; CHECK-NEXT: tail call i8* @objc_retain(i8* %y) [[NUW]] -; CHECK-NEXT: ret void -define void @test18() { - %y = call i8* @returner() - call void @callee() - call i8* @objc_retain(i8* %y) - ret void -} - ; Delete autoreleaseRV+retainRV pairs. ; CHECK: define i8* @test19(i8* %p) { |