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