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 611 // Nulls get added to the worklist when operations are removed, ignore 612 // them. 613 if (op == nullptr) 614 continue; 615 616 assert((!strictMode || strictModeFilteredOps.contains(op)) && 617 "unexpected op was inserted under strict mode"); 618 619 // If the operation is trivially dead - remove it. 620 if (isOpTriviallyDead(op)) { 621 notifyOperationRemoved(op); 622 op->erase(); 623 changed = true; 624 continue; 625 } 626 627 // Collects all the operands and result uses of the given `op` into work 628 // list. Also remove `op` and nested ops from worklist. 629 originalOperands.assign(op->operand_begin(), op->operand_end()); 630 auto preReplaceAction = [&](Operation *op) { 631 // Add the operands to the worklist for visitation. 632 addOperandsToWorklist(originalOperands); 633 634 // Add all the users of the result to the worklist so we make sure 635 // to revisit them. 636 for (Value result : op->getResults()) 637 for (Operation *userOp : result.getUsers()) { 638 if (!strictMode || strictModeFilteredOps.contains(userOp)) 639 addToWorklist(userOp); 640 } 641 notifyOperationRemoved(op); 642 }; 643 644 // Add the given operation generated by the folder to the worklist. 645 auto processGeneratedConstants = [this](Operation *op) { 646 // Newly created ops are also simplified -- these are also "local". 647 addToWorklist(op); 648 // When strict mode is off, we don't need to maintain 649 // strictModeFilteredOps. 650 if (strictMode) 651 strictModeFilteredOps.insert(op); 652 }; 653 654 // Try to fold this op. 655 bool inPlaceUpdate; 656 if (succeeded(folder.tryToFold(op, processGeneratedConstants, 657 preReplaceAction, &inPlaceUpdate))) { 658 changed = true; 659 if (!inPlaceUpdate) { 660 // Op has been erased. 661 continue; 662 } 663 } 664 665 // Try to match one of the patterns. The rewriter is automatically 666 // notified of any necessary changes, so there is nothing else to do 667 // here. 668 changed |= succeeded(matcher.matchAndRewrite(op, *this)); 669 } 670 671 return changed; 672 } 673 674 /// Rewrites only `op` using the supplied canonicalization patterns and 675 /// folding. `erased` is set to true if the op is erased as a result of being 676 /// folded, replaced, or dead. 677 LogicalResult mlir::applyOpPatternsAndFold( 678 Operation *op, const FrozenRewritePatternSet &patterns, bool *erased) { 679 // Start the pattern driver. 680 GreedyRewriteConfig config; 681 OpPatternRewriteDriver driver(op->getContext(), patterns); 682 bool opErased; 683 LogicalResult converged = 684 driver.simplifyLocally(op, config.maxIterations, opErased); 685 if (erased) 686 *erased = opErased; 687 LLVM_DEBUG(if (failed(converged)) { 688 llvm::dbgs() << "The pattern rewrite doesn't converge after scanning " 689 << config.maxIterations << " times"; 690 }); 691 return converged; 692 } 693 694 bool mlir::applyOpPatternsAndFold(ArrayRef<Operation *> ops, 695 const FrozenRewritePatternSet &patterns, 696 bool strict) { 697 if (ops.empty()) 698 return false; 699 700 // Start the pattern driver. 701 MultiOpPatternRewriteDriver driver(ops.front()->getContext(), patterns, 702 strict); 703 return driver.simplifyLocally(ops); 704 } 705