diff --git a/lib/Transforms/Scalar/ObjCARC.cpp b/lib/Transforms/Scalar/ObjCARC.cpp index da74e9c3ec7..4e068d51c8e 100644 --- a/lib/Transforms/Scalar/ObjCARC.cpp +++ b/lib/Transforms/Scalar/ObjCARC.cpp @@ -1158,6 +1158,11 @@ namespace { /// with the "tail" keyword. bool IsTailCallRelease; + /// Partial - True of we've seen an opportunity for partial RR elimination, + /// such as pushing calls into a CFG triangle or into one side of a + /// CFG diamond. + bool Partial; + /// ReleaseMetadata - If the Calls are objc_release calls and they all have /// a clang.imprecise_release tag, this is the metadata tag. MDNode *ReleaseMetadata; @@ -1172,6 +1177,7 @@ namespace { RRInfo() : KnownSafe(false), IsRetainBlock(false), IsTailCallRelease(false), + Partial(false), ReleaseMetadata(0) {} void clear(); @@ -1182,6 +1188,7 @@ void RRInfo::clear() { KnownSafe = false; IsRetainBlock = false; IsTailCallRelease = false; + Partial = false; ReleaseMetadata = 0; Calls.clear(); ReverseInsertPts.clear(); @@ -1272,8 +1279,16 @@ PtrState::Merge(const PtrState &Other, bool TopDown) { if (RRI.IsRetainBlock != Other.RRI.IsRetainBlock) Seq = S_None; + // If we're not in a sequence (anymore), drop all associated state. if (Seq == S_None) { RRI.clear(); + } else if (RRI.Partial || Other.RRI.Partial) { + // If we're doing a merge on a path that's previously seen a partial + // merge, conservatively drop the sequence, to avoid doing partial + // RR elimination. If the branch predicates for the two merge differ, + // mixing them is unsafe. + Seq = S_None; + RRI.clear(); } else { // Conservatively merge the ReleaseMetadata information. if (RRI.ReleaseMetadata != Other.RRI.ReleaseMetadata) @@ -1282,8 +1297,15 @@ PtrState::Merge(const PtrState &Other, bool TopDown) { RRI.KnownSafe = RRI.KnownSafe && Other.RRI.KnownSafe; RRI.IsTailCallRelease = RRI.IsTailCallRelease && Other.RRI.IsTailCallRelease; RRI.Calls.insert(Other.RRI.Calls.begin(), Other.RRI.Calls.end()); - RRI.ReverseInsertPts.insert(Other.RRI.ReverseInsertPts.begin(), - Other.RRI.ReverseInsertPts.end()); + + // Merge the insert point sets. If there are any differences, + // that makes this a partial merge. + RRI.Partial = RRI.ReverseInsertPts.size() != + Other.RRI.ReverseInsertPts.size(); + for (SmallPtrSet::const_iterator + I = Other.RRI.ReverseInsertPts.begin(), + E = Other.RRI.ReverseInsertPts.end(); I != E; ++I) + RRI.Partial |= RRI.ReverseInsertPts.insert(*I); } } diff --git a/test/Transforms/ObjCARC/basic.ll b/test/Transforms/ObjCARC/basic.ll index 575cf42d4e6..861173b9c9e 100644 --- a/test/Transforms/ObjCARC/basic.ll +++ b/test/Transforms/ObjCARC/basic.ll @@ -86,6 +86,37 @@ alt_return: ret void } +; Don't do partial elimination into two different CFG diamonds. + +; CHECK: define void @test1b( +; CHECK: entry: +; CHECK: tail call i8* @objc_retain(i8* %x) nounwind +; CHECK-NOT: @objc_ +; CHECK: if.end5: +; CHECK: tail call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0 +; CHECK-NOT: @objc_ +; CHECK: } +define void @test1b(i8* %x, i1 %p, i1 %q) { +entry: + tail call i8* @objc_retain(i8* %x) nounwind + br i1 %p, label %if.then, label %if.end + +if.then: ; preds = %entry + tail call void @callee() + br label %if.end + +if.end: ; preds = %if.then, %entry + br i1 %q, label %if.then3, label %if.end5 + +if.then3: ; preds = %if.end + tail call void @use_pointer(i8* %x) + br label %if.end5 + +if.end5: ; preds = %if.then3, %if.end + tail call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0 + ret void +} + ; Like test0 but the pointer is passed to an intervening call, ; so the optimization is not safe.