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