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 return WalkResult::advance(); 167 } 168 return WalkResult::skip(); 169 }); 170 } 171 172 // Reverse the list so our pop-back loop processes them in-order. 173 std::reverse(worklist.begin(), worklist.end()); 174 // Remember the reverse index. 175 for (size_t i = 0, e = worklist.size(); i != e; ++i) 176 worklistMap[worklist[i]] = i; 177 } 178 179 // These are scratch vectors used in the folding loop below. 180 SmallVector<Value, 8> originalOperands, resultValues; 181 182 changed = false; 183 while (!worklist.empty()) { 184 auto *op = popFromWorklist(); 185 186 // Nulls get added to the worklist when operations are removed, ignore 187 // them. 188 if (op == nullptr) 189 continue; 190 191 LLVM_DEBUG({ 192 logger.getOStream() << "\n"; 193 logger.startLine() << logLineComment; 194 logger.startLine() << "Processing operation : '" << op->getName() 195 << "'(" << op << ") {\n"; 196 logger.indent(); 197 198 // If the operation has no regions, just print it here. 199 if (op->getNumRegions() == 0) { 200 op->print( 201 logger.startLine(), 202 OpPrintingFlags().printGenericOpForm().elideLargeElementsAttrs()); 203 logger.getOStream() << "\n\n"; 204 } 205 }); 206 207 // If the operation is trivially dead - remove it. 208 if (isOpTriviallyDead(op)) { 209 notifyOperationRemoved(op); 210 op->erase(); 211 changed = true; 212 213 LLVM_DEBUG(logResultWithLine("success", "operation is trivially dead")); 214 continue; 215 } 216 217 // Collects all the operands and result uses of the given `op` into work 218 // list. Also remove `op` and nested ops from worklist. 219 originalOperands.assign(op->operand_begin(), op->operand_end()); 220 auto preReplaceAction = [&](Operation *op) { 221 // Add the operands to the worklist for visitation. 222 addToWorklist(originalOperands); 223 224 // Add all the users of the result to the worklist so we make sure 225 // to revisit them. 226 for (auto result : op->getResults()) 227 for (auto *userOp : result.getUsers()) 228 addToWorklist(userOp); 229 230 notifyOperationRemoved(op); 231 }; 232 233 // Add the given operation to the worklist. 234 auto collectOps = [this](Operation *op) { addToWorklist(op); }; 235 236 // Try to fold this op. 237 bool inPlaceUpdate; 238 if ((succeeded(folder.tryToFold(op, collectOps, preReplaceAction, 239 &inPlaceUpdate)))) { 240 LLVM_DEBUG(logResultWithLine("success", "operation was folded")); 241 242 changed = true; 243 if (!inPlaceUpdate) 244 continue; 245 } 246 247 // Try to match one of the patterns. The rewriter is automatically 248 // notified of any necessary changes, so there is nothing else to do 249 // here. 250 #ifndef NDEBUG 251 auto canApply = [&](const Pattern &pattern) { 252 LLVM_DEBUG({ 253 logger.getOStream() << "\n"; 254 logger.startLine() << "* Pattern " << pattern.getDebugName() << " : '" 255 << op->getName() << " -> ("; 256 llvm::interleaveComma(pattern.getGeneratedOps(), logger.getOStream()); 257 logger.getOStream() << ")' {\n"; 258 logger.indent(); 259 }); 260 return true; 261 }; 262 auto onFailure = [&](const Pattern &pattern) { 263 LLVM_DEBUG(logResult("failure", "pattern failed to match")); 264 }; 265 auto onSuccess = [&](const Pattern &pattern) { 266 LLVM_DEBUG(logResult("success", "pattern applied successfully")); 267 return success(); 268 }; 269 270 LogicalResult matchResult = 271 matcher.matchAndRewrite(op, *this, canApply, onFailure, onSuccess); 272 if (succeeded(matchResult)) 273 LLVM_DEBUG(logResultWithLine("success", "pattern matched")); 274 else 275 LLVM_DEBUG(logResultWithLine("failure", "pattern failed to match")); 276 #else 277 LogicalResult matchResult = matcher.matchAndRewrite(op, *this); 278 #endif 279 changed |= succeeded(matchResult); 280 } 281 282 // After applying patterns, make sure that the CFG of each of the regions 283 // is kept up to date. 284 if (config.enableRegionSimplification) 285 changed |= succeeded(simplifyRegions(*this, regions)); 286 } while (changed && 287 (iteration++ < config.maxIterations || 288 config.maxIterations == GreedyRewriteConfig::kNoIterationLimit)); 289 290 // Whether the rewrite converges, i.e. wasn't changed in the last iteration. 291 return !changed; 292 } 293 294 void GreedyPatternRewriteDriver::addToWorklist(Operation *op) { 295 // Check to see if the worklist already contains this op. 296 if (worklistMap.count(op)) 297 return; 298 299 worklistMap[op] = worklist.size(); 300 worklist.push_back(op); 301 } 302 303 Operation *GreedyPatternRewriteDriver::popFromWorklist() { 304 auto *op = worklist.back(); 305 worklist.pop_back(); 306 307 // This operation is no longer in the worklist, keep worklistMap up to date. 308 if (op) 309 worklistMap.erase(op); 310 return op; 311 } 312 313 void GreedyPatternRewriteDriver::removeFromWorklist(Operation *op) { 314 auto it = worklistMap.find(op); 315 if (it != worklistMap.end()) { 316 assert(worklist[it->second] == op && "malformed worklist data structure"); 317 worklist[it->second] = nullptr; 318 worklistMap.erase(it); 319 } 320 } 321 322 void GreedyPatternRewriteDriver::notifyOperationInserted(Operation *op) { 323 LLVM_DEBUG({ 324 logger.startLine() << "** Insert : '" << op->getName() << "'(" << op 325 << ")\n"; 326 }); 327 addToWorklist(op); 328 } 329 330 template <typename Operands> 331 void GreedyPatternRewriteDriver::addToWorklist(Operands &&operands) { 332 for (Value operand : operands) { 333 // If the use count of this operand is now < 2, we re-add the defining 334 // operation to the worklist. 335 // TODO: This is based on the fact that zero use operations 336 // may be deleted, and that single use values often have more 337 // canonicalization opportunities. 338 if (!operand || (!operand.use_empty() && !operand.hasOneUse())) 339 continue; 340 if (auto *defOp = operand.getDefiningOp()) 341 addToWorklist(defOp); 342 } 343 } 344 345 void GreedyPatternRewriteDriver::notifyOperationRemoved(Operation *op) { 346 addToWorklist(op->getOperands()); 347 op->walk([this](Operation *operation) { 348 removeFromWorklist(operation); 349 folder.notifyRemoval(operation); 350 }); 351 } 352 353 void GreedyPatternRewriteDriver::notifyRootReplaced(Operation *op) { 354 LLVM_DEBUG({ 355 logger.startLine() << "** Replace : '" << op->getName() << "'(" << op 356 << ")\n"; 357 }); 358 for (auto result : op->getResults()) 359 for (auto *user : result.getUsers()) 360 addToWorklist(user); 361 } 362 363 void GreedyPatternRewriteDriver::eraseOp(Operation *op) { 364 LLVM_DEBUG({ 365 logger.startLine() << "** Erase : '" << op->getName() << "'(" << op 366 << ")\n"; 367 }); 368 PatternRewriter::eraseOp(op); 369 } 370 371 LogicalResult GreedyPatternRewriteDriver::notifyMatchFailure( 372 Location loc, function_ref<void(Diagnostic &)> reasonCallback) { 373 LLVM_DEBUG({ 374 Diagnostic diag(loc, DiagnosticSeverity::Remark); 375 reasonCallback(diag); 376 logger.startLine() << "** Failure : " << diag.str() << "\n"; 377 }); 378 return failure(); 379 } 380 381 /// Rewrite the regions of the specified operation, which must be isolated from 382 /// above, by repeatedly applying the highest benefit patterns in a greedy 383 /// work-list driven manner. Return success if no more patterns can be matched 384 /// in the result operation regions. Note: This does not apply patterns to the 385 /// top-level operation itself. 386 /// 387 LogicalResult 388 mlir::applyPatternsAndFoldGreedily(MutableArrayRef<Region> regions, 389 const FrozenRewritePatternSet &patterns, 390 GreedyRewriteConfig config) { 391 if (regions.empty()) 392 return success(); 393 394 // The top-level operation must be known to be isolated from above to 395 // prevent performing canonicalizations on operations defined at or above 396 // the region containing 'op'. 397 auto regionIsIsolated = [](Region ®ion) { 398 return region.getParentOp()->hasTrait<OpTrait::IsIsolatedFromAbove>(); 399 }; 400 (void)regionIsIsolated; 401 assert(llvm::all_of(regions, regionIsIsolated) && 402 "patterns can only be applied to operations IsolatedFromAbove"); 403 404 // Start the pattern driver. 405 GreedyPatternRewriteDriver driver(regions[0].getContext(), patterns, config); 406 bool converged = driver.simplify(regions); 407 LLVM_DEBUG(if (!converged) { 408 llvm::dbgs() << "The pattern rewrite doesn't converge after scanning " 409 << config.maxIterations << " times\n"; 410 }); 411 return success(converged); 412 } 413 414 //===----------------------------------------------------------------------===// 415 // OpPatternRewriteDriver 416 //===----------------------------------------------------------------------===// 417 418 namespace { 419 /// This is a simple driver for the PatternMatcher to apply patterns and perform 420 /// folding on a single op. It repeatedly applies locally optimal patterns. 421 class OpPatternRewriteDriver : public PatternRewriter { 422 public: 423 explicit OpPatternRewriteDriver(MLIRContext *ctx, 424 const FrozenRewritePatternSet &patterns) 425 : PatternRewriter(ctx), matcher(patterns), folder(ctx) { 426 // Apply a simple cost model based solely on pattern benefit. 427 matcher.applyDefaultCostModel(); 428 } 429 430 LogicalResult simplifyLocally(Operation *op, int maxIterations, bool &erased); 431 432 // These are hooks implemented for PatternRewriter. 433 protected: 434 /// If an operation is about to be removed, mark it so that we can let clients 435 /// know. 436 void notifyOperationRemoved(Operation *op) override { 437 opErasedViaPatternRewrites = true; 438 } 439 440 // When a root is going to be replaced, its removal will be notified as well. 441 // So there is nothing to do here. 442 void notifyRootReplaced(Operation *op) override {} 443 444 private: 445 /// The low-level pattern applicator. 446 PatternApplicator matcher; 447 448 /// Non-pattern based folder for operations. 449 OperationFolder folder; 450 451 /// Set to true if the operation has been erased via pattern rewrites. 452 bool opErasedViaPatternRewrites = false; 453 }; 454 455 } // namespace 456 457 /// Performs the rewrites and folding only on `op`. The simplification 458 /// converges if the op is erased as a result of being folded, replaced, or 459 /// becoming dead, or no more changes happen in an iteration. Returns success if 460 /// the rewrite converges in `maxIterations`. `erased` is set to true if `op` 461 /// gets erased. 462 LogicalResult OpPatternRewriteDriver::simplifyLocally(Operation *op, 463 int maxIterations, 464 bool &erased) { 465 bool changed = false; 466 erased = false; 467 opErasedViaPatternRewrites = false; 468 int iterations = 0; 469 // Iterate until convergence or until maxIterations. Deletion of the op as 470 // a result of being dead or folded is convergence. 471 do { 472 changed = false; 473 474 // If the operation is trivially dead - remove it. 475 if (isOpTriviallyDead(op)) { 476 op->erase(); 477 erased = true; 478 return success(); 479 } 480 481 // Try to fold this op. 482 bool inPlaceUpdate; 483 if (succeeded(folder.tryToFold(op, /*processGeneratedConstants=*/nullptr, 484 /*preReplaceAction=*/nullptr, 485 &inPlaceUpdate))) { 486 changed = true; 487 if (!inPlaceUpdate) { 488 erased = true; 489 return success(); 490 } 491 } 492 493 // Try to match one of the patterns. The rewriter is automatically 494 // notified of any necessary changes, so there is nothing else to do here. 495 changed |= succeeded(matcher.matchAndRewrite(op, *this)); 496 if ((erased = opErasedViaPatternRewrites)) 497 return success(); 498 } while (changed && 499 (++iterations < maxIterations || 500 maxIterations == GreedyRewriteConfig::kNoIterationLimit)); 501 502 // Whether the rewrite converges, i.e. wasn't changed in the last iteration. 503 return failure(changed); 504 } 505 506 //===----------------------------------------------------------------------===// 507 // MultiOpPatternRewriteDriver 508 //===----------------------------------------------------------------------===// 509 510 namespace { 511 512 /// This is a specialized GreedyPatternRewriteDriver to apply patterns and 513 /// perform folding for a supplied set of ops. It repeatedly simplifies while 514 /// restricting the rewrites to only the provided set of ops or optionally 515 /// to those directly affected by it (result users or operand providers). 516 class MultiOpPatternRewriteDriver : public GreedyPatternRewriteDriver { 517 public: 518 explicit MultiOpPatternRewriteDriver(MLIRContext *ctx, 519 const FrozenRewritePatternSet &patterns, 520 bool strict) 521 : GreedyPatternRewriteDriver(ctx, patterns, GreedyRewriteConfig()), 522 strictMode(strict) {} 523 524 bool simplifyLocally(ArrayRef<Operation *> op); 525 526 private: 527 // Look over the provided operands for any defining operations that should 528 // be re-added to the worklist. This function should be called when an 529 // operation is modified or removed, as it may trigger further 530 // simplifications. If `strict` is set to true, only ops in 531 // `strictModeFilteredOps` are considered. 532 template <typename Operands> 533 void addOperandsToWorklist(Operands &&operands) { 534 for (Value operand : operands) { 535 if (auto *defOp = operand.getDefiningOp()) { 536 if (!strictMode || strictModeFilteredOps.contains(defOp)) 537 addToWorklist(defOp); 538 } 539 } 540 } 541 542 void notifyOperationInserted(Operation *op) override { 543 GreedyPatternRewriteDriver::notifyOperationInserted(op); 544 if (strictMode) 545 strictModeFilteredOps.insert(op); 546 } 547 548 void notifyOperationRemoved(Operation *op) override { 549 GreedyPatternRewriteDriver::notifyOperationRemoved(op); 550 if (strictMode) 551 strictModeFilteredOps.erase(op); 552 } 553 554 void notifyRootReplaced(Operation *op) override { 555 for (auto result : op->getResults()) { 556 for (auto *user : result.getUsers()) { 557 if (!strictMode || strictModeFilteredOps.contains(user)) 558 addToWorklist(user); 559 } 560 } 561 } 562 563 /// If `strictMode` is true, any pre-existing ops outside of 564 /// `strictModeFilteredOps` remain completely untouched by the rewrite driver. 565 /// If `strictMode` is false, operations that use results of (or supply 566 /// operands to) any rewritten ops stemming from the simplification of the 567 /// provided ops are in turn simplified; any other ops still remain untouched 568 /// (i.e., regardless of `strictMode`). 569 bool strictMode = false; 570 571 /// The list of ops we are restricting our rewrites to if `strictMode` is on. 572 /// These include the supplied set of ops as well as new ops created while 573 /// rewriting those ops. This set is not maintained when strictMode is off. 574 llvm::SmallDenseSet<Operation *, 4> strictModeFilteredOps; 575 }; 576 577 } // namespace 578 579 /// Performs the specified rewrites on `ops` while also trying to fold these ops 580 /// as well as any other ops that were in turn created due to these rewrite 581 /// patterns. Any pre-existing ops outside of `ops` remain completely 582 /// unmodified if `strictMode` is true. If `strictMode` is false, other 583 /// operations that use results of rewritten ops or supply operands to such ops 584 /// are in turn simplified; any other ops still remain unmodified (i.e., 585 /// regardless of `strictMode`). Note that ops in `ops` could be erased as a 586 /// result of folding, becoming dead, or via pattern rewrites. Returns true if 587 /// at all any changes happened. 588 // Unlike `OpPatternRewriteDriver::simplifyLocally` which works on a single op 589 // or GreedyPatternRewriteDriver::simplify, this method just iterates until 590 // the worklist is empty. As our objective is to keep simplification "local", 591 // there is no strong rationale to re-add all operations into the worklist and 592 // rerun until an iteration changes nothing. If more widereaching simplification 593 // is desired, GreedyPatternRewriteDriver should be used. 594 bool MultiOpPatternRewriteDriver::simplifyLocally(ArrayRef<Operation *> ops) { 595 if (strictMode) { 596 strictModeFilteredOps.clear(); 597 strictModeFilteredOps.insert(ops.begin(), ops.end()); 598 } 599 600 bool changed = false; 601 worklist.clear(); 602 worklistMap.clear(); 603 for (Operation *op : ops) 604 addToWorklist(op); 605 606 // These are scratch vectors used in the folding loop below. 607 SmallVector<Value, 8> originalOperands, resultValues; 608 while (!worklist.empty()) { 609 Operation *op = popFromWorklist(); 610 assert((!strictMode || strictModeFilteredOps.contains(op)) && 611 "unexpected op was inserted under strict mode"); 612 613 // Nulls get added to the worklist when operations are removed, ignore 614 // them. 615 if (op == nullptr) 616 continue; 617 618 // If the operation is trivially dead - remove it. 619 if (isOpTriviallyDead(op)) { 620 notifyOperationRemoved(op); 621 op->erase(); 622 changed = true; 623 continue; 624 } 625 626 // Collects all the operands and result uses of the given `op` into work 627 // list. Also remove `op` and nested ops from worklist. 628 originalOperands.assign(op->operand_begin(), op->operand_end()); 629 auto preReplaceAction = [&](Operation *op) { 630 // Add the operands to the worklist for visitation. 631 addOperandsToWorklist(originalOperands); 632 633 // Add all the users of the result to the worklist so we make sure 634 // to revisit them. 635 for (Value result : op->getResults()) 636 for (Operation *userOp : result.getUsers()) { 637 if (!strictMode || strictModeFilteredOps.contains(userOp)) 638 addToWorklist(userOp); 639 } 640 notifyOperationRemoved(op); 641 }; 642 643 // Add the given operation generated by the folder to the worklist. 644 auto processGeneratedConstants = [this](Operation *op) { 645 // Newly created ops are also simplified -- these are also "local". 646 addToWorklist(op); 647 // When strict mode is off, we don't need to maintain 648 // strictModeFilteredOps. 649 if (strictMode) 650 strictModeFilteredOps.insert(op); 651 }; 652 653 // Try to fold this op. 654 bool inPlaceUpdate; 655 if (succeeded(folder.tryToFold(op, processGeneratedConstants, 656 preReplaceAction, &inPlaceUpdate))) { 657 changed = true; 658 if (!inPlaceUpdate) { 659 // Op has been erased. 660 continue; 661 } 662 } 663 664 // Try to match one of the patterns. The rewriter is automatically 665 // notified of any necessary changes, so there is nothing else to do 666 // here. 667 changed |= succeeded(matcher.matchAndRewrite(op, *this)); 668 } 669 670 return changed; 671 } 672 673 /// Rewrites only `op` using the supplied canonicalization patterns and 674 /// folding. `erased` is set to true if the op is erased as a result of being 675 /// folded, replaced, or dead. 676 LogicalResult mlir::applyOpPatternsAndFold( 677 Operation *op, const FrozenRewritePatternSet &patterns, bool *erased) { 678 // Start the pattern driver. 679 GreedyRewriteConfig config; 680 OpPatternRewriteDriver driver(op->getContext(), patterns); 681 bool opErased; 682 LogicalResult converged = 683 driver.simplifyLocally(op, config.maxIterations, opErased); 684 if (erased) 685 *erased = opErased; 686 LLVM_DEBUG(if (failed(converged)) { 687 llvm::dbgs() << "The pattern rewrite doesn't converge after scanning " 688 << config.maxIterations << " times"; 689 }); 690 return converged; 691 } 692 693 bool mlir::applyOpPatternsAndFold(ArrayRef<Operation *> ops, 694 const FrozenRewritePatternSet &patterns, 695 bool strict) { 696 if (ops.empty()) 697 return false; 698 699 // Start the pattern driver. 700 MultiOpPatternRewriteDriver driver(ops.front()->getContext(), patterns, 701 strict); 702 return driver.simplifyLocally(ops); 703 } 704