1 | //===- PassManager.h - Pass Management Interface ----------------*- C++ -*-===// |
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 | #ifndef MLIR_PASS_PASSMANAGER_H |
10 | #define MLIR_PASS_PASSMANAGER_H |
11 | |
12 | #include "mlir/IR/Dialect.h" |
13 | #include "mlir/IR/OperationSupport.h" |
14 | #include "mlir/Support/LogicalResult.h" |
15 | #include "mlir/Support/Timing.h" |
16 | #include "llvm/ADT/SmallVector.h" |
17 | #include "llvm/ADT/iterator.h" |
18 | #include "llvm/Support/raw_ostream.h" |
19 | |
20 | #include <functional> |
21 | #include <vector> |
22 | #include <optional> |
23 | |
24 | namespace mlir { |
25 | class AnalysisManager; |
26 | class MLIRContext; |
27 | class Operation; |
28 | class Pass; |
29 | class PassInstrumentation; |
30 | class PassInstrumentor; |
31 | |
32 | namespace detail { |
33 | struct OpPassManagerImpl; |
34 | class OpToOpPassAdaptor; |
35 | class PassCrashReproducerGenerator; |
36 | struct PassExecutionState; |
37 | } // namespace detail |
38 | |
39 | //===----------------------------------------------------------------------===// |
40 | // OpPassManager |
41 | //===----------------------------------------------------------------------===// |
42 | |
43 | /// This class represents a pass manager that runs passes on either a specific |
44 | /// operation type, or any isolated operation. This pass manager can not be run |
45 | /// on an operation directly, but must be run either as part of a top-level |
46 | /// `PassManager`(e.g. when constructed via `nest` calls), or dynamically within |
47 | /// a pass by using the `Pass::runPipeline` API. |
48 | class OpPassManager { |
49 | public: |
50 | /// This enum represents the nesting behavior of the pass manager. |
51 | enum class Nesting { |
52 | /// Implicit nesting behavior. This allows for adding passes operating on |
53 | /// operations different from this pass manager, in which case a new pass |
54 | /// manager is implicitly nested for the operation type of the new pass. |
55 | Implicit, |
56 | /// Explicit nesting behavior. This requires that any passes added to this |
57 | /// pass manager support its operation type. |
58 | Explicit |
59 | }; |
60 | |
61 | /// Construct a new op-agnostic ("any") pass manager with the given operation |
62 | /// type and nesting behavior. This is the same as invoking: |
63 | /// `OpPassManager(getAnyOpAnchorName(), nesting)`. |
64 | OpPassManager(Nesting nesting = Nesting::Explicit); |
65 | |
66 | /// Construct a new pass manager with the given anchor operation type and |
67 | /// nesting behavior. |
68 | OpPassManager(StringRef name, Nesting nesting = Nesting::Explicit); |
69 | OpPassManager(OperationName name, Nesting nesting = Nesting::Explicit); |
70 | OpPassManager(OpPassManager &&rhs); |
71 | OpPassManager(const OpPassManager &rhs); |
72 | ~OpPassManager(); |
73 | OpPassManager &operator=(const OpPassManager &rhs); |
74 | OpPassManager &operator=(OpPassManager &&rhs); |
75 | |
76 | /// Iterator over the passes in this pass manager. |
77 | using pass_iterator = |
78 | llvm::pointee_iterator<MutableArrayRef<std::unique_ptr<Pass>>::iterator>; |
79 | pass_iterator begin(); |
80 | pass_iterator end(); |
81 | iterator_range<pass_iterator> getPasses() { return {begin(), end()}; } |
82 | |
83 | using const_pass_iterator = |
84 | llvm::pointee_iterator<ArrayRef<std::unique_ptr<Pass>>::const_iterator>; |
85 | const_pass_iterator begin() const; |
86 | const_pass_iterator end() const; |
87 | iterator_range<const_pass_iterator> getPasses() const { |
88 | return {begin(), end()}; |
89 | } |
90 | |
91 | /// Returns true if the pass manager has no passes. |
92 | bool empty() const { return begin() == end(); } |
93 | |
94 | /// Nest a new operation pass manager for the given operation kind under this |
95 | /// pass manager. |
96 | OpPassManager &nest(OperationName nestedName); |
97 | OpPassManager &nest(StringRef nestedName); |
98 | template <typename OpT> |
99 | OpPassManager &nest() { |
100 | return nest(OpT::getOperationName()); |
101 | } |
102 | |
103 | /// Nest a new op-agnostic ("any") pass manager under this pass manager. |
104 | /// Note: This is the same as invoking `nest(getAnyOpAnchorName())`. |
105 | OpPassManager &nestAny(); |
106 | |
107 | /// Add the given pass to this pass manager. If this pass has a concrete |
108 | /// operation type, it must be the same type as this pass manager. |
109 | void addPass(std::unique_ptr<Pass> pass); |
110 | |
111 | /// Clear the pipeline, but not the other options set on this OpPassManager. |
112 | void clear(); |
113 | |
114 | /// Add the given pass to a nested pass manager for the given operation kind |
115 | /// `OpT`. |
116 | template <typename OpT> |
117 | void addNestedPass(std::unique_ptr<Pass> pass) { |
118 | nest<OpT>().addPass(std::move(pass)); |
119 | } |
120 | |
121 | /// Returns the number of passes held by this manager. |
122 | size_t size() const; |
123 | |
124 | /// Return the operation name that this pass manager operates on, or |
125 | /// std::nullopt if this is an op-agnostic pass manager. |
126 | std::optional<OperationName> getOpName(MLIRContext &context) const; |
127 | |
128 | /// Return the operation name that this pass manager operates on, or |
129 | /// std::nullopt if this is an op-agnostic pass manager. |
130 | std::optional<StringRef> getOpName() const; |
131 | |
132 | /// Return the name used to anchor this pass manager. This is either the name |
133 | /// of an operation, or the result of `getAnyOpAnchorName()` in the case of an |
134 | /// op-agnostic pass manager. |
135 | StringRef getOpAnchorName() const; |
136 | |
137 | /// Return the string name used to anchor op-agnostic pass managers that |
138 | /// operate generically on any viable operation. |
139 | static StringRef getAnyOpAnchorName() { return "any" ; } |
140 | |
141 | /// Returns the internal implementation instance. |
142 | detail::OpPassManagerImpl &getImpl(); |
143 | |
144 | /// Prints out the passes of the pass manager as the textual representation |
145 | /// of pipelines. |
146 | /// Note: The quality of the string representation depends entirely on the |
147 | /// the correctness of per-pass overrides of Pass::printAsTextualPipeline. |
148 | void printAsTextualPipeline(raw_ostream &os) const; |
149 | |
150 | /// Raw dump of the pass manager to llvm::errs(). |
151 | void dump(); |
152 | |
153 | /// Merge the pass statistics of this class into 'other'. |
154 | void mergeStatisticsInto(OpPassManager &other); |
155 | |
156 | /// Register dependent dialects for the current pass manager. |
157 | /// This is forwarding to every pass in this PassManager, see the |
158 | /// documentation for the same method on the Pass class. |
159 | void getDependentDialects(DialectRegistry &dialects) const; |
160 | |
161 | /// Enable or disable the implicit nesting on this particular PassManager. |
162 | /// This will also apply to any newly nested PassManager built from this |
163 | /// instance. |
164 | void setNesting(Nesting nesting); |
165 | |
166 | /// Return the current nesting mode. |
167 | Nesting getNesting(); |
168 | |
169 | private: |
170 | /// Initialize all of the passes within this pass manager with the given |
171 | /// initialization generation. The initialization generation is used to detect |
172 | /// if a pass manager has already been initialized. |
173 | LogicalResult initialize(MLIRContext *context, unsigned newInitGeneration); |
174 | |
175 | /// Compute a hash of the pipeline, so that we can detect changes (a pass is |
176 | /// added...). |
177 | llvm::hash_code hash(); |
178 | |
179 | /// A pointer to an internal implementation instance. |
180 | std::unique_ptr<detail::OpPassManagerImpl> impl; |
181 | |
182 | /// Allow access to initialize. |
183 | friend detail::OpToOpPassAdaptor; |
184 | |
185 | /// Allow access to the constructor. |
186 | friend class PassManager; |
187 | friend class Pass; |
188 | |
189 | /// Allow access. |
190 | friend detail::OpPassManagerImpl; |
191 | }; |
192 | |
193 | //===----------------------------------------------------------------------===// |
194 | // PassManager |
195 | //===----------------------------------------------------------------------===// |
196 | |
197 | /// An enum describing the different display modes for the information within |
198 | /// the pass manager. |
199 | enum class PassDisplayMode { |
200 | // In this mode the results are displayed in a list sorted by total, |
201 | // with each pass/analysis instance aggregated into one unique result. |
202 | List, |
203 | |
204 | // In this mode the results are displayed in a nested pipeline view that |
205 | // mirrors the internal pass pipeline that is being executed in the pass |
206 | // manager. |
207 | Pipeline, |
208 | }; |
209 | |
210 | /// Streams on which to output crash reproducer. |
211 | struct ReproducerStream { |
212 | virtual ~ReproducerStream() = default; |
213 | |
214 | /// Description of the reproducer stream. |
215 | virtual StringRef description() = 0; |
216 | |
217 | /// Stream on which to output reproducer. |
218 | virtual raw_ostream &os() = 0; |
219 | }; |
220 | |
221 | /// Method type for constructing ReproducerStream. |
222 | using ReproducerStreamFactory = |
223 | std::function<std::unique_ptr<ReproducerStream>(std::string &error)>; |
224 | |
225 | std::string |
226 | makeReproducer(StringRef anchorName, |
227 | const llvm::iterator_range<OpPassManager::pass_iterator> &passes, |
228 | Operation *op, StringRef outputFile, bool disableThreads = false, |
229 | bool verifyPasses = false); |
230 | |
231 | /// The main pass manager and pipeline builder. |
232 | class PassManager : public OpPassManager { |
233 | public: |
234 | /// Create a new pass manager under the given context with a specific nesting |
235 | /// style. The created pass manager can schedule operations that match |
236 | /// `operationName`. |
237 | PassManager(MLIRContext *ctx, |
238 | StringRef operationName = PassManager::getAnyOpAnchorName(), |
239 | Nesting nesting = Nesting::Explicit); |
240 | PassManager(OperationName operationName, Nesting nesting = Nesting::Explicit); |
241 | ~PassManager(); |
242 | |
243 | /// Create a new pass manager under the given context with a specific nesting |
244 | /// style. The created pass manager can schedule operations that match |
245 | /// `OperationTy`. |
246 | template <typename OperationTy> |
247 | static PassManager on(MLIRContext *ctx, Nesting nesting = Nesting::Explicit) { |
248 | return PassManager(ctx, OperationTy::getOperationName(), nesting); |
249 | } |
250 | |
251 | /// Run the passes within this manager on the provided operation. The |
252 | /// specified operation must have the same name as the one provided the pass |
253 | /// manager on construction. |
254 | LogicalResult run(Operation *op); |
255 | |
256 | /// Return an instance of the context. |
257 | MLIRContext *getContext() const { return context; } |
258 | |
259 | /// Enable support for the pass manager to generate a reproducer on the event |
260 | /// of a crash or a pass failure. `outputFile` is a .mlir filename used to |
261 | /// write the generated reproducer. If `genLocalReproducer` is true, the pass |
262 | /// manager will attempt to generate a local reproducer that contains the |
263 | /// smallest pipeline. |
264 | void enableCrashReproducerGeneration(StringRef outputFile, |
265 | bool genLocalReproducer = false); |
266 | |
267 | /// Enable support for the pass manager to generate a reproducer on the event |
268 | /// of a crash or a pass failure. `factory` is used to construct the streams |
269 | /// to write the generated reproducer to. If `genLocalReproducer` is true, the |
270 | /// pass manager will attempt to generate a local reproducer that contains the |
271 | /// smallest pipeline. |
272 | void enableCrashReproducerGeneration(ReproducerStreamFactory factory, |
273 | bool genLocalReproducer = false); |
274 | |
275 | /// Runs the verifier after each individual pass. |
276 | void enableVerifier(bool enabled = true); |
277 | |
278 | //===--------------------------------------------------------------------===// |
279 | // Instrumentations |
280 | //===--------------------------------------------------------------------===// |
281 | |
282 | /// Add the provided instrumentation to the pass manager. |
283 | void addInstrumentation(std::unique_ptr<PassInstrumentation> pi); |
284 | |
285 | //===--------------------------------------------------------------------===// |
286 | // IR Printing |
287 | |
288 | /// A configuration struct provided to the IR printer instrumentation. |
289 | class IRPrinterConfig { |
290 | public: |
291 | using PrintCallbackFn = function_ref<void(raw_ostream &)>; |
292 | |
293 | /// Initialize the configuration. |
294 | /// * 'printModuleScope' signals if the top-level module IR should always be |
295 | /// printed. This should only be set to true when multi-threading is |
296 | /// disabled, otherwise we may try to print IR that is being modified |
297 | /// asynchronously. |
298 | /// * 'printAfterOnlyOnChange' signals that when printing the IR after a |
299 | /// pass, in the case of a non-failure, we should first check if any |
300 | /// potential mutations were made. This allows for reducing the number of |
301 | /// logs that don't contain meaningful changes. |
302 | /// * 'printAfterOnlyOnFailure' signals that when printing the IR after a |
303 | /// pass, we only print in the case of a failure. |
304 | /// - This option should *not* be used with the other `printAfter` flags |
305 | /// above. |
306 | /// * 'opPrintingFlags' sets up the printing flags to use when printing the |
307 | /// IR. |
308 | explicit IRPrinterConfig( |
309 | bool printModuleScope = false, bool printAfterOnlyOnChange = false, |
310 | bool printAfterOnlyOnFailure = false, |
311 | OpPrintingFlags opPrintingFlags = OpPrintingFlags()); |
312 | virtual ~IRPrinterConfig(); |
313 | |
314 | /// A hook that may be overridden by a derived config that checks if the IR |
315 | /// of 'operation' should be dumped *before* the pass 'pass' has been |
316 | /// executed. If the IR should be dumped, 'printCallback' should be invoked |
317 | /// with the stream to dump into. |
318 | virtual void printBeforeIfEnabled(Pass *pass, Operation *operation, |
319 | PrintCallbackFn printCallback); |
320 | |
321 | /// A hook that may be overridden by a derived config that checks if the IR |
322 | /// of 'operation' should be dumped *after* the pass 'pass' has been |
323 | /// executed. If the IR should be dumped, 'printCallback' should be invoked |
324 | /// with the stream to dump into. |
325 | virtual void printAfterIfEnabled(Pass *pass, Operation *operation, |
326 | PrintCallbackFn printCallback); |
327 | |
328 | /// Returns true if the IR should always be printed at the top-level scope. |
329 | bool shouldPrintAtModuleScope() const { return printModuleScope; } |
330 | |
331 | /// Returns true if the IR should only printed after a pass if the IR |
332 | /// "changed". |
333 | bool shouldPrintAfterOnlyOnChange() const { return printAfterOnlyOnChange; } |
334 | |
335 | /// Returns true if the IR should only printed after a pass if the pass |
336 | /// "failed". |
337 | bool shouldPrintAfterOnlyOnFailure() const { |
338 | return printAfterOnlyOnFailure; |
339 | } |
340 | |
341 | /// Returns the printing flags to be used to print the IR. |
342 | OpPrintingFlags getOpPrintingFlags() const { return opPrintingFlags; } |
343 | |
344 | private: |
345 | /// A flag that indicates if the IR should be printed at module scope. |
346 | bool printModuleScope; |
347 | |
348 | /// A flag that indicates that the IR after a pass should only be printed if |
349 | /// a change is detected. |
350 | bool printAfterOnlyOnChange; |
351 | |
352 | /// A flag that indicates that the IR after a pass should only be printed if |
353 | /// the pass failed. |
354 | bool printAfterOnlyOnFailure; |
355 | |
356 | /// Flags to control printing behavior. |
357 | OpPrintingFlags opPrintingFlags; |
358 | }; |
359 | |
360 | /// Add an instrumentation to print the IR before and after pass execution, |
361 | /// using the provided configuration. |
362 | void enableIRPrinting(std::unique_ptr<IRPrinterConfig> config); |
363 | |
364 | /// Add an instrumentation to print the IR before and after pass execution, |
365 | /// using the provided fields to generate a default configuration: |
366 | /// * 'shouldPrintBeforePass' and 'shouldPrintAfterPass' correspond to filter |
367 | /// functions that take a 'Pass *' and `Operation *`. These function should |
368 | /// return true if the IR should be printed or not. |
369 | /// * 'printModuleScope' signals if the module IR should be printed, even |
370 | /// for non module passes. |
371 | /// * 'printAfterOnlyOnChange' signals that when printing the IR after a |
372 | /// pass, in the case of a non-failure, we should first check if any |
373 | /// potential mutations were made. |
374 | /// * 'printAfterOnlyOnFailure' signals that when printing the IR after a |
375 | /// pass, we only print in the case of a failure. |
376 | /// - This option should *not* be used with the other `printAfter` flags |
377 | /// above. |
378 | /// * 'out' corresponds to the stream to output the printed IR to. |
379 | /// * 'opPrintingFlags' sets up the printing flags to use when printing the |
380 | /// IR. |
381 | void enableIRPrinting( |
382 | std::function<bool(Pass *, Operation *)> shouldPrintBeforePass = |
383 | [](Pass *, Operation *) { return true; }, |
384 | std::function<bool(Pass *, Operation *)> shouldPrintAfterPass = |
385 | [](Pass *, Operation *) { return true; }, |
386 | bool printModuleScope = true, bool printAfterOnlyOnChange = true, |
387 | bool printAfterOnlyOnFailure = false, raw_ostream &out = llvm::errs(), |
388 | OpPrintingFlags opPrintingFlags = OpPrintingFlags()); |
389 | |
390 | //===--------------------------------------------------------------------===// |
391 | // Pass Timing |
392 | |
393 | /// Add an instrumentation to time the execution of passes and the computation |
394 | /// of analyses. Timing will be reported by nesting timers into the provided |
395 | /// `timingScope`. |
396 | /// |
397 | /// Note: Timing should be enabled after all other instrumentations to avoid |
398 | /// any potential "ghost" timing from other instrumentations being |
399 | /// unintentionally included in the timing results. |
400 | void enableTiming(TimingScope &timingScope); |
401 | |
402 | /// Add an instrumentation to time the execution of passes and the computation |
403 | /// of analyses. The pass manager will take ownership of the timing manager |
404 | /// passed to the function and timing will be reported by nesting timers into |
405 | /// the timing manager's root scope. |
406 | /// |
407 | /// Note: Timing should be enabled after all other instrumentations to avoid |
408 | /// any potential "ghost" timing from other instrumentations being |
409 | /// unintentionally included in the timing results. |
410 | void enableTiming(std::unique_ptr<TimingManager> tm); |
411 | |
412 | /// Add an instrumentation to time the execution of passes and the computation |
413 | /// of analyses. Creates a temporary TimingManager owned by this PassManager |
414 | /// which will be used to report timing. |
415 | /// |
416 | /// Note: Timing should be enabled after all other instrumentations to avoid |
417 | /// any potential "ghost" timing from other instrumentations being |
418 | /// unintentionally included in the timing results. |
419 | void enableTiming(); |
420 | |
421 | //===--------------------------------------------------------------------===// |
422 | // Pass Statistics |
423 | |
424 | /// Prompts the pass manager to print the statistics collected for each of the |
425 | /// held passes after each call to 'run'. |
426 | void |
427 | enableStatistics(PassDisplayMode displayMode = PassDisplayMode::Pipeline); |
428 | |
429 | private: |
430 | /// Dump the statistics of the passes within this pass manager. |
431 | void dumpStatistics(); |
432 | |
433 | /// Run the pass manager with crash recovery enabled. |
434 | LogicalResult runWithCrashRecovery(Operation *op, AnalysisManager am); |
435 | |
436 | /// Run the passes of the pass manager, and return the result. |
437 | LogicalResult runPasses(Operation *op, AnalysisManager am); |
438 | |
439 | /// Context this PassManager was initialized with. |
440 | MLIRContext *context; |
441 | |
442 | /// Flag that specifies if pass statistics should be dumped. |
443 | std::optional<PassDisplayMode> passStatisticsMode; |
444 | |
445 | /// A manager for pass instrumentations. |
446 | std::unique_ptr<PassInstrumentor> instrumentor; |
447 | |
448 | /// An optional crash reproducer generator, if this pass manager is setup to |
449 | /// generate reproducers. |
450 | std::unique_ptr<detail::PassCrashReproducerGenerator> crashReproGenerator; |
451 | |
452 | /// Hash keys used to detect when reinitialization is necessary. |
453 | llvm::hash_code initializationKey = |
454 | DenseMapInfo<llvm::hash_code>::getTombstoneKey(); |
455 | llvm::hash_code pipelineInitializationKey = |
456 | DenseMapInfo<llvm::hash_code>::getTombstoneKey(); |
457 | |
458 | /// Flag that specifies if pass timing is enabled. |
459 | bool passTiming : 1; |
460 | |
461 | /// A flag that indicates if the IR should be verified in between passes. |
462 | bool verifyPasses : 1; |
463 | }; |
464 | |
465 | /// Register a set of useful command-line options that can be used to configure |
466 | /// a pass manager. The values of these options can be applied via the |
467 | /// 'applyPassManagerCLOptions' method below. |
468 | void registerPassManagerCLOptions(); |
469 | |
470 | /// Apply any values provided to the pass manager options that were registered |
471 | /// with 'registerPassManagerOptions'. |
472 | LogicalResult applyPassManagerCLOptions(PassManager &pm); |
473 | |
474 | /// Apply any values provided to the timing manager options that were registered |
475 | /// with `registerDefaultTimingManagerOptions`. This is a handy helper function |
476 | /// if you do not want to bother creating your own timing manager and passing it |
477 | /// to the pass manager. |
478 | void applyDefaultTimingPassManagerCLOptions(PassManager &pm); |
479 | |
480 | } // namespace mlir |
481 | |
482 | #endif // MLIR_PASS_PASSMANAGER_H |
483 | |