/// Handle invalidation events from the new pass manager.
/// By definition, this result is stateless and so remains valid.
- bool invalidate(Function &, const PreservedAnalyses &) { return false; }
+ bool invalidate(Function &, const PreservedAnalyses &,
+ FunctionAnalysisManager::Invalidator &) {
+ return false;
+ }
/// Evict the given function from cache
void evict(const Function &Fn);
/// Handle invalidation events from the new pass manager.
///
/// By definition, this result is stateless and so remains valid.
- bool invalidate(Function &, const PreservedAnalyses &) { return false; }
+ bool invalidate(Function &, const PreservedAnalyses &,
+ FunctionAnalysisManager::Invalidator &) {
+ return false;
+ }
/// \brief Inserts the given Function into the cache.
void scan(Function *Fn);
struct CGSCCUpdateResult;
+/// Extern template declaration for the analysis set for this IR unit.
+extern template class AllAnalysesOn<LazyCallGraph::SCC>;
+
extern template class AnalysisManager<LazyCallGraph::SCC, LazyCallGraph &>;
/// \brief The CGSCC analysis manager.
///
/// Handle invalidation events from the new pass manager.
///
/// By definition, this result is stateless and so remains valid.
- bool invalidate(Function &, const PreservedAnalyses &) { return false; }
+ bool invalidate(Function &, const PreservedAnalyses &,
+ FunctionAnalysisManager::Invalidator &) {
+ return false;
+ }
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB);
bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal);
/// Handle invalidation events from the new pass manager.
///
/// By definition, this result is stateless and so remains valid.
- bool invalidate(Function &, const PreservedAnalyses &) { return false; }
+ bool invalidate(Function &, const PreservedAnalyses &,
+ FunctionAnalysisManager::Invalidator &) {
+ return false;
+ }
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB);
ModRefInfo getModRefInfo(ImmutableCallSite CS, const MemoryLocation &Loc);
///
/// If we try to invalidate this info, just return false. It cannot become
/// invalid even if the module or function changes.
- bool invalidate(Module &, const PreservedAnalyses &) { return false; }
- bool invalidate(Function &, const PreservedAnalyses &) { return false; }
+ bool invalidate(Module &, const PreservedAnalyses &,
+ ModuleAnalysisManager::Invalidator &) {
+ return false;
+ }
+ bool invalidate(Function &, const PreservedAnalyses &,
+ FunctionAnalysisManager::Invalidator &) {
+ return false;
+ }
};
/// Analysis pass providing the \c TargetLibraryInfo.
/// When used as a result of \c TargetIRAnalysis this method will be called
/// when the function this was computed for changes. When it returns false,
/// the information is preserved across those changes.
- bool invalidate(Function &, const PreservedAnalyses &) {
+ bool invalidate(Function &, const PreservedAnalyses &,
+ FunctionAnalysisManager::Invalidator &) {
// FIXME: We should probably in some way ensure that the subtarget
// information for a function hasn't changed.
return false;
/// Handle invalidation events from the new pass manager.
///
/// By definition, this result is stateless and so remains valid.
- bool invalidate(Function &, const PreservedAnalyses &) { return false; }
+ bool invalidate(Function &, const PreservedAnalyses &,
+ FunctionAnalysisManager::Invalidator &) {
+ return false;
+ }
AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB);
bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal);
/// IR unit sufficies as its identity. It manages the cache for a unit of IR via
/// the address of each unit of IR cached.
template <typename IRUnitT, typename... ExtraArgTs> class AnalysisManager {
- typedef detail::AnalysisResultConcept<IRUnitT> ResultConceptT;
- typedef detail::AnalysisPassConcept<IRUnitT, ExtraArgTs...> PassConceptT;
+public:
+ class Invalidator;
+
+private:
+ // Now that we've defined our invalidator, we can build types for the concept
+ // types.
+ typedef detail::AnalysisResultConcept<IRUnitT, PreservedAnalyses, Invalidator>
+ ResultConceptT;
+ typedef detail::AnalysisPassConcept<IRUnitT, PreservedAnalyses, Invalidator,
+ ExtraArgTs...>
+ PassConceptT;
+
+ /// \brief List of function analysis pass IDs and associated concept pointers.
+ ///
+ /// Requires iterators to be valid across appending new entries and arbitrary
+ /// erases. Provides the analysis ID to enable finding iterators to a given entry
+ /// in maps below, and provides the storage for the actual result concept.
+ typedef std::list<std::pair<AnalysisKey *, std::unique_ptr<ResultConceptT>>>
+ AnalysisResultListT;
+
+ /// \brief Map type from IRUnitT pointer to our custom list type.
+ typedef DenseMap<IRUnitT *, AnalysisResultListT> AnalysisResultListMapT;
+
+ /// \brief Map type from a pair of analysis ID and IRUnitT pointer to an
+ /// iterator into a particular result list which is where the actual result
+ /// is stored.
+ typedef DenseMap<std::pair<AnalysisKey *, IRUnitT *>,
+ typename AnalysisResultListT::iterator>
+ AnalysisResultMapT;
public:
- // Most public APIs are inherited from the CRTP base class.
+ /// API to communicate dependencies between analyses during invalidation.
+ ///
+ /// When an analysis result embeds handles to other analysis results, it
+ /// needs to be invalidated both when its own information isn't preserved and
+ /// if any of those embedded analysis results end up invalidated. We pass in
+ /// an \c Invalidator object from the analysis manager in order to let the
+ /// analysis results themselves define the dependency graph on the fly. This
+ /// avoids building an explicit data structure representation of the
+ /// dependencies between analysis results.
+ class Invalidator {
+ public:
+ /// Trigger the invalidation of some other analysis pass if not already
+ /// handled and return whether it will in fact be invalidated.
+ ///
+ /// This is expected to be called from within a given analysis result's \c
+ /// invalidate method to trigger a depth-first walk of all inter-analysis
+ /// dependencies. The same \p IR unit and \p PA passed to that result's \c
+ /// invalidate method should in turn be provided to this routine.
+ ///
+ /// The first time this is called for a given analysis pass, it will
+ /// trigger the corresponding result's \c invalidate method to be called.
+ /// Subsequent calls will use a cache of the results of that initial call.
+ /// It is an error to form cyclic dependencies between analysis results.
+ ///
+ /// This returns true if the given analysis pass's result is invalid and
+ /// any dependecies on it will become invalid as a result.
+ template <typename PassT>
+ bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA) {
+ AnalysisKey *ID = PassT::ID();
+ SmallDenseMap<AnalysisKey *, bool, 8>::iterator IMapI;
+ bool Inserted;
+ std::tie(IMapI, Inserted) = IsResultInvalidated.insert({ID, false});
+
+ // If we've already visited this pass, return true if it was invalidated
+ // and false otherwise.
+ if (!Inserted)
+ return IMapI->second;
+
+ // Otherwise look up the result object.
+ auto RI = Results.find({ID, &IR});
+ assert(RI != Results.end() &&
+ "Trying to invalidate a dependent result that isn't in the "
+ "manager's cache is always an error, likely due to a stale result "
+ "handle!");
+
+ typedef detail::AnalysisResultModel<IRUnitT, PassT,
+ typename PassT::Result,
+ PreservedAnalyses, Invalidator>
+ ResultModelT;
+ auto &ResultModel = static_cast<ResultModelT &>(*RI->second->second);
+
+ // Mark in the map whether the result should be invalidated and return
+ // that.
+ IMapI->second = ResultModel.invalidate(IR, PA, *this);
+ return IMapI->second;
+ }
+
+ private:
+ friend class AnalysisManager;
+
+ Invalidator(SmallDenseMap<AnalysisKey *, bool, 8> &IsResultInvalidated,
+ const AnalysisResultMapT &Results)
+ : IsResultInvalidated(IsResultInvalidated), Results(Results) {}
+
+ SmallDenseMap<AnalysisKey *, bool, 8> &IsResultInvalidated;
+ const AnalysisResultMapT &Results;
+ };
/// \brief Construct an empty analysis manager.
///
"This analysis pass was not registered prior to being queried");
ResultConceptT &ResultConcept =
getResultImpl(PassT::ID(), IR, ExtraArgs...);
- typedef detail::AnalysisResultModel<IRUnitT, PassT, typename PassT::Result>
+ typedef detail::AnalysisResultModel<IRUnitT, PassT, typename PassT::Result,
+ PreservedAnalyses, Invalidator>
ResultModelT;
return static_cast<ResultModelT &>(ResultConcept).Result;
}
if (!ResultConcept)
return nullptr;
- typedef detail::AnalysisResultModel<IRUnitT, PassT, typename PassT::Result>
+ typedef detail::AnalysisResultModel<IRUnitT, PassT, typename PassT::Result,
+ PreservedAnalyses, Invalidator>
ResultModelT;
return &static_cast<ResultModelT *>(ResultConcept)->Result;
}
/// away.
template <typename PassBuilderT> bool registerPass(PassBuilderT PassBuilder) {
typedef decltype(PassBuilder()) PassT;
- typedef detail::AnalysisPassModel<IRUnitT, PassT, ExtraArgTs...> PassModelT;
+ typedef detail::AnalysisPassModel<IRUnitT, PassT, PreservedAnalyses,
+ Invalidator, ExtraArgTs...>
+ PassModelT;
auto &PassPtr = AnalysisPasses[PassT::ID()];
if (PassPtr)
dbgs() << "Invalidating all non-preserved analyses for: " << IR.getName()
<< "\n";
- // Clear all the invalidated results associated specifically with this
- // function.
- SmallVector<AnalysisKey *, 8> InvalidatedIDs;
+ // Track whether each pass's result is invalidated. Memoize the results
+ // using the IsResultInvalidated map.
+ SmallDenseMap<AnalysisKey *, bool, 8> IsResultInvalidated;
+ Invalidator Inv(IsResultInvalidated, AnalysisResults);
AnalysisResultListT &ResultsList = AnalysisResultLists[&IR];
- for (typename AnalysisResultListT::iterator I = ResultsList.begin(),
- E = ResultsList.end();
- I != E;) {
- AnalysisKey *ID = I->first;
+ for (auto &AnalysisResultPair : ResultsList) {
+ // This is basically the same thing as Invalidator::invalidate, but we
+ // can't call it here because we're operating on the type-erased result.
+ // Moreover if we instead called invalidate() directly, it would do an
+ // unnecessary look up in ResultsList.
+ AnalysisKey *ID = AnalysisResultPair.first;
+ auto &Result = *AnalysisResultPair.second;
+
+ SmallDenseMap<AnalysisKey *, bool, 8>::iterator IMapI;
+ bool Inserted;
+ std::tie(IMapI, Inserted) = IsResultInvalidated.insert({ID, false});
+ if (!Inserted)
+ // This result was already handled via the Invalidator.
+ continue;
+
+ // Try to invalidate the result, giving it the Invalidator so it can
+ // recursively query for any dependencies it has and record the result.
+ IMapI->second = Result.invalidate(IR, PA, Inv);
+ }
- // Pass the invalidation down to the pass itself to see if it thinks it is
- // necessary. The analysis pass can return false if no action on the part
- // of the analysis manager is required for this invalidation event.
- if (I->second->invalidate(IR, PA)) {
- if (DebugLogging)
- dbgs() << "Invalidating analysis: " << this->lookupPass(ID).name()
- << "\n";
-
- InvalidatedIDs.push_back(I->first);
- I = ResultsList.erase(I);
- } else {
+ // Now erase the results that were marked above as invalidated.
+ for (auto I = ResultsList.begin(), E = ResultsList.end(); I != E;) {
+ AnalysisKey *ID = I->first;
+ if (!IsResultInvalidated.lookup(ID)) {
++I;
+ continue;
}
+
+ if (DebugLogging)
+ dbgs() << "Invalidating analysis: " << this->lookupPass(ID).name()
+ << "\n";
+
+ I = ResultsList.erase(I);
+ AnalysisResults.erase({ID, &IR});
}
- while (!InvalidatedIDs.empty())
- AnalysisResults.erase(std::make_pair(InvalidatedIDs.pop_back_val(), &IR));
if (ResultsList.empty())
AnalysisResultLists.erase(&IR);
/// \brief Collection of module analysis passes, indexed by ID.
AnalysisPassMapT AnalysisPasses;
- /// \brief List of function analysis pass IDs and associated concept pointers.
- ///
- /// Requires iterators to be valid across appending new entries and arbitrary
- /// erases. Provides both the pass ID and concept pointer such that it is
- /// half of a bijection and provides storage for the actual result concept.
- typedef std::list<std::pair<
- AnalysisKey *, std::unique_ptr<detail::AnalysisResultConcept<IRUnitT>>>>
- AnalysisResultListT;
-
- /// \brief Map type from function pointer to our custom list type.
- typedef DenseMap<IRUnitT *, AnalysisResultListT> AnalysisResultListMapT;
-
/// \brief Map from function to a list of function analysis results.
///
/// Provides linear time removal of all analysis results for a function and
/// the ultimate storage for a particular cached analysis result.
AnalysisResultListMapT AnalysisResultLists;
- /// \brief Map type from a pair of analysis ID and function pointer to an
- /// iterator into a particular result list.
- typedef DenseMap<std::pair<AnalysisKey *, IRUnitT *>,
- typename AnalysisResultListT::iterator>
- AnalysisResultMapT;
-
/// \brief Map from an analysis ID and function to a particular cached
/// analysis result.
AnalysisResultMapT AnalysisResults;
/// 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(IRUnitT &IR, const PreservedAnalyses &PA) {
+ bool invalidate(
+ IRUnitT &IR, const PreservedAnalyses &PA,
+ typename AnalysisManager<IRUnitT, ExtraArgTs...>::Invalidator &) {
// If this proxy isn't marked as preserved, then we can't even invalidate
// individual function analyses, there may be an invalid set of Function
// objects in the cache making it impossible to incrementally preserve
const AnalysisManagerT &getManager() const { return *AM; }
/// \brief Handle invalidation by ignoring it, this pass is immutable.
- bool invalidate(IRUnitT &, const PreservedAnalyses &) { return false; }
+ bool invalidate(
+ IRUnitT &, const PreservedAnalyses &,
+ typename AnalysisManager<IRUnitT, ExtraArgTs...>::Invalidator &) {
+ return false;
+ }
private:
const AnalysisManagerT *AM;
namespace llvm {
template <typename IRUnitT, typename... ExtraArgTs> class AnalysisManager;
+class Invalidator;
class PreservedAnalyses;
/// \brief Implementation details of the pass manager interfaces.
///
/// This concept is parameterized over the IR unit that this result pertains
/// to.
-template <typename IRUnitT> struct AnalysisResultConcept {
+template <typename IRUnitT, typename PreservedAnalysesT, typename InvalidatorT>
+struct AnalysisResultConcept {
virtual ~AnalysisResultConcept() = default;
/// \brief Method to try and mark a result as invalid.
/// 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.
///
- /// 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.
+ /// \p PA is 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.
+ ///
+ /// \p Inv is typically a \c AnalysisManager::Invalidator object that can be
+ /// used by a particular analysis result to discover if other analyses
+ /// results are also invalidated in the event that this result depends on
+ /// them. See the documentation in the \c AnalysisManager for more details.
///
/// \returns true if the result is indeed invalid (the default).
- virtual bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA) = 0;
+ virtual bool invalidate(IRUnitT &IR, const PreservedAnalysesT &PA,
+ InvalidatorT &Inv) = 0;
};
/// \brief SFINAE metafunction for computing whether \c ResultT provides an
/// an invalidation handler. It is only selected when the invalidation handler
/// is not part of the ResultT's interface.
template <typename IRUnitT, typename PassT, typename ResultT,
- typename PreservedAnalysesT = PreservedAnalyses,
+ typename PreservedAnalysesT, typename InvalidatorT,
bool HasInvalidateHandler =
ResultHasInvalidateMethod<IRUnitT, ResultT>::Value>
struct AnalysisResultModel;
/// \brief Specialization of \c AnalysisResultModel which provides the default
/// invalidate functionality.
template <typename IRUnitT, typename PassT, typename ResultT,
- typename PreservedAnalysesT>
-struct AnalysisResultModel<IRUnitT, PassT, ResultT, PreservedAnalysesT, false>
- : AnalysisResultConcept<IRUnitT> {
+ typename PreservedAnalysesT, typename InvalidatorT>
+struct AnalysisResultModel<IRUnitT, PassT, ResultT, PreservedAnalysesT,
+ InvalidatorT, false>
+ : AnalysisResultConcept<IRUnitT, PreservedAnalysesT, InvalidatorT> {
explicit AnalysisResultModel(ResultT Result) : Result(std::move(Result)) {}
// We have to explicitly define all the special member functions because MSVC
// refuses to generate them.
// 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.
- bool invalidate(IRUnitT &, const PreservedAnalysesT &PA) override {
+ bool invalidate(IRUnitT &, const PreservedAnalysesT &PA,
+ InvalidatorT &) override {
return !PA.preserved(PassT::ID());
}
/// \brief Specialization of \c AnalysisResultModel which delegates invalidate
/// handling to \c ResultT.
template <typename IRUnitT, typename PassT, typename ResultT,
- typename PreservedAnalysesT>
-struct AnalysisResultModel<IRUnitT, PassT, ResultT, PreservedAnalysesT, true>
- : AnalysisResultConcept<IRUnitT> {
+ typename PreservedAnalysesT, typename InvalidatorT>
+struct AnalysisResultModel<IRUnitT, PassT, ResultT, PreservedAnalysesT,
+ InvalidatorT, true>
+ : AnalysisResultConcept<IRUnitT, PreservedAnalysesT, InvalidatorT> {
explicit AnalysisResultModel(ResultT Result) : Result(std::move(Result)) {}
// We have to explicitly define all the special member functions because MSVC
// refuses to generate them.
}
/// \brief The model delegates to the \c ResultT method.
- bool invalidate(IRUnitT &IR, const PreservedAnalysesT &PA) override {
- return Result.invalidate(IR, PA);
+ bool invalidate(IRUnitT &IR, const PreservedAnalysesT &PA,
+ InvalidatorT &Inv) override {
+ return Result.invalidate(IR, PA, Inv);
}
ResultT Result;
///
/// This concept is parameterized over the IR unit that it can run over and
/// produce an analysis result.
-template <typename IRUnitT, typename... ExtraArgTs> struct AnalysisPassConcept {
+template <typename IRUnitT, typename PreservedAnalysesT, typename InvalidatorT,
+ typename... ExtraArgTs>
+struct AnalysisPassConcept {
virtual ~AnalysisPassConcept() = default;
/// \brief Method to run this analysis over a unit of IR.
/// \returns A unique_ptr to the analysis result object to be queried by
/// users.
- virtual std::unique_ptr<AnalysisResultConcept<IRUnitT>>
+ virtual std::unique_ptr<
+ AnalysisResultConcept<IRUnitT, PreservedAnalysesT, InvalidatorT>>
run(IRUnitT &IR, AnalysisManager<IRUnitT, ExtraArgTs...> &AM,
ExtraArgTs... ExtraArgs) = 0;
/// Can wrap any type which implements a suitable \c run method. The method
/// must accept an \c IRUnitT& and an \c AnalysisManager<IRUnitT>& as arguments
/// and produce an object which can be wrapped in a \c AnalysisResultModel.
-template <typename IRUnitT, typename PassT, typename... ExtraArgTs>
-struct AnalysisPassModel : AnalysisPassConcept<IRUnitT, ExtraArgTs...> {
+template <typename IRUnitT, typename PassT, typename PreservedAnalysesT,
+ typename InvalidatorT, typename... ExtraArgTs>
+struct AnalysisPassModel : AnalysisPassConcept<IRUnitT, PreservedAnalysesT,
+ InvalidatorT, ExtraArgTs...> {
explicit AnalysisPassModel(PassT Pass) : Pass(std::move(Pass)) {}
// We have to explicitly define all the special member functions because MSVC
// refuses to generate them.
}
// FIXME: Replace PassT::Result with type traits when we use C++11.
- typedef AnalysisResultModel<IRUnitT, PassT, typename PassT::Result>
+ typedef AnalysisResultModel<IRUnitT, PassT, typename PassT::Result,
+ PreservedAnalysesT, InvalidatorT>
ResultModelT;
/// \brief The model delegates to the \c PassT::run method.
///
/// The return is wrapped in an \c AnalysisResultModel.
- std::unique_ptr<AnalysisResultConcept<IRUnitT>>
+ std::unique_ptr<
+ AnalysisResultConcept<IRUnitT, PreservedAnalysesT, InvalidatorT>>
run(IRUnitT &IR, AnalysisManager<IRUnitT, ExtraArgTs...> &AM,
ExtraArgTs... ExtraArgs) override {
return make_unique<ResultModelT>(Pass.run(IR, AM, ExtraArgs...));
namespace llvm {
// Explicit instantiations for the core proxy templates.
+template class AllAnalysesOn<LazyCallGraph::SCC>;
template class AnalysisManager<LazyCallGraph::SCC, LazyCallGraph &>;
template class PassManager<LazyCallGraph::SCC, CGSCCAnalysisManager,
LazyCallGraph &, CGSCCUpdateResult &>;
: public AnalysisInfoMixin<TestImmutableFunctionAnalysis> {
public:
struct Result {
- bool invalidate(Function &, const PreservedAnalyses &) { return false; }
+ bool invalidate(Function &, const PreservedAnalyses &,
+ FunctionAnalysisManager::Invalidator &) {
+ return false;
+ }
};
TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {}
// And ensure that we accumulated the correct result.
EXPECT_EQ(42 * (int)M->size(), Result);
}
+
+/// A test analysis pass which caches in its result another analysis pass and
+/// uses it to serve queries. This requires the result to invalidate itself
+/// when its dependency is invalidated.
+struct TestIndirectFunctionAnalysis
+ : public AnalysisInfoMixin<TestIndirectFunctionAnalysis> {
+ struct Result {
+ Result(TestFunctionAnalysis::Result &Dep) : Dep(Dep) {}
+ TestFunctionAnalysis::Result &Dep;
+
+ bool invalidate(Function &F, const PreservedAnalyses &PA,
+ FunctionAnalysisManager::Invalidator &Inv) {
+ return !PA.preserved<TestIndirectFunctionAnalysis>() ||
+ Inv.invalidate<TestFunctionAnalysis>(F, PA);
+ }
+ };
+
+ TestIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {}
+
+ /// Run the analysis pass over the function and return a result.
+ Result run(Function &F, FunctionAnalysisManager &AM) {
+ ++Runs;
+ return Result(AM.getResult<TestFunctionAnalysis>(F));
+ }
+
+private:
+ friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>;
+ static AnalysisKey Key;
+
+ int &Runs;
+};
+
+AnalysisKey TestIndirectFunctionAnalysis::Key;
+
+struct LambdaPass : public PassInfoMixin<LambdaPass> {
+ using FuncT = std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)>;
+
+ LambdaPass(FuncT Func) : Func(std::move(Func)) {}
+
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
+ return Func(F, AM);
+ }
+
+ FuncT Func;
+};
+
+TEST_F(PassManagerTest, IndirectAnalysisInvalidation) {
+ FunctionAnalysisManager FAM(/*DebugLogging*/ true);
+ int AnalysisRuns = 0, IndirectAnalysisRuns = 0;
+ FAM.registerPass([&] { return TestFunctionAnalysis(AnalysisRuns); });
+ FAM.registerPass(
+ [&] { return TestIndirectFunctionAnalysis(IndirectAnalysisRuns); });
+
+ ModuleAnalysisManager MAM(/*DebugLogging*/ true);
+ MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
+ FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
+
+ int InstrCount = 0;
+ ModulePassManager MPM(/*DebugLogging*/ true);
+ FunctionPassManager FPM(/*DebugLogging*/ true);
+ // First just use the analysis to get the instruction count, and preserve
+ // everything.
+ FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
+ InstrCount +=
+ AM.getResult<TestIndirectFunctionAnalysis>(F).Dep.InstructionCount;
+ return PreservedAnalyses::all();
+ }));
+ // Next, invalidate
+ // - both analyses for "f",
+ // - just the underlying (indirect) analysis for "g", and
+ // - just the direct analysis for "h".
+ FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
+ InstrCount +=
+ AM.getResult<TestIndirectFunctionAnalysis>(F).Dep.InstructionCount;
+ auto PA = PreservedAnalyses::none();
+ if (F.getName() == "g")
+ PA.preserve<TestFunctionAnalysis>();
+ else if (F.getName() == "h")
+ PA.preserve<TestIndirectFunctionAnalysis>();
+ return PA;
+ }));
+ // Finally, use the analysis again on each function, forcing re-computation
+ // for all of them.
+ FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
+ InstrCount +=
+ AM.getResult<TestIndirectFunctionAnalysis>(F).Dep.InstructionCount;
+ return PreservedAnalyses::all();
+ }));
+ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+ MPM.run(*M, MAM);
+
+ // There are generally two possible runs for each of the three functions. But
+ // for one function, we only invalidate the indirect analysis so the base one
+ // only gets run five times.
+ EXPECT_EQ(5, AnalysisRuns);
+ // The indirect analysis is invalidated for each function (either directly or
+ // indirectly) and run twice for each.
+ EXPECT_EQ(6, IndirectAnalysisRuns);
+
+ // There are five instructions in the module and we add the count three
+ // times.
+ EXPECT_EQ(5 * 3, InstrCount);
+}
}