1 | //===- unittests/IR/PassBuilderCallbacksTest.cpp - PB Callback 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/Testing/Support/Error.h" |
10 | #include <functional> |
11 | #include <gmock/gmock.h> |
12 | #include <gtest/gtest.h> |
13 | #include <llvm/ADT/Any.h> |
14 | #include <llvm/Analysis/CGSCCPassManager.h> |
15 | #include <llvm/Analysis/LoopAnalysisManager.h> |
16 | #include <llvm/AsmParser/Parser.h> |
17 | #include <llvm/IR/LLVMContext.h> |
18 | #include <llvm/IR/PassInstrumentation.h> |
19 | #include <llvm/IR/PassManager.h> |
20 | #include <llvm/Passes/PassBuilder.h> |
21 | #include <llvm/Support/Regex.h> |
22 | #include <llvm/Support/SourceMgr.h> |
23 | #include <llvm/Transforms/Scalar/LoopPassManager.h> |
24 | |
25 | using namespace llvm; |
26 | |
27 | namespace { |
28 | using testing::AnyNumber; |
29 | using testing::DoAll; |
30 | using testing::Not; |
31 | using testing::Return; |
32 | using testing::WithArgs; |
33 | using testing::_; |
34 | |
35 | /// A CRTP base for analysis mock handles |
36 | /// |
37 | /// This class reconciles mocking with the value semantics implementation of the |
38 | /// AnalysisManager. Analysis mock handles should derive from this class and |
39 | /// call \c setDefault() in their constroctur for wiring up the defaults defined |
40 | /// by this base with their mock run() and invalidate() implementations. |
41 | template <typename DerivedT, typename IRUnitT, |
42 | typename AnalysisManagerT = AnalysisManager<IRUnitT>, |
43 | typename... ExtraArgTs> |
44 | class MockAnalysisHandleBase { |
45 | public: |
46 | class Analysis : public AnalysisInfoMixin<Analysis> { |
47 | friend AnalysisInfoMixin<Analysis>; |
48 | friend MockAnalysisHandleBase; |
49 | static AnalysisKey Key; |
50 | |
51 | DerivedT *Handle; |
52 | |
53 | Analysis(DerivedT &Handle) : Handle(&Handle) { |
54 | static_assert(std::is_base_of<MockAnalysisHandleBase, DerivedT>::value, |
55 | "Must pass the derived type to this template!" ); |
56 | } |
57 | |
58 | public: |
59 | class Result { |
60 | friend MockAnalysisHandleBase; |
61 | |
62 | DerivedT *Handle; |
63 | |
64 | Result(DerivedT &Handle) : Handle(&Handle) {} |
65 | |
66 | public: |
67 | // Forward invalidation events to the mock handle. |
68 | bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA, |
69 | typename AnalysisManagerT::Invalidator &Inv) { |
70 | return Handle->invalidate(IR, PA, Inv); |
71 | } |
72 | }; |
73 | |
74 | Result run(IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ) { |
75 | return Handle->run(IR, AM, ExtraArgs...); |
76 | } |
77 | }; |
78 | |
79 | Analysis getAnalysis() { return Analysis(static_cast<DerivedT &>(*this)); } |
80 | typename Analysis::Result getResult() { |
81 | return typename Analysis::Result(static_cast<DerivedT &>(*this)); |
82 | } |
83 | static StringRef getName() { return llvm::getTypeName<DerivedT>(); } |
84 | |
85 | protected: |
86 | // FIXME: MSVC seems unable to handle a lambda argument to Invoke from within |
87 | // the template, so we use a boring static function. |
88 | static bool invalidateCallback(IRUnitT &IR, const PreservedAnalyses &PA, |
89 | typename AnalysisManagerT::Invalidator &Inv) { |
90 | auto PAC = PA.template getChecker<Analysis>(); |
91 | return !PAC.preserved() && |
92 | !PAC.template preservedSet<AllAnalysesOn<IRUnitT>>(); |
93 | } |
94 | |
95 | /// Derived classes should call this in their constructor to set up default |
96 | /// mock actions. (We can't do this in our constructor because this has to |
97 | /// run after the DerivedT is constructed.) |
98 | void setDefaults() { |
99 | ON_CALL(static_cast<DerivedT &>(*this), |
100 | run(_, _, testing::Matcher<ExtraArgTs>(_)...)) |
101 | .WillByDefault(Return(this->getResult())); |
102 | ON_CALL(static_cast<DerivedT &>(*this), invalidate(_, _, _)) |
103 | .WillByDefault(&invalidateCallback); |
104 | } |
105 | }; |
106 | |
107 | /// A CRTP base for pass mock handles |
108 | /// |
109 | /// This class reconciles mocking with the value semantics implementation of the |
110 | /// PassManager. Pass mock handles should derive from this class and |
111 | /// call \c setDefault() in their constroctur for wiring up the defaults defined |
112 | /// by this base with their mock run() and invalidate() implementations. |
113 | template <typename DerivedT, typename IRUnitT, typename AnalysisManagerT, |
114 | typename... ExtraArgTs> |
115 | AnalysisKey MockAnalysisHandleBase<DerivedT, IRUnitT, AnalysisManagerT, |
116 | ExtraArgTs...>::Analysis::Key; |
117 | |
118 | template <typename DerivedT, typename IRUnitT, |
119 | typename AnalysisManagerT = AnalysisManager<IRUnitT>, |
120 | typename... ExtraArgTs> |
121 | class MockPassHandleBase { |
122 | public: |
123 | class Pass : public PassInfoMixin<Pass> { |
124 | friend MockPassHandleBase; |
125 | |
126 | DerivedT *Handle; |
127 | |
128 | Pass(DerivedT &Handle) : Handle(&Handle) { |
129 | static_assert(std::is_base_of<MockPassHandleBase, DerivedT>::value, |
130 | "Must pass the derived type to this template!" ); |
131 | } |
132 | |
133 | public: |
134 | PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM, |
135 | ExtraArgTs... ) { |
136 | return Handle->run(IR, AM, ExtraArgs...); |
137 | } |
138 | }; |
139 | |
140 | static StringRef getName() { return llvm::getTypeName<DerivedT>(); } |
141 | |
142 | Pass getPass() { return Pass(static_cast<DerivedT &>(*this)); } |
143 | |
144 | protected: |
145 | /// Derived classes should call this in their constructor to set up default |
146 | /// mock actions. (We can't do this in our constructor because this has to |
147 | /// run after the DerivedT is constructed.) |
148 | void setDefaults() { |
149 | ON_CALL(static_cast<DerivedT &>(*this), |
150 | run(_, _, testing::Matcher<ExtraArgTs>(_)...)) |
151 | .WillByDefault(Return(value: PreservedAnalyses::all())); |
152 | } |
153 | }; |
154 | |
155 | /// Mock handles for passes for the IRUnits Module, CGSCC, Function, Loop. |
156 | /// These handles define the appropriate run() mock interface for the respective |
157 | /// IRUnit type. |
158 | template <typename IRUnitT> struct MockPassHandle; |
159 | template <> |
160 | struct MockPassHandle<Loop> |
161 | : MockPassHandleBase<MockPassHandle<Loop>, Loop, LoopAnalysisManager, |
162 | LoopStandardAnalysisResults &, LPMUpdater &> { |
163 | MOCK_METHOD4(run, |
164 | PreservedAnalyses(Loop &, LoopAnalysisManager &, |
165 | LoopStandardAnalysisResults &, LPMUpdater &)); |
166 | static void invalidateLoop(Loop &L, LoopAnalysisManager &, |
167 | LoopStandardAnalysisResults &, |
168 | LPMUpdater &Updater) { |
169 | Updater.markLoopAsDeleted(L, Name: L.getName()); |
170 | } |
171 | MockPassHandle() { setDefaults(); } |
172 | }; |
173 | |
174 | template <> |
175 | struct MockPassHandle<LoopNest> |
176 | : MockPassHandleBase<MockPassHandle<LoopNest>, LoopNest, |
177 | LoopAnalysisManager, LoopStandardAnalysisResults &, |
178 | LPMUpdater &> { |
179 | MOCK_METHOD4(run, |
180 | PreservedAnalyses(LoopNest &, LoopAnalysisManager &, |
181 | LoopStandardAnalysisResults &, LPMUpdater &)); |
182 | static void invalidateLoopNest(LoopNest &L, LoopAnalysisManager &, |
183 | LoopStandardAnalysisResults &, |
184 | LPMUpdater &Updater) { |
185 | Updater.markLoopAsDeleted(L&: L.getOutermostLoop(), Name: L.getName()); |
186 | } |
187 | MockPassHandle() { setDefaults(); } |
188 | }; |
189 | |
190 | template <> |
191 | struct MockPassHandle<Function> |
192 | : MockPassHandleBase<MockPassHandle<Function>, Function> { |
193 | MOCK_METHOD2(run, PreservedAnalyses(Function &, FunctionAnalysisManager &)); |
194 | |
195 | MockPassHandle() { setDefaults(); } |
196 | }; |
197 | |
198 | template <> |
199 | struct MockPassHandle<LazyCallGraph::SCC> |
200 | : MockPassHandleBase<MockPassHandle<LazyCallGraph::SCC>, LazyCallGraph::SCC, |
201 | CGSCCAnalysisManager, LazyCallGraph &, |
202 | CGSCCUpdateResult &> { |
203 | MOCK_METHOD4(run, |
204 | PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &, |
205 | LazyCallGraph &G, CGSCCUpdateResult &UR)); |
206 | |
207 | static void invalidateSCC(LazyCallGraph::SCC &C, CGSCCAnalysisManager &, |
208 | LazyCallGraph &, CGSCCUpdateResult &UR) { |
209 | UR.InvalidatedSCCs.insert(Ptr: &C); |
210 | } |
211 | |
212 | MockPassHandle() { setDefaults(); } |
213 | }; |
214 | |
215 | template <> |
216 | struct MockPassHandle<Module> |
217 | : MockPassHandleBase<MockPassHandle<Module>, Module> { |
218 | MOCK_METHOD2(run, PreservedAnalyses(Module &, ModuleAnalysisManager &)); |
219 | |
220 | MockPassHandle() { setDefaults(); } |
221 | }; |
222 | |
223 | /// Mock handles for analyses for the IRUnits Module, CGSCC, Function, Loop. |
224 | /// These handles define the appropriate run() and invalidate() mock interfaces |
225 | /// for the respective IRUnit type. |
226 | template <typename IRUnitT> struct MockAnalysisHandle; |
227 | template <> |
228 | struct MockAnalysisHandle<Loop> |
229 | : MockAnalysisHandleBase<MockAnalysisHandle<Loop>, Loop, |
230 | LoopAnalysisManager, |
231 | LoopStandardAnalysisResults &> { |
232 | |
233 | MOCK_METHOD3_T(run, typename Analysis::Result(Loop &, LoopAnalysisManager &, |
234 | LoopStandardAnalysisResults &)); |
235 | |
236 | MOCK_METHOD3_T(invalidate, bool(Loop &, const PreservedAnalyses &, |
237 | LoopAnalysisManager::Invalidator &)); |
238 | |
239 | MockAnalysisHandle() { this->setDefaults(); } |
240 | }; |
241 | |
242 | template <> |
243 | struct MockAnalysisHandle<Function> |
244 | : MockAnalysisHandleBase<MockAnalysisHandle<Function>, Function> { |
245 | MOCK_METHOD2(run, Analysis::Result(Function &, FunctionAnalysisManager &)); |
246 | |
247 | MOCK_METHOD3(invalidate, bool(Function &, const PreservedAnalyses &, |
248 | FunctionAnalysisManager::Invalidator &)); |
249 | |
250 | MockAnalysisHandle() { setDefaults(); } |
251 | }; |
252 | |
253 | template <> |
254 | struct MockAnalysisHandle<LazyCallGraph::SCC> |
255 | : MockAnalysisHandleBase<MockAnalysisHandle<LazyCallGraph::SCC>, |
256 | LazyCallGraph::SCC, CGSCCAnalysisManager, |
257 | LazyCallGraph &> { |
258 | MOCK_METHOD3(run, Analysis::Result(LazyCallGraph::SCC &, |
259 | CGSCCAnalysisManager &, LazyCallGraph &)); |
260 | |
261 | MOCK_METHOD3(invalidate, bool(LazyCallGraph::SCC &, const PreservedAnalyses &, |
262 | CGSCCAnalysisManager::Invalidator &)); |
263 | |
264 | MockAnalysisHandle() { setDefaults(); } |
265 | }; |
266 | |
267 | template <> |
268 | struct MockAnalysisHandle<Module> |
269 | : MockAnalysisHandleBase<MockAnalysisHandle<Module>, Module> { |
270 | MOCK_METHOD2(run, Analysis::Result(Module &, ModuleAnalysisManager &)); |
271 | |
272 | MOCK_METHOD3(invalidate, bool(Module &, const PreservedAnalyses &, |
273 | ModuleAnalysisManager::Invalidator &)); |
274 | |
275 | MockAnalysisHandle() { setDefaults(); } |
276 | }; |
277 | |
278 | static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { |
279 | SMDiagnostic Err; |
280 | return parseAssemblyString(AsmString: IR, Err, Context&: C); |
281 | } |
282 | |
283 | /// Helper for HasName matcher that returns getName both for IRUnit and |
284 | /// for IRUnit pointer wrapper into llvm::Any (wrapped by PassInstrumentation). |
285 | template <typename IRUnitT> std::string getName(const IRUnitT &IR) { |
286 | return std::string(IR.getName()); |
287 | } |
288 | |
289 | template <> std::string getName(const StringRef &name) { |
290 | return std::string(name); |
291 | } |
292 | |
293 | template <> std::string getName(const Any &WrappedIR) { |
294 | if (const auto *const *M = llvm::any_cast<const Module *>(Value: &WrappedIR)) |
295 | return (*M)->getName().str(); |
296 | if (const auto *const *F = llvm::any_cast<const Function *>(Value: &WrappedIR)) |
297 | return (*F)->getName().str(); |
298 | if (const auto *const *L = llvm::any_cast<const Loop *>(Value: &WrappedIR)) |
299 | return (*L)->getName().str(); |
300 | if (const auto *const *L = llvm::any_cast<const LoopNest *>(Value: &WrappedIR)) |
301 | return (*L)->getName().str(); |
302 | if (const auto *const *C = |
303 | llvm::any_cast<const LazyCallGraph::SCC *>(Value: &WrappedIR)) |
304 | return (*C)->getName(); |
305 | return "<UNKNOWN>" ; |
306 | } |
307 | /// Define a custom matcher for objects which support a 'getName' method. |
308 | /// |
309 | /// LLVM often has IR objects or analysis objects which expose a name |
310 | /// and in tests it is convenient to match these by name for readability. |
311 | /// Usually, this name is either a StringRef or a plain std::string. This |
312 | /// matcher supports any type exposing a getName() method of this form whose |
313 | /// return value is compatible with an std::ostream. For StringRef, this uses |
314 | /// the shift operator defined above. |
315 | /// |
316 | /// It should be used as: |
317 | /// |
318 | /// HasName("my_function") |
319 | /// |
320 | /// No namespace or other qualification is required. |
321 | MATCHER_P(HasName, Name, "" ) { |
322 | *result_listener << "has name '" << getName(arg) << "'" ; |
323 | return Name == getName(arg); |
324 | } |
325 | |
326 | MATCHER_P(HasNameRegex, Name, "" ) { |
327 | *result_listener << "has name '" << getName(arg) << "'" ; |
328 | llvm::Regex r(Name); |
329 | return r.match(String: getName(arg)); |
330 | } |
331 | |
332 | struct MockPassInstrumentationCallbacks { |
333 | PassInstrumentationCallbacks Callbacks; |
334 | |
335 | MockPassInstrumentationCallbacks() { |
336 | ON_CALL(*this, runBeforePass(_, _)).WillByDefault(action: Return(value: true)); |
337 | } |
338 | MOCK_METHOD2(runBeforePass, bool(StringRef PassID, llvm::Any)); |
339 | MOCK_METHOD2(runBeforeSkippedPass, void(StringRef PassID, llvm::Any)); |
340 | MOCK_METHOD2(runBeforeNonSkippedPass, void(StringRef PassID, llvm::Any)); |
341 | MOCK_METHOD3(runAfterPass, |
342 | void(StringRef PassID, llvm::Any, const PreservedAnalyses &PA)); |
343 | MOCK_METHOD2(runAfterPassInvalidated, |
344 | void(StringRef PassID, const PreservedAnalyses &PA)); |
345 | MOCK_METHOD2(runBeforeAnalysis, void(StringRef PassID, llvm::Any)); |
346 | MOCK_METHOD2(runAfterAnalysis, void(StringRef PassID, llvm::Any)); |
347 | |
348 | void registerPassInstrumentation() { |
349 | Callbacks.registerShouldRunOptionalPassCallback( |
350 | C: [this](StringRef P, llvm::Any IR) { |
351 | return this->runBeforePass(gmock_a0: P, gmock_a1: IR); |
352 | }); |
353 | Callbacks.registerBeforeSkippedPassCallback( |
354 | C: [this](StringRef P, llvm::Any IR) { |
355 | this->runBeforeSkippedPass(gmock_a0: P, gmock_a1: IR); |
356 | }); |
357 | Callbacks.registerBeforeNonSkippedPassCallback( |
358 | C: [this](StringRef P, llvm::Any IR) { |
359 | this->runBeforeNonSkippedPass(gmock_a0: P, gmock_a1: IR); |
360 | }); |
361 | Callbacks.registerAfterPassCallback( |
362 | C: [this](StringRef P, llvm::Any IR, const PreservedAnalyses &PA) { |
363 | this->runAfterPass(gmock_a0: P, gmock_a1: IR, gmock_a2: PA); |
364 | }); |
365 | Callbacks.registerAfterPassInvalidatedCallback( |
366 | C: [this](StringRef P, const PreservedAnalyses &PA) { |
367 | this->runAfterPassInvalidated(gmock_a0: P, gmock_a1: PA); |
368 | }); |
369 | Callbacks.registerBeforeAnalysisCallback(C: [this](StringRef P, llvm::Any IR) { |
370 | return this->runBeforeAnalysis(gmock_a0: P, gmock_a1: IR); |
371 | }); |
372 | Callbacks.registerAfterAnalysisCallback( |
373 | C: [this](StringRef P, llvm::Any IR) { this->runAfterAnalysis(gmock_a0: P, gmock_a1: IR); }); |
374 | } |
375 | |
376 | void ignoreNonMockPassInstrumentation(StringRef IRName) { |
377 | // Generic EXPECT_CALLs are needed to match instrumentation on unimportant |
378 | // parts of a pipeline that we do not care about (e.g. various passes added |
379 | // by default by PassBuilder - Verifier pass etc). |
380 | // Make sure to avoid ignoring Mock passes/analysis, we definitely want |
381 | // to check these explicitly. |
382 | EXPECT_CALL(*this, |
383 | runBeforePass(Not(HasNameRegex("Mock" )), HasName(IRName))) |
384 | .Times(a_cardinality: AnyNumber()); |
385 | EXPECT_CALL( |
386 | *this, runBeforeSkippedPass(Not(HasNameRegex("Mock" )), HasName(IRName))) |
387 | .Times(a_cardinality: AnyNumber()); |
388 | EXPECT_CALL(*this, runBeforeNonSkippedPass(Not(HasNameRegex("Mock" )), |
389 | HasName(IRName))) |
390 | .Times(a_cardinality: AnyNumber()); |
391 | EXPECT_CALL(*this, |
392 | runAfterPass(Not(HasNameRegex("Mock" )), HasName(IRName), _)) |
393 | .Times(a_cardinality: AnyNumber()); |
394 | EXPECT_CALL(*this, |
395 | runBeforeAnalysis(Not(HasNameRegex("Mock" )), HasName(IRName))) |
396 | .Times(a_cardinality: AnyNumber()); |
397 | EXPECT_CALL(*this, |
398 | runAfterAnalysis(Not(HasNameRegex("Mock" )), HasName(IRName))) |
399 | .Times(a_cardinality: AnyNumber()); |
400 | } |
401 | }; |
402 | |
403 | template <typename IRUnitT> |
404 | using ExtraMockPassHandle = |
405 | std::conditional_t<std::is_same_v<IRUnitT, Loop>, MockPassHandle<LoopNest>, |
406 | MockPassHandle<IRUnitT>>; |
407 | |
408 | template <typename PassManagerT> class PassBuilderCallbacksTest; |
409 | |
410 | /// This test fixture is shared between all the actual tests below and |
411 | /// takes care of setting up appropriate defaults. |
412 | /// |
413 | /// The template specialization serves to extract the IRUnit and AM types from |
414 | /// the given PassManagerT. |
415 | template <typename TestIRUnitT, typename... ExtraPassArgTs, |
416 | typename... ExtraAnalysisArgTs> |
417 | class PassBuilderCallbacksTest<PassManager< |
418 | TestIRUnitT, AnalysisManager<TestIRUnitT, ExtraAnalysisArgTs...>, |
419 | ExtraPassArgTs...>> : public testing::Test { |
420 | protected: |
421 | using IRUnitT = TestIRUnitT; |
422 | using AnalysisManagerT = AnalysisManager<TestIRUnitT, ExtraAnalysisArgTs...>; |
423 | using PassManagerT = |
424 | PassManager<TestIRUnitT, AnalysisManagerT, ExtraPassArgTs...>; |
425 | using AnalysisT = typename MockAnalysisHandle<IRUnitT>::Analysis; |
426 | |
427 | LLVMContext Context; |
428 | std::unique_ptr<Module> M; |
429 | |
430 | MockPassInstrumentationCallbacks CallbacksHandle; |
431 | |
432 | PassBuilder PB; |
433 | ModulePassManager PM; |
434 | LoopAnalysisManager LAM; |
435 | FunctionAnalysisManager FAM; |
436 | CGSCCAnalysisManager CGAM; |
437 | ModuleAnalysisManager AM; |
438 | |
439 | MockPassHandle<IRUnitT> PassHandle; |
440 | ExtraMockPassHandle<IRUnitT> ExtraPassHandle; |
441 | |
442 | MockAnalysisHandle<IRUnitT> AnalysisHandle; |
443 | |
444 | static PreservedAnalyses getAnalysisResult(IRUnitT &U, AnalysisManagerT &AM, |
445 | ExtraAnalysisArgTs &&... Args) { |
446 | (void)AM.template getResult<AnalysisT>( |
447 | U, std::forward<ExtraAnalysisArgTs>(Args)...); |
448 | return PreservedAnalyses::all(); |
449 | } |
450 | |
451 | PassBuilderCallbacksTest() |
452 | : M(parseIR(C&: Context, |
453 | IR: "declare void @bar()\n" |
454 | "define void @foo(i32 %n) {\n" |
455 | "entry:\n" |
456 | " br label %loop\n" |
457 | "loop:\n" |
458 | " %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]\n" |
459 | " %iv.next = add i32 %iv, 1\n" |
460 | " tail call void @bar()\n" |
461 | " %cmp = icmp eq i32 %iv, %n\n" |
462 | " br i1 %cmp, label %exit, label %loop\n" |
463 | "exit:\n" |
464 | " ret void\n" |
465 | "}\n" )), |
466 | CallbacksHandle(), PB(nullptr, PipelineTuningOptions(), std::nullopt, |
467 | &CallbacksHandle.Callbacks), |
468 | PM(), LAM(), FAM(), CGAM(), AM() { |
469 | |
470 | EXPECT_TRUE(&CallbacksHandle.Callbacks == |
471 | PB.getPassInstrumentationCallbacks()); |
472 | |
473 | /// Register a callback for analysis registration. |
474 | /// |
475 | /// The callback is a function taking a reference to an AnalyisManager |
476 | /// object. When called, the callee gets to register its own analyses with |
477 | /// this PassBuilder instance. |
478 | PB.registerAnalysisRegistrationCallback([this](AnalysisManagerT &AM) { |
479 | // Register our mock analysis |
480 | AM.registerPass([this] { return AnalysisHandle.getAnalysis(); }); |
481 | }); |
482 | |
483 | /// Register a callback for pipeline parsing. |
484 | /// |
485 | /// During parsing of a textual pipeline, the PassBuilder will call these |
486 | /// callbacks for each encountered pass name that it does not know. This |
487 | /// includes both simple pass names as well as names of sub-pipelines. In |
488 | /// the latter case, the InnerPipeline is not empty. |
489 | PB.registerPipelineParsingCallback( |
490 | [this](StringRef Name, PassManagerT &PM, |
491 | ArrayRef<PassBuilder::PipelineElement> InnerPipeline) { |
492 | /// Handle parsing of the names of analysis utilities such as |
493 | /// require<test-analysis> and invalidate<test-analysis> for our |
494 | /// analysis mock handle |
495 | if (parseAnalysisUtilityPasses<AnalysisT>("test-analysis" , Name, PM)) |
496 | return true; |
497 | |
498 | /// Parse the name of our pass mock handle |
499 | if (Name == "test-transform" ) { |
500 | PM.addPass(PassHandle.getPass()); |
501 | if (std::is_same<IRUnitT, Loop>::value) |
502 | PM.addPass(ExtraPassHandle.getPass()); |
503 | return true; |
504 | } |
505 | return false; |
506 | }); |
507 | |
508 | /// Register builtin analyses and cross-register the analysis proxies |
509 | PB.registerModuleAnalyses(MAM&: AM); |
510 | PB.registerCGSCCAnalyses(CGAM); |
511 | PB.registerFunctionAnalyses(FAM); |
512 | PB.registerLoopAnalyses(LAM); |
513 | PB.crossRegisterProxies(LAM, FAM, CGAM, MAM&: AM); |
514 | } |
515 | }; |
516 | |
517 | using ModuleCallbacksTest = PassBuilderCallbacksTest<ModulePassManager>; |
518 | using CGSCCCallbacksTest = PassBuilderCallbacksTest<CGSCCPassManager>; |
519 | using FunctionCallbacksTest = PassBuilderCallbacksTest<FunctionPassManager>; |
520 | using LoopCallbacksTest = PassBuilderCallbacksTest<LoopPassManager>; |
521 | |
522 | /// Test parsing of the name of our mock pass for all IRUnits. |
523 | /// |
524 | /// The pass should by default run our mock analysis and then preserve it. |
525 | TEST_F(ModuleCallbacksTest, Passes) { |
526 | EXPECT_CALL(AnalysisHandle, run(HasName("<string>" ), _)); |
527 | EXPECT_CALL(PassHandle, run(HasName("<string>" ), _)) |
528 | .WillOnce(once_action: &getAnalysisResult); |
529 | |
530 | StringRef PipelineText = "test-transform" ; |
531 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
532 | << "Pipeline was: " << PipelineText; |
533 | |
534 | PM.run(IR&: *M, AM); |
535 | } |
536 | |
537 | TEST_F(ModuleCallbacksTest, InstrumentedPasses) { |
538 | EXPECT_CALL(AnalysisHandle, run(HasName("<string>" ), _)); |
539 | EXPECT_CALL(PassHandle, run(HasName("<string>" ), _)) |
540 | .WillOnce(once_action: &getAnalysisResult); |
541 | |
542 | CallbacksHandle.registerPassInstrumentation(); |
543 | // Non-mock instrumentation not specifically mentioned below can be ignored. |
544 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
545 | |
546 | // PassInstrumentation calls should happen in-sequence, in the same order |
547 | // as passes/analyses are scheduled. |
548 | ::testing::Sequence PISequence; |
549 | EXPECT_CALL(CallbacksHandle, runBeforePass(HasNameRegex("MockPassHandle" ), |
550 | HasName("<string>" ))) |
551 | .InSequence(s: PISequence); |
552 | EXPECT_CALL(CallbacksHandle, |
553 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), |
554 | HasName("<string>" ))) |
555 | .InSequence(s: PISequence); |
556 | EXPECT_CALL(CallbacksHandle, |
557 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), |
558 | HasName("<string>" ))) |
559 | .InSequence(s: PISequence); |
560 | EXPECT_CALL( |
561 | CallbacksHandle, |
562 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("<string>" ))) |
563 | .InSequence(s: PISequence); |
564 | EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle" ), |
565 | HasName("<string>" ), _)) |
566 | .InSequence(s: PISequence); |
567 | |
568 | // No passes are skipped, so there should be no calls to |
569 | // runBeforeSkippedPass(). |
570 | EXPECT_CALL( |
571 | CallbacksHandle, |
572 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), HasName("<string>" ))) |
573 | .Times(n: 0); |
574 | |
575 | StringRef PipelineText = "test-transform" ; |
576 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
577 | << "Pipeline was: " << PipelineText; |
578 | |
579 | PM.run(IR&: *M, AM); |
580 | } |
581 | |
582 | TEST_F(ModuleCallbacksTest, InstrumentedSkippedPasses) { |
583 | CallbacksHandle.registerPassInstrumentation(); |
584 | // Non-mock instrumentation run here can safely be ignored. |
585 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
586 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
587 | |
588 | // Skip all passes by returning false. Pass managers and adaptor passes are |
589 | // also passes that observed by the callbacks. |
590 | EXPECT_CALL(CallbacksHandle, runBeforePass(_, _)) |
591 | .WillRepeatedly(action: Return(value: false)); |
592 | |
593 | EXPECT_CALL(CallbacksHandle, |
594 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), _)) |
595 | .Times(n: 3); |
596 | |
597 | EXPECT_CALL(AnalysisHandle, run(HasName("<string>" ), _)).Times(n: 0); |
598 | EXPECT_CALL(PassHandle, run(HasName("<string>" ), _)).Times(n: 0); |
599 | |
600 | // As the pass is skipped there is no nonskippedpass/afterPass, |
601 | // beforeAnalysis/afterAnalysis as well. |
602 | EXPECT_CALL(CallbacksHandle, |
603 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), _)) |
604 | .Times(n: 0); |
605 | EXPECT_CALL(CallbacksHandle, |
606 | runAfterPass(HasNameRegex("MockPassHandle" ), _, _)) |
607 | .Times(n: 0); |
608 | EXPECT_CALL(CallbacksHandle, |
609 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
610 | .Times(n: 0); |
611 | EXPECT_CALL(CallbacksHandle, |
612 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
613 | .Times(n: 0); |
614 | |
615 | // Order is important here. `Adaptor` expectations should be checked first |
616 | // because the its argument contains 'PassManager' (for example: |
617 | // ModuleToFunctionPassAdaptor{{.*}}PassManager{{.*}}). Check |
618 | // `runBeforeNonSkippedPass` and `runAfterPass` to show that they are not |
619 | // skipped. |
620 | // |
621 | // Pass managers are not ignored. |
622 | // 5 = (1) ModulePassManager + (2) FunctionPassMangers + (1) LoopPassManager + |
623 | // (1) CGSCCPassManager |
624 | EXPECT_CALL(CallbacksHandle, |
625 | runBeforeNonSkippedPass(HasNameRegex("PassManager" ), _)) |
626 | .Times(n: 5); |
627 | EXPECT_CALL( |
628 | CallbacksHandle, |
629 | runBeforeNonSkippedPass(HasNameRegex("ModuleToFunctionPassAdaptor" ), _)) |
630 | .Times(n: 1); |
631 | EXPECT_CALL(CallbacksHandle, |
632 | runBeforeNonSkippedPass( |
633 | HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor" ), _)) |
634 | .Times(n: 1); |
635 | EXPECT_CALL( |
636 | CallbacksHandle, |
637 | runBeforeNonSkippedPass(HasNameRegex("CGSCCToFunctionPassAdaptor" ), _)) |
638 | .Times(n: 1); |
639 | EXPECT_CALL( |
640 | CallbacksHandle, |
641 | runBeforeNonSkippedPass(HasNameRegex("FunctionToLoopPassAdaptor" ), _)) |
642 | .Times(n: 1); |
643 | |
644 | // The `runAfterPass` checks are the same as these of |
645 | // `runBeforeNonSkippedPass`. |
646 | EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("PassManager" ), _, _)) |
647 | .Times(n: 5); |
648 | EXPECT_CALL(CallbacksHandle, |
649 | runAfterPass(HasNameRegex("ModuleToFunctionPassAdaptor" ), _, _)) |
650 | .Times(n: 1); |
651 | EXPECT_CALL( |
652 | CallbacksHandle, |
653 | runAfterPass(HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor" ), _, _)) |
654 | .Times(n: 1); |
655 | EXPECT_CALL(CallbacksHandle, |
656 | runAfterPass(HasNameRegex("CGSCCToFunctionPassAdaptor" ), _, _)) |
657 | .Times(n: 1); |
658 | EXPECT_CALL(CallbacksHandle, |
659 | runAfterPass(HasNameRegex("FunctionToLoopPassAdaptor" ), _, _)) |
660 | .Times(n: 1); |
661 | |
662 | // Ignore analyses introduced by adaptor passes. |
663 | EXPECT_CALL(CallbacksHandle, |
664 | runBeforeAnalysis(Not(HasNameRegex("MockAnalysisHandle" )), _)) |
665 | .Times(a_cardinality: AnyNumber()); |
666 | EXPECT_CALL(CallbacksHandle, |
667 | runAfterAnalysis(Not(HasNameRegex("MockAnalysisHandle" )), _)) |
668 | .Times(a_cardinality: AnyNumber()); |
669 | |
670 | // Register Funtion and Loop version of "test-transform" for testing |
671 | PB.registerPipelineParsingCallback( |
672 | C: [](StringRef Name, FunctionPassManager &FPM, |
673 | ArrayRef<PassBuilder::PipelineElement>) { |
674 | if (Name == "test-transform" ) { |
675 | FPM.addPass(Pass: MockPassHandle<Function>().getPass()); |
676 | return true; |
677 | } |
678 | return false; |
679 | }); |
680 | PB.registerPipelineParsingCallback( |
681 | C: [](StringRef Name, LoopPassManager &LPM, |
682 | ArrayRef<PassBuilder::PipelineElement>) { |
683 | if (Name == "test-transform" ) { |
684 | LPM.addPass(Pass: MockPassHandle<Loop>().getPass()); |
685 | return true; |
686 | } |
687 | return false; |
688 | }); |
689 | |
690 | StringRef PipelineText = "test-transform,function(test-transform),cgscc(" |
691 | "function(loop(test-transform)))" ; |
692 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
693 | << "Pipeline was: " << PipelineText; |
694 | |
695 | PM.run(IR&: *M, AM); |
696 | } |
697 | |
698 | TEST_F(FunctionCallbacksTest, Passes) { |
699 | EXPECT_CALL(AnalysisHandle, run(HasName("foo" ), _)); |
700 | EXPECT_CALL(PassHandle, run(HasName("foo" ), _)).WillOnce(once_action: &getAnalysisResult); |
701 | |
702 | StringRef PipelineText = "test-transform" ; |
703 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
704 | << "Pipeline was: " << PipelineText; |
705 | PM.run(IR&: *M, AM); |
706 | } |
707 | |
708 | TEST_F(FunctionCallbacksTest, InstrumentedPasses) { |
709 | CallbacksHandle.registerPassInstrumentation(); |
710 | // Non-mock instrumentation not specifically mentioned below can be ignored. |
711 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
712 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
713 | |
714 | EXPECT_CALL(AnalysisHandle, run(HasName("foo" ), _)); |
715 | EXPECT_CALL(PassHandle, run(HasName("foo" ), _)).WillOnce(once_action: &getAnalysisResult); |
716 | |
717 | // PassInstrumentation calls should happen in-sequence, in the same order |
718 | // as passes/analyses are scheduled. |
719 | ::testing::Sequence PISequence; |
720 | EXPECT_CALL(CallbacksHandle, |
721 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("foo" ))) |
722 | .InSequence(s: PISequence); |
723 | EXPECT_CALL( |
724 | CallbacksHandle, |
725 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), HasName("foo" ))) |
726 | .InSequence(s: PISequence); |
727 | EXPECT_CALL( |
728 | CallbacksHandle, |
729 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("foo" ))) |
730 | .InSequence(s: PISequence); |
731 | EXPECT_CALL( |
732 | CallbacksHandle, |
733 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("foo" ))) |
734 | .InSequence(s: PISequence); |
735 | EXPECT_CALL(CallbacksHandle, |
736 | runAfterPass(HasNameRegex("MockPassHandle" ), HasName("foo" ), _)) |
737 | .InSequence(s: PISequence); |
738 | |
739 | // No passes are skipped, so there should be no calls to |
740 | // runBeforeSkippedPass(). |
741 | EXPECT_CALL( |
742 | CallbacksHandle, |
743 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), HasName("foo" ))) |
744 | .Times(n: 0); |
745 | |
746 | // Our mock pass does not invalidate IR. |
747 | EXPECT_CALL(CallbacksHandle, |
748 | runAfterPassInvalidated(HasNameRegex("MockPassHandle" ), _)) |
749 | .Times(n: 0); |
750 | |
751 | StringRef PipelineText = "test-transform" ; |
752 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
753 | << "Pipeline was: " << PipelineText; |
754 | PM.run(IR&: *M, AM); |
755 | } |
756 | |
757 | TEST_F(FunctionCallbacksTest, InstrumentedSkippedPasses) { |
758 | CallbacksHandle.registerPassInstrumentation(); |
759 | // Non-mock instrumentation run here can safely be ignored. |
760 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
761 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
762 | |
763 | // Skip the pass by returning false. |
764 | EXPECT_CALL(CallbacksHandle, |
765 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("foo" ))) |
766 | .WillOnce(once_action: Return(value: false)); |
767 | |
768 | EXPECT_CALL( |
769 | CallbacksHandle, |
770 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), HasName("foo" ))) |
771 | .Times(n: 1); |
772 | |
773 | EXPECT_CALL(AnalysisHandle, run(HasName("foo" ), _)).Times(n: 0); |
774 | EXPECT_CALL(PassHandle, run(HasName("foo" ), _)).Times(n: 0); |
775 | |
776 | // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis |
777 | // as well. |
778 | EXPECT_CALL(CallbacksHandle, |
779 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), _)) |
780 | .Times(n: 0); |
781 | EXPECT_CALL(CallbacksHandle, |
782 | runAfterPass(HasNameRegex("MockPassHandle" ), _, _)) |
783 | .Times(n: 0); |
784 | EXPECT_CALL(CallbacksHandle, |
785 | runAfterPassInvalidated(HasNameRegex("MockPassHandle" ), _)) |
786 | .Times(n: 0); |
787 | EXPECT_CALL(CallbacksHandle, |
788 | runAfterPass(HasNameRegex("MockPassHandle" ), _, _)) |
789 | .Times(n: 0); |
790 | EXPECT_CALL(CallbacksHandle, |
791 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
792 | .Times(n: 0); |
793 | EXPECT_CALL(CallbacksHandle, |
794 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
795 | .Times(n: 0); |
796 | |
797 | StringRef PipelineText = "test-transform" ; |
798 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
799 | << "Pipeline was: " << PipelineText; |
800 | PM.run(IR&: *M, AM); |
801 | } |
802 | |
803 | TEST_F(LoopCallbacksTest, Passes) { |
804 | EXPECT_CALL(AnalysisHandle, run(HasName("loop" ), _, _)); |
805 | EXPECT_CALL(PassHandle, run(HasName("loop" ), _, _, _)) |
806 | .WillOnce(once_action: WithArgs<0, 1, 2>(action: &getAnalysisResult)); |
807 | EXPECT_CALL(ExtraPassHandle, run(HasName("loop" ), _, _, _)); |
808 | |
809 | StringRef PipelineText = "test-transform" ; |
810 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
811 | << "Pipeline was: " << PipelineText; |
812 | PM.run(IR&: *M, AM); |
813 | } |
814 | |
815 | TEST_F(LoopCallbacksTest, InstrumentedPasses) { |
816 | CallbacksHandle.registerPassInstrumentation(); |
817 | // Non-mock instrumentation not specifically mentioned below can be ignored. |
818 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
819 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
820 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "loop" ); |
821 | |
822 | EXPECT_CALL(AnalysisHandle, run(HasName("loop" ), _, _)); |
823 | EXPECT_CALL(PassHandle, run(HasName("loop" ), _, _, _)) |
824 | .WillOnce(once_action: WithArgs<0, 1, 2>(action: &getAnalysisResult)); |
825 | EXPECT_CALL(ExtraPassHandle, run(HasName("loop" ), _, _, _)); |
826 | |
827 | // PassInstrumentation calls should happen in-sequence, in the same order |
828 | // as passes/analyses are scheduled. |
829 | ::testing::Sequence PISequence; |
830 | EXPECT_CALL(CallbacksHandle, |
831 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("loop" ))) |
832 | .InSequence(s: PISequence); |
833 | EXPECT_CALL( |
834 | CallbacksHandle, |
835 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), HasName("loop" ))) |
836 | .InSequence(s: PISequence); |
837 | EXPECT_CALL( |
838 | CallbacksHandle, |
839 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("loop" ))) |
840 | .InSequence(s: PISequence); |
841 | EXPECT_CALL( |
842 | CallbacksHandle, |
843 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("loop" ))) |
844 | .InSequence(s: PISequence); |
845 | EXPECT_CALL(CallbacksHandle, |
846 | runAfterPass(HasNameRegex("MockPassHandle" ), HasName("loop" ), _)) |
847 | .InSequence(s: PISequence); |
848 | |
849 | EXPECT_CALL(CallbacksHandle, |
850 | runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>" ), |
851 | HasName("loop" ))) |
852 | .InSequence(s: PISequence); |
853 | EXPECT_CALL(CallbacksHandle, |
854 | runBeforeNonSkippedPass( |
855 | HasNameRegex("MockPassHandle<.*LoopNest>" ), HasName("loop" ))) |
856 | .InSequence(s: PISequence); |
857 | EXPECT_CALL(CallbacksHandle, |
858 | runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>" ), |
859 | HasName("loop" ), _)) |
860 | .InSequence(s: PISequence); |
861 | |
862 | // Our mock pass does not invalidate IR. |
863 | EXPECT_CALL(CallbacksHandle, |
864 | runAfterPassInvalidated(HasNameRegex("MockPassHandle" ), _)) |
865 | .Times(n: 0); |
866 | |
867 | // No passes are skipped, so there should be no calls to |
868 | // runBeforeSkippedPass(). |
869 | EXPECT_CALL( |
870 | CallbacksHandle, |
871 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), HasName("loop" ))) |
872 | .Times(n: 0); |
873 | |
874 | StringRef PipelineText = "test-transform" ; |
875 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
876 | << "Pipeline was: " << PipelineText; |
877 | PM.run(IR&: *M, AM); |
878 | } |
879 | |
880 | TEST_F(LoopCallbacksTest, InstrumentedInvalidatingPasses) { |
881 | CallbacksHandle.registerPassInstrumentation(); |
882 | // Non-mock instrumentation not specifically mentioned below can be ignored. |
883 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
884 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
885 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "loop" ); |
886 | |
887 | EXPECT_CALL(AnalysisHandle, run(HasName("loop" ), _, _)); |
888 | EXPECT_CALL(PassHandle, run(HasName("loop" ), _, _, _)) |
889 | .WillOnce(once_action: DoAll(action: WithArgs<0, 1, 2, 3>(action: &PassHandle.invalidateLoop), |
890 | action: WithArgs<0, 1, 2>(action: &getAnalysisResult))); |
891 | |
892 | // PassInstrumentation calls should happen in-sequence, in the same order |
893 | // as passes/analyses are scheduled. |
894 | ::testing::Sequence PISequence; |
895 | EXPECT_CALL(CallbacksHandle, |
896 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("loop" ))) |
897 | .InSequence(s: PISequence); |
898 | EXPECT_CALL( |
899 | CallbacksHandle, |
900 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), HasName("loop" ))) |
901 | .InSequence(s: PISequence); |
902 | EXPECT_CALL( |
903 | CallbacksHandle, |
904 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("loop" ))) |
905 | .InSequence(s: PISequence); |
906 | EXPECT_CALL( |
907 | CallbacksHandle, |
908 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("loop" ))) |
909 | .InSequence(s: PISequence); |
910 | EXPECT_CALL(CallbacksHandle, |
911 | runAfterPassInvalidated(HasNameRegex("MockPassHandle" ), _)) |
912 | .InSequence(s: PISequence); |
913 | EXPECT_CALL(CallbacksHandle, |
914 | runAfterPassInvalidated(HasNameRegex("^PassManager" ), _)) |
915 | .InSequence(s: PISequence); |
916 | |
917 | // Our mock pass invalidates IR, thus normal runAfterPass is never called. |
918 | EXPECT_CALL(CallbacksHandle, |
919 | runAfterPass(HasNameRegex("MockPassHandle" ), HasName("loop" ), _)) |
920 | .Times(n: 0); |
921 | |
922 | StringRef PipelineText = "test-transform" ; |
923 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
924 | << "Pipeline was: " << PipelineText; |
925 | PM.run(IR&: *M, AM); |
926 | } |
927 | |
928 | TEST_F(LoopCallbacksTest, InstrumentedInvalidatingLoopNestPasses) { |
929 | CallbacksHandle.registerPassInstrumentation(); |
930 | // Non-mock instrumentation not specifically mentioned below can be ignored. |
931 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
932 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
933 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "loop" ); |
934 | |
935 | EXPECT_CALL(AnalysisHandle, run(HasName("loop" ), _, _)); |
936 | EXPECT_CALL(PassHandle, run(HasName("loop" ), _, _, _)) |
937 | .WillOnce(once_action: WithArgs<0, 1, 2>(action: &getAnalysisResult)); |
938 | EXPECT_CALL(ExtraPassHandle, run(HasName("loop" ), _, _, _)) |
939 | .WillOnce(once_action: DoAll(action: &ExtraPassHandle.invalidateLoopNest, |
940 | action: [&](LoopNest &, LoopAnalysisManager &, |
941 | LoopStandardAnalysisResults &, |
942 | LPMUpdater &) { return PreservedAnalyses::all(); })); |
943 | |
944 | // PassInstrumentation calls should happen in-sequence, in the same order |
945 | // as passes/analyses are scheduled. |
946 | ::testing::Sequence PISequence; |
947 | EXPECT_CALL(CallbacksHandle, |
948 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("loop" ))) |
949 | .InSequence(s: PISequence); |
950 | EXPECT_CALL( |
951 | CallbacksHandle, |
952 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), HasName("loop" ))) |
953 | .InSequence(s: PISequence); |
954 | EXPECT_CALL( |
955 | CallbacksHandle, |
956 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("loop" ))) |
957 | .InSequence(s: PISequence); |
958 | EXPECT_CALL( |
959 | CallbacksHandle, |
960 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("loop" ))) |
961 | .InSequence(s: PISequence); |
962 | EXPECT_CALL(CallbacksHandle, |
963 | runAfterPass(HasNameRegex("MockPassHandle" ), HasName("loop" ), _)) |
964 | .InSequence(s: PISequence); |
965 | |
966 | EXPECT_CALL(CallbacksHandle, |
967 | runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>" ), |
968 | HasName("loop" ))) |
969 | .InSequence(s: PISequence); |
970 | EXPECT_CALL(CallbacksHandle, |
971 | runBeforeNonSkippedPass( |
972 | HasNameRegex("MockPassHandle<.*LoopNest>" ), HasName("loop" ))) |
973 | .InSequence(s: PISequence); |
974 | EXPECT_CALL( |
975 | CallbacksHandle, |
976 | runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*LoopNest>" ), _)) |
977 | .InSequence(s: PISequence); |
978 | |
979 | EXPECT_CALL(CallbacksHandle, |
980 | runAfterPassInvalidated(HasNameRegex("^PassManager" ), _)) |
981 | .InSequence(s: PISequence); |
982 | |
983 | // Our mock pass invalidates IR, thus normal runAfterPass is never called. |
984 | EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( |
985 | HasNameRegex("MockPassHandle<.*Loop>" ), _)) |
986 | .Times(n: 0); |
987 | EXPECT_CALL(CallbacksHandle, |
988 | runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>" ), |
989 | HasName("loop" ), _)) |
990 | .Times(n: 0); |
991 | |
992 | StringRef PipelineText = "test-transform" ; |
993 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
994 | << "Pipeline was: " << PipelineText; |
995 | PM.run(IR&: *M, AM); |
996 | } |
997 | |
998 | TEST_F(LoopCallbacksTest, InstrumentedSkippedPasses) { |
999 | CallbacksHandle.registerPassInstrumentation(); |
1000 | // Non-mock instrumentation run here can safely be ignored. |
1001 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
1002 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
1003 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "loop" ); |
1004 | |
1005 | // Skip the pass by returning false. |
1006 | EXPECT_CALL( |
1007 | CallbacksHandle, |
1008 | runBeforePass(HasNameRegex("MockPassHandle<.*Loop>" ), HasName("loop" ))) |
1009 | .WillOnce(once_action: Return(value: false)); |
1010 | |
1011 | EXPECT_CALL(CallbacksHandle, |
1012 | runBeforeSkippedPass(HasNameRegex("MockPassHandle<.*Loop>" ), |
1013 | HasName("loop" ))) |
1014 | .Times(n: 1); |
1015 | |
1016 | EXPECT_CALL(CallbacksHandle, |
1017 | runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>" ), |
1018 | HasName("loop" ))) |
1019 | .WillOnce(once_action: Return(value: false)); |
1020 | |
1021 | EXPECT_CALL(CallbacksHandle, |
1022 | runBeforeSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>" ), |
1023 | HasName("loop" ))) |
1024 | .Times(n: 1); |
1025 | |
1026 | EXPECT_CALL(AnalysisHandle, run(HasName("loop" ), _, _)).Times(n: 0); |
1027 | EXPECT_CALL(PassHandle, run(HasName("loop" ), _, _, _)).Times(n: 0); |
1028 | EXPECT_CALL(ExtraPassHandle, run(HasName("loop" ), _, _, _)).Times(n: 0); |
1029 | |
1030 | // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis |
1031 | // as well. |
1032 | EXPECT_CALL(CallbacksHandle, runBeforeNonSkippedPass( |
1033 | HasNameRegex("MockPassHandle<.*Loop>" ), _)) |
1034 | .Times(n: 0); |
1035 | EXPECT_CALL(CallbacksHandle, |
1036 | runAfterPass(HasNameRegex("MockPassHandle<.*Loop>" ), _, _)) |
1037 | .Times(n: 0); |
1038 | EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( |
1039 | HasNameRegex("MockPassHandle<.*Loop>" ), _)) |
1040 | .Times(n: 0); |
1041 | EXPECT_CALL( |
1042 | CallbacksHandle, |
1043 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>" ), _)) |
1044 | .Times(n: 0); |
1045 | EXPECT_CALL(CallbacksHandle, |
1046 | runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>" ), _, _)) |
1047 | .Times(n: 0); |
1048 | EXPECT_CALL( |
1049 | CallbacksHandle, |
1050 | runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*LoopNest>" ), _)) |
1051 | .Times(n: 0); |
1052 | EXPECT_CALL(CallbacksHandle, |
1053 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
1054 | .Times(n: 0); |
1055 | EXPECT_CALL(CallbacksHandle, |
1056 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
1057 | .Times(n: 0); |
1058 | |
1059 | StringRef PipelineText = "test-transform" ; |
1060 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1061 | << "Pipeline was: " << PipelineText; |
1062 | PM.run(IR&: *M, AM); |
1063 | } |
1064 | |
1065 | TEST_F(CGSCCCallbacksTest, Passes) { |
1066 | EXPECT_CALL(AnalysisHandle, run(HasName("(foo)" ), _, _)); |
1067 | EXPECT_CALL(PassHandle, run(HasName("(foo)" ), _, _, _)) |
1068 | .WillOnce(once_action: WithArgs<0, 1, 2>(action: &getAnalysisResult)); |
1069 | |
1070 | StringRef PipelineText = "test-transform" ; |
1071 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1072 | << "Pipeline was: " << PipelineText; |
1073 | PM.run(IR&: *M, AM); |
1074 | } |
1075 | |
1076 | TEST_F(CGSCCCallbacksTest, InstrumentedPasses) { |
1077 | CallbacksHandle.registerPassInstrumentation(); |
1078 | // Non-mock instrumentation not specifically mentioned below can be ignored. |
1079 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
1080 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
1081 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "(foo)" ); |
1082 | |
1083 | EXPECT_CALL(AnalysisHandle, run(HasName("(foo)" ), _, _)); |
1084 | EXPECT_CALL(PassHandle, run(HasName("(foo)" ), _, _, _)) |
1085 | .WillOnce(once_action: WithArgs<0, 1, 2>(action: &getAnalysisResult)); |
1086 | |
1087 | // PassInstrumentation calls should happen in-sequence, in the same order |
1088 | // as passes/analyses are scheduled. |
1089 | ::testing::Sequence PISequence; |
1090 | EXPECT_CALL(CallbacksHandle, |
1091 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ))) |
1092 | .InSequence(s: PISequence); |
1093 | EXPECT_CALL( |
1094 | CallbacksHandle, |
1095 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ))) |
1096 | .InSequence(s: PISequence); |
1097 | EXPECT_CALL( |
1098 | CallbacksHandle, |
1099 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("(foo)" ))) |
1100 | .InSequence(s: PISequence); |
1101 | EXPECT_CALL( |
1102 | CallbacksHandle, |
1103 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("(foo)" ))) |
1104 | .InSequence(s: PISequence); |
1105 | EXPECT_CALL(CallbacksHandle, |
1106 | runAfterPass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ), _)) |
1107 | .InSequence(s: PISequence); |
1108 | |
1109 | // Our mock pass does not invalidate IR. |
1110 | EXPECT_CALL(CallbacksHandle, |
1111 | runAfterPassInvalidated(HasNameRegex("MockPassHandle" ), _)) |
1112 | .Times(n: 0); |
1113 | |
1114 | // No passes are skipped, so there should be no calls to |
1115 | // runBeforeSkippedPass(). |
1116 | EXPECT_CALL( |
1117 | CallbacksHandle, |
1118 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ))) |
1119 | .Times(n: 0); |
1120 | |
1121 | StringRef PipelineText = "test-transform" ; |
1122 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1123 | << "Pipeline was: " << PipelineText; |
1124 | PM.run(IR&: *M, AM); |
1125 | } |
1126 | |
1127 | TEST_F(CGSCCCallbacksTest, InstrumentedInvalidatingPasses) { |
1128 | CallbacksHandle.registerPassInstrumentation(); |
1129 | // Non-mock instrumentation not specifically mentioned below can be ignored. |
1130 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
1131 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
1132 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "(foo)" ); |
1133 | |
1134 | EXPECT_CALL(AnalysisHandle, run(HasName("(foo)" ), _, _)); |
1135 | EXPECT_CALL(PassHandle, run(HasName("(foo)" ), _, _, _)) |
1136 | .WillOnce(once_action: DoAll(action: WithArgs<0, 1, 2, 3>(action: &PassHandle.invalidateSCC), |
1137 | action: WithArgs<0, 1, 2>(action: &getAnalysisResult))); |
1138 | |
1139 | // PassInstrumentation calls should happen in-sequence, in the same order |
1140 | // as passes/analyses are scheduled. |
1141 | ::testing::Sequence PISequence; |
1142 | EXPECT_CALL(CallbacksHandle, |
1143 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ))) |
1144 | .InSequence(s: PISequence); |
1145 | EXPECT_CALL( |
1146 | CallbacksHandle, |
1147 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ))) |
1148 | .InSequence(s: PISequence); |
1149 | EXPECT_CALL( |
1150 | CallbacksHandle, |
1151 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("(foo)" ))) |
1152 | .InSequence(s: PISequence); |
1153 | EXPECT_CALL( |
1154 | CallbacksHandle, |
1155 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("(foo)" ))) |
1156 | .InSequence(s: PISequence); |
1157 | EXPECT_CALL(CallbacksHandle, |
1158 | runAfterPassInvalidated(HasNameRegex("MockPassHandle" ), _)) |
1159 | .InSequence(s: PISequence); |
1160 | EXPECT_CALL(CallbacksHandle, |
1161 | runAfterPassInvalidated(HasNameRegex("^PassManager" ), _)) |
1162 | .InSequence(s: PISequence); |
1163 | |
1164 | // Our mock pass does invalidate IR, thus normal runAfterPass is never called. |
1165 | EXPECT_CALL(CallbacksHandle, |
1166 | runAfterPass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ), _)) |
1167 | .Times(n: 0); |
1168 | |
1169 | StringRef PipelineText = "test-transform" ; |
1170 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1171 | << "Pipeline was: " << PipelineText; |
1172 | PM.run(IR&: *M, AM); |
1173 | } |
1174 | |
1175 | TEST_F(CGSCCCallbacksTest, InstrumentedSkippedPasses) { |
1176 | CallbacksHandle.registerPassInstrumentation(); |
1177 | // Non-mock instrumentation run here can safely be ignored. |
1178 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "<string>" ); |
1179 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "foo" ); |
1180 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "(foo)" ); |
1181 | |
1182 | // Skip the pass by returning false. |
1183 | EXPECT_CALL(CallbacksHandle, |
1184 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ))) |
1185 | .WillOnce(once_action: Return(value: false)); |
1186 | |
1187 | EXPECT_CALL( |
1188 | CallbacksHandle, |
1189 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), HasName("(foo)" ))) |
1190 | .Times(n: 1); |
1191 | |
1192 | // neither Analysis nor Pass are called. |
1193 | EXPECT_CALL(AnalysisHandle, run(HasName("(foo)" ), _, _)).Times(n: 0); |
1194 | EXPECT_CALL(PassHandle, run(HasName("(foo)" ), _, _, _)).Times(n: 0); |
1195 | |
1196 | // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis |
1197 | // as well. |
1198 | EXPECT_CALL(CallbacksHandle, |
1199 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), _)) |
1200 | .Times(n: 0); |
1201 | EXPECT_CALL(CallbacksHandle, |
1202 | runAfterPass(HasNameRegex("MockPassHandle" ), _, _)) |
1203 | .Times(n: 0); |
1204 | EXPECT_CALL(CallbacksHandle, |
1205 | runAfterPassInvalidated(HasNameRegex("MockPassHandle" ), _)) |
1206 | .Times(n: 0); |
1207 | EXPECT_CALL(CallbacksHandle, |
1208 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
1209 | .Times(n: 0); |
1210 | EXPECT_CALL(CallbacksHandle, |
1211 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
1212 | .Times(n: 0); |
1213 | |
1214 | StringRef PipelineText = "test-transform" ; |
1215 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1216 | << "Pipeline was: " << PipelineText; |
1217 | PM.run(IR&: *M, AM); |
1218 | } |
1219 | |
1220 | /// Test parsing of the names of analysis utilities for our mock analysis |
1221 | /// for all IRUnits. |
1222 | /// |
1223 | /// We first require<>, then invalidate<> it, expecting the analysis to be run |
1224 | /// once and subsequently invalidated. |
1225 | TEST_F(ModuleCallbacksTest, AnalysisUtilities) { |
1226 | EXPECT_CALL(AnalysisHandle, run(HasName("<string>" ), _)); |
1227 | EXPECT_CALL(AnalysisHandle, invalidate(HasName("<string>" ), _, _)); |
1228 | |
1229 | StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>" ; |
1230 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1231 | << "Pipeline was: " << PipelineText; |
1232 | PM.run(IR&: *M, AM); |
1233 | } |
1234 | |
1235 | TEST_F(CGSCCCallbacksTest, PassUtilities) { |
1236 | EXPECT_CALL(AnalysisHandle, run(HasName("(foo)" ), _, _)); |
1237 | EXPECT_CALL(AnalysisHandle, invalidate(HasName("(foo)" ), _, _)); |
1238 | |
1239 | StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>" ; |
1240 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1241 | << "Pipeline was: " << PipelineText; |
1242 | PM.run(IR&: *M, AM); |
1243 | } |
1244 | |
1245 | TEST_F(FunctionCallbacksTest, AnalysisUtilities) { |
1246 | EXPECT_CALL(AnalysisHandle, run(HasName("foo" ), _)); |
1247 | EXPECT_CALL(AnalysisHandle, invalidate(HasName("foo" ), _, _)); |
1248 | |
1249 | StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>" ; |
1250 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1251 | << "Pipeline was: " << PipelineText; |
1252 | PM.run(IR&: *M, AM); |
1253 | } |
1254 | |
1255 | TEST_F(LoopCallbacksTest, PassUtilities) { |
1256 | EXPECT_CALL(AnalysisHandle, run(HasName("loop" ), _, _)); |
1257 | EXPECT_CALL(AnalysisHandle, invalidate(HasName("loop" ), _, _)); |
1258 | |
1259 | StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>" ; |
1260 | |
1261 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1262 | << "Pipeline was: " << PipelineText; |
1263 | PM.run(IR&: *M, AM); |
1264 | } |
1265 | |
1266 | /// Test parsing of the top-level pipeline. |
1267 | /// |
1268 | /// The ParseTopLevelPipeline callback takes over parsing of the entire pipeline |
1269 | /// from PassBuilder if it encounters an unknown pipeline entry at the top level |
1270 | /// (i.e., the first entry on the pipeline). |
1271 | /// This test parses a pipeline named 'another-pipeline', whose only elements |
1272 | /// may be the test-transform pass or the analysis utilities |
1273 | TEST_F(ModuleCallbacksTest, ParseTopLevelPipeline) { |
1274 | PB.registerParseTopLevelPipelineCallback( |
1275 | C: [this](ModulePassManager &MPM, |
1276 | ArrayRef<PassBuilder::PipelineElement> Pipeline) { |
1277 | auto &FirstName = Pipeline.front().Name; |
1278 | auto &InnerPipeline = Pipeline.front().InnerPipeline; |
1279 | if (FirstName == "another-pipeline" ) { |
1280 | for (auto &E : InnerPipeline) { |
1281 | if (parseAnalysisUtilityPasses<AnalysisT>(AnalysisName: "test-analysis" , PipelineName: E.Name, |
1282 | PM)) |
1283 | continue; |
1284 | |
1285 | if (E.Name == "test-transform" ) { |
1286 | PM.addPass(Pass: PassHandle.getPass()); |
1287 | continue; |
1288 | } |
1289 | return false; |
1290 | } |
1291 | } |
1292 | return true; |
1293 | }); |
1294 | |
1295 | EXPECT_CALL(AnalysisHandle, run(HasName("<string>" ), _)); |
1296 | EXPECT_CALL(PassHandle, run(HasName("<string>" ), _)) |
1297 | .WillOnce(once_action: &getAnalysisResult); |
1298 | EXPECT_CALL(AnalysisHandle, invalidate(HasName("<string>" ), _, _)); |
1299 | |
1300 | StringRef PipelineText = |
1301 | "another-pipeline(test-transform,invalidate<test-analysis>)" ; |
1302 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
1303 | << "Pipeline was: " << PipelineText; |
1304 | PM.run(IR&: *M, AM); |
1305 | |
1306 | /// Test the negative case |
1307 | PipelineText = "another-pipeline(instcombine)" ; |
1308 | ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Failed()) |
1309 | << "Pipeline was: " << PipelineText; |
1310 | } |
1311 | } // end anonymous namespace |
1312 | |