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 | |
16 | using namespace mlir; |
17 | |
18 | namespace { |
19 | struct 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 | }; |
29 | struct 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 | }; |
39 | struct 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 | }; |
53 | struct 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. |
100 | struct 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. |
113 | struct 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. |
124 | struct 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 ®istry) 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. |
156 | struct 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 ®istry) 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. |
178 | struct 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 | |
202 | static 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 | |
216 | static void testNestedPipelineTextual(OpPassManager &pm) { |
217 | (void)parsePassPipeline(pipeline: "test-pm-nested-pipeline" , pm); |
218 | } |
219 | |
220 | namespace mlir { |
221 | void 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 | |