1 //===- GreedyPatternRewriteDriver.cpp - A greedy rewriter -----------------===// 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 mlir::applyPatternsAndFoldGreedily. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "mlir/Transforms/GreedyPatternRewriteDriver.h" 14 #include "mlir/Interfaces/SideEffectInterfaces.h" 15 #include "mlir/Rewrite/PatternApplicator.h" 16 #include "mlir/Transforms/FoldUtils.h" 17 #include "mlir/Transforms/RegionUtils.h" 18 #include "llvm/ADT/DenseMap.h" 19 #include "llvm/Support/CommandLine.h" 20 #include "llvm/Support/Debug.h" 21 #include "llvm/Support/ScopedPrinter.h" 22 #include "llvm/Support/raw_ostream.h" 23 24 using namespace mlir; 25 26 #define DEBUG_TYPE "greedy-rewriter" 27 28 //===----------------------------------------------------------------------===// 29 // GreedyPatternRewriteDriver 30 //===----------------------------------------------------------------------===// 31 32 namespace { 33 /// This is a worklist-driven driver for the PatternMatcher, which repeatedly 34 /// applies the locally optimal patterns in a roughly "bottom up" way. 35 class GreedyPatternRewriteDriver : public PatternRewriter { 36 public: 37 explicit GreedyPatternRewriteDriver(MLIRContext *ctx, 38 const FrozenRewritePatternSet &patterns, 39 const GreedyRewriteConfig &config); 40 41 /// Simplify the operations within the given regions. 42 bool simplify(MutableArrayRef<Region> regions); 43 44 /// Add the given operation to the worklist. 45 void addToWorklist(Operation *op); 46 47 /// Pop the next operation from the worklist. 48 Operation *popFromWorklist(); 49 50 /// If the specified operation is in the worklist, remove it. 51 void removeFromWorklist(Operation *op); 52 53 protected: 54 // Implement the hook for inserting operations, and make sure that newly 55 // inserted ops are added to the worklist for processing. 56 void notifyOperationInserted(Operation *op) override; 57 58 // Look over the provided operands for any defining operations that should 59 // be re-added to the worklist. This function should be called when an 60 // operation is modified or removed, as it may trigger further 61 // simplifications. 62 template <typename Operands> 63 void addToWorklist(Operands &&operands); 64 65 // If an operation is about to be removed, make sure it is not in our 66 // worklist anymore because we'd get dangling references to it. 67 void notifyOperationRemoved(Operation *op) override; 68 69 // When the root of a pattern is about to be replaced, it can trigger 70 // simplifications to its users - make sure to add them to the worklist 71 // before the root is changed. 72 void notifyRootReplaced(Operation *op) override; 73 74 /// PatternRewriter hook for erasing a dead operation. 75 void eraseOp(Operation *op) override; 76 77 /// PatternRewriter hook for notifying match failure reasons. 78 LogicalResult 79 notifyMatchFailure(Operation *op, 80 function_ref<void(Diagnostic &)> reasonCallback) override; 81 82 /// The low-level pattern applicator. 83 PatternApplicator matcher; 84 85 /// The worklist for this transformation keeps track of the operations that 86 /// need to be revisited, plus their index in the worklist. This allows us to 87 /// efficiently remove operations from the worklist when they are erased, even 88 /// if they aren't the root of a pattern. 89 std::vector<Operation *> worklist; 90 DenseMap<Operation *, unsigned> worklistMap; 91 92 /// Non-pattern based folder for operations. 93 OperationFolder folder; 94 95 private: 96 /// Configuration information for how to simplify. 97 GreedyRewriteConfig config; 98 99 #ifndef NDEBUG 100 /// A logger used to emit information during the application process. 101 llvm::ScopedPrinter logger{llvm::dbgs()}; 102 #endif 103 }; 104 } // end anonymous namespace 105 106 GreedyPatternRewriteDriver::GreedyPatternRewriteDriver( 107 MLIRContext *ctx, const FrozenRewritePatternSet &patterns, 108 const GreedyRewriteConfig &config) 109 : PatternRewriter(ctx), matcher(patterns), folder(ctx), config(config) { 110 worklist.reserve(64); 111 112 // Apply a simple cost model based solely on pattern benefit. 113 matcher.applyDefaultCostModel(); 114 } 115 116 bool GreedyPatternRewriteDriver::simplify(MutableArrayRef<Region> regions) { 117 #ifndef NDEBUG 118 const char *logLineComment = 119 "//===-------------------------------------------===//\n"; 120 121 /// A utility function to log a process result for the given reason. 122 auto logResult = [&](StringRef result, const llvm::Twine &msg = {}) { 123 logger.unindent(); 124 logger.startLine() << "} -> " << result; 125 if (!msg.isTriviallyEmpty()) 126 logger.getOStream() << " : " << msg; 127 logger.getOStream() << "\n"; 128 }; 129 auto logResultWithLine = [&](StringRef result, const llvm::Twine &msg = {}) { 130 logResult(result, msg); 131 logger.startLine() << logLineComment; 132 }; 133 #endif 134 135 bool changed = false; 136 unsigned iteration = 0; 137 do { 138 worklist.clear(); 139 worklistMap.clear(); 140 141 if (!config.useTopDownTraversal) { 142 // Add operations to the worklist in postorder. 143 for (auto ®ion : regions) 144 region.walk([this](Operation *op) { addToWorklist(op); }); 145 } else { 146 // Add all nested operations to the worklist in preorder. 147 for (auto ®ion : regions) 148 region.walk<WalkOrder::PreOrder>( 149 [this](Operation *op) { worklist.push_back(op); }); 150 151 // Reverse the list so our pop-back loop processes them in-order. 152 std::reverse(worklist.begin(), worklist.end()); 153 // Remember the reverse index. 154 for (size_t i = 0, e = worklist.size(); i != e; ++i) 155 worklistMap[worklist[i]] = i; 156 } 157 158 // These are scratch vectors used in the folding loop below. 159 SmallVector<Value, 8> originalOperands, resultValues; 160 161 changed = false; 162 while (!worklist.empty()) { 163 auto *op = popFromWorklist(); 164 165 // Nulls get added to the worklist when operations are removed, ignore 166 // them. 167 if (op == nullptr) 168 continue; 169 170 LLVM_DEBUG({ 171 logger.getOStream() << "\n"; 172 logger.startLine() << logLineComment; 173 logger.startLine() << "Processing operation : '" << op->getName() 174 << "'(" << op << ") {\n"; 175 logger.indent(); 176 177 // If the operation has no regions, just print it here. 178 if (op->getNumRegions() == 0) { 179 op->print( 180 logger.startLine(), 181 OpPrintingFlags().printGenericOpForm().elideLargeElementsAttrs()); 182 logger.getOStream() << "\n\n"; 183 } 184 }); 185 186 // If the operation is trivially dead - remove it. 187 if (isOpTriviallyDead(op)) { 188 notifyOperationRemoved(op); 189 op->erase(); 190 changed = true; 191 192 LLVM_DEBUG(logResultWithLine("success", "operation is trivially dead")); 193 continue; 194 } 195 196 // Collects all the operands and result uses of the given `op` into work 197 // list. Also remove `op` and nested ops from worklist. 198 originalOperands.assign(op->operand_begin(), op->operand_end()); 199 auto preReplaceAction = [&](Operation *op) { 200 // Add the operands to the worklist for visitation. 201 addToWorklist(originalOperands); 202 203 // Add all the users of the result to the worklist so we make sure 204 // to revisit them. 205 for (auto result : op->getResults()) 206 for (auto *userOp : result.getUsers()) 207 addToWorklist(userOp); 208 209 notifyOperationRemoved(op); 210 }; 211 212 // Add the given operation to the worklist. 213 auto collectOps = [this](Operation *op) { addToWorklist(op); }; 214 215 // Try to fold this op. 216 bool inPlaceUpdate; 217 if ((succeeded(folder.tryToFold(op, collectOps, preReplaceAction, 218 &inPlaceUpdate)))) { 219 LLVM_DEBUG(logResultWithLine("success", "operation was folded")); 220 221 changed = true; 222 if (!inPlaceUpdate) 223 continue; 224 } 225 226 // Try to match one of the patterns. The rewriter is automatically 227 // notified of any necessary changes, so there is nothing else to do 228 // here. 229 #ifndef NDEBUG 230 auto canApply = [&](const Pattern &pattern) { 231 LLVM_DEBUG({ 232 logger.getOStream() << "\n"; 233 logger.startLine() << "* Pattern " << pattern.getDebugName() << " : '" 234 << op->getName() << " -> ("; 235 llvm::interleaveComma(pattern.getGeneratedOps(), logger.getOStream()); 236 logger.getOStream() << ")' {\n"; 237 logger.indent(); 238 }); 239 return true; 240 }; 241 auto onFailure = [&](const Pattern &pattern) { 242 LLVM_DEBUG(logResult("failure", "pattern failed to match")); 243 }; 244 auto onSuccess = [&](const Pattern &pattern) { 245 LLVM_DEBUG(logResult("success", "pattern applied successfully")); 246 return success(); 247 }; 248 249 LogicalResult matchResult = 250 matcher.matchAndRewrite(op, *this, canApply, onFailure, onSuccess); 251 if (succeeded(matchResult)) 252 LLVM_DEBUG(logResultWithLine("success", "pattern matched")); 253 else 254 LLVM_DEBUG(logResultWithLine("failure", "pattern failed to match")); 255 #else 256 LogicalResult matchResult = matcher.matchAndRewrite(op, *this); 257 #endif 258 259 260 #ifndef NDEBUG 261 #endif 262 263 changed |= succeeded(matchResult); 264 } 265 266 // After applying patterns, make sure that the CFG of each of the regions 267 // is kept up to date. 268 if (config.enableRegionSimplification) 269 changed |= succeeded(simplifyRegions(*this, regions)); 270 } while (changed && 271 (++iteration < config.maxIterations || 272 config.maxIterations == GreedyRewriteConfig::kNoIterationLimit)); 273 274 // Whether the rewrite converges, i.e. wasn't changed in the last iteration. 275 return !changed; 276 } 277 278 void GreedyPatternRewriteDriver::addToWorklist(Operation *op) { 279 // Check to see if the worklist already contains this op. 280 if (worklistMap.count(op)) 281 return; 282 283 worklistMap[op] = worklist.size(); 284 worklist.push_back(op); 285 } 286 287 Operation *GreedyPatternRewriteDriver::popFromWorklist() { 288 auto *op = worklist.back(); 289 worklist.pop_back(); 290 291 // This operation is no longer in the worklist, keep worklistMap up to date. 292 if (op) 293 worklistMap.erase(op); 294 return op; 295 } 296 297 void GreedyPatternRewriteDriver::removeFromWorklist(Operation *op) { 298 auto it = worklistMap.find(op); 299 if (it != worklistMap.end()) { 300 assert(worklist[it->second] == op && "malformed worklist data structure"); 301 worklist[it->second] = nullptr; 302 worklistMap.erase(it); 303 } 304 } 305 306 void GreedyPatternRewriteDriver::notifyOperationInserted(Operation *op) { 307 LLVM_DEBUG({ 308 logger.startLine() << "** Insert : '" << op->getName() << "'(" << op 309 << ")\n"; 310 }); 311 addToWorklist(op); 312 } 313 314 template <typename Operands> 315 void GreedyPatternRewriteDriver::addToWorklist(Operands &&operands) { 316 for (Value operand : operands) { 317 // If the use count of this operand is now < 2, we re-add the defining 318 // operation to the worklist. 319 // TODO: This is based on the fact that zero use operations 320 // may be deleted, and that single use values often have more 321 // canonicalization opportunities. 322 if (!operand || (!operand.use_empty() && !operand.hasOneUse())) 323 continue; 324 if (auto *defOp = operand.getDefiningOp()) 325 addToWorklist(defOp); 326 } 327 } 328 329 void GreedyPatternRewriteDriver::notifyOperationRemoved(Operation *op) { 330 addToWorklist(op->getOperands()); 331 op->walk([this](Operation *operation) { 332 removeFromWorklist(operation); 333 folder.notifyRemoval(operation); 334 }); 335 } 336 337 void GreedyPatternRewriteDriver::notifyRootReplaced(Operation *op) { 338 LLVM_DEBUG({ 339 logger.startLine() << "** Replace : '" << op->getName() << "'(" << op 340 << ")\n"; 341 }); 342 for (auto result : op->getResults()) 343 for (auto *user : result.getUsers()) 344 addToWorklist(user); 345 } 346 347 void GreedyPatternRewriteDriver::eraseOp(Operation *op) { 348 LLVM_DEBUG({ 349 logger.startLine() << "** Erase : '" << op->getName() << "'(" << op 350 << ")\n"; 351 }); 352 PatternRewriter::eraseOp(op); 353 } 354 355 LogicalResult GreedyPatternRewriteDriver::notifyMatchFailure( 356 Operation *op, function_ref<void(Diagnostic &)> reasonCallback) { 357 LLVM_DEBUG({ 358 Diagnostic diag(op->getLoc(), DiagnosticSeverity::Remark); 359 reasonCallback(diag); 360 logger.startLine() << "** Failure : " << diag.str() << "\n"; 361 }); 362 return failure(); 363 } 364 365 /// Rewrite the regions of the specified operation, which must be isolated from 366 /// above, by repeatedly applying the highest benefit patterns in a greedy 367 /// work-list driven manner. Return success if no more patterns can be matched 368 /// in the result operation regions. Note: This does not apply patterns to the 369 /// top-level operation itself. 370 /// 371 LogicalResult 372 mlir::applyPatternsAndFoldGreedily(MutableArrayRef<Region> regions, 373 const FrozenRewritePatternSet &patterns, 374 GreedyRewriteConfig config) { 375 if (regions.empty()) 376 return success(); 377 378 // The top-level operation must be known to be isolated from above to 379 // prevent performing canonicalizations on operations defined at or above 380 // the region containing 'op'. 381 auto regionIsIsolated = [](Region ®ion) { 382 return region.getParentOp()->hasTrait<OpTrait::IsIsolatedFromAbove>(); 383 }; 384 (void)regionIsIsolated; 385 assert(llvm::all_of(regions, regionIsIsolated) && 386 "patterns can only be applied to operations IsolatedFromAbove"); 387 388 // Start the pattern driver. 389 GreedyPatternRewriteDriver driver(regions[0].getContext(), patterns, config); 390 bool converged = driver.simplify(regions); 391 LLVM_DEBUG(if (!converged) { 392 llvm::dbgs() << "The pattern rewrite doesn't converge after scanning " 393 << config.maxIterations << " times\n"; 394 }); 395 return success(converged); 396 } 397 398 //===----------------------------------------------------------------------===// 399 // OpPatternRewriteDriver 400 //===----------------------------------------------------------------------===// 401 402 namespace { 403 /// This is a simple driver for the PatternMatcher to apply patterns and perform 404 /// folding on a single op. It repeatedly applies locally optimal patterns. 405 class OpPatternRewriteDriver : public PatternRewriter { 406 public: 407 explicit OpPatternRewriteDriver(MLIRContext *ctx, 408 const FrozenRewritePatternSet &patterns) 409 : PatternRewriter(ctx), matcher(patterns), folder(ctx) { 410 // Apply a simple cost model based solely on pattern benefit. 411 matcher.applyDefaultCostModel(); 412 } 413 414 LogicalResult simplifyLocally(Operation *op, int maxIterations, bool &erased); 415 416 // These are hooks implemented for PatternRewriter. 417 protected: 418 /// If an operation is about to be removed, mark it so that we can let clients 419 /// know. 420 void notifyOperationRemoved(Operation *op) override { 421 opErasedViaPatternRewrites = true; 422 } 423 424 // When a root is going to be replaced, its removal will be notified as well. 425 // So there is nothing to do here. 426 void notifyRootReplaced(Operation *op) override {} 427 428 private: 429 /// The low-level pattern applicator. 430 PatternApplicator matcher; 431 432 /// Non-pattern based folder for operations. 433 OperationFolder folder; 434 435 /// Set to true if the operation has been erased via pattern rewrites. 436 bool opErasedViaPatternRewrites = false; 437 }; 438 439 } // anonymous namespace 440 441 /// Performs the rewrites and folding only on `op`. The simplification 442 /// converges if the op is erased as a result of being folded, replaced, or 443 /// becoming dead, or no more changes happen in an iteration. Returns success if 444 /// the rewrite converges in `maxIterations`. `erased` is set to true if `op` 445 /// gets erased. 446 LogicalResult OpPatternRewriteDriver::simplifyLocally(Operation *op, 447 int maxIterations, 448 bool &erased) { 449 bool changed = false; 450 erased = false; 451 opErasedViaPatternRewrites = false; 452 int iterations = 0; 453 // Iterate until convergence or until maxIterations. Deletion of the op as 454 // a result of being dead or folded is convergence. 455 do { 456 changed = false; 457 458 // If the operation is trivially dead - remove it. 459 if (isOpTriviallyDead(op)) { 460 op->erase(); 461 erased = true; 462 return success(); 463 } 464 465 // Try to fold this op. 466 bool inPlaceUpdate; 467 if (succeeded(folder.tryToFold(op, /*processGeneratedConstants=*/nullptr, 468 /*preReplaceAction=*/nullptr, 469 &inPlaceUpdate))) { 470 changed = true; 471 if (!inPlaceUpdate) { 472 erased = true; 473 return success(); 474 } 475 } 476 477 // Try to match one of the patterns. The rewriter is automatically 478 // notified of any necessary changes, so there is nothing else to do here. 479 changed |= succeeded(matcher.matchAndRewrite(op, *this)); 480 if ((erased = opErasedViaPatternRewrites)) 481 return success(); 482 } while (changed && 483 (++iterations < maxIterations || 484 maxIterations == GreedyRewriteConfig::kNoIterationLimit)); 485 486 // Whether the rewrite converges, i.e. wasn't changed in the last iteration. 487 return failure(changed); 488 } 489 490 //===----------------------------------------------------------------------===// 491 // MultiOpPatternRewriteDriver 492 //===----------------------------------------------------------------------===// 493 494 namespace { 495 496 /// This is a specialized GreedyPatternRewriteDriver to apply patterns and 497 /// perform folding for a supplied set of ops. It repeatedly simplifies while 498 /// restricting the rewrites to only the provided set of ops or optionally 499 /// to those directly affected by it (result users or operand providers). 500 class MultiOpPatternRewriteDriver : public GreedyPatternRewriteDriver { 501 public: 502 explicit MultiOpPatternRewriteDriver(MLIRContext *ctx, 503 const FrozenRewritePatternSet &patterns, 504 bool strict) 505 : GreedyPatternRewriteDriver(ctx, patterns, GreedyRewriteConfig()), 506 strictMode(strict) {} 507 508 bool simplifyLocally(ArrayRef<Operation *> op); 509 510 private: 511 // Look over the provided operands for any defining operations that should 512 // be re-added to the worklist. This function should be called when an 513 // operation is modified or removed, as it may trigger further 514 // simplifications. If `strict` is set to true, only ops in 515 // `strictModeFilteredOps` are considered. 516 template <typename Operands> 517 void addOperandsToWorklist(Operands &&operands) { 518 for (Value operand : operands) { 519 if (auto *defOp = operand.getDefiningOp()) { 520 if (!strictMode || strictModeFilteredOps.contains(defOp)) 521 addToWorklist(defOp); 522 } 523 } 524 } 525 526 void notifyOperationRemoved(Operation *op) override { 527 GreedyPatternRewriteDriver::notifyOperationRemoved(op); 528 if (strictMode) 529 strictModeFilteredOps.erase(op); 530 } 531 532 /// If `strictMode` is true, any pre-existing ops outside of 533 /// `strictModeFilteredOps` remain completely untouched by the rewrite driver. 534 /// If `strictMode` is false, operations that use results of (or supply 535 /// operands to) any rewritten ops stemming from the simplification of the 536 /// provided ops are in turn simplified; any other ops still remain untouched 537 /// (i.e., regardless of `strictMode`). 538 bool strictMode = false; 539 540 /// The list of ops we are restricting our rewrites to if `strictMode` is on. 541 /// These include the supplied set of ops as well as new ops created while 542 /// rewriting those ops. This set is not maintained when strictMode is off. 543 llvm::SmallDenseSet<Operation *, 4> strictModeFilteredOps; 544 }; 545 546 } // end anonymous namespace 547 548 /// Performs the specified rewrites on `ops` while also trying to fold these ops 549 /// as well as any other ops that were in turn created due to these rewrite 550 /// patterns. Any pre-existing ops outside of `ops` remain completely 551 /// unmodified if `strictMode` is true. If `strictMode` is false, other 552 /// operations that use results of rewritten ops or supply operands to such ops 553 /// are in turn simplified; any other ops still remain unmodified (i.e., 554 /// regardless of `strictMode`). Note that ops in `ops` could be erased as a 555 /// result of folding, becoming dead, or via pattern rewrites. Returns true if 556 /// at all any changes happened. 557 // Unlike `OpPatternRewriteDriver::simplifyLocally` which works on a single op 558 // or GreedyPatternRewriteDriver::simplify, this method just iterates until 559 // the worklist is empty. As our objective is to keep simplification "local", 560 // there is no strong rationale to re-add all operations into the worklist and 561 // rerun until an iteration changes nothing. If more widereaching simplification 562 // is desired, GreedyPatternRewriteDriver should be used. 563 bool MultiOpPatternRewriteDriver::simplifyLocally(ArrayRef<Operation *> ops) { 564 if (strictMode) { 565 strictModeFilteredOps.clear(); 566 strictModeFilteredOps.insert(ops.begin(), ops.end()); 567 } 568 569 bool changed = false; 570 worklist.clear(); 571 worklistMap.clear(); 572 for (Operation *op : ops) 573 addToWorklist(op); 574 575 // These are scratch vectors used in the folding loop below. 576 SmallVector<Value, 8> originalOperands, resultValues; 577 while (!worklist.empty()) { 578 Operation *op = popFromWorklist(); 579 580 // Nulls get added to the worklist when operations are removed, ignore 581 // them. 582 if (op == nullptr) 583 continue; 584 585 // If the operation is trivially dead - remove it. 586 if (isOpTriviallyDead(op)) { 587 notifyOperationRemoved(op); 588 op->erase(); 589 changed = true; 590 continue; 591 } 592 593 // Collects all the operands and result uses of the given `op` into work 594 // list. Also remove `op` and nested ops from worklist. 595 originalOperands.assign(op->operand_begin(), op->operand_end()); 596 auto preReplaceAction = [&](Operation *op) { 597 // Add the operands to the worklist for visitation. 598 addOperandsToWorklist(originalOperands); 599 600 // Add all the users of the result to the worklist so we make sure 601 // to revisit them. 602 for (Value result : op->getResults()) 603 for (Operation *userOp : result.getUsers()) { 604 if (!strictMode || strictModeFilteredOps.contains(userOp)) 605 addToWorklist(userOp); 606 } 607 notifyOperationRemoved(op); 608 }; 609 610 // Add the given operation generated by the folder to the worklist. 611 auto processGeneratedConstants = [this](Operation *op) { 612 // Newly created ops are also simplified -- these are also "local". 613 addToWorklist(op); 614 // When strict mode is off, we don't need to maintain 615 // strictModeFilteredOps. 616 if (strictMode) 617 strictModeFilteredOps.insert(op); 618 }; 619 620 // Try to fold this op. 621 bool inPlaceUpdate; 622 if (succeeded(folder.tryToFold(op, processGeneratedConstants, 623 preReplaceAction, &inPlaceUpdate))) { 624 changed = true; 625 if (!inPlaceUpdate) { 626 // Op has been erased. 627 continue; 628 } 629 } 630 631 // Try to match one of the patterns. The rewriter is automatically 632 // notified of any necessary changes, so there is nothing else to do 633 // here. 634 changed |= succeeded(matcher.matchAndRewrite(op, *this)); 635 } 636 637 return changed; 638 } 639 640 /// Rewrites only `op` using the supplied canonicalization patterns and 641 /// folding. `erased` is set to true if the op is erased as a result of being 642 /// folded, replaced, or dead. 643 LogicalResult mlir::applyOpPatternsAndFold( 644 Operation *op, const FrozenRewritePatternSet &patterns, bool *erased) { 645 // Start the pattern driver. 646 GreedyRewriteConfig config; 647 OpPatternRewriteDriver driver(op->getContext(), patterns); 648 bool opErased; 649 LogicalResult converged = 650 driver.simplifyLocally(op, config.maxIterations, opErased); 651 if (erased) 652 *erased = opErased; 653 LLVM_DEBUG(if (failed(converged)) { 654 llvm::dbgs() << "The pattern rewrite doesn't converge after scanning " 655 << config.maxIterations << " times"; 656 }); 657 return converged; 658 } 659 660 bool mlir::applyOpPatternsAndFold(ArrayRef<Operation *> ops, 661 const FrozenRewritePatternSet &patterns, 662 bool strict) { 663 if (ops.empty()) 664 return false; 665 666 // Start the pattern driver. 667 MultiOpPatternRewriteDriver driver(ops.front()->getContext(), patterns, 668 strict); 669 return driver.simplifyLocally(ops); 670 } 671