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