| //===--- RefCountState.h - Represents a Reference Count ---------*- C++ -*-===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_SILOPTIMIZER_PASSMANAGER_ARC_REFCOUNTSTATE_H |
| #define SWIFT_SILOPTIMIZER_PASSMANAGER_ARC_REFCOUNTSTATE_H |
| |
| #include "RCStateTransition.h" |
| #include "swift/Basic/type_traits.h" |
| #include "swift/Basic/ImmutablePointerSet.h" |
| #include "swift/SIL/SILInstruction.h" |
| #include "swift/SIL/SILArgument.h" |
| #include "swift/SIL/SILBasicBlock.h" |
| #include "swift/SIL/InstructionUtils.h" |
| #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" |
| #include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h" |
| #include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" |
| #include <algorithm> |
| |
| namespace swift { |
| class AliasAnalysis; |
| } // end namespace swift |
| |
| //===----------------------------------------------------------------------===// |
| // Ref Count State |
| //===----------------------------------------------------------------------===// |
| |
| namespace swift { |
| |
| /// A struct that abstracts over reference counts manipulated by strong_retain, |
| /// retain_value, strong_release, |
| class RefCountState { |
| protected: |
| /// Return the SILValue that represents the RCRoot that we are |
| /// tracking. |
| SILValue RCRoot; |
| |
| /// The last state transition that this RefCountState went through. None if we |
| /// have not see any transition on this ref count yet. |
| RCStateTransition Transition; |
| |
| /// Was the pointer we are tracking known incremented when we visited the |
| /// current increment we are tracking? In that case we know that it is safe |
| /// to move the inner retain over instructions that may decrement ref counts |
| /// since the outer retain will keep the reference counted value alive. |
| bool KnownSafe = false; |
| |
| public: |
| RefCountState() = default; |
| ~RefCountState() = default; |
| RefCountState(const RefCountState &) = default; |
| RefCountState &operator=(const RefCountState &) = default; |
| RefCountState(RefCountState &&) = default; |
| RefCountState &operator=(RefCountState &&) = default; |
| |
| /// Initializes/reinitialized the state for I. If we reinitialize we return |
| /// true. |
| bool initWithMutatorInst(ImmutablePointerSet<SILInstruction> *I, |
| RCIdentityFunctionInfo *RCFI) { |
| assert(I->size() == 1); |
| |
| // Are we already tracking a ref count modification? |
| bool Nested = isTrackingRefCount(); |
| |
| Transition = RCStateTransition(I); |
| assert(Transition.isMutator() && "Expected I to be a mutator!\n"); |
| |
| // Initialize KnownSafe to a conservative false value. |
| KnownSafe = false; |
| |
| // Initialize value. |
| RCRoot = RCFI->getRCIdentityRoot((*I->begin())->getOperand(0)); |
| |
| return Nested; |
| } |
| |
| /// Uninitialize the current state. |
| void clear() { |
| KnownSafe = false; |
| } |
| |
| /// Is this ref count initialized and tracking a ref count ptr. |
| bool isTrackingRefCount() const { return Transition.isValid(); } |
| |
| /// Are we tracking an instruction currently? This returns false when given an |
| /// uninitialized ReferenceCountState. |
| bool isTrackingRefCountInst() const { |
| return Transition.isValid() && Transition.isMutator(); |
| } |
| |
| /// Are we tracking a source of ref counts? This currently means that we are |
| /// tracking an argument that is @owned. In the future this will include |
| /// return values of functions that are @owned. |
| bool isTrackingRefCountSource() const { |
| return Transition.isValid() && Transition.isEndPoint(); |
| } |
| |
| /// Return the increment we are tracking. |
| RCStateTransition::mutator_range getInstructions() const { |
| return Transition.getMutators(); |
| } |
| |
| /// Returns true if I is in the instructions we are tracking. |
| bool containsInstruction(SILInstruction *I) const { |
| return Transition.isValid() && Transition.containsMutator(I); |
| } |
| |
| /// Return the value with reference semantics that is the operand of our |
| /// increment. |
| SILValue getRCRoot() const { |
| assert(RCRoot && "Value should never be null here"); |
| return RCRoot; |
| } |
| |
| /// Returns true if we have a valid value that we are tracking. |
| bool hasRCRoot() const { |
| return (bool)RCRoot; |
| } |
| |
| /// This retain is known safe if the operand we are tracking was already known |
| /// incremented previously. This occurs when you have nested increments. |
| bool isKnownSafe() const { return KnownSafe; } |
| |
| /// Set KnownSafe to true if \p NewValue is true. If \p NewValue is false, |
| /// this is a no-op. |
| void updateKnownSafe(bool NewValue) { |
| KnownSafe |= NewValue; |
| } |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // Bottom Up Ref Count State |
| //===----------------------------------------------------------------------===// |
| |
| class BottomUpRefCountState : public RefCountState { |
| public: |
| /// Sequence of states that a value with reference semantics can go through |
| /// when visiting decrements bottom up. The reason why I have this separate |
| /// from TopDownSubstruct is I think it gives more clarity to the algorithm by |
| /// giving it typed form. |
| enum class LatticeState { |
| None, ///< The pointer has no information associated with it. |
| Decremented, ///< The pointer will be decremented. |
| MightBeUsed, ///< The pointer will be used and then at this point |
| /// be decremented |
| MightBeDecremented, ///< The pointer might be decremented again implying |
| /// that we cannot, without being known safe remove |
| /// this decrement. |
| }; |
| |
| private: |
| using SuperTy = RefCountState; |
| |
| /// Current place in the sequence of the value. |
| LatticeState LatState = LatticeState::None; |
| |
| /// True if we have seen a NonARCUser of this instruction. This means bottom |
| /// up assuming we have this property as a meet over all paths property, we |
| /// know that all releases we see are known safe. |
| bool FoundNonARCUser = false; |
| |
| public: |
| BottomUpRefCountState() = default; |
| ~BottomUpRefCountState() = default; |
| BottomUpRefCountState(const BottomUpRefCountState &) = default; |
| BottomUpRefCountState &operator=(const BottomUpRefCountState &) = default; |
| BottomUpRefCountState(BottomUpRefCountState &&) = default; |
| BottomUpRefCountState &operator=(BottomUpRefCountState &&) = default; |
| |
| /// Return true if the release can be moved to the retain. |
| bool isCodeMotionSafe() const { |
| return LatState != LatticeState::MightBeDecremented; |
| } |
| |
| /// Initializes/reinitialized the state for I. If we reinitialize we return |
| /// true. |
| bool initWithMutatorInst(ImmutablePointerSet<SILInstruction> *I, |
| RCIdentityFunctionInfo *RCFI); |
| |
| /// Update this reference count's state given the instruction \p I. \p |
| /// InsertPt is the point furthest up the CFG where we can move the currently |
| /// tracked reference count. |
| void |
| updateForSameLoopInst(SILInstruction *I, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Update this reference count's state given the instruction \p I. \p |
| /// InsertPts are the points furthest up the CFG where we can move the |
| /// currently tracked reference count. |
| // |
| /// The main difference in between this routine and update for same loop inst |
| /// is that if we see any decrements on a value, we treat it as being |
| /// guaranteed used. We treat any uses as regular uses. |
| void updateForDifferentLoopInst( |
| SILInstruction *I, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| // Determine the conservative effect of the given list of predecessor |
| // terminators upon this reference count. |
| void updateForPredTerminators( |
| ArrayRef<SILInstruction *> PredTerms, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Attempt to merge \p Other into this ref count state. Return true if we |
| /// succeed and false otherwise. |
| bool merge(const BottomUpRefCountState &Other); |
| |
| /// Returns true if the passed in ref count inst matches the ref count inst |
| /// we are tracking. This handles generically retains/release. |
| bool isRefCountInstMatchedToTrackedInstruction(SILInstruction *RefCountInst); |
| |
| /// Uninitialize the current state. |
| void clear(); |
| |
| private: |
| /// Return true if we *might* remove this instruction. |
| /// |
| /// This is a conservative query given the information we know, so as we |
| /// perform the dataflow it may change value. |
| bool mightRemoveMutators(); |
| |
| /// Can we guarantee that the given reference counted value has been modified? |
| bool isRefCountStateModified() const; |
| |
| /// Returns true if given the current lattice state, do we care if the value |
| /// we are tracking is decremented. |
| bool valueCanBeDecrementedGivenLatticeState() const; |
| |
| /// If advance the state's sequence appropriately for a decrement. If we do |
| /// advance return true. Otherwise return false. |
| bool handleDecrement(); |
| |
| /// Check if PotentialDecrement can decrement the reference count associated |
| /// with the value we are tracking. If so advance the state's sequence |
| /// appropriately and return true. Otherwise return false. |
| bool handlePotentialDecrement(SILInstruction *Decrement, AliasAnalysis *AA); |
| |
| /// Returns true if given the current lattice state, do we care if the value |
| /// we are tracking is used. |
| bool valueCanBeUsedGivenLatticeState() const; |
| |
| /// Given the current lattice state, if we have seen a use, advance the |
| /// lattice state. Return true if we do so and false otherwise. \p InsertPt is |
| /// the location where if \p PotentialUser is a user of this ref count, we |
| /// would insert a release. |
| bool handleUser(SILValue RCIdentity, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Check if PotentialUser could be a use of the reference counted value that |
| /// requires user to be alive. If so advance the state's sequence |
| /// appropriately and return true. Otherwise return false. |
| bool |
| handlePotentialUser(SILInstruction *PotentialUser, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Returns true if given the current lattice state, do we care if the value |
| /// we are tracking is used. |
| bool valueCanBeGuaranteedUsedGivenLatticeState() const; |
| |
| /// Given the current lattice state, if we have seen a use, advance the |
| /// lattice state. Return true if we do so and false otherwise. \p InsertPt is |
| /// the location where if \p PotentialUser is a user of this ref count, we |
| /// would insert a release. |
| bool |
| handleGuaranteedUser(SILValue RCIdentity, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Check if PotentialGuaranteedUser can use the reference count associated |
| /// with the value we are tracking. If so advance the state's sequence |
| /// appropriately and return true. Otherwise return false. |
| bool handlePotentialGuaranteedUser( |
| SILInstruction *User, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// We have a matching ref count inst. Return true if we advance the sequence |
| /// and false otherwise. |
| bool handleRefCountInstMatch(SILInstruction *RefCountInst); |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // Top Down Ref Count State |
| //===----------------------------------------------------------------------===// |
| |
| class TopDownRefCountState : public RefCountState { |
| public: |
| /// Sequence of states that a value with reference semantics can go through |
| /// when visiting decrements bottom up. The reason why I have this separate |
| /// from BottomUpRefCountState is I think it gives more clarity to the |
| /// algorithm by giving it typed form. |
| enum class LatticeState { |
| None, ///< The pointer has no information associated with it. |
| Incremented, ///< The pointer has been incremented. |
| MightBeDecremented, ///< The pointer has been incremented and might be |
| /// decremented. be decremented again implying |
| MightBeUsed, ///< The pointer has been incremented, |
| }; |
| |
| private: |
| using SuperTy = RefCountState; |
| |
| /// Current place in the sequence of the value. |
| LatticeState LatState = LatticeState::None; |
| |
| public: |
| TopDownRefCountState() = default; |
| ~TopDownRefCountState() = default; |
| TopDownRefCountState(const TopDownRefCountState &) = default; |
| TopDownRefCountState &operator=(const TopDownRefCountState &) = default; |
| TopDownRefCountState(TopDownRefCountState &&) = default; |
| TopDownRefCountState &operator=(TopDownRefCountState &&) = default; |
| |
| /// Return true if the retain can be moved to the release. |
| bool isCodeMotionSafe() const { |
| return LatState != LatticeState::MightBeUsed; |
| } |
| |
| /// Initializes/reinitialized the state for I. If we reinitialize we return |
| /// true. |
| bool initWithMutatorInst(ImmutablePointerSet<SILInstruction> *I, |
| RCIdentityFunctionInfo *RCFI); |
| |
| /// Initialize the state given the consumed argument Arg. |
| void initWithArg(SILFunctionArgument *Arg); |
| |
| /// Initialize this RefCountState with an instruction which introduces a new |
| /// ref count at +1. |
| void initWithEntranceInst(ImmutablePointerSet<SILInstruction> *I, |
| SILValue RCIdentity); |
| |
| /// Uninitialize the current state. |
| void clear(); |
| |
| /// Update this reference count's state given the instruction \p I. \p |
| /// InsertPt is the point furthest up the CFG where we can move the currently |
| /// tracked reference count. |
| void |
| updateForSameLoopInst(SILInstruction *I, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Update this reference count's state given the instruction \p I. \p |
| /// InsertPts are the points furthest up the CFG where we can move the |
| /// currently tracked reference count. |
| /// |
| /// The main difference in between this routine and update for same loop inst |
| /// is that if we see any decrements on a value, we treat it as being |
| /// guaranteed used. We treat any uses as regular uses. |
| void updateForDifferentLoopInst( |
| SILInstruction *I, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Returns true if the passed in ref count inst matches the ref count inst |
| /// we are tracking. This handles generically retains/release. |
| bool isRefCountInstMatchedToTrackedInstruction(SILInstruction *RefCountInst); |
| |
| /// Attempt to merge \p Other into this ref count state. Return true if we |
| /// succeed and false otherwise. |
| bool merge(const TopDownRefCountState &Other); |
| |
| private: |
| /// Can we guarantee that the given reference counted value has been modified? |
| bool isRefCountStateModified() const; |
| |
| /// Returns true if given the current lattice state, do we care if the value |
| /// we are tracking is decremented. |
| bool valueCanBeDecrementedGivenLatticeState() const; |
| |
| /// If advance the state's sequence appropriately for a decrement. If we do |
| /// advance return true. Otherwise return false. |
| bool handleDecrement(SILInstruction *PotentialDecrement, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory); |
| |
| /// Check if PotentialDecrement can decrement the reference count associated |
| /// with the value we are tracking. If so advance the state's sequence |
| /// appropriately and return true. Otherwise return false. |
| bool handlePotentialDecrement( |
| SILInstruction *PotentialDecrement, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Returns true if given the current lattice state, do we care if the value |
| /// we are tracking is used. |
| bool valueCanBeUsedGivenLatticeState() const; |
| |
| /// Given the current lattice state, if we have seen a use, advance the |
| /// lattice state. Return true if we do so and false otherwise. |
| bool handleUser(SILInstruction *PotentialUser, |
| SILValue RCIdentity, AliasAnalysis *AA); |
| |
| /// Check if PotentialUser could be a use of the reference counted value that |
| /// requires user to be alive. If so advance the state's sequence |
| /// appropriately and return true. Otherwise return false. |
| bool handlePotentialUser(SILInstruction *PotentialUser, AliasAnalysis *AA); |
| |
| /// Returns true if given the current lattice state, do we care if the value |
| /// we are tracking is used. |
| bool valueCanBeGuaranteedUsedGivenLatticeState() const; |
| |
| /// Given the current lattice state, if we have seen a use, advance the |
| /// lattice state. Return true if we do so and false otherwise. |
| bool |
| handleGuaranteedUser(SILInstruction *PotentialGuaranteedUser, |
| SILValue RCIdentity, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// Check if PotentialGuaranteedUser can use the reference count associated |
| /// with the value we are tracking. If so advance the state's sequence |
| /// appropriately and return true. Otherwise return false. |
| bool handlePotentialGuaranteedUser( |
| SILInstruction *PotentialGuaranteedUser, |
| ImmutablePointerSetFactory<SILInstruction> &SetFactory, |
| AliasAnalysis *AA); |
| |
| /// We have a matching ref count inst. Return true if we advance the sequence |
| /// and false otherwise. |
| bool handleRefCountInstMatch(SILInstruction *RefCountInst); |
| }; |
| |
| // These static asserts are here for performance reasons. |
| static_assert(IsTriviallyCopyable<BottomUpRefCountState>::value, |
| "All ref count states must be trivially copyable"); |
| static_assert(IsTriviallyCopyable<TopDownRefCountState>::value, |
| "All ref count states must be trivially copyable"); |
| |
| } // end swift namespace |
| |
| namespace llvm { |
| |
| raw_ostream &operator<<(raw_ostream &OS, |
| swift::BottomUpRefCountState::LatticeState S); |
| raw_ostream &operator<<(raw_ostream &OS, |
| swift::TopDownRefCountState::LatticeState S); |
| |
| } // end namespace llvm |
| |
| #endif |