[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
This commit is contained in:
Chandler Carruth 2013-11-21 02:11:31 +00:00
parent 7b98dd33fd
commit 7fac06c423
3 changed files with 185 additions and 44 deletions

View File

@ -305,38 +305,6 @@ private:
std::vector<polymorphic_ptr<FunctionPassConcept> > Passes; std::vector<polymorphic_ptr<FunctionPassConcept> > Passes;
}; };
/// \brief Trivial adaptor that maps from a module to its functions.
///
/// Designed to allow composition of a FunctionPass(Manager) and a
/// ModulePassManager.
template <typename FunctionPassT>
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 <typename FunctionPassT>
ModuleToFunctionPassAdaptor<FunctionPassT>
createModuleToFunctionPassAdaptor(FunctionPassT Pass) {
return ModuleToFunctionPassAdaptor<FunctionPassT>(llvm_move(Pass));
}
/// \brief A module analysis pass manager with lazy running and caching of /// \brief A module analysis pass manager with lazy running and caching of
/// results. /// results.
class ModuleAnalysisManager { class ModuleAnalysisManager {
@ -476,6 +444,17 @@ public:
/// PreservedAnalyses set. /// PreservedAnalyses set.
void invalidate(Function *F, const PreservedAnalyses &PA); 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: private:
/// \brief Get a function pass result, running the pass if necessary. /// \brief Get a function pass result, running the pass if necessary.
const detail::AnalysisResultConcept<Function> &getResultImpl(void *PassID, const detail::AnalysisResultConcept<Function> &getResultImpl(void *PassID,
@ -522,4 +501,104 @@ private:
FunctionAnalysisResultMapT FunctionAnalysisResults; 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 <typename FunctionPassT>
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<FunctionAnalysisModuleProxy>(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 <typename FunctionPassT>
ModuleToFunctionPassAdaptor<FunctionPassT>
createModuleToFunctionPassAdaptor(FunctionPassT Pass,
ModuleAnalysisManager *MAM = 0) {
return ModuleToFunctionPassAdaptor<FunctionPassT>(llvm_move(Pass), MAM);
}
} }

View File

@ -87,6 +87,19 @@ void FunctionAnalysisManager::invalidate(Function *F, const PreservedAnalyses &P
std::make_pair(InvalidatedPassIDs.pop_back_val(), F)); 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<Function> & const detail::AnalysisResultConcept<Function> &
FunctionAnalysisManager::getResultImpl(void *PassID, Function *F) { FunctionAnalysisManager::getResultImpl(void *PassID, Function *F) {
FunctionAnalysisResultMapT::iterator RI; FunctionAnalysisResultMapT::iterator RI;
@ -117,3 +130,27 @@ void FunctionAnalysisManager::invalidateImpl(void *PassID, Function *F) {
FunctionAnalysisResultLists[F].erase(RI->second); 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;
}

View File

@ -32,8 +32,11 @@ public:
/// \brief Returns an opaque, unique ID for this pass type. /// \brief Returns an opaque, unique ID for this pass type.
static void *ID() { return (void *)&PassID; } static void *ID() { return (void *)&PassID; }
TestAnalysisPass(int &Runs) : Runs(Runs) {}
/// \brief Run the analysis pass over the function and return a result. /// \brief Run the analysis pass over the function and return a result.
Result run(Function *F) { Result run(Function *F) {
++Runs;
int Count = 0; int Count = 0;
for (Function::iterator BBI = F->begin(), BBE = F->end(); BBI != BBE; ++BBI) for (Function::iterator BBI = F->begin(), BBE = F->end(); BBI != BBE; ++BBI)
for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE; for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE;
@ -45,6 +48,8 @@ public:
private: private:
/// \brief Private static data to provide unique ID. /// \brief Private static data to provide unique ID.
static char PassID; static char PassID;
int &Runs;
}; };
char TestAnalysisPass::PassID; char TestAnalysisPass::PassID;
@ -71,7 +76,7 @@ struct TestFunctionPass {
const TestAnalysisPass::Result &AR = AM.getResult<TestAnalysisPass>(F); const TestAnalysisPass::Result &AR = AM.getResult<TestAnalysisPass>(F);
AnalyzedInstrCount += AR.InstructionCount; AnalyzedInstrCount += AR.InstructionCount;
return PreservedAnalyses::none(); return PreservedAnalyses::all();
} }
FunctionAnalysisManager &AM; FunctionAnalysisManager &AM;
@ -106,25 +111,45 @@ public:
}; };
TEST_F(PassManagerTest, Basic) { TEST_F(PassManagerTest, Basic) {
FunctionAnalysisManager AM; FunctionAnalysisManager FAM;
AM.registerPass(TestAnalysisPass()); int AnalysisRuns = 0;
FAM.registerPass(TestAnalysisPass(AnalysisRuns));
ModulePassManager MPM; ModuleAnalysisManager MAM;
FunctionPassManager FPM(&AM); 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. // Count the runs over a module.
int ModulePassRunCount = 0; int ModulePassRunCount = 0;
MPM.addPass(TestModulePass(ModulePassRunCount)); MPM.addPass(TestModulePass(ModulePassRunCount));
// Count the runs over a Function. // Count the runs over a Function in a separate manager.
int FunctionPassRunCount = 0; FunctionPassManager FPM2(&FAM);
int AnalyzedInstrCount = 0; int FunctionPassRunCount2 = 0;
FPM.addPass(TestFunctionPass(AM, FunctionPassRunCount, AnalyzedInstrCount)); int AnalyzedInstrCount2 = 0;
MPM.addPass(createModuleToFunctionPassAdaptor(FPM)); FPM2.addPass(TestFunctionPass(FAM, FunctionPassRunCount2, AnalyzedInstrCount2));
MPM.addPass(createModuleToFunctionPassAdaptor(FPM2, &MAM));
MPM.run(M.get()); MPM.run(M.get());
// Validate module pass counters.
EXPECT_EQ(1, ModulePassRunCount); 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);
} }
} }