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 "llvm/ADT/Optional.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 
23 namespace llvm {
24 class Any;
25 } // end namespace llvm
26 
27 namespace mlir {
28 class AnalysisManager;
29 class Identifier;
30 class MLIRContext;
31 class Operation;
32 class Pass;
33 class PassInstrumentation;
34 class PassInstrumentor;
35 
36 namespace detail {
37 struct OpPassManagerImpl;
38 struct PassExecutionState;
39 } // end namespace detail
40 
41 //===----------------------------------------------------------------------===//
42 // OpPassManager
43 //===----------------------------------------------------------------------===//
44 
45 /// This class represents a pass manager that runs passes on a specific
46 /// operation type. This class is not constructed directly, but nested within
47 /// other OpPassManagers or the top-level PassManager.
48 class OpPassManager {
49 public:
50   enum class Nesting { Implicit, Explicit };
51   OpPassManager(Identifier name, Nesting nesting);
52   OpPassManager(StringRef name, Nesting nesting);
53   OpPassManager(OpPassManager &&rhs);
54   OpPassManager(const OpPassManager &rhs);
55   ~OpPassManager();
56   OpPassManager &operator=(const OpPassManager &rhs);
57 
58   /// Iterator over the passes in this pass manager.
59   using pass_iterator =
60       llvm::pointee_iterator<MutableArrayRef<std::unique_ptr<Pass>>::iterator>;
61   pass_iterator begin();
62   pass_iterator end();
getPasses()63   iterator_range<pass_iterator> getPasses() { return {begin(), end()}; }
64 
65   using const_pass_iterator =
66       llvm::pointee_iterator<ArrayRef<std::unique_ptr<Pass>>::const_iterator>;
67   const_pass_iterator begin() const;
68   const_pass_iterator end() const;
getPasses()69   iterator_range<const_pass_iterator> getPasses() const {
70     return {begin(), end()};
71   }
72 
73   /// Nest a new operation pass manager for the given operation kind under this
74   /// pass manager.
75   OpPassManager &nest(Identifier nestedName);
76   OpPassManager &nest(StringRef nestedName);
nest()77   template <typename OpT> OpPassManager &nest() {
78     return nest(OpT::getOperationName());
79   }
80 
81   /// Add the given pass to this pass manager. If this pass has a concrete
82   /// operation type, it must be the same type as this pass manager.
83   void addPass(std::unique_ptr<Pass> pass);
84 
85   /// Add the given pass to a nested pass manager for the given operation kind
86   /// `OpT`.
addNestedPass(std::unique_ptr<Pass> pass)87   template <typename OpT> void addNestedPass(std::unique_ptr<Pass> pass) {
88     nest<OpT>().addPass(std::move(pass));
89   }
90 
91   /// Returns the number of passes held by this manager.
92   size_t size() const;
93 
94   /// Return the operation name that this pass manager operates on.
95   Identifier getOpName(MLIRContext &context) const;
96 
97   /// Return the operation name that this pass manager operates on.
98   StringRef getOpName() const;
99 
100   /// Returns the internal implementation instance.
101   detail::OpPassManagerImpl &getImpl();
102 
103   /// Prints out the passes of the pass manager as the textual representation
104   /// of pipelines.
105   /// Note: The quality of the string representation depends entirely on the
106   /// the correctness of per-pass overrides of Pass::printAsTextualPipeline.
107   void printAsTextualPipeline(raw_ostream &os);
108 
109   /// Raw dump of the pass manager to llvm::errs().
110   void dump();
111 
112   /// Merge the pass statistics of this class into 'other'.
113   void mergeStatisticsInto(OpPassManager &other);
114 
115   /// Register dependent dialects for the current pass manager.
116   /// This is forwarding to every pass in this PassManager, see the
117   /// documentation for the same method on the Pass class.
118   void getDependentDialects(DialectRegistry &dialects) const;
119 
120   /// Enable or disable the implicit nesting on this particular PassManager.
121   /// This will also apply to any newly nested PassManager built from this
122   /// instance.
123   void setNesting(Nesting nesting);
124 
125   /// Return the current nesting mode.
126   Nesting getNesting();
127 
128 private:
129   /// A pointer to an internal implementation instance.
130   std::unique_ptr<detail::OpPassManagerImpl> impl;
131 
132   /// Allow access to the constructor.
133   friend class PassManager;
134   friend class Pass;
135 
136   /// Allow access.
137   friend detail::OpPassManagerImpl;
138 };
139 
140 //===----------------------------------------------------------------------===//
141 // PassManager
142 //===----------------------------------------------------------------------===//
143 
144 /// An enum describing the different display modes for the information within
145 /// the pass manager.
146 enum class PassDisplayMode {
147   // In this mode the results are displayed in a list sorted by total,
148   // with each pass/analysis instance aggregated into one unique result.
149   List,
150 
151   // In this mode the results are displayed in a nested pipeline view that
152   // mirrors the internal pass pipeline that is being executed in the pass
153   // manager.
154   Pipeline,
155 };
156 
157 /// The main pass manager and pipeline builder.
158 class PassManager : public OpPassManager {
159 public:
160   /// Create a new pass manager under the given context with a specific nesting
161   /// style. The created pass manager can schedule operations that match
162   /// `operationName`.
163   PassManager(MLIRContext *ctx, Nesting nesting = Nesting::Explicit,
164               StringRef operationName = "module");
PassManager(MLIRContext * ctx,StringRef operationName)165   PassManager(MLIRContext *ctx, StringRef operationName)
166       : PassManager(ctx, Nesting::Explicit, operationName) {}
167   ~PassManager();
168 
169   /// Run the passes within this manager on the provided operation. The
170   /// specified operation must have the same name as the one provided the pass
171   /// manager on construction.
172   LLVM_NODISCARD
173   LogicalResult run(Operation *op);
174 
175   /// Return an instance of the context.
getContext()176   MLIRContext *getContext() const { return context; }
177 
178   /// Enable support for the pass manager to generate a reproducer on the event
179   /// of a crash or a pass failure. `outputFile` is a .mlir filename used to
180   /// write the generated reproducer. If `genLocalReproducer` is true, the pass
181   /// manager will attempt to generate a local reproducer that contains the
182   /// smallest pipeline.
183   void enableCrashReproducerGeneration(StringRef outputFile,
184                                        bool genLocalReproducer = false);
185 
186   /// Runs the verifier after each individual pass.
187   void enableVerifier(bool enabled = true);
188 
189   //===--------------------------------------------------------------------===//
190   // Instrumentations
191   //===--------------------------------------------------------------------===//
192 
193   /// Add the provided instrumentation to the pass manager.
194   void addInstrumentation(std::unique_ptr<PassInstrumentation> pi);
195 
196   //===--------------------------------------------------------------------===//
197   // IR Printing
198 
199   /// A configuration struct provided to the IR printer instrumentation.
200   class IRPrinterConfig {
201   public:
202     using PrintCallbackFn = function_ref<void(raw_ostream &)>;
203 
204     /// Initialize the configuration.
205     /// * 'printModuleScope' signals if the top-level module IR should always be
206     ///   printed. This should only be set to true when multi-threading is
207     ///   disabled, otherwise we may try to print IR that is being modified
208     ///   asynchronously.
209     /// * 'printAfterOnlyOnChange' signals that when printing the IR after a
210     ///   pass, in the case of a non-failure, we should first check if any
211     ///   potential mutations were made. This allows for reducing the number of
212     ///   logs that don't contain meaningful changes.
213     /// * 'opPrintingFlags' sets up the printing flags to use when printing the
214     ///   IR.
215     explicit IRPrinterConfig(
216         bool printModuleScope = false, bool printAfterOnlyOnChange = false,
217         OpPrintingFlags opPrintingFlags = OpPrintingFlags());
218     virtual ~IRPrinterConfig();
219 
220     /// A hook that may be overridden by a derived config that checks if the IR
221     /// of 'operation' should be dumped *before* the pass 'pass' has been
222     /// executed. If the IR should be dumped, 'printCallback' should be invoked
223     /// with the stream to dump into.
224     virtual void printBeforeIfEnabled(Pass *pass, Operation *operation,
225                                       PrintCallbackFn printCallback);
226 
227     /// A hook that may be overridden by a derived config that checks if the IR
228     /// of 'operation' should be dumped *after* the pass 'pass' has been
229     /// executed. If the IR should be dumped, 'printCallback' should be invoked
230     /// with the stream to dump into.
231     virtual void printAfterIfEnabled(Pass *pass, Operation *operation,
232                                      PrintCallbackFn printCallback);
233 
234     /// Returns true if the IR should always be printed at the top-level scope.
shouldPrintAtModuleScope()235     bool shouldPrintAtModuleScope() const { return printModuleScope; }
236 
237     /// Returns true if the IR should only printed after a pass if the IR
238     /// "changed".
shouldPrintAfterOnlyOnChange()239     bool shouldPrintAfterOnlyOnChange() const { return printAfterOnlyOnChange; }
240 
241     /// Returns the printing flags to be used to print the IR.
getOpPrintingFlags()242     OpPrintingFlags getOpPrintingFlags() const { return opPrintingFlags; }
243 
244   private:
245     /// A flag that indicates if the IR should be printed at module scope.
246     bool printModuleScope;
247 
248     /// A flag that indicates that the IR after a pass should only be printed if
249     /// a change is detected.
250     bool printAfterOnlyOnChange;
251 
252     /// Flags to control printing behavior.
253     OpPrintingFlags opPrintingFlags;
254   };
255 
256   /// Add an instrumentation to print the IR before and after pass execution,
257   /// using the provided configuration.
258   void enableIRPrinting(std::unique_ptr<IRPrinterConfig> config);
259 
260   /// Add an instrumentation to print the IR before and after pass execution,
261   /// using the provided fields to generate a default configuration:
262   /// * 'shouldPrintBeforePass' and 'shouldPrintAfterPass' correspond to filter
263   ///   functions that take a 'Pass *' and `Operation *`. These function should
264   ///   return true if the IR should be printed or not.
265   /// * 'printModuleScope' signals if the module IR should be printed, even
266   ///   for non module passes.
267   /// * 'printAfterOnlyOnChange' signals that when printing the IR after a
268   ///   pass, in the case of a non-failure, we should first check if any
269   ///   potential mutations were made.
270   /// * 'opPrintingFlags' sets up the printing flags to use when printing the
271   ///   IR.
272   /// * 'out' corresponds to the stream to output the printed IR to.
273   void enableIRPrinting(
274       std::function<bool(Pass *, Operation *)> shouldPrintBeforePass =
275           [](Pass *, Operation *) { return true; },
276       std::function<bool(Pass *, Operation *)> shouldPrintAfterPass =
277           [](Pass *, Operation *) { return true; },
278       bool printModuleScope = true, bool printAfterOnlyOnChange = true,
279       raw_ostream &out = llvm::errs(),
280       OpPrintingFlags opPrintingFlags = OpPrintingFlags());
281 
282   //===--------------------------------------------------------------------===//
283   // Pass Timing
284 
285   /// A configuration struct provided to the pass timing feature.
286   class PassTimingConfig {
287   public:
288     using PrintCallbackFn = function_ref<void(raw_ostream &)>;
289 
290     /// Initialize the configuration.
291     /// * 'displayMode' switch between list or pipeline display (see the
292     /// `PassDisplayMode` enum documentation).
293     explicit PassTimingConfig(
294         PassDisplayMode displayMode = PassDisplayMode::Pipeline)
displayMode(displayMode)295         : displayMode(displayMode) {}
296 
297     virtual ~PassTimingConfig();
298 
299     /// A hook that may be overridden by a derived config to control the
300     /// printing. The callback is supplied by the framework and the config is
301     /// responsible to call it back with a stream for the output.
302     virtual void printTiming(PrintCallbackFn printCallback);
303 
304     /// Return the `PassDisplayMode` this config was created with.
getDisplayMode()305     PassDisplayMode getDisplayMode() { return displayMode; }
306 
307   private:
308     PassDisplayMode displayMode;
309   };
310 
311   /// Add an instrumentation to time the execution of passes and the computation
312   /// of analyses.
313   /// Note: Timing should be enabled after all other instrumentations to avoid
314   /// any potential "ghost" timing from other instrumentations being
315   /// unintentionally included in the timing results.
316   void enableTiming(std::unique_ptr<PassTimingConfig> config = nullptr);
317 
318   /// Prompts the pass manager to print the statistics collected for each of the
319   /// held passes after each call to 'run'.
320   void
321   enableStatistics(PassDisplayMode displayMode = PassDisplayMode::Pipeline);
322 
323 private:
324   /// Dump the statistics of the passes within this pass manager.
325   void dumpStatistics();
326 
327   /// Run the pass manager with crash recover enabled.
328   LogicalResult runWithCrashRecovery(Operation *op, AnalysisManager am);
329   /// Run the given passes with crash recover enabled.
330   LogicalResult
331   runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes,
332                        Operation *op, AnalysisManager am);
333 
334   /// Context this PassManager was initialized with.
335   MLIRContext *context;
336 
337   /// Flag that specifies if pass statistics should be dumped.
338   Optional<PassDisplayMode> passStatisticsMode;
339 
340   /// A manager for pass instrumentations.
341   std::unique_ptr<PassInstrumentor> instrumentor;
342 
343   /// An optional filename to use when generating a crash reproducer if valid.
344   Optional<std::string> crashReproducerFileName;
345 
346   /// Flag that specifies if pass timing is enabled.
347   bool passTiming : 1;
348 
349   /// Flag that specifies if the generated crash reproducer should be local.
350   bool localReproducer : 1;
351 
352   /// A flag that indicates if the IR should be verified in between passes.
353   bool verifyPasses : 1;
354 };
355 
356 /// Register a set of useful command-line options that can be used to configure
357 /// a pass manager. The values of these options can be applied via the
358 /// 'applyPassManagerCLOptions' method below.
359 void registerPassManagerCLOptions();
360 
361 /// Apply any values provided to the pass manager options that were registered
362 /// with 'registerPassManagerOptions'.
363 void applyPassManagerCLOptions(PassManager &pm);
364 } // end namespace mlir
365 
366 #endif // MLIR_PASS_PASSMANAGER_H
367