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 FunctionAnalysisManager FAM; 179 CGSCCAnalysisManager CGAM; 180 ModuleAnalysisManager MAM; 181 182 std::unique_ptr<Module> M; 183 184 public: 185 CGSCCPassManagerTest() 186 : FAM(/*DebugLogging*/ true), CGAM(/*DebugLogging*/ true), 187 MAM(/*DebugLogging*/ true), M(parseIR("define void @f() {\n" 188 "entry:\n" 189 " call void @g()\n" 190 " call void @h1()\n" 191 " ret void\n" 192 "}\n" 193 "define void @g() {\n" 194 "entry:\n" 195 " call void @g()\n" 196 " call void @x()\n" 197 " ret void\n" 198 "}\n" 199 "define void @h1() {\n" 200 "entry:\n" 201 " call void @h2()\n" 202 " ret void\n" 203 "}\n" 204 "define void @h2() {\n" 205 "entry:\n" 206 " call void @h3()\n" 207 " call void @x()\n" 208 " ret void\n" 209 "}\n" 210 "define void @h3() {\n" 211 "entry:\n" 212 " call void @h1()\n" 213 " ret void\n" 214 "}\n" 215 "define void @x() {\n" 216 "entry:\n" 217 " ret void\n" 218 "}\n")) { 219 MAM.registerPass([&] { return LazyCallGraphAnalysis(); }); 220 MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); 221 MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); }); 222 CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(FAM); }); 223 CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); }); 224 FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); }); 225 FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); 226 } 227 }; 228 229 TEST_F(CGSCCPassManagerTest, Basic) { 230 int FunctionAnalysisRuns = 0; 231 FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); 232 int ImmutableFunctionAnalysisRuns = 0; 233 FAM.registerPass([&] { 234 return TestImmutableFunctionAnalysis(ImmutableFunctionAnalysisRuns); 235 }); 236 237 int SCCAnalysisRuns = 0; 238 CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); }); 239 240 int ModuleAnalysisRuns = 0; 241 MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); 242 243 ModulePassManager MPM(/*DebugLogging*/ true); 244 MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); 245 246 CGSCCPassManager CGPM1(/*DebugLogging*/ true); 247 int SCCPassRunCount1 = 0; 248 int AnalyzedInstrCount1 = 0; 249 int AnalyzedSCCFunctionCount1 = 0; 250 int AnalyzedModuleFunctionCount1 = 0; 251 CGPM1.addPass( 252 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 253 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 254 ++SCCPassRunCount1; 255 256 const ModuleAnalysisManager &MAM = 257 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 258 FunctionAnalysisManager &FAM = 259 AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); 260 if (TestModuleAnalysis::Result *TMA = 261 MAM.getCachedResult<TestModuleAnalysis>( 262 *C.begin()->getFunction().getParent())) 263 AnalyzedModuleFunctionCount1 += TMA->FunctionCount; 264 265 TestSCCAnalysis::Result &AR = AM.getResult<TestSCCAnalysis>(C, CG); 266 AnalyzedSCCFunctionCount1 += AR.FunctionCount; 267 for (LazyCallGraph::Node &N : C) { 268 TestFunctionAnalysis::Result &FAR = 269 FAM.getResult<TestFunctionAnalysis>(N.getFunction()); 270 AnalyzedInstrCount1 += FAR.InstructionCount; 271 272 // Just ensure we get the immutable results. 273 (void)FAM.getResult<TestImmutableFunctionAnalysis>(N.getFunction()); 274 } 275 276 return PreservedAnalyses::all(); 277 })); 278 279 FunctionPassManager FPM1(/*DebugLogging*/ true); 280 int FunctionPassRunCount1 = 0; 281 FPM1.addPass(LambdaFunctionPass([&](Function &, FunctionAnalysisManager &) { 282 ++FunctionPassRunCount1; 283 return PreservedAnalyses::all(); 284 })); 285 CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); 286 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); 287 288 MPM.run(*M, MAM); 289 290 EXPECT_EQ(1, ModuleAnalysisRuns); 291 EXPECT_EQ(4, SCCAnalysisRuns); 292 EXPECT_EQ(6, FunctionAnalysisRuns); 293 EXPECT_EQ(6, ImmutableFunctionAnalysisRuns); 294 295 EXPECT_EQ(4, SCCPassRunCount1); 296 EXPECT_EQ(14, AnalyzedInstrCount1); 297 EXPECT_EQ(6, AnalyzedSCCFunctionCount1); 298 EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1); 299 } 300 301 // Test that an SCC pass which fails to preserve a module analysis does in fact 302 // invalidate that module analysis. 303 TEST_F(CGSCCPassManagerTest, TestSCCPassInvalidatesModuleAnalysis) { 304 int ModuleAnalysisRuns = 0; 305 MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); 306 307 ModulePassManager MPM(/*DebugLogging*/ true); 308 MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); 309 310 // The first CGSCC run we preserve everything and make sure that works and 311 // the module analysis is available in the second CGSCC run from the one 312 // required module pass above. 313 CGSCCPassManager CGPM1(/*DebugLogging*/ true); 314 int CountFoundModuleAnalysis1 = 0; 315 CGPM1.addPass( 316 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 317 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 318 const auto &MAM = 319 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 320 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( 321 *C.begin()->getFunction().getParent()); 322 323 if (TMA) 324 ++CountFoundModuleAnalysis1; 325 326 return PreservedAnalyses::all(); 327 })); 328 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); 329 330 // The second CGSCC run checks that the module analysis got preserved the 331 // previous time and in one SCC fails to preserve it. 332 CGSCCPassManager CGPM2(/*DebugLogging*/ true); 333 int CountFoundModuleAnalysis2 = 0; 334 CGPM2.addPass( 335 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 336 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 337 const auto &MAM = 338 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 339 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( 340 *C.begin()->getFunction().getParent()); 341 342 if (TMA) 343 ++CountFoundModuleAnalysis2; 344 345 // Only fail to preserve analyses on one SCC and make sure that gets 346 // propagated. 347 return C.getName() == "(g)" ? PreservedAnalyses::none() 348 : PreservedAnalyses::all(); 349 })); 350 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); 351 352 // The third CGSCC run should fail to find a cached module analysis as it 353 // should have been invalidated by the above CGSCC run. 354 CGSCCPassManager CGPM3(/*DebugLogging*/ true); 355 int CountFoundModuleAnalysis3 = 0; 356 CGPM3.addPass( 357 LambdaSCCPass([&](LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, 358 LazyCallGraph &CG, CGSCCUpdateResult &UR) { 359 const auto &MAM = 360 AM.getResult<ModuleAnalysisManagerCGSCCProxy>(C, CG).getManager(); 361 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>( 362 *C.begin()->getFunction().getParent()); 363 364 if (TMA) 365 ++CountFoundModuleAnalysis3; 366 367 return PreservedAnalyses::none(); 368 })); 369 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); 370 371 MPM.run(*M, MAM); 372 373 EXPECT_EQ(1, ModuleAnalysisRuns); 374 EXPECT_EQ(4, CountFoundModuleAnalysis1); 375 EXPECT_EQ(4, CountFoundModuleAnalysis2); 376 EXPECT_EQ(0, CountFoundModuleAnalysis3); 377 } 378 379 // Similar to the above, but test that this works for function passes embedded 380 // *within* a CGSCC layer. 381 TEST_F(CGSCCPassManagerTest, TestFunctionPassInsideCGSCCInvalidatesModuleAnalysis) { 382 int ModuleAnalysisRuns = 0; 383 MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); 384 385 ModulePassManager MPM(/*DebugLogging*/ true); 386 MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>()); 387 388 // The first run we preserve everything and make sure that works and the 389 // module analysis is available in the second run from the one required 390 // module pass above. 391 FunctionPassManager FPM1(/*DebugLogging*/ true); 392 // Start true and mark false if we ever failed to find a module analysis 393 // because we expect this to succeed for each SCC. 394 bool FoundModuleAnalysis1 = true; 395 FPM1.addPass( 396 LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { 397 const auto &MAM = 398 AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); 399 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); 400 401 if (!TMA) 402 FoundModuleAnalysis1 = false; 403 404 return PreservedAnalyses::all(); 405 })); 406 CGSCCPassManager CGPM1(/*DebugLogging*/ true); 407 CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1))); 408 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1))); 409 410 // The second run checks that the module analysis got preserved the previous 411 // time and in one function fails to preserve it. 412 FunctionPassManager FPM2(/*DebugLogging*/ true); 413 // Again, start true and mark false if we ever failed to find a module analysis 414 // because we expect this to succeed for each SCC. 415 bool FoundModuleAnalysis2 = true; 416 FPM2.addPass( 417 LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { 418 const auto &MAM = 419 AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); 420 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); 421 422 if (!TMA) 423 FoundModuleAnalysis2 = false; 424 425 // Only fail to preserve analyses on one SCC and make sure that gets 426 // propagated. 427 return F.getName() == "h2" ? PreservedAnalyses::none() 428 : PreservedAnalyses::all(); 429 })); 430 CGSCCPassManager CGPM2(/*DebugLogging*/ true); 431 CGPM2.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM2))); 432 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM2))); 433 434 // The third run should fail to find a cached module analysis as it should 435 // have been invalidated by the above run. 436 FunctionPassManager FPM3(/*DebugLogging*/ true); 437 // Start false and mark true if we ever *succeeded* to find a module 438 // analysis, as we expect this to fail for every function. 439 bool FoundModuleAnalysis3 = false; 440 FPM3.addPass( 441 LambdaFunctionPass([&](Function &F, FunctionAnalysisManager &AM) { 442 const auto &MAM = 443 AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager(); 444 auto *TMA = MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()); 445 446 if (TMA) 447 FoundModuleAnalysis3 = true; 448 449 return PreservedAnalyses::none(); 450 })); 451 CGSCCPassManager CGPM3(/*DebugLogging*/ true); 452 CGPM3.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM3))); 453 MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM3))); 454 455 MPM.run(*M, MAM); 456 457 EXPECT_EQ(1, ModuleAnalysisRuns); 458 EXPECT_TRUE(FoundModuleAnalysis1); 459 EXPECT_TRUE(FoundModuleAnalysis2); 460 EXPECT_FALSE(FoundModuleAnalysis3); 461 } 462 463 } 464