From: Chandler Carruth Date: Thu, 21 Nov 2013 02:11:31 +0000 (+0000) Subject: [PM] Add a module analysis pass proxy for the function analysis manager. X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=7fac06c4233ea2769fc1b6ed4bb1d23ce538a89f [PM] Add a module analysis pass proxy for the function analysis manager. This proxy will fill the role of proxying invalidation events down IR unit layers so that when a module changes we correctly invalidate function analyses. Currently this is a very coarse solution -- any change blows away the entire thing -- but the next step is to make invalidation handling more nuanced so that we can propagate specific amounts of invalidation from one layer to the next. The test is extended to place a module pass between two function pass managers each of which have preserved function analyses which get correctly invalidated by the module pass that might have changed what functions are even in the module. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@195304 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/IR/PassManager.h b/include/llvm/IR/PassManager.h index 3fccdf4e3f2..368ebaeb178 100644 --- a/include/llvm/IR/PassManager.h +++ b/include/llvm/IR/PassManager.h @@ -305,38 +305,6 @@ private: std::vector > Passes; }; -/// \brief Trivial adaptor that maps from a module to its functions. -/// -/// Designed to allow composition of a FunctionPass(Manager) and a -/// ModulePassManager. -template -class ModuleToFunctionPassAdaptor { -public: - explicit ModuleToFunctionPassAdaptor(FunctionPassT Pass) - : Pass(llvm_move(Pass)) {} - - /// \brief Runs the function pass across every function in the module. - 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: - FunctionPassT Pass; -}; - -/// \brief A function to deduce a function pass type and wrap it in the -/// templated adaptor. -template -ModuleToFunctionPassAdaptor -createModuleToFunctionPassAdaptor(FunctionPassT Pass) { - return ModuleToFunctionPassAdaptor(llvm_move(Pass)); -} - /// \brief A module analysis pass manager with lazy running and caching of /// results. class ModuleAnalysisManager { @@ -476,6 +444,17 @@ public: /// PreservedAnalyses set. void invalidate(Function *F, const PreservedAnalyses &PA); + /// \brief Returns true if the analysis manager has an empty results cache. + bool empty() const; + + /// \brief Clear the function analysis result cache. + /// + /// This routine allows cleaning up when the set of functions itself has + /// potentially changed, and thus we can't even look up a a result and + /// invalidate it directly. Notably, this does *not* call invalidate + /// functions as there is nothing to be done for them. + void clear(); + private: /// \brief Get a function pass result, running the pass if necessary. const detail::AnalysisResultConcept &getResultImpl(void *PassID, @@ -522,4 +501,104 @@ private: FunctionAnalysisResultMapT FunctionAnalysisResults; }; +/// \brief A module analysis which acts as a proxy for a function analysis +/// manager. +/// +/// This primarily proxies invalidation information from the module analysis +/// manager and module pass manager to a function analysis manager. You should +/// never use a function analysis manager from within (transitively) a module +/// pass manager unless your parent module pass has received a proxy result +/// object for it. +/// +/// FIXME: It might be really nice to "enforce" this (softly) by making this +/// proxy the API path to access a function analysis manager within a module +/// pass. +class FunctionAnalysisModuleProxy { +public: + typedef Module IRUnitT; + class Result; + + static void *ID() { return (void *)&PassID; } + + FunctionAnalysisModuleProxy(FunctionAnalysisManager &FAM) : FAM(FAM) {} + + /// \brief Run the analysis pass and create our proxy result object. + /// + /// This doesn't do any interesting work, it is primarily used to insert our + /// proxy result object into the module analysis cache so that we can proxy + /// invalidation to the function analysis manager. + /// + /// In debug builds, it will also assert that the analysis manager is empty + /// as no queries should arrive at the function analysis manager prior to + /// this analysis being requested. + Result run(Module *M); + +private: + static char PassID; + + FunctionAnalysisManager &FAM; +}; + +/// \brief The result proxy object for the \c FunctionAnalysisModuleProxy. +/// +/// See its documentation for more information. +class FunctionAnalysisModuleProxy::Result { +public: + Result(FunctionAnalysisManager &FAM) : FAM(FAM) {} + ~Result(); + + /// \brief Handler for invalidation of the module. + bool invalidate(Module *M); + +private: + FunctionAnalysisManager &FAM; +}; + +/// \brief Trivial adaptor that maps from a module to its functions. +/// +/// Designed to allow composition of a FunctionPass(Manager) and a +/// ModulePassManager. Note that if this pass is constructed with a pointer to +/// a \c ModuleAnalysisManager it will run the \c FunctionAnalysisModuleProxy +/// analysis prior to running the function pass over the module to enable a \c +/// FunctionAnalysisManager to be used within this run safely. +template +class ModuleToFunctionPassAdaptor { +public: + explicit ModuleToFunctionPassAdaptor(FunctionPassT Pass, + ModuleAnalysisManager *MAM = 0) + : Pass(llvm_move(Pass)), MAM(MAM) {} + + /// \brief Runs the function pass across every function in the module. + PreservedAnalyses run(Module *M) { + if (MAM) + // Pull in the analysis proxy so that the function analysis manager is + // appropriately set up. + (void)MAM->getResult(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: + FunctionPassT Pass; + ModuleAnalysisManager *MAM; +}; + +/// \brief A function to deduce a function pass type and wrap it in the +/// templated adaptor. +/// +/// \param MAM is an optional \c ModuleAnalysisManager which (if provided) will +/// be queried for a \c FunctionAnalysisModuleProxy to enable the function +/// pass(es) to safely interact with a \c FunctionAnalysisManager. +template +ModuleToFunctionPassAdaptor +createModuleToFunctionPassAdaptor(FunctionPassT Pass, + ModuleAnalysisManager *MAM = 0) { + return ModuleToFunctionPassAdaptor(llvm_move(Pass), MAM); +} + } diff --git a/lib/IR/PassManager.cpp b/lib/IR/PassManager.cpp index 0508b3cb281..50ace5880a5 100644 --- a/lib/IR/PassManager.cpp +++ b/lib/IR/PassManager.cpp @@ -87,6 +87,19 @@ void FunctionAnalysisManager::invalidate(Function *F, const PreservedAnalyses &P std::make_pair(InvalidatedPassIDs.pop_back_val(), F)); } +bool FunctionAnalysisManager::empty() const { + assert(FunctionAnalysisResults.empty() == + FunctionAnalysisResultLists.empty() && + "The storage and index of analysis results disagree on how many there " + "are!"); + return FunctionAnalysisResults.empty(); +} + +void FunctionAnalysisManager::clear() { + FunctionAnalysisResults.clear(); + FunctionAnalysisResultLists.clear(); +} + const detail::AnalysisResultConcept & FunctionAnalysisManager::getResultImpl(void *PassID, Function *F) { FunctionAnalysisResultMapT::iterator RI; @@ -117,3 +130,27 @@ void FunctionAnalysisManager::invalidateImpl(void *PassID, Function *F) { FunctionAnalysisResultLists[F].erase(RI->second); } + +char FunctionAnalysisModuleProxy::PassID; + +FunctionAnalysisModuleProxy::Result +FunctionAnalysisModuleProxy::run(Module *M) { + assert(FAM.empty() && "Function analyses ran prior to the module proxy!"); + return Result(FAM); +} + +FunctionAnalysisModuleProxy::Result::~Result() { + // Clear out the analysis manager if we're being destroyed -- it means we + // didn't even see an invalidate call when we got invalidated. + FAM.clear(); +} + +bool FunctionAnalysisModuleProxy::Result::invalidate(Module *M) { + // FIXME: We should pull the preserved analysis set into the invalidation + // handler so that we can detect when there is no need to clear the entire + // function analysis manager. + FAM.clear(); + + // Return false to indicate that this result is still a valid proxy. + return false; +} diff --git a/unittests/IR/PassManagerTest.cpp b/unittests/IR/PassManagerTest.cpp index f2ce2214764..7cb4f8d8b03 100644 --- a/unittests/IR/PassManagerTest.cpp +++ b/unittests/IR/PassManagerTest.cpp @@ -32,8 +32,11 @@ public: /// \brief Returns an opaque, unique ID for this pass type. static void *ID() { return (void *)&PassID; } + TestAnalysisPass(int &Runs) : Runs(Runs) {} + /// \brief Run the analysis pass over the function and return a result. Result run(Function *F) { + ++Runs; int Count = 0; for (Function::iterator BBI = F->begin(), BBE = F->end(); BBI != BBE; ++BBI) for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE; @@ -45,6 +48,8 @@ public: private: /// \brief Private static data to provide unique ID. static char PassID; + + int &Runs; }; char TestAnalysisPass::PassID; @@ -71,7 +76,7 @@ struct TestFunctionPass { const TestAnalysisPass::Result &AR = AM.getResult(F); AnalyzedInstrCount += AR.InstructionCount; - return PreservedAnalyses::none(); + return PreservedAnalyses::all(); } FunctionAnalysisManager &AM; @@ -106,25 +111,45 @@ public: }; TEST_F(PassManagerTest, Basic) { - FunctionAnalysisManager AM; - AM.registerPass(TestAnalysisPass()); + FunctionAnalysisManager FAM; + int AnalysisRuns = 0; + FAM.registerPass(TestAnalysisPass(AnalysisRuns)); + + ModuleAnalysisManager MAM; + MAM.registerPass(FunctionAnalysisModuleProxy(FAM)); + + ModulePassManager MPM(&MAM); - ModulePassManager MPM; - FunctionPassManager FPM(&AM); + // Count the runs over a Function. + FunctionPassManager FPM1(&FAM); + int FunctionPassRunCount1 = 0; + int AnalyzedInstrCount1 = 0; + FPM1.addPass(TestFunctionPass(FAM, FunctionPassRunCount1, AnalyzedInstrCount1)); + MPM.addPass(createModuleToFunctionPassAdaptor(FPM1, &MAM)); // Count the runs over a module. int ModulePassRunCount = 0; MPM.addPass(TestModulePass(ModulePassRunCount)); - // Count the runs over a Function. - int FunctionPassRunCount = 0; - int AnalyzedInstrCount = 0; - FPM.addPass(TestFunctionPass(AM, FunctionPassRunCount, AnalyzedInstrCount)); - MPM.addPass(createModuleToFunctionPassAdaptor(FPM)); + // Count the runs over a Function in a separate manager. + FunctionPassManager FPM2(&FAM); + int FunctionPassRunCount2 = 0; + int AnalyzedInstrCount2 = 0; + FPM2.addPass(TestFunctionPass(FAM, FunctionPassRunCount2, AnalyzedInstrCount2)); + MPM.addPass(createModuleToFunctionPassAdaptor(FPM2, &MAM)); MPM.run(M.get()); + + // Validate module pass counters. EXPECT_EQ(1, ModulePassRunCount); - EXPECT_EQ(3, FunctionPassRunCount); - EXPECT_EQ(5, AnalyzedInstrCount); + + // Validate both function pass counter sets. + EXPECT_EQ(3, FunctionPassRunCount1); + EXPECT_EQ(5, AnalyzedInstrCount1); + EXPECT_EQ(3, FunctionPassRunCount2); + EXPECT_EQ(5, AnalyzedInstrCount2); + + // Validate the analysis counters. + EXPECT_EQ(6, AnalysisRuns); } }