Skip to content

Commit

Permalink
[PM] Widen the interface for invalidate on an analysis result now that
Browse files Browse the repository at this point in the history
it is completely optional, and sink the logic for handling the preserved
analysis set into it.

This allows us to implement the delegation logic desired in the proxy
module analysis for the function analysis manager where if the proxy
itself is preserved we assume the set of functions hasn't changed and we
do a fine grained invalidation by walking the functions in the module
and running the invalidate for them all at the manager level and letting
it try to invalidate any passes.

This in turn makes it blindingly obvious why we should hoist the
invalidate trait and have two collections of results. That allows
handling invalidation for almost all analyses without indirect calls and
it allows short circuiting when the preserved set is all.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@195338 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
chandlerc committed Nov 21, 2013
1 parent 0bafaac commit edd2b49
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 22 deletions.
53 changes: 39 additions & 14 deletions include/llvm/IR/PassManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,15 @@ template <typename IRUnitT> struct AnalysisResultConcept {

/// \brief Method to try and mark a result as invalid.
///
/// When the outer \c AnalysisManager detects a change in some underlying
/// When the outer analysis manager detects a change in some underlying
/// unit of the IR, it will call this method on all of the results cached.
///
/// \returns true if the result should indeed be invalidated (the default).
virtual bool invalidate(IRUnitT *IR) = 0;
/// This method also receives a set of preserved analyses which can be used
/// to avoid invalidation because the pass which changed the underlying IR
/// took care to update or preserve the analysis result in some way.
///
/// \returns true if the result is indeed invalid (the default).
virtual bool invalidate(IRUnitT *IR, const PreservedAnalyses &PA) = 0;
};

/// \brief Wrapper to model the analysis result concept.
Expand All @@ -198,19 +202,22 @@ template <typename IRUnitT> struct AnalysisResultConcept {
/// implementation so that the actual analysis result doesn't need to provide
/// an invalidation handler. It is only selected when the invalidation handler
/// is not part of the ResultT's interface.
template <typename IRUnitT, typename ResultT, bool HasInvalidateHandler = false>
template <typename IRUnitT, typename PassT, typename ResultT,
bool HasInvalidateHandler = false>
struct AnalysisResultModel : AnalysisResultConcept<IRUnitT> {
AnalysisResultModel(ResultT Result) : Result(llvm_move(Result)) {}
virtual AnalysisResultModel *clone() {
return new AnalysisResultModel(Result);
}

/// \brief The model returns true to allow the invalidation.
/// \brief The model bases invalidation soley on being in the preserved set.
//
// FIXME: We should actually use two different concepts for analysis results
// rather than two different models, and avoid the indirect function call for
// ones that use the trivial behavior.
virtual bool invalidate(IRUnitT *) { return true; }
virtual bool invalidate(IRUnitT *, const PreservedAnalyses &PA) {
return !PA.preserved(PassT::ID());
}

ResultT Result;
};
Expand All @@ -219,15 +226,18 @@ struct AnalysisResultModel : AnalysisResultConcept<IRUnitT> {
///
/// Can wrap any type which implements a suitable invalidate member and model
/// the AnalysisResultConcept for the AnalysisManager.
template <typename IRUnitT, typename ResultT>
struct AnalysisResultModel<IRUnitT, ResultT, true> : AnalysisResultConcept<IRUnitT> {
template <typename IRUnitT, typename PassT, typename ResultT>
struct AnalysisResultModel<IRUnitT, PassT, ResultT,
true> : AnalysisResultConcept<IRUnitT> {
AnalysisResultModel(ResultT Result) : Result(llvm_move(Result)) {}
virtual AnalysisResultModel *clone() {
return new AnalysisResultModel(Result);
}

/// \brief The model delegates to the \c ResultT method.
virtual bool invalidate(IRUnitT *IR) { return Result.invalidate(IR); }
virtual bool invalidate(IRUnitT *IR, const PreservedAnalyses &PA) {
return Result.invalidate(IR, PA);
}

ResultT Result;
};
Expand All @@ -237,7 +247,10 @@ struct AnalysisResultModel<IRUnitT, ResultT, true> : AnalysisResultConcept<IRUni
template <typename IRUnitT, typename ResultT> class ResultHasInvalidateMethod {
typedef char SmallType;
struct BigType { char a, b; };
template <typename T, bool (T::*)(IRUnitT *)> struct Checker;

template <typename T, bool (T::*)(IRUnitT *, const PreservedAnalyses &)>
struct Checker;

template <typename T> static SmallType f(Checker<T, &T::invalidate> *);
template <typename T> static BigType f(...);

Expand Down Expand Up @@ -274,7 +287,7 @@ struct AnalysisPassModel : AnalysisPassConcept<typename PassT::IRUnitT> {

// FIXME: Replace PassT::Result with type traits when we use C++11.
typedef AnalysisResultModel<
IRUnitT, typename PassT::Result,
IRUnitT, PassT, typename PassT::Result,
ResultHasInvalidateMethod<IRUnitT, typename PassT::Result>::Value>
ResultModelT;

Expand Down Expand Up @@ -363,7 +376,7 @@ class ModuleAnalysisManager {
const detail::AnalysisResultConcept<Module> &ResultConcept =
getResultImpl(PassT::ID(), M);
typedef detail::AnalysisResultModel<
Module, typename PassT::Result,
Module, PassT, typename PassT::Result,
detail::ResultHasInvalidateMethod<
Module, typename PassT::Result>::Value> ResultModelT;
return static_cast<const ResultModelT &>(ResultConcept).Result;
Expand Down Expand Up @@ -447,7 +460,7 @@ class FunctionAnalysisManager {
const detail::AnalysisResultConcept<Function> &ResultConcept =
getResultImpl(PassT::ID(), F);
typedef detail::AnalysisResultModel<
Function, typename PassT::Result,
Function, PassT, typename PassT::Result,
detail::ResultHasInvalidateMethod<
Function, typename PassT::Result>::Value> ResultModelT;
return static_cast<const ResultModelT &>(ResultConcept).Result;
Expand Down Expand Up @@ -591,7 +604,16 @@ class FunctionAnalysisModuleProxy::Result {
~Result();

/// \brief Handler for invalidation of the module.
bool invalidate(Module *M);
///
/// If this analysis itself is preserved, then we assume that the set of \c
/// Function objects in the \c Module hasn't changed and thus we don't need
/// to invalidate *all* cached data associated with a \c Function* in the \c
/// FunctionAnalysisManager.
///
/// Regardless of whether this analysis is marked as preserved, all of the
/// analyses in the \c FunctionAnalysisManager are potentially invalidated
/// based on the set of preserved analyses.
bool invalidate(Module *M, const PreservedAnalyses &PA);

private:
FunctionAnalysisManager &FAM;
Expand Down Expand Up @@ -623,6 +645,9 @@ class ModuleToFunctionPassAdaptor {
PreservedAnalyses PassPA = Pass.run(I);
PA.intersect(llvm_move(PassPA));
}

// By definition we preserve the proxy.
PA.preserve<FunctionAnalysisModuleProxy>();
return PA;
}

Expand Down
22 changes: 15 additions & 7 deletions lib/IR/PassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void ModuleAnalysisManager::invalidate(Module *M, const PreservedAnalyses &PA) {
for (ModuleAnalysisResultMapT::iterator I = ModuleAnalysisResults.begin(),
E = ModuleAnalysisResults.end();
I != E; ++I)
if (!PA.preserved(I->first) && I->second->invalidate(M))
if (I->second->invalidate(M, PA))
ModuleAnalysisResults.erase(I);
}

Expand Down Expand Up @@ -76,7 +76,7 @@ void FunctionAnalysisManager::invalidate(Function *F, const PreservedAnalyses &P
for (FunctionAnalysisResultListT::iterator I = ResultsList.begin(),
E = ResultsList.end();
I != E;)
if (!PA.preserved(I->first) && I->second->invalidate(F)) {
if (I->second->invalidate(F, PA)) {
InvalidatedPassIDs.push_back(I->first);
I = ResultsList.erase(I);
} else {
Expand Down Expand Up @@ -145,11 +145,19 @@ FunctionAnalysisModuleProxy::Result::~Result() {
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();
bool FunctionAnalysisModuleProxy::Result::invalidate(Module *M, const PreservedAnalyses &PA) {
// If this proxy isn't marked as preserved, then it is has an invalid set of
// Function objects in the cache making it impossible to incrementally
// preserve them. Just clear the entire manager.
if (!PA.preserved(ID())) {
FAM.clear();
return false;
}

// The set of functions was preserved some how, so just directly invalidate
// any analysis results not preserved.
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
FAM.invalidate(I, PA);

// Return false to indicate that this result is still a valid proxy.
return false;
Expand Down
36 changes: 35 additions & 1 deletion unittests/IR/PassManagerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ struct TestModulePass {
int &RunCount;
};

struct TestPreservingModulePass {
PreservedAnalyses run(Module *M) {
return PreservedAnalyses::all();
}
};

struct TestMinPreservingModulePass {
PreservedAnalyses run(Module *M) {
PreservedAnalyses PA;
PA.preserve<FunctionAnalysisModuleProxy>();
return PA;
}
};

struct TestFunctionPass {
TestFunctionPass(FunctionAnalysisManager &AM, int &RunCount,
int &AnalyzedInstrCount)
Expand Down Expand Up @@ -137,6 +151,22 @@ TEST_F(PassManagerTest, Basic) {
FPM2.addPass(TestFunctionPass(FAM, FunctionPassRunCount2, AnalyzedInstrCount2));
MPM.addPass(createModuleToFunctionPassAdaptor(FPM2, &MAM));

// A third function pass manager but with only preserving intervening passes.
MPM.addPass(TestPreservingModulePass());
FunctionPassManager FPM3(&FAM);
int FunctionPassRunCount3 = 0;
int AnalyzedInstrCount3 = 0;
FPM3.addPass(TestFunctionPass(FAM, FunctionPassRunCount3, AnalyzedInstrCount3));
MPM.addPass(createModuleToFunctionPassAdaptor(FPM3, &MAM));

// A fourth function pass manager but with a minimal intervening passes.
MPM.addPass(TestMinPreservingModulePass());
FunctionPassManager FPM4(&FAM);
int FunctionPassRunCount4 = 0;
int AnalyzedInstrCount4 = 0;
FPM4.addPass(TestFunctionPass(FAM, FunctionPassRunCount4, AnalyzedInstrCount4));
MPM.addPass(createModuleToFunctionPassAdaptor(FPM4, &MAM));

MPM.run(M.get());

// Validate module pass counters.
Expand All @@ -147,8 +177,12 @@ TEST_F(PassManagerTest, Basic) {
EXPECT_EQ(5, AnalyzedInstrCount1);
EXPECT_EQ(3, FunctionPassRunCount2);
EXPECT_EQ(5, AnalyzedInstrCount2);
EXPECT_EQ(3, FunctionPassRunCount3);
EXPECT_EQ(5, AnalyzedInstrCount3);
EXPECT_EQ(3, FunctionPassRunCount4);
EXPECT_EQ(5, AnalyzedInstrCount4);

// Validate the analysis counters.
EXPECT_EQ(6, AnalysisRuns);
EXPECT_EQ(9, AnalysisRuns);
}
}

0 comments on commit edd2b49

Please sign in to comment.