| //===- 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/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: |
| struct Result { |
| Result(int Count) : FunctionCount(Count) {} |
| int FunctionCount; |
| }; |
| |
| static void *ID() { return (void *)&PassID; } |
| static StringRef name() { return "TestModuleAnalysis"; } |
| |
| TestModuleAnalysis(int &Runs) : Runs(Runs) {} |
| |
| Result run(Module &M, ModuleAnalysisManager &AM) { |
| ++Runs; |
| return Result(M.size()); |
| } |
| |
| private: |
| static char PassID; |
| |
| int &Runs; |
| }; |
| |
| char TestModuleAnalysis::PassID; |
| |
| class TestSCCAnalysis { |
| public: |
| struct Result { |
| Result(int Count) : FunctionCount(Count) {} |
| int FunctionCount; |
| }; |
| |
| static void *ID() { return (void *)&PassID; } |
| static StringRef name() { return "TestSCCAnalysis"; } |
| |
| TestSCCAnalysis(int &Runs) : Runs(Runs) {} |
| |
| Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM) { |
| ++Runs; |
| return Result(C.size()); |
| } |
| |
| private: |
| static char PassID; |
| |
| int &Runs; |
| }; |
| |
| char TestSCCAnalysis::PassID; |
| |
| class TestFunctionAnalysis { |
| public: |
| struct Result { |
| Result(int Count) : InstructionCount(Count) {} |
| int InstructionCount; |
| }; |
| |
| static void *ID() { return (void *)&PassID; } |
| static StringRef name() { return "TestFunctionAnalysis"; } |
| |
| 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: |
| static char PassID; |
| |
| int &Runs; |
| }; |
| |
| char TestFunctionAnalysis::PassID; |
| |
| class TestImmutableFunctionAnalysis { |
| public: |
| struct Result { |
| bool invalidate(Function &, const PreservedAnalyses &) { return false; } |
| }; |
| |
| static void *ID() { return (void *)&PassID; } |
| static StringRef name() { return "TestImmutableFunctionAnalysis"; } |
| |
| TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {} |
| |
| Result run(Function &F, FunctionAnalysisManager &AM) { |
| ++Runs; |
| return Result(); |
| } |
| |
| private: |
| static char PassID; |
| |
| int &Runs; |
| }; |
| |
| char TestImmutableFunctionAnalysis::PassID; |
| |
| struct TestModulePass { |
| TestModulePass(int &RunCount) : RunCount(RunCount) {} |
| |
| PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { |
| ++RunCount; |
| (void)AM.getResult<TestModuleAnalysis>(M); |
| return PreservedAnalyses::all(); |
| } |
| |
| static StringRef name() { return "TestModulePass"; } |
| |
| int &RunCount; |
| }; |
| |
| struct TestSCCPass { |
| TestSCCPass(int &RunCount, int &AnalyzedInstrCount, |
| int &AnalyzedSCCFunctionCount, int &AnalyzedModuleFunctionCount, |
| bool OnlyUseCachedResults = false) |
| : RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount), |
| AnalyzedSCCFunctionCount(AnalyzedSCCFunctionCount), |
| AnalyzedModuleFunctionCount(AnalyzedModuleFunctionCount), |
| OnlyUseCachedResults(OnlyUseCachedResults) {} |
| |
| PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM) { |
| ++RunCount; |
| |
| const ModuleAnalysisManager &MAM = |
| AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C).getManager(); |
| FunctionAnalysisManager &FAM = |
| AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C).getManager(); |
| if (TestModuleAnalysis::Result *TMA = |
| MAM.getCachedResult<TestModuleAnalysis>( |
| *C.begin()->getFunction().getParent())) |
| AnalyzedModuleFunctionCount += TMA->FunctionCount; |
| |
| if (OnlyUseCachedResults) { |
| // Hack to force the use of the cached interface. |
| if (TestSCCAnalysis::Result *AR = AM.getCachedResult<TestSCCAnalysis>(C)) |
| AnalyzedSCCFunctionCount += AR->FunctionCount; |
| for (LazyCallGraph::Node &N : C) |
| if (TestFunctionAnalysis::Result *FAR = |
| FAM.getCachedResult<TestFunctionAnalysis>(N.getFunction())) |
| AnalyzedInstrCount += FAR->InstructionCount; |
| } else { |
| // Typical path just runs the analysis as needed. |
| TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C); |
| AnalyzedSCCFunctionCount += AR.FunctionCount; |
| for (LazyCallGraph::Node &N : C) { |
| TestFunctionAnalysis::Result &FAR = |
| FAM.getResult<TestFunctionAnalysis>(N.getFunction()); |
| AnalyzedInstrCount += FAR.InstructionCount; |
| |
| // Just ensure we get the immutable results. |
| (void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction()); |
| } |
| } |
| |
| return PreservedAnalyses::all(); |
| } |
| |
| static StringRef name() { return "TestSCCPass"; } |
| |
| int &RunCount; |
| int &AnalyzedInstrCount; |
| int &AnalyzedSCCFunctionCount; |
| int &AnalyzedModuleFunctionCount; |
| bool OnlyUseCachedResults; |
| }; |
| |
| struct TestFunctionPass { |
| TestFunctionPass(int &RunCount) : RunCount(RunCount) {} |
| |
| PreservedAnalyses run(Function &F, AnalysisManager<Function> &) { |
| ++RunCount; |
| return PreservedAnalyses::none(); |
| } |
| |
| static StringRef name() { return "TestFunctionPass"; } |
| |
| int &RunCount; |
| }; |
| |
| 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); |
| } |
| |
| TEST(CGSCCPassManagerTest, Basic) { |
| auto M = parseIR("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"); |
| FunctionAnalysisManager FAM(/*DebugLogging*/ true); |
| int FunctionAnalysisRuns = 0; |
| FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); |
| int ImmutableFunctionAnalysisRuns = 0; |
| FAM.registerPass([&] { |
| return TestImmutableFunctionAnalysis(ImmutableFunctionAnalysisRuns); |
| }); |
| |
| CGSCCAnalysisManager CGAM(/*DebugLogging*/ true); |
| int SCCAnalysisRuns = 0; |
| CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); |
| |
| ModuleAnalysisManager MAM(/*DebugLogging*/ true); |
| int ModuleAnalysisRuns = 0; |
| MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); |
| MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); |
| |
| MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
| MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); }); |
| CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(FAM); }); |
| CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); }); |
| FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); }); |
| FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); |
| |
| ModulePassManager MPM(/*DebugLogging*/ true); |
| int ModulePassRunCount1 = 0; |
| MPM.addPass(TestModulePass(ModulePassRunCount1)); |
| |
| CGSCCPassManager CGPM1(/*DebugLogging*/ true); |
| int SCCPassRunCount1 = 0; |
| int AnalyzedInstrCount1 = 0; |
| int AnalyzedSCCFunctionCount1 = 0; |
| int AnalyzedModuleFunctionCount1 = 0; |
| CGPM1.addPass(TestSCCPass(SCCPassRunCount1, AnalyzedInstrCount1, |
| AnalyzedSCCFunctionCount1, |
| AnalyzedModuleFunctionCount1)); |
| |
| FunctionPassManager FPM1(/*DebugLogging*/ true); |
| int FunctionPassRunCount1 = 0; |
| FPM1.addPass(TestFunctionPass(FunctionPassRunCount1)); |
| CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); |
| MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); |
| |
| MPM.run(*M, MAM); |
| |
| EXPECT_EQ(1, ModulePassRunCount1); |
| |
| EXPECT_EQ(1, ModuleAnalysisRuns); |
| EXPECT_EQ(4, SCCAnalysisRuns); |
| EXPECT_EQ(6, FunctionAnalysisRuns); |
| EXPECT_EQ(6, ImmutableFunctionAnalysisRuns); |
| |
| EXPECT_EQ(4, SCCPassRunCount1); |
| EXPECT_EQ(14, AnalyzedInstrCount1); |
| EXPECT_EQ(6, AnalyzedSCCFunctionCount1); |
| EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1); |
| } |
| |
| } |