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