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