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