1//===- TestPassManager.cpp - Test pass manager functionality --------------===//
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 "TestDialect.h"
10#include "TestOps.h"
11#include "mlir/Dialect/Func/IR/FuncOps.h"
12#include "mlir/IR/BuiltinOps.h"
13#include "mlir/Pass/Pass.h"
14#include "mlir/Pass/PassManager.h"
15
16using namespace mlir;
17
18namespace {
19struct TestModulePass
20 : public PassWrapper<TestModulePass, OperationPass<ModuleOp>> {
21 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestModulePass)
22
23 void runOnOperation() final {}
24 StringRef getArgument() const final { return "test-module-pass"; }
25 StringRef getDescription() const final {
26 return "Test a module pass in the pass manager";
27 }
28};
29struct TestFunctionPass
30 : public PassWrapper<TestFunctionPass, OperationPass<func::FuncOp>> {
31 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestFunctionPass)
32
33 void runOnOperation() final {}
34 StringRef getArgument() const final { return "test-function-pass"; }
35 StringRef getDescription() const final {
36 return "Test a function pass in the pass manager";
37 }
38};
39struct TestInterfacePass
40 : public PassWrapper<TestInterfacePass,
41 InterfacePass<FunctionOpInterface>> {
42 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestInterfacePass)
43
44 void runOnOperation() final {
45 getOperation()->emitRemark() << "Executing interface pass on operation";
46 }
47 StringRef getArgument() const final { return "test-interface-pass"; }
48 StringRef getDescription() const final {
49 return "Test an interface pass (running on FunctionOpInterface) in the "
50 "pass manager";
51 }
52};
53struct TestOptionsPass
54 : public PassWrapper<TestOptionsPass, OperationPass<func::FuncOp>> {
55 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOptionsPass)
56
57 enum Enum { Zero, One, Two };
58
59 struct Options : public PassPipelineOptions<Options> {
60 ListOption<int> listOption{*this, "list",
61 llvm::cl::desc("Example list option")};
62 ListOption<std::string> stringListOption{
63 *this, "string-list", llvm::cl::desc("Example string list option")};
64 Option<std::string> stringOption{*this, "string",
65 llvm::cl::desc("Example string option")};
66 Option<Enum> enumOption{
67 *this, "enum", llvm::cl::desc("Example enum option"),
68 llvm::cl::values(clEnumValN(0, "zero", "Example zero value"),
69 clEnumValN(1, "one", "Example one value"),
70 clEnumValN(2, "two", "Example two value"))};
71
72 Options() = default;
73 Options(const Options &rhs) { *this = rhs; }
74 Options &operator=(const Options &rhs) {
75 copyOptionValuesFrom(other: rhs);
76 return *this;
77 }
78 };
79 TestOptionsPass() = default;
80 TestOptionsPass(const TestOptionsPass &) : PassWrapper() {}
81 TestOptionsPass(const Options &options) {
82 listOption = options.listOption;
83 stringOption = options.stringOption;
84 stringListOption = options.stringListOption;
85 enumOption = options.enumOption;
86 }
87
88 void runOnOperation() final {}
89 StringRef getArgument() const final { return "test-options-pass"; }
90 StringRef getDescription() const final {
91 return "Test options parsing capabilities";
92 }
93
94 ListOption<int> listOption{*this, "list",
95 llvm::cl::desc("Example list option")};
96 ListOption<std::string> stringListOption{
97 *this, "string-list", llvm::cl::desc("Example string list option")};
98 Option<std::string> stringOption{*this, "string",
99 llvm::cl::desc("Example string option")};
100 Option<Enum> enumOption{
101 *this, "enum", llvm::cl::desc("Example enum option"),
102 llvm::cl::values(clEnumValN(0, "zero", "Example zero value"),
103 clEnumValN(1, "one", "Example one value"),
104 clEnumValN(2, "two", "Example two value"))};
105};
106
107struct TestOptionsSuperPass
108 : public PassWrapper<TestOptionsSuperPass, OperationPass<func::FuncOp>> {
109 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOptionsSuperPass)
110
111 struct Options : public PassPipelineOptions<Options> {
112 ListOption<TestOptionsPass::Options> listOption{
113 *this, "super-list",
114 llvm::cl::desc("Example list of PassPipelineOptions option")};
115
116 Options() = default;
117 };
118
119 TestOptionsSuperPass() = default;
120 TestOptionsSuperPass(const TestOptionsSuperPass &) : PassWrapper() {}
121 TestOptionsSuperPass(const Options &options) {
122 listOption = options.listOption;
123 }
124
125 void runOnOperation() final {}
126 StringRef getArgument() const final { return "test-options-super-pass"; }
127 StringRef getDescription() const final {
128 return "Test options of options parsing capabilities";
129 }
130
131 ListOption<TestOptionsPass::Options> listOption{
132 *this, "list",
133 llvm::cl::desc("Example list of PassPipelineOptions option")};
134};
135
136struct TestOptionsPassA
137 : public PassWrapper<TestOptionsPassA, OperationPass<func::FuncOp>> {
138 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOptionsPassA)
139
140 struct Options : public PassPipelineOptions<Options> {
141 Option<bool> foo{*this, "foo", llvm::cl::desc("Example boolean option")};
142 };
143
144 TestOptionsPassA() = default;
145 TestOptionsPassA(const TestOptionsPassA &) : PassWrapper() {}
146 TestOptionsPassA(const Options &options) { this->options.foo = options.foo; }
147
148 void runOnOperation() final {}
149 StringRef getArgument() const final { return "test-options-pass-a"; }
150 StringRef getDescription() const final {
151 return "Test superset options parsing capabilities - subset A";
152 }
153
154 Options options;
155};
156
157struct TestOptionsPassB
158 : public PassWrapper<TestOptionsPassB, OperationPass<func::FuncOp>> {
159 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOptionsPassB)
160
161 struct Options : public PassPipelineOptions<Options> {
162 Option<bool> bar{*this, "bar", llvm::cl::desc("Example boolean option")};
163 };
164
165 TestOptionsPassB() = default;
166 TestOptionsPassB(const TestOptionsPassB &) : PassWrapper() {}
167 TestOptionsPassB(const Options &options) { this->options.bar = options.bar; }
168
169 void runOnOperation() final {}
170 StringRef getArgument() const final { return "test-options-pass-b"; }
171 StringRef getDescription() const final {
172 return "Test superset options parsing capabilities - subset B";
173 }
174
175 Options options;
176};
177
178struct TestPipelineOptionsSuperSetAB : TestOptionsPassA::Options,
179 TestOptionsPassB::Options {};
180
181/// A test pass that always aborts to enable testing the crash recovery
182/// mechanism of the pass manager.
183struct TestCrashRecoveryPass
184 : public PassWrapper<TestCrashRecoveryPass, OperationPass<>> {
185 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestCrashRecoveryPass)
186
187 void runOnOperation() final { abort(); }
188 StringRef getArgument() const final { return "test-pass-crash"; }
189 StringRef getDescription() const final {
190 return "Test a pass in the pass manager that always crashes";
191 }
192};
193
194/// A test pass that always fails to enable testing the failure recovery
195/// mechanisms of the pass manager.
196struct TestFailurePass : public PassWrapper<TestFailurePass, OperationPass<>> {
197 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestFailurePass)
198
199 TestFailurePass() = default;
200 TestFailurePass(const TestFailurePass &other) : PassWrapper(other) {}
201
202 void runOnOperation() final {
203 signalPassFailure();
204 if (genDiagnostics)
205 mlir::emitError(loc: getOperation()->getLoc(), message: "illegal operation");
206 }
207 StringRef getArgument() const final { return "test-pass-failure"; }
208 StringRef getDescription() const final {
209 return "Test a pass in the pass manager that always fails";
210 }
211
212 Option<bool> genDiagnostics{*this, "gen-diagnostics",
213 llvm::cl::desc("Generate a diagnostic message")};
214};
215
216/// A test pass that creates an invalid operation in a function body.
217struct TestInvalidIRPass
218 : public PassWrapper<TestInvalidIRPass,
219 InterfacePass<FunctionOpInterface>> {
220 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestInvalidIRPass)
221
222 TestInvalidIRPass() = default;
223 TestInvalidIRPass(const TestInvalidIRPass &other) : PassWrapper(other) {}
224
225 StringRef getArgument() const final { return "test-pass-create-invalid-ir"; }
226 StringRef getDescription() const final {
227 return "Test pass that adds an invalid operation in a function body";
228 }
229 void getDependentDialects(DialectRegistry &registry) const final {
230 registry.insert<test::TestDialect>();
231 }
232 void runOnOperation() final {
233 if (signalFailure)
234 signalPassFailure();
235 if (!emitInvalidIR)
236 return;
237 OpBuilder b(getOperation().getFunctionBody());
238 OperationState state(b.getUnknownLoc(), "test.any_attr_of_i32_str");
239 b.create(state);
240 }
241 Option<bool> signalFailure{*this, "signal-pass-failure",
242 llvm::cl::desc("Trigger a pass failure")};
243 Option<bool> emitInvalidIR{*this, "emit-invalid-ir", llvm::cl::init(Val: true),
244 llvm::cl::desc("Emit invalid IR")};
245};
246
247/// A test pass that always fails to enable testing the failure recovery
248/// mechanisms of the pass manager.
249struct TestInvalidParentPass
250 : public PassWrapper<TestInvalidParentPass,
251 InterfacePass<FunctionOpInterface>> {
252 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestInvalidParentPass)
253
254 StringRef getArgument() const final { return "test-pass-invalid-parent"; }
255 StringRef getDescription() const final {
256 return "Test a pass in the pass manager that makes the parent operation "
257 "invalid";
258 }
259 void getDependentDialects(DialectRegistry &registry) const final {
260 registry.insert<test::TestDialect>();
261 }
262 void runOnOperation() final {
263 FunctionOpInterface op = getOperation();
264 OpBuilder b(op.getFunctionBody());
265 b.create<test::TestCallOp>(location: op.getLoc(), args: TypeRange(), args: "some_unknown_func",
266 args: ValueRange());
267 }
268};
269
270/// A test pass that contains a statistic.
271struct TestStatisticPass
272 : public PassWrapper<TestStatisticPass, OperationPass<>> {
273 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestStatisticPass)
274
275 TestStatisticPass() = default;
276 TestStatisticPass(const TestStatisticPass &) : PassWrapper() {}
277 StringRef getArgument() const final { return "test-stats-pass"; }
278 StringRef getDescription() const final { return "Test pass statistics"; }
279
280 // Use a couple of statistics to verify their ordering
281 // in the print out. The statistics are registered in the order
282 // of construction, so put "num-ops2" before "num-ops" and
283 // make sure that the order is reversed.
284 Statistic opCountDuplicate{this, "num-ops2",
285 "Number of operations counted one more time"};
286 Statistic opCount{this, "num-ops", "Number of operations counted"};
287
288 void runOnOperation() final {
289 getOperation()->walk(callback: [&](Operation *) { ++opCount; });
290 getOperation()->walk(callback: [&](Operation *) { ++opCountDuplicate; });
291 }
292};
293} // namespace
294
295static void testNestedPipeline(OpPassManager &pm) {
296 // Nest a module pipeline that contains:
297 /// A module pass.
298 auto &modulePM = pm.nest<ModuleOp>();
299 modulePM.addPass(pass: std::make_unique<TestModulePass>());
300 /// A nested function pass.
301 auto &nestedFunctionPM = modulePM.nest<func::FuncOp>();
302 nestedFunctionPM.addPass(pass: std::make_unique<TestFunctionPass>());
303
304 // Nest a function pipeline that contains a single pass.
305 auto &functionPM = pm.nest<func::FuncOp>();
306 functionPM.addPass(pass: std::make_unique<TestFunctionPass>());
307}
308
309static void testNestedPipelineTextual(OpPassManager &pm) {
310 (void)parsePassPipeline(pipeline: "test-pm-nested-pipeline", pm);
311}
312
313namespace mlir {
314void registerPassManagerTestPass() {
315 PassRegistration<TestOptionsPass>();
316 PassRegistration<TestOptionsSuperPass>();
317
318 PassRegistration<TestOptionsPassA>();
319 PassRegistration<TestOptionsPassB>();
320
321 PassRegistration<TestModulePass>();
322
323 PassRegistration<TestFunctionPass>();
324
325 PassRegistration<TestInterfacePass>();
326
327 PassRegistration<TestCrashRecoveryPass>();
328 PassRegistration<TestFailurePass>();
329 PassRegistration<TestInvalidIRPass>();
330 PassRegistration<TestInvalidParentPass>();
331
332 PassRegistration<TestStatisticPass>();
333
334 PassPipelineRegistration<>("test-pm-nested-pipeline",
335 "Test a nested pipeline in the pass manager",
336 testNestedPipeline);
337 PassPipelineRegistration<>("test-textual-pm-nested-pipeline",
338 "Test a nested pipeline in the pass manager",
339 testNestedPipelineTextual);
340
341 PassPipelineRegistration<TestOptionsPass::Options>
342 registerOptionsPassPipeline(
343 "test-options-pass-pipeline",
344 "Parses options using pass pipeline registration",
345 [](OpPassManager &pm, const TestOptionsPass::Options &options) {
346 pm.addPass(pass: std::make_unique<TestOptionsPass>(args: options));
347 });
348
349 PassPipelineRegistration<TestOptionsSuperPass::Options>
350 registerOptionsSuperPassPipeline(
351 "test-options-super-pass-pipeline",
352 "Parses options of PassPipelineOptions using pass pipeline "
353 "registration",
354 [](OpPassManager &pm, const TestOptionsSuperPass::Options &options) {
355 pm.addPass(pass: std::make_unique<TestOptionsSuperPass>(args: options));
356 });
357
358 PassPipelineRegistration<TestPipelineOptionsSuperSetAB>
359 registerPipelineOptionsSuperSetABPipeline(
360 "test-options-super-set-ab-pipeline",
361 "Parses options of PassPipelineOptions using pass pipeline "
362 "registration",
363 [](OpPassManager &pm, const TestPipelineOptionsSuperSetAB &options) {
364 // Pass superset AB options to subset options A and B
365 pm.addPass(pass: std::make_unique<TestOptionsPassA>(args: options));
366 pm.addPass(pass: std::make_unique<TestOptionsPassB>(args: options));
367 });
368}
369} // namespace mlir
370

source code of mlir/test/lib/Pass/TestPassManager.cpp