1 //===- SCCP.cpp - Sparse Conditional Constant Propagation -----------------===// 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 transformation pass performs a sparse conditional constant propagation 10 // in MLIR. It identifies values known to be constant, propagates that 11 // information throughout the IR, and replaces them. This is done with an 12 // optimisitic dataflow analysis that assumes that all values are constant until 13 // proven otherwise. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "PassDetail.h" 18 #include "mlir/IR/Builders.h" 19 #include "mlir/IR/Dialect.h" 20 #include "mlir/Interfaces/ControlFlowInterfaces.h" 21 #include "mlir/Interfaces/SideEffects.h" 22 #include "mlir/Pass/Pass.h" 23 #include "mlir/Transforms/FoldUtils.h" 24 #include "mlir/Transforms/Passes.h" 25 26 using namespace mlir; 27 28 namespace { 29 /// This class represents a single lattice value. A lattive value corresponds to 30 /// the various different states that a value in the SCCP dataflow anaylsis can 31 /// take. See 'Kind' below for more details on the different states a value can 32 /// take. 33 class LatticeValue { 34 enum Kind { 35 /// A value with a yet to be determined value. This state may be changed to 36 /// anything. 37 Unknown, 38 39 /// A value that is known to be a constant. This state may be changed to 40 /// overdefined. 41 Constant, 42 43 /// A value that cannot statically be determined to be a constant. This 44 /// state cannot be changed. 45 Overdefined 46 }; 47 48 public: 49 /// Initialize a lattice value with "Unknown". 50 LatticeValue() 51 : constantAndTag(nullptr, Kind::Unknown), constantDialect(nullptr) {} 52 /// Initialize a lattice value with a constant. 53 LatticeValue(Attribute attr, Dialect *dialect) 54 : constantAndTag(attr, Kind::Constant), constantDialect(dialect) {} 55 56 /// Returns true if this lattice value is unknown. 57 bool isUnknown() const { return constantAndTag.getInt() == Kind::Unknown; } 58 59 /// Mark the lattice value as overdefined. 60 void markOverdefined() { 61 constantAndTag.setPointerAndInt(nullptr, Kind::Overdefined); 62 constantDialect = nullptr; 63 } 64 65 /// Returns true if the lattice is overdefined. 66 bool isOverdefined() const { 67 return constantAndTag.getInt() == Kind::Overdefined; 68 } 69 70 /// Mark the lattice value as constant. 71 void markConstant(Attribute value, Dialect *dialect) { 72 constantAndTag.setPointerAndInt(value, Kind::Constant); 73 constantDialect = dialect; 74 } 75 76 /// If this lattice is constant, return the constant. Returns nullptr 77 /// otherwise. 78 Attribute getConstant() const { return constantAndTag.getPointer(); } 79 80 /// If this lattice is constant, return the dialect to use when materializing 81 /// the constant. 82 Dialect *getConstantDialect() const { 83 assert(getConstant() && "expected valid constant"); 84 return constantDialect; 85 } 86 87 /// Merge in the value of the 'rhs' lattice into this one. Returns true if the 88 /// lattice value changed. 89 bool meet(const LatticeValue &rhs) { 90 // If we are already overdefined, or rhs is unknown, there is nothing to do. 91 if (isOverdefined() || rhs.isUnknown()) 92 return false; 93 // If we are unknown, just take the value of rhs. 94 if (isUnknown()) { 95 constantAndTag = rhs.constantAndTag; 96 constantDialect = rhs.constantDialect; 97 return true; 98 } 99 100 // Otherwise, if this value doesn't match rhs go straight to overdefined. 101 if (constantAndTag != rhs.constantAndTag) { 102 markOverdefined(); 103 return true; 104 } 105 return false; 106 } 107 108 private: 109 /// The attribute value if this is a constant and the tag for the element 110 /// kind. 111 llvm::PointerIntPair<Attribute, 2, Kind> constantAndTag; 112 113 /// The dialect the constant originated from. This is only valid if the 114 /// lattice is a constant. This is not used as part of the key, and is only 115 /// needed to materialize the held constant if necessary. 116 Dialect *constantDialect; 117 }; 118 119 /// This class represents the solver for the SCCP analysis. This class acts as 120 /// the propagation engine for computing which values form constants. 121 class SCCPSolver { 122 public: 123 /// Initialize the solver with a given set of regions. 124 SCCPSolver(MutableArrayRef<Region> regions); 125 126 /// Run the solver until it converges. 127 void solve(); 128 129 /// Rewrite the given regions using the computing analysis. This replaces the 130 /// uses of all values that have been computed to be constant, and erases as 131 /// many newly dead operations. 132 void rewrite(MLIRContext *context, MutableArrayRef<Region> regions); 133 134 private: 135 /// Replace the given value with a constant if the corresponding lattice 136 /// represents a constant. Returns success if the value was replaced, failure 137 /// otherwise. 138 LogicalResult replaceWithConstant(OpBuilder &builder, OperationFolder &folder, 139 Value value); 140 141 /// Visit the users of the given IR that reside within executable blocks. 142 template <typename T> 143 void visitUsers(T &value) { 144 for (Operation *user : value.getUsers()) 145 if (isBlockExecutable(user->getBlock())) 146 visitOperation(user); 147 } 148 149 /// Visit the given operation and compute any necessary lattice state. 150 void visitOperation(Operation *op); 151 152 /// Visit the given operation, which defines regions, and compute any 153 /// necessary lattice state. This also resolves the lattice state of both the 154 /// operation results and any nested regions. 155 void visitRegionOperation(Operation *op, 156 ArrayRef<Attribute> constantOperands); 157 158 /// Visit the given set of region successors, computing any necessary lattice 159 /// state. The provided function returns the input operands to the region at 160 /// the given index. If the index is 'None', the input operands correspond to 161 /// the parent operation results. 162 void visitRegionSuccessors( 163 Operation *parentOp, ArrayRef<RegionSuccessor> regionSuccessors, 164 function_ref<OperandRange(Optional<unsigned>)> getInputsForRegion); 165 166 /// Visit the given terminator operation and compute any necessary lattice 167 /// state. 168 void visitTerminatorOperation(Operation *op, 169 ArrayRef<Attribute> constantOperands); 170 171 /// Visit the given block and compute any necessary lattice state. 172 void visitBlock(Block *block); 173 174 /// Visit argument #'i' of the given block and compute any necessary lattice 175 /// state. 176 void visitBlockArgument(Block *block, int i); 177 178 /// Mark the given block as executable. Returns false if the block was already 179 /// marked executable. 180 bool markBlockExecutable(Block *block); 181 182 /// Returns true if the given block is executable. 183 bool isBlockExecutable(Block *block) const; 184 185 /// Mark the edge between 'from' and 'to' as executable. 186 void markEdgeExecutable(Block *from, Block *to); 187 188 /// Return true if the edge between 'from' and 'to' is executable. 189 bool isEdgeExecutable(Block *from, Block *to) const; 190 191 /// Mark the given value as overdefined. This means that we cannot refine a 192 /// specific constant for this value. 193 void markOverdefined(Value value); 194 195 /// Mark all of the given values as overdefined. 196 template <typename ValuesT> 197 void markAllOverdefined(ValuesT values) { 198 for (auto value : values) 199 markOverdefined(value); 200 } 201 template <typename ValuesT> 202 void markAllOverdefined(Operation *op, ValuesT values) { 203 markAllOverdefined(values); 204 opWorklist.push_back(op); 205 } 206 template <typename ValuesT> 207 void markAllOverdefinedAndVisitUsers(ValuesT values) { 208 for (auto value : values) { 209 auto &lattice = latticeValues[value]; 210 if (!lattice.isOverdefined()) { 211 lattice.markOverdefined(); 212 visitUsers(value); 213 } 214 } 215 } 216 217 /// Returns true if the given value was marked as overdefined. 218 bool isOverdefined(Value value) const; 219 220 /// Merge in the given lattice 'from' into the lattice 'to'. 'owner' 221 /// corresponds to the parent operation of 'to'. 222 void meet(Operation *owner, LatticeValue &to, const LatticeValue &from); 223 224 /// The lattice for each SSA value. 225 DenseMap<Value, LatticeValue> latticeValues; 226 227 /// The set of blocks that are known to execute, or are intrinsically live. 228 SmallPtrSet<Block *, 16> executableBlocks; 229 230 /// The set of control flow edges that are known to execute. 231 DenseSet<std::pair<Block *, Block *>> executableEdges; 232 233 /// A worklist containing blocks that need to be processed. 234 SmallVector<Block *, 64> blockWorklist; 235 236 /// A worklist of operations that need to be processed. 237 SmallVector<Operation *, 64> opWorklist; 238 }; 239 } // end anonymous namespace 240 241 SCCPSolver::SCCPSolver(MutableArrayRef<Region> regions) { 242 for (Region ®ion : regions) { 243 if (region.empty()) 244 continue; 245 Block *entryBlock = ®ion.front(); 246 247 // Mark the entry block as executable. 248 markBlockExecutable(entryBlock); 249 250 // The values passed to these regions are invisible, so mark any arguments 251 // as overdefined. 252 markAllOverdefined(entryBlock->getArguments()); 253 } 254 } 255 256 void SCCPSolver::solve() { 257 while (!blockWorklist.empty() || !opWorklist.empty()) { 258 // Process any operations in the op worklist. 259 while (!opWorklist.empty()) 260 visitUsers(*opWorklist.pop_back_val()); 261 262 // Process any blocks in the block worklist. 263 while (!blockWorklist.empty()) 264 visitBlock(blockWorklist.pop_back_val()); 265 } 266 } 267 268 void SCCPSolver::rewrite(MLIRContext *context, 269 MutableArrayRef<Region> initialRegions) { 270 SmallVector<Block *, 8> worklist; 271 auto addToWorklist = [&](MutableArrayRef<Region> regions) { 272 for (Region ®ion : regions) 273 for (Block &block : region) 274 if (isBlockExecutable(&block)) 275 worklist.push_back(&block); 276 }; 277 278 // An operation folder used to create and unique constants. 279 OperationFolder folder(context); 280 OpBuilder builder(context); 281 282 addToWorklist(initialRegions); 283 while (!worklist.empty()) { 284 Block *block = worklist.pop_back_val(); 285 286 // Replace any block arguments with constants. 287 builder.setInsertionPointToStart(block); 288 for (BlockArgument arg : block->getArguments()) 289 replaceWithConstant(builder, folder, arg); 290 291 for (Operation &op : llvm::make_early_inc_range(*block)) { 292 builder.setInsertionPoint(&op); 293 294 // Replace any result with constants. 295 bool replacedAll = op.getNumResults() != 0; 296 for (Value res : op.getResults()) 297 replacedAll &= succeeded(replaceWithConstant(builder, folder, res)); 298 299 // If all of the results of the operation were replaced, try to erase 300 // the operation completely. 301 if (replacedAll && wouldOpBeTriviallyDead(&op)) { 302 assert(op.use_empty() && "expected all uses to be replaced"); 303 op.erase(); 304 continue; 305 } 306 307 // Add any the regions of this operation to the worklist. 308 addToWorklist(op.getRegions()); 309 } 310 } 311 } 312 313 LogicalResult SCCPSolver::replaceWithConstant(OpBuilder &builder, 314 OperationFolder &folder, 315 Value value) { 316 auto it = latticeValues.find(value); 317 auto attr = it == latticeValues.end() ? nullptr : it->second.getConstant(); 318 if (!attr) 319 return failure(); 320 321 // Attempt to materialize a constant for the given value. 322 Dialect *dialect = it->second.getConstantDialect(); 323 Value constant = folder.getOrCreateConstant(builder, dialect, attr, 324 value.getType(), value.getLoc()); 325 if (!constant) 326 return failure(); 327 328 value.replaceAllUsesWith(constant); 329 latticeValues.erase(it); 330 return success(); 331 } 332 333 void SCCPSolver::visitOperation(Operation *op) { 334 // Collect all of the constant operands feeding into this operation. If any 335 // are not ready to be resolved, bail out and wait for them to resolve. 336 SmallVector<Attribute, 8> operandConstants; 337 operandConstants.reserve(op->getNumOperands()); 338 for (Value operand : op->getOperands()) { 339 // Make sure all of the operands are resolved first. 340 auto &operandLattice = latticeValues[operand]; 341 if (operandLattice.isUnknown()) 342 return; 343 operandConstants.push_back(operandLattice.getConstant()); 344 } 345 346 // If this is a terminator operation, process any control flow lattice state. 347 if (op->isKnownTerminator()) 348 visitTerminatorOperation(op, operandConstants); 349 350 // Process region holding operations. The region visitor processes result 351 // values, so we can exit afterwards. 352 if (op->getNumRegions()) 353 return visitRegionOperation(op, operandConstants); 354 355 // If this op produces no results, it can't produce any constants. 356 if (op->getNumResults() == 0) 357 return; 358 359 // If all of the results of this operation are already overdefined, bail out 360 // early. 361 auto isOverdefinedFn = [&](Value value) { return isOverdefined(value); }; 362 if (llvm::all_of(op->getResults(), isOverdefinedFn)) 363 return; 364 365 // Save the original operands and attributes just in case the operation folds 366 // in-place. The constant passed in may not correspond to the real runtime 367 // value, so in-place updates are not allowed. 368 SmallVector<Value, 8> originalOperands(op->getOperands()); 369 NamedAttributeList originalAttrs = op->getAttrList(); 370 371 // Simulate the result of folding this operation to a constant. If folding 372 // fails or was an in-place fold, mark the results as overdefined. 373 SmallVector<OpFoldResult, 8> foldResults; 374 foldResults.reserve(op->getNumResults()); 375 if (failed(op->fold(operandConstants, foldResults))) 376 return markAllOverdefined(op, op->getResults()); 377 378 // If the folding was in-place, mark the results as overdefined and reset the 379 // operation. We don't allow in-place folds as the desire here is for 380 // simulated execution, and not general folding. 381 if (foldResults.empty()) { 382 op->setOperands(originalOperands); 383 op->setAttrs(originalAttrs); 384 return markAllOverdefined(op, op->getResults()); 385 } 386 387 // Merge the fold results into the lattice for this operation. 388 assert(foldResults.size() == op->getNumResults() && "invalid result size"); 389 Dialect *opDialect = op->getDialect(); 390 for (unsigned i = 0, e = foldResults.size(); i != e; ++i) { 391 LatticeValue &resultLattice = latticeValues[op->getResult(i)]; 392 393 // Merge in the result of the fold, either a constant or a value. 394 OpFoldResult foldResult = foldResults[i]; 395 if (Attribute foldAttr = foldResult.dyn_cast<Attribute>()) 396 meet(op, resultLattice, LatticeValue(foldAttr, opDialect)); 397 else 398 meet(op, resultLattice, latticeValues[foldResult.get<Value>()]); 399 } 400 } 401 402 void SCCPSolver::visitRegionOperation(Operation *op, 403 ArrayRef<Attribute> constantOperands) { 404 // Check to see if we can reason about the internal control flow of this 405 // region operation. 406 auto regionInterface = dyn_cast<RegionBranchOpInterface>(op); 407 if (!regionInterface) { 408 // If we can't, conservatively mark all regions as executable. 409 for (Region ®ion : op->getRegions()) { 410 if (region.empty()) 411 continue; 412 Block *entryBlock = ®ion.front(); 413 markBlockExecutable(entryBlock); 414 markAllOverdefined(entryBlock->getArguments()); 415 } 416 417 // Don't try to simulate the results of a region operation as we can't 418 // guarantee that folding will be out-of-place. We don't allow in-place 419 // folds as the desire here is for simulated execution, and not general 420 // folding. 421 return markAllOverdefined(op, op->getResults()); 422 } 423 424 // Check to see which regions are executable. 425 SmallVector<RegionSuccessor, 1> successors; 426 regionInterface.getSuccessorRegions(/*index=*/llvm::None, constantOperands, 427 successors); 428 429 // If the interface identified that no region will be executed. Mark 430 // any results of this operation as overdefined, as we can't reason about 431 // them. 432 // TODO: If we had an interface to detect pass through operands, we could 433 // resolve some results based on the lattice state of the operands. We could 434 // also allow for the parent operation to have itself as a region successor. 435 if (successors.empty()) 436 return markAllOverdefined(op, op->getResults()); 437 return visitRegionSuccessors(op, successors, [&](Optional<unsigned> index) { 438 assert(index && "expected valid region index"); 439 return regionInterface.getSuccessorEntryOperands(*index); 440 }); 441 } 442 443 void SCCPSolver::visitRegionSuccessors( 444 Operation *parentOp, ArrayRef<RegionSuccessor> regionSuccessors, 445 function_ref<OperandRange(Optional<unsigned>)> getInputsForRegion) { 446 for (const RegionSuccessor &it : regionSuccessors) { 447 Region *region = it.getSuccessor(); 448 ValueRange succArgs = it.getSuccessorInputs(); 449 450 // Check to see if this is the parent operation. 451 if (!region) { 452 ResultRange results = parentOp->getResults(); 453 if (llvm::all_of(results, [&](Value res) { return isOverdefined(res); })) 454 continue; 455 456 // Mark the results outside of the input range as overdefined. 457 if (succArgs.size() != results.size()) { 458 opWorklist.push_back(parentOp); 459 if (succArgs.empty()) 460 return markAllOverdefined(results); 461 462 unsigned firstResIdx = succArgs[0].cast<OpResult>().getResultNumber(); 463 markAllOverdefined(results.take_front(firstResIdx)); 464 markAllOverdefined(results.drop_front(firstResIdx + succArgs.size())); 465 } 466 467 // Update the lattice for any operation results. 468 OperandRange operands = getInputsForRegion(/*index=*/llvm::None); 469 for (auto it : llvm::zip(succArgs, operands)) 470 meet(parentOp, latticeValues[std::get<0>(it)], 471 latticeValues[std::get<1>(it)]); 472 return; 473 } 474 assert(!region->empty() && "expected region to be non-empty"); 475 Block *entryBlock = ®ion->front(); 476 markBlockExecutable(entryBlock); 477 478 // If all of the arguments are already overdefined, the arguments have 479 // already been fully resolved. 480 auto arguments = entryBlock->getArguments(); 481 if (llvm::all_of(arguments, [&](Value arg) { return isOverdefined(arg); })) 482 continue; 483 484 // Mark any arguments that do not receive inputs as overdefined, we won't be 485 // able to discern if they are constant. 486 if (succArgs.size() != arguments.size()) { 487 if (succArgs.empty()) { 488 markAllOverdefined(arguments); 489 continue; 490 } 491 492 unsigned firstArgIdx = succArgs[0].cast<BlockArgument>().getArgNumber(); 493 markAllOverdefinedAndVisitUsers(arguments.take_front(firstArgIdx)); 494 markAllOverdefinedAndVisitUsers( 495 arguments.drop_front(firstArgIdx + succArgs.size())); 496 } 497 498 // Update the lattice for arguments that have inputs from the predecessor. 499 OperandRange succOperands = getInputsForRegion(region->getRegionNumber()); 500 for (auto it : llvm::zip(succArgs, succOperands)) { 501 LatticeValue &argLattice = latticeValues[std::get<0>(it)]; 502 if (argLattice.meet(latticeValues[std::get<1>(it)])) 503 visitUsers(std::get<0>(it)); 504 } 505 } 506 } 507 508 void SCCPSolver::visitTerminatorOperation( 509 Operation *op, ArrayRef<Attribute> constantOperands) { 510 // If this operation has no successors, we treat it as an exiting terminator. 511 if (op->getNumSuccessors() == 0) { 512 // Check to see if the parent tracks region control flow. 513 Region *parentRegion = op->getParentRegion(); 514 Operation *parentOp = parentRegion->getParentOp(); 515 auto regionInterface = dyn_cast<RegionBranchOpInterface>(parentOp); 516 if (!regionInterface || !isBlockExecutable(parentOp->getBlock())) 517 return; 518 519 // Query the set of successors from the current region. 520 SmallVector<RegionSuccessor, 1> regionSuccessors; 521 regionInterface.getSuccessorRegions(parentRegion->getRegionNumber(), 522 constantOperands, regionSuccessors); 523 if (regionSuccessors.empty()) 524 return; 525 526 // If this terminator is not "region-like", conservatively mark all of the 527 // successor values as overdefined. 528 if (!op->hasTrait<OpTrait::ReturnLike>()) { 529 for (auto &it : regionSuccessors) 530 markAllOverdefinedAndVisitUsers(it.getSuccessorInputs()); 531 return; 532 } 533 534 // Otherwise, propagate the operand lattice states to each of the 535 // successors. 536 OperandRange operands = op->getOperands(); 537 return visitRegionSuccessors(parentOp, regionSuccessors, 538 [&](Optional<unsigned>) { return operands; }); 539 } 540 541 // Try to resolve to a specific successor with the constant operands. 542 if (auto branch = dyn_cast<BranchOpInterface>(op)) { 543 if (Block *singleSucc = branch.getSuccessorForOperands(constantOperands)) { 544 markEdgeExecutable(op->getBlock(), singleSucc); 545 return; 546 } 547 } 548 549 // Otherwise, conservatively treat all edges as executable. 550 Block *block = op->getBlock(); 551 for (Block *succ : op->getSuccessors()) 552 markEdgeExecutable(block, succ); 553 } 554 555 void SCCPSolver::visitBlock(Block *block) { 556 // If the block is not the entry block we need to compute the lattice state 557 // for the block arguments. Entry block argument lattices are computed 558 // elsewhere, such as when visiting the parent operation. 559 if (!block->isEntryBlock()) { 560 for (int i : llvm::seq<int>(0, block->getNumArguments())) 561 visitBlockArgument(block, i); 562 } 563 564 // Visit all of the operations within the block. 565 for (Operation &op : *block) 566 visitOperation(&op); 567 } 568 569 void SCCPSolver::visitBlockArgument(Block *block, int i) { 570 BlockArgument arg = block->getArgument(i); 571 LatticeValue &argLattice = latticeValues[arg]; 572 if (argLattice.isOverdefined()) 573 return; 574 575 bool updatedLattice = false; 576 for (auto it = block->pred_begin(), e = block->pred_end(); it != e; ++it) { 577 Block *pred = *it; 578 579 // We only care about this predecessor if it is going to execute. 580 if (!isEdgeExecutable(pred, block)) 581 continue; 582 583 // Try to get the operand forwarded by the predecessor. If we can't reason 584 // about the terminator of the predecessor, mark overdefined. 585 Optional<OperandRange> branchOperands; 586 if (auto branch = dyn_cast<BranchOpInterface>(pred->getTerminator())) 587 branchOperands = branch.getSuccessorOperands(it.getSuccessorIndex()); 588 if (!branchOperands) { 589 updatedLattice = true; 590 argLattice.markOverdefined(); 591 break; 592 } 593 594 // If the operand hasn't been resolved, it is unknown which can merge with 595 // anything. 596 auto operandLattice = latticeValues.find((*branchOperands)[i]); 597 if (operandLattice == latticeValues.end()) 598 continue; 599 600 // Otherwise, meet the two lattice values. 601 updatedLattice |= argLattice.meet(operandLattice->second); 602 if (argLattice.isOverdefined()) 603 break; 604 } 605 606 // If the lattice was updated, visit any executable users of the argument. 607 if (updatedLattice) 608 visitUsers(arg); 609 } 610 611 bool SCCPSolver::markBlockExecutable(Block *block) { 612 bool marked = executableBlocks.insert(block).second; 613 if (marked) 614 blockWorklist.push_back(block); 615 return marked; 616 } 617 618 bool SCCPSolver::isBlockExecutable(Block *block) const { 619 return executableBlocks.count(block); 620 } 621 622 void SCCPSolver::markEdgeExecutable(Block *from, Block *to) { 623 if (!executableEdges.insert(std::make_pair(from, to)).second) 624 return; 625 // Mark the destination as executable, and reprocess its arguments if it was 626 // already executable. 627 if (!markBlockExecutable(to)) { 628 for (int i : llvm::seq<int>(0, to->getNumArguments())) 629 visitBlockArgument(to, i); 630 } 631 } 632 633 bool SCCPSolver::isEdgeExecutable(Block *from, Block *to) const { 634 return executableEdges.count(std::make_pair(from, to)); 635 } 636 637 void SCCPSolver::markOverdefined(Value value) { 638 latticeValues[value].markOverdefined(); 639 } 640 641 bool SCCPSolver::isOverdefined(Value value) const { 642 auto it = latticeValues.find(value); 643 return it != latticeValues.end() && it->second.isOverdefined(); 644 } 645 646 void SCCPSolver::meet(Operation *owner, LatticeValue &to, 647 const LatticeValue &from) { 648 if (to.meet(from)) 649 opWorklist.push_back(owner); 650 } 651 652 //===----------------------------------------------------------------------===// 653 // SCCP Pass 654 //===----------------------------------------------------------------------===// 655 656 namespace { 657 struct SCCP : public SCCPBase<SCCP> { 658 void runOnOperation() override; 659 }; 660 } // end anonymous namespace 661 662 void SCCP::runOnOperation() { 663 Operation *op = getOperation(); 664 665 // Solve for SCCP constraints within nested regions. 666 SCCPSolver solver(op->getRegions()); 667 solver.solve(); 668 669 // Cleanup any operations using the solver analysis. 670 solver.rewrite(&getContext(), op->getRegions()); 671 } 672 673 std::unique_ptr<Pass> mlir::createSCCPPass() { 674 return std::make_unique<SCCP>(); 675 } 676