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 #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ALIASANALYSIS_H
#define SWIFT_SILOPTIMIZER_ANALYSIS_ALIASANALYSIS_H #define SWIFT_SILOPTIMIZER_ANALYSIS_ALIASANALYSIS_H
#include "swift/Basic/ValueEnumerator.h"
#include "swift/SIL/ApplySite.h" #include "swift/SIL/ApplySite.h"
#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h"
#include "llvm/ADT/DenseMap.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 { namespace swift {
class SILInstruction;
class ValueBase;
class SideEffectAnalysis; class SideEffectAnalysis;
class EscapeAnalysis; class EscapeAnalysis;
/// This class is a simple wrapper around an alias analysis cache. This is /// This class is a simple wrapper around an alias analysis cache. This is
/// needed since we do not have an "analysis" infrastructure. /// needed since we do not have an "analysis" infrastructure.
class AliasAnalysis : public SILAnalysis { class AliasAnalysis {
public: public:
/// This enum describes the different kinds of aliasing relations between /// This enum describes the different kinds of aliasing relations between
@@ -89,12 +55,28 @@ public:
}; };
private: private:
SILModule *Mod; /// A key used for the AliasAnalysis cache.
SideEffectAnalysis *SEA; ///
EscapeAnalysis *EA; /// 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>; using TBAACacheKey = std::pair<SILType, SILType>;
SideEffectAnalysis *SEA;
EscapeAnalysis *EA;
/// A cache for the computation of TBAA. True means that the types may /// A cache for the computation of TBAA. True means that the types may
/// alias. False means that the types must not alias. /// alias. False means that the types must not alias.
/// ///
@@ -105,33 +87,27 @@ private:
/// AliasAnalysis value cache. /// AliasAnalysis value cache.
/// ///
/// The alias() method uses this map to cache queries. /// The alias() method uses this map to cache queries.
llvm::DenseMap<AliasKeyTy, AliasResult> AliasCache; llvm::DenseMap<AliasCacheKey, AliasResult> AliasCache;
using MemoryBehavior = SILInstruction::MemoryBehavior; using MemoryBehavior = SILInstruction::MemoryBehavior;
/// MemoryBehavior value cache. /// MemoryBehavior value cache.
/// ///
/// The computeMemoryBehavior() method uses this map to cache queries. /// 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. /// Set of instructions inside immutable-scopes.
/// ///
/// Contains pairs of intruction indices: the first instruction is the begin- /// Contains pairs of intructions: the first instruction is the begin-scope
/// scope instruction (e.g. begin_access), the second instruction is an /// instruction (e.g. begin_access), the second instruction is an
/// instruction inside the scope (only may-write instructions are considered). /// instruction inside the scope (only may-write instructions are considered).
llvm::DenseSet<MemBehaviorKeyTy> instsInImmutableScopes; llvm::DenseSet<ScopeCacheKey> instsInImmutableScopes;
/// Computed immutable scopes. /// Computed immutable scopes.
/// ///
/// Contains the begin-scope instruction's indices (e.g. begin_access) of /// Contains the begin-scope instructions (e.g. begin_access) of all computed
/// all computed scopes. /// scopes.
llvm::DenseSet<ValueIndexTy> immutableScopeComputed; llvm::SmallPtrSet<SILInstruction *, 16> 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;
AliasResult aliasAddressProjection(SILValue V1, SILValue V2, AliasResult aliasAddressProjection(SILValue V1, SILValue V2,
SILValue O1, SILValue O2); SILValue O1, SILValue O2);
@@ -144,34 +120,15 @@ private:
/// Returns True if memory of type \p T1 and \p T2 may alias. /// Returns True if memory of type \p T1 and \p T2 may alias.
bool typesMayAlias(SILType T1, SILType T2, const SILFunction &F); bool typesMayAlias(SILType T1, SILType T2, const SILFunction &F);
virtual void handleDeleteNotification(SILNode *node) override; void computeImmutableScope(SingleValueInstruction *beginScopeInst);
virtual bool needsNotifications() override { return true; }
void computeImmutableScope(SingleValueInstruction *beginScopeInst,
ValueIndexTy beginIdx);
bool isInImmutableScope(SILInstruction *inst, SILValue V); bool isInImmutableScope(SILInstruction *inst, SILValue V);
public: public:
AliasAnalysis(SILModule *M) AliasAnalysis(SideEffectAnalysis *SEA, EscapeAnalysis *EA)
: SILAnalysis(SILAnalysisKind::Alias), Mod(M), SEA(nullptr), EA(nullptr) { : SEA(SEA), EA(EA) {}
}
static bool classof(const SILAnalysis *S) { static SILAnalysisKind getAnalysisKind() { return SILAnalysisKind::Alias; }
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());
}
/// Perform an alias query to see if V1, V2 refer to the same values. /// Perform an alias query to see if V1, V2 refer to the same values.
AliasResult alias(SILValue V1, SILValue V2, SILType TBAAType1 = SILType(), 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. /// Returns true if \p Ptr may be released by the builtin \p BI.
bool canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr); 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 } // end namespace swift
namespace llvm { namespace llvm {
template <> struct DenseMapInfo<AliasKeyTy> { template <> struct DenseMapInfo<swift::AliasAnalysis::AliasCacheKey> {
static inline AliasKeyTy getEmptyKey() { using AliasCacheKey = swift::AliasAnalysis::AliasCacheKey;
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<MemBehaviorKeyTy> { static inline AliasCacheKey getEmptyKey() {
static inline MemBehaviorKeyTy getEmptyKey() { return {DenseMapInfo<swift::SILValue>::getEmptyKey(), swift::SILValue(),
auto Allone = std::numeric_limits<swift::ValueIndexTy>::max(); nullptr, nullptr};
return {0, Allone}; }
} static inline AliasCacheKey getTombstoneKey() {
static inline MemBehaviorKeyTy getTombstoneKey() { return {DenseMapInfo<swift::SILValue>::getTombstoneKey(), swift::SILValue(),
auto Allone = std::numeric_limits<swift::ValueIndexTy>::max(); nullptr, nullptr};
return {Allone, 0}; }
} static unsigned getHashValue(const AliasCacheKey Val) {
static unsigned getHashValue(const MemBehaviorKeyTy V) { unsigned H = 0;
unsigned H = 0; H ^= DenseMapInfo<swift::SILValue>::getHashValue(Val.V1);
H ^= DenseMapInfo<swift::ValueIndexTy>::getHashValue(V.V1); H ^= DenseMapInfo<swift::SILValue>::getHashValue(Val.V2);
H ^= DenseMapInfo<swift::ValueIndexTy>::getHashValue(V.V2); H ^= DenseMapInfo<void *>::getHashValue(Val.T1);
return H; H ^= DenseMapInfo<void *>::getHashValue(Val.T2);
} return H;
static bool isEqual(const MemBehaviorKeyTy LHS, }
const MemBehaviorKeyTy RHS) { static bool isEqual(const AliasCacheKey LHS, const AliasCacheKey RHS) {
return LHS.V1 == RHS.V1 && LHS.V2 == RHS.V2; return LHS.V1 == RHS.V1 &&
} LHS.V2 == RHS.V2 &&
}; LHS.T1 == RHS.T1 &&
LHS.T2 == RHS.T2;
}
};
} }
#endif #endif

View File

@@ -273,18 +273,17 @@ public:
}; };
class EpilogueARCAnalysis : public FunctionAnalysisBase<EpilogueARCFunctionInfo> { class EpilogueARCAnalysis : public FunctionAnalysisBase<EpilogueARCFunctionInfo> {
/// Backlink to the pass manager.
SILPassManager *passManager = nullptr;
/// Current post order analysis we are using. /// Current post order analysis we are using.
PostOrderAnalysis *PO; PostOrderAnalysis *PO = nullptr;
/// Current alias analysis we are using.
AliasAnalysis *AA;
/// Current RC Identity analysis we are using. /// Current RC Identity analysis we are using.
RCIdentityAnalysis *RC; RCIdentityAnalysis *RC = nullptr;
public: public:
EpilogueARCAnalysis(SILModule *) EpilogueARCAnalysis(SILModule *)
: FunctionAnalysisBase<EpilogueARCFunctionInfo>( : FunctionAnalysisBase<EpilogueARCFunctionInfo>(
SILAnalysisKind::EpilogueARC), SILAnalysisKind::EpilogueARC) {}
PO(nullptr), AA(nullptr), RC(nullptr) {}
EpilogueARCAnalysis(const EpilogueARCAnalysis &) = delete; EpilogueARCAnalysis(const EpilogueARCAnalysis &) = delete;
EpilogueARCAnalysis &operator=(const EpilogueARCAnalysis &) = delete; EpilogueARCAnalysis &operator=(const EpilogueARCAnalysis &) = delete;
@@ -312,9 +311,7 @@ public:
virtual void initialize(SILPassManager *PM) override; virtual void initialize(SILPassManager *PM) override;
virtual std::unique_ptr<EpilogueARCFunctionInfo> virtual std::unique_ptr<EpilogueARCFunctionInfo>
newFunctionAnalysis(SILFunction *F) override { newFunctionAnalysis(SILFunction *F) override;
return std::make_unique<EpilogueARCFunctionInfo>(F, PO, AA, RC);
}
virtual bool shouldInvalidate(SILAnalysis::InvalidationKind K) override { virtual bool shouldInvalidate(SILAnalysis::InvalidationKind K) override {
return true; return true;

View File

@@ -132,6 +132,15 @@ public:
llvm_unreachable("Unable to find analysis for requested type."); 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. /// \returns the module that the pass manager owns.
SILModule *getModule() { return Mod; } SILModule *getModule() { return Mod; }

View File

@@ -86,6 +86,9 @@ namespace swift {
template<typename T> template<typename T>
T* getAnalysis() { return PM->getAnalysis<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(); } const SILOptions &getOptions() { return PM->getOptions(); }
}; };

View File

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

View File

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

View File

@@ -31,12 +31,6 @@
using namespace swift; 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 // AA Debugging
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@@ -538,27 +532,6 @@ bool AliasAnalysis::typesMayAlias(SILType T1, SILType T2,
return MA; 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 // Entry Points
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@@ -567,7 +540,8 @@ void AliasAnalysis::handleDeleteNotification(SILNode *node) {
/// to disambiguate the two values. /// to disambiguate the two values.
AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2, AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2,
SILType TBAAType1, SILType TBAAType2) { 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. // Check if we've already computed this result.
auto It = AliasCache.find(Key); auto It = AliasCache.find(Key);
@@ -575,11 +549,6 @@ AliasResult AliasAnalysis::alias(SILValue V1, SILValue V2,
return It->second; 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. // Calculate the aliasing result and store it in the cache.
auto Result = aliasInner(V1, V2, TBAAType1, TBAAType2); auto Result = aliasInner(V1, V2, TBAAType1, TBAAType2);
AliasCache[Key] = Result; AliasCache[Key] = Result;
@@ -807,24 +776,35 @@ bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(
return EA->mayReleaseReferenceContent(releasedReference, accessedPointer); return EA->mayReleaseReferenceContent(releasedReference, accessedPointer);
} }
void AliasAnalysis::initialize(SILPassManager *PM) { namespace {
SEA = PM->getAnalysis<SideEffectAnalysis>();
EA = PM->getAnalysis<EscapeAnalysis>(); 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) { SILAnalysis *swift::createAliasAnalysis(SILModule *M) {
return new AliasAnalysis(M); return new AliasAnalysisContainer();
}
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};
} }

View File

@@ -172,11 +172,17 @@ bool EpilogueARCContext::computeEpilogueARC() {
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
void EpilogueARCAnalysis::initialize(SILPassManager *PM) { void EpilogueARCAnalysis::initialize(SILPassManager *PM) {
AA = PM->getAnalysis<AliasAnalysis>(); passManager = PM;
PO = PM->getAnalysis<PostOrderAnalysis>(); PO = PM->getAnalysis<PostOrderAnalysis>();
RC = PM->getAnalysis<RCIdentityAnalysis>(); 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) { SILAnalysis *swift::createEpilogueARCAnalysis(SILModule *M) {
return new EpilogueARCAnalysis(M); return new EpilogueARCAnalysis(M);
} }

View File

@@ -25,16 +25,6 @@
using namespace swift; 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 // Memory Behavior Implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@@ -525,18 +515,13 @@ visitBeginCOWMutationInst(BeginCOWMutationInst *BCMI) {
MemBehavior MemBehavior
AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V) { AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V) {
MemBehaviorKeyTy Key = toMemoryBehaviorKey(Inst, V); MemBehaviorCacheKey Key = {V, Inst};
// Check if we've already computed this result. // Check if we've already computed this result.
auto It = MemoryBehaviorCache.find(Key); auto It = MemoryBehaviorCache.find(Key);
if (It != MemoryBehaviorCache.end()) { if (It != MemoryBehaviorCache.end()) {
return It->second; 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. // Calculate the aliasing result and store it in the cache.
auto Result = computeMemoryBehaviorInner(Inst, V); auto Result = computeMemoryBehaviorInner(Inst, V);
MemoryBehaviorCache[Key] = Result; 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 /// 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 /// borrow scope (begin_borrow, load_borrow) of an immutable copy-on-write
/// buffer. /// buffer.
void AliasAnalysis::computeImmutableScope(SingleValueInstruction *beginScopeInst, void AliasAnalysis::computeImmutableScope(SingleValueInstruction *beginScopeInst) {
ValueIndexTy beginIdx) {
BasicBlockSet visitedBlocks(beginScopeInst->getFunction()); BasicBlockSet visitedBlocks(beginScopeInst->getFunction());
llvm::SmallVector<std::pair<SILInstruction *, SILBasicBlock *>, 16> workList; llvm::SmallVector<std::pair<SILInstruction *, SILBasicBlock *>, 16> workList;
@@ -636,8 +620,7 @@ void AliasAnalysis::computeImmutableScope(SingleValueInstruction *beginScopeInst
break; break;
} }
if (inst->mayWriteToMemory()) { if (inst->mayWriteToMemory()) {
instsInImmutableScopes.insert({beginIdx, instsInImmutableScopes.insert({beginScopeInst, inst});
InstructionToIndex.getIndex(inst)});
} }
} }
} }
@@ -668,20 +651,11 @@ bool AliasAnalysis::isInImmutableScope(SILInstruction *inst, SILValue V) {
if (!beginScopeInst) if (!beginScopeInst)
return false; return false;
ValueIndexTy beginIdx = InstructionToIndex.getIndex(beginScopeInst);
// Recompute the scope if not done yet. // Recompute the scope if not done yet.
if (immutableScopeComputed.insert(beginIdx).second) { if (immutableScopeComputed.insert(beginScopeInst).second) {
// In practice this will never happen. Just be be sure to not run into computeImmutableScope(beginScopeInst);
// quadratic complexity in obscure corner cases.
if (instsInImmutableScopes.size() > ImmutableScopeInstsMaxSize)
return false;
computeImmutableScope(beginScopeInst, beginIdx);
} }
return instsInImmutableScopes.contains({beginScopeInst, inst});
ValueIndexTy instIdx = InstructionToIndex.getIndex(inst);
return instsInImmutableScopes.contains({beginIdx, instIdx});
} }
MemBehavior MemBehavior
@@ -700,14 +674,3 @@ AliasAnalysis::computeMemoryBehaviorInner(SILInstruction *Inst, SILValue V) {
} }
return result; 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>(); DominanceAnalysis *DA = PM->getAnalysis<DominanceAnalysis>();
PostDominanceAnalysis *PDA = PM->getAnalysis<PostDominanceAnalysis>(); PostDominanceAnalysis *PDA = PM->getAnalysis<PostDominanceAnalysis>();
AliasAnalysis *AA = PM->getAnalysis<AliasAnalysis>(); AliasAnalysis *AA = PM->getAnalysis<AliasAnalysis>(F);
SideEffectAnalysis *SEA = PM->getAnalysis<SideEffectAnalysis>(); SideEffectAnalysis *SEA = PM->getAnalysis<SideEffectAnalysis>();
AccessedStorageAnalysis *ASA = getAnalysis<AccessedStorageAnalysis>(); AccessedStorageAnalysis *ASA = getAnalysis<AccessedStorageAnalysis>();
DominanceInfo *DomTree = nullptr; DominanceInfo *DomTree = nullptr;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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