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 "mlir/Dialect/Func/IR/FuncOps.h"
11 #include "mlir/IR/BuiltinOps.h"
12 #include "mlir/Pass/Pass.h"
13 #include "mlir/Pass/PassManager.h"
14 
15 using namespace mlir;
16 
17 namespace {
18 struct TestModulePass
19     : public PassWrapper<TestModulePass, OperationPass<ModuleOp>> {
20   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestModulePass)
21 
22   void runOnOperation() final {}
23   StringRef getArgument() const final { return "test-module-pass"; }
24   StringRef getDescription() const final {
25     return "Test a module pass in the pass manager";
26   }
27 };
28 struct TestFunctionPass
29     : public PassWrapper<TestFunctionPass, OperationPass<func::FuncOp>> {
30   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestFunctionPass)
31 
32   void runOnOperation() final {}
33   StringRef getArgument() const final { return "test-function-pass"; }
34   StringRef getDescription() const final {
35     return "Test a function pass in the pass manager";
36   }
37 };
38 struct TestInterfacePass
39     : public PassWrapper<TestInterfacePass,
40                          InterfacePass<FunctionOpInterface>> {
41   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestInterfacePass)
42 
43   void runOnOperation() final {
44     getOperation()->emitRemark() << "Executing interface pass on operation";
45   }
46   StringRef getArgument() const final { return "test-interface-pass"; }
47   StringRef getDescription() const final {
48     return "Test an interface pass (running on FunctionOpInterface) in the "
49            "pass manager";
50   }
51 };
52 struct TestOptionsPass
53     : public PassWrapper<TestOptionsPass, OperationPass<func::FuncOp>> {
54   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOptionsPass)
55 
56   struct Options : public PassPipelineOptions<Options> {
57     ListOption<int> listOption{*this, "list",
58                                llvm::cl::desc("Example list option")};
59     ListOption<std::string> stringListOption{
60         *this, "string-list", llvm::cl::desc("Example string list option")};
61     Option<std::string> stringOption{*this, "string",
62                                      llvm::cl::desc("Example string option")};
63   };
64   TestOptionsPass() = default;
65   TestOptionsPass(const TestOptionsPass &) : PassWrapper() {}
66   TestOptionsPass(const Options &options) {
67     listOption = options.listOption;
68     stringOption = options.stringOption;
69     stringListOption = options.stringListOption;
70   }
71 
72   void runOnOperation() final {}
73   StringRef getArgument() const final { return "test-options-pass"; }
74   StringRef getDescription() const final {
75     return "Test options parsing capabilities";
76   }
77 
78   ListOption<int> listOption{*this, "list",
79                              llvm::cl::desc("Example list option")};
80   ListOption<std::string> stringListOption{
81       *this, "string-list", llvm::cl::desc("Example string list option")};
82   Option<std::string> stringOption{*this, "string",
83                                    llvm::cl::desc("Example string option")};
84 };
85 
86 /// A test pass that always aborts to enable testing the crash recovery
87 /// mechanism of the pass manager.
88 struct TestCrashRecoveryPass
89     : public PassWrapper<TestCrashRecoveryPass, OperationPass<>> {
90   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestCrashRecoveryPass)
91 
92   void runOnOperation() final { abort(); }
93   StringRef getArgument() const final { return "test-pass-crash"; }
94   StringRef getDescription() const final {
95     return "Test a pass in the pass manager that always crashes";
96   }
97 };
98 
99 /// A test pass that always fails to enable testing the failure recovery
100 /// mechanisms of the pass manager.
101 struct TestFailurePass : public PassWrapper<TestFailurePass, OperationPass<>> {
102   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestFailurePass)
103 
104   void runOnOperation() final { signalPassFailure(); }
105   StringRef getArgument() const final { return "test-pass-failure"; }
106   StringRef getDescription() const final {
107     return "Test a pass in the pass manager that always fails";
108   }
109 };
110 
111 /// A test pass that creates an invalid operation in a function body.
112 struct TestInvalidIRPass
113     : public PassWrapper<TestInvalidIRPass,
114                          InterfacePass<FunctionOpInterface>> {
115   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestInvalidIRPass)
116 
117   TestInvalidIRPass() = default;
118   TestInvalidIRPass(const TestInvalidIRPass &other) : PassWrapper(other) {}
119 
120   StringRef getArgument() const final { return "test-pass-create-invalid-ir"; }
121   StringRef getDescription() const final {
122     return "Test pass that adds an invalid operation in a function body";
123   }
124   void getDependentDialects(DialectRegistry &registry) const final {
125     registry.insert<test::TestDialect>();
126   }
127   void runOnOperation() final {
128     if (signalFailure)
129       signalPassFailure();
130     if (!emitInvalidIR)
131       return;
132     OpBuilder b(getOperation().getBody());
133     OperationState state(b.getUnknownLoc(), "test.any_attr_of_i32_str");
134     b.create(state);
135   }
136   Option<bool> signalFailure{*this, "signal-pass-failure",
137                              llvm::cl::desc("Trigger a pass failure")};
138   Option<bool> emitInvalidIR{*this, "emit-invalid-ir", llvm::cl::init(true),
139                              llvm::cl::desc("Emit invalid IR")};
140 };
141 
142 /// A test pass that always fails to enable testing the failure recovery
143 /// mechanisms of the pass manager.
144 struct TestInvalidParentPass
145     : public PassWrapper<TestInvalidParentPass,
146                          InterfacePass<FunctionOpInterface>> {
147   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestInvalidParentPass)
148 
149   StringRef getArgument() const final { return "test-pass-invalid-parent"; }
150   StringRef getDescription() const final {
151     return "Test a pass in the pass manager that makes the parent operation "
152            "invalid";
153   }
154   void getDependentDialects(DialectRegistry &registry) const final {
155     registry.insert<test::TestDialect>();
156   }
157   void runOnOperation() final {
158     FunctionOpInterface op = getOperation();
159     OpBuilder b(getOperation().getBody());
160     b.create<test::TestCallOp>(op.getLoc(), TypeRange(), "some_unknown_func",
161                                ValueRange());
162   }
163 };
164 
165 /// A test pass that contains a statistic.
166 struct TestStatisticPass
167     : public PassWrapper<TestStatisticPass, OperationPass<>> {
168   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestStatisticPass)
169 
170   TestStatisticPass() = default;
171   TestStatisticPass(const TestStatisticPass &) : PassWrapper() {}
172   StringRef getArgument() const final { return "test-stats-pass"; }
173   StringRef getDescription() const final { return "Test pass statistics"; }
174 
175   Statistic opCount{this, "num-ops", "Number of operations counted"};
176 
177   void runOnOperation() final {
178     getOperation()->walk([&](Operation *) { ++opCount; });
179   }
180 };
181 } // namespace
182 
183 static void testNestedPipeline(OpPassManager &pm) {
184   // Nest a module pipeline that contains:
185   /// A module pass.
186   auto &modulePM = pm.nest<ModuleOp>();
187   modulePM.addPass(std::make_unique<TestModulePass>());
188   /// A nested function pass.
189   auto &nestedFunctionPM = modulePM.nest<func::FuncOp>();
190   nestedFunctionPM.addPass(std::make_unique<TestFunctionPass>());
191 
192   // Nest a function pipeline that contains a single pass.
193   auto &functionPM = pm.nest<func::FuncOp>();
194   functionPM.addPass(std::make_unique<TestFunctionPass>());
195 }
196 
197 static void testNestedPipelineTextual(OpPassManager &pm) {
198   (void)parsePassPipeline("test-pm-nested-pipeline", pm);
199 }
200 
201 namespace mlir {
202 void registerPassManagerTestPass() {
203   PassRegistration<TestOptionsPass>();
204 
205   PassRegistration<TestModulePass>();
206 
207   PassRegistration<TestFunctionPass>();
208 
209   PassRegistration<TestInterfacePass>();
210 
211   PassRegistration<TestCrashRecoveryPass>();
212   PassRegistration<TestFailurePass>();
213   PassRegistration<TestInvalidIRPass>();
214   PassRegistration<TestInvalidParentPass>();
215 
216   PassRegistration<TestStatisticPass>();
217 
218   PassPipelineRegistration<>("test-pm-nested-pipeline",
219                              "Test a nested pipeline in the pass manager",
220                              testNestedPipeline);
221   PassPipelineRegistration<>("test-textual-pm-nested-pipeline",
222                              "Test a nested pipeline in the pass manager",
223                              testNestedPipelineTextual);
224   PassPipelineRegistration<>(
225       "test-dump-pipeline",
226       "Dumps the pipeline build so far for debugging purposes",
227       [](OpPassManager &pm) {
228         pm.printAsTextualPipeline(llvm::errs());
229         llvm::errs() << "\n";
230       });
231 
232   PassPipelineRegistration<TestOptionsPass::Options>
233       registerOptionsPassPipeline(
234           "test-options-pass-pipeline",
235           "Parses options using pass pipeline registration",
236           [](OpPassManager &pm, const TestOptionsPass::Options &options) {
237             pm.addPass(std::make_unique<TestOptionsPass>(options));
238           });
239 }
240 } // namespace mlir
241