1 //===- SCF.cpp - Structured Control Flow Operations -----------------------===//
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 #include "mlir/Dialect/SCF/IR/SCF.h"
10 #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
11 #include "mlir/Dialect/Arithmetic/Utils/Utils.h"
12 #include "mlir/Dialect/Bufferization/IR/Bufferization.h"
13 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
14 #include "mlir/Dialect/MemRef/IR/MemRef.h"
15 #include "mlir/Dialect/Tensor/IR/Tensor.h"
16 #include "mlir/IR/BlockAndValueMapping.h"
17 #include "mlir/IR/FunctionInterfaces.h"
18 #include "mlir/IR/Matchers.h"
19 #include "mlir/IR/PatternMatch.h"
20 #include "mlir/Support/MathExtras.h"
21 #include "mlir/Transforms/InliningUtils.h"
22 
23 using namespace mlir;
24 using namespace mlir::scf;
25 
26 #include "mlir/Dialect/SCF/IR/SCFOpsDialect.cpp.inc"
27 
28 //===----------------------------------------------------------------------===//
29 // SCFDialect Dialect Interfaces
30 //===----------------------------------------------------------------------===//
31 
32 namespace {
33 struct SCFInlinerInterface : public DialectInlinerInterface {
34   using DialectInlinerInterface::DialectInlinerInterface;
35   // We don't have any special restrictions on what can be inlined into
36   // destination regions (e.g. while/conditional bodies). Always allow it.
37   bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
38                        BlockAndValueMapping &valueMapping) const final {
39     return true;
40   }
41   // Operations in scf dialect are always legal to inline since they are
42   // pure.
43   bool isLegalToInline(Operation *, Region *, bool,
44                        BlockAndValueMapping &) const final {
45     return true;
46   }
47   // Handle the given inlined terminator by replacing it with a new operation
48   // as necessary. Required when the region has only one block.
49   void handleTerminator(Operation *op,
50                         ArrayRef<Value> valuesToRepl) const final {
51     auto retValOp = dyn_cast<scf::YieldOp>(op);
52     if (!retValOp)
53       return;
54 
55     for (auto retValue : llvm::zip(valuesToRepl, retValOp.getOperands())) {
56       std::get<0>(retValue).replaceAllUsesWith(std::get<1>(retValue));
57     }
58   }
59 };
60 } // namespace
61 
62 //===----------------------------------------------------------------------===//
63 // SCFDialect
64 //===----------------------------------------------------------------------===//
65 
66 void SCFDialect::initialize() {
67   addOperations<
68 #define GET_OP_LIST
69 #include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
70       >();
71   addInterfaces<SCFInlinerInterface>();
72 }
73 
74 /// Default callback for IfOp builders. Inserts a yield without arguments.
75 void mlir::scf::buildTerminatedBody(OpBuilder &builder, Location loc) {
76   builder.create<scf::YieldOp>(loc);
77 }
78 
79 //===----------------------------------------------------------------------===//
80 // ExecuteRegionOp
81 //===----------------------------------------------------------------------===//
82 
83 /// Replaces the given op with the contents of the given single-block region,
84 /// using the operands of the block terminator to replace operation results.
85 static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op,
86                                 Region &region, ValueRange blockArgs = {}) {
87   assert(llvm::hasSingleElement(region) && "expected single-region block");
88   Block *block = &region.front();
89   Operation *terminator = block->getTerminator();
90   ValueRange results = terminator->getOperands();
91   rewriter.mergeBlockBefore(block, op, blockArgs);
92   rewriter.replaceOp(op, results);
93   rewriter.eraseOp(terminator);
94 }
95 
96 ///
97 /// (ssa-id `=`)? `execute_region` `->` function-result-type `{`
98 ///    block+
99 /// `}`
100 ///
101 /// Example:
102 ///   scf.execute_region -> i32 {
103 ///     %idx = load %rI[%i] : memref<128xi32>
104 ///     return %idx : i32
105 ///   }
106 ///
107 ParseResult ExecuteRegionOp::parse(OpAsmParser &parser,
108                                    OperationState &result) {
109   if (parser.parseOptionalArrowTypeList(result.types))
110     return failure();
111 
112   // Introduce the body region and parse it.
113   Region *body = result.addRegion();
114   if (parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{}) ||
115       parser.parseOptionalAttrDict(result.attributes))
116     return failure();
117 
118   return success();
119 }
120 
121 void ExecuteRegionOp::print(OpAsmPrinter &p) {
122   p.printOptionalArrowTypeList(getResultTypes());
123 
124   p << ' ';
125   p.printRegion(getRegion(),
126                 /*printEntryBlockArgs=*/false,
127                 /*printBlockTerminators=*/true);
128 
129   p.printOptionalAttrDict((*this)->getAttrs());
130 }
131 
132 LogicalResult ExecuteRegionOp::verify() {
133   if (getRegion().empty())
134     return emitOpError("region needs to have at least one block");
135   if (getRegion().front().getNumArguments() > 0)
136     return emitOpError("region cannot have any arguments");
137   return success();
138 }
139 
140 // Inline an ExecuteRegionOp if it only contains one block.
141 //     "test.foo"() : () -> ()
142 //      %v = scf.execute_region -> i64 {
143 //        %x = "test.val"() : () -> i64
144 //        scf.yield %x : i64
145 //      }
146 //      "test.bar"(%v) : (i64) -> ()
147 //
148 //  becomes
149 //
150 //     "test.foo"() : () -> ()
151 //     %x = "test.val"() : () -> i64
152 //     "test.bar"(%x) : (i64) -> ()
153 //
154 struct SingleBlockExecuteInliner : public OpRewritePattern<ExecuteRegionOp> {
155   using OpRewritePattern<ExecuteRegionOp>::OpRewritePattern;
156 
157   LogicalResult matchAndRewrite(ExecuteRegionOp op,
158                                 PatternRewriter &rewriter) const override {
159     if (!llvm::hasSingleElement(op.getRegion()))
160       return failure();
161     replaceOpWithRegion(rewriter, op, op.getRegion());
162     return success();
163   }
164 };
165 
166 // Inline an ExecuteRegionOp if its parent can contain multiple blocks.
167 // TODO generalize the conditions for operations which can be inlined into.
168 // func @func_execute_region_elim() {
169 //     "test.foo"() : () -> ()
170 //     %v = scf.execute_region -> i64 {
171 //       %c = "test.cmp"() : () -> i1
172 //       cf.cond_br %c, ^bb2, ^bb3
173 //     ^bb2:
174 //       %x = "test.val1"() : () -> i64
175 //       cf.br ^bb4(%x : i64)
176 //     ^bb3:
177 //       %y = "test.val2"() : () -> i64
178 //       cf.br ^bb4(%y : i64)
179 //     ^bb4(%z : i64):
180 //       scf.yield %z : i64
181 //     }
182 //     "test.bar"(%v) : (i64) -> ()
183 //   return
184 // }
185 //
186 //  becomes
187 //
188 // func @func_execute_region_elim() {
189 //    "test.foo"() : () -> ()
190 //    %c = "test.cmp"() : () -> i1
191 //    cf.cond_br %c, ^bb1, ^bb2
192 //  ^bb1:  // pred: ^bb0
193 //    %x = "test.val1"() : () -> i64
194 //    cf.br ^bb3(%x : i64)
195 //  ^bb2:  // pred: ^bb0
196 //    %y = "test.val2"() : () -> i64
197 //    cf.br ^bb3(%y : i64)
198 //  ^bb3(%z: i64):  // 2 preds: ^bb1, ^bb2
199 //    "test.bar"(%z) : (i64) -> ()
200 //    return
201 //  }
202 //
203 struct MultiBlockExecuteInliner : public OpRewritePattern<ExecuteRegionOp> {
204   using OpRewritePattern<ExecuteRegionOp>::OpRewritePattern;
205 
206   LogicalResult matchAndRewrite(ExecuteRegionOp op,
207                                 PatternRewriter &rewriter) const override {
208     if (!isa<FunctionOpInterface, ExecuteRegionOp>(op->getParentOp()))
209       return failure();
210 
211     Block *prevBlock = op->getBlock();
212     Block *postBlock = rewriter.splitBlock(prevBlock, op->getIterator());
213     rewriter.setInsertionPointToEnd(prevBlock);
214 
215     rewriter.create<cf::BranchOp>(op.getLoc(), &op.getRegion().front());
216 
217     for (Block &blk : op.getRegion()) {
218       if (YieldOp yieldOp = dyn_cast<YieldOp>(blk.getTerminator())) {
219         rewriter.setInsertionPoint(yieldOp);
220         rewriter.create<cf::BranchOp>(yieldOp.getLoc(), postBlock,
221                                       yieldOp.getResults());
222         rewriter.eraseOp(yieldOp);
223       }
224     }
225 
226     rewriter.inlineRegionBefore(op.getRegion(), postBlock);
227     SmallVector<Value> blockArgs;
228 
229     for (auto res : op.getResults())
230       blockArgs.push_back(postBlock->addArgument(res.getType(), res.getLoc()));
231 
232     rewriter.replaceOp(op, blockArgs);
233     return success();
234   }
235 };
236 
237 void ExecuteRegionOp::getCanonicalizationPatterns(RewritePatternSet &results,
238                                                   MLIRContext *context) {
239   results.add<SingleBlockExecuteInliner, MultiBlockExecuteInliner>(context);
240 }
241 
242 /// Given the region at `index`, or the parent operation if `index` is None,
243 /// return the successor regions. These are the regions that may be selected
244 /// during the flow of control. `operands` is a set of optional attributes that
245 /// correspond to a constant value for each operand, or null if that operand is
246 /// not a constant.
247 void ExecuteRegionOp::getSuccessorRegions(
248     Optional<unsigned> index, ArrayRef<Attribute> operands,
249     SmallVectorImpl<RegionSuccessor> &regions) {
250   // If the predecessor is the ExecuteRegionOp, branch into the body.
251   if (!index) {
252     regions.push_back(RegionSuccessor(&getRegion()));
253     return;
254   }
255 
256   // Otherwise, the region branches back to the parent operation.
257   regions.push_back(RegionSuccessor(getResults()));
258 }
259 
260 //===----------------------------------------------------------------------===//
261 // ConditionOp
262 //===----------------------------------------------------------------------===//
263 
264 MutableOperandRange
265 ConditionOp::getMutableSuccessorOperands(Optional<unsigned> index) {
266   // Pass all operands except the condition to the successor region.
267   return getArgsMutable();
268 }
269 
270 //===----------------------------------------------------------------------===//
271 // ForOp
272 //===----------------------------------------------------------------------===//
273 
274 void ForOp::build(OpBuilder &builder, OperationState &result, Value lb,
275                   Value ub, Value step, ValueRange iterArgs,
276                   BodyBuilderFn bodyBuilder) {
277   result.addOperands({lb, ub, step});
278   result.addOperands(iterArgs);
279   for (Value v : iterArgs)
280     result.addTypes(v.getType());
281   Region *bodyRegion = result.addRegion();
282   bodyRegion->push_back(new Block);
283   Block &bodyBlock = bodyRegion->front();
284   bodyBlock.addArgument(builder.getIndexType(), result.location);
285   for (Value v : iterArgs)
286     bodyBlock.addArgument(v.getType(), v.getLoc());
287 
288   // Create the default terminator if the builder is not provided and if the
289   // iteration arguments are not provided. Otherwise, leave this to the caller
290   // because we don't know which values to return from the loop.
291   if (iterArgs.empty() && !bodyBuilder) {
292     ForOp::ensureTerminator(*bodyRegion, builder, result.location);
293   } else if (bodyBuilder) {
294     OpBuilder::InsertionGuard guard(builder);
295     builder.setInsertionPointToStart(&bodyBlock);
296     bodyBuilder(builder, result.location, bodyBlock.getArgument(0),
297                 bodyBlock.getArguments().drop_front());
298   }
299 }
300 
301 LogicalResult ForOp::verify() {
302   if (auto cst = getStep().getDefiningOp<arith::ConstantIndexOp>())
303     if (cst.value() <= 0)
304       return emitOpError("constant step operand must be positive");
305 
306   auto opNumResults = getNumResults();
307   if (opNumResults == 0)
308     return success();
309   // If ForOp defines values, check that the number and types of
310   // the defined values match ForOp initial iter operands and backedge
311   // basic block arguments.
312   if (getNumIterOperands() != opNumResults)
313     return emitOpError(
314         "mismatch in number of loop-carried values and defined values");
315   return success();
316 }
317 
318 LogicalResult ForOp::verifyRegions() {
319   // Check that the body defines as single block argument for the induction
320   // variable.
321   auto *body = getBody();
322   if (!body->getArgument(0).getType().isIndex())
323     return emitOpError(
324         "expected body first argument to be an index argument for "
325         "the induction variable");
326 
327   auto opNumResults = getNumResults();
328   if (opNumResults == 0)
329     return success();
330 
331   if (getNumRegionIterArgs() != opNumResults)
332     return emitOpError(
333         "mismatch in number of basic block args and defined values");
334 
335   auto iterOperands = getIterOperands();
336   auto iterArgs = getRegionIterArgs();
337   auto opResults = getResults();
338   unsigned i = 0;
339   for (auto e : llvm::zip(iterOperands, iterArgs, opResults)) {
340     if (std::get<0>(e).getType() != std::get<2>(e).getType())
341       return emitOpError() << "types mismatch between " << i
342                            << "th iter operand and defined value";
343     if (std::get<1>(e).getType() != std::get<2>(e).getType())
344       return emitOpError() << "types mismatch between " << i
345                            << "th iter region arg and defined value";
346 
347     i++;
348   }
349   return success();
350 }
351 
352 Optional<Value> ForOp::getSingleInductionVar() { return getInductionVar(); }
353 
354 Optional<OpFoldResult> ForOp::getSingleLowerBound() {
355   return OpFoldResult(getLowerBound());
356 }
357 
358 Optional<OpFoldResult> ForOp::getSingleStep() {
359   return OpFoldResult(getStep());
360 }
361 
362 Optional<OpFoldResult> ForOp::getSingleUpperBound() {
363   return OpFoldResult(getUpperBound());
364 }
365 
366 /// Prints the initialization list in the form of
367 ///   <prefix>(%inner = %outer, %inner2 = %outer2, <...>)
368 /// where 'inner' values are assumed to be region arguments and 'outer' values
369 /// are regular SSA values.
370 static void printInitializationList(OpAsmPrinter &p,
371                                     Block::BlockArgListType blocksArgs,
372                                     ValueRange initializers,
373                                     StringRef prefix = "") {
374   assert(blocksArgs.size() == initializers.size() &&
375          "expected same length of arguments and initializers");
376   if (initializers.empty())
377     return;
378 
379   p << prefix << '(';
380   llvm::interleaveComma(llvm::zip(blocksArgs, initializers), p, [&](auto it) {
381     p << std::get<0>(it) << " = " << std::get<1>(it);
382   });
383   p << ")";
384 }
385 
386 void ForOp::print(OpAsmPrinter &p) {
387   p << " " << getInductionVar() << " = " << getLowerBound() << " to "
388     << getUpperBound() << " step " << getStep();
389 
390   printInitializationList(p, getRegionIterArgs(), getIterOperands(),
391                           " iter_args");
392   if (!getIterOperands().empty())
393     p << " -> (" << getIterOperands().getTypes() << ')';
394   p << ' ';
395   p.printRegion(getRegion(),
396                 /*printEntryBlockArgs=*/false,
397                 /*printBlockTerminators=*/hasIterOperands());
398   p.printOptionalAttrDict((*this)->getAttrs());
399 }
400 
401 ParseResult ForOp::parse(OpAsmParser &parser, OperationState &result) {
402   auto &builder = parser.getBuilder();
403   Type indexType = builder.getIndexType();
404 
405   OpAsmParser::Argument inductionVariable;
406   inductionVariable.type = indexType;
407   OpAsmParser::UnresolvedOperand lb, ub, step;
408 
409   // Parse the induction variable followed by '='.
410   if (parser.parseArgument(inductionVariable) || parser.parseEqual() ||
411       // Parse loop bounds.
412       parser.parseOperand(lb) ||
413       parser.resolveOperand(lb, indexType, result.operands) ||
414       parser.parseKeyword("to") || parser.parseOperand(ub) ||
415       parser.resolveOperand(ub, indexType, result.operands) ||
416       parser.parseKeyword("step") || parser.parseOperand(step) ||
417       parser.resolveOperand(step, indexType, result.operands))
418     return failure();
419 
420   // Parse the optional initial iteration arguments.
421   SmallVector<OpAsmParser::Argument, 4> regionArgs;
422   SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
423   regionArgs.push_back(inductionVariable);
424 
425   if (succeeded(parser.parseOptionalKeyword("iter_args"))) {
426     // Parse assignment list and results type list.
427     if (parser.parseAssignmentList(regionArgs, operands) ||
428         parser.parseArrowTypeList(result.types))
429       return failure();
430 
431     // Resolve input operands.
432     for (auto argOperandType :
433          llvm::zip(llvm::drop_begin(regionArgs), operands, result.types)) {
434       Type type = std::get<2>(argOperandType);
435       std::get<0>(argOperandType).type = type;
436       if (parser.resolveOperand(std::get<1>(argOperandType), type,
437                                 result.operands))
438         return failure();
439     }
440   }
441 
442   if (regionArgs.size() != result.types.size() + 1)
443     return parser.emitError(
444         parser.getNameLoc(),
445         "mismatch in number of loop-carried values and defined values");
446 
447   // Parse the body region.
448   Region *body = result.addRegion();
449   if (parser.parseRegion(*body, regionArgs))
450     return failure();
451 
452   ForOp::ensureTerminator(*body, builder, result.location);
453 
454   // Parse the optional attribute list.
455   if (parser.parseOptionalAttrDict(result.attributes))
456     return failure();
457 
458   return success();
459 }
460 
461 Region &ForOp::getLoopBody() { return getRegion(); }
462 
463 ForOp mlir::scf::getForInductionVarOwner(Value val) {
464   auto ivArg = val.dyn_cast<BlockArgument>();
465   if (!ivArg)
466     return ForOp();
467   assert(ivArg.getOwner() && "unlinked block argument");
468   auto *containingOp = ivArg.getOwner()->getParentOp();
469   return dyn_cast_or_null<ForOp>(containingOp);
470 }
471 
472 /// Return operands used when entering the region at 'index'. These operands
473 /// correspond to the loop iterator operands, i.e., those excluding the
474 /// induction variable. LoopOp only has one region, so 0 is the only valid value
475 /// for `index`.
476 OperandRange ForOp::getSuccessorEntryOperands(Optional<unsigned> index) {
477   assert(index && *index == 0 && "invalid region index");
478 
479   // The initial operands map to the loop arguments after the induction
480   // variable.
481   return getInitArgs();
482 }
483 
484 /// Given the region at `index`, or the parent operation if `index` is None,
485 /// return the successor regions. These are the regions that may be selected
486 /// during the flow of control. `operands` is a set of optional attributes that
487 /// correspond to a constant value for each operand, or null if that operand is
488 /// not a constant.
489 void ForOp::getSuccessorRegions(Optional<unsigned> index,
490                                 ArrayRef<Attribute> operands,
491                                 SmallVectorImpl<RegionSuccessor> &regions) {
492   // If the predecessor is the ForOp, branch into the body using the iterator
493   // arguments.
494   if (!index) {
495     regions.push_back(RegionSuccessor(&getLoopBody(), getRegionIterArgs()));
496     return;
497   }
498 
499   // Otherwise, the loop may branch back to itself or the parent operation.
500   assert(*index == 0 && "expected loop region");
501   regions.push_back(RegionSuccessor(&getLoopBody(), getRegionIterArgs()));
502   regions.push_back(RegionSuccessor(getResults()));
503 }
504 
505 LoopNest mlir::scf::buildLoopNest(
506     OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs,
507     ValueRange steps, ValueRange iterArgs,
508     function_ref<ValueVector(OpBuilder &, Location, ValueRange, ValueRange)>
509         bodyBuilder) {
510   assert(lbs.size() == ubs.size() &&
511          "expected the same number of lower and upper bounds");
512   assert(lbs.size() == steps.size() &&
513          "expected the same number of lower bounds and steps");
514 
515   // If there are no bounds, call the body-building function and return early.
516   if (lbs.empty()) {
517     ValueVector results =
518         bodyBuilder ? bodyBuilder(builder, loc, ValueRange(), iterArgs)
519                     : ValueVector();
520     assert(results.size() == iterArgs.size() &&
521            "loop nest body must return as many values as loop has iteration "
522            "arguments");
523     return LoopNest();
524   }
525 
526   // First, create the loop structure iteratively using the body-builder
527   // callback of `ForOp::build`. Do not create `YieldOp`s yet.
528   OpBuilder::InsertionGuard guard(builder);
529   SmallVector<scf::ForOp, 4> loops;
530   SmallVector<Value, 4> ivs;
531   loops.reserve(lbs.size());
532   ivs.reserve(lbs.size());
533   ValueRange currentIterArgs = iterArgs;
534   Location currentLoc = loc;
535   for (unsigned i = 0, e = lbs.size(); i < e; ++i) {
536     auto loop = builder.create<scf::ForOp>(
537         currentLoc, lbs[i], ubs[i], steps[i], currentIterArgs,
538         [&](OpBuilder &nestedBuilder, Location nestedLoc, Value iv,
539             ValueRange args) {
540           ivs.push_back(iv);
541           // It is safe to store ValueRange args because it points to block
542           // arguments of a loop operation that we also own.
543           currentIterArgs = args;
544           currentLoc = nestedLoc;
545         });
546     // Set the builder to point to the body of the newly created loop. We don't
547     // do this in the callback because the builder is reset when the callback
548     // returns.
549     builder.setInsertionPointToStart(loop.getBody());
550     loops.push_back(loop);
551   }
552 
553   // For all loops but the innermost, yield the results of the nested loop.
554   for (unsigned i = 0, e = loops.size() - 1; i < e; ++i) {
555     builder.setInsertionPointToEnd(loops[i].getBody());
556     builder.create<scf::YieldOp>(loc, loops[i + 1].getResults());
557   }
558 
559   // In the body of the innermost loop, call the body building function if any
560   // and yield its results.
561   builder.setInsertionPointToStart(loops.back().getBody());
562   ValueVector results = bodyBuilder
563                             ? bodyBuilder(builder, currentLoc, ivs,
564                                           loops.back().getRegionIterArgs())
565                             : ValueVector();
566   assert(results.size() == iterArgs.size() &&
567          "loop nest body must return as many values as loop has iteration "
568          "arguments");
569   builder.setInsertionPointToEnd(loops.back().getBody());
570   builder.create<scf::YieldOp>(loc, results);
571 
572   // Return the loops.
573   LoopNest res;
574   res.loops.assign(loops.begin(), loops.end());
575   return res;
576 }
577 
578 LoopNest mlir::scf::buildLoopNest(
579     OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs,
580     ValueRange steps,
581     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilder) {
582   // Delegate to the main function by wrapping the body builder.
583   return buildLoopNest(builder, loc, lbs, ubs, steps, llvm::None,
584                        [&bodyBuilder](OpBuilder &nestedBuilder,
585                                       Location nestedLoc, ValueRange ivs,
586                                       ValueRange) -> ValueVector {
587                          if (bodyBuilder)
588                            bodyBuilder(nestedBuilder, nestedLoc, ivs);
589                          return {};
590                        });
591 }
592 
593 namespace {
594 // Fold away ForOp iter arguments when:
595 // 1) The op yields the iter arguments.
596 // 2) The iter arguments have no use and the corresponding outer region
597 // iterators (inputs) are yielded.
598 // 3) The iter arguments have no use and the corresponding (operation) results
599 // have no use.
600 //
601 // These arguments must be defined outside of
602 // the ForOp region and can just be forwarded after simplifying the op inits,
603 // yields and returns.
604 //
605 // The implementation uses `mergeBlockBefore` to steal the content of the
606 // original ForOp and avoid cloning.
607 struct ForOpIterArgsFolder : public OpRewritePattern<scf::ForOp> {
608   using OpRewritePattern<scf::ForOp>::OpRewritePattern;
609 
610   LogicalResult matchAndRewrite(scf::ForOp forOp,
611                                 PatternRewriter &rewriter) const final {
612     bool canonicalize = false;
613     Block &block = forOp.getRegion().front();
614     auto yieldOp = cast<scf::YieldOp>(block.getTerminator());
615 
616     // An internal flat vector of block transfer
617     // arguments `newBlockTransferArgs` keeps the 1-1 mapping of original to
618     // transformed block argument mappings. This plays the role of a
619     // BlockAndValueMapping for the particular use case of calling into
620     // `mergeBlockBefore`.
621     SmallVector<bool, 4> keepMask;
622     keepMask.reserve(yieldOp.getNumOperands());
623     SmallVector<Value, 4> newBlockTransferArgs, newIterArgs, newYieldValues,
624         newResultValues;
625     newBlockTransferArgs.reserve(1 + forOp.getNumIterOperands());
626     newBlockTransferArgs.push_back(Value()); // iv placeholder with null value
627     newIterArgs.reserve(forOp.getNumIterOperands());
628     newYieldValues.reserve(yieldOp.getNumOperands());
629     newResultValues.reserve(forOp.getNumResults());
630     for (auto it : llvm::zip(forOp.getIterOperands(),   // iter from outside
631                              forOp.getRegionIterArgs(), // iter inside region
632                              forOp.getResults(),        // op results
633                              yieldOp.getOperands()      // iter yield
634                              )) {
635       // Forwarded is `true` when:
636       // 1) The region `iter` argument is yielded.
637       // 2) The region `iter` argument has no use, and the corresponding iter
638       // operand (input) is yielded.
639       // 3) The region `iter` argument has no use, and the corresponding op
640       // result has no use.
641       bool forwarded = ((std::get<1>(it) == std::get<3>(it)) ||
642                         (std::get<1>(it).use_empty() &&
643                          (std::get<0>(it) == std::get<3>(it) ||
644                           std::get<2>(it).use_empty())));
645       keepMask.push_back(!forwarded);
646       canonicalize |= forwarded;
647       if (forwarded) {
648         newBlockTransferArgs.push_back(std::get<0>(it));
649         newResultValues.push_back(std::get<0>(it));
650         continue;
651       }
652       newIterArgs.push_back(std::get<0>(it));
653       newYieldValues.push_back(std::get<3>(it));
654       newBlockTransferArgs.push_back(Value()); // placeholder with null value
655       newResultValues.push_back(Value());      // placeholder with null value
656     }
657 
658     if (!canonicalize)
659       return failure();
660 
661     scf::ForOp newForOp = rewriter.create<scf::ForOp>(
662         forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
663         forOp.getStep(), newIterArgs);
664     newForOp->setAttrs(forOp->getAttrs());
665     Block &newBlock = newForOp.getRegion().front();
666 
667     // Replace the null placeholders with newly constructed values.
668     newBlockTransferArgs[0] = newBlock.getArgument(0); // iv
669     for (unsigned idx = 0, collapsedIdx = 0, e = newResultValues.size();
670          idx != e; ++idx) {
671       Value &blockTransferArg = newBlockTransferArgs[1 + idx];
672       Value &newResultVal = newResultValues[idx];
673       assert((blockTransferArg && newResultVal) ||
674              (!blockTransferArg && !newResultVal));
675       if (!blockTransferArg) {
676         blockTransferArg = newForOp.getRegionIterArgs()[collapsedIdx];
677         newResultVal = newForOp.getResult(collapsedIdx++);
678       }
679     }
680 
681     Block &oldBlock = forOp.getRegion().front();
682     assert(oldBlock.getNumArguments() == newBlockTransferArgs.size() &&
683            "unexpected argument size mismatch");
684 
685     // No results case: the scf::ForOp builder already created a zero
686     // result terminator. Merge before this terminator and just get rid of the
687     // original terminator that has been merged in.
688     if (newIterArgs.empty()) {
689       auto newYieldOp = cast<scf::YieldOp>(newBlock.getTerminator());
690       rewriter.mergeBlockBefore(&oldBlock, newYieldOp, newBlockTransferArgs);
691       rewriter.eraseOp(newBlock.getTerminator()->getPrevNode());
692       rewriter.replaceOp(forOp, newResultValues);
693       return success();
694     }
695 
696     // No terminator case: merge and rewrite the merged terminator.
697     auto cloneFilteredTerminator = [&](scf::YieldOp mergedTerminator) {
698       OpBuilder::InsertionGuard g(rewriter);
699       rewriter.setInsertionPoint(mergedTerminator);
700       SmallVector<Value, 4> filteredOperands;
701       filteredOperands.reserve(newResultValues.size());
702       for (unsigned idx = 0, e = keepMask.size(); idx < e; ++idx)
703         if (keepMask[idx])
704           filteredOperands.push_back(mergedTerminator.getOperand(idx));
705       rewriter.create<scf::YieldOp>(mergedTerminator.getLoc(),
706                                     filteredOperands);
707     };
708 
709     rewriter.mergeBlocks(&oldBlock, &newBlock, newBlockTransferArgs);
710     auto mergedYieldOp = cast<scf::YieldOp>(newBlock.getTerminator());
711     cloneFilteredTerminator(mergedYieldOp);
712     rewriter.eraseOp(mergedYieldOp);
713     rewriter.replaceOp(forOp, newResultValues);
714     return success();
715   }
716 };
717 
718 /// Rewriting pattern that erases loops that are known not to iterate, replaces
719 /// single-iteration loops with their bodies, and removes empty loops that
720 /// iterate at least once and only return values defined outside of the loop.
721 struct SimplifyTrivialLoops : public OpRewritePattern<ForOp> {
722   using OpRewritePattern<ForOp>::OpRewritePattern;
723 
724   LogicalResult matchAndRewrite(ForOp op,
725                                 PatternRewriter &rewriter) const override {
726     // If the upper bound is the same as the lower bound, the loop does not
727     // iterate, just remove it.
728     if (op.getLowerBound() == op.getUpperBound()) {
729       rewriter.replaceOp(op, op.getIterOperands());
730       return success();
731     }
732 
733     auto lb = op.getLowerBound().getDefiningOp<arith::ConstantOp>();
734     auto ub = op.getUpperBound().getDefiningOp<arith::ConstantOp>();
735     if (!lb || !ub)
736       return failure();
737 
738     // If the loop is known to have 0 iterations, remove it.
739     llvm::APInt lbValue = lb.getValue().cast<IntegerAttr>().getValue();
740     llvm::APInt ubValue = ub.getValue().cast<IntegerAttr>().getValue();
741     if (lbValue.sge(ubValue)) {
742       rewriter.replaceOp(op, op.getIterOperands());
743       return success();
744     }
745 
746     auto step = op.getStep().getDefiningOp<arith::ConstantOp>();
747     if (!step)
748       return failure();
749 
750     // If the loop is known to have 1 iteration, inline its body and remove the
751     // loop.
752     llvm::APInt stepValue = step.getValue().cast<IntegerAttr>().getValue();
753     if ((lbValue + stepValue).sge(ubValue)) {
754       SmallVector<Value, 4> blockArgs;
755       blockArgs.reserve(op.getNumIterOperands() + 1);
756       blockArgs.push_back(op.getLowerBound());
757       llvm::append_range(blockArgs, op.getIterOperands());
758       replaceOpWithRegion(rewriter, op, op.getLoopBody(), blockArgs);
759       return success();
760     }
761 
762     // Now we are left with loops that have more than 1 iterations.
763     Block &block = op.getRegion().front();
764     if (!llvm::hasSingleElement(block))
765       return failure();
766     // If the loop is empty, iterates at least once, and only returns values
767     // defined outside of the loop, remove it and replace it with yield values.
768     auto yieldOp = cast<scf::YieldOp>(block.getTerminator());
769     auto yieldOperands = yieldOp.getOperands();
770     if (llvm::any_of(yieldOperands,
771                      [&](Value v) { return !op.isDefinedOutsideOfLoop(v); }))
772       return failure();
773     rewriter.replaceOp(op, yieldOperands);
774     return success();
775   }
776 };
777 
778 /// Perform a replacement of one iter OpOperand of an scf.for to the
779 /// `replacement` value which is expected to be the source of a tensor.cast.
780 /// tensor.cast ops are inserted inside the block to account for the type cast.
781 static ForOp replaceTensorCastForOpIterArg(PatternRewriter &rewriter,
782                                            OpOperand &operand,
783                                            Value replacement) {
784   Type oldType = operand.get().getType(), newType = replacement.getType();
785   assert(oldType.isa<RankedTensorType>() && newType.isa<RankedTensorType>() &&
786          "expected ranked tensor types");
787 
788   // 1. Create new iter operands, exactly 1 is replaced.
789   ForOp forOp = cast<ForOp>(operand.getOwner());
790   assert(operand.getOperandNumber() >= forOp.getNumControlOperands() &&
791          "expected an iter OpOperand");
792   if (operand.get().getType() == replacement.getType())
793     return forOp;
794   SmallVector<Value> newIterOperands;
795   for (OpOperand &opOperand : forOp.getIterOpOperands()) {
796     if (opOperand.getOperandNumber() == operand.getOperandNumber()) {
797       newIterOperands.push_back(replacement);
798       continue;
799     }
800     newIterOperands.push_back(opOperand.get());
801   }
802 
803   // 2. Create the new forOp shell.
804   scf::ForOp newForOp = rewriter.create<scf::ForOp>(
805       forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
806       forOp.getStep(), newIterOperands);
807   newForOp->setAttrs(forOp->getAttrs());
808   Block &newBlock = newForOp.getRegion().front();
809   SmallVector<Value, 4> newBlockTransferArgs(newBlock.getArguments().begin(),
810                                              newBlock.getArguments().end());
811 
812   // 3. Inject an incoming cast op at the beginning of the block for the bbArg
813   // corresponding to the `replacement` value.
814   OpBuilder::InsertionGuard g(rewriter);
815   rewriter.setInsertionPoint(&newBlock, newBlock.begin());
816   BlockArgument newRegionIterArg = newForOp.getRegionIterArgForOpOperand(
817       newForOp->getOpOperand(operand.getOperandNumber()));
818   Value castIn = rewriter.create<tensor::CastOp>(newForOp.getLoc(), oldType,
819                                                  newRegionIterArg);
820   newBlockTransferArgs[newRegionIterArg.getArgNumber()] = castIn;
821 
822   // 4. Steal the old block ops, mapping to the newBlockTransferArgs.
823   Block &oldBlock = forOp.getRegion().front();
824   rewriter.mergeBlocks(&oldBlock, &newBlock, newBlockTransferArgs);
825 
826   // 5. Inject an outgoing cast op at the end of the block and yield it instead.
827   auto clonedYieldOp = cast<scf::YieldOp>(newBlock.getTerminator());
828   rewriter.setInsertionPoint(clonedYieldOp);
829   unsigned yieldIdx =
830       newRegionIterArg.getArgNumber() - forOp.getNumInductionVars();
831   Value castOut = rewriter.create<tensor::CastOp>(
832       newForOp.getLoc(), newType, clonedYieldOp.getOperand(yieldIdx));
833   SmallVector<Value> newYieldOperands = clonedYieldOp.getOperands();
834   newYieldOperands[yieldIdx] = castOut;
835   rewriter.create<scf::YieldOp>(newForOp.getLoc(), newYieldOperands);
836   rewriter.eraseOp(clonedYieldOp);
837 
838   // 6. Inject an outgoing cast op after the forOp.
839   rewriter.setInsertionPointAfter(newForOp);
840   SmallVector<Value> newResults = newForOp.getResults();
841   newResults[yieldIdx] = rewriter.create<tensor::CastOp>(
842       newForOp.getLoc(), oldType, newResults[yieldIdx]);
843 
844   return newForOp;
845 }
846 
847 /// Fold scf.for iter_arg/result pairs that go through incoming/ougoing
848 /// a tensor.cast op pair so as to pull the tensor.cast inside the scf.for:
849 ///
850 /// ```
851 ///   %0 = tensor.cast %t0 : tensor<32x1024xf32> to tensor<?x?xf32>
852 ///   %1 = scf.for %i = %c0 to %c1024 step %c32 iter_args(%iter_t0 = %0)
853 ///      -> (tensor<?x?xf32>) {
854 ///     %2 = call @do(%iter_t0) : (tensor<?x?xf32>) -> tensor<?x?xf32>
855 ///     scf.yield %2 : tensor<?x?xf32>
856 ///   }
857 ///   %2 = tensor.cast %1 : tensor<?x?xf32> to tensor<32x1024xf32>
858 ///   use_of(%2)
859 /// ```
860 ///
861 /// folds into:
862 ///
863 /// ```
864 ///   %0 = scf.for %arg2 = %c0 to %c1024 step %c32 iter_args(%arg3 = %arg0)
865 ///       -> (tensor<32x1024xf32>) {
866 ///     %2 = tensor.cast %arg3 : tensor<32x1024xf32> to tensor<?x?xf32>
867 ///     %3 = call @do(%2) : (tensor<?x?xf32>) -> tensor<?x?xf32>
868 ///     %4 = tensor.cast %3 : tensor<?x?xf32> to tensor<32x1024xf32>
869 ///     scf.yield %4 : tensor<32x1024xf32>
870 ///   }
871 ///   use_of(%0)
872 /// ```
873 struct ForOpTensorCastFolder : public OpRewritePattern<ForOp> {
874   using OpRewritePattern<ForOp>::OpRewritePattern;
875 
876   LogicalResult matchAndRewrite(ForOp op,
877                                 PatternRewriter &rewriter) const override {
878     for (auto it : llvm::zip(op.getIterOpOperands(), op.getResults())) {
879       OpOperand &iterOpOperand = std::get<0>(it);
880       auto incomingCast = iterOpOperand.get().getDefiningOp<tensor::CastOp>();
881       if (!incomingCast)
882         continue;
883       if (!std::get<1>(it).hasOneUse())
884         continue;
885       auto outgoingCastOp =
886           dyn_cast<tensor::CastOp>(*std::get<1>(it).user_begin());
887       if (!outgoingCastOp)
888         continue;
889 
890       // Must be a tensor.cast op pair with matching types.
891       if (outgoingCastOp.getResult().getType() !=
892           incomingCast.getSource().getType())
893         continue;
894 
895       // Create a new ForOp with that iter operand replaced.
896       auto newForOp = replaceTensorCastForOpIterArg(rewriter, iterOpOperand,
897                                                     incomingCast.getSource());
898 
899       // Insert outgoing cast and use it to replace the corresponding result.
900       rewriter.setInsertionPointAfter(newForOp);
901       SmallVector<Value> replacements = newForOp.getResults();
902       unsigned returnIdx =
903           iterOpOperand.getOperandNumber() - op.getNumControlOperands();
904       replacements[returnIdx] = rewriter.create<tensor::CastOp>(
905           op.getLoc(), incomingCast.getDest().getType(),
906           replacements[returnIdx]);
907       rewriter.replaceOp(op, replacements);
908       return success();
909     }
910     return failure();
911   }
912 };
913 
914 /// Canonicalize the iter_args of an scf::ForOp that involve a
915 /// `bufferization.to_tensor` and for which only the last loop iteration is
916 /// actually visible outside of the loop. The canonicalization looks for a
917 /// pattern such as:
918 /// ```
919 ///    %t0 = ... : tensor_type
920 ///    %0 = scf.for ... iter_args(%bb0 : %t0) -> (tensor_type) {
921 ///      ...
922 ///      // %m is either buffer_cast(%bb00) or defined above the loop
923 ///      %m... : memref_type
924 ///      ... // uses of %m with potential inplace updates
925 ///      %new_tensor = bufferization.to_tensor %m : memref_type
926 ///      ...
927 ///      scf.yield %new_tensor : tensor_type
928 ///    }
929 /// ```
930 ///
931 /// `%bb0` may have either 0 or 1 use. If it has 1 use it must be exactly a
932 /// `%m = buffer_cast %bb0` op that feeds into the yielded
933 /// `bufferization.to_tensor` op.
934 ///
935 /// If no aliasing write to the memref `%m`, from which `%new_tensor`is loaded,
936 /// occurs between `bufferization.to_tensor and yield then the value %0
937 /// visible outside of the loop is the last `bufferization.to_tensor`
938 /// produced in the loop.
939 ///
940 /// For now, we approximate the absence of aliasing by only supporting the case
941 /// when the bufferization.to_tensor is the operation immediately preceding
942 /// the yield.
943 //
944 /// The canonicalization rewrites the pattern as:
945 /// ```
946 ///    // %m is either a buffer_cast or defined above
947 ///    %m... : memref_type
948 ///    scf.for ... iter_args(%bb0 : %t0) -> (tensor_type) {
949 ///      ... // uses of %m with potential inplace updates
950 ///      scf.yield %bb0: tensor_type
951 ///    }
952 ///    %0 = bufferization.to_tensor %m : memref_type
953 /// ```
954 ///
955 /// A later bbArg canonicalization will further rewrite as:
956 /// ```
957 ///    // %m is either a buffer_cast or defined above
958 ///    %m... : memref_type
959 ///    scf.for ... { // no iter_args
960 ///      ... // uses of %m with potential inplace updates
961 ///    }
962 ///    %0 = bufferization.to_tensor %m : memref_type
963 /// ```
964 struct LastTensorLoadCanonicalization : public OpRewritePattern<ForOp> {
965   using OpRewritePattern<ForOp>::OpRewritePattern;
966 
967   LogicalResult matchAndRewrite(ForOp forOp,
968                                 PatternRewriter &rewriter) const override {
969     assert(std::next(forOp.getRegion().begin()) == forOp.getRegion().end() &&
970            "unexpected multiple blocks");
971 
972     Location loc = forOp.getLoc();
973     DenseMap<Value, Value> replacements;
974     for (BlockArgument bbArg : forOp.getRegionIterArgs()) {
975       unsigned idx = bbArg.getArgNumber() - /*numIv=*/1;
976       auto yieldOp =
977           cast<scf::YieldOp>(forOp.getRegion().front().getTerminator());
978       Value yieldVal = yieldOp->getOperand(idx);
979       auto tensorLoadOp = yieldVal.getDefiningOp<bufferization::ToTensorOp>();
980       bool isTensor = bbArg.getType().isa<TensorType>();
981 
982       bufferization::ToMemrefOp tensorToMemref;
983       // Either bbArg has no use or it has a single buffer_cast use.
984       if (bbArg.hasOneUse())
985         tensorToMemref =
986             dyn_cast<bufferization::ToMemrefOp>(*bbArg.getUsers().begin());
987       if (!isTensor || !tensorLoadOp || (!bbArg.use_empty() && !tensorToMemref))
988         continue;
989       // If tensorToMemref is present, it must feed into the `ToTensorOp`.
990       if (tensorToMemref && tensorLoadOp.getMemref() != tensorToMemref)
991         continue;
992       // TODO: Any aliasing write of tensorLoadOp.memref() nested under `forOp`
993       // must be before `ToTensorOp` in the block so that the lastWrite
994       // property is not subject to additional side-effects.
995       // For now, we only support the case when ToTensorOp appears
996       // immediately before the terminator.
997       if (tensorLoadOp->getNextNode() != yieldOp)
998         continue;
999 
1000       // Clone the optional tensorToMemref before forOp.
1001       if (tensorToMemref) {
1002         rewriter.setInsertionPoint(forOp);
1003         rewriter.replaceOpWithNewOp<bufferization::ToMemrefOp>(
1004             tensorToMemref, tensorToMemref.getMemref().getType(),
1005             tensorToMemref.getTensor());
1006       }
1007 
1008       // Clone the tensorLoad after forOp.
1009       rewriter.setInsertionPointAfter(forOp);
1010       Value newTensorLoad = rewriter.create<bufferization::ToTensorOp>(
1011           loc, tensorLoadOp.getMemref());
1012       Value forOpResult = forOp.getResult(bbArg.getArgNumber() - /*iv=*/1);
1013       replacements.insert(std::make_pair(forOpResult, newTensorLoad));
1014 
1015       // Make the terminator just yield the bbArg, the old tensorLoadOp + the
1016       // old bbArg (that is now directly yielded) will canonicalize away.
1017       rewriter.startRootUpdate(yieldOp);
1018       yieldOp.setOperand(idx, bbArg);
1019       rewriter.finalizeRootUpdate(yieldOp);
1020     }
1021     if (replacements.empty())
1022       return failure();
1023 
1024     // We want to replace a subset of the results of `forOp`. rewriter.replaceOp
1025     // replaces the whole op and erase it unconditionally. This is wrong for
1026     // `forOp` as it generally contains ops with side effects.
1027     // Instead, use `rewriter.replaceOpWithIf`.
1028     SmallVector<Value> newResults;
1029     newResults.reserve(forOp.getNumResults());
1030     for (Value v : forOp.getResults()) {
1031       auto it = replacements.find(v);
1032       newResults.push_back((it != replacements.end()) ? it->second : v);
1033     }
1034     unsigned idx = 0;
1035     rewriter.replaceOpWithIf(forOp, newResults, [&](OpOperand &op) {
1036       return op.get() != newResults[idx++];
1037     });
1038     return success();
1039   }
1040 };
1041 } // namespace
1042 
1043 void ForOp::getCanonicalizationPatterns(RewritePatternSet &results,
1044                                         MLIRContext *context) {
1045   results.add<ForOpIterArgsFolder, SimplifyTrivialLoops,
1046               LastTensorLoadCanonicalization, ForOpTensorCastFolder>(context);
1047 }
1048 
1049 //===----------------------------------------------------------------------===//
1050 // ForeachThreadOp
1051 //===----------------------------------------------------------------------===//
1052 
1053 LogicalResult ForeachThreadOp::verify() {
1054   // Call terminator's verify to produce most informative error messages.
1055   if (failed(getTerminator().verify()))
1056     return failure();
1057 
1058   // Check that the body defines as single block argument for the thread index.
1059   auto *body = getBody();
1060   if (body->getNumArguments() != getRank())
1061     return emitOpError("region expects ") << getRank() << " arguments";
1062 
1063   // Verify consistency between the result types and the terminator.
1064   auto terminatorTypes = getTerminator().getYieldedTypes();
1065   auto opResults = getResults();
1066   if (opResults.size() != terminatorTypes.size())
1067     return emitOpError("produces ")
1068            << opResults.size() << " results, but its terminator yields "
1069            << terminatorTypes.size() << " value(s)";
1070   unsigned i = 0;
1071   for (auto e : llvm::zip(terminatorTypes, opResults)) {
1072     if (std::get<0>(e) != std::get<1>(e).getType())
1073       return emitOpError() << "type mismatch between result " << i << " ("
1074                            << std::get<1>(e).getType() << ") and terminator ("
1075                            << std::get<0>(e) << ")";
1076     i++;
1077   }
1078   return success();
1079 }
1080 
1081 void ForeachThreadOp::print(OpAsmPrinter &p) {
1082   p << " (";
1083   llvm::interleaveComma(getThreadIndices(), p);
1084   p << ") in (";
1085   llvm::interleaveComma(getNumThreads(), p);
1086   p << ") -> (" << getResultTypes() << ") ";
1087   p.printRegion(getRegion(),
1088                 /*printEntryBlockArgs=*/false,
1089                 /*printBlockTerminators=*/getNumResults() > 0);
1090   p.printOptionalAttrDict(getOperation()->getAttrs());
1091 }
1092 
1093 ParseResult ForeachThreadOp::parse(OpAsmParser &parser,
1094                                    OperationState &result) {
1095   auto &builder = parser.getBuilder();
1096   // Parse an opening `(` followed by thread index variables followed by `)`
1097   // TODO: when we can refer to such "induction variable"-like handles from the
1098   // declarative assembly format, we can implement the parser as a custom hook.
1099   SmallVector<OpAsmParser::Argument, 4> threadIndices;
1100   if (parser.parseArgumentList(threadIndices, OpAsmParser::Delimiter::Paren))
1101     return failure();
1102 
1103   // Parse `in` threadNums.
1104   SmallVector<OpAsmParser::UnresolvedOperand, 4> threadNums;
1105   if (parser.parseKeyword("in") ||
1106       parser.parseOperandList(threadNums, threadIndices.size(),
1107                               OpAsmParser::Delimiter::Paren) ||
1108       parser.resolveOperands(threadNums, builder.getIndexType(),
1109                              result.operands))
1110     return failure();
1111 
1112   // Parse optional results.
1113   if (parser.parseOptionalArrowTypeList(result.types))
1114     return failure();
1115 
1116   // Parse region.
1117   std::unique_ptr<Region> region = std::make_unique<Region>();
1118   for (auto &idx : threadIndices)
1119     idx.type = builder.getIndexType();
1120   if (parser.parseRegion(*region, threadIndices))
1121     return failure();
1122 
1123   // Ensure terminator and move region.
1124   OpBuilder b(builder.getContext());
1125   ForeachThreadOp::ensureTerminator(*region, b, result.location);
1126   result.addRegion(std::move(region));
1127 
1128   // Parse the optional attribute list.
1129   if (parser.parseOptionalAttrDict(result.attributes))
1130     return failure();
1131 
1132   return success();
1133 }
1134 
1135 // Bodyless builder, result types must be specified.
1136 void ForeachThreadOp::build(mlir::OpBuilder &builder,
1137                             mlir::OperationState &result, TypeRange resultTypes,
1138                             ValueRange numThreads,
1139                             ArrayRef<int64_t> threadDimMapping) {
1140   result.addOperands(numThreads);
1141   result.addAttribute(
1142       // TODO: getThreadDimMappingAttrName() but it is not a static member.
1143       "thread_dim_mapping", builder.getI64ArrayAttr(threadDimMapping));
1144 
1145   Region *bodyRegion = result.addRegion();
1146   OpBuilder::InsertionGuard g(builder);
1147   // createBlock sets the IP inside the block.
1148   // Generally we would guard against that but the default ensureTerminator impl
1149   // expects it ..
1150   builder.createBlock(bodyRegion);
1151   Block &bodyBlock = bodyRegion->front();
1152   bodyBlock.addArguments(
1153       SmallVector<Type>(numThreads.size(), builder.getIndexType()),
1154       SmallVector<Location>(numThreads.size(), result.location));
1155   ForeachThreadOp::ensureTerminator(*bodyRegion, builder, result.location);
1156   result.addTypes(resultTypes);
1157 }
1158 
1159 // Builder that takes a bodyBuilder lambda, result types are inferred from
1160 // the terminator.
1161 void ForeachThreadOp::build(
1162     mlir::OpBuilder &builder, mlir::OperationState &result,
1163     ValueRange numThreads, ArrayRef<int64_t> threadDimMapping,
1164     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilder) {
1165   result.addOperands(numThreads);
1166   result.addAttribute(
1167       // TODO: getThreadDimMappingAttrName() but it is not a static member.
1168       "thread_dim_mapping", builder.getI64ArrayAttr(threadDimMapping));
1169 
1170   OpBuilder::InsertionGuard g(builder);
1171   Region *bodyRegion = result.addRegion();
1172   builder.createBlock(bodyRegion);
1173   Block &bodyBlock = bodyRegion->front();
1174   bodyBlock.addArguments(
1175       SmallVector<Type>(numThreads.size(), builder.getIndexType()),
1176       SmallVector<Location>(numThreads.size(), result.location));
1177 
1178   OpBuilder::InsertionGuard guard(builder);
1179   builder.setInsertionPointToStart(&bodyBlock);
1180   bodyBuilder(builder, result.location, bodyBlock.getArguments());
1181   auto terminator =
1182       llvm::dyn_cast<PerformConcurrentlyOp>(bodyBlock.getTerminator());
1183   assert(terminator &&
1184          "expected bodyBuilder to create PerformConcurrentlyOp terminator");
1185   result.addTypes(terminator.getYieldedTypes());
1186 }
1187 
1188 // The ensureTerminator method generated by SingleBlockImplicitTerminator is
1189 // unaware of the fact that our terminator also needs a region to be
1190 // well-formed. We override it here to ensure that we do the right thing.
1191 void ForeachThreadOp::ensureTerminator(Region &region, OpBuilder &builder,
1192                                        Location loc) {
1193   OpTrait::SingleBlockImplicitTerminator<PerformConcurrentlyOp>::Impl<
1194       ForeachThreadOp>::ensureTerminator(region, builder, loc);
1195   auto terminator =
1196       llvm::dyn_cast<PerformConcurrentlyOp>(region.front().getTerminator());
1197   if (terminator.getRegion().empty())
1198     builder.createBlock(&terminator.getRegion());
1199 }
1200 
1201 PerformConcurrentlyOp ForeachThreadOp::getTerminator() {
1202   return cast<PerformConcurrentlyOp>(getBody()->getTerminator());
1203 }
1204 
1205 ForeachThreadOp mlir::scf::getForeachThreadOpThreadIndexOwner(Value val) {
1206   auto tidxArg = val.dyn_cast<BlockArgument>();
1207   if (!tidxArg)
1208     return ForeachThreadOp();
1209   assert(tidxArg.getOwner() && "unlinked block argument");
1210   auto *containingOp = tidxArg.getOwner()->getParentOp();
1211   return dyn_cast<ForeachThreadOp>(containingOp);
1212 }
1213 
1214 //===----------------------------------------------------------------------===//
1215 // ParallelInsertSliceOp
1216 //===----------------------------------------------------------------------===//
1217 
1218 OpResult ParallelInsertSliceOp::getTiedOpResult() {
1219   ParallelCombiningOpInterface parallelCombiningParent =
1220       getParallelCombiningParent();
1221   for (const auto &it :
1222        llvm::enumerate(parallelCombiningParent.getYieldingOps())) {
1223     Operation &nextOp = it.value();
1224     if (&nextOp == getOperation())
1225       return parallelCombiningParent.getParentResult(it.index());
1226   }
1227   llvm_unreachable("ParallelInsertSliceOp no tied OpResult found");
1228 }
1229 
1230 // Build a ParallelInsertSliceOp with mixed static and dynamic entries.
1231 void ParallelInsertSliceOp::build(OpBuilder &b, OperationState &result,
1232                                   Value source, Value dest,
1233                                   ArrayRef<OpFoldResult> offsets,
1234                                   ArrayRef<OpFoldResult> sizes,
1235                                   ArrayRef<OpFoldResult> strides,
1236                                   ArrayRef<NamedAttribute> attrs) {
1237   SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;
1238   SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;
1239   dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets,
1240                              ShapedType::kDynamicStrideOrOffset);
1241   dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes,
1242                              ShapedType::kDynamicSize);
1243   dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides,
1244                              ShapedType::kDynamicStrideOrOffset);
1245   build(b, result, {}, source, dest, dynamicOffsets, dynamicSizes,
1246         dynamicStrides, b.getI64ArrayAttr(staticOffsets),
1247         b.getI64ArrayAttr(staticSizes), b.getI64ArrayAttr(staticStrides));
1248   result.addAttributes(attrs);
1249 }
1250 
1251 // Build a ParallelInsertSliceOp with dynamic entries.
1252 void ParallelInsertSliceOp::build(OpBuilder &b, OperationState &result,
1253                                   Value source, Value dest, ValueRange offsets,
1254                                   ValueRange sizes, ValueRange strides,
1255                                   ArrayRef<NamedAttribute> attrs) {
1256   SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>(
1257       llvm::map_range(offsets, [](Value v) -> OpFoldResult { return v; }));
1258   SmallVector<OpFoldResult> sizeValues = llvm::to_vector<4>(
1259       llvm::map_range(sizes, [](Value v) -> OpFoldResult { return v; }));
1260   SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(
1261       llvm::map_range(strides, [](Value v) -> OpFoldResult { return v; }));
1262   build(b, result, source, dest, offsetValues, sizeValues, strideValues);
1263 }
1264 
1265 LogicalResult ParallelInsertSliceOp::verify() {
1266   if (!isa<ParallelCombiningOpInterface>(getOperation()->getParentOp()))
1267     return this->emitError("expected ParallelCombiningOpInterface parent, got:")
1268            << *(getOperation()->getParentOp());
1269   return success();
1270 }
1271 
1272 namespace {
1273 /// Pattern to rewrite a parallel_insert_slice op with constant arguments.
1274 class ParallelInsertSliceOpConstantArgumentFolder final
1275     : public OpRewritePattern<ParallelInsertSliceOp> {
1276 public:
1277   using OpRewritePattern<ParallelInsertSliceOp>::OpRewritePattern;
1278 
1279   LogicalResult matchAndRewrite(ParallelInsertSliceOp insertSliceOp,
1280                                 PatternRewriter &rewriter) const override {
1281     // No constant operand, just return.
1282     if (llvm::none_of(insertSliceOp.getOperands(), [](Value operand) {
1283           return matchPattern(operand, matchConstantIndex());
1284         }))
1285       return failure();
1286 
1287     // At least one of offsets/sizes/strides is a new constant.
1288     // Form the new list of operands and constant attributes from the
1289     // existing.
1290     SmallVector<OpFoldResult> mixedOffsets(insertSliceOp.getMixedOffsets());
1291     SmallVector<OpFoldResult> mixedSizes(insertSliceOp.getMixedSizes());
1292     SmallVector<OpFoldResult> mixedStrides(insertSliceOp.getMixedStrides());
1293     canonicalizeSubViewPart(mixedOffsets, ShapedType::isDynamicStrideOrOffset);
1294     canonicalizeSubViewPart(mixedSizes, ShapedType::isDynamic);
1295     canonicalizeSubViewPart(mixedStrides, ShapedType::isDynamicStrideOrOffset);
1296 
1297     // Create the new op in canonical form.
1298     rewriter.replaceOpWithNewOp<ParallelInsertSliceOp>(
1299         insertSliceOp, insertSliceOp.getSource(), insertSliceOp.getDest(),
1300         mixedOffsets, mixedSizes, mixedStrides);
1301     return success();
1302   }
1303 };
1304 } // namespace
1305 
1306 /// Fold a parallel_insert_slice source coming from a tensor.cast op.
1307 ///
1308 /// Example:
1309 /// ```
1310 /// %0 = scf.foreach_thread (%arg0) in (%c2) -> (tensor<128xf32>) {
1311 ///   %1 = compute_some_tensor() : tensor<64xf32>
1312 ///   %2 = tensor.cast %1 : tensor<64xf32> to tensor<?xf32>
1313 ///   scf.foreach_thread.perform_concurrently {
1314 ///     scf.foreach_thread.parallel_insert_slice %2 into %out[...] [64] [1] :
1315 ///        tensor<?xf32> into tensor<128xf32>
1316 ///   }
1317 /// }
1318 /// ```
1319 ///
1320 /// is folded into:
1321 /// ```
1322 /// %0 = scf.foreach_thread (%arg0) in (%c2) -> (tensor<128xf32>) {
1323 ///   %1 = compute_some_tensor() : tensor<64xf32>
1324 ///   scf.foreach_thread.perform_concurrently {
1325 ///     scf.foreach_thread.parallel_insert_slice %1 into %out[...] [64] [1] :
1326 ///        tensor<64xf32> into tensor<128xf32>
1327 ///   }
1328 /// }
1329 /// ```
1330 LogicalResult
1331 ParallelInsertSliceOp::fold(ArrayRef<Attribute> operands,
1332                             SmallVectorImpl<OpFoldResult> &results) {
1333   auto sourceCast = getSource().getDefiningOp<tensor::CastOp>();
1334   if (!sourceCast)
1335     return failure();
1336   getSourceMutable().assign(sourceCast.getSource());
1337   return success();
1338 }
1339 
1340 void ParallelInsertSliceOp::getCanonicalizationPatterns(
1341     RewritePatternSet &results, MLIRContext *context) {
1342   results.add<ParallelInsertSliceOpConstantArgumentFolder>(context);
1343 }
1344 
1345 //===----------------------------------------------------------------------===//
1346 // PerformConcurrentlyOp
1347 //===----------------------------------------------------------------------===//
1348 
1349 // Build a PerformConcurrentlyOp with mixed static and dynamic entries.
1350 void PerformConcurrentlyOp::build(OpBuilder &b, OperationState &result) {
1351   OpBuilder::InsertionGuard g(b);
1352   Region *bodyRegion = result.addRegion();
1353   b.createBlock(bodyRegion);
1354 }
1355 
1356 LogicalResult PerformConcurrentlyOp::verify() {
1357   // TODO: PerformConcurrentlyOpInterface.
1358   for (const Operation &op : getRegion().front().getOperations())
1359     if (!isa<ParallelInsertSliceOp>(op))
1360       return emitOpError(
1361           "expected only scf.foreach_thread.parallel_insert_slice ops");
1362   return success();
1363 }
1364 
1365 void PerformConcurrentlyOp::print(OpAsmPrinter &p) {
1366   p << " ";
1367   p.printRegion(getRegion(),
1368                 /*printEntryBlockArgs=*/false,
1369                 /*printBlockTerminators=*/false);
1370   p.printOptionalAttrDict(getOperation()->getAttrs());
1371 }
1372 
1373 ParseResult PerformConcurrentlyOp::parse(OpAsmParser &parser,
1374                                          OperationState &result) {
1375   auto &builder = parser.getBuilder();
1376 
1377   SmallVector<OpAsmParser::Argument, 8> regionOperands;
1378   std::unique_ptr<Region> region = std::make_unique<Region>();
1379   if (parser.parseRegion(*region, regionOperands))
1380     return failure();
1381 
1382   if (region->empty())
1383     OpBuilder(builder.getContext()).createBlock(region.get());
1384   result.addRegion(std::move(region));
1385 
1386   // Parse the optional attribute list.
1387   if (parser.parseOptionalAttrDict(result.attributes))
1388     return failure();
1389   return success();
1390 }
1391 
1392 OpResult PerformConcurrentlyOp::getParentResult(int64_t idx) {
1393   return getOperation()->getParentOp()->getResult(idx);
1394 }
1395 
1396 SmallVector<Type> PerformConcurrentlyOp::getYieldedTypes() {
1397   return llvm::to_vector<4>(
1398       llvm::map_range(getYieldingOps(), [](Operation &op) {
1399         auto insertSliceOp = dyn_cast<ParallelInsertSliceOp>(&op);
1400         return insertSliceOp ? insertSliceOp.yieldedType() : Type();
1401       }));
1402 }
1403 
1404 llvm::iterator_range<Block::iterator> PerformConcurrentlyOp::getYieldingOps() {
1405   return getRegion().front().getOperations();
1406 }
1407 
1408 //===----------------------------------------------------------------------===//
1409 // IfOp
1410 //===----------------------------------------------------------------------===//
1411 
1412 bool mlir::scf::insideMutuallyExclusiveBranches(Operation *a, Operation *b) {
1413   assert(a && "expected non-empty operation");
1414   assert(b && "expected non-empty operation");
1415 
1416   IfOp ifOp = a->getParentOfType<IfOp>();
1417   while (ifOp) {
1418     // Check if b is inside ifOp. (We already know that a is.)
1419     if (ifOp->isProperAncestor(b))
1420       // b is contained in ifOp. a and b are in mutually exclusive branches if
1421       // they are in different blocks of ifOp.
1422       return static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*a)) !=
1423              static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*b));
1424     // Check next enclosing IfOp.
1425     ifOp = ifOp->getParentOfType<IfOp>();
1426   }
1427 
1428   // Could not find a common IfOp among a's and b's ancestors.
1429   return false;
1430 }
1431 
1432 void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
1433                  bool withElseRegion) {
1434   build(builder, result, /*resultTypes=*/llvm::None, cond, withElseRegion);
1435 }
1436 
1437 void IfOp::build(OpBuilder &builder, OperationState &result,
1438                  TypeRange resultTypes, Value cond, bool withElseRegion) {
1439   auto addTerminator = [&](OpBuilder &nested, Location loc) {
1440     if (resultTypes.empty())
1441       IfOp::ensureTerminator(*nested.getInsertionBlock()->getParent(), nested,
1442                              loc);
1443   };
1444 
1445   build(builder, result, resultTypes, cond, addTerminator,
1446         withElseRegion ? addTerminator
1447                        : function_ref<void(OpBuilder &, Location)>());
1448 }
1449 
1450 void IfOp::build(OpBuilder &builder, OperationState &result,
1451                  TypeRange resultTypes, Value cond,
1452                  function_ref<void(OpBuilder &, Location)> thenBuilder,
1453                  function_ref<void(OpBuilder &, Location)> elseBuilder) {
1454   assert(thenBuilder && "the builder callback for 'then' must be present");
1455 
1456   result.addOperands(cond);
1457   result.addTypes(resultTypes);
1458 
1459   OpBuilder::InsertionGuard guard(builder);
1460   Region *thenRegion = result.addRegion();
1461   builder.createBlock(thenRegion);
1462   thenBuilder(builder, result.location);
1463 
1464   Region *elseRegion = result.addRegion();
1465   if (!elseBuilder)
1466     return;
1467 
1468   builder.createBlock(elseRegion);
1469   elseBuilder(builder, result.location);
1470 }
1471 
1472 void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
1473                  function_ref<void(OpBuilder &, Location)> thenBuilder,
1474                  function_ref<void(OpBuilder &, Location)> elseBuilder) {
1475   build(builder, result, TypeRange(), cond, thenBuilder, elseBuilder);
1476 }
1477 
1478 LogicalResult IfOp::verify() {
1479   if (getNumResults() != 0 && getElseRegion().empty())
1480     return emitOpError("must have an else block if defining values");
1481   return success();
1482 }
1483 
1484 ParseResult IfOp::parse(OpAsmParser &parser, OperationState &result) {
1485   // Create the regions for 'then'.
1486   result.regions.reserve(2);
1487   Region *thenRegion = result.addRegion();
1488   Region *elseRegion = result.addRegion();
1489 
1490   auto &builder = parser.getBuilder();
1491   OpAsmParser::UnresolvedOperand cond;
1492   Type i1Type = builder.getIntegerType(1);
1493   if (parser.parseOperand(cond) ||
1494       parser.resolveOperand(cond, i1Type, result.operands))
1495     return failure();
1496   // Parse optional results type list.
1497   if (parser.parseOptionalArrowTypeList(result.types))
1498     return failure();
1499   // Parse the 'then' region.
1500   if (parser.parseRegion(*thenRegion, /*arguments=*/{}, /*argTypes=*/{}))
1501     return failure();
1502   IfOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location);
1503 
1504   // If we find an 'else' keyword then parse the 'else' region.
1505   if (!parser.parseOptionalKeyword("else")) {
1506     if (parser.parseRegion(*elseRegion, /*arguments=*/{}, /*argTypes=*/{}))
1507       return failure();
1508     IfOp::ensureTerminator(*elseRegion, parser.getBuilder(), result.location);
1509   }
1510 
1511   // Parse the optional attribute list.
1512   if (parser.parseOptionalAttrDict(result.attributes))
1513     return failure();
1514   return success();
1515 }
1516 
1517 void IfOp::print(OpAsmPrinter &p) {
1518   bool printBlockTerminators = false;
1519 
1520   p << " " << getCondition();
1521   if (!getResults().empty()) {
1522     p << " -> (" << getResultTypes() << ")";
1523     // Print yield explicitly if the op defines values.
1524     printBlockTerminators = true;
1525   }
1526   p << ' ';
1527   p.printRegion(getThenRegion(),
1528                 /*printEntryBlockArgs=*/false,
1529                 /*printBlockTerminators=*/printBlockTerminators);
1530 
1531   // Print the 'else' regions if it exists and has a block.
1532   auto &elseRegion = getElseRegion();
1533   if (!elseRegion.empty()) {
1534     p << " else ";
1535     p.printRegion(elseRegion,
1536                   /*printEntryBlockArgs=*/false,
1537                   /*printBlockTerminators=*/printBlockTerminators);
1538   }
1539 
1540   p.printOptionalAttrDict((*this)->getAttrs());
1541 }
1542 
1543 /// Given the region at `index`, or the parent operation if `index` is None,
1544 /// return the successor regions. These are the regions that may be selected
1545 /// during the flow of control. `operands` is a set of optional attributes that
1546 /// correspond to a constant value for each operand, or null if that operand is
1547 /// not a constant.
1548 void IfOp::getSuccessorRegions(Optional<unsigned> index,
1549                                ArrayRef<Attribute> operands,
1550                                SmallVectorImpl<RegionSuccessor> &regions) {
1551   // The `then` and the `else` region branch back to the parent operation.
1552   if (index) {
1553     regions.push_back(RegionSuccessor(getResults()));
1554     return;
1555   }
1556 
1557   // Don't consider the else region if it is empty.
1558   Region *elseRegion = &this->getElseRegion();
1559   if (elseRegion->empty())
1560     elseRegion = nullptr;
1561 
1562   // Otherwise, the successor is dependent on the condition.
1563   bool condition;
1564   if (auto condAttr = operands.front().dyn_cast_or_null<IntegerAttr>()) {
1565     condition = condAttr.getValue().isOneValue();
1566   } else {
1567     // If the condition isn't constant, both regions may be executed.
1568     regions.push_back(RegionSuccessor(&getThenRegion()));
1569     // If the else region does not exist, it is not a viable successor.
1570     if (elseRegion)
1571       regions.push_back(RegionSuccessor(elseRegion));
1572     return;
1573   }
1574 
1575   // Add the successor regions using the condition.
1576   regions.push_back(RegionSuccessor(condition ? &getThenRegion() : elseRegion));
1577 }
1578 
1579 LogicalResult IfOp::fold(ArrayRef<Attribute> operands,
1580                          SmallVectorImpl<OpFoldResult> &results) {
1581   // if (!c) then A() else B() -> if c then B() else A()
1582   if (getElseRegion().empty())
1583     return failure();
1584 
1585   arith::XOrIOp xorStmt = getCondition().getDefiningOp<arith::XOrIOp>();
1586   if (!xorStmt)
1587     return failure();
1588 
1589   if (!matchPattern(xorStmt.getRhs(), m_One()))
1590     return failure();
1591 
1592   getConditionMutable().assign(xorStmt.getLhs());
1593   Block *thenBlock = &getThenRegion().front();
1594   // It would be nicer to use iplist::swap, but that has no implemented
1595   // callbacks See: https://llvm.org/doxygen/ilist_8h_source.html#l00224
1596   getThenRegion().getBlocks().splice(getThenRegion().getBlocks().begin(),
1597                                      getElseRegion().getBlocks());
1598   getElseRegion().getBlocks().splice(getElseRegion().getBlocks().begin(),
1599                                      getThenRegion().getBlocks(), thenBlock);
1600   return success();
1601 }
1602 
1603 void IfOp::getRegionInvocationBounds(
1604     ArrayRef<Attribute> operands,
1605     SmallVectorImpl<InvocationBounds> &invocationBounds) {
1606   if (auto cond = operands[0].dyn_cast_or_null<BoolAttr>()) {
1607     // If the condition is known, then one region is known to be executed once
1608     // and the other zero times.
1609     invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
1610     invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
1611   } else {
1612     // Non-constant condition. Each region may be executed 0 or 1 times.
1613     invocationBounds.assign(2, {0, 1});
1614   }
1615 }
1616 
1617 namespace {
1618 // Pattern to remove unused IfOp results.
1619 struct RemoveUnusedResults : public OpRewritePattern<IfOp> {
1620   using OpRewritePattern<IfOp>::OpRewritePattern;
1621 
1622   void transferBody(Block *source, Block *dest, ArrayRef<OpResult> usedResults,
1623                     PatternRewriter &rewriter) const {
1624     // Move all operations to the destination block.
1625     rewriter.mergeBlocks(source, dest);
1626     // Replace the yield op by one that returns only the used values.
1627     auto yieldOp = cast<scf::YieldOp>(dest->getTerminator());
1628     SmallVector<Value, 4> usedOperands;
1629     llvm::transform(usedResults, std::back_inserter(usedOperands),
1630                     [&](OpResult result) {
1631                       return yieldOp.getOperand(result.getResultNumber());
1632                     });
1633     rewriter.updateRootInPlace(yieldOp,
1634                                [&]() { yieldOp->setOperands(usedOperands); });
1635   }
1636 
1637   LogicalResult matchAndRewrite(IfOp op,
1638                                 PatternRewriter &rewriter) const override {
1639     // Compute the list of used results.
1640     SmallVector<OpResult, 4> usedResults;
1641     llvm::copy_if(op.getResults(), std::back_inserter(usedResults),
1642                   [](OpResult result) { return !result.use_empty(); });
1643 
1644     // Replace the operation if only a subset of its results have uses.
1645     if (usedResults.size() == op.getNumResults())
1646       return failure();
1647 
1648     // Compute the result types of the replacement operation.
1649     SmallVector<Type, 4> newTypes;
1650     llvm::transform(usedResults, std::back_inserter(newTypes),
1651                     [](OpResult result) { return result.getType(); });
1652 
1653     // Create a replacement operation with empty then and else regions.
1654     auto emptyBuilder = [](OpBuilder &, Location) {};
1655     auto newOp = rewriter.create<IfOp>(op.getLoc(), newTypes, op.getCondition(),
1656                                        emptyBuilder, emptyBuilder);
1657 
1658     // Move the bodies and replace the terminators (note there is a then and
1659     // an else region since the operation returns results).
1660     transferBody(op.getBody(0), newOp.getBody(0), usedResults, rewriter);
1661     transferBody(op.getBody(1), newOp.getBody(1), usedResults, rewriter);
1662 
1663     // Replace the operation by the new one.
1664     SmallVector<Value, 4> repResults(op.getNumResults());
1665     for (const auto &en : llvm::enumerate(usedResults))
1666       repResults[en.value().getResultNumber()] = newOp.getResult(en.index());
1667     rewriter.replaceOp(op, repResults);
1668     return success();
1669   }
1670 };
1671 
1672 struct RemoveStaticCondition : public OpRewritePattern<IfOp> {
1673   using OpRewritePattern<IfOp>::OpRewritePattern;
1674 
1675   LogicalResult matchAndRewrite(IfOp op,
1676                                 PatternRewriter &rewriter) const override {
1677     auto constant = op.getCondition().getDefiningOp<arith::ConstantOp>();
1678     if (!constant)
1679       return failure();
1680 
1681     if (constant.getValue().cast<BoolAttr>().getValue())
1682       replaceOpWithRegion(rewriter, op, op.getThenRegion());
1683     else if (!op.getElseRegion().empty())
1684       replaceOpWithRegion(rewriter, op, op.getElseRegion());
1685     else
1686       rewriter.eraseOp(op);
1687 
1688     return success();
1689   }
1690 };
1691 
1692 /// Hoist any yielded results whose operands are defined outside
1693 /// the if, to a select instruction.
1694 struct ConvertTrivialIfToSelect : public OpRewritePattern<IfOp> {
1695   using OpRewritePattern<IfOp>::OpRewritePattern;
1696 
1697   LogicalResult matchAndRewrite(IfOp op,
1698                                 PatternRewriter &rewriter) const override {
1699     if (op->getNumResults() == 0)
1700       return failure();
1701 
1702     auto cond = op.getCondition();
1703     auto thenYieldArgs = op.thenYield().getOperands();
1704     auto elseYieldArgs = op.elseYield().getOperands();
1705 
1706     SmallVector<Type> nonHoistable;
1707     for (const auto &it :
1708          llvm::enumerate(llvm::zip(thenYieldArgs, elseYieldArgs))) {
1709       Value trueVal = std::get<0>(it.value());
1710       Value falseVal = std::get<1>(it.value());
1711       if (&op.getThenRegion() == trueVal.getParentRegion() ||
1712           &op.getElseRegion() == falseVal.getParentRegion())
1713         nonHoistable.push_back(trueVal.getType());
1714     }
1715     // Early exit if there aren't any yielded values we can
1716     // hoist outside the if.
1717     if (nonHoistable.size() == op->getNumResults())
1718       return failure();
1719 
1720     IfOp replacement = rewriter.create<IfOp>(op.getLoc(), nonHoistable, cond);
1721     if (replacement.thenBlock())
1722       rewriter.eraseBlock(replacement.thenBlock());
1723     replacement.getThenRegion().takeBody(op.getThenRegion());
1724     replacement.getElseRegion().takeBody(op.getElseRegion());
1725 
1726     SmallVector<Value> results(op->getNumResults());
1727     assert(thenYieldArgs.size() == results.size());
1728     assert(elseYieldArgs.size() == results.size());
1729 
1730     SmallVector<Value> trueYields;
1731     SmallVector<Value> falseYields;
1732     rewriter.setInsertionPoint(replacement);
1733     for (const auto &it :
1734          llvm::enumerate(llvm::zip(thenYieldArgs, elseYieldArgs))) {
1735       Value trueVal = std::get<0>(it.value());
1736       Value falseVal = std::get<1>(it.value());
1737       if (&replacement.getThenRegion() == trueVal.getParentRegion() ||
1738           &replacement.getElseRegion() == falseVal.getParentRegion()) {
1739         results[it.index()] = replacement.getResult(trueYields.size());
1740         trueYields.push_back(trueVal);
1741         falseYields.push_back(falseVal);
1742       } else if (trueVal == falseVal)
1743         results[it.index()] = trueVal;
1744       else
1745         results[it.index()] = rewriter.create<arith::SelectOp>(
1746             op.getLoc(), cond, trueVal, falseVal);
1747     }
1748 
1749     rewriter.setInsertionPointToEnd(replacement.thenBlock());
1750     rewriter.replaceOpWithNewOp<YieldOp>(replacement.thenYield(), trueYields);
1751 
1752     rewriter.setInsertionPointToEnd(replacement.elseBlock());
1753     rewriter.replaceOpWithNewOp<YieldOp>(replacement.elseYield(), falseYields);
1754 
1755     rewriter.replaceOp(op, results);
1756     return success();
1757   }
1758 };
1759 
1760 /// Allow the true region of an if to assume the condition is true
1761 /// and vice versa. For example:
1762 ///
1763 ///   scf.if %cmp {
1764 ///      print(%cmp)
1765 ///   }
1766 ///
1767 ///  becomes
1768 ///
1769 ///   scf.if %cmp {
1770 ///      print(true)
1771 ///   }
1772 ///
1773 struct ConditionPropagation : public OpRewritePattern<IfOp> {
1774   using OpRewritePattern<IfOp>::OpRewritePattern;
1775 
1776   LogicalResult matchAndRewrite(IfOp op,
1777                                 PatternRewriter &rewriter) const override {
1778     // Early exit if the condition is constant since replacing a constant
1779     // in the body with another constant isn't a simplification.
1780     if (op.getCondition().getDefiningOp<arith::ConstantOp>())
1781       return failure();
1782 
1783     bool changed = false;
1784     mlir::Type i1Ty = rewriter.getI1Type();
1785 
1786     // These variables serve to prevent creating duplicate constants
1787     // and hold constant true or false values.
1788     Value constantTrue = nullptr;
1789     Value constantFalse = nullptr;
1790 
1791     for (OpOperand &use :
1792          llvm::make_early_inc_range(op.getCondition().getUses())) {
1793       if (op.getThenRegion().isAncestor(use.getOwner()->getParentRegion())) {
1794         changed = true;
1795 
1796         if (!constantTrue)
1797           constantTrue = rewriter.create<arith::ConstantOp>(
1798               op.getLoc(), i1Ty, rewriter.getIntegerAttr(i1Ty, 1));
1799 
1800         rewriter.updateRootInPlace(use.getOwner(),
1801                                    [&]() { use.set(constantTrue); });
1802       } else if (op.getElseRegion().isAncestor(
1803                      use.getOwner()->getParentRegion())) {
1804         changed = true;
1805 
1806         if (!constantFalse)
1807           constantFalse = rewriter.create<arith::ConstantOp>(
1808               op.getLoc(), i1Ty, rewriter.getIntegerAttr(i1Ty, 0));
1809 
1810         rewriter.updateRootInPlace(use.getOwner(),
1811                                    [&]() { use.set(constantFalse); });
1812       }
1813     }
1814 
1815     return success(changed);
1816   }
1817 };
1818 
1819 /// Remove any statements from an if that are equivalent to the condition
1820 /// or its negation. For example:
1821 ///
1822 ///    %res:2 = scf.if %cmp {
1823 ///       yield something(), true
1824 ///    } else {
1825 ///       yield something2(), false
1826 ///    }
1827 ///    print(%res#1)
1828 ///
1829 ///  becomes
1830 ///    %res = scf.if %cmp {
1831 ///       yield something()
1832 ///    } else {
1833 ///       yield something2()
1834 ///    }
1835 ///    print(%cmp)
1836 ///
1837 /// Additionally if both branches yield the same value, replace all uses
1838 /// of the result with the yielded value.
1839 ///
1840 ///    %res:2 = scf.if %cmp {
1841 ///       yield something(), %arg1
1842 ///    } else {
1843 ///       yield something2(), %arg1
1844 ///    }
1845 ///    print(%res#1)
1846 ///
1847 ///  becomes
1848 ///    %res = scf.if %cmp {
1849 ///       yield something()
1850 ///    } else {
1851 ///       yield something2()
1852 ///    }
1853 ///    print(%arg1)
1854 ///
1855 struct ReplaceIfYieldWithConditionOrValue : public OpRewritePattern<IfOp> {
1856   using OpRewritePattern<IfOp>::OpRewritePattern;
1857 
1858   LogicalResult matchAndRewrite(IfOp op,
1859                                 PatternRewriter &rewriter) const override {
1860     // Early exit if there are no results that could be replaced.
1861     if (op.getNumResults() == 0)
1862       return failure();
1863 
1864     auto trueYield =
1865         cast<scf::YieldOp>(op.getThenRegion().back().getTerminator());
1866     auto falseYield =
1867         cast<scf::YieldOp>(op.getElseRegion().back().getTerminator());
1868 
1869     rewriter.setInsertionPoint(op->getBlock(),
1870                                op.getOperation()->getIterator());
1871     bool changed = false;
1872     Type i1Ty = rewriter.getI1Type();
1873     for (auto tup : llvm::zip(trueYield.getResults(), falseYield.getResults(),
1874                               op.getResults())) {
1875       Value trueResult, falseResult, opResult;
1876       std::tie(trueResult, falseResult, opResult) = tup;
1877 
1878       if (trueResult == falseResult) {
1879         if (!opResult.use_empty()) {
1880           opResult.replaceAllUsesWith(trueResult);
1881           changed = true;
1882         }
1883         continue;
1884       }
1885 
1886       auto trueYield = trueResult.getDefiningOp<arith::ConstantOp>();
1887       if (!trueYield)
1888         continue;
1889 
1890       if (!trueYield.getType().isInteger(1))
1891         continue;
1892 
1893       auto falseYield = falseResult.getDefiningOp<arith::ConstantOp>();
1894       if (!falseYield)
1895         continue;
1896 
1897       bool trueVal = trueYield.getValue().cast<BoolAttr>().getValue();
1898       bool falseVal = falseYield.getValue().cast<BoolAttr>().getValue();
1899       if (!trueVal && falseVal) {
1900         if (!opResult.use_empty()) {
1901           Value notCond = rewriter.create<arith::XOrIOp>(
1902               op.getLoc(), op.getCondition(),
1903               rewriter.create<arith::ConstantOp>(
1904                   op.getLoc(), i1Ty, rewriter.getIntegerAttr(i1Ty, 1)));
1905           opResult.replaceAllUsesWith(notCond);
1906           changed = true;
1907         }
1908       }
1909       if (trueVal && !falseVal) {
1910         if (!opResult.use_empty()) {
1911           opResult.replaceAllUsesWith(op.getCondition());
1912           changed = true;
1913         }
1914       }
1915     }
1916     return success(changed);
1917   }
1918 };
1919 
1920 /// Merge any consecutive scf.if's with the same condition.
1921 ///
1922 ///    scf.if %cond {
1923 ///       firstCodeTrue();...
1924 ///    } else {
1925 ///       firstCodeFalse();...
1926 ///    }
1927 ///    %res = scf.if %cond {
1928 ///       secondCodeTrue();...
1929 ///    } else {
1930 ///       secondCodeFalse();...
1931 ///    }
1932 ///
1933 ///  becomes
1934 ///    %res = scf.if %cmp {
1935 ///       firstCodeTrue();...
1936 ///       secondCodeTrue();...
1937 ///    } else {
1938 ///       firstCodeFalse();...
1939 ///       secondCodeFalse();...
1940 ///    }
1941 struct CombineIfs : public OpRewritePattern<IfOp> {
1942   using OpRewritePattern<IfOp>::OpRewritePattern;
1943 
1944   LogicalResult matchAndRewrite(IfOp nextIf,
1945                                 PatternRewriter &rewriter) const override {
1946     Block *parent = nextIf->getBlock();
1947     if (nextIf == &parent->front())
1948       return failure();
1949 
1950     auto prevIf = dyn_cast<IfOp>(nextIf->getPrevNode());
1951     if (!prevIf)
1952       return failure();
1953 
1954     // Determine the logical then/else blocks when prevIf's
1955     // condition is used. Null means the block does not exist
1956     // in that case (e.g. empty else). If neither of these
1957     // are set, the two conditions cannot be compared.
1958     Block *nextThen = nullptr;
1959     Block *nextElse = nullptr;
1960     if (nextIf.getCondition() == prevIf.getCondition()) {
1961       nextThen = nextIf.thenBlock();
1962       if (!nextIf.getElseRegion().empty())
1963         nextElse = nextIf.elseBlock();
1964     }
1965     if (arith::XOrIOp notv =
1966             nextIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
1967       if (notv.getLhs() == prevIf.getCondition() &&
1968           matchPattern(notv.getRhs(), m_One())) {
1969         nextElse = nextIf.thenBlock();
1970         if (!nextIf.getElseRegion().empty())
1971           nextThen = nextIf.elseBlock();
1972       }
1973     }
1974     if (arith::XOrIOp notv =
1975             prevIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
1976       if (notv.getLhs() == nextIf.getCondition() &&
1977           matchPattern(notv.getRhs(), m_One())) {
1978         nextElse = nextIf.thenBlock();
1979         if (!nextIf.getElseRegion().empty())
1980           nextThen = nextIf.elseBlock();
1981       }
1982     }
1983 
1984     if (!nextThen && !nextElse)
1985       return failure();
1986 
1987     SmallVector<Value> prevElseYielded;
1988     if (!prevIf.getElseRegion().empty())
1989       prevElseYielded = prevIf.elseYield().getOperands();
1990     // Replace all uses of return values of op within nextIf with the
1991     // corresponding yields
1992     for (auto it : llvm::zip(prevIf.getResults(),
1993                              prevIf.thenYield().getOperands(), prevElseYielded))
1994       for (OpOperand &use :
1995            llvm::make_early_inc_range(std::get<0>(it).getUses())) {
1996         if (nextThen && nextThen->getParent()->isAncestor(
1997                             use.getOwner()->getParentRegion())) {
1998           rewriter.startRootUpdate(use.getOwner());
1999           use.set(std::get<1>(it));
2000           rewriter.finalizeRootUpdate(use.getOwner());
2001         } else if (nextElse && nextElse->getParent()->isAncestor(
2002                                    use.getOwner()->getParentRegion())) {
2003           rewriter.startRootUpdate(use.getOwner());
2004           use.set(std::get<2>(it));
2005           rewriter.finalizeRootUpdate(use.getOwner());
2006         }
2007       }
2008 
2009     SmallVector<Type> mergedTypes(prevIf.getResultTypes());
2010     llvm::append_range(mergedTypes, nextIf.getResultTypes());
2011 
2012     IfOp combinedIf = rewriter.create<IfOp>(
2013         nextIf.getLoc(), mergedTypes, prevIf.getCondition(), /*hasElse=*/false);
2014     rewriter.eraseBlock(&combinedIf.getThenRegion().back());
2015 
2016     rewriter.inlineRegionBefore(prevIf.getThenRegion(),
2017                                 combinedIf.getThenRegion(),
2018                                 combinedIf.getThenRegion().begin());
2019 
2020     if (nextThen) {
2021       YieldOp thenYield = combinedIf.thenYield();
2022       YieldOp thenYield2 = cast<YieldOp>(nextThen->getTerminator());
2023       rewriter.mergeBlocks(nextThen, combinedIf.thenBlock());
2024       rewriter.setInsertionPointToEnd(combinedIf.thenBlock());
2025 
2026       SmallVector<Value> mergedYields(thenYield.getOperands());
2027       llvm::append_range(mergedYields, thenYield2.getOperands());
2028       rewriter.create<YieldOp>(thenYield2.getLoc(), mergedYields);
2029       rewriter.eraseOp(thenYield);
2030       rewriter.eraseOp(thenYield2);
2031     }
2032 
2033     rewriter.inlineRegionBefore(prevIf.getElseRegion(),
2034                                 combinedIf.getElseRegion(),
2035                                 combinedIf.getElseRegion().begin());
2036 
2037     if (nextElse) {
2038       if (combinedIf.getElseRegion().empty()) {
2039         rewriter.inlineRegionBefore(*nextElse->getParent(),
2040                                     combinedIf.getElseRegion(),
2041                                     combinedIf.getElseRegion().begin());
2042       } else {
2043         YieldOp elseYield = combinedIf.elseYield();
2044         YieldOp elseYield2 = cast<YieldOp>(nextElse->getTerminator());
2045         rewriter.mergeBlocks(nextElse, combinedIf.elseBlock());
2046 
2047         rewriter.setInsertionPointToEnd(combinedIf.elseBlock());
2048 
2049         SmallVector<Value> mergedElseYields(elseYield.getOperands());
2050         llvm::append_range(mergedElseYields, elseYield2.getOperands());
2051 
2052         rewriter.create<YieldOp>(elseYield2.getLoc(), mergedElseYields);
2053         rewriter.eraseOp(elseYield);
2054         rewriter.eraseOp(elseYield2);
2055       }
2056     }
2057 
2058     SmallVector<Value> prevValues;
2059     SmallVector<Value> nextValues;
2060     for (const auto &pair : llvm::enumerate(combinedIf.getResults())) {
2061       if (pair.index() < prevIf.getNumResults())
2062         prevValues.push_back(pair.value());
2063       else
2064         nextValues.push_back(pair.value());
2065     }
2066     rewriter.replaceOp(prevIf, prevValues);
2067     rewriter.replaceOp(nextIf, nextValues);
2068     return success();
2069   }
2070 };
2071 
2072 /// Pattern to remove an empty else branch.
2073 struct RemoveEmptyElseBranch : public OpRewritePattern<IfOp> {
2074   using OpRewritePattern<IfOp>::OpRewritePattern;
2075 
2076   LogicalResult matchAndRewrite(IfOp ifOp,
2077                                 PatternRewriter &rewriter) const override {
2078     // Cannot remove else region when there are operation results.
2079     if (ifOp.getNumResults())
2080       return failure();
2081     Block *elseBlock = ifOp.elseBlock();
2082     if (!elseBlock || !llvm::hasSingleElement(*elseBlock))
2083       return failure();
2084     auto newIfOp = rewriter.cloneWithoutRegions(ifOp);
2085     rewriter.inlineRegionBefore(ifOp.getThenRegion(), newIfOp.getThenRegion(),
2086                                 newIfOp.getThenRegion().begin());
2087     rewriter.eraseOp(ifOp);
2088     return success();
2089   }
2090 };
2091 
2092 /// Convert nested `if`s into `arith.andi` + single `if`.
2093 ///
2094 ///    scf.if %arg0 {
2095 ///      scf.if %arg1 {
2096 ///        ...
2097 ///        scf.yield
2098 ///      }
2099 ///      scf.yield
2100 ///    }
2101 ///  becomes
2102 ///
2103 ///    %0 = arith.andi %arg0, %arg1
2104 ///    scf.if %0 {
2105 ///      ...
2106 ///      scf.yield
2107 ///    }
2108 struct CombineNestedIfs : public OpRewritePattern<IfOp> {
2109   using OpRewritePattern<IfOp>::OpRewritePattern;
2110 
2111   LogicalResult matchAndRewrite(IfOp op,
2112                                 PatternRewriter &rewriter) const override {
2113     auto nestedOps = op.thenBlock()->without_terminator();
2114     // Nested `if` must be the only op in block.
2115     if (!llvm::hasSingleElement(nestedOps))
2116       return failure();
2117 
2118     // If there is an else block, it can only yield
2119     if (op.elseBlock() && !llvm::hasSingleElement(*op.elseBlock()))
2120       return failure();
2121 
2122     auto nestedIf = dyn_cast<IfOp>(*nestedOps.begin());
2123     if (!nestedIf)
2124       return failure();
2125 
2126     if (nestedIf.elseBlock() && !llvm::hasSingleElement(*nestedIf.elseBlock()))
2127       return failure();
2128 
2129     SmallVector<Value> thenYield(op.thenYield().getOperands());
2130     SmallVector<Value> elseYield;
2131     if (op.elseBlock())
2132       llvm::append_range(elseYield, op.elseYield().getOperands());
2133 
2134     // A list of indices for which we should upgrade the value yielded
2135     // in the else to a select.
2136     SmallVector<unsigned> elseYieldsToUpgradeToSelect;
2137 
2138     // If the outer scf.if yields a value produced by the inner scf.if,
2139     // only permit combining if the value yielded when the condition
2140     // is false in the outer scf.if is the same value yielded when the
2141     // inner scf.if condition is false.
2142     // Note that the array access to elseYield will not go out of bounds
2143     // since it must have the same length as thenYield, since they both
2144     // come from the same scf.if.
2145     for (const auto &tup : llvm::enumerate(thenYield)) {
2146       if (tup.value().getDefiningOp() == nestedIf) {
2147         auto nestedIdx = tup.value().cast<OpResult>().getResultNumber();
2148         if (nestedIf.elseYield().getOperand(nestedIdx) !=
2149             elseYield[tup.index()]) {
2150           return failure();
2151         }
2152         // If the correctness test passes, we will yield
2153         // corresponding value from the inner scf.if
2154         thenYield[tup.index()] = nestedIf.thenYield().getOperand(nestedIdx);
2155         continue;
2156       }
2157 
2158       // Otherwise, we need to ensure the else block of the combined
2159       // condition still returns the same value when the outer condition is
2160       // true and the inner condition is false. This can be accomplished if
2161       // the then value is defined outside the outer scf.if and we replace the
2162       // value with a select that considers just the outer condition. Since
2163       // the else region contains just the yield, its yielded value is
2164       // defined outside the scf.if, by definition.
2165 
2166       // If the then value is defined within the scf.if, bail.
2167       if (tup.value().getParentRegion() == &op.getThenRegion()) {
2168         return failure();
2169       }
2170       elseYieldsToUpgradeToSelect.push_back(tup.index());
2171     }
2172 
2173     Location loc = op.getLoc();
2174     Value newCondition = rewriter.create<arith::AndIOp>(
2175         loc, op.getCondition(), nestedIf.getCondition());
2176     auto newIf = rewriter.create<IfOp>(loc, op.getResultTypes(), newCondition);
2177 
2178     SmallVector<Value> results;
2179     llvm::append_range(results, newIf.getResults());
2180     rewriter.setInsertionPoint(newIf);
2181 
2182     for (auto idx : elseYieldsToUpgradeToSelect)
2183       results[idx] = rewriter.create<arith::SelectOp>(
2184           op.getLoc(), op.getCondition(), thenYield[idx], elseYield[idx]);
2185 
2186     Block *newIfBlock = newIf.thenBlock();
2187     if (newIfBlock)
2188       rewriter.eraseOp(newIfBlock->getTerminator());
2189     else
2190       newIfBlock = rewriter.createBlock(&newIf.getThenRegion());
2191     rewriter.mergeBlocks(nestedIf.thenBlock(), newIfBlock);
2192     rewriter.setInsertionPointToEnd(newIf.thenBlock());
2193     rewriter.replaceOpWithNewOp<YieldOp>(newIf.thenYield(), thenYield);
2194     if (!elseYield.empty()) {
2195       rewriter.createBlock(&newIf.getElseRegion());
2196       rewriter.setInsertionPointToEnd(newIf.elseBlock());
2197       rewriter.create<YieldOp>(loc, elseYield);
2198     }
2199     rewriter.replaceOp(op, results);
2200     return success();
2201   }
2202 };
2203 
2204 } // namespace
2205 
2206 void IfOp::getCanonicalizationPatterns(RewritePatternSet &results,
2207                                        MLIRContext *context) {
2208   results.add<CombineIfs, CombineNestedIfs, ConditionPropagation,
2209               ConvertTrivialIfToSelect, RemoveEmptyElseBranch,
2210               RemoveStaticCondition, RemoveUnusedResults,
2211               ReplaceIfYieldWithConditionOrValue>(context);
2212 }
2213 
2214 Block *IfOp::thenBlock() { return &getThenRegion().back(); }
2215 YieldOp IfOp::thenYield() { return cast<YieldOp>(&thenBlock()->back()); }
2216 Block *IfOp::elseBlock() {
2217   Region &r = getElseRegion();
2218   if (r.empty())
2219     return nullptr;
2220   return &r.back();
2221 }
2222 YieldOp IfOp::elseYield() { return cast<YieldOp>(&elseBlock()->back()); }
2223 
2224 //===----------------------------------------------------------------------===//
2225 // ParallelOp
2226 //===----------------------------------------------------------------------===//
2227 
2228 void ParallelOp::build(
2229     OpBuilder &builder, OperationState &result, ValueRange lowerBounds,
2230     ValueRange upperBounds, ValueRange steps, ValueRange initVals,
2231     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
2232         bodyBuilderFn) {
2233   result.addOperands(lowerBounds);
2234   result.addOperands(upperBounds);
2235   result.addOperands(steps);
2236   result.addOperands(initVals);
2237   result.addAttribute(
2238       ParallelOp::getOperandSegmentSizeAttr(),
2239       builder.getI32VectorAttr({static_cast<int32_t>(lowerBounds.size()),
2240                                 static_cast<int32_t>(upperBounds.size()),
2241                                 static_cast<int32_t>(steps.size()),
2242                                 static_cast<int32_t>(initVals.size())}));
2243   result.addTypes(initVals.getTypes());
2244 
2245   OpBuilder::InsertionGuard guard(builder);
2246   unsigned numIVs = steps.size();
2247   SmallVector<Type, 8> argTypes(numIVs, builder.getIndexType());
2248   SmallVector<Location, 8> argLocs(numIVs, result.location);
2249   Region *bodyRegion = result.addRegion();
2250   Block *bodyBlock = builder.createBlock(bodyRegion, {}, argTypes, argLocs);
2251 
2252   if (bodyBuilderFn) {
2253     builder.setInsertionPointToStart(bodyBlock);
2254     bodyBuilderFn(builder, result.location,
2255                   bodyBlock->getArguments().take_front(numIVs),
2256                   bodyBlock->getArguments().drop_front(numIVs));
2257   }
2258   ParallelOp::ensureTerminator(*bodyRegion, builder, result.location);
2259 }
2260 
2261 void ParallelOp::build(
2262     OpBuilder &builder, OperationState &result, ValueRange lowerBounds,
2263     ValueRange upperBounds, ValueRange steps,
2264     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {
2265   // Only pass a non-null wrapper if bodyBuilderFn is non-null itself. Make sure
2266   // we don't capture a reference to a temporary by constructing the lambda at
2267   // function level.
2268   auto wrappedBuilderFn = [&bodyBuilderFn](OpBuilder &nestedBuilder,
2269                                            Location nestedLoc, ValueRange ivs,
2270                                            ValueRange) {
2271     bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
2272   };
2273   function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)> wrapper;
2274   if (bodyBuilderFn)
2275     wrapper = wrappedBuilderFn;
2276 
2277   build(builder, result, lowerBounds, upperBounds, steps, ValueRange(),
2278         wrapper);
2279 }
2280 
2281 LogicalResult ParallelOp::verify() {
2282   // Check that there is at least one value in lowerBound, upperBound and step.
2283   // It is sufficient to test only step, because it is ensured already that the
2284   // number of elements in lowerBound, upperBound and step are the same.
2285   Operation::operand_range stepValues = getStep();
2286   if (stepValues.empty())
2287     return emitOpError(
2288         "needs at least one tuple element for lowerBound, upperBound and step");
2289 
2290   // Check whether all constant step values are positive.
2291   for (Value stepValue : stepValues)
2292     if (auto cst = stepValue.getDefiningOp<arith::ConstantIndexOp>())
2293       if (cst.value() <= 0)
2294         return emitOpError("constant step operand must be positive");
2295 
2296   // Check that the body defines the same number of block arguments as the
2297   // number of tuple elements in step.
2298   Block *body = getBody();
2299   if (body->getNumArguments() != stepValues.size())
2300     return emitOpError() << "expects the same number of induction variables: "
2301                          << body->getNumArguments()
2302                          << " as bound and step values: " << stepValues.size();
2303   for (auto arg : body->getArguments())
2304     if (!arg.getType().isIndex())
2305       return emitOpError(
2306           "expects arguments for the induction variable to be of index type");
2307 
2308   // Check that the yield has no results
2309   Operation *yield = body->getTerminator();
2310   if (yield->getNumOperands() != 0)
2311     return yield->emitOpError() << "not allowed to have operands inside '"
2312                                 << ParallelOp::getOperationName() << "'";
2313 
2314   // Check that the number of results is the same as the number of ReduceOps.
2315   SmallVector<ReduceOp, 4> reductions(body->getOps<ReduceOp>());
2316   auto resultsSize = getResults().size();
2317   auto reductionsSize = reductions.size();
2318   auto initValsSize = getInitVals().size();
2319   if (resultsSize != reductionsSize)
2320     return emitOpError() << "expects number of results: " << resultsSize
2321                          << " to be the same as number of reductions: "
2322                          << reductionsSize;
2323   if (resultsSize != initValsSize)
2324     return emitOpError() << "expects number of results: " << resultsSize
2325                          << " to be the same as number of initial values: "
2326                          << initValsSize;
2327 
2328   // Check that the types of the results and reductions are the same.
2329   for (auto resultAndReduce : llvm::zip(getResults(), reductions)) {
2330     auto resultType = std::get<0>(resultAndReduce).getType();
2331     auto reduceOp = std::get<1>(resultAndReduce);
2332     auto reduceType = reduceOp.getOperand().getType();
2333     if (resultType != reduceType)
2334       return reduceOp.emitOpError()
2335              << "expects type of reduce: " << reduceType
2336              << " to be the same as result type: " << resultType;
2337   }
2338   return success();
2339 }
2340 
2341 ParseResult ParallelOp::parse(OpAsmParser &parser, OperationState &result) {
2342   auto &builder = parser.getBuilder();
2343   // Parse an opening `(` followed by induction variables followed by `)`
2344   SmallVector<OpAsmParser::Argument, 4> ivs;
2345   if (parser.parseArgumentList(ivs, OpAsmParser::Delimiter::Paren))
2346     return failure();
2347 
2348   // Parse loop bounds.
2349   SmallVector<OpAsmParser::UnresolvedOperand, 4> lower;
2350   if (parser.parseEqual() ||
2351       parser.parseOperandList(lower, ivs.size(),
2352                               OpAsmParser::Delimiter::Paren) ||
2353       parser.resolveOperands(lower, builder.getIndexType(), result.operands))
2354     return failure();
2355 
2356   SmallVector<OpAsmParser::UnresolvedOperand, 4> upper;
2357   if (parser.parseKeyword("to") ||
2358       parser.parseOperandList(upper, ivs.size(),
2359                               OpAsmParser::Delimiter::Paren) ||
2360       parser.resolveOperands(upper, builder.getIndexType(), result.operands))
2361     return failure();
2362 
2363   // Parse step values.
2364   SmallVector<OpAsmParser::UnresolvedOperand, 4> steps;
2365   if (parser.parseKeyword("step") ||
2366       parser.parseOperandList(steps, ivs.size(),
2367                               OpAsmParser::Delimiter::Paren) ||
2368       parser.resolveOperands(steps, builder.getIndexType(), result.operands))
2369     return failure();
2370 
2371   // Parse init values.
2372   SmallVector<OpAsmParser::UnresolvedOperand, 4> initVals;
2373   if (succeeded(parser.parseOptionalKeyword("init"))) {
2374     if (parser.parseOperandList(initVals, OpAsmParser::Delimiter::Paren))
2375       return failure();
2376   }
2377 
2378   // Parse optional results in case there is a reduce.
2379   if (parser.parseOptionalArrowTypeList(result.types))
2380     return failure();
2381 
2382   // Now parse the body.
2383   Region *body = result.addRegion();
2384   for (auto &iv : ivs)
2385     iv.type = builder.getIndexType();
2386   if (parser.parseRegion(*body, ivs))
2387     return failure();
2388 
2389   // Set `operand_segment_sizes` attribute.
2390   result.addAttribute(
2391       ParallelOp::getOperandSegmentSizeAttr(),
2392       builder.getI32VectorAttr({static_cast<int32_t>(lower.size()),
2393                                 static_cast<int32_t>(upper.size()),
2394                                 static_cast<int32_t>(steps.size()),
2395                                 static_cast<int32_t>(initVals.size())}));
2396 
2397   // Parse attributes.
2398   if (parser.parseOptionalAttrDict(result.attributes) ||
2399       parser.resolveOperands(initVals, result.types, parser.getNameLoc(),
2400                              result.operands))
2401     return failure();
2402 
2403   // Add a terminator if none was parsed.
2404   ForOp::ensureTerminator(*body, builder, result.location);
2405   return success();
2406 }
2407 
2408 void ParallelOp::print(OpAsmPrinter &p) {
2409   p << " (" << getBody()->getArguments() << ") = (" << getLowerBound()
2410     << ") to (" << getUpperBound() << ") step (" << getStep() << ")";
2411   if (!getInitVals().empty())
2412     p << " init (" << getInitVals() << ")";
2413   p.printOptionalArrowTypeList(getResultTypes());
2414   p << ' ';
2415   p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
2416   p.printOptionalAttrDict(
2417       (*this)->getAttrs(),
2418       /*elidedAttrs=*/ParallelOp::getOperandSegmentSizeAttr());
2419 }
2420 
2421 Region &ParallelOp::getLoopBody() { return getRegion(); }
2422 
2423 ParallelOp mlir::scf::getParallelForInductionVarOwner(Value val) {
2424   auto ivArg = val.dyn_cast<BlockArgument>();
2425   if (!ivArg)
2426     return ParallelOp();
2427   assert(ivArg.getOwner() && "unlinked block argument");
2428   auto *containingOp = ivArg.getOwner()->getParentOp();
2429   return dyn_cast<ParallelOp>(containingOp);
2430 }
2431 
2432 namespace {
2433 // Collapse loop dimensions that perform a single iteration.
2434 struct CollapseSingleIterationLoops : public OpRewritePattern<ParallelOp> {
2435   using OpRewritePattern<ParallelOp>::OpRewritePattern;
2436 
2437   LogicalResult matchAndRewrite(ParallelOp op,
2438                                 PatternRewriter &rewriter) const override {
2439     BlockAndValueMapping mapping;
2440     // Compute new loop bounds that omit all single-iteration loop dimensions.
2441     SmallVector<Value, 2> newLowerBounds;
2442     SmallVector<Value, 2> newUpperBounds;
2443     SmallVector<Value, 2> newSteps;
2444     newLowerBounds.reserve(op.getLowerBound().size());
2445     newUpperBounds.reserve(op.getUpperBound().size());
2446     newSteps.reserve(op.getStep().size());
2447     for (auto dim : llvm::zip(op.getLowerBound(), op.getUpperBound(),
2448                               op.getStep(), op.getInductionVars())) {
2449       Value lowerBound, upperBound, step, iv;
2450       std::tie(lowerBound, upperBound, step, iv) = dim;
2451       // Collect the statically known loop bounds.
2452       auto lowerBoundConstant =
2453           dyn_cast_or_null<arith::ConstantIndexOp>(lowerBound.getDefiningOp());
2454       auto upperBoundConstant =
2455           dyn_cast_or_null<arith::ConstantIndexOp>(upperBound.getDefiningOp());
2456       auto stepConstant =
2457           dyn_cast_or_null<arith::ConstantIndexOp>(step.getDefiningOp());
2458       // Replace the loop induction variable by the lower bound if the loop
2459       // performs a single iteration. Otherwise, copy the loop bounds.
2460       if (lowerBoundConstant && upperBoundConstant && stepConstant &&
2461           (upperBoundConstant.value() - lowerBoundConstant.value()) > 0 &&
2462           (upperBoundConstant.value() - lowerBoundConstant.value()) <=
2463               stepConstant.value()) {
2464         mapping.map(iv, lowerBound);
2465       } else {
2466         newLowerBounds.push_back(lowerBound);
2467         newUpperBounds.push_back(upperBound);
2468         newSteps.push_back(step);
2469       }
2470     }
2471     // Exit if none of the loop dimensions perform a single iteration.
2472     if (newLowerBounds.size() == op.getLowerBound().size())
2473       return failure();
2474 
2475     if (newLowerBounds.empty()) {
2476       // All of the loop dimensions perform a single iteration. Inline
2477       // loop body and nested ReduceOp's
2478       SmallVector<Value> results;
2479       results.reserve(op.getInitVals().size());
2480       for (auto &bodyOp : op.getLoopBody().front().without_terminator()) {
2481         auto reduce = dyn_cast<ReduceOp>(bodyOp);
2482         if (!reduce) {
2483           rewriter.clone(bodyOp, mapping);
2484           continue;
2485         }
2486         Block &reduceBlock = reduce.getReductionOperator().front();
2487         auto initValIndex = results.size();
2488         mapping.map(reduceBlock.getArgument(0), op.getInitVals()[initValIndex]);
2489         mapping.map(reduceBlock.getArgument(1),
2490                     mapping.lookupOrDefault(reduce.getOperand()));
2491         for (auto &reduceBodyOp : reduceBlock.without_terminator())
2492           rewriter.clone(reduceBodyOp, mapping);
2493 
2494         auto result = mapping.lookupOrDefault(
2495             cast<ReduceReturnOp>(reduceBlock.getTerminator()).getResult());
2496         results.push_back(result);
2497       }
2498       rewriter.replaceOp(op, results);
2499       return success();
2500     }
2501     // Replace the parallel loop by lower-dimensional parallel loop.
2502     auto newOp =
2503         rewriter.create<ParallelOp>(op.getLoc(), newLowerBounds, newUpperBounds,
2504                                     newSteps, op.getInitVals(), nullptr);
2505     // Clone the loop body and remap the block arguments of the collapsed loops
2506     // (inlining does not support a cancellable block argument mapping).
2507     rewriter.cloneRegionBefore(op.getRegion(), newOp.getRegion(),
2508                                newOp.getRegion().begin(), mapping);
2509     rewriter.replaceOp(op, newOp.getResults());
2510     return success();
2511   }
2512 };
2513 
2514 /// Removes parallel loops in which at least one lower/upper bound pair consists
2515 /// of the same values - such loops have an empty iteration domain.
2516 struct RemoveEmptyParallelLoops : public OpRewritePattern<ParallelOp> {
2517   using OpRewritePattern<ParallelOp>::OpRewritePattern;
2518 
2519   LogicalResult matchAndRewrite(ParallelOp op,
2520                                 PatternRewriter &rewriter) const override {
2521     for (auto dim : llvm::zip(op.getLowerBound(), op.getUpperBound())) {
2522       if (std::get<0>(dim) == std::get<1>(dim)) {
2523         rewriter.replaceOp(op, op.getInitVals());
2524         return success();
2525       }
2526     }
2527     return failure();
2528   }
2529 };
2530 
2531 struct MergeNestedParallelLoops : public OpRewritePattern<ParallelOp> {
2532   using OpRewritePattern<ParallelOp>::OpRewritePattern;
2533 
2534   LogicalResult matchAndRewrite(ParallelOp op,
2535                                 PatternRewriter &rewriter) const override {
2536     Block &outerBody = op.getLoopBody().front();
2537     if (!llvm::hasSingleElement(outerBody.without_terminator()))
2538       return failure();
2539 
2540     auto innerOp = dyn_cast<ParallelOp>(outerBody.front());
2541     if (!innerOp)
2542       return failure();
2543 
2544     for (auto val : outerBody.getArguments())
2545       if (llvm::is_contained(innerOp.getLowerBound(), val) ||
2546           llvm::is_contained(innerOp.getUpperBound(), val) ||
2547           llvm::is_contained(innerOp.getStep(), val))
2548         return failure();
2549 
2550     // Reductions are not supported yet.
2551     if (!op.getInitVals().empty() || !innerOp.getInitVals().empty())
2552       return failure();
2553 
2554     auto bodyBuilder = [&](OpBuilder &builder, Location /*loc*/,
2555                            ValueRange iterVals, ValueRange) {
2556       Block &innerBody = innerOp.getLoopBody().front();
2557       assert(iterVals.size() ==
2558              (outerBody.getNumArguments() + innerBody.getNumArguments()));
2559       BlockAndValueMapping mapping;
2560       mapping.map(outerBody.getArguments(),
2561                   iterVals.take_front(outerBody.getNumArguments()));
2562       mapping.map(innerBody.getArguments(),
2563                   iterVals.take_back(innerBody.getNumArguments()));
2564       for (Operation &op : innerBody.without_terminator())
2565         builder.clone(op, mapping);
2566     };
2567 
2568     auto concatValues = [](const auto &first, const auto &second) {
2569       SmallVector<Value> ret;
2570       ret.reserve(first.size() + second.size());
2571       ret.assign(first.begin(), first.end());
2572       ret.append(second.begin(), second.end());
2573       return ret;
2574     };
2575 
2576     auto newLowerBounds =
2577         concatValues(op.getLowerBound(), innerOp.getLowerBound());
2578     auto newUpperBounds =
2579         concatValues(op.getUpperBound(), innerOp.getUpperBound());
2580     auto newSteps = concatValues(op.getStep(), innerOp.getStep());
2581 
2582     rewriter.replaceOpWithNewOp<ParallelOp>(op, newLowerBounds, newUpperBounds,
2583                                             newSteps, llvm::None, bodyBuilder);
2584     return success();
2585   }
2586 };
2587 
2588 } // namespace
2589 
2590 void ParallelOp::getCanonicalizationPatterns(RewritePatternSet &results,
2591                                              MLIRContext *context) {
2592   results.add<CollapseSingleIterationLoops, RemoveEmptyParallelLoops,
2593               MergeNestedParallelLoops>(context);
2594 }
2595 
2596 //===----------------------------------------------------------------------===//
2597 // ReduceOp
2598 //===----------------------------------------------------------------------===//
2599 
2600 void ReduceOp::build(
2601     OpBuilder &builder, OperationState &result, Value operand,
2602     function_ref<void(OpBuilder &, Location, Value, Value)> bodyBuilderFn) {
2603   auto type = operand.getType();
2604   result.addOperands(operand);
2605 
2606   OpBuilder::InsertionGuard guard(builder);
2607   Region *bodyRegion = result.addRegion();
2608   Block *body = builder.createBlock(bodyRegion, {}, ArrayRef<Type>{type, type},
2609                                     {result.location, result.location});
2610   if (bodyBuilderFn)
2611     bodyBuilderFn(builder, result.location, body->getArgument(0),
2612                   body->getArgument(1));
2613 }
2614 
2615 LogicalResult ReduceOp::verifyRegions() {
2616   // The region of a ReduceOp has two arguments of the same type as its operand.
2617   auto type = getOperand().getType();
2618   Block &block = getReductionOperator().front();
2619   if (block.empty())
2620     return emitOpError("the block inside reduce should not be empty");
2621   if (block.getNumArguments() != 2 ||
2622       llvm::any_of(block.getArguments(), [&](const BlockArgument &arg) {
2623         return arg.getType() != type;
2624       }))
2625     return emitOpError() << "expects two arguments to reduce block of type "
2626                          << type;
2627 
2628   // Check that the block is terminated by a ReduceReturnOp.
2629   if (!isa<ReduceReturnOp>(block.getTerminator()))
2630     return emitOpError("the block inside reduce should be terminated with a "
2631                        "'scf.reduce.return' op");
2632 
2633   return success();
2634 }
2635 
2636 ParseResult ReduceOp::parse(OpAsmParser &parser, OperationState &result) {
2637   // Parse an opening `(` followed by the reduced value followed by `)`
2638   OpAsmParser::UnresolvedOperand operand;
2639   if (parser.parseLParen() || parser.parseOperand(operand) ||
2640       parser.parseRParen())
2641     return failure();
2642 
2643   Type resultType;
2644   // Parse the type of the operand (and also what reduce computes on).
2645   if (parser.parseColonType(resultType) ||
2646       parser.resolveOperand(operand, resultType, result.operands))
2647     return failure();
2648 
2649   // Now parse the body.
2650   Region *body = result.addRegion();
2651   if (parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{}))
2652     return failure();
2653 
2654   return success();
2655 }
2656 
2657 void ReduceOp::print(OpAsmPrinter &p) {
2658   p << "(" << getOperand() << ") ";
2659   p << " : " << getOperand().getType() << ' ';
2660   p.printRegion(getReductionOperator());
2661 }
2662 
2663 //===----------------------------------------------------------------------===//
2664 // ReduceReturnOp
2665 //===----------------------------------------------------------------------===//
2666 
2667 LogicalResult ReduceReturnOp::verify() {
2668   // The type of the return value should be the same type as the type of the
2669   // operand of the enclosing ReduceOp.
2670   auto reduceOp = cast<ReduceOp>((*this)->getParentOp());
2671   Type reduceType = reduceOp.getOperand().getType();
2672   if (reduceType != getResult().getType())
2673     return emitOpError() << "needs to have type " << reduceType
2674                          << " (the type of the enclosing ReduceOp)";
2675   return success();
2676 }
2677 
2678 //===----------------------------------------------------------------------===//
2679 // WhileOp
2680 //===----------------------------------------------------------------------===//
2681 
2682 OperandRange WhileOp::getSuccessorEntryOperands(Optional<unsigned> index) {
2683   assert(index && *index == 0 &&
2684          "WhileOp is expected to branch only to the first region");
2685 
2686   return getInits();
2687 }
2688 
2689 ConditionOp WhileOp::getConditionOp() {
2690   return cast<ConditionOp>(getBefore().front().getTerminator());
2691 }
2692 
2693 YieldOp WhileOp::getYieldOp() {
2694   return cast<YieldOp>(getAfter().front().getTerminator());
2695 }
2696 
2697 Block::BlockArgListType WhileOp::getBeforeArguments() {
2698   return getBefore().front().getArguments();
2699 }
2700 
2701 Block::BlockArgListType WhileOp::getAfterArguments() {
2702   return getAfter().front().getArguments();
2703 }
2704 
2705 void WhileOp::getSuccessorRegions(Optional<unsigned> index,
2706                                   ArrayRef<Attribute> operands,
2707                                   SmallVectorImpl<RegionSuccessor> &regions) {
2708   // The parent op always branches to the condition region.
2709   if (!index) {
2710     regions.emplace_back(&getBefore(), getBefore().getArguments());
2711     return;
2712   }
2713 
2714   assert(*index < 2 && "there are only two regions in a WhileOp");
2715   // The body region always branches back to the condition region.
2716   if (*index == 1) {
2717     regions.emplace_back(&getBefore(), getBefore().getArguments());
2718     return;
2719   }
2720 
2721   // Try to narrow the successor to the condition region.
2722   assert(!operands.empty() && "expected at least one operand");
2723   auto cond = operands[0].dyn_cast_or_null<BoolAttr>();
2724   if (!cond || !cond.getValue())
2725     regions.emplace_back(getResults());
2726   if (!cond || cond.getValue())
2727     regions.emplace_back(&getAfter(), getAfter().getArguments());
2728 }
2729 
2730 /// Parses a `while` op.
2731 ///
2732 /// op ::= `scf.while` assignments `:` function-type region `do` region
2733 ///         `attributes` attribute-dict
2734 /// initializer ::= /* empty */ | `(` assignment-list `)`
2735 /// assignment-list ::= assignment | assignment `,` assignment-list
2736 /// assignment ::= ssa-value `=` ssa-value
2737 ParseResult scf::WhileOp::parse(OpAsmParser &parser, OperationState &result) {
2738   SmallVector<OpAsmParser::Argument, 4> regionArgs;
2739   SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
2740   Region *before = result.addRegion();
2741   Region *after = result.addRegion();
2742 
2743   OptionalParseResult listResult =
2744       parser.parseOptionalAssignmentList(regionArgs, operands);
2745   if (listResult.hasValue() && failed(listResult.getValue()))
2746     return failure();
2747 
2748   FunctionType functionType;
2749   SMLoc typeLoc = parser.getCurrentLocation();
2750   if (failed(parser.parseColonType(functionType)))
2751     return failure();
2752 
2753   result.addTypes(functionType.getResults());
2754 
2755   if (functionType.getNumInputs() != operands.size()) {
2756     return parser.emitError(typeLoc)
2757            << "expected as many input types as operands "
2758            << "(expected " << operands.size() << " got "
2759            << functionType.getNumInputs() << ")";
2760   }
2761 
2762   // Resolve input operands.
2763   if (failed(parser.resolveOperands(operands, functionType.getInputs(),
2764                                     parser.getCurrentLocation(),
2765                                     result.operands)))
2766     return failure();
2767 
2768   // Propagate the types into the region arguments.
2769   for (size_t i = 0, e = regionArgs.size(); i != e; ++i)
2770     regionArgs[i].type = functionType.getInput(i);
2771 
2772   return failure(parser.parseRegion(*before, regionArgs) ||
2773                  parser.parseKeyword("do") || parser.parseRegion(*after) ||
2774                  parser.parseOptionalAttrDictWithKeyword(result.attributes));
2775 }
2776 
2777 /// Prints a `while` op.
2778 void scf::WhileOp::print(OpAsmPrinter &p) {
2779   printInitializationList(p, getBefore().front().getArguments(), getInits(),
2780                           " ");
2781   p << " : ";
2782   p.printFunctionalType(getInits().getTypes(), getResults().getTypes());
2783   p << ' ';
2784   p.printRegion(getBefore(), /*printEntryBlockArgs=*/false);
2785   p << " do ";
2786   p.printRegion(getAfter());
2787   p.printOptionalAttrDictWithKeyword((*this)->getAttrs());
2788 }
2789 
2790 /// Verifies that two ranges of types match, i.e. have the same number of
2791 /// entries and that types are pairwise equals. Reports errors on the given
2792 /// operation in case of mismatch.
2793 template <typename OpTy>
2794 static LogicalResult verifyTypeRangesMatch(OpTy op, TypeRange left,
2795                                            TypeRange right, StringRef message) {
2796   if (left.size() != right.size())
2797     return op.emitOpError("expects the same number of ") << message;
2798 
2799   for (unsigned i = 0, e = left.size(); i < e; ++i) {
2800     if (left[i] != right[i]) {
2801       InFlightDiagnostic diag = op.emitOpError("expects the same types for ")
2802                                 << message;
2803       diag.attachNote() << "for argument " << i << ", found " << left[i]
2804                         << " and " << right[i];
2805       return diag;
2806     }
2807   }
2808 
2809   return success();
2810 }
2811 
2812 /// Verifies that the first block of the given `region` is terminated by a
2813 /// YieldOp. Reports errors on the given operation if it is not the case.
2814 template <typename TerminatorTy>
2815 static TerminatorTy verifyAndGetTerminator(scf::WhileOp op, Region &region,
2816                                            StringRef errorMessage) {
2817   Operation *terminatorOperation = region.front().getTerminator();
2818   if (auto yield = dyn_cast_or_null<TerminatorTy>(terminatorOperation))
2819     return yield;
2820 
2821   auto diag = op.emitOpError(errorMessage);
2822   if (terminatorOperation)
2823     diag.attachNote(terminatorOperation->getLoc()) << "terminator here";
2824   return nullptr;
2825 }
2826 
2827 LogicalResult scf::WhileOp::verify() {
2828   auto beforeTerminator = verifyAndGetTerminator<scf::ConditionOp>(
2829       *this, getBefore(),
2830       "expects the 'before' region to terminate with 'scf.condition'");
2831   if (!beforeTerminator)
2832     return failure();
2833 
2834   auto afterTerminator = verifyAndGetTerminator<scf::YieldOp>(
2835       *this, getAfter(),
2836       "expects the 'after' region to terminate with 'scf.yield'");
2837   return success(afterTerminator != nullptr);
2838 }
2839 
2840 namespace {
2841 /// Replace uses of the condition within the do block with true, since otherwise
2842 /// the block would not be evaluated.
2843 ///
2844 /// scf.while (..) : (i1, ...) -> ... {
2845 ///  %condition = call @evaluate_condition() : () -> i1
2846 ///  scf.condition(%condition) %condition : i1, ...
2847 /// } do {
2848 /// ^bb0(%arg0: i1, ...):
2849 ///    use(%arg0)
2850 ///    ...
2851 ///
2852 /// becomes
2853 /// scf.while (..) : (i1, ...) -> ... {
2854 ///  %condition = call @evaluate_condition() : () -> i1
2855 ///  scf.condition(%condition) %condition : i1, ...
2856 /// } do {
2857 /// ^bb0(%arg0: i1, ...):
2858 ///    use(%true)
2859 ///    ...
2860 struct WhileConditionTruth : public OpRewritePattern<WhileOp> {
2861   using OpRewritePattern<WhileOp>::OpRewritePattern;
2862 
2863   LogicalResult matchAndRewrite(WhileOp op,
2864                                 PatternRewriter &rewriter) const override {
2865     auto term = op.getConditionOp();
2866 
2867     // These variables serve to prevent creating duplicate constants
2868     // and hold constant true or false values.
2869     Value constantTrue = nullptr;
2870 
2871     bool replaced = false;
2872     for (auto yieldedAndBlockArgs :
2873          llvm::zip(term.getArgs(), op.getAfterArguments())) {
2874       if (std::get<0>(yieldedAndBlockArgs) == term.getCondition()) {
2875         if (!std::get<1>(yieldedAndBlockArgs).use_empty()) {
2876           if (!constantTrue)
2877             constantTrue = rewriter.create<arith::ConstantOp>(
2878                 op.getLoc(), term.getCondition().getType(),
2879                 rewriter.getBoolAttr(true));
2880 
2881           std::get<1>(yieldedAndBlockArgs).replaceAllUsesWith(constantTrue);
2882           replaced = true;
2883         }
2884       }
2885     }
2886     return success(replaced);
2887   }
2888 };
2889 
2890 /// Remove loop invariant arguments from `before` block of scf.while.
2891 /// A before block argument is considered loop invariant if :-
2892 ///   1. i-th yield operand is equal to the i-th while operand.
2893 ///   2. i-th yield operand is k-th after block argument which is (k+1)-th
2894 ///      condition operand AND this (k+1)-th condition operand is equal to i-th
2895 ///      iter argument/while operand.
2896 /// For the arguments which are removed, their uses inside scf.while
2897 /// are replaced with their corresponding initial value.
2898 ///
2899 /// Eg:
2900 ///    INPUT :-
2901 ///    %res = scf.while <...> iter_args(%arg0_before = %a, %arg1_before = %b,
2902 ///                                     ..., %argN_before = %N)
2903 ///           {
2904 ///                ...
2905 ///                scf.condition(%cond) %arg1_before, %arg0_before,
2906 ///                                     %arg2_before, %arg0_before, ...
2907 ///           } do {
2908 ///             ^bb0(%arg1_after, %arg0_after_1, %arg2_after, %arg0_after_2,
2909 ///                  ..., %argK_after):
2910 ///                ...
2911 ///                scf.yield %arg0_after_2, %b, %arg1_after, ..., %argN
2912 ///           }
2913 ///
2914 ///    OUTPUT :-
2915 ///    %res = scf.while <...> iter_args(%arg2_before = %c, ..., %argN_before =
2916 ///                                     %N)
2917 ///           {
2918 ///                ...
2919 ///                scf.condition(%cond) %b, %a, %arg2_before, %a, ...
2920 ///           } do {
2921 ///             ^bb0(%arg1_after, %arg0_after_1, %arg2_after, %arg0_after_2,
2922 ///                  ..., %argK_after):
2923 ///                ...
2924 ///                scf.yield %arg1_after, ..., %argN
2925 ///           }
2926 ///
2927 ///    EXPLANATION:
2928 ///      We iterate over each yield operand.
2929 ///        1. 0-th yield operand %arg0_after_2 is 4-th condition operand
2930 ///           %arg0_before, which in turn is the 0-th iter argument. So we
2931 ///           remove 0-th before block argument and yield operand, and replace
2932 ///           all uses of the 0-th before block argument with its initial value
2933 ///           %a.
2934 ///        2. 1-th yield operand %b is equal to the 1-th iter arg's initial
2935 ///           value. So we remove this operand and the corresponding before
2936 ///           block argument and replace all uses of 1-th before block argument
2937 ///           with %b.
2938 struct RemoveLoopInvariantArgsFromBeforeBlock
2939     : public OpRewritePattern<WhileOp> {
2940   using OpRewritePattern<WhileOp>::OpRewritePattern;
2941 
2942   LogicalResult matchAndRewrite(WhileOp op,
2943                                 PatternRewriter &rewriter) const override {
2944     Block &afterBlock = op.getAfter().front();
2945     Block::BlockArgListType beforeBlockArgs = op.getBeforeArguments();
2946     ConditionOp condOp = op.getConditionOp();
2947     OperandRange condOpArgs = condOp.getArgs();
2948     Operation *yieldOp = afterBlock.getTerminator();
2949     ValueRange yieldOpArgs = yieldOp->getOperands();
2950 
2951     bool canSimplify = false;
2952     for (const auto &it :
2953          llvm::enumerate(llvm::zip(op.getOperands(), yieldOpArgs))) {
2954       auto index = static_cast<unsigned>(it.index());
2955       Value initVal, yieldOpArg;
2956       std::tie(initVal, yieldOpArg) = it.value();
2957       // If i-th yield operand is equal to the i-th operand of the scf.while,
2958       // the i-th before block argument is a loop invariant.
2959       if (yieldOpArg == initVal) {
2960         canSimplify = true;
2961         break;
2962       }
2963       // If the i-th yield operand is k-th after block argument, then we check
2964       // if the (k+1)-th condition op operand is equal to either the i-th before
2965       // block argument or the initial value of i-th before block argument. If
2966       // the comparison results `true`, i-th before block argument is a loop
2967       // invariant.
2968       auto yieldOpBlockArg = yieldOpArg.dyn_cast<BlockArgument>();
2969       if (yieldOpBlockArg && yieldOpBlockArg.getOwner() == &afterBlock) {
2970         Value condOpArg = condOpArgs[yieldOpBlockArg.getArgNumber()];
2971         if (condOpArg == beforeBlockArgs[index] || condOpArg == initVal) {
2972           canSimplify = true;
2973           break;
2974         }
2975       }
2976     }
2977 
2978     if (!canSimplify)
2979       return failure();
2980 
2981     SmallVector<Value> newInitArgs, newYieldOpArgs;
2982     DenseMap<unsigned, Value> beforeBlockInitValMap;
2983     SmallVector<Location> newBeforeBlockArgLocs;
2984     for (const auto &it :
2985          llvm::enumerate(llvm::zip(op.getOperands(), yieldOpArgs))) {
2986       auto index = static_cast<unsigned>(it.index());
2987       Value initVal, yieldOpArg;
2988       std::tie(initVal, yieldOpArg) = it.value();
2989 
2990       // If i-th yield operand is equal to the i-th operand of the scf.while,
2991       // the i-th before block argument is a loop invariant.
2992       if (yieldOpArg == initVal) {
2993         beforeBlockInitValMap.insert({index, initVal});
2994         continue;
2995       } else {
2996         // If the i-th yield operand is k-th after block argument, then we check
2997         // if the (k+1)-th condition op operand is equal to either the i-th
2998         // before block argument or the initial value of i-th before block
2999         // argument. If the comparison results `true`, i-th before block
3000         // argument is a loop invariant.
3001         auto yieldOpBlockArg = yieldOpArg.dyn_cast<BlockArgument>();
3002         if (yieldOpBlockArg && yieldOpBlockArg.getOwner() == &afterBlock) {
3003           Value condOpArg = condOpArgs[yieldOpBlockArg.getArgNumber()];
3004           if (condOpArg == beforeBlockArgs[index] || condOpArg == initVal) {
3005             beforeBlockInitValMap.insert({index, initVal});
3006             continue;
3007           }
3008         }
3009       }
3010       newInitArgs.emplace_back(initVal);
3011       newYieldOpArgs.emplace_back(yieldOpArg);
3012       newBeforeBlockArgLocs.emplace_back(beforeBlockArgs[index].getLoc());
3013     }
3014 
3015     {
3016       OpBuilder::InsertionGuard g(rewriter);
3017       rewriter.setInsertionPoint(yieldOp);
3018       rewriter.replaceOpWithNewOp<YieldOp>(yieldOp, newYieldOpArgs);
3019     }
3020 
3021     auto newWhile =
3022         rewriter.create<WhileOp>(op.getLoc(), op.getResultTypes(), newInitArgs);
3023 
3024     Block &newBeforeBlock = *rewriter.createBlock(
3025         &newWhile.getBefore(), /*insertPt*/ {},
3026         ValueRange(newYieldOpArgs).getTypes(), newBeforeBlockArgLocs);
3027 
3028     Block &beforeBlock = op.getBefore().front();
3029     SmallVector<Value> newBeforeBlockArgs(beforeBlock.getNumArguments());
3030     // For each i-th before block argument we find it's replacement value as :-
3031     //   1. If i-th before block argument is a loop invariant, we fetch it's
3032     //      initial value from `beforeBlockInitValMap` by querying for key `i`.
3033     //   2. Else we fetch j-th new before block argument as the replacement
3034     //      value of i-th before block argument.
3035     for (unsigned i = 0, j = 0, n = beforeBlock.getNumArguments(); i < n; i++) {
3036       // If the index 'i' argument was a loop invariant we fetch it's initial
3037       // value from `beforeBlockInitValMap`.
3038       if (beforeBlockInitValMap.count(i) != 0)
3039         newBeforeBlockArgs[i] = beforeBlockInitValMap[i];
3040       else
3041         newBeforeBlockArgs[i] = newBeforeBlock.getArgument(j++);
3042     }
3043 
3044     rewriter.mergeBlocks(&beforeBlock, &newBeforeBlock, newBeforeBlockArgs);
3045     rewriter.inlineRegionBefore(op.getAfter(), newWhile.getAfter(),
3046                                 newWhile.getAfter().begin());
3047 
3048     rewriter.replaceOp(op, newWhile.getResults());
3049     return success();
3050   }
3051 };
3052 
3053 /// Remove loop invariant value from result (condition op) of scf.while.
3054 /// A value is considered loop invariant if the final value yielded by
3055 /// scf.condition is defined outside of the `before` block. We remove the
3056 /// corresponding argument in `after` block and replace the use with the value.
3057 /// We also replace the use of the corresponding result of scf.while with the
3058 /// value.
3059 ///
3060 /// Eg:
3061 ///    INPUT :-
3062 ///    %res_input:K = scf.while <...> iter_args(%arg0_before = , ...,
3063 ///                                             %argN_before = %N) {
3064 ///                ...
3065 ///                scf.condition(%cond) %arg0_before, %a, %b, %arg1_before, ...
3066 ///           } do {
3067 ///             ^bb0(%arg0_after, %arg1_after, %arg2_after, ..., %argK_after):
3068 ///                ...
3069 ///                some_func(%arg1_after)
3070 ///                ...
3071 ///                scf.yield %arg0_after, %arg2_after, ..., %argN_after
3072 ///           }
3073 ///
3074 ///    OUTPUT :-
3075 ///    %res_output:M = scf.while <...> iter_args(%arg0 = , ..., %argN = %N) {
3076 ///                ...
3077 ///                scf.condition(%cond) %arg0, %arg1, ..., %argM
3078 ///           } do {
3079 ///             ^bb0(%arg0, %arg3, ..., %argM):
3080 ///                ...
3081 ///                some_func(%a)
3082 ///                ...
3083 ///                scf.yield %arg0, %b, ..., %argN
3084 ///           }
3085 ///
3086 ///     EXPLANATION:
3087 ///       1. The 1-th and 2-th operand of scf.condition are defined outside the
3088 ///          before block of scf.while, so they get removed.
3089 ///       2. %res_input#1's uses are replaced by %a and %res_input#2's uses are
3090 ///          replaced by %b.
3091 ///       3. The corresponding after block argument %arg1_after's uses are
3092 ///          replaced by %a and %arg2_after's uses are replaced by %b.
3093 struct RemoveLoopInvariantValueYielded : public OpRewritePattern<WhileOp> {
3094   using OpRewritePattern<WhileOp>::OpRewritePattern;
3095 
3096   LogicalResult matchAndRewrite(WhileOp op,
3097                                 PatternRewriter &rewriter) const override {
3098     Block &beforeBlock = op.getBefore().front();
3099     ConditionOp condOp = op.getConditionOp();
3100     OperandRange condOpArgs = condOp.getArgs();
3101 
3102     bool canSimplify = false;
3103     for (Value condOpArg : condOpArgs) {
3104       // Those values not defined within `before` block will be considered as
3105       // loop invariant values. We map the corresponding `index` with their
3106       // value.
3107       if (condOpArg.getParentBlock() != &beforeBlock) {
3108         canSimplify = true;
3109         break;
3110       }
3111     }
3112 
3113     if (!canSimplify)
3114       return failure();
3115 
3116     Block::BlockArgListType afterBlockArgs = op.getAfterArguments();
3117 
3118     SmallVector<Value> newCondOpArgs;
3119     SmallVector<Type> newAfterBlockType;
3120     DenseMap<unsigned, Value> condOpInitValMap;
3121     SmallVector<Location> newAfterBlockArgLocs;
3122     for (const auto &it : llvm::enumerate(condOpArgs)) {
3123       auto index = static_cast<unsigned>(it.index());
3124       Value condOpArg = it.value();
3125       // Those values not defined within `before` block will be considered as
3126       // loop invariant values. We map the corresponding `index` with their
3127       // value.
3128       if (condOpArg.getParentBlock() != &beforeBlock) {
3129         condOpInitValMap.insert({index, condOpArg});
3130       } else {
3131         newCondOpArgs.emplace_back(condOpArg);
3132         newAfterBlockType.emplace_back(condOpArg.getType());
3133         newAfterBlockArgLocs.emplace_back(afterBlockArgs[index].getLoc());
3134       }
3135     }
3136 
3137     {
3138       OpBuilder::InsertionGuard g(rewriter);
3139       rewriter.setInsertionPoint(condOp);
3140       rewriter.replaceOpWithNewOp<ConditionOp>(condOp, condOp.getCondition(),
3141                                                newCondOpArgs);
3142     }
3143 
3144     auto newWhile = rewriter.create<WhileOp>(op.getLoc(), newAfterBlockType,
3145                                              op.getOperands());
3146 
3147     Block &newAfterBlock =
3148         *rewriter.createBlock(&newWhile.getAfter(), /*insertPt*/ {},
3149                               newAfterBlockType, newAfterBlockArgLocs);
3150 
3151     Block &afterBlock = op.getAfter().front();
3152     // Since a new scf.condition op was created, we need to fetch the new
3153     // `after` block arguments which will be used while replacing operations of
3154     // previous scf.while's `after` blocks. We'd also be fetching new result
3155     // values too.
3156     SmallVector<Value> newAfterBlockArgs(afterBlock.getNumArguments());
3157     SmallVector<Value> newWhileResults(afterBlock.getNumArguments());
3158     for (unsigned i = 0, j = 0, n = afterBlock.getNumArguments(); i < n; i++) {
3159       Value afterBlockArg, result;
3160       // If index 'i' argument was loop invariant we fetch it's value from the
3161       // `condOpInitMap` map.
3162       if (condOpInitValMap.count(i) != 0) {
3163         afterBlockArg = condOpInitValMap[i];
3164         result = afterBlockArg;
3165       } else {
3166         afterBlockArg = newAfterBlock.getArgument(j);
3167         result = newWhile.getResult(j);
3168         j++;
3169       }
3170       newAfterBlockArgs[i] = afterBlockArg;
3171       newWhileResults[i] = result;
3172     }
3173 
3174     rewriter.mergeBlocks(&afterBlock, &newAfterBlock, newAfterBlockArgs);
3175     rewriter.inlineRegionBefore(op.getBefore(), newWhile.getBefore(),
3176                                 newWhile.getBefore().begin());
3177 
3178     rewriter.replaceOp(op, newWhileResults);
3179     return success();
3180   }
3181 };
3182 
3183 /// Remove WhileOp results that are also unused in 'after' block.
3184 ///
3185 ///  %0:2 = scf.while () : () -> (i32, i64) {
3186 ///    %condition = "test.condition"() : () -> i1
3187 ///    %v1 = "test.get_some_value"() : () -> i32
3188 ///    %v2 = "test.get_some_value"() : () -> i64
3189 ///    scf.condition(%condition) %v1, %v2 : i32, i64
3190 ///  } do {
3191 ///  ^bb0(%arg0: i32, %arg1: i64):
3192 ///    "test.use"(%arg0) : (i32) -> ()
3193 ///    scf.yield
3194 ///  }
3195 ///  return %0#0 : i32
3196 ///
3197 /// becomes
3198 ///  %0 = scf.while () : () -> (i32) {
3199 ///    %condition = "test.condition"() : () -> i1
3200 ///    %v1 = "test.get_some_value"() : () -> i32
3201 ///    %v2 = "test.get_some_value"() : () -> i64
3202 ///    scf.condition(%condition) %v1 : i32
3203 ///  } do {
3204 ///  ^bb0(%arg0: i32):
3205 ///    "test.use"(%arg0) : (i32) -> ()
3206 ///    scf.yield
3207 ///  }
3208 ///  return %0 : i32
3209 struct WhileUnusedResult : public OpRewritePattern<WhileOp> {
3210   using OpRewritePattern<WhileOp>::OpRewritePattern;
3211 
3212   LogicalResult matchAndRewrite(WhileOp op,
3213                                 PatternRewriter &rewriter) const override {
3214     auto term = op.getConditionOp();
3215     auto afterArgs = op.getAfterArguments();
3216     auto termArgs = term.getArgs();
3217 
3218     // Collect results mapping, new terminator args and new result types.
3219     SmallVector<unsigned> newResultsIndices;
3220     SmallVector<Type> newResultTypes;
3221     SmallVector<Value> newTermArgs;
3222     SmallVector<Location> newArgLocs;
3223     bool needUpdate = false;
3224     for (const auto &it :
3225          llvm::enumerate(llvm::zip(op.getResults(), afterArgs, termArgs))) {
3226       auto i = static_cast<unsigned>(it.index());
3227       Value result = std::get<0>(it.value());
3228       Value afterArg = std::get<1>(it.value());
3229       Value termArg = std::get<2>(it.value());
3230       if (result.use_empty() && afterArg.use_empty()) {
3231         needUpdate = true;
3232       } else {
3233         newResultsIndices.emplace_back(i);
3234         newTermArgs.emplace_back(termArg);
3235         newResultTypes.emplace_back(result.getType());
3236         newArgLocs.emplace_back(result.getLoc());
3237       }
3238     }
3239 
3240     if (!needUpdate)
3241       return failure();
3242 
3243     {
3244       OpBuilder::InsertionGuard g(rewriter);
3245       rewriter.setInsertionPoint(term);
3246       rewriter.replaceOpWithNewOp<ConditionOp>(term, term.getCondition(),
3247                                                newTermArgs);
3248     }
3249 
3250     auto newWhile =
3251         rewriter.create<WhileOp>(op.getLoc(), newResultTypes, op.getInits());
3252 
3253     Block &newAfterBlock = *rewriter.createBlock(
3254         &newWhile.getAfter(), /*insertPt*/ {}, newResultTypes, newArgLocs);
3255 
3256     // Build new results list and new after block args (unused entries will be
3257     // null).
3258     SmallVector<Value> newResults(op.getNumResults());
3259     SmallVector<Value> newAfterBlockArgs(op.getNumResults());
3260     for (const auto &it : llvm::enumerate(newResultsIndices)) {
3261       newResults[it.value()] = newWhile.getResult(it.index());
3262       newAfterBlockArgs[it.value()] = newAfterBlock.getArgument(it.index());
3263     }
3264 
3265     rewriter.inlineRegionBefore(op.getBefore(), newWhile.getBefore(),
3266                                 newWhile.getBefore().begin());
3267 
3268     Block &afterBlock = op.getAfter().front();
3269     rewriter.mergeBlocks(&afterBlock, &newAfterBlock, newAfterBlockArgs);
3270 
3271     rewriter.replaceOp(op, newResults);
3272     return success();
3273   }
3274 };
3275 
3276 /// Replace operations equivalent to the condition in the do block with true,
3277 /// since otherwise the block would not be evaluated.
3278 ///
3279 /// scf.while (..) : (i32, ...) -> ... {
3280 ///  %z = ... : i32
3281 ///  %condition = cmpi pred %z, %a
3282 ///  scf.condition(%condition) %z : i32, ...
3283 /// } do {
3284 /// ^bb0(%arg0: i32, ...):
3285 ///    %condition2 = cmpi pred %arg0, %a
3286 ///    use(%condition2)
3287 ///    ...
3288 ///
3289 /// becomes
3290 /// scf.while (..) : (i32, ...) -> ... {
3291 ///  %z = ... : i32
3292 ///  %condition = cmpi pred %z, %a
3293 ///  scf.condition(%condition) %z : i32, ...
3294 /// } do {
3295 /// ^bb0(%arg0: i32, ...):
3296 ///    use(%true)
3297 ///    ...
3298 struct WhileCmpCond : public OpRewritePattern<scf::WhileOp> {
3299   using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3300 
3301   LogicalResult matchAndRewrite(scf::WhileOp op,
3302                                 PatternRewriter &rewriter) const override {
3303     using namespace scf;
3304     auto cond = op.getConditionOp();
3305     auto cmp = cond.getCondition().getDefiningOp<arith::CmpIOp>();
3306     if (!cmp)
3307       return failure();
3308     bool changed = false;
3309     for (auto tup :
3310          llvm::zip(cond.getArgs(), op.getAfter().front().getArguments())) {
3311       for (size_t opIdx = 0; opIdx < 2; opIdx++) {
3312         if (std::get<0>(tup) != cmp.getOperand(opIdx))
3313           continue;
3314         for (OpOperand &u :
3315              llvm::make_early_inc_range(std::get<1>(tup).getUses())) {
3316           auto cmp2 = dyn_cast<arith::CmpIOp>(u.getOwner());
3317           if (!cmp2)
3318             continue;
3319           // For a binary operator 1-opIdx gets the other side.
3320           if (cmp2.getOperand(1 - opIdx) != cmp.getOperand(1 - opIdx))
3321             continue;
3322           bool samePredicate;
3323           if (cmp2.getPredicate() == cmp.getPredicate())
3324             samePredicate = true;
3325           else if (cmp2.getPredicate() ==
3326                    arith::invertPredicate(cmp.getPredicate()))
3327             samePredicate = false;
3328           else
3329             continue;
3330 
3331           rewriter.replaceOpWithNewOp<arith::ConstantIntOp>(cmp2, samePredicate,
3332                                                             1);
3333           changed = true;
3334         }
3335       }
3336     }
3337     return success(changed);
3338   }
3339 };
3340 
3341 struct WhileUnusedArg : public OpRewritePattern<WhileOp> {
3342   using OpRewritePattern<WhileOp>::OpRewritePattern;
3343 
3344   LogicalResult matchAndRewrite(WhileOp op,
3345                                 PatternRewriter &rewriter) const override {
3346 
3347     if (!llvm::any_of(op.getBeforeArguments(),
3348                       [](Value arg) { return arg.use_empty(); }))
3349       return failure();
3350 
3351     YieldOp yield = op.getYieldOp();
3352 
3353     // Collect results mapping, new terminator args and new result types.
3354     SmallVector<Value> newYields;
3355     SmallVector<Value> newInits;
3356     SmallVector<unsigned> argsToErase;
3357     for (const auto &it : llvm::enumerate(llvm::zip(
3358              op.getBeforeArguments(), yield.getOperands(), op.getInits()))) {
3359       Value beforeArg = std::get<0>(it.value());
3360       Value yieldValue = std::get<1>(it.value());
3361       Value initValue = std::get<2>(it.value());
3362       if (beforeArg.use_empty()) {
3363         argsToErase.push_back(it.index());
3364       } else {
3365         newYields.emplace_back(yieldValue);
3366         newInits.emplace_back(initValue);
3367       }
3368     }
3369 
3370     if (argsToErase.empty())
3371       return failure();
3372 
3373     rewriter.startRootUpdate(op);
3374     op.getBefore().front().eraseArguments(argsToErase);
3375     rewriter.finalizeRootUpdate(op);
3376 
3377     WhileOp replacement =
3378         rewriter.create<WhileOp>(op.getLoc(), op.getResultTypes(), newInits);
3379     replacement.getBefore().takeBody(op.getBefore());
3380     replacement.getAfter().takeBody(op.getAfter());
3381     rewriter.replaceOp(op, replacement.getResults());
3382 
3383     rewriter.setInsertionPoint(yield);
3384     rewriter.replaceOpWithNewOp<YieldOp>(yield, newYields);
3385     return success();
3386   }
3387 };
3388 } // namespace
3389 
3390 void WhileOp::getCanonicalizationPatterns(RewritePatternSet &results,
3391                                           MLIRContext *context) {
3392   results.add<RemoveLoopInvariantArgsFromBeforeBlock,
3393               RemoveLoopInvariantValueYielded, WhileConditionTruth,
3394               WhileCmpCond, WhileUnusedResult>(context);
3395 }
3396 
3397 //===----------------------------------------------------------------------===//
3398 // TableGen'd op method definitions
3399 //===----------------------------------------------------------------------===//
3400 
3401 #define GET_OP_CLASSES
3402 #include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
3403