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