blob: 1bfb1ab44e133dcddca6fa66e338a22e76446c3a [file] [log] [blame]
//===--- 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