1 | //===- llvm/unittest/IR/PassManager.cpp - PassManager tests ---------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/IR/PassManager.h" |
10 | #include "llvm/Analysis/AssumptionCache.h" |
11 | #include "llvm/Analysis/TargetTransformInfo.h" |
12 | #include "llvm/AsmParser/Parser.h" |
13 | #include "llvm/IR/Dominators.h" |
14 | #include "llvm/IR/Function.h" |
15 | #include "llvm/IR/LLVMContext.h" |
16 | #include "llvm/IR/Module.h" |
17 | #include "llvm/IR/PassManagerImpl.h" |
18 | #include "llvm/Passes/StandardInstrumentations.h" |
19 | #include "llvm/Support/SourceMgr.h" |
20 | #include "llvm/Transforms/Scalar/SimplifyCFG.h" |
21 | #include "gtest/gtest.h" |
22 | |
23 | using namespace llvm; |
24 | |
25 | namespace { |
26 | |
27 | class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> { |
28 | public: |
29 | struct Result { |
30 | Result(int Count) : InstructionCount(Count) {} |
31 | int InstructionCount; |
32 | bool invalidate(Function &, const PreservedAnalyses &PA, |
33 | FunctionAnalysisManager::Invalidator &) { |
34 | // Check whether the analysis or all analyses on functions have been |
35 | // preserved. |
36 | auto PAC = PA.getChecker<TestFunctionAnalysis>(); |
37 | return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()); |
38 | } |
39 | }; |
40 | |
41 | TestFunctionAnalysis(int &Runs) : Runs(Runs) {} |
42 | |
43 | /// Run the analysis pass over the function and return a result. |
44 | Result run(Function &F, FunctionAnalysisManager &AM) { |
45 | ++Runs; |
46 | int Count = 0; |
47 | for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) |
48 | for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE; |
49 | ++II) |
50 | ++Count; |
51 | return Result(Count); |
52 | } |
53 | |
54 | private: |
55 | friend AnalysisInfoMixin<TestFunctionAnalysis>; |
56 | static AnalysisKey Key; |
57 | |
58 | int &Runs; |
59 | }; |
60 | |
61 | AnalysisKey TestFunctionAnalysis::Key; |
62 | |
63 | class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> { |
64 | public: |
65 | struct Result { |
66 | Result(int Count) : FunctionCount(Count) {} |
67 | int FunctionCount; |
68 | bool invalidate(Module &, const PreservedAnalyses &PA, |
69 | ModuleAnalysisManager::Invalidator &) { |
70 | // Check whether the analysis or all analyses on modules have been |
71 | // preserved. |
72 | auto PAC = PA.getChecker<TestModuleAnalysis>(); |
73 | return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Module>>()); |
74 | } |
75 | }; |
76 | |
77 | TestModuleAnalysis(int &Runs) : Runs(Runs) {} |
78 | |
79 | Result run(Module &M, ModuleAnalysisManager &AM) { |
80 | ++Runs; |
81 | int Count = 0; |
82 | for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) |
83 | ++Count; |
84 | return Result(Count); |
85 | } |
86 | |
87 | private: |
88 | friend AnalysisInfoMixin<TestModuleAnalysis>; |
89 | static AnalysisKey Key; |
90 | |
91 | int &Runs; |
92 | }; |
93 | |
94 | AnalysisKey TestModuleAnalysis::Key; |
95 | |
96 | struct TestModulePass : PassInfoMixin<TestModulePass> { |
97 | TestModulePass(int &RunCount) : RunCount(RunCount) {} |
98 | |
99 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &) { |
100 | ++RunCount; |
101 | return PreservedAnalyses::none(); |
102 | } |
103 | |
104 | int &RunCount; |
105 | }; |
106 | |
107 | struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> { |
108 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &) { |
109 | return PreservedAnalyses::all(); |
110 | } |
111 | }; |
112 | |
113 | struct TestFunctionPass : PassInfoMixin<TestFunctionPass> { |
114 | TestFunctionPass(int &RunCount, int &AnalyzedInstrCount, |
115 | int &AnalyzedFunctionCount, ModuleAnalysisManager &MAM, |
116 | bool OnlyUseCachedResults = false) |
117 | : RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount), |
118 | AnalyzedFunctionCount(AnalyzedFunctionCount), MAM(MAM), |
119 | OnlyUseCachedResults(OnlyUseCachedResults) {} |
120 | |
121 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { |
122 | ++RunCount; |
123 | |
124 | // Getting a cached result that isn't stateless through the proxy will |
125 | // trigger an assert: |
126 | // auto &ModuleProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F); |
127 | // Use MAM, for the purposes of this unittest. |
128 | if (TestModuleAnalysis::Result *TMA = |
129 | MAM.getCachedResult<TestModuleAnalysis>(IR&: *F.getParent())) { |
130 | AnalyzedFunctionCount += TMA->FunctionCount; |
131 | } |
132 | |
133 | if (OnlyUseCachedResults) { |
134 | // Hack to force the use of the cached interface. |
135 | if (TestFunctionAnalysis::Result *AR = |
136 | AM.getCachedResult<TestFunctionAnalysis>(IR&: F)) |
137 | AnalyzedInstrCount += AR->InstructionCount; |
138 | } else { |
139 | // Typical path just runs the analysis as needed. |
140 | TestFunctionAnalysis::Result &AR = AM.getResult<TestFunctionAnalysis>(IR&: F); |
141 | AnalyzedInstrCount += AR.InstructionCount; |
142 | } |
143 | |
144 | return PreservedAnalyses::all(); |
145 | } |
146 | |
147 | int &RunCount; |
148 | int &AnalyzedInstrCount; |
149 | int &AnalyzedFunctionCount; |
150 | ModuleAnalysisManager &MAM; |
151 | bool OnlyUseCachedResults; |
152 | }; |
153 | |
154 | // A test function pass that invalidates all function analyses for a function |
155 | // with a specific name. |
156 | struct TestInvalidationFunctionPass |
157 | : PassInfoMixin<TestInvalidationFunctionPass> { |
158 | TestInvalidationFunctionPass(StringRef FunctionName) : Name(FunctionName) {} |
159 | |
160 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { |
161 | return F.getName() == Name ? PreservedAnalyses::none() |
162 | : PreservedAnalyses::all(); |
163 | } |
164 | |
165 | StringRef Name; |
166 | }; |
167 | |
168 | std::unique_ptr<Module> parseIR(LLVMContext &Context, const char *IR) { |
169 | SMDiagnostic Err; |
170 | return parseAssemblyString(AsmString: IR, Err, Context); |
171 | } |
172 | |
173 | class PassManagerTest : public ::testing::Test { |
174 | protected: |
175 | LLVMContext Context; |
176 | std::unique_ptr<Module> M; |
177 | |
178 | public: |
179 | PassManagerTest() |
180 | : M(parseIR(Context, IR: "define void @f() {\n" |
181 | "entry:\n" |
182 | " call void @g()\n" |
183 | " call void @h()\n" |
184 | " ret void\n" |
185 | "}\n" |
186 | "define void @g() {\n" |
187 | " ret void\n" |
188 | "}\n" |
189 | "define void @h() {\n" |
190 | " ret void\n" |
191 | "}\n" )) {} |
192 | }; |
193 | |
194 | TEST(PreservedAnalysesTest, Basic) { |
195 | PreservedAnalyses PA1 = PreservedAnalyses(); |
196 | { |
197 | auto PAC = PA1.getChecker<TestFunctionAnalysis>(); |
198 | EXPECT_FALSE(PAC.preserved()); |
199 | EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); |
200 | } |
201 | { |
202 | auto PAC = PA1.getChecker<TestModuleAnalysis>(); |
203 | EXPECT_FALSE(PAC.preserved()); |
204 | EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>()); |
205 | } |
206 | auto PA2 = PreservedAnalyses::none(); |
207 | { |
208 | auto PAC = PA2.getChecker<TestFunctionAnalysis>(); |
209 | EXPECT_FALSE(PAC.preserved()); |
210 | EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); |
211 | } |
212 | auto PA3 = PreservedAnalyses::all(); |
213 | { |
214 | auto PAC = PA3.getChecker<TestFunctionAnalysis>(); |
215 | EXPECT_TRUE(PAC.preserved()); |
216 | EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>()); |
217 | } |
218 | PreservedAnalyses PA4 = PA1; |
219 | { |
220 | auto PAC = PA4.getChecker<TestFunctionAnalysis>(); |
221 | EXPECT_FALSE(PAC.preserved()); |
222 | EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); |
223 | } |
224 | PA4 = PA3; |
225 | { |
226 | auto PAC = PA4.getChecker<TestFunctionAnalysis>(); |
227 | EXPECT_TRUE(PAC.preserved()); |
228 | EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>()); |
229 | } |
230 | PA4 = std::move(PA2); |
231 | { |
232 | auto PAC = PA4.getChecker<TestFunctionAnalysis>(); |
233 | EXPECT_FALSE(PAC.preserved()); |
234 | EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>()); |
235 | } |
236 | auto PA5 = PreservedAnalyses::allInSet<AllAnalysesOn<Function>>(); |
237 | { |
238 | auto PAC = PA5.getChecker<TestFunctionAnalysis>(); |
239 | EXPECT_FALSE(PAC.preserved()); |
240 | EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>()); |
241 | EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>()); |
242 | } |
243 | } |
244 | |
245 | TEST(PreservedAnalysesTest, Preserve) { |
246 | auto PA = PreservedAnalyses::none(); |
247 | PA.preserve<TestFunctionAnalysis>(); |
248 | EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); |
249 | EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved()); |
250 | PA.preserve<TestModuleAnalysis>(); |
251 | EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); |
252 | EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved()); |
253 | |
254 | // Redundant calls are fine. |
255 | PA.preserve<TestFunctionAnalysis>(); |
256 | EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved()); |
257 | EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved()); |
258 | } |
259 | |
260 | TEST(PreservedAnalysesTest, PreserveSets) { |
261 | auto PA = PreservedAnalyses::none(); |
262 | PA.preserveSet<AllAnalysesOn<Function>>(); |
263 | EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() |
264 | .preservedSet<AllAnalysesOn<Function>>()); |
265 | EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>() |
266 | .preservedSet<AllAnalysesOn<Module>>()); |
267 | PA.preserveSet<AllAnalysesOn<Module>>(); |
268 | EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() |
269 | .preservedSet<AllAnalysesOn<Function>>()); |
270 | EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() |
271 | .preservedSet<AllAnalysesOn<Module>>()); |
272 | |
273 | // Mixing is fine. |
274 | PA.preserve<TestFunctionAnalysis>(); |
275 | EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() |
276 | .preservedSet<AllAnalysesOn<Function>>()); |
277 | EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() |
278 | .preservedSet<AllAnalysesOn<Module>>()); |
279 | |
280 | // Redundant calls are fine. |
281 | PA.preserveSet<AllAnalysesOn<Module>>(); |
282 | EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>() |
283 | .preservedSet<AllAnalysesOn<Function>>()); |
284 | EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>() |
285 | .preservedSet<AllAnalysesOn<Module>>()); |
286 | } |
287 | |
288 | TEST(PreservedAnalysisTest, Intersect) { |
289 | // Setup the initial sets. |
290 | auto PA1 = PreservedAnalyses::none(); |
291 | PA1.preserve<TestFunctionAnalysis>(); |
292 | PA1.preserveSet<AllAnalysesOn<Module>>(); |
293 | auto PA2 = PreservedAnalyses::none(); |
294 | PA2.preserve<TestFunctionAnalysis>(); |
295 | PA2.preserveSet<AllAnalysesOn<Function>>(); |
296 | PA2.preserve<TestModuleAnalysis>(); |
297 | PA2.preserveSet<AllAnalysesOn<Module>>(); |
298 | auto PA3 = PreservedAnalyses::none(); |
299 | PA3.preserve<TestModuleAnalysis>(); |
300 | PA3.preserveSet<AllAnalysesOn<Function>>(); |
301 | |
302 | // Self intersection is a no-op. |
303 | auto Intersected = PA1; |
304 | Intersected.intersect(Arg: PA1); |
305 | EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
306 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
307 | .preservedSet<AllAnalysesOn<Function>>()); |
308 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
309 | EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() |
310 | .preservedSet<AllAnalysesOn<Module>>()); |
311 | |
312 | // Intersecting with all is a no-op. |
313 | Intersected.intersect(Arg: PreservedAnalyses::all()); |
314 | EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
315 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
316 | .preservedSet<AllAnalysesOn<Function>>()); |
317 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
318 | EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() |
319 | .preservedSet<AllAnalysesOn<Module>>()); |
320 | |
321 | // Intersecting a narrow set with a more broad set is the narrow set. |
322 | Intersected.intersect(Arg: PA2); |
323 | EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
324 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
325 | .preservedSet<AllAnalysesOn<Function>>()); |
326 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
327 | EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() |
328 | .preservedSet<AllAnalysesOn<Module>>()); |
329 | |
330 | // Intersecting a broad set with a more narrow set is the narrow set. |
331 | Intersected = PA2; |
332 | Intersected.intersect(Arg: PA1); |
333 | EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
334 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
335 | .preservedSet<AllAnalysesOn<Function>>()); |
336 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
337 | EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() |
338 | .preservedSet<AllAnalysesOn<Module>>()); |
339 | |
340 | // Intersecting with empty clears. |
341 | Intersected.intersect(Arg: PreservedAnalyses::none()); |
342 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
343 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
344 | .preservedSet<AllAnalysesOn<Function>>()); |
345 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
346 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>() |
347 | .preservedSet<AllAnalysesOn<Module>>()); |
348 | |
349 | // Intersecting non-overlapping clears. |
350 | Intersected = PA1; |
351 | Intersected.intersect(Arg: PA3); |
352 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
353 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
354 | .preservedSet<AllAnalysesOn<Function>>()); |
355 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
356 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>() |
357 | .preservedSet<AllAnalysesOn<Module>>()); |
358 | |
359 | // Intersecting with moves works in when there is storage on both sides. |
360 | Intersected = PA1; |
361 | auto Tmp = PA2; |
362 | Intersected.intersect(Arg: std::move(Tmp)); |
363 | EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
364 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
365 | .preservedSet<AllAnalysesOn<Function>>()); |
366 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
367 | EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() |
368 | .preservedSet<AllAnalysesOn<Module>>()); |
369 | |
370 | // Intersecting with move works for incoming all and existing all. |
371 | auto Tmp2 = PreservedAnalyses::all(); |
372 | Intersected.intersect(Arg: std::move(Tmp2)); |
373 | EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
374 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
375 | .preservedSet<AllAnalysesOn<Function>>()); |
376 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
377 | EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() |
378 | .preservedSet<AllAnalysesOn<Module>>()); |
379 | Intersected = PreservedAnalyses::all(); |
380 | auto Tmp3 = PA1; |
381 | Intersected.intersect(Arg: std::move(Tmp3)); |
382 | EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved()); |
383 | EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>() |
384 | .preservedSet<AllAnalysesOn<Function>>()); |
385 | EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved()); |
386 | EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>() |
387 | .preservedSet<AllAnalysesOn<Module>>()); |
388 | } |
389 | |
390 | TEST(PreservedAnalysisTest, Abandon) { |
391 | auto PA = PreservedAnalyses::none(); |
392 | |
393 | // We can abandon things after they are preserved. |
394 | PA.preserve<TestFunctionAnalysis>(); |
395 | PA.abandon<TestFunctionAnalysis>(); |
396 | EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved()); |
397 | |
398 | // Repeated is fine, and abandoning if they were never preserved is fine. |
399 | PA.abandon<TestFunctionAnalysis>(); |
400 | EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved()); |
401 | PA.abandon<TestModuleAnalysis>(); |
402 | EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved()); |
403 | |
404 | // Even if the sets are preserved, the abandoned analyses' checker won't |
405 | // return true for those sets. |
406 | PA.preserveSet<AllAnalysesOn<Function>>(); |
407 | PA.preserveSet<AllAnalysesOn<Module>>(); |
408 | EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>() |
409 | .preservedSet<AllAnalysesOn<Function>>()); |
410 | EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>() |
411 | .preservedSet<AllAnalysesOn<Module>>()); |
412 | |
413 | // But an arbitrary (opaque) analysis will still observe the sets as |
414 | // preserved. This also checks that we can use an explicit ID rather than |
415 | // a type. |
416 | AnalysisKey FakeKey, *FakeID = &FakeKey; |
417 | EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Function>>()); |
418 | EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Module>>()); |
419 | } |
420 | |
421 | TEST_F(PassManagerTest, Basic) { |
422 | FunctionAnalysisManager FAM; |
423 | int FunctionAnalysisRuns = 0; |
424 | FAM.registerPass(PassBuilder: [&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); |
425 | |
426 | ModuleAnalysisManager MAM; |
427 | int ModuleAnalysisRuns = 0; |
428 | MAM.registerPass(PassBuilder: [&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); |
429 | MAM.registerPass(PassBuilder: [&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
430 | FAM.registerPass(PassBuilder: [&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); |
431 | |
432 | MAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(); }); |
433 | FAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(); }); |
434 | |
435 | ModulePassManager MPM; |
436 | |
437 | // Count the runs over a Function. |
438 | int FunctionPassRunCount1 = 0; |
439 | int AnalyzedInstrCount1 = 0; |
440 | int AnalyzedFunctionCount1 = 0; |
441 | { |
442 | // Pointless scoped copy to test move assignment. |
443 | ModulePassManager NestedMPM; |
444 | FunctionPassManager FPM; |
445 | { |
446 | // Pointless scope to test move assignment. |
447 | FunctionPassManager NestedFPM; |
448 | NestedFPM.addPass(Pass: TestFunctionPass(FunctionPassRunCount1, |
449 | AnalyzedInstrCount1, |
450 | AnalyzedFunctionCount1, MAM)); |
451 | FPM = std::move(NestedFPM); |
452 | } |
453 | NestedMPM.addPass(Pass: createModuleToFunctionPassAdaptor(Pass: std::move(FPM))); |
454 | MPM = std::move(NestedMPM); |
455 | } |
456 | |
457 | // Count the runs over a module. |
458 | int ModulePassRunCount = 0; |
459 | MPM.addPass(Pass: TestModulePass(ModulePassRunCount)); |
460 | |
461 | // Count the runs over a Function in a separate manager. |
462 | int FunctionPassRunCount2 = 0; |
463 | int AnalyzedInstrCount2 = 0; |
464 | int AnalyzedFunctionCount2 = 0; |
465 | { |
466 | FunctionPassManager FPM; |
467 | FPM.addPass(Pass: TestFunctionPass(FunctionPassRunCount2, AnalyzedInstrCount2, |
468 | AnalyzedFunctionCount2, MAM)); |
469 | MPM.addPass(Pass: createModuleToFunctionPassAdaptor(Pass: std::move(FPM))); |
470 | } |
471 | |
472 | // A third function pass manager but with only preserving intervening passes |
473 | // and with a function pass that invalidates exactly one analysis. |
474 | MPM.addPass(Pass: TestPreservingModulePass()); |
475 | int FunctionPassRunCount3 = 0; |
476 | int AnalyzedInstrCount3 = 0; |
477 | int AnalyzedFunctionCount3 = 0; |
478 | { |
479 | FunctionPassManager FPM; |
480 | FPM.addPass(Pass: TestFunctionPass(FunctionPassRunCount3, AnalyzedInstrCount3, |
481 | AnalyzedFunctionCount3, MAM)); |
482 | FPM.addPass(Pass: TestInvalidationFunctionPass("f" )); |
483 | MPM.addPass(Pass: createModuleToFunctionPassAdaptor(Pass: std::move(FPM))); |
484 | } |
485 | |
486 | // A fourth function pass manager but with only preserving intervening |
487 | // passes but triggering the module analysis. |
488 | MPM.addPass(Pass: RequireAnalysisPass<TestModuleAnalysis, Module>()); |
489 | int FunctionPassRunCount4 = 0; |
490 | int AnalyzedInstrCount4 = 0; |
491 | int AnalyzedFunctionCount4 = 0; |
492 | { |
493 | FunctionPassManager FPM; |
494 | FPM.addPass(Pass: TestFunctionPass(FunctionPassRunCount4, AnalyzedInstrCount4, |
495 | AnalyzedFunctionCount4, MAM)); |
496 | MPM.addPass(Pass: createModuleToFunctionPassAdaptor(Pass: std::move(FPM))); |
497 | } |
498 | |
499 | // A fifth function pass manager which invalidates one function first but |
500 | // uses only cached results. |
501 | int FunctionPassRunCount5 = 0; |
502 | int AnalyzedInstrCount5 = 0; |
503 | int AnalyzedFunctionCount5 = 0; |
504 | { |
505 | FunctionPassManager FPM; |
506 | FPM.addPass(Pass: TestInvalidationFunctionPass("f" )); |
507 | FPM.addPass(Pass: TestFunctionPass(FunctionPassRunCount5, AnalyzedInstrCount5, |
508 | AnalyzedFunctionCount5, MAM, |
509 | /*OnlyUseCachedResults=*/true)); |
510 | MPM.addPass(Pass: createModuleToFunctionPassAdaptor(Pass: std::move(FPM))); |
511 | } |
512 | |
513 | MPM.run(IR&: *M, AM&: MAM); |
514 | |
515 | // Validate module pass counters. |
516 | EXPECT_EQ(1, ModulePassRunCount); |
517 | |
518 | // Validate all function pass counter sets are the same. |
519 | EXPECT_EQ(3, FunctionPassRunCount1); |
520 | EXPECT_EQ(5, AnalyzedInstrCount1); |
521 | EXPECT_EQ(0, AnalyzedFunctionCount1); |
522 | EXPECT_EQ(3, FunctionPassRunCount2); |
523 | EXPECT_EQ(5, AnalyzedInstrCount2); |
524 | EXPECT_EQ(0, AnalyzedFunctionCount2); |
525 | EXPECT_EQ(3, FunctionPassRunCount3); |
526 | EXPECT_EQ(5, AnalyzedInstrCount3); |
527 | EXPECT_EQ(0, AnalyzedFunctionCount3); |
528 | EXPECT_EQ(3, FunctionPassRunCount4); |
529 | EXPECT_EQ(5, AnalyzedInstrCount4); |
530 | EXPECT_EQ(9, AnalyzedFunctionCount4); |
531 | EXPECT_EQ(3, FunctionPassRunCount5); |
532 | EXPECT_EQ(2, AnalyzedInstrCount5); // Only 'g' and 'h' were cached. |
533 | EXPECT_EQ(9, AnalyzedFunctionCount5); |
534 | |
535 | // Validate the analysis counters: |
536 | // first run over 3 functions, then module pass invalidates |
537 | // second run over 3 functions, nothing invalidates |
538 | // third run over 0 functions, but 1 function invalidated |
539 | // fourth run over 1 function |
540 | // fifth run invalidates 1 function first, but runs over 0 functions |
541 | EXPECT_EQ(7, FunctionAnalysisRuns); |
542 | |
543 | EXPECT_EQ(1, ModuleAnalysisRuns); |
544 | } |
545 | |
546 | // A customized pass manager that passes extra arguments through the |
547 | // infrastructure. |
548 | typedef AnalysisManager<Function, int> CustomizedAnalysisManager; |
549 | typedef PassManager<Function, CustomizedAnalysisManager, int, int &> |
550 | CustomizedPassManager; |
551 | |
552 | class CustomizedAnalysis : public AnalysisInfoMixin<CustomizedAnalysis> { |
553 | public: |
554 | struct Result { |
555 | Result(int I) : I(I) {} |
556 | int I; |
557 | }; |
558 | |
559 | Result run(Function &F, CustomizedAnalysisManager &AM, int I) { |
560 | return Result(I); |
561 | } |
562 | |
563 | private: |
564 | friend AnalysisInfoMixin<CustomizedAnalysis>; |
565 | static AnalysisKey Key; |
566 | }; |
567 | |
568 | AnalysisKey CustomizedAnalysis::Key; |
569 | |
570 | struct CustomizedPass : PassInfoMixin<CustomizedPass> { |
571 | std::function<void(CustomizedAnalysis::Result &, int &)> Callback; |
572 | |
573 | template <typename CallbackT> |
574 | CustomizedPass(CallbackT Callback) : Callback(Callback) {} |
575 | |
576 | PreservedAnalyses run(Function &F, CustomizedAnalysisManager &AM, int I, |
577 | int &O) { |
578 | Callback(AM.getResult<CustomizedAnalysis>(IR&: F, ExtraArgs: I), O); |
579 | return PreservedAnalyses::none(); |
580 | } |
581 | }; |
582 | |
583 | TEST_F(PassManagerTest, CustomizedPassManagerArgs) { |
584 | CustomizedAnalysisManager AM; |
585 | AM.registerPass(PassBuilder: [&] { return CustomizedAnalysis(); }); |
586 | PassInstrumentationCallbacks PIC; |
587 | AM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
588 | |
589 | CustomizedPassManager PM; |
590 | |
591 | // Add an instance of the customized pass that just accumulates the input |
592 | // after it is round-tripped through the analysis. |
593 | int Result = 0; |
594 | PM.addPass( |
595 | Pass: CustomizedPass([](CustomizedAnalysis::Result &R, int &O) { O += R.I; })); |
596 | |
597 | // Run this over every function with the input of 42. |
598 | for (Function &F : *M) |
599 | PM.run(IR&: F, AM, ExtraArgs: 42, ExtraArgs&: Result); |
600 | |
601 | // And ensure that we accumulated the correct result. |
602 | EXPECT_EQ(42 * (int)M->size(), Result); |
603 | } |
604 | |
605 | /// A test analysis pass which caches in its result another analysis pass and |
606 | /// uses it to serve queries. This requires the result to invalidate itself |
607 | /// when its dependency is invalidated. |
608 | struct TestIndirectFunctionAnalysis |
609 | : public AnalysisInfoMixin<TestIndirectFunctionAnalysis> { |
610 | struct Result { |
611 | Result(TestFunctionAnalysis::Result &FDep, TestModuleAnalysis::Result &MDep) |
612 | : FDep(FDep), MDep(MDep) {} |
613 | TestFunctionAnalysis::Result &FDep; |
614 | TestModuleAnalysis::Result &MDep; |
615 | |
616 | bool invalidate(Function &F, const PreservedAnalyses &PA, |
617 | FunctionAnalysisManager::Invalidator &Inv) { |
618 | auto PAC = PA.getChecker<TestIndirectFunctionAnalysis>(); |
619 | return !(PAC.preserved() || |
620 | PAC.preservedSet<AllAnalysesOn<Function>>()) || |
621 | Inv.invalidate<TestFunctionAnalysis>(IR&: F, PA); |
622 | } |
623 | }; |
624 | |
625 | TestIndirectFunctionAnalysis(int &Runs, ModuleAnalysisManager &MAM) |
626 | : Runs(Runs), MAM(MAM) {} |
627 | |
628 | /// Run the analysis pass over the function and return a result. |
629 | Result run(Function &F, FunctionAnalysisManager &AM) { |
630 | ++Runs; |
631 | auto &FDep = AM.getResult<TestFunctionAnalysis>(IR&: F); |
632 | auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(IR&: F); |
633 | // For the test, we insist that the module analysis starts off in the |
634 | // cache. Getting a cached result that isn't stateless trigger an assert. |
635 | // Use MAM, for the purposes of this unittest. |
636 | auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(IR&: *F.getParent()); |
637 | // And register the dependency as module analysis dependencies have to be |
638 | // pre-registered on the proxy. |
639 | MAMProxy.registerOuterAnalysisInvalidation<TestModuleAnalysis, |
640 | TestIndirectFunctionAnalysis>(); |
641 | return Result(FDep, MDep); |
642 | } |
643 | |
644 | private: |
645 | friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>; |
646 | static AnalysisKey Key; |
647 | |
648 | int &Runs; |
649 | ModuleAnalysisManager &MAM; |
650 | }; |
651 | |
652 | AnalysisKey TestIndirectFunctionAnalysis::Key; |
653 | |
654 | /// A test analysis pass which chaches in its result the result from the above |
655 | /// indirect analysis pass. |
656 | /// |
657 | /// This allows us to ensure that whenever an analysis pass is invalidated due |
658 | /// to dependencies (especially dependencies across IR units that trigger |
659 | /// asynchronous invalidation) we correctly detect that this may in turn cause |
660 | /// other analysis to be invalidated. |
661 | struct TestDoublyIndirectFunctionAnalysis |
662 | : public AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis> { |
663 | struct Result { |
664 | Result(TestIndirectFunctionAnalysis::Result &IDep) : IDep(IDep) {} |
665 | TestIndirectFunctionAnalysis::Result &IDep; |
666 | |
667 | bool invalidate(Function &F, const PreservedAnalyses &PA, |
668 | FunctionAnalysisManager::Invalidator &Inv) { |
669 | auto PAC = PA.getChecker<TestDoublyIndirectFunctionAnalysis>(); |
670 | return !(PAC.preserved() || |
671 | PAC.preservedSet<AllAnalysesOn<Function>>()) || |
672 | Inv.invalidate<TestIndirectFunctionAnalysis>(IR&: F, PA); |
673 | } |
674 | }; |
675 | |
676 | TestDoublyIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {} |
677 | |
678 | /// Run the analysis pass over the function and return a result. |
679 | Result run(Function &F, FunctionAnalysisManager &AM) { |
680 | ++Runs; |
681 | auto &IDep = AM.getResult<TestIndirectFunctionAnalysis>(IR&: F); |
682 | return Result(IDep); |
683 | } |
684 | |
685 | private: |
686 | friend AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis>; |
687 | static AnalysisKey Key; |
688 | |
689 | int &Runs; |
690 | }; |
691 | |
692 | AnalysisKey TestDoublyIndirectFunctionAnalysis::Key; |
693 | |
694 | struct LambdaPass : public PassInfoMixin<LambdaPass> { |
695 | using FuncT = std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)>; |
696 | |
697 | LambdaPass(FuncT Func) : Func(std::move(Func)) {} |
698 | |
699 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { |
700 | return Func(F, AM); |
701 | } |
702 | |
703 | FuncT Func; |
704 | }; |
705 | |
706 | TEST_F(PassManagerTest, IndirectAnalysisInvalidation) { |
707 | FunctionAnalysisManager FAM; |
708 | ModuleAnalysisManager MAM; |
709 | int FunctionAnalysisRuns = 0, ModuleAnalysisRuns = 0, |
710 | IndirectAnalysisRuns = 0, DoublyIndirectAnalysisRuns = 0; |
711 | FAM.registerPass(PassBuilder: [&] { return TestFunctionAnalysis(FunctionAnalysisRuns); }); |
712 | FAM.registerPass( |
713 | PassBuilder: [&] { return TestIndirectFunctionAnalysis(IndirectAnalysisRuns, MAM); }); |
714 | FAM.registerPass(PassBuilder: [&] { |
715 | return TestDoublyIndirectFunctionAnalysis(DoublyIndirectAnalysisRuns); |
716 | }); |
717 | |
718 | MAM.registerPass(PassBuilder: [&] { return TestModuleAnalysis(ModuleAnalysisRuns); }); |
719 | MAM.registerPass(PassBuilder: [&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
720 | FAM.registerPass(PassBuilder: [&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); |
721 | |
722 | PassInstrumentationCallbacks PIC; |
723 | MAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
724 | FAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
725 | |
726 | int InstrCount = 0, FunctionCount = 0; |
727 | ModulePassManager MPM; |
728 | FunctionPassManager FPM; |
729 | // First just use the analysis to get the instruction count, and preserve |
730 | // everything. |
731 | FPM.addPass(Pass: LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { |
732 | auto &DoublyIndirectResult = |
733 | AM.getResult<TestDoublyIndirectFunctionAnalysis>(IR&: F); |
734 | auto &IndirectResult = DoublyIndirectResult.IDep; |
735 | InstrCount += IndirectResult.FDep.InstructionCount; |
736 | FunctionCount += IndirectResult.MDep.FunctionCount; |
737 | return PreservedAnalyses::all(); |
738 | })); |
739 | // Next, invalidate |
740 | // - both analyses for "f", |
741 | // - just the underlying (indirect) analysis for "g", and |
742 | // - just the direct analysis for "h". |
743 | FPM.addPass(Pass: LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { |
744 | auto &DoublyIndirectResult = |
745 | AM.getResult<TestDoublyIndirectFunctionAnalysis>(IR&: F); |
746 | auto &IndirectResult = DoublyIndirectResult.IDep; |
747 | InstrCount += IndirectResult.FDep.InstructionCount; |
748 | FunctionCount += IndirectResult.MDep.FunctionCount; |
749 | auto PA = PreservedAnalyses::none(); |
750 | if (F.getName() == "g" ) |
751 | PA.preserve<TestFunctionAnalysis>(); |
752 | else if (F.getName() == "h" ) |
753 | PA.preserve<TestIndirectFunctionAnalysis>(); |
754 | return PA; |
755 | })); |
756 | // Finally, use the analysis again on each function, forcing re-computation |
757 | // for all of them. |
758 | FPM.addPass(Pass: LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { |
759 | auto &DoublyIndirectResult = |
760 | AM.getResult<TestDoublyIndirectFunctionAnalysis>(IR&: F); |
761 | auto &IndirectResult = DoublyIndirectResult.IDep; |
762 | InstrCount += IndirectResult.FDep.InstructionCount; |
763 | FunctionCount += IndirectResult.MDep.FunctionCount; |
764 | return PreservedAnalyses::all(); |
765 | })); |
766 | |
767 | // Create a second function pass manager. This will cause the module-level |
768 | // invalidation to occur, which will force yet another invalidation of the |
769 | // indirect function-level analysis as the module analysis it depends on gets |
770 | // invalidated. |
771 | FunctionPassManager FPM2; |
772 | FPM2.addPass(Pass: LambdaPass([&](Function &F, FunctionAnalysisManager &AM) { |
773 | auto &DoublyIndirectResult = |
774 | AM.getResult<TestDoublyIndirectFunctionAnalysis>(IR&: F); |
775 | auto &IndirectResult = DoublyIndirectResult.IDep; |
776 | InstrCount += IndirectResult.FDep.InstructionCount; |
777 | FunctionCount += IndirectResult.MDep.FunctionCount; |
778 | return PreservedAnalyses::all(); |
779 | })); |
780 | |
781 | // Add a requires pass to populate the module analysis and then our function |
782 | // pass pipeline. |
783 | MPM.addPass(Pass: RequireAnalysisPass<TestModuleAnalysis, Module>()); |
784 | MPM.addPass(Pass: createModuleToFunctionPassAdaptor(Pass: std::move(FPM))); |
785 | // Now require the module analysis again (it will have been invalidated once) |
786 | // and then use it again from a function pass manager. |
787 | MPM.addPass(Pass: RequireAnalysisPass<TestModuleAnalysis, Module>()); |
788 | MPM.addPass(Pass: createModuleToFunctionPassAdaptor(Pass: std::move(FPM2))); |
789 | MPM.run(IR&: *M, AM&: MAM); |
790 | |
791 | // There are generally two possible runs for each of the three functions. But |
792 | // for one function, we only invalidate the indirect analysis so the base one |
793 | // only gets run five times. |
794 | EXPECT_EQ(5, FunctionAnalysisRuns); |
795 | // The module analysis pass should be run twice here. |
796 | EXPECT_EQ(2, ModuleAnalysisRuns); |
797 | // The indirect analysis is invalidated for each function (either directly or |
798 | // indirectly) and run twice for each. |
799 | EXPECT_EQ(9, IndirectAnalysisRuns); |
800 | EXPECT_EQ(9, DoublyIndirectAnalysisRuns); |
801 | |
802 | // There are five instructions in the module and we add the count four |
803 | // times. |
804 | EXPECT_EQ(5 * 4, InstrCount); |
805 | |
806 | // There are three functions and we count them four times for each of the |
807 | // three functions. |
808 | EXPECT_EQ(3 * 4 * 3, FunctionCount); |
809 | } |
810 | |
811 | // Run SimplifyCFGPass that makes CFG changes and reports PreservedAnalyses |
812 | // without CFGAnalyses. So the CFGChecker does not complain. |
813 | TEST_F(PassManagerTest, FunctionPassCFGChecker) { |
814 | LLVMContext Context; |
815 | // SimplifyCFG changes this function to |
816 | // define void @foo {next: ret void} |
817 | auto M = parseIR(Context, IR: "define void @foo() {\n" |
818 | " br label %next\n" |
819 | "next:\n" |
820 | " br label %exit\n" |
821 | "exit:\n" |
822 | " ret void\n" |
823 | "}\n" ); |
824 | |
825 | auto *F = M->getFunction(Name: "foo" ); |
826 | FunctionAnalysisManager FAM; |
827 | ModuleAnalysisManager MAM; |
828 | FunctionPassManager FPM; |
829 | PassInstrumentationCallbacks PIC; |
830 | StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ true); |
831 | SI.registerCallbacks(PIC, MAM: &MAM); |
832 | MAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
833 | MAM.registerPass(PassBuilder: [&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
834 | FAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
835 | FAM.registerPass(PassBuilder: [&] { return DominatorTreeAnalysis(); }); |
836 | FAM.registerPass(PassBuilder: [&] { return AssumptionAnalysis(); }); |
837 | FAM.registerPass(PassBuilder: [&] { return TargetIRAnalysis(); }); |
838 | |
839 | FPM.addPass(Pass: SimplifyCFGPass()); |
840 | FPM.run(IR&: *F, AM&: FAM); |
841 | } |
842 | |
843 | // FunctionPass that manually invalidates analyses and always returns |
844 | // PreservedAnalyses::all(). |
845 | struct TestSimplifyCFGInvalidatingAnalysisPass |
846 | : PassInfoMixin<TestSimplifyCFGInvalidatingAnalysisPass> { |
847 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) { |
848 | // Run SimplifyCFG and if it changes CFG then invalidate the CFG analysis. |
849 | // This allows to return PreserveAnalysis::all(). |
850 | PreservedAnalyses PA = CFGSimplifier.run(F, AM&: FAM); |
851 | FAM.invalidate(IR&: F, PA); |
852 | return PreservedAnalyses::all(); |
853 | } |
854 | |
855 | SimplifyCFGPass CFGSimplifier; |
856 | }; |
857 | |
858 | // Run TestSimplifyCFGInvalidatingAnalysisPass which changes CFG by running |
859 | // SimplifyCFGPass then manually invalidates analyses and always returns |
860 | // PreservedAnalyses::all(). CFGChecker does not complain because it resets |
861 | // its saved CFG snapshot when the analyses are invalidated manually. |
862 | TEST_F(PassManagerTest, FunctionPassCFGCheckerInvalidateAnalysis) { |
863 | LLVMContext Context; |
864 | // SimplifyCFG changes this function to |
865 | // define void @foo {next: ret void} |
866 | auto M = parseIR(Context, IR: "define void @foo() {\n" |
867 | " br label %next\n" |
868 | "next:\n" |
869 | " br label %exit\n" |
870 | "exit:\n" |
871 | " ret void\n" |
872 | "}\n" ); |
873 | |
874 | auto *F = M->getFunction(Name: "foo" ); |
875 | FunctionAnalysisManager FAM; |
876 | ModuleAnalysisManager MAM; |
877 | FunctionPassManager FPM; |
878 | PassInstrumentationCallbacks PIC; |
879 | StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ true); |
880 | SI.registerCallbacks(PIC, MAM: &MAM); |
881 | MAM.registerPass(PassBuilder: [&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
882 | MAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
883 | FAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
884 | FAM.registerPass(PassBuilder: [&] { return DominatorTreeAnalysis(); }); |
885 | FAM.registerPass(PassBuilder: [&] { return AssumptionAnalysis(); }); |
886 | FAM.registerPass(PassBuilder: [&] { return TargetIRAnalysis(); }); |
887 | |
888 | FPM.addPass(Pass: TestSimplifyCFGInvalidatingAnalysisPass()); |
889 | FPM.run(IR&: *F, AM&: FAM); |
890 | } |
891 | |
892 | // Wrap a FunctionPassManager running SimplifyCFG pass with another |
893 | // FunctionPassManager. |
894 | struct TestSimplifyCFGWrapperPass : PassInfoMixin<TestSimplifyCFGWrapperPass> { |
895 | TestSimplifyCFGWrapperPass(FunctionPassManager &InnerPM) : InnerPM(InnerPM) {} |
896 | |
897 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) { |
898 | // Here we simulate exactly what FunctionPassManager::run() does but |
899 | // instead of running all passes from InnerPM.Passes we run them in bulk |
900 | // by calling InnerPM.run(). |
901 | PreservedAnalyses PA = PreservedAnalyses::all(); |
902 | PassInstrumentation PI = FAM.getResult<PassInstrumentationAnalysis>(IR&: F); |
903 | |
904 | if (!PI.runBeforePass<Function>(Pass: InnerPM, IR: F)) |
905 | return PreservedAnalyses::all(); |
906 | |
907 | PreservedAnalyses PassPA = InnerPM.run(IR&: F, AM&: FAM); |
908 | PI.runAfterPass(Pass: InnerPM, IR: F, PA: PassPA); |
909 | FAM.invalidate(IR&: F, PA: PassPA); |
910 | PA.intersect(Arg: PassPA); |
911 | PA.preserveSet<AllAnalysesOn<Function>>(); |
912 | return PA; |
913 | } |
914 | |
915 | FunctionPassManager &InnerPM; |
916 | }; |
917 | |
918 | // Run TestSimplifyCFGWrapperPass which simulates behavior of |
919 | // FunctionPassManager::run() except that it runs all passes at once by calling |
920 | // an inner pass manager's passes with PassManager::run(). This is how one pass |
921 | // manager is expected to wrap another pass manager. |
922 | // SimplifyCFGPass, which is called by the inner pass manager, changes the CFG. |
923 | // The CFGChecker's AfterPassCallback, run right after SimplifyCFGPass, does not |
924 | // complain because CFGAnalyses is not in the PreservedAnalises set returned by |
925 | // SimplifyCFGPass. Then the CFG analysis is invalidated by the analysis manager |
926 | // according to the PreservedAnalises set. Further calls to CFGChecker's |
927 | // AfterPassCallback see that all analyses for the current function are |
928 | // preserved but there is no CFG snapshot available (i.e. |
929 | // AM.getCachedResult<PreservedCFGCheckerAnalysis>(F) returns nullptr). |
930 | TEST_F(PassManagerTest, FunctionPassCFGCheckerWrapped) { |
931 | LLVMContext Context; |
932 | // SimplifyCFG changes this function to |
933 | // define void @foo {next: ret void} |
934 | auto M = parseIR(Context, IR: "define void @foo() {\n" |
935 | " br label %next\n" |
936 | "next:\n" |
937 | " br label %exit\n" |
938 | "exit:\n" |
939 | " ret void\n" |
940 | "}\n" ); |
941 | |
942 | auto *F = M->getFunction(Name: "foo" ); |
943 | FunctionAnalysisManager FAM; |
944 | ModuleAnalysisManager MAM; |
945 | FunctionPassManager FPM; |
946 | PassInstrumentationCallbacks PIC; |
947 | StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ true); |
948 | SI.registerCallbacks(PIC, MAM: &MAM); |
949 | MAM.registerPass(PassBuilder: [&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
950 | MAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
951 | FAM.registerPass(PassBuilder: [&] { return PassInstrumentationAnalysis(&PIC); }); |
952 | FAM.registerPass(PassBuilder: [&] { return DominatorTreeAnalysis(); }); |
953 | FAM.registerPass(PassBuilder: [&] { return AssumptionAnalysis(); }); |
954 | FAM.registerPass(PassBuilder: [&] { return TargetIRAnalysis(); }); |
955 | |
956 | FunctionPassManager InnerFPM; |
957 | InnerFPM.addPass(Pass: SimplifyCFGPass()); |
958 | |
959 | FPM.addPass(Pass: TestSimplifyCFGWrapperPass(InnerFPM)); |
960 | FPM.run(IR&: *F, AM&: FAM); |
961 | } |
962 | |
963 | #ifdef EXPENSIVE_CHECKS |
964 | |
965 | struct WrongFunctionPass : PassInfoMixin<WrongFunctionPass> { |
966 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) { |
967 | F.getEntryBlock().begin()->eraseFromParent(); |
968 | return PreservedAnalyses::all(); |
969 | } |
970 | static StringRef name() { return "WrongFunctionPass" ; } |
971 | }; |
972 | |
973 | TEST_F(PassManagerTest, FunctionPassMissedFunctionAnalysisInvalidation) { |
974 | LLVMContext Context; |
975 | auto M = parseIR(Context, "define void @foo() {\n" |
976 | " %a = add i32 0, 0\n" |
977 | " ret void\n" |
978 | "}\n" ); |
979 | |
980 | FunctionAnalysisManager FAM; |
981 | ModuleAnalysisManager MAM; |
982 | PassInstrumentationCallbacks PIC; |
983 | StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ false); |
984 | SI.registerCallbacks(PIC, &MAM); |
985 | MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
986 | MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); |
987 | FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); |
988 | FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); |
989 | |
990 | FunctionPassManager FPM; |
991 | FPM.addPass(WrongFunctionPass()); |
992 | |
993 | auto *F = M->getFunction("foo" ); |
994 | EXPECT_DEATH(FPM.run(*F, FAM), "Function @foo changed by WrongFunctionPass without invalidating analyses" ); |
995 | } |
996 | |
997 | struct WrongModulePass : PassInfoMixin<WrongModulePass> { |
998 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) { |
999 | for (Function &F : M) |
1000 | F.getEntryBlock().begin()->eraseFromParent(); |
1001 | |
1002 | PreservedAnalyses PA; |
1003 | PA.preserveSet<AllAnalysesOn<Function>>(); |
1004 | PA.preserve<FunctionAnalysisManagerModuleProxy>(); |
1005 | return PA; |
1006 | } |
1007 | static StringRef name() { return "WrongModulePass" ; } |
1008 | }; |
1009 | |
1010 | TEST_F(PassManagerTest, ModulePassMissedFunctionAnalysisInvalidation) { |
1011 | LLVMContext Context; |
1012 | auto M = parseIR(Context, "define void @foo() {\n" |
1013 | " %a = add i32 0, 0\n" |
1014 | " ret void\n" |
1015 | "}\n" ); |
1016 | |
1017 | FunctionAnalysisManager FAM; |
1018 | ModuleAnalysisManager MAM; |
1019 | PassInstrumentationCallbacks PIC; |
1020 | StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ false); |
1021 | SI.registerCallbacks(PIC, &MAM); |
1022 | MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
1023 | MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); |
1024 | FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); |
1025 | FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); |
1026 | |
1027 | ModulePassManager MPM; |
1028 | MPM.addPass(WrongModulePass()); |
1029 | |
1030 | EXPECT_DEATH( |
1031 | MPM.run(*M, MAM), |
1032 | "Function @foo changed by WrongModulePass without invalidating analyses" ); |
1033 | } |
1034 | |
1035 | struct WrongModulePass2 : PassInfoMixin<WrongModulePass2> { |
1036 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) { |
1037 | for (Function &F : M) |
1038 | F.getEntryBlock().begin()->eraseFromParent(); |
1039 | |
1040 | PreservedAnalyses PA; |
1041 | PA.preserveSet<AllAnalysesOn<Module>>(); |
1042 | PA.abandon<FunctionAnalysisManagerModuleProxy>(); |
1043 | return PA; |
1044 | } |
1045 | static StringRef name() { return "WrongModulePass2" ; } |
1046 | }; |
1047 | |
1048 | TEST_F(PassManagerTest, ModulePassMissedModuleAnalysisInvalidation) { |
1049 | LLVMContext Context; |
1050 | auto M = parseIR(Context, "define void @foo() {\n" |
1051 | " %a = add i32 0, 0\n" |
1052 | " ret void\n" |
1053 | "}\n" ); |
1054 | |
1055 | FunctionAnalysisManager FAM; |
1056 | ModuleAnalysisManager MAM; |
1057 | PassInstrumentationCallbacks PIC; |
1058 | StandardInstrumentations SI(M->getContext(), /*DebugLogging*/ false); |
1059 | SI.registerCallbacks(PIC, &MAM); |
1060 | MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); |
1061 | MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); |
1062 | FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); |
1063 | FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); |
1064 | |
1065 | ModulePassManager MPM; |
1066 | MPM.addPass(WrongModulePass2()); |
1067 | |
1068 | EXPECT_DEATH( |
1069 | MPM.run(*M, MAM), |
1070 | "Module changed by WrongModulePass2 without invalidating analyses" ); |
1071 | } |
1072 | |
1073 | #endif |
1074 | } |
1075 | |