123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284 |
- //===- CGSCCPassManagerTest.cpp -------------------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #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")) {
- FAM.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. However, this also
- // invalidates all three SCCs further down in the PO walk.
- EXPECT_EQ(4 + 3 + 1 + 3, 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, re-compute for it and the two SCCs above it
- // in the graph, and then run over the whole module again.
- EXPECT_EQ(4 + 2 + 7 + 1 + 3 + 4, IndirectSCCAnalysisRuns);
- EXPECT_EQ(4 + 2 + 7 + 1 + 3 + 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, then those three
- // functions again, the two functions in SCCs above it in the graph, and then
- // over the whole module again.
- EXPECT_EQ(6 + 3 + 6 + 3 + 3 + 2 + 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,
- // those three functions again, then the two functions in SCCs above it in
- // the graph, and then over the whole module.
- EXPECT_EQ(6 + 3 + 6 + 3 + 3 + 2 + 6, IndirectFunctionAnalysisRuns);
- }
- }
|