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:
Erik Eckstein
2021-05-26 21:36:56 +02:00
parent 24799e1526
commit d2fc6eb3b5
21 changed files with 194 additions and 339 deletions

View File

@@ -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};
template <> struct DenseMapInfo<swift::AliasAnalysis::AliasCacheKey> {
using AliasCacheKey = swift::AliasAnalysis::AliasCacheKey;
static inline AliasCacheKey getEmptyKey() {
return {DenseMapInfo<swift::SILValue>::getEmptyKey(), swift::SILValue(),
nullptr, nullptr};
}
static inline AliasKeyTy getTombstoneKey() {
auto Allone = std::numeric_limits<swift::ValueIndexTy>::max();
return {Allone, 0, nullptr, nullptr};
static inline AliasCacheKey getTombstoneKey() {
return {DenseMapInfo<swift::SILValue>::getTombstoneKey(), swift::SILValue(),
nullptr, nullptr};
}
static unsigned getHashValue(const AliasKeyTy Val) {
static unsigned getHashValue(const AliasCacheKey Val) {
unsigned H = 0;
H ^= DenseMapInfo<swift::ValueIndexTy>::getHashValue(Val.V1);
H ^= DenseMapInfo<swift::ValueIndexTy>::getHashValue(Val.V2);
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 AliasKeyTy LHS, const AliasKeyTy RHS) {
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;
}
};
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;
}
};
}
#endif

View File

@@ -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;

View File

@@ -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; }

View File

@@ -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(); }
};

View File

@@ -66,7 +66,7 @@ class ARCLoopOpts : public SILFunctionTransform {
}
// Get all of the analyses that we need.
auto *AA = getAnalysis<AliasAnalysis>();
auto *AA = getAnalysis<AliasAnalysis>(F);
auto *RCFI = getAnalysis<RCIdentityAnalysis>()->get(F);
auto *EAFI = getAnalysis<EpilogueARCAnalysis>()->get(F);
auto *LRFI = getAnalysis<LoopRegionAnalysis>()->get(F);

View File

@@ -260,7 +260,7 @@ class ARCSequenceOpts : public SILFunctionTransform {
return;
if (!EnableLoopARC) {
auto *AA = getAnalysis<AliasAnalysis>();
auto *AA = getAnalysis<AliasAnalysis>(F);
auto *POTA = getAnalysis<PostOrderAnalysis>();
auto *RCFI = getAnalysis<RCIdentityAnalysis>()->get(F);
auto *EAFI = getAnalysis<EpilogueARCAnalysis>()->get(F);
@@ -288,7 +288,7 @@ class ARCSequenceOpts : public SILFunctionTransform {
LA->unlockInvalidation();
}
auto *AA = getAnalysis<AliasAnalysis>();
auto *AA = getAnalysis<AliasAnalysis>(F);
auto *POTA = getAnalysis<PostOrderAnalysis>();
auto *RCFI = getAnalysis<RCIdentityAnalysis>()->get(F);
auto *EAFI = getAnalysis<EpilogueARCAnalysis>()->get(F);

View File

@@ -31,12 +31,6 @@
using namespace swift;
// The AliasAnalysis Cache must not grow beyond this size.
// We limit the size of the AA cache to 2**14 because we want to limit the
// memory usage of this cache.
static const int AliasAnalysisMaxCacheSize = 16384;
//===----------------------------------------------------------------------===//
// AA Debugging
//===----------------------------------------------------------------------===//
@@ -538,27 +532,6 @@ bool AliasAnalysis::typesMayAlias(SILType T1, SILType T2,
return MA;
}
void AliasAnalysis::handleDeleteNotification(SILNode *node) {
// 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 index caches.
if (auto *value = dyn_cast<ValueBase>(node))
ValueToIndex.invalidateValue(value);
if (auto *inst = dyn_cast<SILInstruction>(node)) {
InstructionToIndex.invalidateValue(inst);
// When a MultipleValueInstruction is deleted, we have to invalidate all
// the instruction results.
if (auto *mvi = dyn_cast<MultipleValueInstruction>(inst)) {
for (unsigned idx = 0, end = mvi->getNumResults(); idx < end; ++idx) {
ValueToIndex.invalidateValue(mvi->getResult(idx));
}
}
}
}
//===----------------------------------------------------------------------===//
// Entry Points
//===----------------------------------------------------------------------===//
@@ -567,7 +540,8 @@ void AliasAnalysis::handleDeleteNotification(SILNode *node) {
/// to disambiguate the two values.
AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2,
SILType TBAAType1, SILType TBAAType2) {
AliasKeyTy Key = toAliasKey(V1, V2, TBAAType1, TBAAType2);
AliasCacheKey Key = {V1, V2, TBAAType1.getOpaqueValue(),
TBAAType2.getOpaqueValue()};
// Check if we've already computed this result.
auto It = AliasCache.find(Key);
@@ -575,11 +549,6 @@ AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2,
return It->second;
}
// Flush the cache if the size of the cache is too large.
if (AliasCache.size() > AliasAnalysisMaxCacheSize) {
AliasCache.clear();
}
// Calculate the aliasing result and store it in the cache.
auto Result = aliasInner(V1, V2, TBAAType1, TBAAType2);
AliasCache[Key] = Result;
@@ -807,24 +776,35 @@ bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(
return EA->mayReleaseReferenceContent(releasedReference, accessedPointer);
}
void AliasAnalysis::initialize(SILPassManager *PM) {
namespace {
class AliasAnalysisContainer : public FunctionAnalysisBase<AliasAnalysis> {
SideEffectAnalysis *SEA = nullptr;
EscapeAnalysis *EA = nullptr;
public:
AliasAnalysisContainer() : FunctionAnalysisBase(SILAnalysisKind::Alias) {}
virtual bool shouldInvalidate(SILAnalysis::InvalidationKind K) override {
return K & InvalidationKind::Instructions;
}
// Computes loop information for the given function using dominance
// information.
virtual std::unique_ptr<AliasAnalysis>
newFunctionAnalysis(SILFunction *F) override {
assert(EA && SEA && "dependent analysis not initialized");
return std::make_unique<AliasAnalysis>(SEA, EA);
}
virtual void initialize(SILPassManager *PM) override {
SEA = PM->getAnalysis<SideEffectAnalysis>();
EA = PM->getAnalysis<EscapeAnalysis>();
}
};
} // end anonymous namespace
SILAnalysis *swift::createAliasAnalysis(SILModule *M) {
return new AliasAnalysis(M);
}
AliasKeyTy AliasAnalysis::toAliasKey(SILValue V1, SILValue V2,
SILType Type1, SILType Type2) {
ValueIndexTy idx1 = ValueToIndex.getIndex(V1);
assert(idx1 != std::numeric_limits<ValueIndexTy>::max() &&
"~0 index reserved for empty/tombstone keys");
ValueIndexTy idx2 = ValueToIndex.getIndex(V2);
assert(idx2 != std::numeric_limits<ValueIndexTy>::max() &&
"~0 index reserved for empty/tombstone keys");
void *t1 = Type1.getOpaqueValue();
void *t2 = Type2.getOpaqueValue();
return {idx1, idx2, t1, t2};
return new AliasAnalysisContainer();
}

View File

@@ -172,11 +172,17 @@ bool EpilogueARCContext::computeEpilogueARC() {
//===----------------------------------------------------------------------===//
void EpilogueARCAnalysis::initialize(SILPassManager *PM) {
AA = PM->getAnalysis<AliasAnalysis>();
passManager = PM;
PO = PM->getAnalysis<PostOrderAnalysis>();
RC = PM->getAnalysis<RCIdentityAnalysis>();
}
std::unique_ptr<EpilogueARCFunctionInfo>
EpilogueARCAnalysis::newFunctionAnalysis(SILFunction *F) {
return std::make_unique<EpilogueARCFunctionInfo>(F, PO,
passManager->getAnalysis<AliasAnalysis>(F), RC);
}
SILAnalysis *swift::createEpilogueARCAnalysis(SILModule *M) {
return new EpilogueARCAnalysis(M);
}

View File

@@ -25,16 +25,6 @@
using namespace swift;
// The MemoryBehavior Cache must not grow beyond this size.
// We limit the size of the MB cache to 2**14 because we want to limit the
// memory usage of this cache.
static const int MemoryBehaviorAnalysisMaxCacheSize = 16384;
// The maximum size of instsInImmutableScopes.
// Usually more than enough. But in corner cases it can grow quadratically (in
// case of many large nested immutable scopes).
static const int ImmutableScopeInstsMaxSize = 18384;
//===----------------------------------------------------------------------===//
// Memory Behavior Implementation
//===----------------------------------------------------------------------===//
@@ -525,18 +515,13 @@ visitBeginCOWMutationInst(BeginCOWMutationInst *BCMI) {
MemBehavior
AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V) {
MemBehaviorKeyTy Key = toMemoryBehaviorKey(Inst, V);
MemBehaviorCacheKey Key = {V, Inst};
// Check if we've already computed this result.
auto It = MemoryBehaviorCache.find(Key);
if (It != MemoryBehaviorCache.end()) {
return It->second;
}
// Flush the cache if the size of the cache is too large.
if (MemoryBehaviorCache.size() > MemoryBehaviorAnalysisMaxCacheSize) {
MemoryBehaviorCache.clear();
}
// Calculate the aliasing result and store it in the cache.
auto Result = computeMemoryBehaviorInner(Inst, V);
MemoryBehaviorCache[Key] = Result;
@@ -586,8 +571,7 @@ static SILValue getBeginScopeInst(SILValue V) {
/// The \p beginScopeInst is either a ``begin_access [read]`` or the begin of a
/// borrow scope (begin_borrow, load_borrow) of an immutable copy-on-write
/// buffer.
void AliasAnalysis::computeImmutableScope(SingleValueInstruction *beginScopeInst,
ValueIndexTy beginIdx) {
void AliasAnalysis::computeImmutableScope(SingleValueInstruction *beginScopeInst) {
BasicBlockSet visitedBlocks(beginScopeInst->getFunction());
llvm::SmallVector<std::pair<SILInstruction *, SILBasicBlock *>, 16> workList;
@@ -636,8 +620,7 @@ void AliasAnalysis::computeImmutableScope(SingleValueInstruction *beginScopeInst
break;
}
if (inst->mayWriteToMemory()) {
instsInImmutableScopes.insert({beginIdx,
InstructionToIndex.getIndex(inst)});
instsInImmutableScopes.insert({beginScopeInst, inst});
}
}
}
@@ -668,20 +651,11 @@ bool AliasAnalysis::isInImmutableScope(SILInstruction *inst, SILValue V) {
if (!beginScopeInst)
return false;
ValueIndexTy beginIdx = InstructionToIndex.getIndex(beginScopeInst);
// Recompute the scope if not done yet.
if (immutableScopeComputed.insert(beginIdx).second) {
// In practice this will never happen. Just be be sure to not run into
// quadratic complexity in obscure corner cases.
if (instsInImmutableScopes.size() > ImmutableScopeInstsMaxSize)
return false;
computeImmutableScope(beginScopeInst, beginIdx);
if (immutableScopeComputed.insert(beginScopeInst).second) {
computeImmutableScope(beginScopeInst);
}
ValueIndexTy instIdx = InstructionToIndex.getIndex(inst);
return instsInImmutableScopes.contains({beginIdx, instIdx});
return instsInImmutableScopes.contains({beginScopeInst, inst});
}
MemBehavior
@@ -700,14 +674,3 @@ AliasAnalysis::computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V) {
}
return result;
}
MemBehaviorKeyTy AliasAnalysis::toMemoryBehaviorKey(SILInstruction *V1,
SILValue V2) {
ValueIndexTy idx1 = InstructionToIndex.getIndex(V1);
assert(idx1 != std::numeric_limits<ValueIndexTy>::max() &&
"~0 index reserved for empty/tombstone keys");
ValueIndexTy idx2 = ValueToIndex.getIndex(V2);
assert(idx2 != std::numeric_limits<ValueIndexTy>::max() &&
"~0 index reserved for empty/tombstone keys");
return {idx1, idx2};
}

View File

@@ -1467,7 +1467,7 @@ public:
DominanceAnalysis *DA = PM->getAnalysis<DominanceAnalysis>();
PostDominanceAnalysis *PDA = PM->getAnalysis<PostDominanceAnalysis>();
AliasAnalysis *AA = PM->getAnalysis<AliasAnalysis>();
AliasAnalysis *AA = PM->getAnalysis<AliasAnalysis>(F);
SideEffectAnalysis *SEA = PM->getAnalysis<SideEffectAnalysis>();
AccessedStorageAnalysis *ASA = getAnalysis<AccessedStorageAnalysis>();
DominanceInfo *DomTree = nullptr;

View File

@@ -387,7 +387,7 @@ class SILCombine : public SILFunctionTransform {
/// The entry point to the transformation.
void run() override {
auto *AA = PM->getAnalysis<AliasAnalysis>();
auto *AA = PM->getAnalysis<AliasAnalysis>(getFunction());
auto *DA = PM->getAnalysis<DominanceAnalysis>();
auto *PCA = PM->getAnalysis<ProtocolConformanceAnalysis>();
auto *CHA = PM->getAnalysis<ClassHierarchyAnalysis>();

View File

@@ -1189,7 +1189,7 @@ public:
POA->invalidateFunction(F);
auto *PO = POA->get(F);
auto *AA = PM->getAnalysis<AliasAnalysis>();
auto *AA = PM->getAnalysis<AliasAnalysis>(F);
auto *RCFI = PM->getAnalysis<RCIdentityAnalysis>()->get(F);
llvm::SpecificBumpPtrAllocator<BlockState> BPA;

View File

@@ -89,7 +89,7 @@ void COWOptsPass::run() {
LLVM_DEBUG(llvm::dbgs() << "*** RedundantPhiElimination on function: "
<< F->getName() << " ***\n");
AA = PM->getAnalysis<AliasAnalysis>();
AA = PM->getAnalysis<AliasAnalysis>(F);
bool changed = false;
for (SILBasicBlock &block : *F) {

View File

@@ -1275,7 +1275,7 @@ public:
LLVM_DEBUG(llvm::dbgs() << "*** DSE on function: " << F->getName()
<< " ***\n");
auto *AA = PM->getAnalysis<AliasAnalysis>();
auto *AA = PM->getAnalysis<AliasAnalysis>(F);
auto *TE = PM->getAnalysis<TypeExpansionAnalysis>();
auto *EAFI = PM->getAnalysis<EpilogueARCAnalysis>()->get(F);

View File

@@ -1681,7 +1681,7 @@ public:
LLVM_DEBUG(llvm::dbgs() << "*** RLE on function: " << F->getName()
<< " ***\n");
auto *AA = PM->getAnalysis<AliasAnalysis>();
auto *AA = PM->getAnalysis<AliasAnalysis>(F);
auto *TE = PM->getAnalysis<TypeExpansionAnalysis>();
auto *PO = PM->getAnalysis<PostOrderAnalysis>()->get(F);
auto *EAFI = PM->getAnalysis<EpilogueARCAnalysis>()->get(F);

View File

@@ -1778,7 +1778,7 @@ public:
if (F->hasOwnership())
return;
auto *AA = getAnalysis<AliasAnalysis>();
auto *AA = getAnalysis<AliasAnalysis>(F);
auto *PO = getAnalysis<PostOrderAnalysis>()->get(F);
auto *RCIA = getAnalysis<RCIdentityAnalysis>()->get(getFunction());

View File

@@ -78,10 +78,8 @@ public:
void run() override;
private:
AliasAnalysis *AA = nullptr;
bool tempLValueOpt(CopyAddrInst *copyInst);
bool combineCopyAndDestroy(CopyAddrInst *copyInst);
void tempLValueOpt(CopyAddrInst *copyInst);
void combineCopyAndDestroy(CopyAddrInst *copyInst);
};
void TempLValueOptPass::run() {
@@ -92,9 +90,6 @@ void TempLValueOptPass::run() {
LLVM_DEBUG(llvm::dbgs() << "*** TempLValueOptPass on function: "
<< F->getName() << " ***\n");
AA = PM->getAnalysis<AliasAnalysis>();
bool changed = false;
for (SILBasicBlock &block : *F) {
// First collect all copy_addr instructions upfront to avoid iterator
// invalidation problems (the optimizations might delete the copy_addr
@@ -106,14 +101,10 @@ void TempLValueOptPass::run() {
}
// Do the optimizations.
for (CopyAddrInst *copyInst : copyInsts) {
changed |= combineCopyAndDestroy(copyInst);
changed |= tempLValueOpt(copyInst);
combineCopyAndDestroy(copyInst);
tempLValueOpt(copyInst);
}
}
if (changed) {
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
}
}
static SingleValueInstruction *isMovableProjection(SILValue addr) {
@@ -129,7 +120,7 @@ static SingleValueInstruction *isMovableProjection(SILValue addr) {
return nullptr;
}
bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
void TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
// An overview of the algorithm:
//
@@ -147,11 +138,11 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
// 'destroy_addr %destination' is inserted before the first use of %temp.
if (!copyInst->isTakeOfSrc())
return false;
return;
auto *temporary = dyn_cast<AllocStackInst>(copyInst->getSrc());
if (!temporary)
return false;
return;
// Collect all users of the temporary into a set. Also, for simplicity,
// require that all users are within a single basic block.
@@ -160,7 +151,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
for (Operand *use : temporary->getUses()) {
SILInstruction *user = use->getUser();
if (user->getParent() != block)
return false;
return;
users.insert(user);
}
@@ -176,7 +167,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
// Just to be on the safe side, bail if it's not the case (instead of an
// assert).
if (users.count(projInst))
return false;
return;
projections.insert(projInst);
destRootAddr = projInst->getOperand(0);
}
@@ -185,6 +176,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
SILInstruction *destRootInst = destRootAddr->getDefiningInstruction();
// Iterate over the liferange of the temporary and make some validity checks.
AliasAnalysis *AA = nullptr;
SILInstruction *beginOfLiferange = nullptr;
bool endOfLiferangeReached = false;
for (auto iter = temporary->getIterator(); iter != block->end(); ++iter) {
@@ -197,7 +189,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
// Check if the copyInst is the last user of the temporary (beside the
// dealloc_stack).
if (endOfLiferangeReached)
return false;
return;
// Find the first user of the temporary to get a more precise liferange.
// It would be too conservative to treat the alloc_stack itself as the
@@ -213,7 +205,10 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
// temporary, we cannot replace all uses of the temporary with the
// destination (it would break the def-use dominance rule).
if (inst == destRootInst)
return false;
return;
if (!AA)
AA = PM->getAnalysis<AliasAnalysis>(getFunction());
// Check if the destination is not accessed within the liferange of
// the temporary.
@@ -223,7 +218,7 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
if (AA->mayReadOrWriteMemory(inst, destination) &&
// Needed to treat init_existential_addr as not-writing projection.
projections.count(inst) == 0)
return false;
return;
}
}
assert(endOfLiferangeReached);
@@ -253,18 +248,17 @@ bool TempLValueOptPass::tempLValueOpt(CopyAddrInst *copyInst) {
user->eraseFromParent();
break;
default:
AA->invalidateInstruction(user);
use->set(destination);
}
}
temporary->eraseFromParent();
copyInst->eraseFromParent();
return true;
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
}
bool TempLValueOptPass::combineCopyAndDestroy(CopyAddrInst *copyInst) {
void TempLValueOptPass::combineCopyAndDestroy(CopyAddrInst *copyInst) {
if (copyInst->isTakeOfSrc())
return false;
return;
// Find a destroy_addr of the copy's source address.
DestroyAddrInst *destroy = nullptr;
@@ -274,7 +268,7 @@ bool TempLValueOptPass::combineCopyAndDestroy(CopyAddrInst *copyInst) {
}
SILBasicBlock *block = copyInst->getParent();
if (!destroy || destroy->getParent() != block)
return false;
return;
assert(destroy->getOperand() == copyInst->getSrc());
// Check if the destroy_addr is after the copy_addr and if there are no
@@ -290,16 +284,16 @@ bool TempLValueOptPass::combineCopyAndDestroy(CopyAddrInst *copyInst) {
for (auto debugInst : debugInsts) {
debugInst->eraseFromParent();
}
return true;
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
return;
}
if (auto *debugInst = dyn_cast<DebugValueAddrInst>(inst)) {
if (debugInst->getOperand() == copyInst->getSrc())
debugInsts.push_back(debugInst);
}
if (inst->mayReadOrWriteMemory())
return false;
return;
}
return false;
}
} // end anonymous namespace

View File

@@ -75,8 +75,6 @@ namespace {
///
/// TODO: Check if we still need to handle stores when RLE supports OSSA.
class TempRValueOptPass : public SILFunctionTransform {
AliasAnalysis *aa = nullptr;
bool collectLoads(Operand *addressUse, CopyAddrInst *originalCopy,
SmallPtrSetImpl<SILInstruction *> &loadInsts);
bool collectLoadsFromProjection(SingleValueInstruction *projection,
@@ -84,16 +82,17 @@ class TempRValueOptPass : public SILFunctionTransform {
SmallPtrSetImpl<SILInstruction *> &loadInsts);
SILInstruction *getLastUseWhileSourceIsNotModified(
CopyAddrInst *copyInst, const SmallPtrSetImpl<SILInstruction *> &useInsts);
CopyAddrInst *copyInst, const SmallPtrSetImpl<SILInstruction *> &useInsts,
AliasAnalysis *aa);
bool
checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst);
bool extendAccessScopes(CopyAddrInst *copyInst, SILInstruction *lastUseInst);
bool extendAccessScopes(CopyAddrInst *copyInst, SILInstruction *lastUseInst,
AliasAnalysis *aa);
bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst);
std::pair<SILBasicBlock::iterator, bool>
tryOptimizeStoreIntoTemp(StoreInst *si);
void tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst);
SILBasicBlock::iterator tryOptimizeStoreIntoTemp(StoreInst *si);
void run() override;
};
@@ -306,7 +305,8 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy,
/// of the temporary and look for the last use, which effectively ends the
/// lifetime.
SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified(
CopyAddrInst *copyInst, const SmallPtrSetImpl<SILInstruction *> &useInsts) {
CopyAddrInst *copyInst, const SmallPtrSetImpl<SILInstruction *> &useInsts,
AliasAnalysis *aa) {
if (useInsts.empty())
return copyInst;
unsigned numLoadsFound = 0;
@@ -358,7 +358,7 @@ SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified(
/// We must not replace %temp with %a after the end_access. Instead we try to
/// move the end_access after "use %temp".
bool TempRValueOptPass::extendAccessScopes(
CopyAddrInst *copyInst, SILInstruction *lastUseInst) {
CopyAddrInst *copyInst, SILInstruction *lastUseInst, AliasAnalysis *aa) {
SILValue copySrc = copyInst->getSrc();
EndAccessInst *endAccessToMove = nullptr;
@@ -460,13 +460,13 @@ bool TempRValueOptPass::checkTempObjectDestroy(
}
/// Tries to perform the temporary rvalue copy elimination for \p copyInst
bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
void TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
if (!copyInst->isInitializationOfDest())
return false;
return;
auto *tempObj = dyn_cast<AllocStackInst>(copyInst->getDest());
if (!tempObj)
return false;
return;
bool isOSSA = copyInst->getFunction()->hasOwnership();
@@ -502,21 +502,23 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
// Otherwise we would risk of inserting the destroy too early.
// So we just treat the destroy_addr as any other use of tempObj.
if (user->getParent() != copyInst->getParent())
return false;
return;
loadInsts.insert(user);
}
continue;
}
if (!collectLoads(useOper, copyInst, loadInsts))
return false;
return;
}
AliasAnalysis *aa = getPassManager()->getAnalysis<AliasAnalysis>(getFunction());
// Check if the source is modified within the lifetime of the temporary.
SILInstruction *lastLoadInst = getLastUseWhileSourceIsNotModified(copyInst,
loadInsts);
SILInstruction *lastLoadInst =
getLastUseWhileSourceIsNotModified(copyInst, loadInsts, aa);
if (!lastLoadInst)
return false;
return;
// We cannot insert the destroy of copySrc after lastLoadInst if copySrc is
// re-initialized by exactly this instruction.
@@ -527,13 +529,13 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
if (needToInsertDestroy && lastLoadInst != copyInst &&
!isa<DestroyAddrInst>(lastLoadInst) &&
aa->mayWriteToMemory(lastLoadInst, copySrc))
return false;
return;
if (!isOSSA && !checkTempObjectDestroy(tempObj, copyInst))
return false;
return;
if (!extendAccessScopes(copyInst, lastLoadInst))
return false;
if (!extendAccessScopes(copyInst, lastLoadInst, aa))
return;
LLVM_DEBUG(llvm::dbgs() << " Success: replace temp" << *tempObj);
@@ -556,7 +558,6 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
while (!tempObj->use_empty()) {
Operand *use = *tempObj->use_begin();
SILInstruction *user = use->getUser();
aa->invalidateInstruction(user);
switch (user->getKind()) {
case SILInstructionKind::DestroyAddrInst:
@@ -591,25 +592,25 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
}
tempObj->eraseFromParent();
return true;
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
}
std::pair<SILBasicBlock::iterator, bool>
SILBasicBlock::iterator
TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
// If our store is an assign, bail.
if (si->getOwnershipQualifier() == StoreOwnershipQualifier::Assign)
return {std::next(si->getIterator()), false};
return std::next(si->getIterator());
auto *tempObj = dyn_cast<AllocStackInst>(si->getDest());
if (!tempObj) {
return {std::next(si->getIterator()), false};
return std::next(si->getIterator());
}
// If our tempObj has a dynamic lifetime (meaning it is conditionally
// initialized, conditionally taken, etc), we can not convert its uses to SSA
// while eliminating it simply. So bail.
if (tempObj->hasDynamicLifetime()) {
return {std::next(si->getIterator()), false};
return std::next(si->getIterator());
}
// Scan all uses of the temporary storage (tempObj) to verify they all refer
@@ -632,14 +633,14 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
break;
case SILInstructionKind::CopyAddrInst:
if (cast<CopyAddrInst>(user)->getDest() == tempObj)
return {std::next(si->getIterator()), false};
return std::next(si->getIterator());
break;
case SILInstructionKind::MarkDependenceInst:
if (cast<MarkDependenceInst>(user)->getValue() == tempObj)
return {std::next(si->getIterator()), false};
return std::next(si->getIterator());
break;
default:
return {std::next(si->getIterator()), false};
return std::next(si->getIterator());
}
}
@@ -734,7 +735,8 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
auto nextIter = std::next(si->getIterator());
si->eraseFromParent();
tempObj->eraseFromParent();
return {nextIter, true};
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
return nextIter;
}
//===----------------------------------------------------------------------===//
@@ -746,9 +748,6 @@ void TempRValueOptPass::run() {
LLVM_DEBUG(llvm::dbgs() << "Copy Peephole in Func "
<< getFunction()->getName() << "\n");
aa = getPassManager()->getAnalysis<AliasAnalysis>();
bool changed = false;
// Find all copy_addr instructions.
llvm::SmallSetVector<CopyAddrInst *, 8> deadCopies;
for (auto &block : *getFunction()) {
@@ -759,13 +758,12 @@ void TempRValueOptPass::run() {
if (auto *copyInst = dyn_cast<CopyAddrInst>(&*ii)) {
// In case of success, this may delete instructions, but not the
// CopyInst itself.
changed |= tryOptimizeCopyIntoTemp(copyInst);
tryOptimizeCopyIntoTemp(copyInst);
// Remove identity copies which either directly result from successfully
// calling tryOptimizeCopyIntoTemp or was created by an earlier
// iteration, where another copy_addr copied the temporary back to the
// source location.
if (copyInst->getSrc() == copyInst->getDest()) {
changed = true;
deadCopies.insert(copyInst);
}
++ii;
@@ -773,9 +771,7 @@ void TempRValueOptPass::run() {
}
if (auto *si = dyn_cast<StoreInst>(&*ii)) {
bool madeSingleChange;
std::tie(ii, madeSingleChange) = tryOptimizeStoreIntoTemp(si);
changed |= madeSingleChange;
ii = tryOptimizeStoreIntoTemp(si);
continue;
}
@@ -794,7 +790,6 @@ void TempRValueOptPass::run() {
DeadEndBlocks deBlocks(getFunction());
for (auto *deadCopy : deadCopies) {
assert(changed);
auto *srcInst = deadCopy->getSrc()->getDefiningInstruction();
deadCopy->eraseFromParent();
// Simplify any access scope markers that were only used by the dead
@@ -803,7 +798,7 @@ void TempRValueOptPass::run() {
simplifyAndReplaceAllSimplifiedUsesAndErase(srcInst, callbacks, &deBlocks);
}
}
if (changed) {
if (!deadCopies.empty()) {
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
}
}

View File

@@ -64,7 +64,7 @@ class SILAADumper : public SILModuleTransform {
if (!gatherValues(Fn, Values))
continue;
AliasAnalysis *AA = PM->getAnalysis<AliasAnalysis>();
AliasAnalysis *AA = PM->getAnalysis<AliasAnalysis>(&Fn);
// A cache
llvm::DenseMap<uint64_t, AliasAnalysis::AliasResult> Results;

View File

@@ -39,13 +39,14 @@ namespace {
class SILEpilogueRetainReleaseMatcherDumper : public SILModuleTransform {
void run() override {
auto *AA = PM->getAnalysis<AliasAnalysis>();
auto *RCIA = getAnalysis<RCIdentityAnalysis>();
for (auto &Fn: *getModule()) {
// Function is not definition.
if (!Fn.isDefinition())
continue;
auto *AA = PM->getAnalysis<AliasAnalysis>(&Fn);
llvm::outs() << "START: sil @" << Fn.getName() << "\n";
// Handle @owned return value.

View File

@@ -83,7 +83,7 @@ class MemBehaviorDumper : public SILModuleTransform {
if (!gatherValues(Fn, Values))
continue;
AliasAnalysis *AA = PM->getAnalysis<AliasAnalysis>();
AliasAnalysis *AA = PM->getAnalysis<AliasAnalysis>(&Fn);
unsigned PairCount = 0;
for (auto &BB : Fn) {