1 | //===- unittests/MIR/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/Analysis/CGSCCPassManager.h" |
10 | #include "llvm/Analysis/LoopAnalysisManager.h" |
11 | #include "llvm/CodeGen/FreeMachineFunction.h" |
12 | #include "llvm/MC/TargetRegistry.h" |
13 | #include "llvm/Target/TargetMachine.h" |
14 | #include "llvm/Testing/Support/Error.h" |
15 | #include <functional> |
16 | #include <gmock/gmock.h> |
17 | #include <gtest/gtest.h> |
18 | #include <llvm/ADT/Any.h> |
19 | #include <llvm/AsmParser/Parser.h> |
20 | #include <llvm/CodeGen/MIRParser/MIRParser.h> |
21 | #include <llvm/CodeGen/MachineFunction.h> |
22 | #include <llvm/CodeGen/MachineModuleInfo.h> |
23 | #include <llvm/CodeGen/MachinePassManager.h> |
24 | #include <llvm/IR/LLVMContext.h> |
25 | #include <llvm/IR/PassInstrumentation.h> |
26 | #include <llvm/IR/PassManager.h> |
27 | #include <llvm/Passes/PassBuilder.h> |
28 | #include <llvm/Support/Regex.h> |
29 | #include <llvm/Support/SourceMgr.h> |
30 | #include <llvm/Support/TargetSelect.h> |
31 | |
32 | using namespace llvm; |
33 | |
34 | namespace { |
35 | using testing::_; |
36 | using testing::AnyNumber; |
37 | using testing::DoAll; |
38 | using testing::Not; |
39 | using testing::Return; |
40 | using testing::WithArgs; |
41 | |
42 | StringRef MIRString = R"MIR( |
43 | --- | |
44 | define void @test() { |
45 | ret void |
46 | } |
47 | ... |
48 | --- |
49 | name: test |
50 | body: | |
51 | bb.0 (%ir-block.0): |
52 | RET64 |
53 | ... |
54 | )MIR" ; |
55 | |
56 | /// Helper for HasName matcher that returns getName both for IRUnit and |
57 | /// for IRUnit pointer wrapper into llvm::Any (wrapped by PassInstrumentation). |
58 | template <typename IRUnitT> std::string getName(const IRUnitT &IR) { |
59 | return std::string(IR.getName()); |
60 | } |
61 | |
62 | template <> std::string getName(const StringRef &name) { |
63 | return std::string(name); |
64 | } |
65 | |
66 | template <> std::string getName(const Any &WrappedIR) { |
67 | if (const auto *const *M = llvm::any_cast<const Module *>(Value: &WrappedIR)) |
68 | return (*M)->getName().str(); |
69 | if (const auto *const *F = llvm::any_cast<const Function *>(Value: &WrappedIR)) |
70 | return (*F)->getName().str(); |
71 | if (const auto *const *MF = |
72 | llvm::any_cast<const MachineFunction *>(Value: &WrappedIR)) |
73 | return (*MF)->getName().str(); |
74 | return "<UNKNOWN>" ; |
75 | } |
76 | /// Define a custom matcher for objects which support a 'getName' method. |
77 | /// |
78 | /// LLVM often has IR objects or analysis objects which expose a name |
79 | /// and in tests it is convenient to match these by name for readability. |
80 | /// Usually, this name is either a StringRef or a plain std::string. This |
81 | /// matcher supports any type exposing a getName() method of this form whose |
82 | /// return value is compatible with an std::ostream. For StringRef, this uses |
83 | /// the shift operator defined above. |
84 | /// |
85 | /// It should be used as: |
86 | /// |
87 | /// HasName("my_function") |
88 | /// |
89 | /// No namespace or other qualification is required. |
90 | MATCHER_P(HasName, Name, "" ) { |
91 | *result_listener << "has name '" << getName(arg) << "'" ; |
92 | return Name == getName(arg); |
93 | } |
94 | |
95 | MATCHER_P(HasNameRegex, Name, "" ) { |
96 | *result_listener << "has name '" << getName(arg) << "'" ; |
97 | llvm::Regex r(Name); |
98 | return r.match(String: getName(arg)); |
99 | } |
100 | |
101 | struct MockPassInstrumentationCallbacks { |
102 | MockPassInstrumentationCallbacks() { |
103 | ON_CALL(*this, runBeforePass(_, _)).WillByDefault(action: Return(value: true)); |
104 | } |
105 | MOCK_METHOD2(runBeforePass, bool(StringRef PassID, llvm::Any)); |
106 | MOCK_METHOD2(runBeforeSkippedPass, void(StringRef PassID, llvm::Any)); |
107 | MOCK_METHOD2(runBeforeNonSkippedPass, void(StringRef PassID, llvm::Any)); |
108 | MOCK_METHOD3(runAfterPass, |
109 | void(StringRef PassID, llvm::Any, const PreservedAnalyses &PA)); |
110 | MOCK_METHOD2(runAfterPassInvalidated, |
111 | void(StringRef PassID, const PreservedAnalyses &PA)); |
112 | MOCK_METHOD2(runBeforeAnalysis, void(StringRef PassID, llvm::Any)); |
113 | MOCK_METHOD2(runAfterAnalysis, void(StringRef PassID, llvm::Any)); |
114 | |
115 | void registerPassInstrumentation(PassInstrumentationCallbacks &Callbacks) { |
116 | Callbacks.registerShouldRunOptionalPassCallback( |
117 | C: [this](StringRef P, llvm::Any IR) { |
118 | return this->runBeforePass(gmock_a0: P, gmock_a1: IR); |
119 | }); |
120 | Callbacks.registerBeforeSkippedPassCallback( |
121 | C: [this](StringRef P, llvm::Any IR) { |
122 | this->runBeforeSkippedPass(gmock_a0: P, gmock_a1: IR); |
123 | }); |
124 | Callbacks.registerBeforeNonSkippedPassCallback( |
125 | C: [this](StringRef P, llvm::Any IR) { |
126 | this->runBeforeNonSkippedPass(gmock_a0: P, gmock_a1: IR); |
127 | }); |
128 | Callbacks.registerAfterPassCallback( |
129 | C: [this](StringRef P, llvm::Any IR, const PreservedAnalyses &PA) { |
130 | this->runAfterPass(gmock_a0: P, gmock_a1: IR, gmock_a2: PA); |
131 | }); |
132 | Callbacks.registerAfterPassInvalidatedCallback( |
133 | C: [this](StringRef P, const PreservedAnalyses &PA) { |
134 | this->runAfterPassInvalidated(gmock_a0: P, gmock_a1: PA); |
135 | }); |
136 | Callbacks.registerBeforeAnalysisCallback(C: [this](StringRef P, llvm::Any IR) { |
137 | return this->runBeforeAnalysis(gmock_a0: P, gmock_a1: IR); |
138 | }); |
139 | Callbacks.registerAfterAnalysisCallback( |
140 | C: [this](StringRef P, llvm::Any IR) { this->runAfterAnalysis(gmock_a0: P, gmock_a1: IR); }); |
141 | } |
142 | |
143 | void ignoreNonMockPassInstrumentation(StringRef IRName) { |
144 | // Generic EXPECT_CALLs are needed to match instrumentation on unimportant |
145 | // parts of a pipeline that we do not care about (e.g. various passes added |
146 | // by default by PassBuilder - Verifier pass etc). |
147 | // Make sure to avoid ignoring Mock passes/analysis, we definitely want |
148 | // to check these explicitly. |
149 | EXPECT_CALL(*this, |
150 | runBeforePass(Not(HasNameRegex("Mock" )), HasName(IRName))) |
151 | .Times(a_cardinality: AnyNumber()) |
152 | .WillRepeatedly(action: Return(value: false)); |
153 | EXPECT_CALL( |
154 | *this, runBeforeSkippedPass(Not(HasNameRegex("Mock" )), HasName(IRName))) |
155 | .Times(a_cardinality: AnyNumber()); |
156 | EXPECT_CALL(*this, runBeforeNonSkippedPass(Not(HasNameRegex("Mock" )), |
157 | HasName(IRName))) |
158 | .Times(a_cardinality: AnyNumber()); |
159 | EXPECT_CALL(*this, |
160 | runAfterPass(Not(HasNameRegex("Mock" )), HasName(IRName), _)) |
161 | .Times(a_cardinality: AnyNumber()); |
162 | EXPECT_CALL(*this, |
163 | runBeforeAnalysis(Not(HasNameRegex("Mock" )), HasName(IRName))) |
164 | .Times(a_cardinality: AnyNumber()); |
165 | EXPECT_CALL(*this, |
166 | runAfterAnalysis(Not(HasNameRegex("Mock" )), HasName(IRName))) |
167 | .Times(a_cardinality: AnyNumber()); |
168 | } |
169 | }; |
170 | |
171 | template <typename DerivedT> class MockAnalysisHandleBase { |
172 | public: |
173 | class Analysis : public AnalysisInfoMixin<Analysis> { |
174 | friend AnalysisInfoMixin<Analysis>; |
175 | friend MockAnalysisHandleBase; |
176 | static AnalysisKey Key; |
177 | |
178 | DerivedT *Handle; |
179 | |
180 | Analysis(DerivedT &Handle) : Handle(&Handle) { |
181 | static_assert(std::is_base_of<MockAnalysisHandleBase, DerivedT>::value, |
182 | "Must pass the derived type to this template!" ); |
183 | } |
184 | |
185 | public: |
186 | class Result { |
187 | friend MockAnalysisHandleBase; |
188 | |
189 | DerivedT *Handle; |
190 | |
191 | Result(DerivedT &Handle) : Handle(&Handle) {} |
192 | |
193 | public: |
194 | // Forward invalidation events to the mock handle. |
195 | bool invalidate(MachineFunction &IR, const PreservedAnalyses &PA, |
196 | MachineFunctionAnalysisManager::Invalidator &Inv) { |
197 | return Handle->invalidate(IR, PA, Inv); |
198 | } |
199 | }; |
200 | |
201 | Result run(MachineFunction &IR, MachineFunctionAnalysisManager &AM) { |
202 | return Handle->run(IR, AM); |
203 | } |
204 | }; |
205 | |
206 | Analysis getAnalysis() { return Analysis(static_cast<DerivedT &>(*this)); } |
207 | typename Analysis::Result getResult() { |
208 | return typename Analysis::Result(static_cast<DerivedT &>(*this)); |
209 | } |
210 | static StringRef getName() { return llvm::getTypeName<DerivedT>(); } |
211 | |
212 | protected: |
213 | // FIXME: MSVC seems unable to handle a lambda argument to Invoke from within |
214 | // the template, so we use a boring static function. |
215 | static bool |
216 | invalidateCallback(MachineFunction &IR, const PreservedAnalyses &PA, |
217 | MachineFunctionAnalysisManager::Invalidator &Inv) { |
218 | auto PAC = PA.template getChecker<Analysis>(); |
219 | return !PAC.preserved() && |
220 | !PAC.template preservedSet<AllAnalysesOn<MachineFunction>>(); |
221 | } |
222 | |
223 | /// Derived classes should call this in their constructor to set up default |
224 | /// mock actions. (We can't do this in our constructor because this has to |
225 | /// run after the DerivedT is constructed.) |
226 | void setDefaults() { |
227 | ON_CALL(static_cast<DerivedT &>(*this), run(_, _)) |
228 | .WillByDefault(Return(this->getResult())); |
229 | ON_CALL(static_cast<DerivedT &>(*this), invalidate(_, _, _)) |
230 | .WillByDefault(&invalidateCallback); |
231 | } |
232 | }; |
233 | |
234 | template <typename DerivedT> class MockPassHandleBase { |
235 | public: |
236 | class Pass : public PassInfoMixin<Pass> { |
237 | friend MockPassHandleBase; |
238 | |
239 | DerivedT *Handle; |
240 | |
241 | Pass(DerivedT &Handle) : Handle(&Handle) { |
242 | static_assert(std::is_base_of<MockPassHandleBase, DerivedT>::value, |
243 | "Must pass the derived type to this template!" ); |
244 | } |
245 | |
246 | public: |
247 | PreservedAnalyses run(MachineFunction &IR, |
248 | MachineFunctionAnalysisManager &AM) { |
249 | return Handle->run(IR, AM); |
250 | } |
251 | }; |
252 | |
253 | static StringRef getName() { return llvm::getTypeName<DerivedT>(); } |
254 | |
255 | Pass getPass() { return Pass(static_cast<DerivedT &>(*this)); } |
256 | |
257 | protected: |
258 | /// Derived classes should call this in their constructor to set up default |
259 | /// mock actions. (We can't do this in our constructor because this has to |
260 | /// run after the DerivedT is constructed.) |
261 | void setDefaults() { |
262 | ON_CALL(static_cast<DerivedT &>(*this), run(_, _)) |
263 | .WillByDefault(Return(value: PreservedAnalyses::all())); |
264 | } |
265 | }; |
266 | |
267 | struct MockAnalysisHandle : public MockAnalysisHandleBase<MockAnalysisHandle> { |
268 | MOCK_METHOD2(run, Analysis::Result(MachineFunction &, |
269 | MachineFunctionAnalysisManager &)); |
270 | |
271 | MOCK_METHOD3(invalidate, bool(MachineFunction &, const PreservedAnalyses &, |
272 | MachineFunctionAnalysisManager::Invalidator &)); |
273 | |
274 | MockAnalysisHandle() { setDefaults(); } |
275 | }; |
276 | |
277 | template <typename DerivedT> |
278 | AnalysisKey MockAnalysisHandleBase<DerivedT>::Analysis::Key; |
279 | |
280 | class MockPassHandle : public MockPassHandleBase<MockPassHandle> { |
281 | public: |
282 | MOCK_METHOD2(run, PreservedAnalyses(MachineFunction &, |
283 | MachineFunctionAnalysisManager &)); |
284 | |
285 | MockPassHandle() { setDefaults(); } |
286 | }; |
287 | |
288 | class MachineFunctionCallbacksTest : public testing::Test { |
289 | protected: |
290 | static void SetUpTestCase() { |
291 | InitializeAllTargetInfos(); |
292 | InitializeAllTargets(); |
293 | InitializeAllTargetMCs(); |
294 | } |
295 | |
296 | LLVMContext Context; |
297 | |
298 | std::unique_ptr<LLVMTargetMachine> TM; |
299 | std::unique_ptr<MachineModuleInfo> MMI; |
300 | |
301 | std::unique_ptr<Module> M; |
302 | |
303 | PassInstrumentationCallbacks PIC; |
304 | std::unique_ptr<PassBuilder> PB; |
305 | ModulePassManager MPM; |
306 | MachineFunctionAnalysisManager MFAM; |
307 | LoopAnalysisManager LAM; |
308 | FunctionAnalysisManager FAM; |
309 | CGSCCAnalysisManager CGAM; |
310 | ModuleAnalysisManager MAM; |
311 | |
312 | MockPassInstrumentationCallbacks CallbacksHandle; |
313 | MockPassHandle PassHandle; |
314 | MockAnalysisHandle AnalysisHandle; |
315 | |
316 | static std::unique_ptr<Module> parseMIR(StringRef MIRCode, |
317 | LLVMContext &Context, |
318 | TargetMachine &TM, |
319 | MachineModuleInfo &MMI) { |
320 | SMDiagnostic Diagnostic; |
321 | std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(InputData: MIRCode); |
322 | std::unique_ptr<MIRParser> MIR = |
323 | createMIRParser(Contents: std::move(MBuffer), Context); |
324 | assert(MIR); |
325 | |
326 | std::unique_ptr<Module> Mod = MIR->parseIRModule(); |
327 | assert(Mod); |
328 | |
329 | // Module identifier is used in tests below. |
330 | Mod->setModuleIdentifier("module" ); |
331 | Mod->setDataLayout(TM.createDataLayout()); |
332 | |
333 | [[maybe_unused]] bool Ret = MIR->parseMachineFunctions(M&: *Mod, MMI); |
334 | assert(!Ret); |
335 | |
336 | return Mod; |
337 | } |
338 | |
339 | static PreservedAnalyses |
340 | getAnalysisResult(MachineFunction &U, MachineFunctionAnalysisManager &MFAM) { |
341 | MFAM.getResult<MockAnalysisHandle::Analysis>(IR&: U); |
342 | return PreservedAnalyses::all(); |
343 | } |
344 | |
345 | void SetUp() override { |
346 | std::string Error; |
347 | auto TripleName = "x86_64-pc-linux-gnu" ; |
348 | auto *T = TargetRegistry::lookupTarget(Triple: TripleName, Error); |
349 | if (!T) |
350 | GTEST_SKIP(); |
351 | TM = std::unique_ptr<LLVMTargetMachine>( |
352 | static_cast<LLVMTargetMachine *>(T->createTargetMachine( |
353 | TT: TripleName, CPU: "" , Features: "" , Options: TargetOptions(), RM: std::nullopt))); |
354 | if (!TM) |
355 | GTEST_SKIP(); |
356 | |
357 | MMI = std::make_unique<MachineModuleInfo>(args: TM.get()); |
358 | M = parseMIR(MIRCode: MIRString, Context, TM&: *TM, MMI&: *MMI); |
359 | PB = std::make_unique<PassBuilder>(args: TM.get(), args: PipelineTuningOptions(), |
360 | args: std::nullopt, args: &PIC); |
361 | |
362 | /// Register a callback for analysis registration. |
363 | /// |
364 | /// The callback is a function taking a reference to an AnalyisManager |
365 | /// object. When called, the callee gets to register its own analyses with |
366 | /// this PassBuilder instance. |
367 | PB->registerAnalysisRegistrationCallback( |
368 | C: [this](MachineFunctionAnalysisManager &AM) { |
369 | // Register our mock analysis |
370 | AM.registerPass(PassBuilder: [this] { return AnalysisHandle.getAnalysis(); }); |
371 | }); |
372 | |
373 | /// Register a callback for pipeline parsing. |
374 | /// |
375 | /// During parsing of a textual pipeline, the PassBuilder will call these |
376 | /// callbacks for each encountered pass name that it does not know. This |
377 | /// includes both simple pass names as well as names of sub-pipelines. In |
378 | /// the latter case, the InnerPipeline is not empty. |
379 | PB->registerPipelineParsingCallback( |
380 | C: [this](StringRef Name, MachineFunctionPassManager &PM, |
381 | ArrayRef<PassBuilder::PipelineElement> InnerPipeline) { |
382 | if (parseAnalysisUtilityPasses<MockAnalysisHandle::Analysis>( |
383 | AnalysisName: "test-analysis" , PipelineName: Name, PM)) |
384 | return true; |
385 | |
386 | /// Parse the name of our pass mock handle |
387 | if (Name == "test-transform" ) { |
388 | PM.addPass(Pass: PassHandle.getPass()); |
389 | return true; |
390 | } |
391 | return false; |
392 | }); |
393 | |
394 | /// Register builtin analyses and cross-register the analysis proxies |
395 | PB->registerModuleAnalyses(MAM); |
396 | PB->registerCGSCCAnalyses(CGAM); |
397 | PB->registerFunctionAnalyses(FAM); |
398 | PB->registerLoopAnalyses(LAM); |
399 | PB->registerMachineFunctionAnalyses(MFAM); |
400 | PB->crossRegisterProxies(LAM, FAM, CGAM, MAM, MFAM: &MFAM); |
401 | MAM.registerPass(PassBuilder: [&] { return MachineModuleAnalysis(*MMI); }); |
402 | } |
403 | }; |
404 | |
405 | TEST_F(MachineFunctionCallbacksTest, Passes) { |
406 | EXPECT_CALL(AnalysisHandle, run(HasName("test" ), _)); |
407 | EXPECT_CALL(PassHandle, run(HasName("test" ), _)).WillOnce(once_action: &getAnalysisResult); |
408 | |
409 | StringRef PipelineText = "test-transform" ; |
410 | ASSERT_THAT_ERROR(PB->parsePassPipeline(MPM, PipelineText), Succeeded()) |
411 | << "Pipeline was: " << PipelineText; |
412 | MPM.run(IR&: *M, AM&: MAM); |
413 | } |
414 | |
415 | TEST_F(MachineFunctionCallbacksTest, InstrumentedPasses) { |
416 | CallbacksHandle.registerPassInstrumentation(Callbacks&: PIC); |
417 | // Non-mock instrumentation not specifically mentioned below can be ignored. |
418 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "test" ); |
419 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "module" ); |
420 | |
421 | // PassInstrumentation calls should happen in-sequence, in the same order |
422 | // as passes/analyses are scheduled. |
423 | ::testing::Sequence PISequence; |
424 | EXPECT_CALL(CallbacksHandle, |
425 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("test" ))) |
426 | .InSequence(s: PISequence) |
427 | .WillOnce(once_action: Return(value: true)); |
428 | EXPECT_CALL( |
429 | CallbacksHandle, |
430 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), HasName("test" ))) |
431 | .InSequence(s: PISequence); |
432 | EXPECT_CALL( |
433 | CallbacksHandle, |
434 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("test" ))) |
435 | .InSequence(s: PISequence); |
436 | EXPECT_CALL( |
437 | CallbacksHandle, |
438 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), HasName("test" ))) |
439 | .InSequence(s: PISequence); |
440 | EXPECT_CALL(CallbacksHandle, |
441 | runAfterPass(HasNameRegex("MockPassHandle" ), HasName("test" ), _)) |
442 | .InSequence(s: PISequence); |
443 | EXPECT_CALL( |
444 | CallbacksHandle, |
445 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), HasName("test" ))) |
446 | .Times(n: 0); |
447 | |
448 | EXPECT_CALL(AnalysisHandle, run(HasName("test" ), _)); |
449 | EXPECT_CALL(PassHandle, run(HasName("test" ), _)).WillOnce(once_action: &getAnalysisResult); |
450 | |
451 | StringRef PipelineText = "test-transform" ; |
452 | ASSERT_THAT_ERROR(PB->parsePassPipeline(MPM, PipelineText), Succeeded()) |
453 | << "Pipeline was: " << PipelineText; |
454 | MPM.run(IR&: *M, AM&: MAM); |
455 | } |
456 | |
457 | TEST_F(MachineFunctionCallbacksTest, InstrumentedSkippedPasses) { |
458 | CallbacksHandle.registerPassInstrumentation(Callbacks&: PIC); |
459 | // Non-mock instrumentation run here can safely be ignored. |
460 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "test" ); |
461 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "module" ); |
462 | |
463 | // Skip the pass by returning false. |
464 | EXPECT_CALL(CallbacksHandle, |
465 | runBeforePass(HasNameRegex("MockPassHandle" ), HasName("test" ))) |
466 | .WillOnce(once_action: Return(value: false)); |
467 | |
468 | EXPECT_CALL( |
469 | CallbacksHandle, |
470 | runBeforeSkippedPass(HasNameRegex("MockPassHandle" ), HasName("test" ))) |
471 | .Times(n: 1); |
472 | |
473 | EXPECT_CALL(AnalysisHandle, run(HasName("test" ), _)).Times(n: 0); |
474 | EXPECT_CALL(PassHandle, run(HasName("test" ), _)).Times(n: 0); |
475 | |
476 | // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis |
477 | // as well. |
478 | EXPECT_CALL(CallbacksHandle, |
479 | runBeforeNonSkippedPass(HasNameRegex("MockPassHandle" ), _)) |
480 | .Times(n: 0); |
481 | EXPECT_CALL(CallbacksHandle, |
482 | runAfterPass(HasNameRegex("MockPassHandle" ), _, _)) |
483 | .Times(n: 0); |
484 | EXPECT_CALL(CallbacksHandle, |
485 | runAfterPassInvalidated(HasNameRegex("MockPassHandle" ), _)) |
486 | .Times(n: 0); |
487 | EXPECT_CALL(CallbacksHandle, |
488 | runAfterPass(HasNameRegex("MockPassHandle" ), _, _)) |
489 | .Times(n: 0); |
490 | EXPECT_CALL(CallbacksHandle, |
491 | runBeforeAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
492 | .Times(n: 0); |
493 | EXPECT_CALL(CallbacksHandle, |
494 | runAfterAnalysis(HasNameRegex("MockAnalysisHandle" ), _)) |
495 | .Times(n: 0); |
496 | |
497 | StringRef PipelineText = "test-transform" ; |
498 | ASSERT_THAT_ERROR(PB->parsePassPipeline(MPM, PipelineText), Succeeded()) |
499 | << "Pipeline was: " << PipelineText; |
500 | MPM.run(IR&: *M, AM&: MAM); |
501 | } |
502 | |
503 | // Check that the Module -> MachineFunction adaptor properly calls |
504 | // runAfterPassInvalidated. |
505 | TEST_F(MachineFunctionCallbacksTest, InstrumentedFreeMFPass) { |
506 | CallbacksHandle.registerPassInstrumentation(Callbacks&: PIC); |
507 | // Non-mock instrumentation run here can safely be ignored. |
508 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "test" ); |
509 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "module" ); |
510 | |
511 | ::testing::Sequence PISequence; |
512 | EXPECT_CALL( |
513 | CallbacksHandle, |
514 | runBeforePass(HasNameRegex("FreeMachineFunctionPass" ), HasName("test" ))) |
515 | .InSequence(s: PISequence) |
516 | .WillOnce(once_action: Return(value: true)); |
517 | EXPECT_CALL(CallbacksHandle, |
518 | runBeforeNonSkippedPass(HasNameRegex("FreeMachineFunctionPass" ), |
519 | HasName("test" ))) |
520 | .InSequence(s: PISequence); |
521 | EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( |
522 | HasNameRegex("FreeMachineFunctionPass" ), _)) |
523 | .InSequence(s: PISequence); |
524 | |
525 | // runAfterPass should not be called since the MachineFunction is no longer |
526 | // valid after FreeMachineFunctionPass. |
527 | EXPECT_CALL(CallbacksHandle, |
528 | runAfterPass(HasNameRegex("FreeMachineFunctionPass" ), _, _)) |
529 | .Times(n: 0); |
530 | |
531 | MPM.addPass( |
532 | Pass: createModuleToMachineFunctionPassAdaptor(Pass: FreeMachineFunctionPass())); |
533 | MPM.run(IR&: *M, AM&: MAM); |
534 | } |
535 | |
536 | // Check that the Module -> MachineFunction adaptor and MachineFunction pass |
537 | // manager properly call runAfterPassInvalidated. |
538 | TEST_F(MachineFunctionCallbacksTest, InstrumentedFreeMFPass2) { |
539 | CallbacksHandle.registerPassInstrumentation(Callbacks&: PIC); |
540 | // Non-mock instrumentation run here can safely be ignored. |
541 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "test" ); |
542 | CallbacksHandle.ignoreNonMockPassInstrumentation(IRName: "module" ); |
543 | |
544 | ::testing::Sequence PISequence; |
545 | EXPECT_CALL( |
546 | CallbacksHandle, |
547 | runBeforePass(HasNameRegex("FreeMachineFunctionPass" ), HasName("test" ))) |
548 | .InSequence(s: PISequence) |
549 | .WillOnce(once_action: Return(value: true)); |
550 | EXPECT_CALL(CallbacksHandle, |
551 | runBeforeNonSkippedPass(HasNameRegex("FreeMachineFunctionPass" ), |
552 | HasName("test" ))) |
553 | .InSequence(s: PISequence); |
554 | EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( |
555 | HasNameRegex("FreeMachineFunctionPass" ), _)) |
556 | .InSequence(s: PISequence); |
557 | EXPECT_CALL(CallbacksHandle, |
558 | runAfterPassInvalidated(HasNameRegex("PassManager" ), _)) |
559 | .InSequence(s: PISequence); |
560 | |
561 | // runAfterPass should not be called since the MachineFunction is no longer |
562 | // valid after FreeMachineFunctionPass. |
563 | EXPECT_CALL(CallbacksHandle, |
564 | runAfterPass(HasNameRegex("FreeMachineFunctionPass" ), _, _)) |
565 | .Times(n: 0); |
566 | EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("PassManager" ), _, _)) |
567 | .Times(n: 0); |
568 | |
569 | MachineFunctionPassManager MFPM; |
570 | MFPM.addPass(Pass: FreeMachineFunctionPass()); |
571 | MPM.addPass(Pass: createModuleToMachineFunctionPassAdaptor(Pass: std::move(MFPM))); |
572 | MPM.run(IR&: *M, AM&: MAM); |
573 | } |
574 | |
575 | } // end anonymous namespace |
576 | |