|  | //===- CGSCCPassManagerTest.cpp -------------------------------------------===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/Analysis/CGSCCPassManager.h" | 
|  | #include "llvm/Analysis/LazyCallGraph.h" | 
|  | #include "llvm/Analysis/TargetLibraryInfo.h" | 
|  | #include "llvm/AsmParser/Parser.h" | 
|  | #include "llvm/IR/Function.h" | 
|  | #include "llvm/IR/InstIterator.h" | 
|  | #include "llvm/IR/LLVMContext.h" | 
|  | #include "llvm/IR/Module.h" | 
|  | #include "llvm/IR/PassManager.h" | 
|  | #include "llvm/Support/SourceMgr.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> { | 
|  | public: | 
|  | struct Result { | 
|  | Result(int Count) : FunctionCount(Count) {} | 
|  | int FunctionCount; | 
|  | }; | 
|  |  | 
|  | TestModuleAnalysis(int &Runs) : Runs(Runs) {} | 
|  |  | 
|  | Result run(Module &M, ModuleAnalysisManager &AM) { | 
|  | ++Runs; | 
|  | return Result(M.size()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend AnalysisInfoMixin<TestModuleAnalysis>; | 
|  | static AnalysisKey Key; | 
|  |  | 
|  | int &Runs; | 
|  | }; | 
|  |  | 
|  | AnalysisKey TestModuleAnalysis::Key; | 
|  |  | 
|  | class TestSCCAnalysis : public AnalysisInfoMixin<TestSCCAnalysis> { | 
|  | public: | 
|  | struct Result { | 
|  | Result(int Count) : FunctionCount(Count) {} | 
|  | int FunctionCount; | 
|  | }; | 
|  |  | 
|  | TestSCCAnalysis(int &Runs) : Runs(Runs) {} | 
|  |  | 
|  | Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &) { | 
|  | ++Runs; | 
|  | return Result(C.size()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend AnalysisInfoMixin<TestSCCAnalysis>; | 
|  | static AnalysisKey Key; | 
|  |  | 
|  | int &Runs; | 
|  | }; | 
|  |  | 
|  | AnalysisKey TestSCCAnalysis::Key; | 
|  |  | 
|  | class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> { | 
|  | public: | 
|  | struct Result { | 
|  | Result(int Count) : InstructionCount(Count) {} | 
|  | int InstructionCount; | 
|  | }; | 
|  |  | 
|  | TestFunctionAnalysis(int &Runs) : Runs(Runs) {} | 
|  |  | 
|  | Result run(Function &F, FunctionAnalysisManager &AM) { | 
|  | ++Runs; | 
|  | int Count = 0; | 
|  | for (Instruction &I : instructions(F)) { | 
|  | (void)I; | 
|  | ++Count; | 
|  | } | 
|  | return Result(Count); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend AnalysisInfoMixin<TestFunctionAnalysis>; | 
|  | static AnalysisKey Key; | 
|  |  | 
|  | int &Runs; | 
|  | }; | 
|  |  | 
|  | AnalysisKey TestFunctionAnalysis::Key; | 
|  |  | 
|  | class TestImmutableFunctionAnalysis | 
|  | : public AnalysisInfoMixin<TestImmutableFunctionAnalysis> { | 
|  | public: | 
|  | struct Result { | 
|  | bool invalidate(Function &, const PreservedAnalyses &, | 
|  | FunctionAnalysisManager::Invalidator &) { | 
|  | return false; | 
|  | } | 
|  | }; | 
|  |  | 
|  | TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {} | 
|  |  | 
|  | Result run(Function &F, FunctionAnalysisManager &AM) { | 
|  | ++Runs; | 
|  | return Result(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend AnalysisInfoMixin<TestImmutableFunctionAnalysis>; | 
|  | static AnalysisKey Key; | 
|  |  | 
|  | int &Runs; | 
|  | }; | 
|  |  | 
|  | AnalysisKey TestImmutableFunctionAnalysis::Key; | 
|  |  | 
|  | struct LambdaModulePass : public PassInfoMixin<LambdaModulePass> { | 
|  | template <typename T> | 
|  | LambdaModulePass(T &&Arg) : Func(std::forward<T>(Arg)) {} | 
|  |  | 
|  | PreservedAnalyses run(Module &F, ModuleAnalysisManager &AM) { | 
|  | return Func(F, AM); | 
|  | } | 
|  |  | 
|  | std::function<PreservedAnalyses(Module &, ModuleAnalysisManager &)> Func; | 
|  | }; | 
|  |  | 
|  | struct LambdaSCCPass : public PassInfoMixin<LambdaSCCPass> { | 
|  | template <typename T> LambdaSCCPass(T &&Arg) : Func(std::forward<T>(Arg)) {} | 
|  |  | 
|  | PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &UR) { | 
|  | return Func(C, AM, CG, UR); | 
|  | } | 
|  |  | 
|  | std::function<PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &, | 
|  | LazyCallGraph &, CGSCCUpdateResult &)> | 
|  | Func; | 
|  | }; | 
|  |  | 
|  | struct LambdaFunctionPass : public PassInfoMixin<LambdaFunctionPass> { | 
|  | template <typename T> | 
|  | LambdaFunctionPass(T &&Arg) : Func(std::forward<T>(Arg)) {} | 
|  |  | 
|  | PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { | 
|  | return Func(F, AM); | 
|  | } | 
|  |  | 
|  | std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)> Func; | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<Module> parseIR(const char *IR) { | 
|  | // We just use a static context here. This is never called from multiple | 
|  | // threads so it is harmless no matter how it is implemented. We just need | 
|  | // the context to outlive the module which it does. | 
|  | static LLVMContext C; | 
|  | SMDiagnostic Err; | 
|  | return parseAssemblyString(IR, Err, C); | 
|  | } | 
|  |  | 
|  | class CGSCCPassManagerTest : public ::testing::Test { | 
|  | protected: | 
|  | LLVMContext Context; | 
|  | FunctionAnalysisManager FAM; | 
|  | CGSCCAnalysisManager CGAM; | 
|  | ModuleAnalysisManager MAM; | 
|  |  | 
|  | std::unique_ptr<Module> M; | 
|  |  | 
|  | public: | 
|  | CGSCCPassManagerTest() | 
|  | : FAM(/*DebugLogging*/ true), CGAM(/*DebugLogging*/ true), | 
|  | MAM(/*DebugLogging*/ true), | 
|  | M(parseIR( | 
|  | // Define a module with the following call graph, where calls go | 
|  | // out the bottom of nodes and enter the top: | 
|  | // | 
|  | // f | 
|  | // |\   _ | 
|  | // | \ / | | 
|  | // g  h1 | | 
|  | // |  |  | | 
|  | // |  h2 | | 
|  | // |  |  | | 
|  | // |  h3 | | 
|  | // | / \_/ | 
|  | // |/ | 
|  | // x | 
|  | // | 
|  | "define void @f() {\n" | 
|  | "entry:\n" | 
|  | "  call void @g()\n" | 
|  | "  call void @h1()\n" | 
|  | "  ret void\n" | 
|  | "}\n" | 
|  | "define void @g() {\n" | 
|  | "entry:\n" | 
|  | "  call void @g()\n" | 
|  | "  call void @x()\n" | 
|  | "  ret void\n" | 
|  | "}\n" | 
|  | "define void @h1() {\n" | 
|  | "entry:\n" | 
|  | "  call void @h2()\n" | 
|  | "  ret void\n" | 
|  | "}\n" | 
|  | "define void @h2() {\n" | 
|  | "entry:\n" | 
|  | "  call void @h3()\n" | 
|  | "  call void @x()\n" | 
|  | "  ret void\n" | 
|  | "}\n" | 
|  | "define void @h3() {\n" | 
|  | "entry:\n" | 
|  | "  call void @h1()\n" | 
|  | "  ret void\n" | 
|  | "}\n" | 
|  | "define void @x() {\n" | 
|  | "entry:\n" | 
|  | "  ret void\n" | 
|  | "}\n")) { | 
|  | MAM.registerPass([&] { return TargetLibraryAnalysis(); }); | 
|  | MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); | 
|  | MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); | 
|  |  | 
|  | // Register required pass instrumentation analysis. | 
|  | MAM.registerPass([&] { return PassInstrumentationAnalysis(); }); | 
|  | CGAM.registerPass([&] { return PassInstrumentationAnalysis(); }); | 
|  | FAM.registerPass([&] { return PassInstrumentationAnalysis(); }); | 
|  |  | 
|  | // Cross-register proxies. | 
|  | MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); }); | 
|  | CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(); }); | 
|  | CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); }); | 
|  | FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); }); | 
|  | FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(CGSCCPassManagerTest, Basic) { | 
|  | int FunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); | 
|  | int ImmutableFunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { | 
|  | return TestImmutableFunctionAnalysis(ImmutableFunctionAnalysisRuns); | 
|  | }); | 
|  |  | 
|  | int SCCAnalysisRuns = 0; | 
|  | CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); | 
|  |  | 
|  | int ModuleAnalysisRuns = 0; | 
|  | MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  |  | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | FunctionPassManager FPM1(/*DebugLogging*/ true); | 
|  | int FunctionPassRunCount1 = 0; | 
|  | FPM1.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { | 
|  | ++FunctionPassRunCount1; | 
|  | return PreservedAnalyses::none(); | 
|  | })); | 
|  | CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); | 
|  |  | 
|  | int SCCPassRunCount1 = 0; | 
|  | int AnalyzedInstrCount1 = 0; | 
|  | int AnalyzedSCCFunctionCount1 = 0; | 
|  | int AnalyzedModuleFunctionCount1 = 0; | 
|  | CGPM1.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &UR) { | 
|  | ++SCCPassRunCount1; | 
|  |  | 
|  | const ModuleAnalysisManager &MAM = | 
|  | AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); | 
|  | FunctionAnalysisManager &FAM = | 
|  | AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); | 
|  | if (TestModuleAnalysis::Result *TMA = | 
|  | MAM.getCachedResult<TestModuleAnalysis>( | 
|  | *C.begin()->getFunction().getParent())) | 
|  | AnalyzedModuleFunctionCount1 += TMA->FunctionCount; | 
|  |  | 
|  | TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C, CG); | 
|  | AnalyzedSCCFunctionCount1 += AR.FunctionCount; | 
|  | for (LazyCallGraph::Node &N : C) { | 
|  | TestFunctionAnalysis::Result &FAR = | 
|  | FAM.getResult<TestFunctionAnalysis>(N.getFunction()); | 
|  | AnalyzedInstrCount1 += FAR.InstructionCount; | 
|  |  | 
|  | // Just ensure we get the immutable results. | 
|  | (void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction()); | 
|  | } | 
|  |  | 
|  | return PreservedAnalyses::all(); | 
|  | })); | 
|  |  | 
|  | FunctionPassManager FPM2(/*DebugLogging*/ true); | 
|  | int FunctionPassRunCount2 = 0; | 
|  | FPM2.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { | 
|  | ++FunctionPassRunCount2; | 
|  | return PreservedAnalyses::none(); | 
|  | })); | 
|  | CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); | 
|  |  | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | FunctionPassManager FPM3(/*DebugLogging*/ true); | 
|  | int FunctionPassRunCount3 = 0; | 
|  | FPM3.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { | 
|  | ++FunctionPassRunCount3; | 
|  | return PreservedAnalyses::none(); | 
|  | })); | 
|  | MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM3))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  |  | 
|  | EXPECT_EQ(4, SCCPassRunCount1); | 
|  | EXPECT_EQ(6, FunctionPassRunCount1); | 
|  | EXPECT_EQ(6, FunctionPassRunCount2); | 
|  | EXPECT_EQ(6, FunctionPassRunCount3); | 
|  |  | 
|  | EXPECT_EQ(1, ModuleAnalysisRuns); | 
|  | EXPECT_EQ(4, SCCAnalysisRuns); | 
|  | EXPECT_EQ(6, FunctionAnalysisRuns); | 
|  | EXPECT_EQ(6, ImmutableFunctionAnalysisRuns); | 
|  |  | 
|  | EXPECT_EQ(14, AnalyzedInstrCount1); | 
|  | EXPECT_EQ(6, AnalyzedSCCFunctionCount1); | 
|  | EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1); | 
|  | } | 
|  |  | 
|  | // Test that an SCC pass which fails to preserve a module analysis does in fact | 
|  | // invalidate that module analysis. | 
|  | TEST_F(CGSCCPassManagerTest, TestSCCPassInvalidatesModuleAnalysis) { | 
|  | int ModuleAnalysisRuns = 0; | 
|  | MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  |  | 
|  | // The first CGSCC run we preserve everything and make sure that works and | 
|  | // the module analysis is available in the second CGSCC run from the one | 
|  | // required module pass above. | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | int CountFoundModuleAnalysis1 = 0; | 
|  | CGPM1.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &UR) { | 
|  | const auto &MAM = | 
|  | AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); | 
|  | auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( | 
|  | *C.begin()->getFunction().getParent()); | 
|  |  | 
|  | if (TMA) | 
|  | ++CountFoundModuleAnalysis1; | 
|  |  | 
|  | return PreservedAnalyses::all(); | 
|  | })); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | // The second CGSCC run checks that the module analysis got preserved the | 
|  | // previous time and in one SCC fails to preserve it. | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | int CountFoundModuleAnalysis2 = 0; | 
|  | CGPM2.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &UR) { | 
|  | const auto &MAM = | 
|  | AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); | 
|  | auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( | 
|  | *C.begin()->getFunction().getParent()); | 
|  |  | 
|  | if (TMA) | 
|  | ++CountFoundModuleAnalysis2; | 
|  |  | 
|  | // Only fail to preserve analyses on one SCC and make sure that gets | 
|  | // propagated. | 
|  | return C.getName() == "(g)" ? PreservedAnalyses::none() | 
|  | : PreservedAnalyses::all(); | 
|  | })); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  |  | 
|  | // The third CGSCC run should fail to find a cached module analysis as it | 
|  | // should have been invalidated by the above CGSCC run. | 
|  | CGSCCPassManager CGPM3(/*DebugLogging*/ true); | 
|  | int CountFoundModuleAnalysis3 = 0; | 
|  | CGPM3.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &UR) { | 
|  | const auto &MAM = | 
|  | AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); | 
|  | auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( | 
|  | *C.begin()->getFunction().getParent()); | 
|  |  | 
|  | if (TMA) | 
|  | ++CountFoundModuleAnalysis3; | 
|  |  | 
|  | return PreservedAnalyses::none(); | 
|  | })); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  |  | 
|  | EXPECT_EQ(1, ModuleAnalysisRuns); | 
|  | EXPECT_EQ(4, CountFoundModuleAnalysis1); | 
|  | EXPECT_EQ(4, CountFoundModuleAnalysis2); | 
|  | EXPECT_EQ(0, CountFoundModuleAnalysis3); | 
|  | } | 
|  |  | 
|  | // Similar to the above, but test that this works for function passes embedded | 
|  | // *within* a CGSCC layer. | 
|  | TEST_F(CGSCCPassManagerTest, TestFunctionPassInsideCGSCCInvalidatesModuleAnalysis) { | 
|  | int ModuleAnalysisRuns = 0; | 
|  | MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  |  | 
|  | // The first run we preserve everything and make sure that works and the | 
|  | // module analysis is available in the second run from the one required | 
|  | // module pass above. | 
|  | FunctionPassManager FPM1(/*DebugLogging*/ true); | 
|  | // Start true and mark false if we ever failed to find a module analysis | 
|  | // because we expect this to succeed for each SCC. | 
|  | bool FoundModuleAnalysis1 = true; | 
|  | FPM1.addPass( | 
|  | LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { | 
|  | const auto &MAM = | 
|  | AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); | 
|  | auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); | 
|  |  | 
|  | if (!TMA) | 
|  | FoundModuleAnalysis1 = false; | 
|  |  | 
|  | return PreservedAnalyses::all(); | 
|  | })); | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | // The second run checks that the module analysis got preserved the previous | 
|  | // time and in one function fails to preserve it. | 
|  | FunctionPassManager FPM2(/*DebugLogging*/ true); | 
|  | // Again, start true and mark false if we ever failed to find a module analysis | 
|  | // because we expect this to succeed for each SCC. | 
|  | bool FoundModuleAnalysis2 = true; | 
|  | FPM2.addPass( | 
|  | LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { | 
|  | const auto &MAM = | 
|  | AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); | 
|  | auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); | 
|  |  | 
|  | if (!TMA) | 
|  | FoundModuleAnalysis2 = false; | 
|  |  | 
|  | // Only fail to preserve analyses on one SCC and make sure that gets | 
|  | // propagated. | 
|  | return F.getName() == "h2" ? PreservedAnalyses::none() | 
|  | : PreservedAnalyses::all(); | 
|  | })); | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  |  | 
|  | // The third run should fail to find a cached module analysis as it should | 
|  | // have been invalidated by the above run. | 
|  | FunctionPassManager FPM3(/*DebugLogging*/ true); | 
|  | // Start false and mark true if we ever *succeeded* to find a module | 
|  | // analysis, as we expect this to fail for every function. | 
|  | bool FoundModuleAnalysis3 = false; | 
|  | FPM3.addPass( | 
|  | LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { | 
|  | const auto &MAM = | 
|  | AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); | 
|  | auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); | 
|  |  | 
|  | if (TMA) | 
|  | FoundModuleAnalysis3 = true; | 
|  |  | 
|  | return PreservedAnalyses::none(); | 
|  | })); | 
|  | CGSCCPassManager CGPM3(/*DebugLogging*/ true); | 
|  | CGPM3.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM3))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  |  | 
|  | EXPECT_EQ(1, ModuleAnalysisRuns); | 
|  | EXPECT_TRUE(FoundModuleAnalysis1); | 
|  | EXPECT_TRUE(FoundModuleAnalysis2); | 
|  | EXPECT_FALSE(FoundModuleAnalysis3); | 
|  | } | 
|  |  | 
|  | // Test that a Module pass which fails to preserve an SCC analysis in fact | 
|  | // invalidates that analysis. | 
|  | TEST_F(CGSCCPassManagerTest, TestModulePassInvalidatesSCCAnalysis) { | 
|  | int SCCAnalysisRuns = 0; | 
|  | CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  |  | 
|  | // First force the analysis to be run. | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, | 
|  | CGSCCAnalysisManager, LazyCallGraph &, | 
|  | CGSCCUpdateResult &>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | // Now run a module pass that preserves the LazyCallGraph and the proxy but | 
|  | // not the SCC analysis. | 
|  | MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { | 
|  | PreservedAnalyses PA; | 
|  | PA.preserve<LazyCallGraphAnalysis>(); | 
|  | PA.preserve<CGSCCAnalysisManagerModuleProxy>(); | 
|  | PA.preserve<FunctionAnalysisManagerModuleProxy>(); | 
|  | return PA; | 
|  | })); | 
|  |  | 
|  | // And now a second CGSCC run which requires the SCC analysis again. This | 
|  | // will trigger re-running it. | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, | 
|  | CGSCCAnalysisManager, LazyCallGraph &, | 
|  | CGSCCUpdateResult &>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  | // Two runs and four SCCs. | 
|  | EXPECT_EQ(2 * 4, SCCAnalysisRuns); | 
|  | } | 
|  |  | 
|  | // Check that marking the SCC analysis preserved is sufficient to avoid | 
|  | // invaliadtion. This should only run the analysis once for each SCC. | 
|  | TEST_F(CGSCCPassManagerTest, TestModulePassCanPreserveSCCAnalysis) { | 
|  | int SCCAnalysisRuns = 0; | 
|  | CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  |  | 
|  | // First force the analysis to be run. | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, | 
|  | CGSCCAnalysisManager, LazyCallGraph &, | 
|  | CGSCCUpdateResult &>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | // Now run a module pass that preserves each of the necessary components | 
|  | // (but not everything). | 
|  | MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { | 
|  | PreservedAnalyses PA; | 
|  | PA.preserve<LazyCallGraphAnalysis>(); | 
|  | PA.preserve<CGSCCAnalysisManagerModuleProxy>(); | 
|  | PA.preserve<FunctionAnalysisManagerModuleProxy>(); | 
|  | PA.preserve<TestSCCAnalysis>(); | 
|  | return PA; | 
|  | })); | 
|  |  | 
|  | // And now a second CGSCC run which requires the SCC analysis again but find | 
|  | // it in the cache. | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, | 
|  | CGSCCAnalysisManager, LazyCallGraph &, | 
|  | CGSCCUpdateResult &>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  | // Four SCCs | 
|  | EXPECT_EQ(4, SCCAnalysisRuns); | 
|  | } | 
|  |  | 
|  | // Check that even when the analysis is preserved, if the SCC information isn't | 
|  | // we still nuke things because the SCC keys could change. | 
|  | TEST_F(CGSCCPassManagerTest, TestModulePassInvalidatesSCCAnalysisOnCGChange) { | 
|  | int SCCAnalysisRuns = 0; | 
|  | CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  |  | 
|  | // First force the analysis to be run. | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | CGPM1.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, | 
|  | CGSCCAnalysisManager, LazyCallGraph &, | 
|  | CGSCCUpdateResult &>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | // Now run a module pass that preserves the analysis but not the call | 
|  | // graph or proxy. | 
|  | MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { | 
|  | PreservedAnalyses PA; | 
|  | PA.preserve<TestSCCAnalysis>(); | 
|  | return PA; | 
|  | })); | 
|  |  | 
|  | // And now a second CGSCC run which requires the SCC analysis again. | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass(RequireAnalysisPass<TestSCCAnalysis, LazyCallGraph::SCC, | 
|  | CGSCCAnalysisManager, LazyCallGraph &, | 
|  | CGSCCUpdateResult &>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  | // Two runs and four SCCs. | 
|  | EXPECT_EQ(2 * 4, SCCAnalysisRuns); | 
|  | } | 
|  |  | 
|  | // Test that an SCC pass which fails to preserve a Function analysis in fact | 
|  | // invalidates that analysis. | 
|  | TEST_F(CGSCCPassManagerTest, TestSCCPassInvalidatesFunctionAnalysis) { | 
|  | int FunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); | 
|  |  | 
|  | // Create a very simple module with a single function and SCC to make testing | 
|  | // these issues much easier. | 
|  | std::unique_ptr<Module> M = parseIR("declare void @g()\n" | 
|  | "declare void @h()\n" | 
|  | "define void @f() {\n" | 
|  | "entry:\n" | 
|  | "  call void @g()\n" | 
|  | "  call void @h()\n" | 
|  | "  ret void\n" | 
|  | "}\n"); | 
|  |  | 
|  | CGSCCPassManager CGPM(/*DebugLogging*/ true); | 
|  |  | 
|  | // First force the analysis to be run. | 
|  | FunctionPassManager FPM1(/*DebugLogging*/ true); | 
|  | FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); | 
|  |  | 
|  | // Now run a module pass that preserves the LazyCallGraph and proxy but not | 
|  | // the SCC analysis. | 
|  | CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &, | 
|  | LazyCallGraph &, CGSCCUpdateResult &) { | 
|  | PreservedAnalyses PA; | 
|  | PA.preserve<LazyCallGraphAnalysis>(); | 
|  | return PA; | 
|  | })); | 
|  |  | 
|  | // And now a second CGSCC run which requires the SCC analysis again. This | 
|  | // will trigger re-running it. | 
|  | FunctionPassManager FPM2(/*DebugLogging*/ true); | 
|  | FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); | 
|  | MPM.run(*M, MAM); | 
|  | EXPECT_EQ(2, FunctionAnalysisRuns); | 
|  | } | 
|  |  | 
|  | // Check that marking the SCC analysis preserved is sufficient. This should | 
|  | // only run the analysis once the SCC. | 
|  | TEST_F(CGSCCPassManagerTest, TestSCCPassCanPreserveFunctionAnalysis) { | 
|  | int FunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); | 
|  |  | 
|  | // Create a very simple module with a single function and SCC to make testing | 
|  | // these issues much easier. | 
|  | std::unique_ptr<Module> M = parseIR("declare void @g()\n" | 
|  | "declare void @h()\n" | 
|  | "define void @f() {\n" | 
|  | "entry:\n" | 
|  | "  call void @g()\n" | 
|  | "  call void @h()\n" | 
|  | "  ret void\n" | 
|  | "}\n"); | 
|  |  | 
|  | CGSCCPassManager CGPM(/*DebugLogging*/ true); | 
|  |  | 
|  | // First force the analysis to be run. | 
|  | FunctionPassManager FPM1(/*DebugLogging*/ true); | 
|  | FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); | 
|  |  | 
|  | // Now run a module pass that preserves each of the necessary components | 
|  | // (but | 
|  | // not everything). | 
|  | CGPM.addPass(LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &, | 
|  | LazyCallGraph &, CGSCCUpdateResult &) { | 
|  | PreservedAnalyses PA; | 
|  | PA.preserve<LazyCallGraphAnalysis>(); | 
|  | PA.preserve<FunctionAnalysisManagerCGSCCProxy>(); | 
|  | PA.preserve<TestFunctionAnalysis>(); | 
|  | return PA; | 
|  | })); | 
|  |  | 
|  | // And now a second CGSCC run which requires the SCC analysis again but find | 
|  | // it in the cache. | 
|  | FunctionPassManager FPM2(/*DebugLogging*/ true); | 
|  | FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); | 
|  | MPM.run(*M, MAM); | 
|  | EXPECT_EQ(1, FunctionAnalysisRuns); | 
|  | } | 
|  |  | 
|  | // Note that there is no test for invalidating the call graph or other | 
|  | // structure with an SCC pass because there is no mechanism to do that from | 
|  | // withinsuch a pass. Instead, such a pass has to directly update the call | 
|  | // graph structure. | 
|  |  | 
|  | // Test that a madule pass invalidates function analyses when the CGSCC proxies | 
|  | // and pass manager. | 
|  | TEST_F(CGSCCPassManagerTest, | 
|  | TestModulePassInvalidatesFunctionAnalysisNestedInCGSCC) { | 
|  | MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); | 
|  |  | 
|  | int FunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  |  | 
|  | // First force the analysis to be run. | 
|  | FunctionPassManager FPM1(/*DebugLogging*/ true); | 
|  | FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | // Now run a module pass that preserves the LazyCallGraph and proxies but not | 
|  | // the Function analysis. | 
|  | MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { | 
|  | PreservedAnalyses PA; | 
|  | PA.preserve<LazyCallGraphAnalysis>(); | 
|  | PA.preserve<CGSCCAnalysisManagerModuleProxy>(); | 
|  | PA.preserve<FunctionAnalysisManagerCGSCCProxy>(); | 
|  | PA.preserve<FunctionAnalysisManagerModuleProxy>(); | 
|  | return PA; | 
|  | })); | 
|  |  | 
|  | // And now a second CGSCC run which requires the SCC analysis again. This | 
|  | // will trigger re-running it. | 
|  | FunctionPassManager FPM2(/*DebugLogging*/ true); | 
|  | FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  | // Two runs and 6 functions. | 
|  | EXPECT_EQ(2 * 6, FunctionAnalysisRuns); | 
|  | } | 
|  |  | 
|  | // Check that by marking the function pass and proxies as preserved, this | 
|  | // propagates all the way through. | 
|  | TEST_F(CGSCCPassManagerTest, | 
|  | TestModulePassCanPreserveFunctionAnalysisNestedInCGSCC) { | 
|  | MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); | 
|  |  | 
|  | int FunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  |  | 
|  | // First force the analysis to be run. | 
|  | FunctionPassManager FPM1(/*DebugLogging*/ true); | 
|  | FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | // Now run a module pass that preserves the LazyCallGraph, the proxy, and | 
|  | // the Function analysis. | 
|  | MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { | 
|  | PreservedAnalyses PA; | 
|  | PA.preserve<LazyCallGraphAnalysis>(); | 
|  | PA.preserve<CGSCCAnalysisManagerModuleProxy>(); | 
|  | PA.preserve<FunctionAnalysisManagerCGSCCProxy>(); | 
|  | PA.preserve<FunctionAnalysisManagerModuleProxy>(); | 
|  | PA.preserve<TestFunctionAnalysis>(); | 
|  | return PA; | 
|  | })); | 
|  |  | 
|  | // And now a second CGSCC run which requires the SCC analysis again. This | 
|  | // will trigger re-running it. | 
|  | FunctionPassManager FPM2(/*DebugLogging*/ true); | 
|  | FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  | // One run and 6 functions. | 
|  | EXPECT_EQ(6, FunctionAnalysisRuns); | 
|  | } | 
|  |  | 
|  | // Check that if the lazy call graph itself isn't preserved we still manage to | 
|  | // invalidate everything. | 
|  | TEST_F(CGSCCPassManagerTest, | 
|  | TestModulePassInvalidatesFunctionAnalysisNestedInCGSCCOnCGChange) { | 
|  | MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); | 
|  |  | 
|  | int FunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  |  | 
|  | // First force the analysis to be run. | 
|  | FunctionPassManager FPM1(/*DebugLogging*/ true); | 
|  | FPM1.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGSCCPassManager CGPM1(/*DebugLogging*/ true); | 
|  | CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); | 
|  |  | 
|  | // Now run a module pass that preserves the LazyCallGraph but not the | 
|  | // Function analysis. | 
|  | MPM.addPass(LambdaModulePass([&](Module &M, ModuleAnalysisManager &) { | 
|  | PreservedAnalyses PA; | 
|  | return PA; | 
|  | })); | 
|  |  | 
|  | // And now a second CGSCC run which requires the SCC analysis again. This | 
|  | // will trigger re-running it. | 
|  | FunctionPassManager FPM2(/*DebugLogging*/ true); | 
|  | FPM2.addPass(RequireAnalysisPass<TestFunctionAnalysis, Function>()); | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  |  | 
|  | MPM.run(*M, MAM); | 
|  | // Two runs and 6 functions. | 
|  | EXPECT_EQ(2 * 6, FunctionAnalysisRuns); | 
|  | } | 
|  |  | 
|  | /// A test CGSCC-level 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. | 
|  | /// | 
|  | /// FIXME: Currently this doesn't also depend on a function analysis, and if it | 
|  | /// did we would fail to invalidate it correctly. | 
|  | struct TestIndirectSCCAnalysis | 
|  | : public AnalysisInfoMixin<TestIndirectSCCAnalysis> { | 
|  | struct Result { | 
|  | Result(TestSCCAnalysis::Result &SCCDep, TestModuleAnalysis::Result &MDep) | 
|  | : SCCDep(SCCDep), MDep(MDep) {} | 
|  | TestSCCAnalysis::Result &SCCDep; | 
|  | TestModuleAnalysis::Result &MDep; | 
|  |  | 
|  | bool invalidate(LazyCallGraph::SCC &C, const PreservedAnalyses &PA, | 
|  | CGSCCAnalysisManager::Invalidator &Inv) { | 
|  | auto PAC = PA.getChecker<TestIndirectSCCAnalysis>(); | 
|  | return !(PAC.preserved() || | 
|  | PAC.preservedSet<AllAnalysesOn<LazyCallGraph::SCC>>()) || | 
|  | Inv.invalidate<TestSCCAnalysis>(C, PA); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TestIndirectSCCAnalysis(int &Runs) : Runs(Runs) {} | 
|  |  | 
|  | /// Run the analysis pass over the function and return a result. | 
|  | Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG) { | 
|  | ++Runs; | 
|  | auto &SCCDep = AM.getResult<TestSCCAnalysis>(C, CG); | 
|  |  | 
|  | auto &ModuleProxy = AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG); | 
|  | const ModuleAnalysisManager &MAM = ModuleProxy.getManager(); | 
|  | // For the test, we insist that the module analysis starts off in the | 
|  | // cache. | 
|  | auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>( | 
|  | *C.begin()->getFunction().getParent()); | 
|  | // Register the dependency as module analysis dependencies have to be | 
|  | // pre-registered on the proxy. | 
|  | ModuleProxy.registerOuterAnalysisInvalidation<TestModuleAnalysis, | 
|  | TestIndirectSCCAnalysis>(); | 
|  |  | 
|  | return Result(SCCDep, MDep); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend AnalysisInfoMixin<TestIndirectSCCAnalysis>; | 
|  | static AnalysisKey Key; | 
|  |  | 
|  | int &Runs; | 
|  | }; | 
|  |  | 
|  | AnalysisKey TestIndirectSCCAnalysis::Key; | 
|  |  | 
|  | /// A test analysis pass which caches 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 TestDoublyIndirectSCCAnalysis | 
|  | : public AnalysisInfoMixin<TestDoublyIndirectSCCAnalysis> { | 
|  | struct Result { | 
|  | Result(TestIndirectSCCAnalysis::Result &IDep) : IDep(IDep) {} | 
|  | TestIndirectSCCAnalysis::Result &IDep; | 
|  |  | 
|  | bool invalidate(LazyCallGraph::SCC &C, const PreservedAnalyses &PA, | 
|  | CGSCCAnalysisManager::Invalidator &Inv) { | 
|  | auto PAC = PA.getChecker<TestDoublyIndirectSCCAnalysis>(); | 
|  | return !(PAC.preserved() || | 
|  | PAC.preservedSet<AllAnalysesOn<LazyCallGraph::SCC>>()) || | 
|  | Inv.invalidate<TestIndirectSCCAnalysis>(C, PA); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TestDoublyIndirectSCCAnalysis(int &Runs) : Runs(Runs) {} | 
|  |  | 
|  | /// Run the analysis pass over the function and return a result. | 
|  | Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG) { | 
|  | ++Runs; | 
|  | auto &IDep = AM.getResult<TestIndirectSCCAnalysis>(C, CG); | 
|  | return Result(IDep); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend AnalysisInfoMixin<TestDoublyIndirectSCCAnalysis>; | 
|  | static AnalysisKey Key; | 
|  |  | 
|  | int &Runs; | 
|  | }; | 
|  |  | 
|  | AnalysisKey TestDoublyIndirectSCCAnalysis::Key; | 
|  |  | 
|  | /// A test analysis pass which caches results from three different IR unit | 
|  | /// layers and requires intermediate layers to correctly propagate the entire | 
|  | /// distance. | 
|  | struct TestIndirectFunctionAnalysis | 
|  | : public AnalysisInfoMixin<TestIndirectFunctionAnalysis> { | 
|  | struct Result { | 
|  | Result(TestFunctionAnalysis::Result &FDep, TestModuleAnalysis::Result &MDep, | 
|  | TestSCCAnalysis::Result &SCCDep) | 
|  | : FDep(FDep), MDep(MDep), SCCDep(SCCDep) {} | 
|  | TestFunctionAnalysis::Result &FDep; | 
|  | TestModuleAnalysis::Result &MDep; | 
|  | TestSCCAnalysis::Result &SCCDep; | 
|  |  | 
|  | 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 &ModuleProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F); | 
|  | const ModuleAnalysisManager &MAM = ModuleProxy.getManager(); | 
|  | // For the test, we insist that the module analysis starts off in the | 
|  | // cache. | 
|  | auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); | 
|  | // Register the dependency as module analysis dependencies have to be | 
|  | // pre-registered on the proxy. | 
|  | ModuleProxy.registerOuterAnalysisInvalidation< | 
|  | TestModuleAnalysis, TestIndirectFunctionAnalysis>(); | 
|  |  | 
|  | // For thet test we assume this is run inside a CGSCC pass manager. | 
|  | const LazyCallGraph &CG = | 
|  | *MAM.getCachedResult<LazyCallGraphAnalysis>(*F.getParent()); | 
|  | auto &CGSCCProxy = AM.getResult<CGSCCAnalysisManagerFunctionProxy>(F); | 
|  | const CGSCCAnalysisManager &CGAM = CGSCCProxy.getManager(); | 
|  | // For the test, we insist that the CGSCC analysis starts off in the cache. | 
|  | auto &SCCDep = | 
|  | *CGAM.getCachedResult<TestSCCAnalysis>(*CG.lookupSCC(*CG.lookup(F))); | 
|  | // Register the dependency as CGSCC analysis dependencies have to be | 
|  | // pre-registered on the proxy. | 
|  | CGSCCProxy.registerOuterAnalysisInvalidation< | 
|  | TestSCCAnalysis, TestIndirectFunctionAnalysis>(); | 
|  |  | 
|  | return Result(FDep, MDep, SCCDep); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>; | 
|  | static AnalysisKey Key; | 
|  |  | 
|  | int &Runs; | 
|  | }; | 
|  |  | 
|  | AnalysisKey TestIndirectFunctionAnalysis::Key; | 
|  |  | 
|  | TEST_F(CGSCCPassManagerTest, TestIndirectAnalysisInvalidation) { | 
|  | int ModuleAnalysisRuns = 0; | 
|  | MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); | 
|  |  | 
|  | int SCCAnalysisRuns = 0, IndirectSCCAnalysisRuns = 0, | 
|  | DoublyIndirectSCCAnalysisRuns = 0; | 
|  | CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); | 
|  | CGAM.registerPass( | 
|  | [&] { return TestIndirectSCCAnalysis(IndirectSCCAnalysisRuns); }); | 
|  | CGAM.registerPass([&] { | 
|  | return TestDoublyIndirectSCCAnalysis(DoublyIndirectSCCAnalysisRuns); | 
|  | }); | 
|  |  | 
|  | int FunctionAnalysisRuns = 0, IndirectFunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); | 
|  | FAM.registerPass([&] { | 
|  | return TestIndirectFunctionAnalysis(IndirectFunctionAnalysisRuns); | 
|  | }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  |  | 
|  | int FunctionCount = 0; | 
|  | CGSCCPassManager CGPM(/*DebugLogging*/ true); | 
|  | // First just use the analysis to get the function count and preserve | 
|  | // everything. | 
|  | CGPM.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &) { | 
|  | auto &DoublyIndirectResult = | 
|  | AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); | 
|  | auto &IndirectResult = DoublyIndirectResult.IDep; | 
|  | FunctionCount += IndirectResult.SCCDep.FunctionCount; | 
|  | return PreservedAnalyses::all(); | 
|  | })); | 
|  | CGPM.addPass(createCGSCCToFunctionPassAdaptor( | 
|  | RequireAnalysisPass<TestIndirectFunctionAnalysis, Function>())); | 
|  |  | 
|  | // Next, invalidate | 
|  | //   - both analyses for the (f) and (x) SCCs, | 
|  | //   - just the underlying (indirect) analysis for (g) SCC, and | 
|  | //   - just the direct analysis for (h1,h2,h3) SCC. | 
|  | CGPM.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &) { | 
|  | auto &DoublyIndirectResult = | 
|  | AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); | 
|  | auto &IndirectResult = DoublyIndirectResult.IDep; | 
|  | FunctionCount += IndirectResult.SCCDep.FunctionCount; | 
|  | auto PA = PreservedAnalyses::none(); | 
|  | PA.preserve<FunctionAnalysisManagerCGSCCProxy>(); | 
|  | PA.preserveSet<AllAnalysesOn<Function>>(); | 
|  | if (C.getName() == "(g)") | 
|  | PA.preserve<TestSCCAnalysis>(); | 
|  | else if (C.getName() == "(h3, h1, h2)") | 
|  | PA.preserve<TestIndirectSCCAnalysis>(); | 
|  | return PA; | 
|  | })); | 
|  | // Finally, use the analysis again on each SCC (and function), forcing | 
|  | // re-computation for all of them. | 
|  | CGPM.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &) { | 
|  | auto &DoublyIndirectResult = | 
|  | AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); | 
|  | auto &IndirectResult = DoublyIndirectResult.IDep; | 
|  | FunctionCount += IndirectResult.SCCDep.FunctionCount; | 
|  | return PreservedAnalyses::all(); | 
|  | })); | 
|  | CGPM.addPass(createCGSCCToFunctionPassAdaptor( | 
|  | RequireAnalysisPass<TestIndirectFunctionAnalysis, Function>())); | 
|  |  | 
|  | // Create a second CGSCC pass manager. This will cause the module-level | 
|  | // invalidation to occur, which will force yet another invalidation of the | 
|  | // indirect SCC-level analysis as the module analysis it depends on gets | 
|  | // invalidated. | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &) { | 
|  | auto &DoublyIndirectResult = | 
|  | AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); | 
|  | auto &IndirectResult = DoublyIndirectResult.IDep; | 
|  | FunctionCount += IndirectResult.SCCDep.FunctionCount; | 
|  | return PreservedAnalyses::all(); | 
|  | })); | 
|  | CGPM2.addPass(createCGSCCToFunctionPassAdaptor( | 
|  | RequireAnalysisPass<TestIndirectFunctionAnalysis, Function>())); | 
|  |  | 
|  | // Add a requires pass to populate the module analysis and then our CGSCC | 
|  | // pass pipeline. | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); | 
|  | // Now require the module analysis again (it will have been invalidated once) | 
|  | // and then use it again from our second CGSCC pipeline.. | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  | MPM.run(*M, MAM); | 
|  |  | 
|  | // There are generally two possible runs for each of the four SCCs. But | 
|  | // for one SCC, we only invalidate the indirect analysis so the base one | 
|  | // only gets run seven times. | 
|  | EXPECT_EQ(7, SCCAnalysisRuns); | 
|  | // The module analysis pass should be run twice here. | 
|  | EXPECT_EQ(2, ModuleAnalysisRuns); | 
|  | // The indirect analysis is invalidated (either directly or indirectly) three | 
|  | // times for each of four SCCs. | 
|  | EXPECT_EQ(3 * 4, IndirectSCCAnalysisRuns); | 
|  | EXPECT_EQ(3 * 4, DoublyIndirectSCCAnalysisRuns); | 
|  |  | 
|  | // We run the indirect function analysis once per function the first time. | 
|  | // Then we re-run it for every SCC but "(g)". Then we re-run it for every | 
|  | // function again. | 
|  | EXPECT_EQ(6 + 5 + 6, IndirectFunctionAnalysisRuns); | 
|  |  | 
|  | // Four passes count each of six functions once (via SCCs). | 
|  | EXPECT_EQ(4 * 6, FunctionCount); | 
|  | } | 
|  |  | 
|  | TEST_F(CGSCCPassManagerTest, TestAnalysisInvalidationCGSCCUpdate) { | 
|  | int ModuleAnalysisRuns = 0; | 
|  | MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); | 
|  |  | 
|  | int SCCAnalysisRuns = 0, IndirectSCCAnalysisRuns = 0, | 
|  | DoublyIndirectSCCAnalysisRuns = 0; | 
|  | CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); | 
|  | CGAM.registerPass( | 
|  | [&] { return TestIndirectSCCAnalysis(IndirectSCCAnalysisRuns); }); | 
|  | CGAM.registerPass([&] { | 
|  | return TestDoublyIndirectSCCAnalysis(DoublyIndirectSCCAnalysisRuns); | 
|  | }); | 
|  |  | 
|  | int FunctionAnalysisRuns = 0, IndirectFunctionAnalysisRuns = 0; | 
|  | FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); | 
|  | FAM.registerPass([&] { | 
|  | return TestIndirectFunctionAnalysis(IndirectFunctionAnalysisRuns); | 
|  | }); | 
|  |  | 
|  | ModulePassManager MPM(/*DebugLogging*/ true); | 
|  |  | 
|  | CGSCCPassManager CGPM(/*DebugLogging*/ true); | 
|  | // First just use the analysis to get the function count and preserve | 
|  | // everything. | 
|  | using RequireTestIndirectFunctionAnalysisPass = | 
|  | RequireAnalysisPass<TestIndirectFunctionAnalysis, Function>; | 
|  | using RequireTestDoublyIndirectSCCAnalysisPass = | 
|  | RequireAnalysisPass<TestDoublyIndirectSCCAnalysis, LazyCallGraph::SCC, | 
|  | CGSCCAnalysisManager, LazyCallGraph &, | 
|  | CGSCCUpdateResult &>; | 
|  | CGPM.addPass(RequireTestDoublyIndirectSCCAnalysisPass()); | 
|  | CGPM.addPass(createCGSCCToFunctionPassAdaptor( | 
|  | RequireTestIndirectFunctionAnalysisPass())); | 
|  |  | 
|  | // Next, we inject an SCC pass that invalidates everything for the `(h3, h1, | 
|  | // h2)` SCC but also deletes the call edge from `h2` to `h3` and updates the | 
|  | // CG. This should successfully invalidate (and force to be re-run) all the | 
|  | // analyses for that SCC and for the functions. | 
|  | CGPM.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &UR) { | 
|  | (void)AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); | 
|  | if (C.getName() != "(h3, h1, h2)") | 
|  | return PreservedAnalyses::all(); | 
|  |  | 
|  | // Build the preserved set. | 
|  | auto PA = PreservedAnalyses::none(); | 
|  | PA.preserve<FunctionAnalysisManagerCGSCCProxy>(); | 
|  | PA.preserve<TestIndirectSCCAnalysis>(); | 
|  | PA.preserve<TestDoublyIndirectSCCAnalysis>(); | 
|  |  | 
|  | // Delete the call from `h2` to `h3`. | 
|  | auto &H2N = *llvm::find_if( | 
|  | C, [](LazyCallGraph::Node &N) { return N.getName() == "h2"; }); | 
|  | auto &H2F = H2N.getFunction(); | 
|  | auto &H3F = *cast<CallInst>(H2F.begin()->begin())->getCalledFunction(); | 
|  | assert(H3F.getName() == "h3" && "Wrong called function!"); | 
|  | H2F.begin()->begin()->eraseFromParent(); | 
|  | // Insert a bitcast of `h3` so that we retain a ref edge to it. | 
|  | (void)CastInst::CreatePointerCast(&H3F, | 
|  | Type::getInt8PtrTy(H2F.getContext()), | 
|  | "dummy", &*H2F.begin()->begin()); | 
|  |  | 
|  | // Now update the call graph. | 
|  | auto &NewC = | 
|  | updateCGAndAnalysisManagerForFunctionPass(CG, C, H2N, AM, UR); | 
|  | assert(&NewC != &C && "Should get a new SCC due to update!"); | 
|  | (void)&NewC; | 
|  |  | 
|  | return PA; | 
|  | })); | 
|  | // Now use the analysis again on each SCC and function, forcing | 
|  | // re-computation for all of them. | 
|  | CGPM.addPass(RequireTestDoublyIndirectSCCAnalysisPass()); | 
|  | CGPM.addPass(createCGSCCToFunctionPassAdaptor( | 
|  | RequireTestIndirectFunctionAnalysisPass())); | 
|  |  | 
|  | // Create another CGSCC pipeline that requires all the analyses again. | 
|  | CGSCCPassManager CGPM2(/*DebugLogging*/ true); | 
|  | CGPM2.addPass(RequireTestDoublyIndirectSCCAnalysisPass()); | 
|  | CGPM2.addPass(createCGSCCToFunctionPassAdaptor( | 
|  | RequireTestIndirectFunctionAnalysisPass())); | 
|  |  | 
|  | // Next we inject an SCC pass that finds the `(h2)` SCC, adds a call to `h3` | 
|  | // back to `h2`, and then invalidates everything for what will then be the | 
|  | // `(h3, h1, h2)` SCC again. | 
|  | CGSCCPassManager CGPM3(/*DebugLogging*/ true); | 
|  | CGPM3.addPass( | 
|  | LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, | 
|  | LazyCallGraph &CG, CGSCCUpdateResult &UR) { | 
|  | (void)AM.getResult<TestDoublyIndirectSCCAnalysis>(C, CG); | 
|  | if (C.getName() != "(h2)") | 
|  | return PreservedAnalyses::all(); | 
|  |  | 
|  | // Build the preserved set. | 
|  | auto PA = PreservedAnalyses::none(); | 
|  | PA.preserve<FunctionAnalysisManagerCGSCCProxy>(); | 
|  | PA.preserve<TestIndirectSCCAnalysis>(); | 
|  | PA.preserve<TestDoublyIndirectSCCAnalysis>(); | 
|  |  | 
|  | // Delete the bitcast of `h3` that we added earlier. | 
|  | auto &H2N = *C.begin(); | 
|  | auto &H2F = H2N.getFunction(); | 
|  | auto &H3F = *cast<Function>(cast<BitCastInst>(H2F.begin()->begin())->getOperand(0)); | 
|  | assert(H3F.getName() == "h3" && "Wrong called function!"); | 
|  | H2F.begin()->begin()->eraseFromParent(); | 
|  | // And insert a call to `h3`. | 
|  | (void)CallInst::Create(&H3F, {}, "", &*H2F.begin()->begin()); | 
|  |  | 
|  | // Now update the call graph. | 
|  | auto &NewC = | 
|  | updateCGAndAnalysisManagerForFunctionPass(CG, C, H2N, AM, UR); | 
|  | assert(&NewC != &C && "Should get a new SCC due to update!"); | 
|  | (void)&NewC; | 
|  |  | 
|  | return PA; | 
|  | })); | 
|  | // Now use the analysis again on each SCC and function, forcing | 
|  | // re-computation for all of them. | 
|  | CGPM3.addPass(RequireTestDoublyIndirectSCCAnalysisPass()); | 
|  | CGPM3.addPass(createCGSCCToFunctionPassAdaptor( | 
|  | RequireTestIndirectFunctionAnalysisPass())); | 
|  |  | 
|  | // Create a second CGSCC pass manager. This will cause the module-level | 
|  | // invalidation to occur, which will force yet another invalidation of the | 
|  | // indirect SCC-level analysis as the module analysis it depends on gets | 
|  | // invalidated. | 
|  | CGSCCPassManager CGPM4(/*DebugLogging*/ true); | 
|  | CGPM4.addPass(RequireTestDoublyIndirectSCCAnalysisPass()); | 
|  | CGPM4.addPass(createCGSCCToFunctionPassAdaptor( | 
|  | RequireTestIndirectFunctionAnalysisPass())); | 
|  |  | 
|  | // Add a requires pass to populate the module analysis and then one of our | 
|  | // CGSCC pipelines. Repeat for all four CGSCC pipelines. | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); | 
|  | MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); | 
|  | MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM4))); | 
|  | MPM.run(*M, MAM); | 
|  |  | 
|  | // We run over four SCCs the first time. But then we split an SCC into three. | 
|  | // And then we merge those three back into one. | 
|  | EXPECT_EQ(4 + 3 + 1, SCCAnalysisRuns); | 
|  | // The module analysis pass should be run three times. | 
|  | EXPECT_EQ(3, ModuleAnalysisRuns); | 
|  | // We run over four SCCs the first time. Then over the two new ones. Then the | 
|  | // entire module is invalidated causing a full run over all seven. Then we | 
|  | // fold three SCCs back to one, and then run over the whole module again. | 
|  | EXPECT_EQ(4 + 2 + 7 + 1 + 4, IndirectSCCAnalysisRuns); | 
|  | EXPECT_EQ(4 + 2 + 7 + 1 + 4, DoublyIndirectSCCAnalysisRuns); | 
|  |  | 
|  | // First we run over all six functions. Then we re-run it over three when we | 
|  | // split their SCCs. Then we re-run over the whole module. Then we re-run | 
|  | // over three functions merged back into a single SCC, and then over the | 
|  | // whole module again. | 
|  | EXPECT_EQ(6 + 3 + 6 + 3 + 6, FunctionAnalysisRuns); | 
|  |  | 
|  | // Re run the function analysis over the entire module, and then re-run it | 
|  | // over the `(h3, h1, h2)` SCC due to invalidation. Then we re-run it over | 
|  | // the entire module, then the three functions merged back into a single SCC, | 
|  | // and then over the whole module. | 
|  | EXPECT_EQ(6 + 3 + 6 + 3 + 6, IndirectFunctionAnalysisRuns); | 
|  | } | 
|  | } |