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 : public AnalysisInfoMixin<TestModuleAnalysis> { 26 public: 27 struct Result { 28 Result(int Count) : FunctionCount(Count) {} 29 int FunctionCount; 30 }; 31 32 TestModuleAnalysis(int &Runs) : Runs(Runs) {} 33 34 Result run(Module &M, ModuleAnalysisManager &AM) { 35 ++Runs; 36 return Result(M.size()); 37 } 38 39 private: 40 friend AnalysisInfoMixin<TestModuleAnalysis>; 41 static AnalysisKey Key; 42 43 int &Runs; 44 }; 45 46 AnalysisKey TestModuleAnalysis::Key; 47 48 class TestSCCAnalysis : public AnalysisInfoMixin<TestSCCAnalysis> { 49 public: 50 struct Result { 51 Result(int Count) : FunctionCount(Count) {} 52 int FunctionCount; 53 }; 54 55 TestSCCAnalysis(int &Runs) : Runs(Runs) {} 56 57 Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &) { 58 ++Runs; 59 return Result(C.size()); 60 } 61 62 private: 63 friend AnalysisInfoMixin<TestSCCAnalysis>; 64 static AnalysisKey Key; 65 66 int &Runs; 67 }; 68 69 AnalysisKey TestSCCAnalysis::Key; 70 71 class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> { 72 public: 73 struct Result { 74 Result(int Count) : InstructionCount(Count) {} 75 int InstructionCount; 76 }; 77 78 TestFunctionAnalysis(int &Runs) : Runs(Runs) {} 79 80 Result run(Function &F, FunctionAnalysisManager &AM) { 81 ++Runs; 82 int Count = 0; 83 for (Instruction &I : instructions(F)) { 84 (void)I; 85 ++Count; 86 } 87 return Result(Count); 88 } 89 90 private: 91 friend AnalysisInfoMixin<TestFunctionAnalysis>; 92 static AnalysisKey Key; 93 94 int &Runs; 95 }; 96 97 AnalysisKey TestFunctionAnalysis::Key; 98 99 class TestImmutableFunctionAnalysis 100 : public AnalysisInfoMixin<TestImmutableFunctionAnalysis> { 101 public: 102 struct Result { 103 bool invalidate(Function &, const PreservedAnalyses &, 104 FunctionAnalysisManager::Invalidator &) { 105 return false; 106 } 107 }; 108 109 TestImmutableFunctionAnalysis(int &Runs) : Runs(Runs) {} 110 111 Result run(Function &F, FunctionAnalysisManager &AM) { 112 ++Runs; 113 return Result(); 114 } 115 116 private: 117 friend AnalysisInfoMixin<TestImmutableFunctionAnalysis>; 118 static AnalysisKey Key; 119 120 int &Runs; 121 }; 122 123 AnalysisKey TestImmutableFunctionAnalysis::Key; 124 125 struct LambdaSCCPass : public PassInfoMixin<LambdaSCCPass> { 126 template <typename T> LambdaSCCPass(T &&Arg) : Func(std::forward<T>(Arg)) {} 127 // We have to explicitly define all the special member functions because MSVC 128 // refuses to generate them. 129 LambdaSCCPass(LambdaSCCPass &&Arg) : Func(std::move(Arg.Func)) {} 130 LambdaSCCPass &operator=(LambdaSCCPass &&RHS) { 131 Func = std::move(RHS.Func); 132 return *this; 133 } 134 135 PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 136 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 137 return Func(C, AM, CG, UR); 138 } 139 140 std::function<PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &, 141 LazyCallGraph &, CGSCCUpdateResult &)> 142 Func; 143 }; 144 145 struct LambdaFunctionPass : public PassInfoMixin<LambdaFunctionPass> { 146 template <typename T> LambdaFunctionPass(T &&Arg) : Func(std::forward<T>(Arg)) {} 147 // We have to explicitly define all the special member functions because MSVC 148 // refuses to generate them. 149 LambdaFunctionPass(LambdaFunctionPass &&Arg) : Func(std::move(Arg.Func)) {} 150 LambdaFunctionPass &operator=(LambdaFunctionPass &&RHS) { 151 Func = std::move(RHS.Func); 152 return *this; 153 } 154 155 PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { 156 return Func(F, AM); 157 } 158 159 std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)> Func; 160 }; 161 162 std::unique_ptr<Module> parseIR(const char *IR) { 163 // We just use a static context here. This is never called from multiple 164 // threads so it is harmless no matter how it is implemented. We just need 165 // the context to outlive the module which it does. 166 static LLVMContext C; 167 SMDiagnostic Err; 168 return parseAssemblyString(IR, Err, C); 169 } 170 171 class CGSCCPassManagerTest : public ::testing::Test { 172 protected: 173 LLVMContext Context; 174 FunctionAnalysisManager FAM; 175 CGSCCAnalysisManager CGAM; 176 ModuleAnalysisManager MAM; 177 178 std::unique_ptr<Module> M; 179 180 public: 181 CGSCCPassManagerTest() 182 : FAM(/*DebugLogging*/ true), CGAM(/*DebugLogging*/ true), 183 MAM(/*DebugLogging*/ true), 184 M(parseIR( 185 // Define a module with the following call graph, where calls go 186 // out the bottom of nodes and enter the top: 187 // 188 // f 189 // |\ _ 190 // | \ / | 191 // g h1 | 192 // | | | 193 // | h2 | 194 // | | | 195 // | h3 | 196 // | / \_/ 197 // |/ 198 // x 199 // 200 "define void @f() {\n" 201 "entry:\n" 202 " call void @g()\n" 203 " call void @h1()\n" 204 " ret void\n" 205 "}\n" 206 "define void @g() {\n" 207 "entry:\n" 208 " call void @g()\n" 209 " call void @x()\n" 210 " ret void\n" 211 "}\n" 212 "define void @h1() {\n" 213 "entry:\n" 214 " call void @h2()\n" 215 " ret void\n" 216 "}\n" 217 "define void @h2() {\n" 218 "entry:\n" 219 " call void @h3()\n" 220 " call void @x()\n" 221 " ret void\n" 222 "}\n" 223 "define void @h3() {\n" 224 "entry:\n" 225 " call void @h1()\n" 226 " ret void\n" 227 "}\n" 228 "define void @x() {\n" 229 "entry:\n" 230 " ret void\n" 231 "}\n")) { 232 MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); 233 MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); 234 MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); }); 235 CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(FAM); }); 236 CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); }); 237 FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); }); 238 FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); 239 } 240 }; 241 242 TEST_F(CGSCCPassManagerTest, Basic) { 243 int FunctionAnalysisRuns = 0; 244 FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); 245 int ImmutableFunctionAnalysisRuns = 0; 246 FAM.registerPass([&] { 247 return TestImmutableFunctionAnalysis(ImmutableFunctionAnalysisRuns); 248 }); 249 250 int SCCAnalysisRuns = 0; 251 CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); 252 253 int ModuleAnalysisRuns = 0; 254 MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); 255 256 ModulePassManager MPM(/*DebugLogging*/ true); 257 MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); 258 259 CGSCCPassManager CGPM1(/*DebugLogging*/ true); 260 int SCCPassRunCount1 = 0; 261 int AnalyzedInstrCount1 = 0; 262 int AnalyzedSCCFunctionCount1 = 0; 263 int AnalyzedModuleFunctionCount1 = 0; 264 CGPM1.addPass( 265 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 266 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 267 ++SCCPassRunCount1; 268 269 const ModuleAnalysisManager &MAM = 270 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 271 FunctionAnalysisManager &FAM = 272 AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); 273 if (TestModuleAnalysis::Result *TMA = 274 MAM.getCachedResult<TestModuleAnalysis>( 275 *C.begin()->getFunction().getParent())) 276 AnalyzedModuleFunctionCount1 += TMA->FunctionCount; 277 278 TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C, CG); 279 AnalyzedSCCFunctionCount1 += AR.FunctionCount; 280 for (LazyCallGraph::Node &N : C) { 281 TestFunctionAnalysis::Result &FAR = 282 FAM.getResult<TestFunctionAnalysis>(N.getFunction()); 283 AnalyzedInstrCount1 += FAR.InstructionCount; 284 285 // Just ensure we get the immutable results. 286 (void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction()); 287 } 288 289 return PreservedAnalyses::all(); 290 })); 291 292 FunctionPassManager FPM1(/*DebugLogging*/ true); 293 int FunctionPassRunCount1 = 0; 294 FPM1.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { 295 ++FunctionPassRunCount1; 296 return PreservedAnalyses::all(); 297 })); 298 CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); 299 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); 300 301 MPM.run(*M, MAM); 302 303 EXPECT_EQ(1, ModuleAnalysisRuns); 304 EXPECT_EQ(4, SCCAnalysisRuns); 305 EXPECT_EQ(6, FunctionAnalysisRuns); 306 EXPECT_EQ(6, ImmutableFunctionAnalysisRuns); 307 308 EXPECT_EQ(4, SCCPassRunCount1); 309 EXPECT_EQ(14, AnalyzedInstrCount1); 310 EXPECT_EQ(6, AnalyzedSCCFunctionCount1); 311 EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1); 312 } 313 314 // Test that an SCC pass which fails to preserve a module analysis does in fact 315 // invalidate that module analysis. 316 TEST_F(CGSCCPassManagerTest, TestSCCPassInvalidatesModuleAnalysis) { 317 int ModuleAnalysisRuns = 0; 318 MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); 319 320 ModulePassManager MPM(/*DebugLogging*/ true); 321 MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); 322 323 // The first CGSCC run we preserve everything and make sure that works and 324 // the module analysis is available in the second CGSCC run from the one 325 // required module pass above. 326 CGSCCPassManager CGPM1(/*DebugLogging*/ true); 327 int CountFoundModuleAnalysis1 = 0; 328 CGPM1.addPass( 329 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 330 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 331 const auto &MAM = 332 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 333 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( 334 *C.begin()->getFunction().getParent()); 335 336 if (TMA) 337 ++CountFoundModuleAnalysis1; 338 339 return PreservedAnalyses::all(); 340 })); 341 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); 342 343 // The second CGSCC run checks that the module analysis got preserved the 344 // previous time and in one SCC fails to preserve it. 345 CGSCCPassManager CGPM2(/*DebugLogging*/ true); 346 int CountFoundModuleAnalysis2 = 0; 347 CGPM2.addPass( 348 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 349 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 350 const auto &MAM = 351 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 352 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( 353 *C.begin()->getFunction().getParent()); 354 355 if (TMA) 356 ++CountFoundModuleAnalysis2; 357 358 // Only fail to preserve analyses on one SCC and make sure that gets 359 // propagated. 360 return C.getName() == "(g)" ? PreservedAnalyses::none() 361 : PreservedAnalyses::all(); 362 })); 363 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); 364 365 // The third CGSCC run should fail to find a cached module analysis as it 366 // should have been invalidated by the above CGSCC run. 367 CGSCCPassManager CGPM3(/*DebugLogging*/ true); 368 int CountFoundModuleAnalysis3 = 0; 369 CGPM3.addPass( 370 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 371 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 372 const auto &MAM = 373 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 374 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( 375 *C.begin()->getFunction().getParent()); 376 377 if (TMA) 378 ++CountFoundModuleAnalysis3; 379 380 return PreservedAnalyses::none(); 381 })); 382 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); 383 384 MPM.run(*M, MAM); 385 386 EXPECT_EQ(1, ModuleAnalysisRuns); 387 EXPECT_EQ(4, CountFoundModuleAnalysis1); 388 EXPECT_EQ(4, CountFoundModuleAnalysis2); 389 EXPECT_EQ(0, CountFoundModuleAnalysis3); 390 } 391 392 // Similar to the above, but test that this works for function passes embedded 393 // *within* a CGSCC layer. 394 TEST_F(CGSCCPassManagerTest, TestFunctionPassInsideCGSCCInvalidatesModuleAnalysis) { 395 int ModuleAnalysisRuns = 0; 396 MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); 397 398 ModulePassManager MPM(/*DebugLogging*/ true); 399 MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); 400 401 // The first run we preserve everything and make sure that works and the 402 // module analysis is available in the second run from the one required 403 // module pass above. 404 FunctionPassManager FPM1(/*DebugLogging*/ true); 405 // Start true and mark false if we ever failed to find a module analysis 406 // because we expect this to succeed for each SCC. 407 bool FoundModuleAnalysis1 = true; 408 FPM1.addPass( 409 LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { 410 const auto &MAM = 411 AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); 412 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); 413 414 if (!TMA) 415 FoundModuleAnalysis1 = false; 416 417 return PreservedAnalyses::all(); 418 })); 419 CGSCCPassManager CGPM1(/*DebugLogging*/ true); 420 CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); 421 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); 422 423 // The second run checks that the module analysis got preserved the previous 424 // time and in one function fails to preserve it. 425 FunctionPassManager FPM2(/*DebugLogging*/ true); 426 // Again, start true and mark false if we ever failed to find a module analysis 427 // because we expect this to succeed for each SCC. 428 bool FoundModuleAnalysis2 = true; 429 FPM2.addPass( 430 LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { 431 const auto &MAM = 432 AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); 433 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); 434 435 if (!TMA) 436 FoundModuleAnalysis2 = false; 437 438 // Only fail to preserve analyses on one SCC and make sure that gets 439 // propagated. 440 return F.getName() == "h2" ? PreservedAnalyses::none() 441 : PreservedAnalyses::all(); 442 })); 443 CGSCCPassManager CGPM2(/*DebugLogging*/ true); 444 CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); 445 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); 446 447 // The third run should fail to find a cached module analysis as it should 448 // have been invalidated by the above run. 449 FunctionPassManager FPM3(/*DebugLogging*/ true); 450 // Start false and mark true if we ever *succeeded* to find a module 451 // analysis, as we expect this to fail for every function. 452 bool FoundModuleAnalysis3 = false; 453 FPM3.addPass( 454 LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { 455 const auto &MAM = 456 AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); 457 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); 458 459 if (TMA) 460 FoundModuleAnalysis3 = true; 461 462 return PreservedAnalyses::none(); 463 })); 464 CGSCCPassManager CGPM3(/*DebugLogging*/ true); 465 CGPM3.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM3))); 466 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); 467 468 MPM.run(*M, MAM); 469 470 EXPECT_EQ(1, ModuleAnalysisRuns); 471 EXPECT_TRUE(FoundModuleAnalysis1); 472 EXPECT_TRUE(FoundModuleAnalysis2); 473 EXPECT_FALSE(FoundModuleAnalysis3); 474 } 475 476 } 477