diff options
-rw-r--r-- | lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 33 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/allocas.ll | 97 |
2 files changed, 113 insertions, 17 deletions
diff --git a/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/lib/Transforms/ObjCARC/ObjCARCOpts.cpp index aa21341..91490d1 100644 --- a/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -30,6 +30,7 @@ #include "ObjCARCAliasAnalysis.h" #include "ProvenanceAnalysis.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/Statistic.h" @@ -454,18 +455,8 @@ namespace { /// sequence. SmallPtrSet<Instruction *, 2> ReverseInsertPts; - /// Does this pointer have multiple owners? - /// - /// In the presence of multiple owners with the same provenance caused by - /// allocas, we can not assume that the frontend will emit balanced code - /// since it could put the release on the pointer loaded from the - /// alloca. This confuses the optimizer so we must be more conservative in - /// that case. - bool MultipleOwners; - RRInfo() : - KnownSafe(false), IsTailCallRelease(false), ReleaseMetadata(0), - MultipleOwners(false) {} + KnownSafe(false), IsTailCallRelease(false), ReleaseMetadata(0) {} void clear(); @@ -478,7 +469,6 @@ namespace { void RRInfo::clear() { KnownSafe = false; IsTailCallRelease = false; - MultipleOwners = false; ReleaseMetadata = 0; Calls.clear(); ReverseInsertPts.clear(); @@ -569,7 +559,6 @@ PtrState::Merge(const PtrState &Other, bool TopDown) { RRI.IsTailCallRelease = RRI.IsTailCallRelease && Other.RRI.IsTailCallRelease; RRI.Calls.insert(Other.RRI.Calls.begin(), Other.RRI.Calls.end()); - RRI.MultipleOwners |= Other.RRI.MultipleOwners; // Merge the insert point sets. If there are any differences, // that makes this a partial merge. @@ -1058,6 +1047,9 @@ namespace { bool Changed; ProvenanceAnalysis PA; + // This is used to track if a pointer is stored into an alloca. + DenseSet<const Value *> MultiOwnersSet; + /// A flag indicating whether this optimization pass should run. bool Run; @@ -1943,7 +1935,7 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst, BBState::ptr_iterator I = MyStates.findPtrBottomUpState( StripPointerCastsAndObjCCalls(SI->getValueOperand())); if (I != MyStates.bottom_up_ptr_end()) - I->second.RRI.MultipleOwners = true; + MultiOwnersSet.insert(I->first); } } break; @@ -2515,7 +2507,8 @@ ObjCARCOpt::ConnectTDBUTraversals(DenseMap<const BasicBlock *, BBState> assert(It != Retains.end()); const RRInfo &NewRetainRRI = It->second; KnownSafeTD &= NewRetainRRI.KnownSafe; - MultipleOwners |= NewRetainRRI.MultipleOwners; + MultipleOwners = + MultipleOwners || MultiOwnersSet.count(GetObjCArg(NewRetain)); for (SmallPtrSet<Instruction *, 2>::const_iterator LI = NewRetainRRI.Calls.begin(), LE = NewRetainRRI.Calls.end(); LI != LE; ++LI) { @@ -2903,8 +2896,14 @@ bool ObjCARCOpt::OptimizeSequences(Function &F) { bool NestingDetected = Visit(F, BBStates, Retains, Releases); // Transform. - return PerformCodePlacement(BBStates, Retains, Releases, F.getParent()) && - NestingDetected; + bool AnyPairsCompletelyEliminated = PerformCodePlacement(BBStates, Retains, + Releases, + F.getParent()); + + // Cleanup. + MultiOwnersSet.clear(); + + return AnyPairsCompletelyEliminated && NestingDetected; } /// Check if there is a dependent call earlier that does not have anything in diff --git a/test/Transforms/ObjCARC/allocas.ll b/test/Transforms/ObjCARC/allocas.ll index eabd54d..58740d2d 100644 --- a/test/Transforms/ObjCARC/allocas.ll +++ b/test/Transforms/ObjCARC/allocas.ll @@ -197,6 +197,103 @@ entry: ret void } +; Make sure that if a store is in a different basic block we handle known safe +; conservatively. + + +; CHECK: define void @test2a(i8* %x) +; CHECK: @objc_retain(i8* %x) +; CHECK: @objc_retain(i8* %x) +; CHECK: @objc_release(i8* %y) +; CHECK: @objc_release(i8* %x) +; CHECK: ret void +; CHECK: } +define void @test2a(i8* %x) { +entry: + %A = alloca i8* + store i8* %x, i8** %A, align 8 + %y = load i8** %A + br label %bb1 + +bb1: + br label %bb2 + +bb2: + br label %bb3 + +bb3: + tail call i8* @objc_retain(i8* %x) + tail call i8* @objc_retain(i8* %x) + call void @use_alloca(i8** %A) + call void @objc_release(i8* %y), !clang.imprecise_release !0 + call void @use_pointer(i8* %x) + call void @objc_release(i8* %x), !clang.imprecise_release !0 + ret void +} + +; CHECK: define void @test2b(i8* %x) +; CHECK: @objc_retain(i8* %x) +; CHECK: @objc_retain(i8* %x) +; CHECK: @objc_release(i8* %y) +; CHECK: @objc_release(i8* %x) +; CHECK: ret void +; CHECK: } +define void @test2b(i8* %x) { +entry: + %A = alloca i8* + %gep1 = getelementptr i8** %A, i32 0 + store i8* %x, i8** %gep1, align 8 + %gep2 = getelementptr i8** %A, i32 0 + %y = load i8** %gep2 + br label %bb1 + +bb1: + br label %bb2 + +bb2: + br label %bb3 + +bb3: + tail call i8* @objc_retain(i8* %x) + tail call i8* @objc_retain(i8* %x) + call void @use_alloca(i8** %A) + call void @objc_release(i8* %y), !clang.imprecise_release !0 + call void @use_pointer(i8* %x) + call void @objc_release(i8* %x), !clang.imprecise_release !0 + ret void +} + +; CHECK: define void @test2c(i8* %x) +; CHECK: @objc_retain(i8* %x) +; CHECK: @objc_retain(i8* %x) +; CHECK: @objc_release(i8* %y) +; CHECK: @objc_release(i8* %x) +; CHECK: ret void +; CHECK: } +define void @test2c(i8* %x) { +entry: + %A = alloca i8*, i32 3 + %gep1 = getelementptr i8** %A, i32 2 + store i8* %x, i8** %gep1, align 8 + %gep2 = getelementptr i8** %A, i32 2 + %y = load i8** %gep2 + tail call i8* @objc_retain(i8* %x) + br label %bb1 + +bb1: + br label %bb2 + +bb2: + br label %bb3 + +bb3: + tail call i8* @objc_retain(i8* %x) + call void @use_alloca(i8** %A) + call void @objc_release(i8* %y), !clang.imprecise_release !0 + call void @use_pointer(i8* %x) + call void @objc_release(i8* %x), !clang.imprecise_release !0 + ret void +} !0 = metadata !{} |