//===- llvm/unittest/Analysis/LoopPassManagerTest.cpp - LPM tests ---------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "llvm/Analysis/LoopPassManager.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/Support/SourceMgr.h" using namespace llvm; namespace { class TestLoopAnalysis { /// \brief Private static data to provide unique ID. static char PassID; int &Runs; public: struct Result { Result(int Count) : BlockCount(Count) {} int BlockCount; }; /// \brief Returns an opaque, unique ID for this pass type. static void *ID() { return (void *)&PassID; } /// \brief Returns the name of the analysis. static StringRef name() { return "TestLoopAnalysis"; } TestLoopAnalysis(int &Runs) : Runs(Runs) {} /// \brief Run the analysis pass over the loop and return a result. Result run(Loop &L, LoopAnalysisManager &AM) { ++Runs; int Count = 0; for (auto I = L.block_begin(), E = L.block_end(); I != E; ++I) ++Count; return Result(Count); } }; char TestLoopAnalysis::PassID; class TestLoopPass { std::vector &VisitedLoops; int &AnalyzedBlockCount; bool OnlyUseCachedResults; public: TestLoopPass(std::vector &VisitedLoops, int &AnalyzedBlockCount, bool OnlyUseCachedResults = false) : VisitedLoops(VisitedLoops), AnalyzedBlockCount(AnalyzedBlockCount), OnlyUseCachedResults(OnlyUseCachedResults) {} PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM) { VisitedLoops.push_back(L.getName()); if (OnlyUseCachedResults) { // Hack to force the use of the cached interface. if (auto *AR = AM.getCachedResult(L)) AnalyzedBlockCount += AR->BlockCount; } else { // Typical path just runs the analysis as needed. auto &AR = AM.getResult(L); AnalyzedBlockCount += AR.BlockCount; } return PreservedAnalyses::all(); } static StringRef name() { return "TestLoopPass"; } }; // A test loop pass that invalidates the analysis for loops with the given name. class TestLoopInvalidatingPass { StringRef Name; public: TestLoopInvalidatingPass(StringRef LoopName) : Name(LoopName) {} PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM) { return L.getName() == Name ? getLoopPassPreservedAnalyses() : PreservedAnalyses::all(); } static StringRef name() { return "TestLoopInvalidatingPass"; } }; std::unique_ptr parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; return parseAssemblyString(IR, Err, C); } class LoopPassManagerTest : public ::testing::Test { protected: LLVMContext Context; std::unique_ptr M; public: LoopPassManagerTest() : M(parseIR(Context, "define void @f() {\n" "entry:\n" " br label %loop.0\n" "loop.0:\n" " br i1 undef, label %loop.0.0, label %end\n" "loop.0.0:\n" " br i1 undef, label %loop.0.0, label %loop.0.1\n" "loop.0.1:\n" " br i1 undef, label %loop.0.1, label %loop.0\n" "end:\n" " ret void\n" "}\n" "\n" "define void @g() {\n" "entry:\n" " br label %loop.g.0\n" "loop.g.0:\n" " br i1 undef, label %loop.g.0, label %end\n" "end:\n" " ret void\n" "}\n")) {} }; #define EXPECT_N_ELEMENTS_EQ(N, EXPECTED, ACTUAL) \ do { \ EXPECT_EQ(N##UL, ACTUAL.size()); \ for (int I = 0; I < N; ++I) \ EXPECT_TRUE(EXPECTED[I] == ACTUAL[I]) << "Element " << I << " is " \ << ACTUAL[I] << ". Expected " \ << EXPECTED[I] << "."; \ } while (0) TEST_F(LoopPassManagerTest, Basic) { LoopAnalysisManager LAM(true); int LoopAnalysisRuns = 0; LAM.registerPass([&] { return TestLoopAnalysis(LoopAnalysisRuns); }); FunctionAnalysisManager FAM(true); // We need DominatorTreeAnalysis for LoopAnalysis. FAM.registerPass([&] { return DominatorTreeAnalysis(); }); FAM.registerPass([&] { return LoopAnalysis(); }); FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); }); LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); }); ModuleAnalysisManager MAM(true); MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); ModulePassManager MPM(true); FunctionPassManager FPM(true); // Visit all of the loops. std::vector VisitedLoops1; int AnalyzedBlockCount1 = 0; { LoopPassManager LPM; LPM.addPass(TestLoopPass(VisitedLoops1, AnalyzedBlockCount1)); FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM))); } // Only use cached analyses. std::vector VisitedLoops2; int AnalyzedBlockCount2 = 0; { LoopPassManager LPM; LPM.addPass(TestLoopInvalidatingPass("loop.g.0")); LPM.addPass(TestLoopPass(VisitedLoops2, AnalyzedBlockCount2, /*OnlyUseCachedResults=*/true)); FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM))); } MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); MPM.run(*M, MAM); StringRef ExpectedLoops[] = {"loop.0.0", "loop.0.1", "loop.0", "loop.g.0"}; // Validate the counters and order of loops visited. // loop.0 has 3 blocks whereas loop.0.0, loop.0.1, and loop.g.0 each have 1. EXPECT_N_ELEMENTS_EQ(4, ExpectedLoops, VisitedLoops1); EXPECT_EQ(6, AnalyzedBlockCount1); EXPECT_N_ELEMENTS_EQ(4, ExpectedLoops, VisitedLoops2); // The block from loop.g.0 won't be counted, since it wasn't cached. EXPECT_EQ(5, AnalyzedBlockCount2); // The first LPM runs the loop analysis for all four loops, the second uses // cached results for everything. EXPECT_EQ(4, LoopAnalysisRuns); } }