diff options
| author | 2017-01-24 08:32:59 +0000 | |
|---|---|---|
| committer | 2017-01-24 08:32:59 +0000 | |
| commit | 53d771aafdbe5b919f264f53cba3788e2c4cffd2 (patch) | |
| tree | 7eca39498be0ff1e3a6daf583cd9ca5886bb2636 /gnu/llvm/unittests/IR/PassManagerTest.cpp | |
| parent | In preparation of compiling our kernels with -ffreestanding, explicitly map (diff) | |
| download | wireguard-openbsd-53d771aafdbe5b919f264f53cba3788e2c4cffd2.tar.xz wireguard-openbsd-53d771aafdbe5b919f264f53cba3788e2c4cffd2.zip | |
Import LLVM 4.0.0 rc1 including clang and lld to help the current
development effort on OpenBSD/arm64.
Diffstat (limited to 'gnu/llvm/unittests/IR/PassManagerTest.cpp')
| -rw-r--r-- | gnu/llvm/unittests/IR/PassManagerTest.cpp | 544 |
1 files changed, 489 insertions, 55 deletions
diff --git a/gnu/llvm/unittests/IR/PassManagerTest.cpp b/gnu/llvm/unittests/IR/PassManagerTest.cpp index c2ac863260a..b3a039a364f 100644 --- a/gnu/llvm/unittests/IR/PassManagerTest.cpp +++ b/gnu/llvm/unittests/IR/PassManagerTest.cpp @@ -41,12 +41,12 @@ public: private: friend AnalysisInfoMixin<TestFunctionAnalysis>; - static char PassID; + static AnalysisKey Key; int &Runs; }; -char TestFunctionAnalysis::PassID; +AnalysisKey TestFunctionAnalysis::Key; class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> { public: @@ -67,12 +67,12 @@ public: private: friend AnalysisInfoMixin<TestModuleAnalysis>; - static char PassID; + static AnalysisKey Key; int &Runs; }; -char TestModuleAnalysis::PassID; +AnalysisKey TestModuleAnalysis::Key; struct TestModulePass : PassInfoMixin<TestModulePass> { TestModulePass(int &RunCount) : RunCount(RunCount) {} @@ -91,19 +91,6 @@ struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> { } }; -struct TestMinPreservingModulePass - : PassInfoMixin<TestMinPreservingModulePass> { - PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { - PreservedAnalyses PA; - - // Force running an analysis. - (void)AM.getResult<TestModuleAnalysis>(M); - - PA.preserve<FunctionAnalysisManagerModuleProxy>(); - return PA; - } -}; - struct TestFunctionPass : PassInfoMixin<TestFunctionPass> { TestFunctionPass(int &RunCount, int &AnalyzedInstrCount, int &AnalyzedFunctionCount, @@ -181,45 +168,232 @@ public: "}\n")) {} }; -TEST_F(PassManagerTest, BasicPreservedAnalyses) { +TEST(PreservedAnalysesTest, Basic) { PreservedAnalyses PA1 = PreservedAnalyses(); - EXPECT_FALSE(PA1.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA1.preserved<TestModuleAnalysis>()); - PreservedAnalyses PA2 = PreservedAnalyses::none(); - EXPECT_FALSE(PA2.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA2.preserved<TestModuleAnalysis>()); - PreservedAnalyses PA3 = PreservedAnalyses::all(); - EXPECT_TRUE(PA3.preserved<TestFunctionAnalysis>()); - EXPECT_TRUE(PA3.preserved<TestModuleAnalysis>()); + { + auto PAC = PA1.getChecker<TestFunctionAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } + { + auto PAC = PA1.getChecker<TestModuleAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>()); + } + auto PA2 = PreservedAnalyses::none(); + { + auto PAC = PA2.getChecker<TestFunctionAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } + auto PA3 = PreservedAnalyses::all(); + { + auto PAC = PA3.getChecker<TestFunctionAnalysis>(); + EXPECT_TRUE(PAC.preserved()); + EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } PreservedAnalyses PA4 = PA1; - EXPECT_FALSE(PA4.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>()); + { + auto PAC = PA4.getChecker<TestFunctionAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } PA4 = PA3; - EXPECT_TRUE(PA4.preserved<TestFunctionAnalysis>()); - EXPECT_TRUE(PA4.preserved<TestModuleAnalysis>()); + { + auto PAC = PA4.getChecker<TestFunctionAnalysis>(); + EXPECT_TRUE(PAC.preserved()); + EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } PA4 = std::move(PA2); - EXPECT_FALSE(PA4.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>()); - PA4.preserve<TestFunctionAnalysis>(); - EXPECT_TRUE(PA4.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>()); - PA1.preserve<TestModuleAnalysis>(); - EXPECT_FALSE(PA1.preserved<TestFunctionAnalysis>()); - EXPECT_TRUE(PA1.preserved<TestModuleAnalysis>()); + { + auto PAC = PA4.getChecker<TestFunctionAnalysis>(); + EXPECT_FALSE(PAC.preserved()); + EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); + } +} + +TEST(PreservedAnalysesTest, Preserve) { + auto PA = PreservedAnalyses::none(); + PA.preserve<TestFunctionAnalysis>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved()); + PA.preserve<TestModuleAnalysis>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved()); + + // Redundant calls are fine. + PA.preserve<TestFunctionAnalysis>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved()); +} + +TEST(PreservedAnalysesTest, PreserveSets) { + auto PA = PreservedAnalyses::none(); + PA.preserveSet<AllAnalysesOn<Function>>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + PA.preserveSet<AllAnalysesOn<Module>>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Mixing is fine. + PA.preserve<TestFunctionAnalysis>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Redundant calls are fine. + PA.preserveSet<AllAnalysesOn<Module>>(); + EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); +} + +TEST(PreservedAnalysisTest, Intersect) { + // Setup the initial sets. + auto PA1 = PreservedAnalyses::none(); PA1.preserve<TestFunctionAnalysis>(); - EXPECT_TRUE(PA1.preserved<TestFunctionAnalysis>()); - EXPECT_TRUE(PA1.preserved<TestModuleAnalysis>()); - PA1.intersect(PA4); - EXPECT_TRUE(PA1.preserved<TestFunctionAnalysis>()); - EXPECT_FALSE(PA1.preserved<TestModuleAnalysis>()); + PA1.preserveSet<AllAnalysesOn<Module>>(); + auto PA2 = PreservedAnalyses::none(); + PA2.preserve<TestFunctionAnalysis>(); + PA2.preserveSet<AllAnalysesOn<Function>>(); + PA2.preserve<TestModuleAnalysis>(); + PA2.preserveSet<AllAnalysesOn<Module>>(); + auto PA3 = PreservedAnalyses::none(); + PA3.preserve<TestModuleAnalysis>(); + PA3.preserveSet<AllAnalysesOn<Function>>(); + + // Self intersection is a no-op. + auto Intersected = PA1; + Intersected.intersect(PA1); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting with all is a no-op. + Intersected.intersect(PreservedAnalyses::all()); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting a narrow set with a more broad set is the narrow set. + Intersected.intersect(PA2); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting a broad set with a more narrow set is the narrow set. + Intersected = PA2; + Intersected.intersect(PA1); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting with empty clears. + Intersected.intersect(PreservedAnalyses::none()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting non-overlapping clears. + Intersected = PA1; + Intersected.intersect(PA3); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting with moves works in when there is storage on both sides. + Intersected = PA1; + auto Tmp = PA2; + Intersected.intersect(std::move(Tmp)); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // Intersecting with move works for incoming all and existing all. + auto Tmp2 = PreservedAnalyses::all(); + Intersected.intersect(std::move(Tmp2)); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + Intersected = PreservedAnalyses::all(); + auto Tmp3 = PA1; + Intersected.intersect(std::move(Tmp3)); + EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); + EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); + EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); +} + +TEST(PreservedAnalysisTest, Abandon) { + auto PA = PreservedAnalyses::none(); + + // We can abandon things after they are preserved. + PA.preserve<TestFunctionAnalysis>(); + PA.abandon<TestFunctionAnalysis>(); + EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved()); + + // Repeated is fine, and abandoning if they were never preserved is fine. + PA.abandon<TestFunctionAnalysis>(); + EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved()); + PA.abandon<TestModuleAnalysis>(); + EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved()); + + // Even if the sets are preserved, the abandoned analyses' checker won't + // return true for those sets. + PA.preserveSet<AllAnalysesOn<Function>>(); + PA.preserveSet<AllAnalysesOn<Module>>(); + EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>() + .preservedSet<AllAnalysesOn<Function>>()); + EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>() + .preservedSet<AllAnalysesOn<Module>>()); + + // But an arbitrary (opaque) analysis will still observe the sets as + // preserved. This also checks that we can use an explicit ID rather than + // a type. + AnalysisKey FakeKey, *FakeID = &FakeKey; + EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Function>>()); + EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Module>>()); } TEST_F(PassManagerTest, Basic) { - FunctionAnalysisManager FAM; + FunctionAnalysisManager FAM(/*DebugLogging*/ true); int FunctionAnalysisRuns = 0; FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); - ModuleAnalysisManager MAM; + ModuleAnalysisManager MAM(/*DebugLogging*/ true); int ModuleAnalysisRuns = 0; MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); @@ -233,11 +407,11 @@ TEST_F(PassManagerTest, Basic) { int AnalyzedFunctionCount1 = 0; { // Pointless scoped copy to test move assignment. - ModulePassManager NestedMPM; + ModulePassManager NestedMPM(/*DebugLogging*/ true); FunctionPassManager FPM; { // Pointless scope to test move assignment. - FunctionPassManager NestedFPM; + FunctionPassManager NestedFPM(/*DebugLogging*/ true); NestedFPM.addPass(TestFunctionPass( FunctionPassRunCount1, AnalyzedInstrCount1, AnalyzedFunctionCount1)); FPM = std::move(NestedFPM); @@ -255,7 +429,7 @@ TEST_F(PassManagerTest, Basic) { int AnalyzedInstrCount2 = 0; int AnalyzedFunctionCount2 = 0; { - FunctionPassManager FPM; + FunctionPassManager FPM(/*DebugLogging*/ true); FPM.addPass(TestFunctionPass(FunctionPassRunCount2, AnalyzedInstrCount2, AnalyzedFunctionCount2)); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); @@ -268,15 +442,16 @@ TEST_F(PassManagerTest, Basic) { int AnalyzedInstrCount3 = 0; int AnalyzedFunctionCount3 = 0; { - FunctionPassManager FPM; + FunctionPassManager FPM(/*DebugLogging*/ true); FPM.addPass(TestFunctionPass(FunctionPassRunCount3, AnalyzedInstrCount3, AnalyzedFunctionCount3)); FPM.addPass(TestInvalidationFunctionPass("f")); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } - // A fourth function pass manager but with a minimal intervening passes. - MPM.addPass(TestMinPreservingModulePass()); + // A fourth function pass manager but with only preserving intervening + // passes but triggering the module analysis. + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); int FunctionPassRunCount4 = 0; int AnalyzedInstrCount4 = 0; int AnalyzedFunctionCount4 = 0; @@ -287,12 +462,13 @@ TEST_F(PassManagerTest, Basic) { MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } - // A fifth function pass manager but which uses only cached results. + // A fifth function pass manager which invalidates one function first but + // uses only cached results. int FunctionPassRunCount5 = 0; int AnalyzedInstrCount5 = 0; int AnalyzedFunctionCount5 = 0; { - FunctionPassManager FPM; + FunctionPassManager FPM(/*DebugLogging*/ true); FPM.addPass(TestInvalidationFunctionPass("f")); FPM.addPass(TestFunctionPass(FunctionPassRunCount5, AnalyzedInstrCount5, AnalyzedFunctionCount5, @@ -317,18 +493,276 @@ TEST_F(PassManagerTest, Basic) { EXPECT_EQ(0, AnalyzedFunctionCount3); EXPECT_EQ(3, FunctionPassRunCount4); EXPECT_EQ(5, AnalyzedInstrCount4); - EXPECT_EQ(0, AnalyzedFunctionCount4); + EXPECT_EQ(9, AnalyzedFunctionCount4); EXPECT_EQ(3, FunctionPassRunCount5); EXPECT_EQ(2, AnalyzedInstrCount5); // Only 'g' and 'h' were cached. - EXPECT_EQ(0, AnalyzedFunctionCount5); + EXPECT_EQ(9, AnalyzedFunctionCount5); // Validate the analysis counters: // first run over 3 functions, then module pass invalidates // second run over 3 functions, nothing invalidates // third run over 0 functions, but 1 function invalidated // fourth run over 1 function + // fifth run invalidates 1 function first, but runs over 0 functions EXPECT_EQ(7, FunctionAnalysisRuns); EXPECT_EQ(1, ModuleAnalysisRuns); } + +// A customized pass manager that passes extra arguments through the +// infrastructure. +typedef AnalysisManager<Function, int> CustomizedAnalysisManager; +typedef PassManager<Function, CustomizedAnalysisManager, int, int &> + CustomizedPassManager; + +class CustomizedAnalysis : public AnalysisInfoMixin<CustomizedAnalysis> { +public: + struct Result { + Result(int I) : I(I) {} + int I; + }; + + Result run(Function &F, CustomizedAnalysisManager &AM, int I) { + return Result(I); + } + +private: + friend AnalysisInfoMixin<CustomizedAnalysis>; + static AnalysisKey Key; +}; + +AnalysisKey CustomizedAnalysis::Key; + +struct CustomizedPass : PassInfoMixin<CustomizedPass> { + std::function<void(CustomizedAnalysis::Result &, int &)> Callback; + + template <typename CallbackT> + CustomizedPass(CallbackT Callback) : Callback(Callback) {} + + PreservedAnalyses run(Function &F, CustomizedAnalysisManager &AM, int I, + int &O) { + Callback(AM.getResult<CustomizedAnalysis>(F, I), O); + return PreservedAnalyses::none(); + } +}; + +TEST_F(PassManagerTest, CustomizedPassManagerArgs) { + CustomizedAnalysisManager AM; + AM.registerPass([&] { return CustomizedAnalysis(); }); + + CustomizedPassManager PM; + + // Add an instance of the customized pass that just accumulates the input + // after it is round-tripped through the analysis. + int Result = 0; + PM.addPass( + CustomizedPass([](CustomizedAnalysis::Result &R, int &O) { O += R.I; })); + + // Run this over every function with the input of 42. + for (Function &F : *M) + PM.run(F, AM, 42, Result); + + // 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 &FDep, TestModuleAnalysis::Result &MDep) + : FDep(FDep), MDep(MDep) {} + TestFunctionAnalysis::Result &FDep; + TestModuleAnalysis::Result &MDep; + + bool invalidate(Function &F, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv) { + auto PAC = PA.getChecker<TestIndirectFunctionAnalysis>(); + return !(PAC.preserved() || + PAC.preservedSet<AllAnalysesOn<Function>>()) || + 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; + auto &FDep = AM.getResult<TestFunctionAnalysis>(F); + auto &Proxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F); + const ModuleAnalysisManager &MAM = Proxy.getManager(); + // For the test, we insist that the module analysis starts off in the + // cache. + auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); + // And register the dependency as module analysis dependencies have to be + // pre-registered on the proxy. + Proxy.registerOuterAnalysisInvalidation<TestModuleAnalysis, + TestIndirectFunctionAnalysis>(); + return Result(FDep, MDep); + } + +private: + friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>; + static AnalysisKey Key; + + int &Runs; +}; + +AnalysisKey TestIndirectFunctionAnalysis::Key; + +/// A test analysis pass which chaches in its result the result from the above +/// indirect analysis pass. +/// +/// This allows us to ensure that whenever an analysis pass is invalidated due +/// to dependencies (especially dependencies across IR units that trigger +/// asynchronous invalidation) we correctly detect that this may in turn cause +/// other analysis to be invalidated. +struct TestDoublyIndirectFunctionAnalysis + : public AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis> { + struct Result { + Result(TestIndirectFunctionAnalysis::Result &IDep) : IDep(IDep) {} + TestIndirectFunctionAnalysis::Result &IDep; + + bool invalidate(Function &F, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv) { + auto PAC = PA.getChecker<TestDoublyIndirectFunctionAnalysis>(); + return !(PAC.preserved() || + PAC.preservedSet<AllAnalysesOn<Function>>()) || + Inv.invalidate<TestIndirectFunctionAnalysis>(F, PA); + } + }; + + TestDoublyIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {} + + /// Run the analysis pass over the function and return a result. + Result run(Function &F, FunctionAnalysisManager &AM) { + ++Runs; + auto &IDep = AM.getResult<TestIndirectFunctionAnalysis>(F); + return Result(IDep); + } + +private: + friend AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis>; + static AnalysisKey Key; + + int &Runs; +}; + +AnalysisKey TestDoublyIndirectFunctionAnalysis::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 FunctionAnalysisRuns = 0, ModuleAnalysisRuns = 0, + IndirectAnalysisRuns = 0, DoublyIndirectAnalysisRuns = 0; + FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); + FAM.registerPass( + [&] { return TestIndirectFunctionAnalysis(IndirectAnalysisRuns); }); + FAM.registerPass([&] { + return TestDoublyIndirectFunctionAnalysis(DoublyIndirectAnalysisRuns); + }); + + ModuleAnalysisManager MAM(/*DebugLogging*/ true); + MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); + MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); + + int InstrCount = 0, FunctionCount = 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) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectFunctionAnalysis>(F); + auto &IndirectResult = DoublyIndirectResult.IDep; + InstrCount += IndirectResult.FDep.InstructionCount; + FunctionCount += IndirectResult.MDep.FunctionCount; + 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) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectFunctionAnalysis>(F); + auto &IndirectResult = DoublyIndirectResult.IDep; + InstrCount += IndirectResult.FDep.InstructionCount; + FunctionCount += IndirectResult.MDep.FunctionCount; + 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) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectFunctionAnalysis>(F); + auto &IndirectResult = DoublyIndirectResult.IDep; + InstrCount += IndirectResult.FDep.InstructionCount; + FunctionCount += IndirectResult.MDep.FunctionCount; + return PreservedAnalyses::all(); + })); + + // Create a second function pass manager. This will cause the module-level + // invalidation to occur, which will force yet another invalidation of the + // indirect function-level analysis as the module analysis it depends on gets + // invalidated. + FunctionPassManager FPM2(/*DebugLogging*/ true); + FPM2.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { + auto &DoublyIndirectResult = + AM.getResult<TestDoublyIndirectFunctionAnalysis>(F); + auto &IndirectResult = DoublyIndirectResult.IDep; + InstrCount += IndirectResult.FDep.InstructionCount; + FunctionCount += IndirectResult.MDep.FunctionCount; + return PreservedAnalyses::all(); + })); + + // Add a requires pass to populate the module analysis and then our function + // pass pipeline. + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + // Now require the module analysis again (it will have been invalidated once) + // and then use it again from a function pass manager. + MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM2))); + 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, FunctionAnalysisRuns); + // The module analysis pass should be run twice here. + EXPECT_EQ(2, ModuleAnalysisRuns); + // The indirect analysis is invalidated for each function (either directly or + // indirectly) and run twice for each. + EXPECT_EQ(9, IndirectAnalysisRuns); + EXPECT_EQ(9, DoublyIndirectAnalysisRuns); + + // There are five instructions in the module and we add the count four + // times. + EXPECT_EQ(5 * 4, InstrCount); + + // There are three functions and we count them four times for each of the + // three functions. + EXPECT_EQ(3 * 4 * 3, FunctionCount); +} } |
