mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
AliasAnalysis: make AliasAnalysis a function analysis and simplify the cache keys
Instead of caching alias results globally for the module, make AliasAnalysis a FunctionAnalysisBase which caches the alias results per function.
Why?
* So far the result caches could only grow. They were reset when they reached a certain size. This was not ideal. Now, they are invalidated whenever the function changes.
* It was not possible to actually invalidate an alias analysis result. This is required, for example in TempRValueOpt and TempLValueOpt (so far it was done manually with invalidateInstruction).
* Type based alias analysis results were also cached for the whole module, while it is actually dependent on the function, because it depends on the function's resilience expansion. This was a potential bug.
I also added a new PassManager API to directly get a function-base analysis:
getAnalysis(SILFunction *f)
The second change of this commit is the removal of the instruction-index indirection for the cache keys. Now the cache keys directly work on instruction pointers instead of instruction indices. This reduces the number of hash table lookups for a cache lookup from 3 to 1.
This indirection was needed to avoid dangling instruction pointers in the cache keys. But this is not needed anymore, because of the new delayed instruction deletion mechanism.
This commit is contained in:
@@ -13,53 +13,19 @@
|
||||
#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 integer 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:
|
||||
swift::ValueIndexTy V1, V2;
|
||||
// The TBAAType pair:
|
||||
void *T1, *T2;
|
||||
};
|
||||
|
||||
/// A key used for the MemoryBehavior Analysis cache.
|
||||
///
|
||||
/// The two SILValue pointers are mapped to integer 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:
|
||||
swift::ValueIndexTy V1, V2;
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
class AliasAnalysis {
|
||||
public:
|
||||
|
||||
/// This enum describes the different kinds of aliasing relations between
|
||||
@@ -89,12 +55,28 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
SILModule *Mod;
|
||||
SideEffectAnalysis *SEA;
|
||||
EscapeAnalysis *EA;
|
||||
/// A key used for the AliasAnalysis cache.
|
||||
///
|
||||
/// This struct represents the argument list to the method 'alias'.
|
||||
struct AliasCacheKey {
|
||||
// The SILValue pair:
|
||||
SILValue V1, V2;
|
||||
// The TBAAType pair:
|
||||
void *T1, *T2;
|
||||
};
|
||||
|
||||
friend struct ::llvm::DenseMapInfo<swift::AliasAnalysis::AliasCacheKey>;
|
||||
|
||||
/// A key used for the MemoryBehavior Analysis cache.
|
||||
using MemBehaviorCacheKey = std::pair<SILValue, SILInstruction *>;
|
||||
|
||||
using ScopeCacheKey = std::pair<SILInstruction *, SILInstruction *>;
|
||||
|
||||
using TBAACacheKey = std::pair<SILType, SILType>;
|
||||
|
||||
SideEffectAnalysis *SEA;
|
||||
EscapeAnalysis *EA;
|
||||
|
||||
/// A cache for the computation of TBAA. True means that the types may
|
||||
/// alias. False means that the types must not alias.
|
||||
///
|
||||
@@ -105,33 +87,27 @@ private:
|
||||
/// AliasAnalysis value cache.
|
||||
///
|
||||
/// The alias() method uses this map to cache queries.
|
||||
llvm::DenseMap<AliasKeyTy, AliasResult> AliasCache;
|
||||
llvm::DenseMap<AliasCacheKey, AliasResult> AliasCache;
|
||||
|
||||
using MemoryBehavior = SILInstruction::MemoryBehavior;
|
||||
|
||||
/// MemoryBehavior value cache.
|
||||
///
|
||||
/// The computeMemoryBehavior() method uses this map to cache queries.
|
||||
llvm::DenseMap<MemBehaviorKeyTy, MemoryBehavior> MemoryBehaviorCache;
|
||||
llvm::DenseMap<MemBehaviorCacheKey, MemoryBehavior> MemoryBehaviorCache;
|
||||
|
||||
/// Set of instructions inside immutable-scopes.
|
||||
///
|
||||
/// Contains pairs of intruction indices: the first instruction is the begin-
|
||||
/// scope instruction (e.g. begin_access), the second instruction is an
|
||||
/// Contains pairs of intructions: the first instruction is the begin-scope
|
||||
/// instruction (e.g. begin_access), the second instruction is an
|
||||
/// instruction inside the scope (only may-write instructions are considered).
|
||||
llvm::DenseSet<MemBehaviorKeyTy> instsInImmutableScopes;
|
||||
llvm::DenseSet<ScopeCacheKey> instsInImmutableScopes;
|
||||
|
||||
/// Computed immutable scopes.
|
||||
///
|
||||
/// Contains the begin-scope instruction's indices (e.g. begin_access) of
|
||||
/// all computed scopes.
|
||||
llvm::DenseSet<ValueIndexTy> immutableScopeComputed;
|
||||
|
||||
/// The caches can't directly map a pair of value/instruction pointers
|
||||
/// to results because we'd like to be able to remove deleted instruction
|
||||
/// pointers without having to scan the whole map. So, instead of storing
|
||||
/// pointers we map pointers to indices and store the indices.
|
||||
ValueEnumerator<ValueBase *> ValueToIndex;
|
||||
ValueEnumerator<SILInstruction *> InstructionToIndex;
|
||||
/// Contains the begin-scope instructions (e.g. begin_access) of all computed
|
||||
/// scopes.
|
||||
llvm::SmallPtrSet<SILInstruction *, 16> immutableScopeComputed;
|
||||
|
||||
AliasResult aliasAddressProjection(SILValue V1, SILValue V2,
|
||||
SILValue O1, SILValue O2);
|
||||
@@ -144,34 +120,15 @@ private:
|
||||
/// 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;
|
||||
|
||||
virtual bool needsNotifications() override { return true; }
|
||||
|
||||
void computeImmutableScope(SingleValueInstruction *beginScopeInst,
|
||||
ValueIndexTy beginIdx);
|
||||
void computeImmutableScope(SingleValueInstruction *beginScopeInst);
|
||||
|
||||
bool isInImmutableScope(SILInstruction *inst, SILValue V);
|
||||
|
||||
public:
|
||||
AliasAnalysis(SILModule *M)
|
||||
: SILAnalysis(SILAnalysisKind::Alias), Mod(M), SEA(nullptr), EA(nullptr) {
|
||||
}
|
||||
AliasAnalysis(SideEffectAnalysis *SEA, EscapeAnalysis *EA)
|
||||
: SEA(SEA), EA(EA) {}
|
||||
|
||||
static bool classof(const SILAnalysis *S) {
|
||||
return S->getKind() == SILAnalysisKind::Alias;
|
||||
}
|
||||
|
||||
virtual void initialize(SILPassManager *PM) override;
|
||||
|
||||
/// Explicitly invalidate an instruction.
|
||||
///
|
||||
/// This can be useful to update the alias analysis within a pass.
|
||||
/// It's needed if e.g. \p inst is an address projection and its operand gets
|
||||
/// replaced with a different underlying object.
|
||||
void invalidateInstruction(SILInstruction *inst) {
|
||||
handleDeleteNotification(inst->asSILNode());
|
||||
}
|
||||
static SILAnalysisKind getAnalysisKind() { return SILAnalysisKind::Alias; }
|
||||
|
||||
/// Perform an alias query to see if V1, V2 refer to the same values.
|
||||
AliasResult alias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(),
|
||||
@@ -249,37 +206,6 @@ public:
|
||||
|
||||
/// 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);
|
||||
|
||||
virtual void invalidate() override {
|
||||
AliasCache.clear();
|
||||
MemoryBehaviorCache.clear();
|
||||
InstructionToIndex.clear();
|
||||
ValueToIndex.clear();
|
||||
instsInImmutableScopes.clear();
|
||||
immutableScopeComputed.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 { }
|
||||
};
|
||||
|
||||
|
||||
@@ -293,51 +219,32 @@ SILType computeTBAAType(SILValue V);
|
||||
} // end namespace swift
|
||||
|
||||
namespace llvm {
|
||||
template <> struct DenseMapInfo<AliasKeyTy> {
|
||||
static inline AliasKeyTy getEmptyKey() {
|
||||
auto Allone = std::numeric_limits<swift::ValueIndexTy>::max();
|
||||
return {0, Allone, nullptr, nullptr};
|
||||
}
|
||||
static inline AliasKeyTy getTombstoneKey() {
|
||||
auto Allone = std::numeric_limits<swift::ValueIndexTy>::max();
|
||||
return {Allone, 0, nullptr, nullptr};
|
||||
}
|
||||
static unsigned getHashValue(const AliasKeyTy Val) {
|
||||
unsigned H = 0;
|
||||
H ^= DenseMapInfo<swift::ValueIndexTy>::getHashValue(Val.V1);
|
||||
H ^= DenseMapInfo<swift::ValueIndexTy>::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<swift::AliasAnalysis::AliasCacheKey> {
|
||||
using AliasCacheKey = swift::AliasAnalysis::AliasCacheKey;
|
||||
|
||||
template <> struct DenseMapInfo<MemBehaviorKeyTy> {
|
||||
static inline MemBehaviorKeyTy getEmptyKey() {
|
||||
auto Allone = std::numeric_limits<swift::ValueIndexTy>::max();
|
||||
return {0, Allone};
|
||||
}
|
||||
static inline MemBehaviorKeyTy getTombstoneKey() {
|
||||
auto Allone = std::numeric_limits<swift::ValueIndexTy>::max();
|
||||
return {Allone, 0};
|
||||
}
|
||||
static unsigned getHashValue(const MemBehaviorKeyTy V) {
|
||||
unsigned H = 0;
|
||||
H ^= DenseMapInfo<swift::ValueIndexTy>::getHashValue(V.V1);
|
||||
H ^= DenseMapInfo<swift::ValueIndexTy>::getHashValue(V.V2);
|
||||
return H;
|
||||
}
|
||||
static bool isEqual(const MemBehaviorKeyTy LHS,
|
||||
const MemBehaviorKeyTy RHS) {
|
||||
return LHS.V1 == RHS.V1 && LHS.V2 == RHS.V2;
|
||||
}
|
||||
};
|
||||
static inline AliasCacheKey getEmptyKey() {
|
||||
return {DenseMapInfo<swift::SILValue>::getEmptyKey(), swift::SILValue(),
|
||||
nullptr, nullptr};
|
||||
}
|
||||
static inline AliasCacheKey getTombstoneKey() {
|
||||
return {DenseMapInfo<swift::SILValue>::getTombstoneKey(), swift::SILValue(),
|
||||
nullptr, nullptr};
|
||||
}
|
||||
static unsigned getHashValue(const AliasCacheKey Val) {
|
||||
unsigned H = 0;
|
||||
H ^= DenseMapInfo<swift::SILValue>::getHashValue(Val.V1);
|
||||
H ^= DenseMapInfo<swift::SILValue>::getHashValue(Val.V2);
|
||||
H ^= DenseMapInfo<void *>::getHashValue(Val.T1);
|
||||
H ^= DenseMapInfo<void *>::getHashValue(Val.T2);
|
||||
return H;
|
||||
}
|
||||
static bool isEqual(const AliasCacheKey LHS, const AliasCacheKey RHS) {
|
||||
return LHS.V1 == RHS.V1 &&
|
||||
LHS.V2 == RHS.V2 &&
|
||||
LHS.T1 == RHS.T1 &&
|
||||
LHS.T2 == RHS.T2;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -273,18 +273,17 @@ public:
|
||||
};
|
||||
|
||||
class EpilogueARCAnalysis : public FunctionAnalysisBase<EpilogueARCFunctionInfo> {
|
||||
/// Backlink to the pass manager.
|
||||
SILPassManager *passManager = nullptr;
|
||||
/// Current post order analysis we are using.
|
||||
PostOrderAnalysis *PO;
|
||||
/// Current alias analysis we are using.
|
||||
AliasAnalysis *AA;
|
||||
PostOrderAnalysis *PO = nullptr;
|
||||
/// Current RC Identity analysis we are using.
|
||||
RCIdentityAnalysis *RC;
|
||||
|
||||
RCIdentityAnalysis *RC = nullptr;
|
||||
|
||||
public:
|
||||
EpilogueARCAnalysis(SILModule *)
|
||||
: FunctionAnalysisBase<EpilogueARCFunctionInfo>(
|
||||
SILAnalysisKind::EpilogueARC),
|
||||
PO(nullptr), AA(nullptr), RC(nullptr) {}
|
||||
SILAnalysisKind::EpilogueARC) {}
|
||||
|
||||
EpilogueARCAnalysis(const EpilogueARCAnalysis &) = delete;
|
||||
EpilogueARCAnalysis &operator=(const EpilogueARCAnalysis &) = delete;
|
||||
@@ -312,9 +311,7 @@ public:
|
||||
virtual void initialize(SILPassManager *PM) override;
|
||||
|
||||
virtual std::unique_ptr<EpilogueARCFunctionInfo>
|
||||
newFunctionAnalysis(SILFunction *F) override {
|
||||
return std::make_unique<EpilogueARCFunctionInfo>(F, PO, AA, RC);
|
||||
}
|
||||
newFunctionAnalysis(SILFunction *F) override;
|
||||
|
||||
virtual bool shouldInvalidate(SILAnalysis::InvalidationKind K) override {
|
||||
return true;
|
||||
|
||||
@@ -132,6 +132,15 @@ public:
|
||||
llvm_unreachable("Unable to find analysis for requested type.");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *getAnalysis(SILFunction *f) {
|
||||
for (SILAnalysis *A : Analyses) {
|
||||
if (A->getKind() == T::getAnalysisKind())
|
||||
return static_cast<FunctionAnalysisBase<T> *>(A)->get(f);
|
||||
}
|
||||
llvm_unreachable("Unable to find analysis for requested type.");
|
||||
}
|
||||
|
||||
/// \returns the module that the pass manager owns.
|
||||
SILModule *getModule() { return Mod; }
|
||||
|
||||
|
||||
@@ -86,6 +86,9 @@ namespace swift {
|
||||
template<typename T>
|
||||
T* getAnalysis() { return PM->getAnalysis<T>(); }
|
||||
|
||||
template<typename T>
|
||||
T* getAnalysis(SILFunction *f) { return PM->getAnalysis<T>(f); }
|
||||
|
||||
const SILOptions &getOptions() { return PM->getOptions(); }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user