From c49e7e6aeedc8d6fbe6cabb5b61551556fdcbb0b Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Wed, 20 Nov 2013 11:31:50 +0000 Subject: [PATCH] [PM] Add the preservation system to the new pass manager. This adds a new set-like type which represents a set of preserved analysis passes. The set is managed via the opaque PassT::ID() void*s. The expected convenience templates for interacting with specific passes are provided. It also supports a symbolic "all" state which is represented by an invalid pointer in the set. This state is nicely saturating as it comes up often. Finally, it supports intersection which is used when finding the set of preserved passes after N different transforms. The pass API is then changed to return the preserved set rather than a bool. This is much more self-documenting than the previous system. Returning "none" is a conservatively correct solution just like returning "true" from todays passes and not marking any passes as preserved. Passes can also be dynamically preserved or not throughout the run of the pass, and whatever gets returned is the binding state. Finally, preserving "all" the passes is allowed for no-op transforms that simply can't harm such things. Finally, the analysis managers are changed to instead of blindly invalidating all of the analyses, invalidate those which were not preserved. This should rig up all of the basic preservation functionality. This also correctly combines the preservation moving up from one IR-layer to the another and the preservation aggregation across N pass runs. Still to go is incrementally correct invalidation and preservation across IR layers incrementally during N pass runs. That will wait until we have a device for even exposing analyses across IR layers. While the core of this change is obvious, I'm not happy with the current testing, so will improve it to cover at least some of the invalidation that I can test easily in a subsequent commit. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@195241 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/IR/PassManager.h | 137 ++++++++++++++++++++++++++++++++++----- lib/IR/PassManager.cpp | 40 +++++++----- unittests/IR/PassManagerTest.cpp | 8 +-- 3 files changed, 145 insertions(+), 40 deletions(-) diff --git a/include/llvm/IR/PassManager.h b/include/llvm/IR/PassManager.h index 2664657694f..3fccdf4e3f2 100644 --- a/include/llvm/IR/PassManager.h +++ b/include/llvm/IR/PassManager.h @@ -36,6 +36,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/polymorphic_ptr.h" #include "llvm/Support/type_traits.h" #include "llvm/IR/Function.h" @@ -48,6 +49,106 @@ namespace llvm { class Module; class Function; +/// \brief An abstract set of preserved analyses following a transformation pass +/// run. +/// +/// When a transformation pass is run, it can return a set of analyses whose +/// results were preserved by that transformation. The default set is "none", +/// and preserving analyses must be done explicitly. +/// +/// There is also an explicit all state which can be used (for example) when +/// the IR is not mutated at all. +class PreservedAnalyses { +public: + /// \brief Convenience factory function for the empty preserved set. + static PreservedAnalyses none() { return PreservedAnalyses(); } + + /// \brief Construct a special preserved set that preserves all passes. + static PreservedAnalyses all() { + PreservedAnalyses PA; + PA.PreservedPassIDs.insert((void *)AllPassesID); + return PA; + } + + PreservedAnalyses &operator=(PreservedAnalyses Arg) { + swap(Arg); + return *this; + } + + void swap(PreservedAnalyses &Arg) { + PreservedPassIDs.swap(Arg.PreservedPassIDs); + } + + /// \brief Mark a particular pass as preserved, adding it to the set. + template void preserve() { + if (!areAllPreserved()) + PreservedPassIDs.insert(PassT::ID()); + } + + /// \brief Intersect this set with another in place. + /// + /// This is a mutating operation on this preserved set, removing all + /// preserved passes which are not also preserved in the argument. + void intersect(const PreservedAnalyses &Arg) { + if (Arg.areAllPreserved()) + return; + if (areAllPreserved()) { + PreservedPassIDs = Arg.PreservedPassIDs; + return; + } + for (SmallPtrSet::const_iterator I = PreservedPassIDs.begin(), + E = PreservedPassIDs.end(); + I != E; ++I) + if (!Arg.PreservedPassIDs.count(*I)) + PreservedPassIDs.erase(*I); + } + +#if LLVM_HAS_RVALUE_REFERENCES + /// \brief Intersect this set with a temporary other set in place. + /// + /// This is a mutating operation on this preserved set, removing all + /// preserved passes which are not also preserved in the argument. + void intersect(PreservedAnalyses &&Arg) { + if (Arg.areAllPreserved()) + return; + if (areAllPreserved()) { + PreservedPassIDs = std::move(Arg.PreservedPassIDs); + return; + } + for (SmallPtrSet::const_iterator I = PreservedPassIDs.begin(), + E = PreservedPassIDs.end(); + I != E; ++I) + if (!Arg.PreservedPassIDs.count(*I)) + PreservedPassIDs.erase(*I); + } +#endif + + /// \brief Query whether a pass is marked as preserved by this set. + template bool preserved() const { + return preserved(PassT::ID()); + } + + /// \brief Query whether an abstract pass ID is marked as preserved by this + /// set. + bool preserved(void *PassID) const { + return PreservedPassIDs.count((void *)AllPassesID) || + PreservedPassIDs.count(PassID); + } + +private: + // Note that this must not be -1 or -2 as those are already used by the + // SmallPtrSet. + static const uintptr_t AllPassesID = (intptr_t)-3; + + bool areAllPreserved() const { return PreservedPassIDs.count((void *)AllPassesID); } + + SmallPtrSet PreservedPassIDs; +}; + +inline void swap(PreservedAnalyses &LHS, PreservedAnalyses &RHS) { + LHS.swap(RHS); +} + /// \brief Implementation details of the pass manager interfaces. namespace detail { @@ -59,7 +160,7 @@ template struct PassConcept { virtual PassConcept *clone() = 0; /// \brief The polymorphic API which runs the pass over a given IR entity. - virtual bool run(T Arg) = 0; + virtual PreservedAnalyses run(T Arg) = 0; }; /// \brief A template wrapper used to implement the polymorphic API. @@ -70,7 +171,7 @@ template struct PassConcept { template struct PassModel : PassConcept { PassModel(PassT Pass) : Pass(llvm_move(Pass)) {} virtual PassModel *clone() { return new PassModel(Pass); } - virtual bool run(T Arg) { return Pass.run(Arg); } + virtual PreservedAnalyses run(T Arg) { return Pass.run(Arg); } PassT Pass; }; @@ -161,7 +262,7 @@ public: /// /// This method should only be called for a single module as there is the /// expectation that the lifetime of a pass is bounded to that of a module. - void run(Module *M); + PreservedAnalyses run(Module *M); template void addPass(ModulePassT Pass) { Passes.push_back(new ModulePassModel(llvm_move(Pass))); @@ -189,7 +290,7 @@ public: Passes.push_back(new FunctionPassModel(llvm_move(Pass))); } - bool run(Function *F); + PreservedAnalyses run(Function *F); private: // Pull in the concept type and model template specialized for functions. @@ -215,11 +316,13 @@ public: : Pass(llvm_move(Pass)) {} /// \brief Runs the function pass across every function in the module. - bool run(Module *M) { - bool Changed = false; - for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) - Changed |= Pass.run(I); - return Changed; + PreservedAnalyses run(Module *M) { + PreservedAnalyses PA = PreservedAnalyses::all(); + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) { + PreservedAnalyses PassPA = Pass.run(I); + PA.intersect(llvm_move(PassPA)); + } + return PA; } private: @@ -286,12 +389,9 @@ public: /// \brief Invalidate analyses cached for an IR Module. /// - /// Note that specific analysis results can disregard invalidation by - /// overriding their invalidate method. - /// - /// The module must be the module this analysis manager was constructed - /// around. - void invalidateAll(Module *M); + /// Walk through all of the analyses pertaining to this module and invalidate + /// them unless they are preserved by the PreservedAnalyses set. + void invalidate(Module *M, const PreservedAnalyses &PA); private: /// \brief Get a module pass result, running the pass if necessary. @@ -371,9 +471,10 @@ public: /// \brief Invalidate analyses cached for an IR Function. /// - /// Note that specific analysis results can disregard invalidation by - /// overriding the invalidate method. - void invalidateAll(Function *F); + /// Walk through all of the analyses cache for this IR function and + /// invalidate them unless they are preserved by the provided + /// PreservedAnalyses set. + void invalidate(Function *F, const PreservedAnalyses &PA); private: /// \brief Get a function pass result, running the pass if necessary. diff --git a/lib/IR/PassManager.cpp b/lib/IR/PassManager.cpp index 35fc534151a..0508b3cb281 100644 --- a/lib/IR/PassManager.cpp +++ b/lib/IR/PassManager.cpp @@ -12,20 +12,24 @@ using namespace llvm; -void ModulePassManager::run(Module *M) { - for (unsigned Idx = 0, Size = Passes.size(); Idx != Size; ++Idx) - if (Passes[Idx]->run(M)) - if (AM) - AM->invalidateAll(M); +PreservedAnalyses ModulePassManager::run(Module *M) { + PreservedAnalyses PA = PreservedAnalyses::all(); + for (unsigned Idx = 0, Size = Passes.size(); Idx != Size; ++Idx) { + PreservedAnalyses PassPA = Passes[Idx]->run(M); + if (AM) + AM->invalidate(M, PassPA); + PA.intersect(llvm_move(PassPA)); + } + return PA; } -void ModuleAnalysisManager::invalidateAll(Module *M) { +void ModuleAnalysisManager::invalidate(Module *M, const PreservedAnalyses &PA) { // FIXME: This is a total hack based on the fact that erasure doesn't // invalidate iteration for DenseMap. for (ModuleAnalysisResultMapT::iterator I = ModuleAnalysisResults.begin(), E = ModuleAnalysisResults.end(); I != E; ++I) - if (I->second->invalidate(M)) + if (!PA.preserved(I->first) && I->second->invalidate(M)) ModuleAnalysisResults.erase(I); } @@ -53,18 +57,18 @@ void ModuleAnalysisManager::invalidateImpl(void *PassID, Module *M) { ModuleAnalysisResults.erase(PassID); } -bool FunctionPassManager::run(Function *F) { - bool Changed = false; - for (unsigned Idx = 0, Size = Passes.size(); Idx != Size; ++Idx) - if (Passes[Idx]->run(F)) { - Changed = true; - if (AM) - AM->invalidateAll(F); - } - return Changed; +PreservedAnalyses FunctionPassManager::run(Function *F) { + PreservedAnalyses PA = PreservedAnalyses::all(); + for (unsigned Idx = 0, Size = Passes.size(); Idx != Size; ++Idx) { + PreservedAnalyses PassPA = Passes[Idx]->run(F); + if (AM) + AM->invalidate(F, PassPA); + PA.intersect(llvm_move(PassPA)); + } + return PA; } -void FunctionAnalysisManager::invalidateAll(Function *F) { +void FunctionAnalysisManager::invalidate(Function *F, const PreservedAnalyses &PA) { // Clear all the invalidated results associated specifically with this // function. SmallVector InvalidatedPassIDs; @@ -72,7 +76,7 @@ void FunctionAnalysisManager::invalidateAll(Function *F) { for (FunctionAnalysisResultListT::iterator I = ResultsList.begin(), E = ResultsList.end(); I != E;) - if (I->second->invalidate(F)) { + if (!PA.preserved(I->first) && I->second->invalidate(F)) { InvalidatedPassIDs.push_back(I->first); I = ResultsList.erase(I); } else { diff --git a/unittests/IR/PassManagerTest.cpp b/unittests/IR/PassManagerTest.cpp index 1e02d6ba02b..f2ce2214764 100644 --- a/unittests/IR/PassManagerTest.cpp +++ b/unittests/IR/PassManagerTest.cpp @@ -52,9 +52,9 @@ char TestAnalysisPass::PassID; struct TestModulePass { TestModulePass(int &RunCount) : RunCount(RunCount) {} - bool run(Module *M) { + PreservedAnalyses run(Module *M) { ++RunCount; - return true; + return PreservedAnalyses::none(); } int &RunCount; @@ -65,13 +65,13 @@ struct TestFunctionPass { int &AnalyzedInstrCount) : AM(AM), RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount) {} - bool run(Function *F) { + PreservedAnalyses run(Function *F) { ++RunCount; const TestAnalysisPass::Result &AR = AM.getResult(F); AnalyzedInstrCount += AR.InstructionCount; - return true; + return PreservedAnalyses::none(); } FunctionAnalysisManager &AM; -- 2.11.0