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 &region : regions) {
243     if (region.empty())
244       continue;
245     Block *entryBlock = &region.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 &region : 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 &region : op->getRegions()) {
410       if (region.empty())
411         continue;
412       Block *entryBlock = &region.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 = &region->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