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