| //===--- AliasAnalysis.h - SIL Alias Analysis -------------------*- 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_ANALYSIS_ALIASANALYSIS_H |
| #define SWIFT_SILOPTIMIZER_ANALYSIS_ALIASANALYSIS_H |
| |
| #include "swift/Basic/ValueEnumerator.h" |
| #include "swift/SIL/ApplySite.h" |
| #include "swift/SIL/SILInstruction.h" |
| #include "swift/SILOptimizer/Analysis/Analysis.h" |
| #include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" |
| #include "llvm/ADT/DenseMap.h" |
| |
| using swift::RetainObserveKind; |
| |
| namespace { |
| |
| /// A key used for the AliasAnalysis cache. |
| /// |
| /// This struct represents the argument list to the method 'alias'. The two |
| /// SILValue pointers are mapped to size_t indices because we need an |
| /// efficient way to invalidate them (the mechanism is described below). The |
| /// Type arguments are translated to void* because their underlying storage is |
| /// opaque pointers that never goes away. |
| struct AliasKeyTy { |
| // The SILValue pair: |
| size_t V1, V2; |
| // The TBAAType pair: |
| void *T1, *T2; |
| }; |
| |
| /// A key used for the MemoryBehavior Analysis cache. |
| /// |
| /// The two SILValue pointers are mapped to size_t indices because we need an |
| /// efficient way to invalidate them (the mechanism is described below). The |
| /// RetainObserveKind represents the inspection mode for the memory behavior |
| /// analysis. |
| struct MemBehaviorKeyTy { |
| // The SILValue pair: |
| size_t V1, V2; |
| RetainObserveKind InspectionMode; |
| }; |
| } |
| |
| namespace swift { |
| |
| class SILInstruction; |
| class ValueBase; |
| class SideEffectAnalysis; |
| class EscapeAnalysis; |
| |
| /// This class is a simple wrapper around an alias analysis cache. This is |
| /// needed since we do not have an "analysis" infrastructure. |
| class AliasAnalysis : public SILAnalysis { |
| public: |
| |
| /// This enum describes the different kinds of aliasing relations between |
| /// pointers. |
| /// |
| /// NoAlias: There is never dependence between memory referenced by the two |
| /// pointers. Example: Two pointers pointing to non-overlapping |
| /// memory ranges. |
| /// |
| /// MayAlias: Two pointers might refer to the same memory location. |
| /// |
| /// |
| /// PartialAlias: The two memory locations are known to be overlapping |
| /// but do not start at the same address. |
| /// |
| /// |
| /// MustAlias: The two memory locations always start at exactly the same |
| /// location. The pointers are equal. |
| /// |
| enum class AliasResult : unsigned { |
| NoAlias=0, ///< The two values have no dependencies on each |
| /// other. |
| MayAlias, ///< The two values cannot be proven to alias or |
| /// not alias. Anything could happen. |
| PartialAlias, ///< The two values overlap in a partial manner. |
| MustAlias, ///< The two values are equal. |
| }; |
| |
| private: |
| SILModule *Mod; |
| SideEffectAnalysis *SEA; |
| EscapeAnalysis *EA; |
| |
| using TBAACacheKey = std::pair<SILType, SILType>; |
| |
| /// A cache for the computation of TBAA. True means that the types may |
| /// alias. False means that the types must not alias. |
| /// |
| /// We don't need to invalidate this cache because type aliasing relations |
| /// never change. |
| llvm::DenseMap<TBAACacheKey, bool> TypesMayAliasCache; |
| |
| /// AliasAnalysis value cache. |
| /// |
| /// The alias() method uses this map to cache queries. |
| llvm::DenseMap<AliasKeyTy, AliasResult> AliasCache; |
| |
| using MemoryBehavior = SILInstruction::MemoryBehavior; |
| /// MemoryBehavior value cache. |
| /// |
| /// The computeMemoryBehavior() method uses this map to cache queries. |
| llvm::DenseMap<MemBehaviorKeyTy, MemoryBehavior> MemoryBehaviorCache; |
| |
| /// The AliasAnalysis cache can't directly map a pair of ValueBase pointers |
| /// to alias results because we'd like to be able to remove deleted pointers |
| /// without having to scan the whole map. So, instead of storing pointers we |
| /// map pointers to indices and store the indices. |
| ValueEnumerator<ValueBase*> AliasValueBaseToIndex; |
| |
| /// Same as AliasValueBaseToIndex, map a pointer to the indices for |
| /// MemoryBehaviorCache. |
| /// |
| /// NOTE: we do not use the same ValueEnumerator for the alias cache, |
| /// as when either cache is cleared, we can not clear the ValueEnumerator |
| /// because doing so could give rise to collisions in the other cache. |
| ValueEnumerator<SILNode*> MemoryBehaviorNodeToIndex; |
| |
| AliasResult aliasAddressProjection(SILValue V1, SILValue V2, |
| SILValue O1, SILValue O2); |
| |
| /// Perform an alias query to see if V1, V2 refer to the same values. |
| AliasResult aliasInner(SILValue V1, SILValue V2, |
| SILType TBAAType1 = SILType(), |
| SILType TBAAType2 = SILType()); |
| |
| /// Returns True if memory of type \p T1 and \p T2 may alias. |
| bool typesMayAlias(SILType T1, SILType T2, const SILFunction &F); |
| |
| virtual void handleDeleteNotification(SILNode *node) override { |
| assert(node->isRepresentativeSILNodeInObject()); |
| |
| // The pointer 'node' is going away. We can't scan the whole cache |
| // and remove all of the occurrences of the pointer. Instead we remove |
| // the pointer from the cache that translates pointers to indices. |
| auto value = dyn_cast<ValueBase>(node); |
| if (!value) return; |
| |
| AliasValueBaseToIndex.invalidateValue(value); |
| MemoryBehaviorNodeToIndex.invalidateValue(node); |
| } |
| |
| virtual bool needsNotifications() override { return true; } |
| |
| |
| public: |
| AliasAnalysis(SILModule *M) |
| : SILAnalysis(SILAnalysisKind::Alias), Mod(M), SEA(nullptr), EA(nullptr) { |
| } |
| |
| static bool classof(const SILAnalysis *S) { |
| return S->getKind() == SILAnalysisKind::Alias; |
| } |
| |
| virtual void initialize(SILPassManager *PM) override; |
| |
| /// Perform an alias query to see if V1, V2 refer to the same values. |
| AliasResult alias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(), |
| SILType TBAAType2 = SILType()); |
| |
| /// Convenience method that returns true if V1 and V2 must alias. |
| bool isMustAlias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(), |
| SILType TBAAType2 = SILType()) { |
| return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MustAlias; |
| } |
| |
| /// Convenience method that returns true if V1 and V2 partially alias. |
| bool isPartialAlias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(), |
| SILType TBAAType2 = SILType()) { |
| return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::PartialAlias; |
| } |
| |
| /// Convenience method that returns true if V1, V2 cannot alias. |
| bool isNoAlias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(), |
| SILType TBAAType2 = SILType()) { |
| return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::NoAlias; |
| } |
| |
| /// Convenience method that returns true if V1, V2 may alias. |
| bool isMayAlias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(), |
| SILType TBAAType2 = SILType()) { |
| return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MayAlias; |
| } |
| |
| /// \returns True if the release of the \p Ptr can access memory accessed by |
| /// \p User. |
| bool mayValueReleaseInterfereWithInstruction(SILInstruction *User, |
| SILValue Ptr); |
| |
| /// Use the alias analysis to determine the memory behavior of Inst with |
| /// respect to V. |
| /// |
| /// TODO: When ref count behavior is separated from generic memory behavior, |
| /// the InspectionMode flag will be unnecessary. |
| MemoryBehavior computeMemoryBehavior(SILInstruction *Inst, SILValue V, |
| RetainObserveKind); |
| |
| /// Use the alias analysis to determine the memory behavior of Inst with |
| /// respect to V. |
| /// |
| /// TODO: When ref count behavior is separated from generic memory behavior, |
| /// the InspectionMode flag will be unnecessary. |
| MemoryBehavior computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V, |
| RetainObserveKind); |
| |
| /// Returns true if \p Inst may read from memory in a manner that |
| /// affects V. |
| bool mayReadFromMemory(SILInstruction *Inst, SILValue V) { |
| auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); |
| return B == MemoryBehavior::MayRead || |
| B == MemoryBehavior::MayReadWrite || |
| B == MemoryBehavior::MayHaveSideEffects; |
| } |
| |
| /// Returns true if \p Inst may write to memory in a manner that |
| /// affects V. |
| bool mayWriteToMemory(SILInstruction *Inst, SILValue V) { |
| auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); |
| return B == MemoryBehavior::MayWrite || |
| B == MemoryBehavior::MayReadWrite || |
| B == MemoryBehavior::MayHaveSideEffects; |
| } |
| |
| /// Returns true if \p Inst may read or write to memory in a manner that |
| /// affects V. |
| bool mayReadOrWriteMemory(SILInstruction *Inst, SILValue V) { |
| auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::IgnoreRetains); |
| return MemoryBehavior::None != B; |
| } |
| |
| /// Returns true if Inst may have side effects in a manner that affects V. |
| bool mayHaveSideEffects(SILInstruction *Inst, SILValue V) { |
| auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::ObserveRetains); |
| return B == MemoryBehavior::MayWrite || |
| B == MemoryBehavior::MayReadWrite || |
| B == MemoryBehavior::MayHaveSideEffects; |
| } |
| |
| /// Returns true if Inst may have side effects in a manner that affects |
| /// V. This is independent of whether or not Inst may write to V and is meant |
| /// to encode notions such as ref count modifications. |
| bool mayHavePureSideEffects(SILInstruction *Inst, SILValue V) { |
| auto B = computeMemoryBehavior(Inst, V, RetainObserveKind::ObserveRetains); |
| return MemoryBehavior::MayHaveSideEffects == B; |
| } |
| |
| /// Returns true if \p Ptr may be released in the function call \p FAS. |
| bool canApplyDecrementRefCount(FullApplySite FAS, SILValue Ptr); |
| |
| /// Returns true if \p Ptr may be released by the builtin \p BI. |
| bool canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr); |
| |
| /// Encodes the alias query as a AliasKeyTy. |
| /// The parameters to this function are identical to the parameters of alias() |
| /// and this method serializes them into a key for the alias analysis cache. |
| AliasKeyTy toAliasKey(SILValue V1, SILValue V2, SILType Type1, SILType Type2); |
| |
| /// Encodes the memory behavior query as a MemBehaviorKeyTy. |
| MemBehaviorKeyTy toMemoryBehaviorKey(SILInstruction *V1, SILValue V2, |
| RetainObserveKind K); |
| |
| virtual void invalidate() override { |
| AliasCache.clear(); |
| MemoryBehaviorCache.clear(); |
| } |
| |
| virtual void invalidate(SILFunction *, |
| SILAnalysis::InvalidationKind K) override { |
| invalidate(); |
| } |
| |
| /// Notify the analysis about a newly created function. |
| virtual void notifyAddedOrModifiedFunction(SILFunction *F) override {} |
| |
| /// Notify the analysis about a function which will be deleted from the |
| /// module. |
| virtual void notifyWillDeleteFunction(SILFunction *F) override {} |
| |
| virtual void invalidateFunctionTables() override { } |
| }; |
| |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
| AliasAnalysis::AliasResult R); |
| |
| /// If this value is an address that obeys strict TBAA, return the address type. |
| /// Otherwise, return an empty type. |
| SILType computeTBAAType(SILValue V); |
| |
| /// Check if \p V points to a let-member. |
| /// Nobody can write into let members. |
| bool isLetPointer(SILValue V); |
| |
| } // end namespace swift |
| |
| namespace llvm { |
| template <> struct DenseMapInfo<AliasKeyTy> { |
| static inline AliasKeyTy getEmptyKey() { |
| auto Allone = std::numeric_limits<size_t>::max(); |
| return {0, Allone, nullptr, nullptr}; |
| } |
| static inline AliasKeyTy getTombstoneKey() { |
| auto Allone = std::numeric_limits<size_t>::max(); |
| return {Allone, 0, nullptr, nullptr}; |
| } |
| static unsigned getHashValue(const AliasKeyTy Val) { |
| unsigned H = 0; |
| H ^= DenseMapInfo<size_t>::getHashValue(Val.V1); |
| H ^= DenseMapInfo<size_t>::getHashValue(Val.V2); |
| H ^= DenseMapInfo<void *>::getHashValue(Val.T1); |
| H ^= DenseMapInfo<void *>::getHashValue(Val.T2); |
| return H; |
| } |
| static bool isEqual(const AliasKeyTy LHS, const AliasKeyTy RHS) { |
| return LHS.V1 == RHS.V1 && |
| LHS.V2 == RHS.V2 && |
| LHS.T1 == RHS.T1 && |
| LHS.T2 == RHS.T2; |
| } |
| }; |
| |
| template <> struct DenseMapInfo<MemBehaviorKeyTy> { |
| static inline MemBehaviorKeyTy getEmptyKey() { |
| auto Allone = std::numeric_limits<size_t>::max(); |
| return {0, Allone, RetainObserveKind::RetainObserveKindEnd}; |
| } |
| static inline MemBehaviorKeyTy getTombstoneKey() { |
| auto Allone = std::numeric_limits<size_t>::max(); |
| return {Allone, 0, RetainObserveKind::RetainObserveKindEnd}; |
| } |
| static unsigned getHashValue(const MemBehaviorKeyTy V) { |
| unsigned H = 0; |
| H ^= DenseMapInfo<size_t>::getHashValue(V.V1); |
| H ^= DenseMapInfo<size_t>::getHashValue(V.V2); |
| H ^= DenseMapInfo<int>::getHashValue(static_cast<int>(V.InspectionMode)); |
| return H; |
| } |
| static bool isEqual(const MemBehaviorKeyTy LHS, |
| const MemBehaviorKeyTy RHS) { |
| return LHS.V1 == RHS.V1 && |
| LHS.V2 == RHS.V2 && |
| LHS.InspectionMode == RHS.InspectionMode; |
| } |
| }; |
| } |
| |
| #endif |