//===--- 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/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; /// 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 TypesMayAliasCache; /// AliasAnalysis value cache. /// /// The alias() method uses this map to cache queries. llvm::DenseMap AliasCache; using MemoryBehavior = SILInstruction::MemoryBehavior; /// MemoryBehavior value cache. /// /// The computeMemoryBehavior() method uses this map to cache queries. llvm::DenseMap 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 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 MemoryBehaviorValueBaseToIndex; 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); virtual void handleDeleteNotification(ValueBase *I) override { // The pointer I 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. AliasValueBaseToIndex.invalidateValue(I); MemoryBehaviorValueBaseToIndex.invalidateValue(I); } virtual bool needsNotifications() override { return true; } public: AliasAnalysis(SILModule *M) : SILAnalysis(AnalysisKind::Alias), Mod(M), SEA(nullptr), EA(nullptr) {} static bool classof(const SILAnalysis *S) { return S->getKind() == AnalysisKind::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(SILValue 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 notifyAddFunction(SILFunction *F) override { } /// Notify the analysis about a function which will be deleted from the /// module. virtual void notifyDeleteFunction(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 llvm::DenseMapInfo { static inline AliasKeyTy getEmptyKey() { auto Allone = std::numeric_limits::max(); return {0, Allone, nullptr, nullptr}; } static inline AliasKeyTy getTombstoneKey() { auto Allone = std::numeric_limits::max(); return {Allone, 0, nullptr, nullptr}; } static unsigned getHashValue(const AliasKeyTy Val) { unsigned H = 0; H ^= DenseMapInfo::getHashValue(Val.V1); H ^= DenseMapInfo::getHashValue(Val.V2); H ^= DenseMapInfo::getHashValue(Val.T1); H ^= DenseMapInfo::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 llvm::DenseMapInfo { static inline MemBehaviorKeyTy getEmptyKey() { auto Allone = std::numeric_limits::max(); return {0, Allone, RetainObserveKind::RetainObserveKindEnd}; } static inline MemBehaviorKeyTy getTombstoneKey() { auto Allone = std::numeric_limits::max(); return {Allone, 0, RetainObserveKind::RetainObserveKindEnd}; } static unsigned getHashValue(const MemBehaviorKeyTy V) { unsigned H = 0; H ^= DenseMapInfo::getHashValue(V.V1); H ^= DenseMapInfo::getHashValue(V.V2); H ^= DenseMapInfo::getHashValue(static_cast(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