| //===- PassManager.h - Pass Management Interface ----------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef MLIR_PASS_PASSMANAGER_H |
| #define MLIR_PASS_PASSMANAGER_H |
| |
| #include "mlir/IR/Dialect.h" |
| #include "mlir/IR/OperationSupport.h" |
| #include "mlir/Support/LogicalResult.h" |
| #include "mlir/Support/Timing.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/iterator.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include <functional> |
| #include <vector> |
| #include <optional> |
| |
| namespace mlir { |
| class AnalysisManager; |
| class MLIRContext; |
| class Operation; |
| class Pass; |
| class PassInstrumentation; |
| class PassInstrumentor; |
| |
| namespace detail { |
| struct OpPassManagerImpl; |
| class OpToOpPassAdaptor; |
| class PassCrashReproducerGenerator; |
| struct PassExecutionState; |
| } // namespace detail |
| |
| //===----------------------------------------------------------------------===// |
| // OpPassManager |
| //===----------------------------------------------------------------------===// |
| |
| /// This class represents a pass manager that runs passes on either a specific |
| /// operation type, or any isolated operation. This pass manager can not be run |
| /// on an operation directly, but must be run either as part of a top-level |
| /// `PassManager`(e.g. when constructed via `nest` calls), or dynamically within |
| /// a pass by using the `Pass::runPipeline` API. |
| class OpPassManager { |
| public: |
| /// This enum represents the nesting behavior of the pass manager. |
| enum class Nesting { |
| /// Implicit nesting behavior. This allows for adding passes operating on |
| /// operations different from this pass manager, in which case a new pass |
| /// manager is implicitly nested for the operation type of the new pass. |
| Implicit, |
| /// Explicit nesting behavior. This requires that any passes added to this |
| /// pass manager support its operation type. |
| Explicit |
| }; |
| |
| /// Construct a new op-agnostic ("any") pass manager with the given operation |
| /// type and nesting behavior. This is the same as invoking: |
| /// `OpPassManager(getAnyOpAnchorName(), nesting)`. |
| OpPassManager(Nesting nesting = Nesting::Explicit); |
| |
| /// Construct a new pass manager with the given anchor operation type and |
| /// nesting behavior. |
| OpPassManager(StringRef name, Nesting nesting = Nesting::Explicit); |
| OpPassManager(OperationName name, Nesting nesting = Nesting::Explicit); |
| OpPassManager(OpPassManager &&rhs); |
| OpPassManager(const OpPassManager &rhs); |
| ~OpPassManager(); |
| OpPassManager &operator=(const OpPassManager &rhs); |
| OpPassManager &operator=(OpPassManager &&rhs); |
| |
| /// Iterator over the passes in this pass manager. |
| using pass_iterator = |
| llvm::pointee_iterator<MutableArrayRef<std::unique_ptr<Pass>>::iterator>; |
| pass_iterator begin(); |
| pass_iterator end(); |
| iterator_range<pass_iterator> getPasses() { return {begin(), end()}; } |
| |
| using const_pass_iterator = |
| llvm::pointee_iterator<ArrayRef<std::unique_ptr<Pass>>::const_iterator>; |
| const_pass_iterator begin() const; |
| const_pass_iterator end() const; |
| iterator_range<const_pass_iterator> getPasses() const { |
| return {begin(), end()}; |
| } |
| |
| /// Returns true if the pass manager has no passes. |
| bool empty() const { return begin() == end(); } |
| |
| /// Nest a new operation pass manager for the given operation kind under this |
| /// pass manager. |
| OpPassManager &nest(OperationName nestedName); |
| OpPassManager &nest(StringRef nestedName); |
| template <typename OpT> |
| OpPassManager &nest() { |
| return nest(OpT::getOperationName()); |
| } |
| |
| /// Nest a new op-agnostic ("any") pass manager under this pass manager. |
| /// Note: This is the same as invoking `nest(getAnyOpAnchorName())`. |
| OpPassManager &nestAny(); |
| |
| /// Add the given pass to this pass manager. If this pass has a concrete |
| /// operation type, it must be the same type as this pass manager. |
| void addPass(std::unique_ptr<Pass> pass); |
| |
| /// Clear the pipeline, but not the other options set on this OpPassManager. |
| void clear(); |
| |
| /// Add the given pass to a nested pass manager for the given operation kind |
| /// `OpT`. |
| template <typename OpT> |
| void addNestedPass(std::unique_ptr<Pass> pass) { |
| nest<OpT>().addPass(std::move(pass)); |
| } |
| |
| /// Returns the number of passes held by this manager. |
| size_t size() const; |
| |
| /// Return the operation name that this pass manager operates on, or |
| /// std::nullopt if this is an op-agnostic pass manager. |
| std::optional<OperationName> getOpName(MLIRContext &context) const; |
| |
| /// Return the operation name that this pass manager operates on, or |
| /// std::nullopt if this is an op-agnostic pass manager. |
| std::optional<StringRef> getOpName() const; |
| |
| /// Return the name used to anchor this pass manager. This is either the name |
| /// of an operation, or the result of `getAnyOpAnchorName()` in the case of an |
| /// op-agnostic pass manager. |
| StringRef getOpAnchorName() const; |
| |
| /// Return the string name used to anchor op-agnostic pass managers that |
| /// operate generically on any viable operation. |
| static StringRef getAnyOpAnchorName() { return "any"; } |
| |
| /// Returns the internal implementation instance. |
| detail::OpPassManagerImpl &getImpl(); |
| |
| /// Prints out the passes of the pass manager as the textual representation |
| /// of pipelines. |
| /// Note: The quality of the string representation depends entirely on the |
| /// the correctness of per-pass overrides of Pass::printAsTextualPipeline. |
| void printAsTextualPipeline(raw_ostream &os) const; |
| |
| /// Raw dump of the pass manager to llvm::errs(). |
| void dump(); |
| |
| /// Merge the pass statistics of this class into 'other'. |
| void mergeStatisticsInto(OpPassManager &other); |
| |
| /// Register dependent dialects for the current pass manager. |
| /// This is forwarding to every pass in this PassManager, see the |
| /// documentation for the same method on the Pass class. |
| void getDependentDialects(DialectRegistry &dialects) const; |
| |
| /// Enable or disable the implicit nesting on this particular PassManager. |
| /// This will also apply to any newly nested PassManager built from this |
| /// instance. |
| void setNesting(Nesting nesting); |
| |
| /// Return the current nesting mode. |
| Nesting getNesting(); |
| |
| private: |
| /// Initialize all of the passes within this pass manager with the given |
| /// initialization generation. The initialization generation is used to detect |
| /// if a pass manager has already been initialized. |
| LogicalResult initialize(MLIRContext *context, unsigned newInitGeneration); |
| |
| /// Compute a hash of the pipeline, so that we can detect changes (a pass is |
| /// added...). |
| llvm::hash_code hash(); |
| |
| /// A pointer to an internal implementation instance. |
| std::unique_ptr<detail::OpPassManagerImpl> impl; |
| |
| /// Allow access to initialize. |
| friend detail::OpToOpPassAdaptor; |
| |
| /// Allow access to the constructor. |
| friend class PassManager; |
| friend class Pass; |
| |
| /// Allow access. |
| friend detail::OpPassManagerImpl; |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // PassManager |
| //===----------------------------------------------------------------------===// |
| |
| /// An enum describing the different display modes for the information within |
| /// the pass manager. |
| enum class PassDisplayMode { |
| // In this mode the results are displayed in a list sorted by total, |
| // with each pass/analysis instance aggregated into one unique result. |
| List, |
| |
| // In this mode the results are displayed in a nested pipeline view that |
| // mirrors the internal pass pipeline that is being executed in the pass |
| // manager. |
| Pipeline, |
| }; |
| |
| /// Streams on which to output crash reproducer. |
| struct ReproducerStream { |
| virtual ~ReproducerStream() = default; |
| |
| /// Description of the reproducer stream. |
| virtual StringRef description() = 0; |
| |
| /// Stream on which to output reproducer. |
| virtual raw_ostream &os() = 0; |
| }; |
| |
| /// Method type for constructing ReproducerStream. |
| using ReproducerStreamFactory = |
| std::function<std::unique_ptr<ReproducerStream>(std::string &error)>; |
| |
| std::string |
| makeReproducer(StringRef anchorName, |
| const llvm::iterator_range<OpPassManager::pass_iterator> &passes, |
| Operation *op, StringRef outputFile, bool disableThreads = false, |
| bool verifyPasses = false); |
| |
| /// The main pass manager and pipeline builder. |
| class PassManager : public OpPassManager { |
| public: |
| /// Create a new pass manager under the given context with a specific nesting |
| /// style. The created pass manager can schedule operations that match |
| /// `operationName`. |
| PassManager(MLIRContext *ctx, |
| StringRef operationName = PassManager::getAnyOpAnchorName(), |
| Nesting nesting = Nesting::Explicit); |
| PassManager(OperationName operationName, Nesting nesting = Nesting::Explicit); |
| ~PassManager(); |
| |
| /// Create a new pass manager under the given context with a specific nesting |
| /// style. The created pass manager can schedule operations that match |
| /// `OperationTy`. |
| template <typename OperationTy> |
| static PassManager on(MLIRContext *ctx, Nesting nesting = Nesting::Explicit) { |
| return PassManager(ctx, OperationTy::getOperationName(), nesting); |
| } |
| |
| /// Run the passes within this manager on the provided operation. The |
| /// specified operation must have the same name as the one provided the pass |
| /// manager on construction. |
| LogicalResult run(Operation *op); |
| |
| /// Return an instance of the context. |
| MLIRContext *getContext() const { return context; } |
| |
| /// Enable support for the pass manager to generate a reproducer on the event |
| /// of a crash or a pass failure. `outputFile` is a .mlir filename used to |
| /// write the generated reproducer. If `genLocalReproducer` is true, the pass |
| /// manager will attempt to generate a local reproducer that contains the |
| /// smallest pipeline. |
| void enableCrashReproducerGeneration(StringRef outputFile, |
| bool genLocalReproducer = false); |
| |
| /// Enable support for the pass manager to generate a reproducer on the event |
| /// of a crash or a pass failure. `factory` is used to construct the streams |
| /// to write the generated reproducer to. If `genLocalReproducer` is true, the |
| /// pass manager will attempt to generate a local reproducer that contains the |
| /// smallest pipeline. |
| void enableCrashReproducerGeneration(ReproducerStreamFactory factory, |
| bool genLocalReproducer = false); |
| |
| /// Runs the verifier after each individual pass. |
| void enableVerifier(bool enabled = true); |
| |
| //===--------------------------------------------------------------------===// |
| // Instrumentations |
| //===--------------------------------------------------------------------===// |
| |
| /// Add the provided instrumentation to the pass manager. |
| void addInstrumentation(std::unique_ptr<PassInstrumentation> pi); |
| |
| //===--------------------------------------------------------------------===// |
| // IR Printing |
| |
| /// A configuration struct provided to the IR printer instrumentation. |
| class IRPrinterConfig { |
| public: |
| using PrintCallbackFn = function_ref<void(raw_ostream &)>; |
| |
| /// Initialize the configuration. |
| /// * 'printModuleScope' signals if the top-level module IR should always be |
| /// printed. This should only be set to true when multi-threading is |
| /// disabled, otherwise we may try to print IR that is being modified |
| /// asynchronously. |
| /// * 'printAfterOnlyOnChange' signals that when printing the IR after a |
| /// pass, in the case of a non-failure, we should first check if any |
| /// potential mutations were made. This allows for reducing the number of |
| /// logs that don't contain meaningful changes. |
| /// * 'printAfterOnlyOnFailure' signals that when printing the IR after a |
| /// pass, we only print in the case of a failure. |
| /// - This option should *not* be used with the other `printAfter` flags |
| /// above. |
| /// * 'opPrintingFlags' sets up the printing flags to use when printing the |
| /// IR. |
| explicit IRPrinterConfig( |
| bool printModuleScope = false, bool printAfterOnlyOnChange = false, |
| bool printAfterOnlyOnFailure = false, |
| OpPrintingFlags opPrintingFlags = OpPrintingFlags()); |
| virtual ~IRPrinterConfig(); |
| |
| /// A hook that may be overridden by a derived config that checks if the IR |
| /// of 'operation' should be dumped *before* the pass 'pass' has been |
| /// executed. If the IR should be dumped, 'printCallback' should be invoked |
| /// with the stream to dump into. |
| virtual void printBeforeIfEnabled(Pass *pass, Operation *operation, |
| PrintCallbackFn printCallback); |
| |
| /// A hook that may be overridden by a derived config that checks if the IR |
| /// of 'operation' should be dumped *after* the pass 'pass' has been |
| /// executed. If the IR should be dumped, 'printCallback' should be invoked |
| /// with the stream to dump into. |
| virtual void printAfterIfEnabled(Pass *pass, Operation *operation, |
| PrintCallbackFn printCallback); |
| |
| /// Returns true if the IR should always be printed at the top-level scope. |
| bool shouldPrintAtModuleScope() const { return printModuleScope; } |
| |
| /// Returns true if the IR should only printed after a pass if the IR |
| /// "changed". |
| bool shouldPrintAfterOnlyOnChange() const { return printAfterOnlyOnChange; } |
| |
| /// Returns true if the IR should only printed after a pass if the pass |
| /// "failed". |
| bool shouldPrintAfterOnlyOnFailure() const { |
| return printAfterOnlyOnFailure; |
| } |
| |
| /// Returns the printing flags to be used to print the IR. |
| OpPrintingFlags getOpPrintingFlags() const { return opPrintingFlags; } |
| |
| private: |
| /// A flag that indicates if the IR should be printed at module scope. |
| bool printModuleScope; |
| |
| /// A flag that indicates that the IR after a pass should only be printed if |
| /// a change is detected. |
| bool printAfterOnlyOnChange; |
| |
| /// A flag that indicates that the IR after a pass should only be printed if |
| /// the pass failed. |
| bool printAfterOnlyOnFailure; |
| |
| /// Flags to control printing behavior. |
| OpPrintingFlags opPrintingFlags; |
| }; |
| |
| /// Add an instrumentation to print the IR before and after pass execution, |
| /// using the provided configuration. |
| void enableIRPrinting(std::unique_ptr<IRPrinterConfig> config); |
| |
| /// Add an instrumentation to print the IR before and after pass execution, |
| /// using the provided fields to generate a default configuration: |
| /// * 'shouldPrintBeforePass' and 'shouldPrintAfterPass' correspond to filter |
| /// functions that take a 'Pass *' and `Operation *`. These function should |
| /// return true if the IR should be printed or not. |
| /// * 'printModuleScope' signals if the module IR should be printed, even |
| /// for non module passes. |
| /// * 'printAfterOnlyOnChange' signals that when printing the IR after a |
| /// pass, in the case of a non-failure, we should first check if any |
| /// potential mutations were made. |
| /// * 'printAfterOnlyOnFailure' signals that when printing the IR after a |
| /// pass, we only print in the case of a failure. |
| /// - This option should *not* be used with the other `printAfter` flags |
| /// above. |
| /// * 'out' corresponds to the stream to output the printed IR to. |
| /// * 'opPrintingFlags' sets up the printing flags to use when printing the |
| /// IR. |
| void enableIRPrinting( |
| std::function<bool(Pass *, Operation *)> shouldPrintBeforePass = |
| [](Pass *, Operation *) { return true; }, |
| std::function<bool(Pass *, Operation *)> shouldPrintAfterPass = |
| [](Pass *, Operation *) { return true; }, |
| bool printModuleScope = true, bool printAfterOnlyOnChange = true, |
| bool printAfterOnlyOnFailure = false, raw_ostream &out = llvm::errs(), |
| OpPrintingFlags opPrintingFlags = OpPrintingFlags()); |
| |
| //===--------------------------------------------------------------------===// |
| // Pass Timing |
| |
| /// Add an instrumentation to time the execution of passes and the computation |
| /// of analyses. Timing will be reported by nesting timers into the provided |
| /// `timingScope`. |
| /// |
| /// Note: Timing should be enabled after all other instrumentations to avoid |
| /// any potential "ghost" timing from other instrumentations being |
| /// unintentionally included in the timing results. |
| void enableTiming(TimingScope &timingScope); |
| |
| /// Add an instrumentation to time the execution of passes and the computation |
| /// of analyses. The pass manager will take ownership of the timing manager |
| /// passed to the function and timing will be reported by nesting timers into |
| /// the timing manager's root scope. |
| /// |
| /// Note: Timing should be enabled after all other instrumentations to avoid |
| /// any potential "ghost" timing from other instrumentations being |
| /// unintentionally included in the timing results. |
| void enableTiming(std::unique_ptr<TimingManager> tm); |
| |
| /// Add an instrumentation to time the execution of passes and the computation |
| /// of analyses. Creates a temporary TimingManager owned by this PassManager |
| /// which will be used to report timing. |
| /// |
| /// Note: Timing should be enabled after all other instrumentations to avoid |
| /// any potential "ghost" timing from other instrumentations being |
| /// unintentionally included in the timing results. |
| void enableTiming(); |
| |
| //===--------------------------------------------------------------------===// |
| // Pass Statistics |
| |
| /// Prompts the pass manager to print the statistics collected for each of the |
| /// held passes after each call to 'run'. |
| void |
| enableStatistics(PassDisplayMode displayMode = PassDisplayMode::Pipeline); |
| |
| private: |
| /// Dump the statistics of the passes within this pass manager. |
| void dumpStatistics(); |
| |
| /// Run the pass manager with crash recovery enabled. |
| LogicalResult runWithCrashRecovery(Operation *op, AnalysisManager am); |
| |
| /// Run the passes of the pass manager, and return the result. |
| LogicalResult runPasses(Operation *op, AnalysisManager am); |
| |
| /// Context this PassManager was initialized with. |
| MLIRContext *context; |
| |
| /// Flag that specifies if pass statistics should be dumped. |
| std::optional<PassDisplayMode> passStatisticsMode; |
| |
| /// A manager for pass instrumentations. |
| std::unique_ptr<PassInstrumentor> instrumentor; |
| |
| /// An optional crash reproducer generator, if this pass manager is setup to |
| /// generate reproducers. |
| std::unique_ptr<detail::PassCrashReproducerGenerator> crashReproGenerator; |
| |
| /// Hash keys used to detect when reinitialization is necessary. |
| llvm::hash_code initializationKey = |
| DenseMapInfo<llvm::hash_code>::getTombstoneKey(); |
| llvm::hash_code pipelineInitializationKey = |
| DenseMapInfo<llvm::hash_code>::getTombstoneKey(); |
| |
| /// Flag that specifies if pass timing is enabled. |
| bool passTiming : 1; |
| |
| /// A flag that indicates if the IR should be verified in between passes. |
| bool verifyPasses : 1; |
| }; |
| |
| /// Register a set of useful command-line options that can be used to configure |
| /// a pass manager. The values of these options can be applied via the |
| /// 'applyPassManagerCLOptions' method below. |
| void registerPassManagerCLOptions(); |
| |
| /// Apply any values provided to the pass manager options that were registered |
| /// with 'registerPassManagerOptions'. |
| LogicalResult applyPassManagerCLOptions(PassManager &pm); |
| |
| /// Apply any values provided to the timing manager options that were registered |
| /// with `registerDefaultTimingManagerOptions`. This is a handy helper function |
| /// if you do not want to bother creating your own timing manager and passing it |
| /// to the pass manager. |
| void applyDefaultTimingPassManagerCLOptions(PassManager &pm); |
| |
| } // namespace mlir |
| |
| #endif // MLIR_PASS_PASSMANAGER_H |