-namespace {
- /// \enum Sequence
- ///
- /// \brief A sequence of states that a pointer may go through in which an
- /// objc_retain and objc_release are actually needed.
- enum Sequence {
- S_None,
- S_Retain, ///< objc_retain(x).
- S_CanRelease, ///< foo(x) -- x could possibly see a ref count decrement.
- S_Use, ///< any use of x.
- S_Stop, ///< like S_Release, but code motion is stopped.
- S_Release, ///< objc_release(x).
- S_MovableRelease ///< objc_release(x), !clang.imprecise_release.
- };
-
- raw_ostream &operator<<(raw_ostream &OS, const Sequence S)
- LLVM_ATTRIBUTE_UNUSED;
- raw_ostream &operator<<(raw_ostream &OS, const Sequence S) {
- switch (S) {
- case S_None:
- return OS << "S_None";
- case S_Retain:
- return OS << "S_Retain";
- case S_CanRelease:
- return OS << "S_CanRelease";
- case S_Use:
- return OS << "S_Use";
- case S_Release:
- return OS << "S_Release";
- case S_MovableRelease:
- return OS << "S_MovableRelease";
- case S_Stop:
- return OS << "S_Stop";
- }
- llvm_unreachable("Unknown sequence type.");
- }
-}
-
-static Sequence MergeSeqs(Sequence A, Sequence B, bool TopDown) {
- // The easy cases.
- if (A == B)
- return A;
- if (A == S_None || B == S_None)
- return S_None;
-
- if (A > B) std::swap(A, B);
- if (TopDown) {
- // Choose the side which is further along in the sequence.
- if ((A == S_Retain || A == S_CanRelease) &&
- (B == S_CanRelease || B == S_Use))
- return B;
- } else {
- // Choose the side which is further along in the sequence.
- if ((A == S_Use || A == S_CanRelease) &&
- (B == S_Use || B == S_Release || B == S_Stop || B == S_MovableRelease))
- return A;
- // If both sides are releases, choose the more conservative one.
- if (A == S_Stop && (B == S_Release || B == S_MovableRelease))
- return A;
- if (A == S_Release && B == S_MovableRelease)
- return A;
- }
-
- return S_None;
-}
-
-namespace {
- /// \brief Unidirectional information about either a
- /// retain-decrement-use-release sequence or release-use-decrement-retain
- /// reverse sequence.
- struct RRInfo {
- /// After an objc_retain, the reference count of the referenced
- /// object is known to be positive. Similarly, before an objc_release, the
- /// reference count of the referenced object is known to be positive. If
- /// there are retain-release pairs in code regions where the retain count
- /// is known to be positive, they can be eliminated, regardless of any side
- /// effects between them.
- ///
- /// Also, a retain+release pair nested within another retain+release
- /// pair all on the known same pointer value can be eliminated, regardless
- /// of any intervening side effects.
- ///
- /// KnownSafe is true when either of these conditions is satisfied.
- bool KnownSafe;
-
- /// True of the objc_release calls are all marked with the "tail" keyword.
- bool IsTailCallRelease;
-
- /// If the Calls are objc_release calls and they all have a
- /// clang.imprecise_release tag, this is the metadata tag.
- MDNode *ReleaseMetadata;
-
- /// For a top-down sequence, the set of objc_retains or
- /// objc_retainBlocks. For bottom-up, the set of objc_releases.
- SmallPtrSet<Instruction *, 2> Calls;
-
- /// The set of optimal insert positions for moving calls in the opposite
- /// sequence.
- SmallPtrSet<Instruction *, 2> ReverseInsertPts;
-
- /// If this is true, we cannot perform code motion but can still remove
- /// retain/release pairs.
- bool CFGHazardAfflicted;
-
- RRInfo() :
- KnownSafe(false), IsTailCallRelease(false), ReleaseMetadata(0),
- CFGHazardAfflicted(false) {}
-
- void clear();
-
- /// Conservatively merge the two RRInfo. Returns true if a partial merge has
- /// occured, false otherwise.
- bool Merge(const RRInfo &Other);
-
- };
-}
-
-void RRInfo::clear() {
- KnownSafe = false;
- IsTailCallRelease = false;
- ReleaseMetadata = 0;
- Calls.clear();
- ReverseInsertPts.clear();
- CFGHazardAfflicted = false;
-}
-
-bool RRInfo::Merge(const RRInfo &Other) {
- // Conservatively merge the ReleaseMetadata information.
- if (ReleaseMetadata != Other.ReleaseMetadata)
- ReleaseMetadata = 0;
-
- // Conservatively merge the boolean state.
- KnownSafe &= Other.KnownSafe;
- IsTailCallRelease &= Other.IsTailCallRelease;
- CFGHazardAfflicted |= Other.CFGHazardAfflicted;
-
- // Merge the call sets.
- Calls.insert(Other.Calls.begin(), Other.Calls.end());
-
- // Merge the insert point sets. If there are any differences,
- // that makes this a partial merge.
- bool Partial = ReverseInsertPts.size() != Other.ReverseInsertPts.size();
- for (SmallPtrSet<Instruction *, 2>::const_iterator
- I = Other.ReverseInsertPts.begin(),
- E = Other.ReverseInsertPts.end(); I != E; ++I)
- Partial |= ReverseInsertPts.insert(*I);
- return Partial;
-}
-
-namespace {
- /// \brief This class summarizes several per-pointer runtime properties which
- /// are propogated through the flow graph.
- class PtrState {
- /// True if the reference count is known to be incremented.
- bool KnownPositiveRefCount;
-
- /// True if 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;
-
- /// The current position in the sequence.
- Sequence Seq : 8;
-
- /// Unidirectional information about the current sequence.
- RRInfo RRI;
-
- public:
- PtrState() : KnownPositiveRefCount(false), Partial(false),
- Seq(S_None) {}
-
-
- bool IsKnownSafe() const {
- return RRI.KnownSafe;
- }
-
- void SetKnownSafe(const bool NewValue) {
- RRI.KnownSafe = NewValue;
- }
-
- bool IsTailCallRelease() const {
- return RRI.IsTailCallRelease;
- }
-
- void SetTailCallRelease(const bool NewValue) {
- RRI.IsTailCallRelease = NewValue;
- }
-
- bool IsTrackingImpreciseReleases() const {
- return RRI.ReleaseMetadata != 0;
- }
-
- const MDNode *GetReleaseMetadata() const {
- return RRI.ReleaseMetadata;
- }
-
- void SetReleaseMetadata(MDNode *NewValue) {
- RRI.ReleaseMetadata = NewValue;
- }
-
- bool IsCFGHazardAfflicted() const {
- return RRI.CFGHazardAfflicted;
- }
-
- void SetCFGHazardAfflicted(const bool NewValue) {
- RRI.CFGHazardAfflicted = NewValue;
- }
-
- void SetKnownPositiveRefCount() {
- DEBUG(dbgs() << "Setting Known Positive.\n");
- KnownPositiveRefCount = true;
- }
-
- void ClearKnownPositiveRefCount() {
- DEBUG(dbgs() << "Clearing Known Positive.\n");
- KnownPositiveRefCount = false;
- }
-
- bool HasKnownPositiveRefCount() const {
- return KnownPositiveRefCount;
- }
-
- void SetSeq(Sequence NewSeq) {
- DEBUG(dbgs() << "Old: " << Seq << "; New: " << NewSeq << "\n");
- Seq = NewSeq;
- }
-
- Sequence GetSeq() const {
- return Seq;
- }
-
- void ClearSequenceProgress() {
- ResetSequenceProgress(S_None);
- }
-
- void ResetSequenceProgress(Sequence NewSeq) {
- DEBUG(dbgs() << "Resetting sequence progress.\n");
- SetSeq(NewSeq);
- Partial = false;
- RRI.clear();
- }
-
- void Merge(const PtrState &Other, bool TopDown);
-
- void InsertCall(Instruction *I) {
- RRI.Calls.insert(I);
- }
-
- void InsertReverseInsertPt(Instruction *I) {
- RRI.ReverseInsertPts.insert(I);
- }
-
- void ClearReverseInsertPts() {
- RRI.ReverseInsertPts.clear();
- }
-
- bool HasReverseInsertPts() const {
- return !RRI.ReverseInsertPts.empty();
- }
-
- const RRInfo &GetRRInfo() const {
- return RRI;
- }
- };
-}
-
-void
-PtrState::Merge(const PtrState &Other, bool TopDown) {
- Seq = MergeSeqs(Seq, Other.Seq, TopDown);
- KnownPositiveRefCount &= Other.KnownPositiveRefCount;
-
- // If we're not in a sequence (anymore), drop all associated state.
- if (Seq == S_None) {
- Partial = false;
- RRI.clear();
- } else if (Partial || Other.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.
- ClearSequenceProgress();
- } else {
- // Otherwise merge the other PtrState's RRInfo into our RRInfo. At this
- // point, we know that currently we are not partial. Stash whether or not
- // the merge operation caused us to undergo a partial merging of reverse
- // insertion points.
- Partial = RRI.Merge(Other.RRI);
- }
-}
-