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 { 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 };
71 TestOptionsPass() = default;
72 TestOptionsPass(const TestOptionsPass &) : PassWrapper() {}
73 TestOptionsPass(const Options &options) {
74 listOption = options.listOption;
75 stringOption = options.stringOption;
76 stringListOption = options.stringListOption;
77 enumOption = options.enumOption;
78 }
79
80 void runOnOperation() final {}
81 StringRef getArgument() const final { return "test-options-pass"; }
82 StringRef getDescription() const final {
83 return "Test options parsing capabilities";
84 }
85
86 ListOption<int> listOption{*this, "list",
87 llvm::cl::desc("Example list option")};
88 ListOption<std::string> stringListOption{
89 *this, "string-list", llvm::cl::desc("Example string list option")};
90 Option<std::string> stringOption{*this, "string",
91 llvm::cl::desc("Example string option")};
92 Option<Enum> enumOption{
93 *this, "enum", llvm::cl::desc("Example enum option"),
94 llvm::cl::values(clEnumValN(0, "zero", "Example zero value"),
95 clEnumValN(1, "one", "Example one value"))};
96};
97
98/// A test pass that always aborts to enable testing the crash recovery
99/// mechanism of the pass manager.
100struct TestCrashRecoveryPass
101 : public PassWrapper<TestCrashRecoveryPass, OperationPass<>> {
102 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestCrashRecoveryPass)
103
104 void runOnOperation() final { abort(); }
105 StringRef getArgument() const final { return "test-pass-crash"; }
106 StringRef getDescription() const final {
107 return "Test a pass in the pass manager that always crashes";
108 }
109};
110
111/// A test pass that always fails to enable testing the failure recovery
112/// mechanisms of the pass manager.
113struct TestFailurePass : public PassWrapper<TestFailurePass, OperationPass<>> {
114 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestFailurePass)
115
116 void runOnOperation() final { signalPassFailure(); }
117 StringRef getArgument() const final { return "test-pass-failure"; }
118 StringRef getDescription() const final {
119 return "Test a pass in the pass manager that always fails";
120 }
121};
122
123/// A test pass that creates an invalid operation in a function body.
124struct TestInvalidIRPass
125 : public PassWrapper<TestInvalidIRPass,
126 InterfacePass<FunctionOpInterface>> {
127 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestInvalidIRPass)
128
129 TestInvalidIRPass() = default;
130 TestInvalidIRPass(const TestInvalidIRPass &other) : PassWrapper(other) {}
131
132 StringRef getArgument() const final { return "test-pass-create-invalid-ir"; }
133 StringRef getDescription() const final {
134 return "Test pass that adds an invalid operation in a function body";
135 }
136 void getDependentDialects(DialectRegistry &registry) const final {
137 registry.insert<test::TestDialect>();
138 }
139 void runOnOperation() final {
140 if (signalFailure)
141 signalPassFailure();
142 if (!emitInvalidIR)
143 return;
144 OpBuilder b(getOperation().getFunctionBody());
145 OperationState state(b.getUnknownLoc(), "test.any_attr_of_i32_str");
146 b.create(state);
147 }
148 Option<bool> signalFailure{*this, "signal-pass-failure",
149 llvm::cl::desc("Trigger a pass failure")};
150 Option<bool> emitInvalidIR{*this, "emit-invalid-ir", llvm::cl::init(Val: true),
151 llvm::cl::desc("Emit invalid IR")};
152};
153
154/// A test pass that always fails to enable testing the failure recovery
155/// mechanisms of the pass manager.
156struct TestInvalidParentPass
157 : public PassWrapper<TestInvalidParentPass,
158 InterfacePass<FunctionOpInterface>> {
159 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestInvalidParentPass)
160
161 StringRef getArgument() const final { return "test-pass-invalid-parent"; }
162 StringRef getDescription() const final {
163 return "Test a pass in the pass manager that makes the parent operation "
164 "invalid";
165 }
166 void getDependentDialects(DialectRegistry &registry) const final {
167 registry.insert<test::TestDialect>();
168 }
169 void runOnOperation() final {
170 FunctionOpInterface op = getOperation();
171 OpBuilder b(op.getFunctionBody());
172 b.create<test::TestCallOp>(op.getLoc(), TypeRange(), "some_unknown_func",
173 ValueRange());
174 }
175};
176
177/// A test pass that contains a statistic.
178struct TestStatisticPass
179 : public PassWrapper<TestStatisticPass, OperationPass<>> {
180 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestStatisticPass)
181
182 TestStatisticPass() = default;
183 TestStatisticPass(const TestStatisticPass &) : PassWrapper() {}
184 StringRef getArgument() const final { return "test-stats-pass"; }
185 StringRef getDescription() const final { return "Test pass statistics"; }
186
187 // Use a couple of statistics to verify their ordering
188 // in the print out. The statistics are registered in the order
189 // of construction, so put "num-ops2" before "num-ops" and
190 // make sure that the order is reversed.
191 Statistic opCountDuplicate{this, "num-ops2",
192 "Number of operations counted one more time"};
193 Statistic opCount{this, "num-ops", "Number of operations counted"};
194
195 void runOnOperation() final {
196 getOperation()->walk(callback: [&](Operation *) { ++opCount; });
197 getOperation()->walk(callback: [&](Operation *) { ++opCountDuplicate; });
198 }
199};
200} // namespace
201
202static void testNestedPipeline(OpPassManager &pm) {
203 // Nest a module pipeline that contains:
204 /// A module pass.
205 auto &modulePM = pm.nest<ModuleOp>();
206 modulePM.addPass(pass: std::make_unique<TestModulePass>());
207 /// A nested function pass.
208 auto &nestedFunctionPM = modulePM.nest<func::FuncOp>();
209 nestedFunctionPM.addPass(std::make_unique<TestFunctionPass>());
210
211 // Nest a function pipeline that contains a single pass.
212 auto &functionPM = pm.nest<func::FuncOp>();
213 functionPM.addPass(std::make_unique<TestFunctionPass>());
214}
215
216static void testNestedPipelineTextual(OpPassManager &pm) {
217 (void)parsePassPipeline(pipeline: "test-pm-nested-pipeline", pm);
218}
219
220namespace mlir {
221void registerPassManagerTestPass() {
222 PassRegistration<TestOptionsPass>();
223
224 PassRegistration<TestModulePass>();
225
226 PassRegistration<TestFunctionPass>();
227
228 PassRegistration<TestInterfacePass>();
229
230 PassRegistration<TestCrashRecoveryPass>();
231 PassRegistration<TestFailurePass>();
232 PassRegistration<TestInvalidIRPass>();
233 PassRegistration<TestInvalidParentPass>();
234
235 PassRegistration<TestStatisticPass>();
236
237 PassPipelineRegistration<>("test-pm-nested-pipeline",
238 "Test a nested pipeline in the pass manager",
239 testNestedPipeline);
240 PassPipelineRegistration<>("test-textual-pm-nested-pipeline",
241 "Test a nested pipeline in the pass manager",
242 testNestedPipelineTextual);
243
244 PassPipelineRegistration<TestOptionsPass::Options>
245 registerOptionsPassPipeline(
246 "test-options-pass-pipeline",
247 "Parses options using pass pipeline registration",
248 [](OpPassManager &pm, const TestOptionsPass::Options &options) {
249 pm.addPass(std::make_unique<TestOptionsPass>(args: options));
250 });
251}
252} // namespace mlir
253

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