1 //===- CGSCCPassManagerTest.cpp -------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "llvm/Analysis/CGSCCPassManager.h" 11 #include "llvm/Analysis/LazyCallGraph.h" 12 #include "llvm/AsmParser/Parser.h" 13 #include "llvm/IR/Function.h" 14 #include "llvm/IR/InstIterator.h" 15 #include "llvm/IR/LLVMContext.h" 16 #include "llvm/IR/Module.h" 17 #include "llvm/IR/PassManager.h" 18 #include "llvm/Support/SourceMgr.h" 19 #include "gtest/gtest.h" 20 21 using namespace llvm; 22 23 namespace { 24 25 class TestModuleAnalysis { 26 public: 27 struct Result { 28 Result(int Count) : FunctionCount(Count) {} 29 int FunctionCount; 30 }; 31 32 static void *ID() { return (void *)&PassID; } 33 static StringRef name() { return "TestModuleAnalysis"; } 34 35 TestModuleAnalysis(int &Runs) : Runs(Runs) {} 36 37 Result run(Module &M, ModuleAnalysisManager &AM) { 38 ++Runs; 39 return Result(M.size()); 40 } 41 42 private: 43 static char PassID; 44 45 int &Runs; 46 }; 47 48 char TestModuleAnalysis::PassID; 49 50 class TestSCCAnalysis { 51 public: 52 struct Result { 53 Result(int Count) : FunctionCount(Count) {} 54 int FunctionCount; 55 }; 56 57 static void *ID() { return (void *)&PassID; } 58 static StringRef name() { return "TestSCCAnalysis"; } 59 60 TestSCCAnalysis(int &Runs) : Runs(Runs) {} 61 62 Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &) { 63 ++Runs; 64 return Result(C.size()); 65 } 66 67 private: 68 static char PassID; 69 70 int &Runs; 71 }; 72 73 char TestSCCAnalysis::PassID; 74 75 class TestFunctionAnalysis { 76 public: 77 struct Result { 78 Result(int Count) : InstructionCount(Count) {} 79 int InstructionCount; 80 }; 81 82 static void *ID() { return (void *)&PassID; } 83 static StringRef name() { return "TestFunctionAnalysis"; } 84 85 TestFunctionAnalysis(int &Runs) : Runs(Runs) {} 86 87 Result run(Function &F, FunctionAnalysisManager &AM) { 88 ++Runs; 89 int Count = 0; 90 for (Instruction &I : instructions(F)) { 91 (void)I; 92 ++Count; 93 } 94 return Result(Count); 95 } 96 97 private: 98 static char PassID; 99 100 int &Runs; 101 }; 102 103 char TestFunctionAnalysis::PassID; 104 105 class TestImmutableFunctionAnalysis { 106 public: 107 struct Result { 108 bool invalidate(Function &, const PreservedAnalyses &) { return false; } 109 }; 110 111 static void *ID() { return (void *)&PassID; } 112 static StringRef name() { return "TestImmutableFunctionAnalysis"; } 113 114 TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {} 115 116 Result run(Function &F, FunctionAnalysisManager &AM) { 117 ++Runs; 118 return Result(); 119 } 120 121 private: 122 static char PassID; 123 124 int &Runs; 125 }; 126 127 char TestImmutableFunctionAnalysis::PassID; 128 129 struct LambdaSCCPass : public PassInfoMixin<LambdaSCCPass> { 130 template <typename T> LambdaSCCPass(T &&Arg) : Func(std::forward<T>(Arg)) {} 131 // We have to explicitly define all the special member functions because MSVC 132 // refuses to generate them. 133 LambdaSCCPass(LambdaSCCPass &&Arg) : Func(std::move(Arg.Func)) {} 134 LambdaSCCPass &operator=(LambdaSCCPass &&RHS) { 135 Func = std::move(RHS.Func); 136 return *this; 137 } 138 139 PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 140 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 141 return Func(C, AM, CG, UR); 142 } 143 144 std::function<PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &, 145 LazyCallGraph &, CGSCCUpdateResult &)> 146 Func; 147 }; 148 149 struct LambdaFunctionPass : public PassInfoMixin<LambdaFunctionPass> { 150 template <typename T> LambdaFunctionPass(T &&Arg) : Func(std::forward<T>(Arg)) {} 151 // We have to explicitly define all the special member functions because MSVC 152 // refuses to generate them. 153 LambdaFunctionPass(LambdaFunctionPass &&Arg) : Func(std::move(Arg.Func)) {} 154 LambdaFunctionPass &operator=(LambdaFunctionPass &&RHS) { 155 Func = std::move(RHS.Func); 156 return *this; 157 } 158 159 PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { 160 return Func(F, AM); 161 } 162 163 std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)> Func; 164 }; 165 166 std::unique_ptr<Module> parseIR(const char *IR) { 167 // We just use a static context here. This is never called from multiple 168 // threads so it is harmless no matter how it is implemented. We just need 169 // the context to outlive the module which it does. 170 static LLVMContext C; 171 SMDiagnostic Err; 172 return parseAssemblyString(IR, Err, C); 173 } 174 175 class CGSCCPassManagerTest : public ::testing::Test { 176 protected: 177 LLVMContext Context; 178 std::unique_ptr<Module> M; 179 180 public: 181 CGSCCPassManagerTest() 182 : M(parseIR("define void @f() {\n" 183 "entry:\n" 184 " call void @g()\n" 185 " call void @h1()\n" 186 " ret void\n" 187 "}\n" 188 "define void @g() {\n" 189 "entry:\n" 190 " call void @g()\n" 191 " call void @x()\n" 192 " ret void\n" 193 "}\n" 194 "define void @h1() {\n" 195 "entry:\n" 196 " call void @h2()\n" 197 " ret void\n" 198 "}\n" 199 "define void @h2() {\n" 200 "entry:\n" 201 " call void @h3()\n" 202 " call void @x()\n" 203 " ret void\n" 204 "}\n" 205 "define void @h3() {\n" 206 "entry:\n" 207 " call void @h1()\n" 208 " ret void\n" 209 "}\n" 210 "define void @x() {\n" 211 "entry:\n" 212 " ret void\n" 213 "}\n")) {} 214 }; 215 216 TEST_F(CGSCCPassManagerTest, Basic) { 217 FunctionAnalysisManager FAM(/*DebugLogging*/ true); 218 int FunctionAnalysisRuns = 0; 219 FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); 220 int ImmutableFunctionAnalysisRuns = 0; 221 FAM.registerPass([&] { 222 return TestImmutableFunctionAnalysis(ImmutableFunctionAnalysisRuns); 223 }); 224 225 CGSCCAnalysisManager CGAM(/*DebugLogging*/ true); 226 int SCCAnalysisRuns = 0; 227 CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); 228 229 ModuleAnalysisManager MAM(/*DebugLogging*/ true); 230 int ModuleAnalysisRuns = 0; 231 MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); 232 MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); 233 234 MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); 235 MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); }); 236 CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(FAM); }); 237 CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); }); 238 FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); }); 239 FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); 240 241 ModulePassManager MPM(/*DebugLogging*/ true); 242 MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); 243 244 CGSCCPassManager CGPM1(/*DebugLogging*/ true); 245 int SCCPassRunCount1 = 0; 246 int AnalyzedInstrCount1 = 0; 247 int AnalyzedSCCFunctionCount1 = 0; 248 int AnalyzedModuleFunctionCount1 = 0; 249 CGPM1.addPass( 250 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 251 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 252 ++SCCPassRunCount1; 253 254 const ModuleAnalysisManager &MAM = 255 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 256 FunctionAnalysisManager &FAM = 257 AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); 258 if (TestModuleAnalysis::Result *TMA = 259 MAM.getCachedResult<TestModuleAnalysis>( 260 *C.begin()->getFunction().getParent())) 261 AnalyzedModuleFunctionCount1 += TMA->FunctionCount; 262 263 TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C, CG); 264 AnalyzedSCCFunctionCount1 += AR.FunctionCount; 265 for (LazyCallGraph::Node &N : C) { 266 TestFunctionAnalysis::Result &FAR = 267 FAM.getResult<TestFunctionAnalysis>(N.getFunction()); 268 AnalyzedInstrCount1 += FAR.InstructionCount; 269 270 // Just ensure we get the immutable results. 271 (void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction()); 272 } 273 274 return PreservedAnalyses::all(); 275 })); 276 277 FunctionPassManager FPM1(/*DebugLogging*/ true); 278 int FunctionPassRunCount1 = 0; 279 FPM1.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { 280 ++FunctionPassRunCount1; 281 return PreservedAnalyses::all(); 282 })); 283 CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); 284 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); 285 286 MPM.run(*M, MAM); 287 288 EXPECT_EQ(1, ModuleAnalysisRuns); 289 EXPECT_EQ(4, SCCAnalysisRuns); 290 EXPECT_EQ(6, FunctionAnalysisRuns); 291 EXPECT_EQ(6, ImmutableFunctionAnalysisRuns); 292 293 EXPECT_EQ(4, SCCPassRunCount1); 294 EXPECT_EQ(14, AnalyzedInstrCount1); 295 EXPECT_EQ(6, AnalyzedSCCFunctionCount1); 296 EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1); 297 } 298 299 } 300