llvm.org GIT mirror llvm / 78a6806
[PM] Extend the explicit 'invalidate' method API on analysis results to accept an Invalidator that allows them to invalidate themselves if their dependencies are in turn invalidated. Rather than recording the dependency graph ahead of time when analysis get results from other analyses, this simply lets each result trigger the immediate invalidation of any analyses they actually depend on. They do this in a way that has three nice properties: 1) They don't have to handle transitive dependencies because the infrastructure will recurse for them. 2) The invalidate methods are still called only once. We just dynamically discover the necessary topological ordering, everything is memoized nicely. 3) The infrastructure still provides a default implementation and can access it so that only analyses which have dependencies need to do anything custom. To make this work at all, the invalidation logic also has to defer the deletion of the result objects themselves so that they can remain alive until we have collected the complete set of results to invalidate. A unittest is added here that has exactly the dependency pattern we are concerned with. It hit the use-after-free described by Sean in much detail in the long thread about analysis invalidation before this change, and even in an intermediate form of this change where we failed to defer the deletion of the result objects. There is an important problem with doing dependency invalidation that *isn't* solved here: we don't *enforce* that results correctly invalidate all the analyses whose results they depend on. I actually looked at what it would take to do that, and it isn't as hard as I had thought but the complexity it introduces seems very likely to outweigh the benefit. The technique would be to provide a base class for an analysis result that would be populated with other results, and automatically provide the invalidate method which immediately does the correct thing. This approach has some nice pros IMO: - Handles the case we care about and nothing else: only *results* that depend on other analyses trigger extra invalidation. - Localized to the result rather than centralized in the analysis manager. - Ties the storage of the reference to another result to the triggering of the invalidation of that analysis. - Still supports extending invalidation in customized ways. But the down sides here are: - Very heavy-weight meta-programming is needed to provide this base class. - Requires a pretty awful API for accessing the dependencies. Ultimately, I fear it will not pull its weight. But we can re-evaluate this at any point if we start discovering consistent problems where the invalidation and dependencies get out of sync. It will fit as a clean layer on top of the facilities in this patch that we can add if and when we need it. Note that I'm not really thrilled with the names for these APIs... The name "Invalidator" seems ok but not great. The method name "invalidate" also. In review some improvements were suggested, but they really need *other* uses of these terms to be updated as well so I'm going to do that in a follow-up commit. I'm working on the actual fixes to various analyses that need to use these, but I want to try to get tests for each of them so we don't regress. And those changes are seperable and obvious so once this goes in I should be able to roll them out throughout LLVM. Many thanks to Sean, Justin, and others for help reviewing here. Differential Revision: https://reviews.llvm.org/D23738 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@288077 91177308-0d34-0410-b5e6-96231b3b80d8 Chandler Carruth 2 years ago
13 changed file(s) with 330 addition(s) and 79 deletion(s). Raw diff Collapse all Expand all
4141
4242 /// Handle invalidation events from the new pass manager.
4343 /// By definition, this result is stateless and so remains valid.
44 bool invalidate(Function &, const PreservedAnalyses &) { return false; }
44 bool invalidate(Function &, const PreservedAnalyses &,
45 FunctionAnalysisManager::Invalidator &) {
46 return false;
47 }
4548 /// Evict the given function from cache
4649 void evict(const Function &Fn);
4750
4444 /// Handle invalidation events from the new pass manager.
4545 ///
4646 /// By definition, this result is stateless and so remains valid.
47 bool invalidate(Function &, const PreservedAnalyses &) { return false; }
47 bool invalidate(Function &, const PreservedAnalyses &,
48 FunctionAnalysisManager::Invalidator &) {
49 return false;
50 }
4851
4952 /// \brief Inserts the given Function into the cache.
5053 void scan(Function *Fn);
9595 namespace llvm {
9696
9797 struct CGSCCUpdateResult;
98
99 /// Extern template declaration for the analysis set for this IR unit.
100 extern template class AllAnalysesOn;
98101
99102 extern template class AnalysisManager;
100103 /// \brief The CGSCC analysis manager.
4747 /// Handle invalidation events from the new pass manager.
4848 ///
4949 /// By definition, this result is stateless and so remains valid.
50 bool invalidate(Function &, const PreservedAnalyses &) { return false; }
50 bool invalidate(Function &, const PreservedAnalyses &,
51 FunctionAnalysisManager::Invalidator &) {
52 return false;
53 }
5154
5255 AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB);
5356 bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal);
2929 /// Handle invalidation events from the new pass manager.
3030 ///
3131 /// By definition, this result is stateless and so remains valid.
32 bool invalidate(Function &, const PreservedAnalyses &) { return false; }
32 bool invalidate(Function &, const PreservedAnalyses &,
33 FunctionAnalysisManager::Invalidator &) {
34 return false;
35 }
3336
3437 AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB);
3538 ModRefInfo getModRefInfo(ImmutableCallSite CS, const MemoryLocation &Loc);
312312 ///
313313 /// If we try to invalidate this info, just return false. It cannot become
314314 /// invalid even if the module or function changes.
315 bool invalidate(Module &, const PreservedAnalyses &) { return false; }
316 bool invalidate(Function &, const PreservedAnalyses &) { return false; }
315 bool invalidate(Module &, const PreservedAnalyses &,
316 ModuleAnalysisManager::Invalidator &) {
317 return false;
318 }
319 bool invalidate(Function &, const PreservedAnalyses &,
320 FunctionAnalysisManager::Invalidator &) {
321 return false;
322 }
317323 };
318324
319325 /// Analysis pass providing the \c TargetLibraryInfo.
8686 /// When used as a result of \c TargetIRAnalysis this method will be called
8787 /// when the function this was computed for changes. When it returns false,
8888 /// the information is preserved across those changes.
89 bool invalidate(Function &, const PreservedAnalyses &) {
89 bool invalidate(Function &, const PreservedAnalyses &,
90 FunctionAnalysisManager::Invalidator &) {
9091 // FIXME: We should probably in some way ensure that the subtarget
9192 // information for a function hasn't changed.
9293 return false;
2929 /// Handle invalidation events from the new pass manager.
3030 ///
3131 /// By definition, this result is stateless and so remains valid.
32 bool invalidate(Function &, const PreservedAnalyses &) { return false; }
32 bool invalidate(Function &, const PreservedAnalyses &,
33 FunctionAnalysisManager::Invalidator &) {
34 return false;
35 }
3336
3437 AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB);
3538 bool pointsToConstantMemory(const MemoryLocation &Loc, bool OrLocal);
338338 /// IR unit sufficies as its identity. It manages the cache for a unit of IR via
339339 /// the address of each unit of IR cached.
340340 template class AnalysisManager {
341 typedef detail::AnalysisResultConcept ResultConceptT;
342 typedef detail::AnalysisPassConcept PassConceptT;
343
344341 public:
345 // Most public APIs are inherited from the CRTP base class.
342 class Invalidator;
343
344 private:
345 // Now that we've defined our invalidator, we can build types for the concept
346 // types.
347 typedef detail::AnalysisResultConcept
348 ResultConceptT;
349 typedef detail::AnalysisPassConcept
350 ExtraArgTs...>
351 PassConceptT;
352
353 /// \brief List of function analysis pass IDs and associated concept pointers.
354 ///
355 /// Requires iterators to be valid across appending new entries and arbitrary
356 /// erases. Provides the analysis ID to enable finding iterators to a given entry
357 /// in maps below, and provides the storage for the actual result concept.
358 typedef std::list>>
359 AnalysisResultListT;
360
361 /// \brief Map type from IRUnitT pointer to our custom list type.
362 typedef DenseMap AnalysisResultListMapT;
363
364 /// \brief Map type from a pair of analysis ID and IRUnitT pointer to an
365 /// iterator into a particular result list which is where the actual result
366 /// is stored.
367 typedef DenseMap,
368 typename AnalysisResultListT::iterator>
369 AnalysisResultMapT;
370
371 public:
372 /// API to communicate dependencies between analyses during invalidation.
373 ///
374 /// When an analysis result embeds handles to other analysis results, it
375 /// needs to be invalidated both when its own information isn't preserved and
376 /// if any of those embedded analysis results end up invalidated. We pass in
377 /// an \c Invalidator object from the analysis manager in order to let the
378 /// analysis results themselves define the dependency graph on the fly. This
379 /// avoids building an explicit data structure representation of the
380 /// dependencies between analysis results.
381 class Invalidator {
382 public:
383 /// Trigger the invalidation of some other analysis pass if not already
384 /// handled and return whether it will in fact be invalidated.
385 ///
386 /// This is expected to be called from within a given analysis result's \c
387 /// invalidate method to trigger a depth-first walk of all inter-analysis
388 /// dependencies. The same \p IR unit and \p PA passed to that result's \c
389 /// invalidate method should in turn be provided to this routine.
390 ///
391 /// The first time this is called for a given analysis pass, it will
392 /// trigger the corresponding result's \c invalidate method to be called.
393 /// Subsequent calls will use a cache of the results of that initial call.
394 /// It is an error to form cyclic dependencies between analysis results.
395 ///
396 /// This returns true if the given analysis pass's result is invalid and
397 /// any dependecies on it will become invalid as a result.
398 template
399 bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA) {
400 AnalysisKey *ID = PassT::ID();
401 SmallDenseMap::iterator IMapI;
402 bool Inserted;
403 std::tie(IMapI, Inserted) = IsResultInvalidated.insert({ID, false});
404
405 // If we've already visited this pass, return true if it was invalidated
406 // and false otherwise.
407 if (!Inserted)
408 return IMapI->second;
409
410 // Otherwise look up the result object.
411 auto RI = Results.find({ID, &IR});
412 assert(RI != Results.end() &&
413 "Trying to invalidate a dependent result that isn't in the "
414 "manager's cache is always an error, likely due to a stale result "
415 "handle!");
416
417 typedef detail::AnalysisResultModel
418 typename PassT::Result,
419 PreservedAnalyses, Invalidator>
420 ResultModelT;
421 auto &ResultModel = static_cast(*RI->second->second);
422
423 // Mark in the map whether the result should be invalidated and return
424 // that.
425 IMapI->second = ResultModel.invalidate(IR, PA, *this);
426 return IMapI->second;
427 }
428
429 private:
430 friend class AnalysisManager;
431
432 Invalidator(SmallDenseMap &IsResultInvalidated,
433 const AnalysisResultMapT &Results)
434 : IsResultInvalidated(IsResultInvalidated), Results(Results) {}
435
436 SmallDenseMap &IsResultInvalidated;
437 const AnalysisResultMapT &Results;
438 };
346439
347440 /// \brief Construct an empty analysis manager.
348441 ///
404497 "This analysis pass was not registered prior to being queried");
405498 ResultConceptT &ResultConcept =
406499 getResultImpl(PassT::ID(), IR, ExtraArgs...);
407 typedef detail::AnalysisResultModel>
500 typedef detail::AnalysisResultModel,
501 PreservedAnalyses, Invalidator>
408502 ResultModelT;
409503 return static_cast(ResultConcept).Result;
410504 }
423517 if (!ResultConcept)
424518 return nullptr;
425519
426 typedef detail::AnalysisResultModel>
520 typedef detail::AnalysisResultModel,
521 PreservedAnalyses, Invalidator>
427522 ResultModelT;
428523 return &static_cast(ResultConcept)->Result;
429524 }
448543 /// away.
449544 template bool registerPass(PassBuilderT PassBuilder) {
450545 typedef decltype(PassBuilder()) PassT;
451 typedef detail::AnalysisPassModelExtraArgTs...> PassModelT;
546 typedef detail::AnalysisPassModelPreservedAnalyses,
547 Invalidator, ExtraArgTs...>
548 PassModelT;
452549
453550 auto &PassPtr = AnalysisPasses[PassT::ID()];
454551 if (PassPtr)
482579 dbgs() << "Invalidating all non-preserved analyses for: " << IR.getName()
483580 << "\n";
484581
485 // Clear all the invalidated results associated specifically with this
486 // function.
487 SmallVector InvalidatedIDs;
582 // Track whether each pass's result is invalidated. Memoize the results
583 // using the IsResultInvalidated map.
584 SmallDenseMap IsResultInvalidated;
585 Invalidator Inv(IsResultInvalidated, AnalysisResults);
488586 AnalysisResultListT &ResultsList = AnalysisResultLists[&IR];
489 for (typename AnalysisResultListT::iterator I = ResultsList.begin(),
490 E = ResultsList.end();
491 I != E;) {
587 for (auto &AnalysisResultPair : ResultsList) {
588 // This is basically the same thing as Invalidator::invalidate, but we
589 // can't call it here because we're operating on the type-erased result.
590 // Moreover if we instead called invalidate() directly, it would do an
591 // unnecessary look up in ResultsList.
592 AnalysisKey *ID = AnalysisResultPair.first;
593 auto &Result = *AnalysisResultPair.second;
594
595 SmallDenseMap::iterator IMapI;
596 bool Inserted;
597 std::tie(IMapI, Inserted) = IsResultInvalidated.insert({ID, false});
598 if (!Inserted)
599 // This result was already handled via the Invalidator.
600 continue;
601
602 // Try to invalidate the result, giving it the Invalidator so it can
603 // recursively query for any dependencies it has and record the result.
604 IMapI->second = Result.invalidate(IR, PA, Inv);
605 }
606
607 // Now erase the results that were marked above as invalidated.
608 for (auto I = ResultsList.begin(), E = ResultsList.end(); I != E;) {
492609 AnalysisKey *ID = I->first;
493
494 // Pass the invalidation down to the pass itself to see if it thinks it is
495 // necessary. The analysis pass can return false if no action on the part
496 // of the analysis manager is required for this invalidation event.
497 if (I->second->invalidate(IR, PA)) {
498 if (DebugLogging)
499 dbgs() << "Invalidating analysis: " << this->lookupPass(ID).name()
500 << "\n";
501
502 InvalidatedIDs.push_back(I->first);
503 I = ResultsList.erase(I);
504 } else {
610 if (!IsResultInvalidated.lookup(ID)) {
505611 ++I;
612 continue;
506613 }
507 }
508 while (!InvalidatedIDs.empty())
509 AnalysisResults.erase(std::make_pair(InvalidatedIDs.pop_back_val(), &IR));
614
615 if (DebugLogging)
616 dbgs() << "Invalidating analysis: " << this->lookupPass(ID).name()
617 << "\n";
618
619 I = ResultsList.erase(I);
620 AnalysisResults.erase({ID, &IR});
621 }
510622 if (ResultsList.empty())
511623 AnalysisResultLists.erase(&IR);
512624
585697 /// \brief Collection of module analysis passes, indexed by ID.
586698 AnalysisPassMapT AnalysisPasses;
587699
588 /// \brief List of function analysis pass IDs and associated concept pointers.
589 ///
590 /// Requires iterators to be valid across appending new entries and arbitrary
591 /// erases. Provides both the pass ID and concept pointer such that it is
592 /// half of a bijection and provides storage for the actual result concept.
593 typedef std::list
594 AnalysisKey *, std::unique_ptr>>>
595 AnalysisResultListT;
596
597 /// \brief Map type from function pointer to our custom list type.
598 typedef DenseMap AnalysisResultListMapT;
599
600700 /// \brief Map from function to a list of function analysis results.
601701 ///
602702 /// Provides linear time removal of all analysis results for a function and
603703 /// the ultimate storage for a particular cached analysis result.
604704 AnalysisResultListMapT AnalysisResultLists;
605
606 /// \brief Map type from a pair of analysis ID and function pointer to an
607 /// iterator into a particular result list.
608 typedef DenseMap,
609 typename AnalysisResultListT::iterator>
610 AnalysisResultMapT;
611705
612706 /// \brief Map from an analysis ID and function to a particular cached
613707 /// analysis result.
682776 /// Regardless of whether this analysis is marked as preserved, all of the
683777 /// analyses in the \c FunctionAnalysisManager are potentially invalidated
684778 /// based on the set of preserved analyses.
685 bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA) {
779 bool invalidate(
780 IRUnitT &IR, const PreservedAnalyses &PA,
781 typename AnalysisManager::Invalidator &) {
686782 // If this proxy isn't marked as preserved, then we can't even invalidate
687783 // individual function analyses, there may be an invalid set of Function
688784 // objects in the cache making it impossible to incrementally preserve
757853 const AnalysisManagerT &getManager() const { return *AM; }
758854
759855 /// \brief Handle invalidation by ignoring it, this pass is immutable.
760 bool invalidate(IRUnitT &, const PreservedAnalyses &) { return false; }
856 bool invalidate(
857 IRUnitT &, const PreservedAnalyses &,
858 typename AnalysisManager::Invalidator &) {
859 return false;
860 }
761861
762862 private:
763863 const AnalysisManagerT *AM;
2525 namespace llvm {
2626
2727 template class AnalysisManager;
28 class Invalidator;
2829 class PreservedAnalyses;
2930
3031 /// \brief Implementation details of the pass manager interfaces.
8788 ///
8889 /// This concept is parameterized over the IR unit that this result pertains
8990 /// to.
90 template > struct AnalysisResultConcept {
91 template , typename PreservedAnalysesT, typename InvalidatorT>
92 struct AnalysisResultConcept {
9193 virtual ~AnalysisResultConcept() = default;
9294
9395 /// \brief Method to try and mark a result as invalid.
9597 /// When the outer analysis manager detects a change in some underlying
9698 /// unit of the IR, it will call this method on all of the results cached.
9799 ///
98 /// This method also receives a set of preserved analyses which can be used
99 /// to avoid invalidation because the pass which changed the underlying IR
100 /// took care to update or preserve the analysis result in some way.
100 /// \p PA is a set of preserved analyses which can be used to avoid
101 /// invalidation because the pass which changed the underlying IR took care
102 /// to update or preserve the analysis result in some way.
103 ///
104 /// \p Inv is typically a \c AnalysisManager::Invalidator object that can be
105 /// used by a particular analysis result to discover if other analyses
106 /// results are also invalidated in the event that this result depends on
107 /// them. See the documentation in the \c AnalysisManager for more details.
101108 ///
102109 /// \returns true if the result is indeed invalid (the default).
103 virtual bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA) = 0;
110 virtual bool invalidate(IRUnitT &IR, const PreservedAnalysesT &PA,
111 InvalidatorT &Inv) = 0;
104112 };
105113
106114 /// \brief SFINAE metafunction for computing whether \c ResultT provides an
146154 /// an invalidation handler. It is only selected when the invalidation handler
147155 /// is not part of the ResultT's interface.
148156 template
149 typename PreservedAnalysesT = PreservedAnalyses,
157 typename PreservedAnalysesT, typename InvalidatorT,
150158 bool HasInvalidateHandler =
151159 ResultHasInvalidateMethod::Value>
152160 struct AnalysisResultModel;
154162 /// \brief Specialization of \c AnalysisResultModel which provides the default
155163 /// invalidate functionality.
156164 template
157 typename PreservedAnalysesT>
158 struct AnalysisResultModel
159 : AnalysisResultConcept {
165 typename PreservedAnalysesT, typename InvalidatorT>
166 struct AnalysisResultModel
167 InvalidatorT, false>
168 : AnalysisResultConcept {
160169 explicit AnalysisResultModel(ResultT Result) : Result(std::move(Result)) {}
161170 // We have to explicitly define all the special member functions because MSVC
162171 // refuses to generate them.
179188 // FIXME: We should actually use two different concepts for analysis results
180189 // rather than two different models, and avoid the indirect function call for
181190 // ones that use the trivial behavior.
182 bool invalidate(IRUnitT &, const PreservedAnalysesT &PA) override {
191 bool invalidate(IRUnitT &, const PreservedAnalysesT &PA,
192 InvalidatorT &) override {
183193 return !PA.preserved(PassT::ID());
184194 }
185195
189199 /// \brief Specialization of \c AnalysisResultModel which delegates invalidate
190200 /// handling to \c ResultT.
191201 template
192 typename PreservedAnalysesT>
193 struct AnalysisResultModel
194 : AnalysisResultConcept {
202 typename PreservedAnalysesT, typename InvalidatorT>
203 struct AnalysisResultModel
204 InvalidatorT, true>
205 : AnalysisResultConcept {
195206 explicit AnalysisResultModel(ResultT Result) : Result(std::move(Result)) {}
196207 // We have to explicitly define all the special member functions because MSVC
197208 // refuses to generate them.
210221 }
211222
212223 /// \brief The model delegates to the \c ResultT method.
213 bool invalidate(IRUnitT &IR, const PreservedAnalysesT &PA) override {
214 return Result.invalidate(IR, PA);
224 bool invalidate(IRUnitT &IR, const PreservedAnalysesT &PA,
225 InvalidatorT &Inv) override {
226 return Result.invalidate(IR, PA, Inv);
215227 }
216228
217229 ResultT Result;
221233 ///
222234 /// This concept is parameterized over the IR unit that it can run over and
223235 /// produce an analysis result.
224 template ... ExtraArgTs> struct AnalysisPassConcept {
236 template PreservedAnalysesT, typename InvalidatorT,
237 typename... ExtraArgTs>
238 struct AnalysisPassConcept {
225239 virtual ~AnalysisPassConcept() = default;
226240
227241 /// \brief Method to run this analysis over a unit of IR.
228242 /// \returns A unique_ptr to the analysis result object to be queried by
229243 /// users.
230 virtual std::unique_ptr<AnalysisResultConcept>
244 virtual std::unique_ptr<
245 AnalysisResultConcept>
231246 run(IRUnitT &IR, AnalysisManager &AM,
232247 ExtraArgTs... ExtraArgs) = 0;
233248
240255 /// Can wrap any type which implements a suitable \c run method. The method
241256 /// must accept an \c IRUnitT& and an \c AnalysisManager& as arguments
242257 /// and produce an object which can be wrapped in a \c AnalysisResultModel.
243 template
244 struct AnalysisPassModel : AnalysisPassConcept {
258 template
259 typename InvalidatorT, typename... ExtraArgTs>
260 struct AnalysisPassModel : AnalysisPassConcept
261 InvalidatorT, ExtraArgTs...> {
245262 explicit AnalysisPassModel(PassT Pass) : Pass(std::move(Pass)) {}
246263 // We have to explicitly define all the special member functions because MSVC
247264 // refuses to generate them.
259276 }
260277
261278 // FIXME: Replace PassT::Result with type traits when we use C++11.
262 typedef AnalysisResultModel>
279 typedef AnalysisResultModel,
280 PreservedAnalysesT, InvalidatorT>
263281 ResultModelT;
264282
265283 /// \brief The model delegates to the \c PassT::run method.
266284 ///
267285 /// The return is wrapped in an \c AnalysisResultModel.
268 std::unique_ptr<AnalysisResultConcept>
286 std::unique_ptr<
287 AnalysisResultConcept>
269288 run(IRUnitT &IR, AnalysisManager &AM,
270289 ExtraArgTs... ExtraArgs) override {
271290 return make_unique(Pass.run(IR, AM, ExtraArgs...));
1414 namespace llvm {
1515
1616 // Explicit instantiations for the core proxy templates.
17 template class AllAnalysesOn;
1718 template class AnalysisManager;
1819 template class PassManager
1920 LazyCallGraph &, CGSCCUpdateResult &>;
9999 : public AnalysisInfoMixin {
100100 public:
101101 struct Result {
102 bool invalidate(Function &, const PreservedAnalyses &) { return false; }
102 bool invalidate(Function &, const PreservedAnalyses &,
103 FunctionAnalysisManager::Invalidator &) {
104 return false;
105 }
103106 };
104107
105108 TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {}
387387 // And ensure that we accumulated the correct result.
388388 EXPECT_EQ(42 * (int)M->size(), Result);
389389 }
390 }
390
391 /// A test analysis pass which caches in its result another analysis pass and
392 /// uses it to serve queries. This requires the result to invalidate itself
393 /// when its dependency is invalidated.
394 struct TestIndirectFunctionAnalysis
395 : public AnalysisInfoMixin {
396 struct Result {
397 Result(TestFunctionAnalysis::Result &Dep) : Dep(Dep) {}
398 TestFunctionAnalysis::Result &Dep;
399
400 bool invalidate(Function &F, const PreservedAnalyses &PA,
401 FunctionAnalysisManager::Invalidator &Inv) {
402 return !PA.preserved() ||
403 Inv.invalidate(F, PA);
404 }
405 };
406
407 TestIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {}
408
409 /// Run the analysis pass over the function and return a result.
410 Result run(Function &F, FunctionAnalysisManager &AM) {
411 ++Runs;
412 return Result(AM.getResult(F));
413 }
414
415 private:
416 friend AnalysisInfoMixin;
417 static AnalysisKey Key;
418
419 int &Runs;
420 };
421
422 AnalysisKey TestIndirectFunctionAnalysis::Key;
423
424 struct LambdaPass : public PassInfoMixin {
425 using FuncT = std::function;
426
427 LambdaPass(FuncT Func) : Func(std::move(Func)) {}
428
429 PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
430 return Func(F, AM);
431 }
432
433 FuncT Func;
434 };
435
436 TEST_F(PassManagerTest, IndirectAnalysisInvalidation) {
437 FunctionAnalysisManager FAM(/*DebugLogging*/ true);
438 int AnalysisRuns = 0, IndirectAnalysisRuns = 0;
439 FAM.registerPass([&] { return TestFunctionAnalysis(AnalysisRuns); });
440 FAM.registerPass(
441 [&] { return TestIndirectFunctionAnalysis(IndirectAnalysisRuns); });
442
443 ModuleAnalysisManager MAM(/*DebugLogging*/ true);
444 MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
445 FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
446
447 int InstrCount = 0;
448 ModulePassManager MPM(/*DebugLogging*/ true);
449 FunctionPassManager FPM(/*DebugLogging*/ true);
450 // First just use the analysis to get the instruction count, and preserve
451 // everything.
452 FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
453 InstrCount +=
454 AM.getResult(F).Dep.InstructionCount;
455 return PreservedAnalyses::all();
456 }));
457 // Next, invalidate
458 // - both analyses for "f",
459 // - just the underlying (indirect) analysis for "g", and
460 // - just the direct analysis for "h".
461 FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
462 InstrCount +=
463 AM.getResult(F).Dep.InstructionCount;
464 auto PA = PreservedAnalyses::none();
465 if (F.getName() == "g")
466 PA.preserve();
467 else if (F.getName() == "h")
468 PA.preserve();
469 return PA;
470 }));
471 // Finally, use the analysis again on each function, forcing re-computation
472 // for all of them.
473 FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
474 InstrCount +=
475 AM.getResult(F).Dep.InstructionCount;
476 return PreservedAnalyses::all();
477 }));
478 MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
479 MPM.run(*M, MAM);
480
481 // There are generally two possible runs for each of the three functions. But
482 // for one function, we only invalidate the indirect analysis so the base one
483 // only gets run five times.
484 EXPECT_EQ(5, AnalysisRuns);
485 // The indirect analysis is invalidated for each function (either directly or
486 // indirectly) and run twice for each.
487 EXPECT_EQ(6, IndirectAnalysisRuns);
488
489 // There are five instructions in the module and we add the count three
490 // times.
491 EXPECT_EQ(5 * 3, InstrCount);
492 }
493 }