1 //===- Pass.cpp - Pass infrastructure implementation ----------------------===// 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 // This file implements common pass infrastructure. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "mlir/Pass/Pass.h" 14 #include "PassDetail.h" 15 #include "mlir/IR/Diagnostics.h" 16 #include "mlir/IR/Dialect.h" 17 #include "mlir/IR/Module.h" 18 #include "mlir/IR/Verifier.h" 19 #include "mlir/Support/FileUtilities.h" 20 #include "llvm/ADT/STLExtras.h" 21 #include "llvm/ADT/ScopeExit.h" 22 #include "llvm/ADT/SetVector.h" 23 #include "llvm/Support/CommandLine.h" 24 #include "llvm/Support/CrashRecoveryContext.h" 25 #include "llvm/Support/Mutex.h" 26 #include "llvm/Support/Parallel.h" 27 #include "llvm/Support/Signals.h" 28 #include "llvm/Support/Threading.h" 29 #include "llvm/Support/ToolOutputFile.h" 30 31 using namespace mlir; 32 using namespace mlir::detail; 33 34 //===----------------------------------------------------------------------===// 35 // Pass 36 //===----------------------------------------------------------------------===// 37 38 /// Out of line virtual method to ensure vtables and metadata are emitted to a 39 /// single .o file. 40 void Pass::anchor() {} 41 42 /// Attempt to initialize the options of this pass from the given string. 43 LogicalResult Pass::initializeOptions(StringRef options) { 44 return passOptions.parseFromString(options); 45 } 46 47 /// Copy the option values from 'other', which is another instance of this 48 /// pass. 49 void Pass::copyOptionValuesFrom(const Pass *other) { 50 passOptions.copyOptionValuesFrom(other->passOptions); 51 } 52 53 /// Prints out the pass in the textual representation of pipelines. If this is 54 /// an adaptor pass, print with the op_name(sub_pass,...) format. 55 void Pass::printAsTextualPipeline(raw_ostream &os) { 56 // Special case for adaptors to use the 'op_name(sub_passes)' format. 57 if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(this)) { 58 llvm::interleaveComma(adaptor->getPassManagers(), os, 59 [&](OpPassManager &pm) { 60 os << pm.getOpName() << "("; 61 pm.printAsTextualPipeline(os); 62 os << ")"; 63 }); 64 return; 65 } 66 // Otherwise, print the pass argument followed by its options. If the pass 67 // doesn't have an argument, print the name of the pass to give some indicator 68 // of what pass was run. 69 StringRef argument = getArgument(); 70 if (!argument.empty()) 71 os << argument; 72 else 73 os << "unknown<" << getName() << ">"; 74 passOptions.print(os); 75 } 76 77 //===----------------------------------------------------------------------===// 78 // OpPassManagerImpl 79 //===----------------------------------------------------------------------===// 80 81 namespace mlir { 82 namespace detail { 83 struct OpPassManagerImpl { 84 OpPassManagerImpl(Identifier identifier, OpPassManager::Nesting nesting) 85 : name(identifier.str()), identifier(identifier), nesting(nesting) {} 86 OpPassManagerImpl(StringRef name, OpPassManager::Nesting nesting) 87 : name(name), nesting(nesting) {} 88 89 /// Merge the passes of this pass manager into the one provided. 90 void mergeInto(OpPassManagerImpl &rhs); 91 92 /// Nest a new operation pass manager for the given operation kind under this 93 /// pass manager. 94 OpPassManager &nest(Identifier nestedName); 95 OpPassManager &nest(StringRef nestedName); 96 97 /// Add the given pass to this pass manager. If this pass has a concrete 98 /// operation type, it must be the same type as this pass manager. 99 void addPass(std::unique_ptr<Pass> pass); 100 101 /// Coalesce adjacent AdaptorPasses into one large adaptor. This runs 102 /// recursively through the pipeline graph. 103 void coalesceAdjacentAdaptorPasses(); 104 105 /// Split all of AdaptorPasses such that each adaptor only contains one leaf 106 /// pass. 107 void splitAdaptorPasses(); 108 109 Identifier getOpName(MLIRContext &context) { 110 if (!identifier) 111 identifier = Identifier::get(name, &context); 112 return *identifier; 113 } 114 115 /// The name of the operation that passes of this pass manager operate on. 116 std::string name; 117 118 /// The cached identifier (internalized in the context) for the name of the 119 /// operation that passes of this pass manager operate on. 120 Optional<Identifier> identifier; 121 122 /// The set of passes to run as part of this pass manager. 123 std::vector<std::unique_ptr<Pass>> passes; 124 125 /// Control the implicit nesting of passes that mismatch the name set for this 126 /// OpPassManager. 127 OpPassManager::Nesting nesting; 128 }; 129 } // end namespace detail 130 } // end namespace mlir 131 132 void OpPassManagerImpl::mergeInto(OpPassManagerImpl &rhs) { 133 assert(name == rhs.name && "merging unrelated pass managers"); 134 for (auto &pass : passes) 135 rhs.passes.push_back(std::move(pass)); 136 passes.clear(); 137 } 138 139 OpPassManager &OpPassManagerImpl::nest(Identifier nestedName) { 140 OpPassManager nested(nestedName, nesting); 141 auto *adaptor = new OpToOpPassAdaptor(std::move(nested)); 142 addPass(std::unique_ptr<Pass>(adaptor)); 143 return adaptor->getPassManagers().front(); 144 } 145 146 OpPassManager &OpPassManagerImpl::nest(StringRef nestedName) { 147 OpPassManager nested(nestedName, nesting); 148 auto *adaptor = new OpToOpPassAdaptor(std::move(nested)); 149 addPass(std::unique_ptr<Pass>(adaptor)); 150 return adaptor->getPassManagers().front(); 151 } 152 153 void OpPassManagerImpl::addPass(std::unique_ptr<Pass> pass) { 154 // If this pass runs on a different operation than this pass manager, then 155 // implicitly nest a pass manager for this operation if enabled. 156 auto passOpName = pass->getOpName(); 157 if (passOpName && passOpName->str() != name) { 158 if (nesting == OpPassManager::Nesting::Implicit) 159 return nest(*passOpName).addPass(std::move(pass)); 160 llvm::report_fatal_error(llvm::Twine("Can't add pass '") + pass->getName() + 161 "' restricted to '" + *passOpName + 162 "' on a PassManager intended to run on '" + name + 163 "', did you intend to nest?"); 164 } 165 166 passes.emplace_back(std::move(pass)); 167 } 168 169 void OpPassManagerImpl::coalesceAdjacentAdaptorPasses() { 170 // Bail out early if there are no adaptor passes. 171 if (llvm::none_of(passes, [](std::unique_ptr<Pass> &pass) { 172 return isa<OpToOpPassAdaptor>(pass.get()); 173 })) 174 return; 175 176 // Walk the pass list and merge adjacent adaptors. 177 OpToOpPassAdaptor *lastAdaptor = nullptr; 178 for (auto it = passes.begin(), e = passes.end(); it != e; ++it) { 179 // Check to see if this pass is an adaptor. 180 if (auto *currentAdaptor = dyn_cast<OpToOpPassAdaptor>(it->get())) { 181 // If it is the first adaptor in a possible chain, remember it and 182 // continue. 183 if (!lastAdaptor) { 184 lastAdaptor = currentAdaptor; 185 continue; 186 } 187 188 // Otherwise, merge into the existing adaptor and delete the current one. 189 currentAdaptor->mergeInto(*lastAdaptor); 190 it->reset(); 191 } else if (lastAdaptor) { 192 // If this pass is not an adaptor, then coalesce and forget any existing 193 // adaptor. 194 for (auto &pm : lastAdaptor->getPassManagers()) 195 pm.getImpl().coalesceAdjacentAdaptorPasses(); 196 lastAdaptor = nullptr; 197 } 198 } 199 200 // If there was an adaptor at the end of the manager, coalesce it as well. 201 if (lastAdaptor) { 202 for (auto &pm : lastAdaptor->getPassManagers()) 203 pm.getImpl().coalesceAdjacentAdaptorPasses(); 204 } 205 206 // Now that the adaptors have been merged, erase the empty slot corresponding 207 // to the merged adaptors that were nulled-out in the loop above. 208 llvm::erase_if(passes, std::logical_not<std::unique_ptr<Pass>>()); 209 } 210 211 void OpPassManagerImpl::splitAdaptorPasses() { 212 std::vector<std::unique_ptr<Pass>> oldPasses; 213 std::swap(passes, oldPasses); 214 215 for (std::unique_ptr<Pass> &pass : oldPasses) { 216 // If this pass isn't an adaptor, move it directly to the new pass list. 217 auto *currentAdaptor = dyn_cast<OpToOpPassAdaptor>(pass.get()); 218 if (!currentAdaptor) { 219 addPass(std::move(pass)); 220 continue; 221 } 222 223 // Otherwise, split the adaptors of each manager within the adaptor. 224 for (OpPassManager &adaptorPM : currentAdaptor->getPassManagers()) { 225 adaptorPM.getImpl().splitAdaptorPasses(); 226 for (std::unique_ptr<Pass> &nestedPass : adaptorPM.getImpl().passes) 227 nest(adaptorPM.getOpName()).addPass(std::move(nestedPass)); 228 } 229 } 230 } 231 232 //===----------------------------------------------------------------------===// 233 // OpPassManager 234 //===----------------------------------------------------------------------===// 235 236 OpPassManager::OpPassManager(Identifier name, Nesting nesting) 237 : impl(new OpPassManagerImpl(name, nesting)) {} 238 OpPassManager::OpPassManager(StringRef name, Nesting nesting) 239 : impl(new OpPassManagerImpl(name, nesting)) {} 240 OpPassManager::OpPassManager(OpPassManager &&rhs) : impl(std::move(rhs.impl)) {} 241 OpPassManager::OpPassManager(const OpPassManager &rhs) { *this = rhs; } 242 OpPassManager &OpPassManager::operator=(const OpPassManager &rhs) { 243 impl.reset(new OpPassManagerImpl(rhs.impl->name, rhs.impl->nesting)); 244 for (auto &pass : rhs.impl->passes) 245 impl->passes.emplace_back(pass->clone()); 246 return *this; 247 } 248 249 OpPassManager::~OpPassManager() {} 250 251 OpPassManager::pass_iterator OpPassManager::begin() { 252 return MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.begin(); 253 } 254 OpPassManager::pass_iterator OpPassManager::end() { 255 return MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.end(); 256 } 257 258 OpPassManager::const_pass_iterator OpPassManager::begin() const { 259 return ArrayRef<std::unique_ptr<Pass>>{impl->passes}.begin(); 260 } 261 OpPassManager::const_pass_iterator OpPassManager::end() const { 262 return ArrayRef<std::unique_ptr<Pass>>{impl->passes}.end(); 263 } 264 265 /// Nest a new operation pass manager for the given operation kind under this 266 /// pass manager. 267 OpPassManager &OpPassManager::nest(Identifier nestedName) { 268 return impl->nest(nestedName); 269 } 270 OpPassManager &OpPassManager::nest(StringRef nestedName) { 271 return impl->nest(nestedName); 272 } 273 274 /// Add the given pass to this pass manager. If this pass has a concrete 275 /// operation type, it must be the same type as this pass manager. 276 void OpPassManager::addPass(std::unique_ptr<Pass> pass) { 277 impl->addPass(std::move(pass)); 278 } 279 280 /// Returns the number of passes held by this manager. 281 size_t OpPassManager::size() const { return impl->passes.size(); } 282 283 /// Returns the internal implementation instance. 284 OpPassManagerImpl &OpPassManager::getImpl() { return *impl; } 285 286 /// Return the operation name that this pass manager operates on. 287 StringRef OpPassManager::getOpName() const { return impl->name; } 288 289 /// Return the operation name that this pass manager operates on. 290 Identifier OpPassManager::getOpName(MLIRContext &context) const { 291 return impl->getOpName(context); 292 } 293 294 /// Prints out the given passes as the textual representation of a pipeline. 295 static void printAsTextualPipeline(ArrayRef<std::unique_ptr<Pass>> passes, 296 raw_ostream &os) { 297 llvm::interleaveComma(passes, os, [&](const std::unique_ptr<Pass> &pass) { 298 pass->printAsTextualPipeline(os); 299 }); 300 } 301 302 /// Prints out the passes of the pass manager as the textual representation 303 /// of pipelines. 304 void OpPassManager::printAsTextualPipeline(raw_ostream &os) { 305 ::printAsTextualPipeline(impl->passes, os); 306 } 307 308 void OpPassManager::dump() { 309 llvm::errs() << "Pass Manager with " << impl->passes.size() << " passes: "; 310 ::printAsTextualPipeline(impl->passes, llvm::errs()); 311 llvm::errs() << "\n"; 312 } 313 314 static void registerDialectsForPipeline(const OpPassManager &pm, 315 DialectRegistry &dialects) { 316 for (const Pass &pass : pm.getPasses()) 317 pass.getDependentDialects(dialects); 318 } 319 320 void OpPassManager::getDependentDialects(DialectRegistry &dialects) const { 321 registerDialectsForPipeline(*this, dialects); 322 } 323 324 OpPassManager::Nesting OpPassManager::getNesting() { return impl->nesting; } 325 326 void OpPassManager::setNesting(Nesting nesting) { impl->nesting = nesting; } 327 328 //===----------------------------------------------------------------------===// 329 // OpToOpPassAdaptor 330 //===----------------------------------------------------------------------===// 331 332 LogicalResult OpToOpPassAdaptor::run(Pass *pass, Operation *op, 333 AnalysisManager am, bool verifyPasses) { 334 if (!op->getName().getAbstractOperation()) 335 return op->emitOpError() 336 << "trying to schedule a pass on an unregistered operation"; 337 if (!op->getName().getAbstractOperation()->hasProperty( 338 OperationProperty::IsolatedFromAbove)) 339 return op->emitOpError() << "trying to schedule a pass on an operation not " 340 "marked as 'IsolatedFromAbove'"; 341 342 // Initialize the pass state with a callback for the pass to dynamically 343 // execute a pipeline on the currently visited operation. 344 auto dynamic_pipeline_callback = 345 [op, &am, verifyPasses](OpPassManager &pipeline, 346 Operation *root) -> LogicalResult { 347 if (!op->isAncestor(root)) 348 return root->emitOpError() 349 << "Trying to schedule a dynamic pipeline on an " 350 "operation that isn't " 351 "nested under the current operation the pass is processing"; 352 353 AnalysisManager nestedAm = am.nest(root); 354 return OpToOpPassAdaptor::runPipeline(pipeline.getPasses(), root, nestedAm, 355 verifyPasses); 356 }; 357 pass->passState.emplace(op, am, dynamic_pipeline_callback); 358 // Instrument before the pass has run. 359 PassInstrumentor *pi = am.getPassInstrumentor(); 360 if (pi) 361 pi->runBeforePass(pass, op); 362 363 // Invoke the virtual runOnOperation method. 364 if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) 365 adaptor->runOnOperation(verifyPasses); 366 else 367 pass->runOnOperation(); 368 bool passFailed = pass->passState->irAndPassFailed.getInt(); 369 370 // Invalidate any non preserved analyses. 371 am.invalidate(pass->passState->preservedAnalyses); 372 373 // Run the verifier if this pass didn't fail already. 374 if (!passFailed && verifyPasses) 375 passFailed = failed(verify(op)); 376 377 // Instrument after the pass has run. 378 if (pi) { 379 if (passFailed) 380 pi->runAfterPassFailed(pass, op); 381 else 382 pi->runAfterPass(pass, op); 383 } 384 385 // Return if the pass signaled a failure. 386 return failure(passFailed); 387 } 388 389 /// Run the given operation and analysis manager on a provided op pass manager. 390 LogicalResult OpToOpPassAdaptor::runPipeline( 391 iterator_range<OpPassManager::pass_iterator> passes, Operation *op, 392 AnalysisManager am, bool verifyPasses) { 393 auto scope_exit = llvm::make_scope_exit([&] { 394 // Clear out any computed operation analyses. These analyses won't be used 395 // any more in this pipeline, and this helps reduce the current working set 396 // of memory. If preserving these analyses becomes important in the future 397 // we can re-evaluate this. 398 am.clear(); 399 }); 400 401 // Run the pipeline over the provided operation. 402 for (Pass &pass : passes) 403 if (failed(run(&pass, op, am, verifyPasses))) 404 return failure(); 405 406 return success(); 407 } 408 409 /// Find an operation pass manager that can operate on an operation of the given 410 /// type, or nullptr if one does not exist. 411 static OpPassManager *findPassManagerFor(MutableArrayRef<OpPassManager> mgrs, 412 StringRef name) { 413 auto it = llvm::find_if( 414 mgrs, [&](OpPassManager &mgr) { return mgr.getOpName() == name; }); 415 return it == mgrs.end() ? nullptr : &*it; 416 } 417 418 /// Find an operation pass manager that can operate on an operation of the given 419 /// type, or nullptr if one does not exist. 420 static OpPassManager *findPassManagerFor(MutableArrayRef<OpPassManager> mgrs, 421 Identifier name, 422 MLIRContext &context) { 423 auto it = llvm::find_if( 424 mgrs, [&](OpPassManager &mgr) { return mgr.getOpName(context) == name; }); 425 return it == mgrs.end() ? nullptr : &*it; 426 } 427 428 OpToOpPassAdaptor::OpToOpPassAdaptor(OpPassManager &&mgr) { 429 mgrs.emplace_back(std::move(mgr)); 430 } 431 432 void OpToOpPassAdaptor::getDependentDialects(DialectRegistry &dialects) const { 433 for (auto &pm : mgrs) 434 pm.getDependentDialects(dialects); 435 } 436 437 /// Merge the current pass adaptor into given 'rhs'. 438 void OpToOpPassAdaptor::mergeInto(OpToOpPassAdaptor &rhs) { 439 for (auto &pm : mgrs) { 440 // If an existing pass manager exists, then merge the given pass manager 441 // into it. 442 if (auto *existingPM = findPassManagerFor(rhs.mgrs, pm.getOpName())) { 443 pm.getImpl().mergeInto(existingPM->getImpl()); 444 } else { 445 // Otherwise, add the given pass manager to the list. 446 rhs.mgrs.emplace_back(std::move(pm)); 447 } 448 } 449 mgrs.clear(); 450 451 // After coalescing, sort the pass managers within rhs by name. 452 llvm::array_pod_sort(rhs.mgrs.begin(), rhs.mgrs.end(), 453 [](const OpPassManager *lhs, const OpPassManager *rhs) { 454 return lhs->getOpName().compare(rhs->getOpName()); 455 }); 456 } 457 458 /// Returns the adaptor pass name. 459 std::string OpToOpPassAdaptor::getAdaptorName() { 460 std::string name = "Pipeline Collection : ["; 461 llvm::raw_string_ostream os(name); 462 llvm::interleaveComma(getPassManagers(), os, [&](OpPassManager &pm) { 463 os << '\'' << pm.getOpName() << '\''; 464 }); 465 os << ']'; 466 return os.str(); 467 } 468 469 void OpToOpPassAdaptor::runOnOperation() { 470 llvm_unreachable( 471 "Unexpected call to Pass::runOnOperation() on OpToOpPassAdaptor"); 472 } 473 474 /// Run the held pipeline over all nested operations. 475 void OpToOpPassAdaptor::runOnOperation(bool verifyPasses) { 476 if (getContext().isMultithreadingEnabled()) 477 runOnOperationAsyncImpl(verifyPasses); 478 else 479 runOnOperationImpl(verifyPasses); 480 } 481 482 /// Run this pass adaptor synchronously. 483 void OpToOpPassAdaptor::runOnOperationImpl(bool verifyPasses) { 484 auto am = getAnalysisManager(); 485 PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(), 486 this}; 487 auto *instrumentor = am.getPassInstrumentor(); 488 for (auto ®ion : getOperation()->getRegions()) { 489 for (auto &block : region) { 490 for (auto &op : block) { 491 auto *mgr = findPassManagerFor(mgrs, op.getName().getIdentifier(), 492 *op.getContext()); 493 if (!mgr) 494 continue; 495 Identifier opName = mgr->getOpName(*getOperation()->getContext()); 496 497 // Run the held pipeline over the current operation. 498 if (instrumentor) 499 instrumentor->runBeforePipeline(opName, parentInfo); 500 LogicalResult result = 501 runPipeline(mgr->getPasses(), &op, am.nest(&op), verifyPasses); 502 if (instrumentor) 503 instrumentor->runAfterPipeline(opName, parentInfo); 504 505 if (failed(result)) 506 return signalPassFailure(); 507 } 508 } 509 } 510 } 511 512 /// Utility functor that checks if the two ranges of pass managers have a size 513 /// mismatch. 514 static bool hasSizeMismatch(ArrayRef<OpPassManager> lhs, 515 ArrayRef<OpPassManager> rhs) { 516 return lhs.size() != rhs.size() || 517 llvm::any_of(llvm::seq<size_t>(0, lhs.size()), 518 [&](size_t i) { return lhs[i].size() != rhs[i].size(); }); 519 } 520 521 /// Run this pass adaptor synchronously. 522 void OpToOpPassAdaptor::runOnOperationAsyncImpl(bool verifyPasses) { 523 AnalysisManager am = getAnalysisManager(); 524 525 // Create the async executors if they haven't been created, or if the main 526 // pipeline has changed. 527 if (asyncExecutors.empty() || hasSizeMismatch(asyncExecutors.front(), mgrs)) 528 asyncExecutors.assign(llvm::hardware_concurrency().compute_thread_count(), 529 mgrs); 530 531 // Run a prepass over the module to collect the operations to execute over. 532 // This ensures that an analysis manager exists for each operation, as well as 533 // providing a queue of operations to execute over. 534 std::vector<std::pair<Operation *, AnalysisManager>> opAMPairs; 535 for (auto ®ion : getOperation()->getRegions()) { 536 for (auto &block : region) { 537 for (auto &op : block) { 538 // Add this operation iff the name matches any of the pass managers. 539 if (findPassManagerFor(mgrs, op.getName().getIdentifier(), 540 getContext())) 541 opAMPairs.emplace_back(&op, am.nest(&op)); 542 } 543 } 544 } 545 546 // A parallel diagnostic handler that provides deterministic diagnostic 547 // ordering. 548 ParallelDiagnosticHandler diagHandler(&getContext()); 549 550 // An index for the current operation/analysis manager pair. 551 std::atomic<unsigned> opIt(0); 552 553 // Get the current thread for this adaptor. 554 PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(), 555 this}; 556 auto *instrumentor = am.getPassInstrumentor(); 557 558 // An atomic failure variable for the async executors. 559 std::atomic<bool> passFailed(false); 560 llvm::parallelForEach( 561 asyncExecutors.begin(), 562 std::next(asyncExecutors.begin(), 563 std::min(asyncExecutors.size(), opAMPairs.size())), 564 [&](MutableArrayRef<OpPassManager> pms) { 565 for (auto e = opAMPairs.size(); !passFailed && opIt < e;) { 566 // Get the next available operation index. 567 unsigned nextID = opIt++; 568 if (nextID >= e) 569 break; 570 571 // Set the order id for this thread in the diagnostic handler. 572 diagHandler.setOrderIDForThread(nextID); 573 574 // Get the pass manager for this operation and execute it. 575 auto &it = opAMPairs[nextID]; 576 auto *pm = findPassManagerFor( 577 pms, it.first->getName().getIdentifier(), getContext()); 578 assert(pm && "expected valid pass manager for operation"); 579 580 Identifier opName = pm->getOpName(*getOperation()->getContext()); 581 if (instrumentor) 582 instrumentor->runBeforePipeline(opName, parentInfo); 583 auto pipelineResult = 584 runPipeline(pm->getPasses(), it.first, it.second, verifyPasses); 585 if (instrumentor) 586 instrumentor->runAfterPipeline(opName, parentInfo); 587 588 // Drop this thread from being tracked by the diagnostic handler. 589 // After this task has finished, the thread may be used outside of 590 // this pass manager context meaning that we don't want to track 591 // diagnostics from it anymore. 592 diagHandler.eraseOrderIDForThread(); 593 594 // Handle a failed pipeline result. 595 if (failed(pipelineResult)) { 596 passFailed = true; 597 break; 598 } 599 } 600 }); 601 602 // Signal a failure if any of the executors failed. 603 if (passFailed) 604 signalPassFailure(); 605 } 606 607 //===----------------------------------------------------------------------===// 608 // PassCrashReproducer 609 //===----------------------------------------------------------------------===// 610 611 namespace { 612 /// This class contains all of the context for generating a recovery reproducer. 613 /// Each recovery context is registered globally to allow for generating 614 /// reproducers when a signal is raised, such as a segfault. 615 struct RecoveryReproducerContext { 616 RecoveryReproducerContext(MutableArrayRef<std::unique_ptr<Pass>> passes, 617 ModuleOp module, StringRef filename, 618 bool disableThreads, bool verifyPasses); 619 ~RecoveryReproducerContext(); 620 621 /// Generate a reproducer with the current context. 622 LogicalResult generate(std::string &error); 623 624 private: 625 /// This function is invoked in the event of a crash. 626 static void crashHandler(void *); 627 628 /// Register a signal handler to run in the event of a crash. 629 static void registerSignalHandler(); 630 631 /// The textual description of the currently executing pipeline. 632 std::string pipeline; 633 634 /// The MLIR module representing the IR before the crash. 635 OwningModuleRef module; 636 637 /// The filename to use when generating the reproducer. 638 StringRef filename; 639 640 /// Various pass manager and context flags. 641 bool disableThreads; 642 bool verifyPasses; 643 644 /// The current set of active reproducer contexts. This is used in the event 645 /// of a crash. This is not thread_local as the pass manager may produce any 646 /// number of child threads. This uses a set to allow for multiple MLIR pass 647 /// managers to be running at the same time. 648 static llvm::ManagedStatic<llvm::sys::SmartMutex<true>> reproducerMutex; 649 static llvm::ManagedStatic< 650 llvm::SmallSetVector<RecoveryReproducerContext *, 1>> 651 reproducerSet; 652 }; 653 } // end anonymous namespace 654 655 llvm::ManagedStatic<llvm::sys::SmartMutex<true>> 656 RecoveryReproducerContext::reproducerMutex; 657 llvm::ManagedStatic<llvm::SmallSetVector<RecoveryReproducerContext *, 1>> 658 RecoveryReproducerContext::reproducerSet; 659 660 RecoveryReproducerContext::RecoveryReproducerContext( 661 MutableArrayRef<std::unique_ptr<Pass>> passes, ModuleOp module, 662 StringRef filename, bool disableThreads, bool verifyPasses) 663 : module(module.clone()), filename(filename), 664 disableThreads(disableThreads), verifyPasses(verifyPasses) { 665 // Grab the textual pipeline being executed.. 666 { 667 llvm::raw_string_ostream pipelineOS(pipeline); 668 ::printAsTextualPipeline(passes, pipelineOS); 669 } 670 671 // Make sure that the handler is registered, and update the current context. 672 llvm::sys::SmartScopedLock<true> producerLock(*reproducerMutex); 673 registerSignalHandler(); 674 reproducerSet->insert(this); 675 } 676 677 RecoveryReproducerContext::~RecoveryReproducerContext() { 678 llvm::sys::SmartScopedLock<true> producerLock(*reproducerMutex); 679 reproducerSet->remove(this); 680 } 681 682 LogicalResult RecoveryReproducerContext::generate(std::string &error) { 683 std::unique_ptr<llvm::ToolOutputFile> outputFile = 684 mlir::openOutputFile(filename, &error); 685 if (!outputFile) 686 return failure(); 687 auto &outputOS = outputFile->os(); 688 689 // Output the current pass manager configuration. 690 outputOS << "// configuration: -pass-pipeline='" << pipeline << "'"; 691 if (disableThreads) 692 outputOS << " -mlir-disable-threading"; 693 694 // TODO: Should this also be configured with a pass manager flag? 695 outputOS << "\n// note: verifyPasses=" << (verifyPasses ? "true" : "false") 696 << "\n"; 697 698 // Output the .mlir module. 699 module->print(outputOS); 700 outputFile->keep(); 701 return success(); 702 } 703 704 void RecoveryReproducerContext::crashHandler(void *) { 705 // Walk the current stack of contexts and generate a reproducer for each one. 706 // We can't know for certain which one was the cause, so we need to generate 707 // a reproducer for all of them. 708 std::string ignored; 709 for (RecoveryReproducerContext *context : *reproducerSet) 710 context->generate(ignored); 711 } 712 713 void RecoveryReproducerContext::registerSignalHandler() { 714 // Ensure that the handler is only registered once. 715 static bool registered = 716 (llvm::sys::AddSignalHandler(crashHandler, nullptr), false); 717 (void)registered; 718 } 719 720 /// Run the pass manager with crash recover enabled. 721 LogicalResult PassManager::runWithCrashRecovery(ModuleOp module, 722 AnalysisManager am) { 723 // If this isn't a local producer, run all of the passes in recovery mode. 724 if (!localReproducer) 725 return runWithCrashRecovery(impl->passes, module, am); 726 727 // Split the passes within adaptors to ensure that each pass can be run in 728 // isolation. 729 impl->splitAdaptorPasses(); 730 731 // If this is a local producer, run each of the passes individually. 732 MutableArrayRef<std::unique_ptr<Pass>> passes = impl->passes; 733 for (std::unique_ptr<Pass> &pass : passes) 734 if (failed(runWithCrashRecovery(pass, module, am))) 735 return failure(); 736 return success(); 737 } 738 739 /// Run the given passes with crash recover enabled. 740 LogicalResult 741 PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes, 742 ModuleOp module, AnalysisManager am) { 743 RecoveryReproducerContext context(passes, module, *crashReproducerFileName, 744 !getContext()->isMultithreadingEnabled(), 745 verifyPasses); 746 747 // Safely invoke the passes within a recovery context. 748 llvm::CrashRecoveryContext::Enable(); 749 LogicalResult passManagerResult = failure(); 750 llvm::CrashRecoveryContext recoveryContext; 751 recoveryContext.RunSafelyOnThread([&] { 752 for (std::unique_ptr<Pass> &pass : passes) 753 if (failed(OpToOpPassAdaptor::run(pass.get(), module, am, verifyPasses))) 754 return; 755 passManagerResult = success(); 756 }); 757 llvm::CrashRecoveryContext::Disable(); 758 if (succeeded(passManagerResult)) 759 return success(); 760 761 std::string error; 762 if (failed(context.generate(error))) 763 return module.emitError("<MLIR-PassManager-Crash-Reproducer>: ") << error; 764 return module.emitError() 765 << "A failure has been detected while processing the MLIR module, a " 766 "reproducer has been generated in '" 767 << *crashReproducerFileName << "'"; 768 } 769 770 //===----------------------------------------------------------------------===// 771 // PassManager 772 //===----------------------------------------------------------------------===// 773 774 PassManager::PassManager(MLIRContext *ctx, Nesting nesting) 775 : OpPassManager(Identifier::get(ModuleOp::getOperationName(), ctx), 776 nesting), 777 context(ctx), passTiming(false), localReproducer(false), 778 verifyPasses(true) {} 779 780 PassManager::~PassManager() {} 781 782 void PassManager::enableVerifier(bool enabled) { verifyPasses = enabled; } 783 784 /// Run the passes within this manager on the provided module. 785 LogicalResult PassManager::run(ModuleOp module) { 786 // Before running, make sure to coalesce any adjacent pass adaptors in the 787 // pipeline. 788 getImpl().coalesceAdjacentAdaptorPasses(); 789 790 // Register all dialects for the current pipeline. 791 DialectRegistry dependentDialects; 792 getDependentDialects(dependentDialects); 793 dependentDialects.loadAll(module.getContext()); 794 795 // Construct an analysis manager for the pipeline. 796 ModuleAnalysisManager am(module, instrumentor.get()); 797 798 // Notify the context that we start running a pipeline for book keeping. 799 module.getContext()->enterMultiThreadedExecution(); 800 801 // If reproducer generation is enabled, run the pass manager with crash 802 // handling enabled. 803 LogicalResult result = crashReproducerFileName 804 ? runWithCrashRecovery(module, am) 805 : OpToOpPassAdaptor::runPipeline( 806 getPasses(), module, am, verifyPasses); 807 808 // Notify the context that the run is done. 809 module.getContext()->exitMultiThreadedExecution(); 810 811 // Dump all of the pass statistics if necessary. 812 if (passStatisticsMode) 813 dumpStatistics(); 814 return result; 815 } 816 817 /// Enable support for the pass manager to generate a reproducer on the event 818 /// of a crash or a pass failure. `outputFile` is a .mlir filename used to write 819 /// the generated reproducer. If `genLocalReproducer` is true, the pass manager 820 /// will attempt to generate a local reproducer that contains the smallest 821 /// pipeline. 822 void PassManager::enableCrashReproducerGeneration(StringRef outputFile, 823 bool genLocalReproducer) { 824 crashReproducerFileName = std::string(outputFile); 825 localReproducer = genLocalReproducer; 826 } 827 828 /// Add the provided instrumentation to the pass manager. 829 void PassManager::addInstrumentation(std::unique_ptr<PassInstrumentation> pi) { 830 if (!instrumentor) 831 instrumentor = std::make_unique<PassInstrumentor>(); 832 833 instrumentor->addInstrumentation(std::move(pi)); 834 } 835 836 //===----------------------------------------------------------------------===// 837 // AnalysisManager 838 //===----------------------------------------------------------------------===// 839 840 /// Returns a pass instrumentation object for the current operation. 841 PassInstrumentor *AnalysisManager::getPassInstrumentor() const { 842 ParentPointerT curParent = parent; 843 while (auto *parentAM = curParent.dyn_cast<const AnalysisManager *>()) 844 curParent = parentAM->parent; 845 return curParent.get<const ModuleAnalysisManager *>()->getPassInstrumentor(); 846 } 847 848 /// Get an analysis manager for the given child operation. 849 AnalysisManager AnalysisManager::nest(Operation *op) { 850 auto it = impl->childAnalyses.find(op); 851 if (it == impl->childAnalyses.end()) 852 it = impl->childAnalyses 853 .try_emplace(op, std::make_unique<NestedAnalysisMap>(op)) 854 .first; 855 return {this, it->second.get()}; 856 } 857 858 /// Invalidate any non preserved analyses. 859 void detail::NestedAnalysisMap::invalidate( 860 const detail::PreservedAnalyses &pa) { 861 // If all analyses were preserved, then there is nothing to do here. 862 if (pa.isAll()) 863 return; 864 865 // Invalidate the analyses for the current operation directly. 866 analyses.invalidate(pa); 867 868 // If no analyses were preserved, then just simply clear out the child 869 // analysis results. 870 if (pa.isNone()) { 871 childAnalyses.clear(); 872 return; 873 } 874 875 // Otherwise, invalidate each child analysis map. 876 SmallVector<NestedAnalysisMap *, 8> mapsToInvalidate(1, this); 877 while (!mapsToInvalidate.empty()) { 878 auto *map = mapsToInvalidate.pop_back_val(); 879 for (auto &analysisPair : map->childAnalyses) { 880 analysisPair.second->invalidate(pa); 881 if (!analysisPair.second->childAnalyses.empty()) 882 mapsToInvalidate.push_back(analysisPair.second.get()); 883 } 884 } 885 } 886 887 //===----------------------------------------------------------------------===// 888 // PassInstrumentation 889 //===----------------------------------------------------------------------===// 890 891 PassInstrumentation::~PassInstrumentation() {} 892 893 //===----------------------------------------------------------------------===// 894 // PassInstrumentor 895 //===----------------------------------------------------------------------===// 896 897 namespace mlir { 898 namespace detail { 899 struct PassInstrumentorImpl { 900 /// Mutex to keep instrumentation access thread-safe. 901 llvm::sys::SmartMutex<true> mutex; 902 903 /// Set of registered instrumentations. 904 std::vector<std::unique_ptr<PassInstrumentation>> instrumentations; 905 }; 906 } // end namespace detail 907 } // end namespace mlir 908 909 PassInstrumentor::PassInstrumentor() : impl(new PassInstrumentorImpl()) {} 910 PassInstrumentor::~PassInstrumentor() {} 911 912 /// See PassInstrumentation::runBeforePipeline for details. 913 void PassInstrumentor::runBeforePipeline( 914 Identifier name, 915 const PassInstrumentation::PipelineParentInfo &parentInfo) { 916 llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); 917 for (auto &instr : impl->instrumentations) 918 instr->runBeforePipeline(name, parentInfo); 919 } 920 921 /// See PassInstrumentation::runAfterPipeline for details. 922 void PassInstrumentor::runAfterPipeline( 923 Identifier name, 924 const PassInstrumentation::PipelineParentInfo &parentInfo) { 925 llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); 926 for (auto &instr : llvm::reverse(impl->instrumentations)) 927 instr->runAfterPipeline(name, parentInfo); 928 } 929 930 /// See PassInstrumentation::runBeforePass for details. 931 void PassInstrumentor::runBeforePass(Pass *pass, Operation *op) { 932 llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); 933 for (auto &instr : impl->instrumentations) 934 instr->runBeforePass(pass, op); 935 } 936 937 /// See PassInstrumentation::runAfterPass for details. 938 void PassInstrumentor::runAfterPass(Pass *pass, Operation *op) { 939 llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); 940 for (auto &instr : llvm::reverse(impl->instrumentations)) 941 instr->runAfterPass(pass, op); 942 } 943 944 /// See PassInstrumentation::runAfterPassFailed for details. 945 void PassInstrumentor::runAfterPassFailed(Pass *pass, Operation *op) { 946 llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); 947 for (auto &instr : llvm::reverse(impl->instrumentations)) 948 instr->runAfterPassFailed(pass, op); 949 } 950 951 /// See PassInstrumentation::runBeforeAnalysis for details. 952 void PassInstrumentor::runBeforeAnalysis(StringRef name, TypeID id, 953 Operation *op) { 954 llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); 955 for (auto &instr : impl->instrumentations) 956 instr->runBeforeAnalysis(name, id, op); 957 } 958 959 /// See PassInstrumentation::runAfterAnalysis for details. 960 void PassInstrumentor::runAfterAnalysis(StringRef name, TypeID id, 961 Operation *op) { 962 llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); 963 for (auto &instr : llvm::reverse(impl->instrumentations)) 964 instr->runAfterAnalysis(name, id, op); 965 } 966 967 /// Add the given instrumentation to the collection. 968 void PassInstrumentor::addInstrumentation( 969 std::unique_ptr<PassInstrumentation> pi) { 970 llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex); 971 impl->instrumentations.emplace_back(std::move(pi)); 972 } 973