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)); - ModulePassManager MPM; - FunctionPassManager FPM(&AM); + ModuleAnalysisManager MAM; + MAM.registerPass(FunctionAnalysisModuleProxy(FAM)); + + ModulePassManager MPM(&MAM); + + // 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); } }