diff --git a/include/swift/Basic/ValueEnumerator.h b/include/swift/Basic/ValueEnumerator.h deleted file mode 100644 index 1435613b990eb..0000000000000 --- a/include/swift/Basic/ValueEnumerator.h +++ /dev/null @@ -1,61 +0,0 @@ -//===--- ValueEnumerator.h --- Enumerates values ----------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_BASIC_VALUEENUMERATOR_H -#define SWIFT_BASIC_VALUEENUMERATOR_H - -#include "llvm/ADT/DenseMap.h" -#include "llvm/Support/raw_ostream.h" - -namespace swift { - -typedef unsigned ValueIndexTy; - -/// / This class maps values to unique indices. -template -class ValueEnumerator { - /// A running counter to enumerate values. - ValueIndexTy counter = 0; - - /// Maps values to unique integers. - llvm::DenseMap ValueToIndex; - -public: - /// Return the index of value \p v. - ValueIndexTy getIndex(const ValueTy &v) { - // Return the index of this Key, if we've assigned one already. - auto It = ValueToIndex.find(v); - if (It != ValueToIndex.end()) { - return It->second; - } - - // Generate a new counter for the key. - ValueToIndex[v] = ++counter; - assert(counter != 0 && "counter overflow in ValueEnumerator"); - return counter; - } - - ValueEnumerator() = default; - - /// Forget about key \p v. - void invalidateValue(const ValueTy &v) { ValueToIndex.erase(v); } - - /// Clear the enumeration state of the - void clear() { - ValueToIndex.clear(); - counter = 0; - } -}; - -} // end namespace swift - -#endif // SWIFT_BASIC_VALUEENUMERATOR_H diff --git a/include/swift/SIL/Notifications.h b/include/swift/SIL/Notifications.h index 91ab29c2c5bff..f7ecd87024c70 100644 --- a/include/swift/SIL/Notifications.h +++ b/include/swift/SIL/Notifications.h @@ -239,27 +239,6 @@ class DeserializationNotificationHandlerSet final void didDeserialize(ModuleDecl *mod, SILDefaultWitnessTable *wtable) override; }; - -/// A protocol (or interface) for handling value deletion notifications. -/// -/// This class is used as a base class for any class that need to accept -/// instruction deletion notification messages. This is used by passes and -/// analysis that need to invalidate data structures that contain pointers. -/// This is similar to LLVM's ValueHandle. -struct DeleteNotificationHandler { - DeleteNotificationHandler() { } - virtual ~DeleteNotificationHandler() {} - - /// Handle the invalidation message for the value \p Value. - virtual void handleDeleteNotification(SILNode *value) { } - - /// Returns True if the pass, analysis or other entity wants to receive - /// notifications. This callback is called once when the class is being - /// registered, and not once per notification. Entities that implement - /// this callback should always return a constant answer (true/false). - virtual bool needsNotifications() { return false; } -}; - } // namespace swift #endif diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index 3168541b2c756..fee40c6d0fee0 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -127,7 +127,10 @@ public llvm::ilist_node, public SILAllocated { void push_back(SILInstruction *I); void push_front(SILInstruction *I); void remove(SILInstruction *I); - iterator erase(SILInstruction *I); + void erase(SILInstruction *I); + void erase(SILInstruction *I, SILModule &module); + + void eraseAllInstructions(SILModule &module); SILInstruction &back() { return InstList.back(); } const SILInstruction &back() const { @@ -439,8 +442,6 @@ public llvm::ilist_node, public SILAllocated { I.dropAllReferences(); } - void eraseInstructions(); - private: friend class SILArgument; diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 31f48012bfe18..e3809219194fb 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -981,6 +981,10 @@ class SILFunction void clear(); + /// Like `clear`, but does not call `dropAllReferences`, which is the + /// responsibility of the caller. + void eraseAllBlocks(); + /// Return the identity substitutions necessary to forward this call if it is /// generic. SubstitutionMap getForwardingSubstitutionMap(); diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index 096247af2d3b4..185177b2ecd61 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -181,6 +181,11 @@ class SILGlobalVariable StaticInitializerBlock.dropAllReferences(); } + void clear() { + dropAllReferences(); + StaticInitializerBlock.eraseAllInstructions(Module); + } + /// Return whether this variable corresponds to a Clang node. bool hasClangNode() const; diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index d111fff0493d2..4d8bd48c24fd8 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -320,6 +320,7 @@ class SILInstruction : public llvm::ilist_node { friend llvm::ilist_traits; friend llvm::ilist_traits; friend SILBasicBlock; + friend SILModule; /// A backreference to the containing basic block. This is maintained by /// ilist_traits. @@ -381,6 +382,10 @@ class SILInstruction : public llvm::ilist_node { return C.allocateInst(Bytes, Alignment); } + /// Returns true if this instruction is removed from its function and + /// scheduled to be deleted. + bool isDeleted() const { return !ParentBB; } + enum class MemoryBehavior { None, /// The instruction may read memory. diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index bd9622f55a4c0..54396034e24a6 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -189,6 +189,17 @@ class SILModule { /// For consistency checking. size_t numAllocatedSlabs = 0; + /// When an instruction is "deleted" from the SIL, it is put into this list. + /// The instructions in this list are eventually deleted for real in + /// flushDeletedInsts(), which is called by the pass manager after each pass + /// run. + /// In other words: instruction deletion is deferred to the end of a pass. + /// + /// This avoids dangling instruction pointers within the run of a pass and in + /// analysis caches. Note that the analysis invalidation mechanism ensures + /// that analysis caches are invalidated before flushDeletedInsts(). + llvm::iplist scheduledForDeletion; + /// The swift Module associated with this SILModule. ModuleDecl *TheSwiftModule; @@ -334,10 +345,6 @@ class SILModule { /// Action to be executed for serializing the SILModule. ActionCallback SerializeSILAction; - /// A list of clients that need to be notified when an instruction - /// invalidation message is sent. - llvm::SetVector NotificationHandlers; - SILModule(llvm::PointerUnion context, Lowering::TypeConverter &TC, const SILOptions &Options); @@ -413,16 +420,6 @@ class SILModule { /// Called after an instruction is moved from one function to another. void notifyMovedInstruction(SILInstruction *inst, SILFunction *fromFunction); - /// Add a delete notification handler \p Handler to the module context. - void registerDeleteNotificationHandler(DeleteNotificationHandler* Handler); - - /// Remove the delete notification handler \p Handler from the module context. - void removeDeleteNotificationHandler(DeleteNotificationHandler* Handler); - - /// Send the invalidation message that \p V is being deleted to all - /// registered handlers. The order of handlers is deterministic but arbitrary. - void notifyDeleteHandlers(SILNode *node); - /// Set a serialization action. void setSerializeSILAction(ActionCallback SerializeSILAction); ActionCallback getSerializeSILAction() const; @@ -849,8 +846,17 @@ class SILModule { /// Allocate memory for an instruction using the module's internal allocator. void *allocateInst(unsigned Size, unsigned Align) const; - /// Deallocate memory of an instruction. - void deallocateInst(SILInstruction *I); + /// Called before \p I is removed from its basic block and scheduled for + /// deletion. + void willDeleteInstruction(SILInstruction *I); + + /// Schedules the (already removed) instruction \p I for deletion. + /// See scheduledForDeletion for details. + void scheduleForDeletion(SILInstruction *I); + + /// Deletes all scheuled instructions for real. + /// See scheduledForDeletion for details. + void flushDeletedInsts(); /// Looks up the llvm intrinsic ID and type for the builtin function. /// diff --git a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h index 1aba911f6ce8a..1ac6109973359 100644 --- a/include/swift/SILOptimizer/Analysis/AliasAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/AliasAnalysis.h @@ -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 @@ class AliasAnalysis : public SILAnalysis { }; 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; + + /// A key used for the MemoryBehavior Analysis cache. + using MemBehaviorCacheKey = std::pair; + + using ScopeCacheKey = std::pair; using TBAACacheKey = std::pair; + 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 @@ class AliasAnalysis : public SILAnalysis { /// AliasAnalysis value cache. /// /// The alias() method uses this map to cache queries. - llvm::DenseMap AliasCache; + llvm::DenseMap AliasCache; using MemoryBehavior = SILInstruction::MemoryBehavior; + /// MemoryBehavior value cache. /// /// The computeMemoryBehavior() method uses this map to cache queries. - llvm::DenseMap MemoryBehaviorCache; + llvm::DenseMap 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 instsInImmutableScopes; + llvm::DenseSet instsInImmutableScopes; /// Computed immutable scopes. /// - /// Contains the begin-scope instruction's indices (e.g. begin_access) of - /// all computed scopes. - llvm::DenseSet 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 ValueToIndex; - ValueEnumerator InstructionToIndex; + /// Contains the begin-scope instructions (e.g. begin_access) of all computed + /// scopes. + llvm::SmallPtrSet immutableScopeComputed; AliasResult aliasAddressProjection(SILValue V1, SILValue V2, SILValue O1, SILValue O2); @@ -144,34 +120,15 @@ class AliasAnalysis : public SILAnalysis { /// 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) { - } - - static bool classof(const SILAnalysis *S) { - return S->getKind() == SILAnalysisKind::Alias; - } - - virtual void initialize(SILPassManager *PM) override; + AliasAnalysis(SideEffectAnalysis *SEA, EscapeAnalysis *EA) + : SEA(SEA), EA(EA) {} - /// 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 @@ class AliasAnalysis : public SILAnalysis { /// 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 { - static inline AliasKeyTy getEmptyKey() { - auto Allone = std::numeric_limits::max(); - return {0, Allone, nullptr, nullptr}; - } - static inline AliasKeyTy getTombstoneKey() { - auto Allone = std::numeric_limits::max(); - return {Allone, 0, nullptr, nullptr}; - } - static unsigned getHashValue(const AliasKeyTy Val) { - unsigned H = 0; - H ^= DenseMapInfo::getHashValue(Val.V1); - H ^= DenseMapInfo::getHashValue(Val.V2); - H ^= DenseMapInfo::getHashValue(Val.T1); - H ^= DenseMapInfo::getHashValue(Val.T2); - return H; - } - static bool isEqual(const AliasKeyTy LHS, const AliasKeyTy RHS) { - return LHS.V1 == RHS.V1 && - LHS.V2 == RHS.V2 && - LHS.T1 == RHS.T1 && - LHS.T2 == RHS.T2; - } - }; +template <> struct DenseMapInfo { + using AliasCacheKey = swift::AliasAnalysis::AliasCacheKey; - template <> struct DenseMapInfo { - static inline MemBehaviorKeyTy getEmptyKey() { - auto Allone = std::numeric_limits::max(); - return {0, Allone}; - } - static inline MemBehaviorKeyTy getTombstoneKey() { - auto Allone = std::numeric_limits::max(); - return {Allone, 0}; - } - static unsigned getHashValue(const MemBehaviorKeyTy V) { - unsigned H = 0; - H ^= DenseMapInfo::getHashValue(V.V1); - H ^= DenseMapInfo::getHashValue(V.V2); - return H; - } - static bool isEqual(const MemBehaviorKeyTy LHS, - const MemBehaviorKeyTy RHS) { - return LHS.V1 == RHS.V1 && LHS.V2 == RHS.V2; - } - }; + static inline AliasCacheKey getEmptyKey() { + return {DenseMapInfo::getEmptyKey(), swift::SILValue(), + nullptr, nullptr}; + } + static inline AliasCacheKey getTombstoneKey() { + return {DenseMapInfo::getTombstoneKey(), swift::SILValue(), + nullptr, nullptr}; + } + static unsigned getHashValue(const AliasCacheKey Val) { + unsigned H = 0; + H ^= DenseMapInfo::getHashValue(Val.V1); + H ^= DenseMapInfo::getHashValue(Val.V2); + H ^= DenseMapInfo::getHashValue(Val.T1); + H ^= DenseMapInfo::getHashValue(Val.T2); + return H; + } + static bool isEqual(const AliasCacheKey LHS, const AliasCacheKey RHS) { + return LHS.V1 == RHS.V1 && + LHS.V2 == RHS.V2 && + LHS.T1 == RHS.T1 && + LHS.T2 == RHS.T2; + } +}; } #endif diff --git a/include/swift/SILOptimizer/Analysis/Analysis.h b/include/swift/SILOptimizer/Analysis/Analysis.h index 938f4f9e1a2b8..e0c5c931574e5 100644 --- a/include/swift/SILOptimizer/Analysis/Analysis.h +++ b/include/swift/SILOptimizer/Analysis/Analysis.h @@ -36,7 +36,7 @@ struct SILAnalysisKind { }; /// The base class for all SIL-level analysis. -class SILAnalysis : public DeleteNotificationHandler { +class SILAnalysis { public: /// This is a list of values that allow passes to communicate to analysis /// which traits of the code were invalidated. Based on this information diff --git a/include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h b/include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h index d483ca695e2ea..2f752d4935431 100644 --- a/include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h @@ -235,12 +235,6 @@ class EpilogueARCFunctionInfo { llvm::DenseMap EpilogueReleaseInstCache; public: - void handleDeleteNotification(SILNode *node) { - // Being conservative and clear everything for now. - EpilogueRetainInstCache.clear(); - EpilogueReleaseInstCache.clear(); - } - /// Constructor. EpilogueARCFunctionInfo(SILFunction *F, PostOrderAnalysis *PO, AliasAnalysis *AA, RCIdentityAnalysis *RC) @@ -273,38 +267,21 @@ class EpilogueARCFunctionInfo { }; class EpilogueARCAnalysis : public FunctionAnalysisBase { + /// 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( - SILAnalysisKind::EpilogueARC), - PO(nullptr), AA(nullptr), RC(nullptr) {} + SILAnalysisKind::EpilogueARC) {} EpilogueARCAnalysis(const EpilogueARCAnalysis &) = delete; EpilogueARCAnalysis &operator=(const EpilogueARCAnalysis &) = delete; - virtual void handleDeleteNotification(SILNode *node) override { - // If the parent function of this instruction was just turned into an - // external declaration, bail. This happens during SILFunction destruction. - SILFunction *F = node->getFunction(); - if (F->isExternalDeclaration()) { - return; - } - - // If we do have an analysis, tell it to handle its delete notifications. - if (auto A = maybeGet(F)) { - A.get()->handleDeleteNotification(node); - } - } - - virtual bool needsNotifications() override { return true; } - static bool classof(const SILAnalysis *S) { return S->getKind() == SILAnalysisKind::EpilogueARC; } @@ -312,9 +289,7 @@ class EpilogueARCAnalysis : public FunctionAnalysisBase virtual void initialize(SILPassManager *PM) override; virtual std::unique_ptr - newFunctionAnalysis(SILFunction *F) override { - return std::make_unique(F, PO, AA, RC); - } + newFunctionAnalysis(SILFunction *F) override; virtual bool shouldInvalidate(SILAnalysis::InvalidationKind K) override { return true; diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index 46c618b35f60d..ff62e3e4147f5 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -832,11 +832,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Propagates the escape states through the graph. void propagateEscapeStates(); - /// Remove a value from the graph. Do not delete the mapped node, but reset - /// mappedValue if it is set to this value, and make sure that the node - /// cannot be looked up with getNode(). - void removeFromGraph(ValueBase *V); - enum class Traversal { Follow, Backtrack, Halt }; /// Traverse backward from startNode, following predecessor edges. @@ -1237,10 +1232,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Notify the analysis about changed witness or vtables. virtual void invalidateFunctionTables() override { } - virtual void handleDeleteNotification(SILNode *N) override; - - virtual bool needsNotifications() override { return true; } - virtual void verify() const override; virtual void verify(SILFunction *F) const override; diff --git a/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h b/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h index 3064fc889abb2..9311142ccbdb5 100644 --- a/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/RCIdentityAnalysis.h @@ -69,20 +69,6 @@ class RCIdentityFunctionInfo { /// intermediate array. void visitRCUses(SILValue V, function_ref Visitor); - void handleDeleteNotification(SILNode *node) { - auto value = dyn_cast(node); - if (!value) - return; - - // Check the cache. If we don't find it, there is nothing to do. - auto Iter = RCCache.find(SILValue(value)); - if (Iter == RCCache.end()) - return; - - // Then erase Iter from the cache. - RCCache.erase(Iter); - } - private: SILValue getRCIdentityRootInner(SILValue V, unsigned RecursionDepth); SILValue stripRCIdentityPreservingOps(SILValue V, unsigned RecursionDepth); @@ -104,18 +90,6 @@ class RCIdentityAnalysis : public FunctionAnalysisBase { RCIdentityAnalysis(const RCIdentityAnalysis &) = delete; RCIdentityAnalysis &operator=(const RCIdentityAnalysis &) = delete; - virtual void handleDeleteNotification(SILNode *node) override { - // If the parent function of this instruction was just turned into an - // external declaration, bail. This happens during SILFunction destruction. - SILFunction *F = node->getFunction(); - if (F->isExternalDeclaration()) { - return; - } - get(F)->handleDeleteNotification(node); - } - - virtual bool needsNotifications() override { return true; } - static bool classof(const SILAnalysis *S) { return S->getKind() == SILAnalysisKind::RCIdentity; } diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index 8c82ff0169dc0..713891eab9c00 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -132,6 +132,15 @@ class SILPassManager { llvm_unreachable("Unable to find analysis for requested type."); } + template + T *getAnalysis(SILFunction *f) { + for (SILAnalysis *A : Analyses) { + if (A->getKind() == T::getAnalysisKind()) + return static_cast *>(A)->get(f); + } + llvm_unreachable("Unable to find analysis for requested type."); + } + /// \returns the module that the pass manager owns. SILModule *getModule() { return Mod; } diff --git a/include/swift/SILOptimizer/PassManager/Transforms.h b/include/swift/SILOptimizer/PassManager/Transforms.h index edc9177a11c4b..ce8d8169eb2a9 100644 --- a/include/swift/SILOptimizer/PassManager/Transforms.h +++ b/include/swift/SILOptimizer/PassManager/Transforms.h @@ -22,7 +22,7 @@ namespace swift { class PrettyStackTraceSILFunctionTransform; /// The base class for all SIL-level transformations. - class SILTransform : public DeleteNotificationHandler { + class SILTransform { public: /// The kind of transformation passes we use. enum class TransformKind { @@ -86,6 +86,9 @@ namespace swift { template T* getAnalysis() { return PM->getAnalysis(); } + template + T* getAnalysis(SILFunction *f) { return PM->getAnalysis(f); } + const SILOptions &getOptions() { return PM->getOptions(); } }; diff --git a/lib/SIL/IR/SILBasicBlock.cpp b/lib/SIL/IR/SILBasicBlock.cpp index 49de6f0604a84..a2ad877268446 100644 --- a/lib/SIL/IR/SILBasicBlock.cpp +++ b/lib/SIL/IR/SILBasicBlock.cpp @@ -41,35 +41,8 @@ SILBasicBlock::~SILBasicBlock() { return; } - SILModule &M = getModule(); - - // Invalidate all of the basic block arguments. - for (auto *Arg : ArgumentList) { - M.notifyDeleteHandlers(Arg); - } - dropAllReferences(); - - for (auto I = begin(), E = end(); I != E;) { - auto Inst = &*I; - ++I; - erase(Inst); - } - assert(InstList.empty()); -} - -void SILBasicBlock::clearStaticInitializerBlock(SILModule &module) { - assert(!getParent() && "not a global variable's static initializer block"); - assert(ArgumentList.empty() && - "a static initializer block must not have arguments"); - - for (auto I = begin(), E = end(); I != E;) { - SILInstruction *Inst = &*I; - ++I; - InstList.erase(Inst); - module.deallocateInst(Inst); - } - assert(InstList.empty()); + eraseAllInstructions(getModule()); } int SILBasicBlock::getDebugID() const { @@ -104,22 +77,21 @@ void SILBasicBlock::remove(SILInstruction *I) { InstList.remove(I); } -void SILBasicBlock::eraseInstructions() { - for (auto It = begin(); It != end();) { - auto *Inst = &*It++; - Inst->replaceAllUsesOfAllResultsWithUndef(); - Inst->eraseFromParent(); +void SILBasicBlock::eraseAllInstructions(SILModule &module) { + while (!empty()) { + erase(&*begin(), module); } } /// Returns the iterator following the erased instruction. -SILBasicBlock::iterator SILBasicBlock::erase(SILInstruction *I) { - // Notify the delete handlers that this instruction is going away. - SILModule &module = getModule(); - module.notifyDeleteHandlers(I->asSILNode()); - auto nextIter = InstList.erase(I); - module.deallocateInst(I); - return nextIter; +void SILBasicBlock::erase(SILInstruction *I) { + erase(I, getModule()); +} + +void SILBasicBlock::erase(SILInstruction *I, SILModule &module) { + module.willDeleteInstruction(I); + InstList.remove(I); + module.scheduleForDeletion(I); } /// This method unlinks 'self' from the containing SILFunction and deletes it. @@ -186,9 +158,6 @@ SILFunctionArgument *SILBasicBlock::replaceFunctionArgument( assert(ArgumentList[i]->use_empty() && "Expected no uses of the old arg!"); - // Notify the delete handlers that this argument is being deleted. - M.notifyDeleteHandlers(ArgumentList[i]); - SILFunctionArgument *NewArg = new (M) SILFunctionArgument(Ty, Kind, D); NewArg->setParent(this); @@ -212,9 +181,6 @@ SILPhiArgument *SILBasicBlock::replacePhiArgument(unsigned i, SILType Ty, assert(ArgumentList[i]->use_empty() && "Expected no uses of the old BB arg!"); - // Notify the delete handlers that this argument is being deleted. - M.notifyDeleteHandlers(ArgumentList[i]); - SILPhiArgument *NewArg = new (M) SILPhiArgument(Ty, Kind, D); NewArg->setParent(this); @@ -274,8 +240,6 @@ SILPhiArgument *SILBasicBlock::insertPhiArgument(unsigned AtArgPos, SILType Ty, void SILBasicBlock::eraseArgument(int Index) { assert(getArgument(Index)->use_empty() && "Erasing block argument that has uses!"); - // Notify the delete handlers that this BB argument is going away. - getModule().notifyDeleteHandlers(getArgument(Index)); ArgumentList.erase(ArgumentList.begin() + Index); } diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index a87ea09451cf6..dd5393412460c 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -201,16 +201,7 @@ SILFunction::~SILFunction() { auto &M = getModule(); for (auto &BB : *this) { - for (auto I = BB.begin(), E = BB.end(); I != E;) { - auto Inst = &*I; - ++I; - SILInstruction::destroy(Inst); - // TODO: It is only safe to directly deallocate an - // instruction if this BB is being removed in scope - // of destructing a SILFunction. - M.deallocateInst(Inst); - } - BB.InstList.clearAndLeakNodesUnsafely(); + BB.eraseAllInstructions(M); } assert(RefCount == 0 && @@ -689,6 +680,10 @@ bool SILFunction::isExternallyUsedSymbol() const { void SILFunction::clear() { dropAllReferences(); + eraseAllBlocks(); +} + +void SILFunction::eraseAllBlocks() { BlockList.clear(); } diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index d9519576fac20..347634305ef18 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -58,8 +58,7 @@ SILGlobalVariable::SILGlobalVariable(SILModule &Module, SILLinkage Linkage, } SILGlobalVariable::~SILGlobalVariable() { - StaticInitializerBlock.dropAllReferences(); - StaticInitializerBlock.clearStaticInitializerBlock(Module); + clear(); } bool SILGlobalVariable::isPossiblyUsedExternally() const { diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index 2af1c5c78067b..0180b9319f018 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -68,7 +68,6 @@ void llvm::ilist_traits::addNodeToList(SILInstruction *I) { void llvm::ilist_traits::removeNodeFromList(SILInstruction *I) { // When an instruction is removed from a BB, clear the parent pointer. - assert(I->ParentBB && "Not in a list!"); I->ParentBB = nullptr; } @@ -147,6 +146,12 @@ void SILInstruction::dropAllReferences() { OpI->drop(); } + if (auto *termInst = dyn_cast(this)) { + for (SILSuccessor &succ : termInst->getSuccessors()) { + succ = nullptr; + } + } + // If we have a function ref inst, we need to especially drop its function // argument so that it gets a proper ref decrement. if (auto *FRI = dyn_cast(this)) { diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 1096e93a77f33..dc486f95260fe 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -126,8 +126,9 @@ SILModule::~SILModule() { assert(!hasUnresolvedOpenedArchetypeDefinitions()); // Decrement ref count for each SILGlobalVariable with static initializers. - for (SILGlobalVariable &v : silGlobals) - v.dropAllReferences(); + for (SILGlobalVariable &v : silGlobals) { + v.clear(); + } for (auto vt : vtables) vt->~SILVTable(); @@ -143,10 +144,16 @@ SILModule::~SILModule() { F.dropDynamicallyReplacedFunction(); F.clearSpecializeAttrs(); } + + for (SILFunction &F : *this) { + F.eraseAllBlocks(); + } + flushDeletedInsts(); } void SILModule::checkForLeaks() const { - int instsInModule = 0; + int instsInModule = std::distance(scheduledForDeletion.begin(), + scheduledForDeletion.end()); for (const SILFunction &F : *this) { for (const SILBasicBlock &block : F) { instsInModule += std::distance(block.begin(), block.end()); @@ -231,8 +238,30 @@ void *SILModule::allocateInst(unsigned Size, unsigned Align) const { return AlignedAlloc(Size, Align); } -void SILModule::deallocateInst(SILInstruction *I) { - AlignedFree(I); +void SILModule::willDeleteInstruction(SILInstruction *I) { + // Update openedArchetypeDefs. + if (auto *svi = dyn_cast(I)) { + if (CanArchetypeType archeTy = svi->getOpenedArchetype()) { + OpenedArchetypeKey key = {archeTy, svi->getFunction()}; + assert(openedArchetypeDefs.lookup(key) == svi && + "archetype def was not registered"); + openedArchetypeDefs.erase(key); + } + } +} + +void SILModule::scheduleForDeletion(SILInstruction *I) { + I->dropAllReferences(); + scheduledForDeletion.push_back(I); + I->ParentBB = nullptr; +} + +void SILModule::flushDeletedInsts() { + while (!scheduledForDeletion.empty()) { + SILInstruction *inst = &*scheduledForDeletion.begin(); + scheduledForDeletion.erase(inst); + AlignedFree(inst); + } } SILWitnessTable * @@ -779,36 +808,6 @@ void SILModule::notifyMovedInstruction(SILInstruction *inst, } } -void SILModule::registerDeleteNotificationHandler( - DeleteNotificationHandler *handler) { - // Ask the handler (that can be an analysis, a pass, or some other data - // structure) if it wants to receive delete notifications. - if (handler->needsNotifications()) { - NotificationHandlers.insert(handler); - } -} - -void SILModule:: -removeDeleteNotificationHandler(DeleteNotificationHandler* Handler) { - NotificationHandlers.remove(Handler); -} - -void SILModule::notifyDeleteHandlers(SILNode *node) { - // Update openedArchetypeDefs. - if (auto *svi = dyn_cast(node)) { - if (CanArchetypeType archeTy = svi->getOpenedArchetype()) { - OpenedArchetypeKey key = {archeTy, svi->getFunction()}; - assert(openedArchetypeDefs.lookup(key) == svi && - "archetype def was not registered"); - openedArchetypeDefs.erase(key); - } - } - - for (auto *Handler : NotificationHandlers) { - Handler->handleDeleteNotification(node); - } -} - // TODO: We should have an "isNoReturn" bit on Swift's BuiltinInfo, but for // now, let's recognize noreturn intrinsics and builtins specially here. bool SILModule::isNoReturnBuiltinOrIntrinsic(Identifier Name) { diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index cf7c335093d4d..fbf38e82254e6 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -1767,7 +1767,9 @@ SILBasicBlock::iterator swift::removeBeginAccess(BeginAccessInst *beginAccess) { op->set(beginAccess->getSource()); } } - return beginAccess->getParent()->erase(beginAccess); + auto nextIter = std::next(beginAccess->getIterator()); + beginAccess->getParent()->erase(beginAccess); + return nextIter; } //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/ARC/ARCLoopOpts.cpp b/lib/SILOptimizer/ARC/ARCLoopOpts.cpp index aa54592497e51..24f0ace63479b 100644 --- a/lib/SILOptimizer/ARC/ARCLoopOpts.cpp +++ b/lib/SILOptimizer/ARC/ARCLoopOpts.cpp @@ -66,7 +66,7 @@ class ARCLoopOpts : public SILFunctionTransform { } // Get all of the analyses that we need. - auto *AA = getAnalysis(); + auto *AA = getAnalysis(F); auto *RCFI = getAnalysis()->get(F); auto *EAFI = getAnalysis()->get(F); auto *LRFI = getAnalysis()->get(F); diff --git a/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp b/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp index a3a0cb3598097..4b932aa15d5ba 100644 --- a/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp +++ b/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp @@ -260,7 +260,7 @@ class ARCSequenceOpts : public SILFunctionTransform { return; if (!EnableLoopARC) { - auto *AA = getAnalysis(); + auto *AA = getAnalysis(F); auto *POTA = getAnalysis(); auto *RCFI = getAnalysis()->get(F); auto *EAFI = getAnalysis()->get(F); @@ -288,7 +288,7 @@ class ARCSequenceOpts : public SILFunctionTransform { LA->unlockInvalidation(); } - auto *AA = getAnalysis(); + auto *AA = getAnalysis(F); auto *POTA = getAnalysis(); auto *RCFI = getAnalysis()->get(F); auto *EAFI = getAnalysis()->get(F); diff --git a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp index 421a3784253e0..3bdab76f0cf97 100644 --- a/lib/SILOptimizer/Analysis/AliasAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AliasAnalysis.cpp @@ -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(node)) - ValueToIndex.invalidateValue(value); - - if (auto *inst = dyn_cast(node)) { - InstructionToIndex.invalidateValue(inst); - - // When a MultipleValueInstruction is deleted, we have to invalidate all - // the instruction results. - if (auto *mvi = dyn_cast(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) { - SEA = PM->getAnalysis(); - EA = PM->getAnalysis(); -} +namespace { -SILAnalysis *swift::createAliasAnalysis(SILModule *M) { - return new AliasAnalysis(M); -} +class AliasAnalysisContainer : public FunctionAnalysisBase { + 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 + newFunctionAnalysis(SILFunction *F) override { + assert(EA && SEA && "dependent analysis not initialized"); + return std::make_unique(SEA, EA); + } -AliasKeyTy AliasAnalysis::toAliasKey(SILValue V1, SILValue V2, - SILType Type1, SILType Type2) { - ValueIndexTy idx1 = ValueToIndex.getIndex(V1); - assert(idx1 != std::numeric_limits::max() && - "~0 index reserved for empty/tombstone keys"); - ValueIndexTy idx2 = ValueToIndex.getIndex(V2); - assert(idx2 != std::numeric_limits::max() && - "~0 index reserved for empty/tombstone keys"); - void *t1 = Type1.getOpaqueValue(); - void *t2 = Type2.getOpaqueValue(); - return {idx1, idx2, t1, t2}; + virtual void initialize(SILPassManager *PM) override { + SEA = PM->getAnalysis(); + EA = PM->getAnalysis(); + } +}; + +} // end anonymous namespace + +SILAnalysis *swift::createAliasAnalysis(SILModule *M) { + return new AliasAnalysisContainer(); } diff --git a/lib/SILOptimizer/Analysis/EpilogueARCAnalysis.cpp b/lib/SILOptimizer/Analysis/EpilogueARCAnalysis.cpp index cd057ace2ec83..325895dbc4f4a 100644 --- a/lib/SILOptimizer/Analysis/EpilogueARCAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EpilogueARCAnalysis.cpp @@ -172,11 +172,17 @@ bool EpilogueARCContext::computeEpilogueARC() { //===----------------------------------------------------------------------===// void EpilogueARCAnalysis::initialize(SILPassManager *PM) { - AA = PM->getAnalysis(); + passManager = PM; PO = PM->getAnalysis(); RC = PM->getAnalysis(); } +std::unique_ptr +EpilogueARCAnalysis::newFunctionAnalysis(SILFunction *F) { + return std::make_unique(F, PO, + passManager->getAnalysis(F), RC); +} + SILAnalysis *swift::createEpilogueARCAnalysis(SILModule *M) { return new EpilogueARCAnalysis(M); } diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 10f397eb59aba..06a87fd280148 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -1271,15 +1271,6 @@ bool EscapeAnalysis::ConnectionGraph::forwardTraverseDefer( return true; } -void EscapeAnalysis::ConnectionGraph::removeFromGraph(ValueBase *V) { - CGNode *node = Values2Nodes.lookup(V); - if (!node) - return; - Values2Nodes.erase(V); - if (node->mappedValue == V) - node->mappedValue = nullptr; -} - //===----------------------------------------------------------------------===// // Dumping, Viewing and Verification //===----------------------------------------------------------------------===// @@ -2922,21 +2913,6 @@ void EscapeAnalysis::invalidate(SILFunction *F, InvalidationKind K) { } } -void EscapeAnalysis::handleDeleteNotification(SILNode *node) { - auto value = dyn_cast(node); - if (!value) return; - - if (SILBasicBlock *Parent = node->getParentBlock()) { - SILFunction *F = Parent->getParent(); - if (FunctionInfo *FInfo = Function2Info.lookup(F)) { - if (FInfo->isValid()) { - FInfo->Graph.removeFromGraph(value); - FInfo->SummaryGraph.removeFromGraph(value); - } - } - } -} - void EscapeAnalysis::verify() const { #ifndef NDEBUG for (auto Iter : Function2Info) { diff --git a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp index 39b0695aeafcd..39dcb0d09d936 100644 --- a/lib/SILOptimizer/Analysis/MemoryBehavior.cpp +++ b/lib/SILOptimizer/Analysis/MemoryBehavior.cpp @@ -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, 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::max() && - "~0 index reserved for empty/tombstone keys"); - ValueIndexTy idx2 = ValueToIndex.getIndex(V2); - assert(idx2 != std::numeric_limits::max() && - "~0 index reserved for empty/tombstone keys"); - return {idx1, idx2}; -} diff --git a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp index 2971a255c3904..70e4ae33723da 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp @@ -53,6 +53,7 @@ #define DEBUG_TYPE "array-property-opt" #include "ArrayOpt.h" +#include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/Analysis/LoopAnalysis.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index b73da1f4b685b..cd5cf1254d3a1 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -1467,7 +1467,7 @@ class LICM : public SILFunctionTransform { DominanceAnalysis *DA = PM->getAnalysis(); PostDominanceAnalysis *PDA = PM->getAnalysis(); - AliasAnalysis *AA = PM->getAnalysis(); + AliasAnalysis *AA = PM->getAnalysis(F); SideEffectAnalysis *SEA = PM->getAnalysis(); AccessedStorageAnalysis *ASA = getAnalysis(); DominanceInfo *DomTree = nullptr; diff --git a/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp b/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp index ab574e1f689e2..1b37cb62b7f61 100644 --- a/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp +++ b/lib/SILOptimizer/Mandatory/AccessMarkerElimination.cpp @@ -53,8 +53,9 @@ struct AccessMarkerElimination { } SILBasicBlock::iterator eraseInst(SILInstruction *inst) { + auto nextIter = std::next(inst->getIterator()); notifyErased(inst); - return inst->getParent()->erase(inst); + return nextIter; }; bool shouldPreserveAccess(SILAccessEnforcement enforcement); diff --git a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp index 8f11ab051fb7d..d7530d9f57b89 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp @@ -296,6 +296,9 @@ bool MandatoryCombiner::doOneIteration(SILFunction &function, // its contents to the worklist and then clear said list in preparation // for the next iteration. for (SILInstruction *instruction : createdInstructions) { + if (instruction->isDeleted()) + continue; + LLVM_DEBUG(llvm::dbgs() << "MC: add " << *instruction << " from tracking list to worklist\n"); worklist.add(instruction); @@ -400,26 +403,6 @@ class MandatoryCombine final : public SILFunctionTransform { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } } - -protected: - void handleDeleteNotification(SILNode *node) override { - // Remove instructions that were both created and deleted from the list of - // created instructions which will eventually be added to the worklist. - - auto *instruction = dyn_cast(node); - if (instruction == nullptr) { - return; - } - - // Linear searching the tracking list doesn't hurt because usually it only - // contains a few elements. - auto iterator = find(createdInstructions, instruction); - if (createdInstructions.end() != iterator) { - createdInstructions.erase(iterator); - } - } - - bool needsNotifications() override { return true; } }; } // end anonymous namespace diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp index 08551142e1ce4..3acad7765303d 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp @@ -417,46 +417,6 @@ static void cleanupCalleeValue(SILValue calleeValue, namespace { /// Cleanup dead closures after inlining. class ClosureCleanup { - using DeadInstSet = SmallBlotSetVector; - - /// A helper class to update the set of dead instructions. - /// - /// Since this is called by the SILModule callback, the instruction may longer - /// be well-formed. Do not visit its operands. However, it's position in the - /// basic block is still valid. - /// - /// FIXME: Using the Module's callback mechanism for this is terrible. - /// Instead, cleanupCalleeValue could be easily rewritten to use its own - /// instruction deletion helper and pass a callback to tryDeleteDeadClosure - /// and recursivelyDeleteTriviallyDeadInstructions. - class DeleteUpdateHandler : public DeleteNotificationHandler { - SILModule &Module; - DeadInstSet &DeadInsts; - - public: - DeleteUpdateHandler(SILModule &M, DeadInstSet &DeadInsts) - : Module(M), DeadInsts(DeadInsts) { - Module.registerDeleteNotificationHandler(this); - } - - ~DeleteUpdateHandler() override { - // Unregister the handler. - Module.removeDeleteNotificationHandler(this); - } - - // Handling of instruction removal notifications. - bool needsNotifications() override { return true; } - - // Handle notifications about removals of instructions. - void handleDeleteNotification(SILNode *node) override { - auto deletedI = dyn_cast(node); - if (!deletedI) - return; - - DeadInsts.erase(deletedI); - } - }; - SmallBlotSetVector deadFunctionVals; public: @@ -500,9 +460,8 @@ class ClosureCleanup { // set needs to continue to be updated (by this handler) when deleting // instructions. This assumes that DeadFunctionValSet::erase() is stable. void cleanupDeadClosures(SILFunction *F) { - DeleteUpdateHandler deleteUpdate(F->getModule(), deadFunctionVals); for (Optional I : deadFunctionVals) { - if (!I.hasValue()) + if (!I.hasValue() || I.getValue()->isDeleted()) continue; if (auto *SVI = dyn_cast(I.getValue())) diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index 31829e7bdd4ec..5345a5c4f8c03 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -326,7 +326,6 @@ SILPassManager::SILPassManager(SILModule *M, bool isMandatory, for (SILAnalysis *A : Analyses) { A->initialize(this); - M->registerDeleteNotificationHandler(A); } std::unique_ptr handler( @@ -455,7 +454,6 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { } llvm::sys::TimePoint<> StartTime = std::chrono::system_clock::now(); - Mod->registerDeleteNotificationHandler(SFT); if (breakBeforeRunning(F->getName(), SFT)) LLVM_BUILTIN_DEBUGTRAP; if (SILForceVerifyAll || @@ -472,7 +470,7 @@ void SILPassManager::runPassOnFunction(unsigned TransIdx, SILFunction *F) { verifyAnalyses(F); } assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); - Mod->removeDeleteNotificationHandler(SFT); + Mod->flushDeletedInsts(); auto Delta = (std::chrono::system_clock::now() - StartTime).count(); if (SILPrintPassTime) { @@ -615,10 +613,9 @@ void SILPassManager::runModulePass(unsigned TransIdx) { llvm::sys::TimePoint<> StartTime = std::chrono::system_clock::now(); assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); - Mod->registerDeleteNotificationHandler(SMT); SMT->run(); - Mod->removeDeleteNotificationHandler(SMT); assert(analysesUnlocked() && "Expected all analyses to be unlocked!"); + Mod->flushDeletedInsts(); auto Delta = (std::chrono::system_clock::now() - StartTime).count(); if (SILPrintPassTime) { @@ -731,7 +728,6 @@ SILPassManager::~SILPassManager() { // delete the analysis. for (auto *A : Analyses) { - Mod->removeDeleteNotificationHandler(A); assert(!A->isLocked() && "Deleting a locked analysis. Did we forget to unlock ?"); delete A; diff --git a/lib/SILOptimizer/SILCombiner/SILCombine.cpp b/lib/SILOptimizer/SILCombiner/SILCombine.cpp index f254a2a80d919..438c288f8edfb 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombine.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombine.cpp @@ -317,9 +317,11 @@ bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) { if (TrackingList.size() && Builder.hasOwnership()) { SmallSetVector defsToCanonicalize; for (auto *trackedInst : TrackingList) { - if (auto *cvi = dyn_cast(trackedInst)) { - defsToCanonicalize.insert( - CanonicalizeOSSALifetime::getCanonicalCopiedDef(cvi)); + if (!trackedInst->isDeleted()) { + if (auto *cvi = dyn_cast(trackedInst)) { + defsToCanonicalize.insert( + CanonicalizeOSSALifetime::getCanonicalCopiedDef(cvi)); + } } } if (defsToCanonicalize.size()) { @@ -336,9 +338,11 @@ bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) { } } for (SILInstruction *I : TrackingList) { - LLVM_DEBUG(llvm::dbgs() << "SC: add " << *I - << " from tracking list to worklist\n"); - Worklist.add(I); + if (!I->isDeleted()) { + LLVM_DEBUG(llvm::dbgs() << "SC: add " << *I + << " from tracking list to worklist\n"); + Worklist.add(I); + } } TrackingList.clear(); } @@ -387,7 +391,7 @@ class SILCombine : public SILFunctionTransform { /// The entry point to the transformation. void run() override { - auto *AA = PM->getAnalysis(); + auto *AA = PM->getAnalysis(getFunction()); auto *DA = PM->getAnalysis(); auto *PCA = PM->getAnalysis(); auto *CHA = PM->getAnalysis(); @@ -408,20 +412,6 @@ class SILCombine : public SILFunctionTransform { invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody); } } - - void handleDeleteNotification(SILNode *node) override { - auto I = dyn_cast(node); - if (!I) return; - - // Linear searching the tracking list doesn't hurt because usually it only - // contains a few elements. - auto Iter = std::find(TrackingList.begin(), TrackingList.end(), I); - if (Iter != TrackingList.end()) - TrackingList.erase(Iter); - } - - bool needsNotifications() override { return true; } - }; } // end anonymous namespace diff --git a/lib/SILOptimizer/Transforms/ARCCodeMotion.cpp b/lib/SILOptimizer/Transforms/ARCCodeMotion.cpp index 9baaf56dea80c..c731ec820cd75 100644 --- a/lib/SILOptimizer/Transforms/ARCCodeMotion.cpp +++ b/lib/SILOptimizer/Transforms/ARCCodeMotion.cpp @@ -1189,7 +1189,7 @@ class ARCCodeMotion : public SILFunctionTransform { POA->invalidateFunction(F); auto *PO = POA->get(F); - auto *AA = PM->getAnalysis(); + auto *AA = PM->getAnalysis(F); auto *RCFI = PM->getAnalysis()->get(F); llvm::SpecificBumpPtrAllocator BPA; diff --git a/lib/SILOptimizer/Transforms/COWOpts.cpp b/lib/SILOptimizer/Transforms/COWOpts.cpp index de13369819e7f..7a2c0e96e2769 100644 --- a/lib/SILOptimizer/Transforms/COWOpts.cpp +++ b/lib/SILOptimizer/Transforms/COWOpts.cpp @@ -89,7 +89,7 @@ void COWOptsPass::run() { LLVM_DEBUG(llvm::dbgs() << "*** RedundantPhiElimination on function: " << F->getName() << " ***\n"); - AA = PM->getAnalysis(); + AA = PM->getAnalysis(F); bool changed = false; for (SILBasicBlock &block : *F) { diff --git a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp index 8d01e26413bb8..8cd1566a9c545 100644 --- a/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadObjectElimination.cpp @@ -785,34 +785,6 @@ namespace { class DeadObjectElimination : public SILFunctionTransform { llvm::DenseMap DestructorAnalysisCache; - // This is not a SILBasicBlock::iterator because we need a null value and we - // don't have a specific block whose end() we could use. - SILInstruction *nextInstToProcess = nullptr; - - /// Advance nextInstToProcess to the next instruction in its block. - /// If nextInstToProcess is the last instruction in its block, it is set to - /// null. - void advance() { - if (!nextInstToProcess) - return; - SILBasicBlock *block = nextInstToProcess->getParent(); - auto nextIter = std::next(nextInstToProcess->getIterator()); - if (nextIter == block->end()) { - nextInstToProcess = nullptr; - } else { - nextInstToProcess = &*nextIter; - } - } - - void handleDeleteNotification(SILNode *node) override { - if (auto *inst = dyn_cast(node)) { - if (inst == nextInstToProcess) - advance(); - } - } - - bool needsNotifications() override { return true; } - bool processAllocRef(AllocRefInst *ARI); bool processAllocStack(AllocStackInst *ASI); bool processKeyPath(KeyPathInst *KPI); @@ -826,13 +798,19 @@ class DeadObjectElimination : public SILFunctionTransform { bool Changed = false; for (auto &BB : Fn) { - nextInstToProcess = &BB.front(); - - // We cannot just iterate over the instructions, because the processing - // functions might deleted instruction before or after the current - // instruction - and also inst itself. - while (SILInstruction *inst = nextInstToProcess) { - advance(); + + llvm::SmallVector instsToProcess; + for (SILInstruction &inst : BB) { + if (isa(&inst) || + isAllocatingApply(&inst) || + isa(&inst)) { + instsToProcess.push_back(&inst); + } + } + + for (SILInstruction *inst : instsToProcess) { + if (inst->isDeleted()) + continue; if (auto *A = dyn_cast(inst)) Changed |= processAllocRef(A); diff --git a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp index 41a146cf09e66..c8e6bd4d2872a 100644 --- a/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadStoreElimination.cpp @@ -1275,7 +1275,7 @@ class DeadStoreElimination : public SILFunctionTransform { LLVM_DEBUG(llvm::dbgs() << "*** DSE on function: " << F->getName() << " ***\n"); - auto *AA = PM->getAnalysis(); + auto *AA = PM->getAnalysis(F); auto *TE = PM->getAnalysis(); auto *EAFI = PM->getAnalysis()->get(F); diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 8b30d36e311f1..14e527762418c 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -306,6 +306,13 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { return FunctionType; } +static void eraseBlock(SILBasicBlock *block) { + for (SILInstruction &inst : *block) { + inst.replaceAllUsesOfAllResultsWithUndef(); + } + block->eraseFromParent(); +} + std::pair BridgedProperty::outline(SILModule &M) { // Get the function type. @@ -362,14 +369,10 @@ BridgedProperty::outline(SILModule &M) { // Delete the outlined instructions/blocks. if (Release) Release->eraseFromParent(); - OutlinedEntryBB->eraseInstructions(); - OutlinedEntryBB->eraseFromParent(); - switchInfo.NoneBB->eraseInstructions(); - switchInfo.NoneBB->eraseFromParent(); - switchInfo.SomeBB->eraseInstructions(); - switchInfo.SomeBB->eraseFromParent(); - OldMergeBB->eraseInstructions(); - OldMergeBB->eraseFromParent(); + eraseBlock(OutlinedEntryBB); + eraseBlock(switchInfo.NoneBB); + eraseBlock(switchInfo.SomeBB); + eraseBlock(OldMergeBB); return std::make_pair(nullptr, std::prev(StartBB->end())); } @@ -904,14 +907,10 @@ void BridgedReturn::outline(SILFunction *Fun, ApplyInst *NewOutlinedCall) { // Outlined function already existed. Just delete instructions and wire up // blocks. if (!Fun) { - OutlinedEntryBB->eraseInstructions(); - OutlinedEntryBB->eraseFromParent(); - switchInfo.NoneBB->eraseInstructions(); - switchInfo.NoneBB->eraseFromParent(); - switchInfo.SomeBB->eraseInstructions(); - switchInfo.SomeBB->eraseFromParent(); - OldMergeBB->eraseInstructions(); - OldMergeBB->eraseFromParent(); + eraseBlock(OutlinedEntryBB); + eraseBlock(switchInfo.NoneBB); + eraseBlock(switchInfo.SomeBB); + eraseBlock(OldMergeBB); return; } diff --git a/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp b/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp index 5a71965f8be34..c3b8ee87d9a56 100644 --- a/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp +++ b/lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp @@ -1681,7 +1681,7 @@ class RedundantLoadElimination : public SILFunctionTransform { LLVM_DEBUG(llvm::dbgs() << "*** RLE on function: " << F->getName() << " ***\n"); - auto *AA = PM->getAnalysis(); + auto *AA = PM->getAnalysis(F); auto *TE = PM->getAnalysis(); auto *PO = PM->getAnalysis()->get(F); auto *EAFI = PM->getAnalysis()->get(F); diff --git a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp index b0a1c444f8899..5f6cd3a25a23f 100644 --- a/lib/SILOptimizer/Transforms/SILCodeMotion.cpp +++ b/lib/SILOptimizer/Transforms/SILCodeMotion.cpp @@ -1778,7 +1778,7 @@ class SILCodeMotion : public SILFunctionTransform { if (F->hasOwnership()) return; - auto *AA = getAnalysis(); + auto *AA = getAnalysis(F); auto *PO = getAnalysis()->get(F); auto *RCIA = getAnalysis()->get(getFunction()); diff --git a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp index 9679c17609cba..26c41f8940543 100644 --- a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp +++ b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp @@ -78,10 +78,8 @@ class TempLValueOptPass : public SILFunctionTransform { 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(); - - 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(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,8 +205,11 @@ 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(getFunction()); + // Check if the destination is not accessed within the liferange of // the temporary. // This is unlikely, because the destination is initialized at the @@ -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(inst)) { if (debugInst->getOperand() == copyInst->getSrc()) debugInsts.push_back(debugInst); } if (inst->mayReadOrWriteMemory()) - return false; + return; } - return false; } } // end anonymous namespace diff --git a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp index 993486709d9ed..becb48225bb9c 100644 --- a/lib/SILOptimizer/Transforms/TempRValueElimination.cpp +++ b/lib/SILOptimizer/Transforms/TempRValueElimination.cpp @@ -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 &loadInsts); bool collectLoadsFromProjection(SingleValueInstruction *projection, @@ -84,16 +82,17 @@ class TempRValueOptPass : public SILFunctionTransform { SmallPtrSetImpl &loadInsts); SILInstruction *getLastUseWhileSourceIsNotModified( - CopyAddrInst *copyInst, const SmallPtrSetImpl &useInsts); + CopyAddrInst *copyInst, const SmallPtrSetImpl &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 - 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 &useInsts) { + CopyAddrInst *copyInst, const SmallPtrSetImpl &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(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(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(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 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(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(user)->getDest() == tempObj) - return {std::next(si->getIterator()), false}; + return std::next(si->getIterator()); break; case SILInstructionKind::MarkDependenceInst: if (cast(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(); - bool changed = false; - // Find all copy_addr instructions. llvm::SmallSetVector deadCopies; for (auto &block : *getFunction()) { @@ -759,13 +758,12 @@ void TempRValueOptPass::run() { if (auto *copyInst = dyn_cast(&*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(&*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); } } diff --git a/lib/SILOptimizer/UtilityPasses/AADumper.cpp b/lib/SILOptimizer/UtilityPasses/AADumper.cpp index 6da3331d83768..6fd874947401a 100644 --- a/lib/SILOptimizer/UtilityPasses/AADumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/AADumper.cpp @@ -64,7 +64,7 @@ class SILAADumper : public SILModuleTransform { if (!gatherValues(Fn, Values)) continue; - AliasAnalysis *AA = PM->getAnalysis(); + AliasAnalysis *AA = PM->getAnalysis(&Fn); // A cache llvm::DenseMap Results; diff --git a/lib/SILOptimizer/UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp b/lib/SILOptimizer/UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp index c5bba2cd4a39b..9abaf9b53a382 100644 --- a/lib/SILOptimizer/UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/EpilogueRetainReleaseMatcherDumper.cpp @@ -39,13 +39,14 @@ namespace { class SILEpilogueRetainReleaseMatcherDumper : public SILModuleTransform { void run() override { - auto *AA = PM->getAnalysis(); auto *RCIA = getAnalysis(); for (auto &Fn: *getModule()) { // Function is not definition. if (!Fn.isDefinition()) continue; + auto *AA = PM->getAnalysis(&Fn); + llvm::outs() << "START: sil @" << Fn.getName() << "\n"; // Handle @owned return value. diff --git a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp index 7750ccd5403a9..6bd67c6f88ce9 100644 --- a/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp +++ b/lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp @@ -83,7 +83,7 @@ class MemBehaviorDumper : public SILModuleTransform { if (!gatherValues(Fn, Values)) continue; - AliasAnalysis *AA = PM->getAnalysis(); + AliasAnalysis *AA = PM->getAnalysis(&Fn); unsigned PairCount = 0; for (auto &BB : Fn) { diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 70f3b16e77035..ddfa6442d9115 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -25,6 +25,7 @@ #include "swift/SIL/SILModule.h" #include "swift/SIL/SILUndef.h" #include "swift/SIL/TypeLowering.h" +#include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" diff --git a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp index f0bba8d993a44..3d7c99510387e 100644 --- a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp +++ b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "swift/SILOptimizer/Analysis/ArraySemantic.h" +#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" #include "swift/SILOptimizer/Utils/PerformanceInlinerUtils.h" #include "swift/AST/Module.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt index 3c5b0d0fdc6e1..594e54a20efa5 100644 --- a/unittests/Basic/CMakeLists.txt +++ b/unittests/Basic/CMakeLists.txt @@ -34,7 +34,6 @@ add_swift_unittest(SwiftBasicTests TransformRangeTest.cpp TypeLookupError.cpp UnicodeTest.cpp - ValueEnumeratorTest.cpp ${generated_tests} ) diff --git a/unittests/Basic/OptionSetTest.cpp b/unittests/Basic/OptionSetTest.cpp index eef5ff3fd892e..e5bd90036c4bf 100644 --- a/unittests/Basic/OptionSetTest.cpp +++ b/unittests/Basic/OptionSetTest.cpp @@ -12,7 +12,6 @@ #include "swift/Basic/OptionSet.h" #include "swift/Basic/Range.h" -#include "swift/Basic/ValueEnumerator.h" #include "gtest/gtest.h" using namespace swift; diff --git a/unittests/Basic/RangeTest.cpp b/unittests/Basic/RangeTest.cpp index d73488779c52b..34d4d3d836629 100644 --- a/unittests/Basic/RangeTest.cpp +++ b/unittests/Basic/RangeTest.cpp @@ -12,7 +12,6 @@ #include "swift/Basic/Range.h" #include "swift/Basic/OptionSet.h" -#include "swift/Basic/ValueEnumerator.h" #include "gtest/gtest.h" using namespace swift; diff --git a/unittests/Basic/ValueEnumeratorTest.cpp b/unittests/Basic/ValueEnumeratorTest.cpp deleted file mode 100644 index 65bb9cd470bcd..0000000000000 --- a/unittests/Basic/ValueEnumeratorTest.cpp +++ /dev/null @@ -1,76 +0,0 @@ -//===--- ValueEnumeratorTest.cpp ------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#include "swift/Basic/ValueEnumerator.h" -#include "swift/Basic/OptionSet.h" -#include "swift/Basic/Range.h" -#include "gtest/gtest.h" - -using namespace swift; - -TEST(ValueEnumerator, basic) { - - { - ValueEnumerator Trans; - // Check that indexing is persistent. - EXPECT_EQ(Trans.getIndex(99), Trans.getIndex(99)); - EXPECT_EQ(Trans.getIndex(100), Trans.getIndex(100)); - - // Check that we don't have collisions. - bool SameIndex = Trans.getIndex(82) == Trans.getIndex(73); - EXPECT_FALSE(SameIndex); - - // Check that invalidation works. - // After invalidation the old index must not be equal to the new index. - size_t oldIndex = Trans.getIndex(99); - Trans.invalidateValue(99); - size_t newIndex = Trans.getIndex(99); - EXPECT_FALSE(newIndex == oldIndex); - } - - { - const char *string_1 = "hello"; - const char *string_2 = "goodbye"; - const char *string_3 = ":-)"; - ValueEnumerator Trans; - EXPECT_EQ(Trans.getIndex(nullptr), Trans.getIndex(nullptr)); - EXPECT_EQ(Trans.getIndex(string_1), Trans.getIndex(string_1)); - EXPECT_EQ(Trans.getIndex(string_2), Trans.getIndex(string_2)); - - // Check that invalidation works. - size_t oldIndex = Trans.getIndex(string_3); - Trans.invalidateValue(string_3); - size_t newIndex = Trans.getIndex(string_3); - EXPECT_FALSE(newIndex == oldIndex); - - // Check that different values don't give the same index. - EXPECT_FALSE(Trans.getIndex(string_2) == Trans.getIndex(string_3)); - } - - { - ValueEnumerator Trans; - // Check a bunch of integers. - for (int i = 1; i < 10000; i++) { - EXPECT_TRUE(Trans.getIndex(0) != Trans.getIndex(i)); - } - - // Check that there are no accidental collisions. - for (int i = 0; i < 10000; i++) { - for (int j = 1; j < 10; j++) { - EXPECT_TRUE(Trans.getIndex(i) != Trans.getIndex(i + j)); - } - } - - // Check that indexing is still persistent. - EXPECT_EQ(Trans.getIndex(100), Trans.getIndex(100)); - } -}