MLIR 24.0.0git
SCF.cpp
Go to the documentation of this file.
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
21#include "mlir/IR/IRMapping.h"
22#include "mlir/IR/Matchers.h"
23#include "mlir/IR/Operation.h"
31#include "llvm/ADT/MapVector.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/ADT/SmallPtrSet.h"
34#include "llvm/Support/Casting.h"
35#include "llvm/Support/DebugLog.h"
36#include <optional>
37
38using namespace mlir;
39using namespace mlir::scf;
40
41#include "mlir/Dialect/SCF/IR/SCFOpsDialect.cpp.inc"
42
43//===----------------------------------------------------------------------===//
44// SCFDialect Dialect Interfaces
45//===----------------------------------------------------------------------===//
46
47namespace {
48struct SCFInlinerInterface : public DialectInlinerInterface {
49 using DialectInlinerInterface::DialectInlinerInterface;
50 // We don't have any special restrictions on what can be inlined into
51 // destination regions (e.g. while/conditional bodies). Always allow it.
52 bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
53 IRMapping &valueMapping) const final {
54 return true;
55 }
56 // Operations in scf dialect are always legal to inline since they are
57 // pure.
58 bool isLegalToInline(Operation *, Region *, bool, IRMapping &) const final {
59 return true;
60 }
61 // Handle the given inlined terminator by replacing it with a new operation
62 // as necessary. Required when the region has only one block.
63 void handleTerminator(Operation *op, ValueRange valuesToRepl) const final {
64 auto retValOp = dyn_cast<scf::YieldOp>(op);
65 if (!retValOp)
66 return;
67
68 for (auto retValue : llvm::zip(valuesToRepl, retValOp.getOperands())) {
69 std::get<0>(retValue).replaceAllUsesWith(std::get<1>(retValue));
70 }
71 }
72};
73} // namespace
74
75//===----------------------------------------------------------------------===//
76// SCFDialect
77//===----------------------------------------------------------------------===//
78
79void SCFDialect::initialize() {
80 addOperations<
81#define GET_OP_LIST
82#include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
83 >();
84 addInterfaces<SCFInlinerInterface>();
85 declarePromisedInterface<ConvertToEmitCPatternInterface, SCFDialect>();
86 declarePromisedInterfaces<bufferization::BufferDeallocationOpInterface,
87 InParallelOp, ReduceReturnOp>();
88 declarePromisedInterfaces<bufferization::BufferizableOpInterface, ConditionOp,
89 ExecuteRegionOp, ForOp, IfOp, IndexSwitchOp,
90 ForallOp, InParallelOp, WhileOp, YieldOp>();
91 declarePromisedInterface<ValueBoundsOpInterface, ForOp>();
92}
93
94/// Default callback for IfOp builders. Inserts a yield without arguments.
96 scf::YieldOp::create(builder, loc);
97}
98
99/// Verifies that the first block of the given `region` is terminated by a
100/// TerminatorTy. Reports errors on the given operation if it is not the case.
101template <typename TerminatorTy>
102static TerminatorTy verifyAndGetTerminator(Operation *op, Region &region,
103 StringRef errorMessage) {
104 Operation *terminatorOperation = nullptr;
105 if (!region.empty() && !region.front().empty()) {
106 terminatorOperation = &region.front().back();
107 if (auto yield = dyn_cast_or_null<TerminatorTy>(terminatorOperation))
108 return yield;
109 }
110 auto diag = op->emitOpError(errorMessage);
111 if (terminatorOperation)
112 diag.attachNote(terminatorOperation->getLoc()) << "terminator here";
113 return nullptr;
114}
115
116std::optional<llvm::APSInt> mlir::scf::computeUbMinusLb(Value lb, Value ub,
117 bool isSigned) {
118 llvm::APSInt diff;
119 auto addOp = ub.getDefiningOp<arith::AddIOp>();
120 if (!addOp)
121 return std::nullopt;
122 if ((isSigned && !addOp.hasNoSignedWrap()) ||
123 (!isSigned && !addOp.hasNoUnsignedWrap()))
124 return std::nullopt;
125
126 if (addOp.getLhs() != lb ||
127 !matchPattern(addOp.getRhs(), m_ConstantInt(&diff)))
128 return std::nullopt;
129 return diff;
130}
131
132//===----------------------------------------------------------------------===//
133// ExecuteRegionOp
134//===----------------------------------------------------------------------===//
135
136///
137/// (ssa-id `=`)? `execute_region` `->` function-result-type `{`
138/// block+
139/// `}`
140///
141/// Example:
142/// scf.execute_region -> i32 {
143/// %idx = load %rI[%i] : memref<128xi32>
144/// return %idx : i32
145/// }
146///
147ParseResult ExecuteRegionOp::parse(OpAsmParser &parser,
149 if (parser.parseOptionalArrowTypeList(result.types))
150 return failure();
151
152 if (succeeded(parser.parseOptionalKeyword("no_inline")))
153 result.addAttribute("no_inline", parser.getBuilder().getUnitAttr());
154
155 // Introduce the body region and parse it.
156 Region *body = result.addRegion();
157 if (parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{}) ||
158 parser.parseOptionalAttrDict(result.attributes))
159 return failure();
160
161 return success();
162}
163
164void ExecuteRegionOp::print(OpAsmPrinter &p) {
165 p.printOptionalArrowTypeList(getResultTypes());
166 p << ' ';
167 if (getNoInline())
168 p << "no_inline ";
169 p.printRegion(getRegion(),
170 /*printEntryBlockArgs=*/false,
171 /*printBlockTerminators=*/true);
172 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
173}
174
175LogicalResult ExecuteRegionOp::verify() {
176 if (getRegion().empty())
177 return emitOpError("region needs to have at least one block");
178 if (getRegion().front().getNumArguments() > 0)
179 return emitOpError("region cannot have any arguments");
180 return success();
181}
182
183// Inline an ExecuteRegionOp if its parent can contain multiple blocks.
184// TODO generalize the conditions for operations which can be inlined into.
185// func @func_execute_region_elim() {
186// "test.foo"() : () -> ()
187// %v = scf.execute_region -> i64 {
188// %c = "test.cmp"() : () -> i1
189// cf.cond_br %c, ^bb2, ^bb3
190// ^bb2:
191// %x = "test.val1"() : () -> i64
192// cf.br ^bb4(%x : i64)
193// ^bb3:
194// %y = "test.val2"() : () -> i64
195// cf.br ^bb4(%y : i64)
196// ^bb4(%z : i64):
197// scf.yield %z : i64
198// }
199// "test.bar"(%v) : (i64) -> ()
200// return
201// }
202//
203// becomes
204//
205// func @func_execute_region_elim() {
206// "test.foo"() : () -> ()
207// %c = "test.cmp"() : () -> i1
208// cf.cond_br %c, ^bb1, ^bb2
209// ^bb1: // pred: ^bb0
210// %x = "test.val1"() : () -> i64
211// cf.br ^bb3(%x : i64)
212// ^bb2: // pred: ^bb0
213// %y = "test.val2"() : () -> i64
214// cf.br ^bb3(%y : i64)
215// ^bb3(%z: i64): // 2 preds: ^bb1, ^bb2
216// "test.bar"(%z) : (i64) -> ()
217// return
218// }
219//
220struct MultiBlockExecuteInliner : public OpRewritePattern<ExecuteRegionOp> {
221 using OpRewritePattern<ExecuteRegionOp>::OpRewritePattern;
222
223 LogicalResult matchAndRewrite(ExecuteRegionOp op,
224 PatternRewriter &rewriter) const override {
225 if (op.getNoInline())
226 return failure();
227 if (!isa<FunctionOpInterface, ExecuteRegionOp>(op->getParentOp()))
228 return failure();
229
230 Block *prevBlock = op->getBlock();
231 Block *postBlock = rewriter.splitBlock(prevBlock, op->getIterator());
232 rewriter.setInsertionPointToEnd(prevBlock);
233
234 cf::BranchOp::create(rewriter, op.getLoc(), &op.getRegion().front());
235
236 for (Block &blk : op.getRegion()) {
237 if (YieldOp yieldOp = dyn_cast<YieldOp>(blk.getTerminator())) {
238 rewriter.setInsertionPoint(yieldOp);
239 cf::BranchOp::create(rewriter, yieldOp.getLoc(), postBlock,
240 yieldOp.getResults());
241 rewriter.eraseOp(yieldOp);
242 }
243 }
244
245 rewriter.inlineRegionBefore(op.getRegion(), postBlock);
246 SmallVector<Value> blockArgs;
247
248 for (auto res : op.getResults())
249 blockArgs.push_back(postBlock->addArgument(res.getType(), res.getLoc()));
250
251 rewriter.replaceOp(op, blockArgs);
252 return success();
253 }
254};
255
256void ExecuteRegionOp::getCanonicalizationPatterns(RewritePatternSet &results,
257 MLIRContext *context) {
258 results.add<MultiBlockExecuteInliner>(context);
260 results, ExecuteRegionOp::getOperationName());
261 // Inline ops with a single block that are not marked as "no_inline".
263 results, ExecuteRegionOp::getOperationName(),
265 return failure(cast<ExecuteRegionOp>(op).getNoInline());
266 });
267}
268
269void ExecuteRegionOp::getSuccessorRegions(
271 // If the predecessor is the ExecuteRegionOp, branch into the body.
272 if (point.isParent()) {
273 regions.push_back(RegionSuccessor(&getRegion()));
274 return;
275 }
276
277 // Otherwise, the region branches back to the parent operation.
278 regions.push_back(RegionSuccessor(getOperation()));
279}
280
281void ExecuteRegionOp::getRegionInvocationBounds(
283 bounds.emplace_back(/*lb=*/1, /*ub=*/1);
284}
285
286ValueRange ExecuteRegionOp::getSuccessorInputs(RegionSuccessor successor) {
287 return successor.isOperation() ? ValueRange(getOperation()->getResults())
288 : ValueRange();
289}
290
291//===----------------------------------------------------------------------===//
292// ConditionOp
293//===----------------------------------------------------------------------===//
294
296ConditionOp::getMutableSuccessorOperands(RegionSuccessor point) {
297 assert((point.isOperation() ||
298 point.getSuccessor() == &getParentOp().getAfter()) &&
299 "condition op can only exit the loop or branch to the after"
300 "region");
301 // Pass all operands except the condition to the successor region.
302 return getArgsMutable();
303}
304
305void ConditionOp::getSuccessorRegions(
307 FoldAdaptor adaptor(operands, *this);
308
309 WhileOp whileOp = getParentOp();
310
311 // Condition can either lead to the after region or back to the parent op
312 // depending on whether the condition is true or not.
313 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
314 if (!boolAttr || boolAttr.getValue())
315 regions.emplace_back(&whileOp.getAfter());
316 if (!boolAttr || !boolAttr.getValue())
317 regions.push_back(RegionSuccessor(whileOp.getOperation()));
318}
319
320//===----------------------------------------------------------------------===//
321// ForOp
322//===----------------------------------------------------------------------===//
323
324void ForOp::build(OpBuilder &builder, OperationState &result, Value lb,
325 Value ub, Value step, ValueRange initArgs,
326 BodyBuilderFn bodyBuilder, bool unsignedCmp) {
327 OpBuilder::InsertionGuard guard(builder);
328
329 if (unsignedCmp)
330 result.addAttribute(getUnsignedCmpAttrName(result.name),
331 builder.getUnitAttr());
332 result.addOperands({lb, ub, step});
333 result.addOperands(initArgs);
334 for (Value v : initArgs)
335 result.addTypes(v.getType());
336 Type t = lb.getType();
337 Region *bodyRegion = result.addRegion();
338 Block *bodyBlock = builder.createBlock(bodyRegion);
339 bodyBlock->addArgument(t, result.location);
340 for (Value v : initArgs)
341 bodyBlock->addArgument(v.getType(), v.getLoc());
342
343 // Create the default terminator if the builder is not provided and if the
344 // iteration arguments are not provided. Otherwise, leave this to the caller
345 // because we don't know which values to return from the loop.
346 if (initArgs.empty() && !bodyBuilder) {
347 ForOp::ensureTerminator(*bodyRegion, builder, result.location);
348 } else if (bodyBuilder) {
349 OpBuilder::InsertionGuard guard(builder);
350 builder.setInsertionPointToStart(bodyBlock);
351 bodyBuilder(builder, result.location, bodyBlock->getArgument(0),
352 bodyBlock->getArguments().drop_front());
353 }
354}
355
356LogicalResult ForOp::verify() {
357 // Check that the body block has at least the induction variable argument.
358 // This must be checked before verifyRegions() and before any region trait
359 // verifiers (e.g. LoopLikeOpInterface) that call getRegionIterArgs(), to
360 // avoid crashing with an out-of-bounds drop_front on an empty arg list.
361 if (getBody()->getNumArguments() < getNumInductionVars())
362 return emitOpError("expected body to have at least ")
363 << getNumInductionVars()
364 << " argument(s) for the induction variable, but got "
365 << getBody()->getNumArguments();
366
367 // Check that the number of init args and op results is the same.
368 if (getInitArgs().size() != getNumResults())
369 return emitOpError(
370 "mismatch in number of loop-carried values and defined values");
371
372 return success();
373}
374
375LogicalResult ForOp::verifyRegions() {
376 // Check that the body block has at least the induction variable argument.
377 if (getBody()->getNumArguments() < getNumInductionVars())
378 return emitOpError("expected body to have at least ")
379 << getNumInductionVars() << " argument(s) for the induction "
380 << "variable, but got " << getBody()->getNumArguments();
381
382 // Check that the body defines as single block argument for the induction
383 // variable.
384 if (getInductionVar().getType() != getLowerBound().getType())
385 return emitOpError(
386 "expected induction variable to be same type as bounds and step");
387
388 if (getNumRegionIterArgs() != getNumResults())
389 return emitOpError(
390 "mismatch in number of basic block args and defined values");
391
392 auto initArgs = getInitArgs();
393 auto iterArgs = getRegionIterArgs();
394 auto opResults = getResults();
395 unsigned i = 0;
396 for (auto e : llvm::zip(initArgs, iterArgs, opResults)) {
397 if (std::get<0>(e).getType() != std::get<2>(e).getType())
398 return emitOpError() << "types mismatch between " << i
399 << "th iter operand and defined value";
400 if (std::get<1>(e).getType() != std::get<2>(e).getType())
401 return emitOpError() << "types mismatch between " << i
402 << "th iter region arg and defined value";
403
404 ++i;
405 }
406 return success();
407}
408
409std::optional<SmallVector<Value>> ForOp::getLoopInductionVars() {
410 return SmallVector<Value>{getInductionVar()};
411}
412
413std::optional<SmallVector<OpFoldResult>> ForOp::getLoopLowerBounds() {
415}
416
417std::optional<SmallVector<OpFoldResult>> ForOp::getLoopSteps() {
418 return SmallVector<OpFoldResult>{OpFoldResult(getStep())};
419}
420
421std::optional<SmallVector<OpFoldResult>> ForOp::getLoopUpperBounds() {
423}
424
425bool ForOp::isValidInductionVarType(Type type) {
426 return type.isIndex() || type.isSignlessInteger();
427}
428
429LogicalResult ForOp::setLoopLowerBounds(ArrayRef<OpFoldResult> bounds) {
430 if (bounds.size() != 1)
431 return failure();
432 if (auto val = dyn_cast<Value>(bounds[0])) {
433 setLowerBound(val);
434 return success();
435 }
436 return failure();
437}
438
439LogicalResult ForOp::setLoopUpperBounds(ArrayRef<OpFoldResult> bounds) {
440 if (bounds.size() != 1)
441 return failure();
442 if (auto val = dyn_cast<Value>(bounds[0])) {
443 setUpperBound(val);
444 return success();
445 }
446 return failure();
447}
448
449LogicalResult ForOp::setLoopSteps(ArrayRef<OpFoldResult> steps) {
450 if (steps.size() != 1)
451 return failure();
452 if (auto val = dyn_cast<Value>(steps[0])) {
453 setStep(val);
454 return success();
455 }
456 return failure();
457}
458
459std::optional<ResultRange> ForOp::getLoopResults() { return getResults(); }
460
461/// Promotes the loop body of a forOp to its containing block if the forOp
462/// it can be determined that the loop has a single iteration.
463LogicalResult ForOp::promoteIfSingleIteration(RewriterBase &rewriter) {
464 std::optional<APInt> tripCount = getStaticTripCount();
465 LDBG() << "promoteIfSingleIteration tripCount is " << tripCount
466 << " for loop "
467 << OpWithFlags(getOperation(), OpPrintingFlags().skipRegions());
468 if (!tripCount.has_value() || tripCount->getZExtValue() > 1)
469 return failure();
470
471 if (*tripCount == 0) {
472 rewriter.replaceAllUsesWith(getResults(), getInitArgs());
473 rewriter.eraseOp(*this);
474 return success();
475 }
476
477 // Replace all results with the yielded values.
478 auto yieldOp = cast<scf::YieldOp>(getBody()->getTerminator());
479 rewriter.replaceAllUsesWith(getResults(), getYieldedValues());
480
481 // Replace block arguments with lower bound (replacement for IV) and
482 // iter_args.
483 SmallVector<Value> bbArgReplacements;
484 bbArgReplacements.push_back(getLowerBound());
485 llvm::append_range(bbArgReplacements, getInitArgs());
486
487 // Move the loop body operations to the loop's containing block.
488 rewriter.inlineBlockBefore(getBody(), getOperation()->getBlock(),
489 getOperation()->getIterator(), bbArgReplacements);
490
491 // Erase the old terminator and the loop.
492 rewriter.eraseOp(yieldOp);
493 rewriter.eraseOp(*this);
494
495 return success();
496}
497
498/// Prints the initialization list in the form of
499/// <prefix>(%inner = %outer, %inner2 = %outer2, <...>)
500/// where 'inner' values are assumed to be region arguments and 'outer' values
501/// are regular SSA values.
503 Block::BlockArgListType blocksArgs,
504 ValueRange initializers,
505 StringRef prefix = "") {
506 assert(blocksArgs.size() == initializers.size() &&
507 "expected same length of arguments and initializers");
508 if (initializers.empty())
509 return;
510
511 p << prefix << '(';
512 llvm::interleaveComma(llvm::zip(blocksArgs, initializers), p, [&](auto it) {
513 p << std::get<0>(it) << " = " << std::get<1>(it);
514 });
515 p << ")";
516}
517
518void ForOp::print(OpAsmPrinter &p) {
519 if (getUnsignedCmp())
520 p << " unsigned";
521
522 p << " " << getInductionVar() << " = " << getLowerBound() << " to "
523 << getUpperBound() << " step " << getStep();
524
525 printInitializationList(p, getRegionIterArgs(), getInitArgs(), " iter_args");
526 if (!getInitArgs().empty())
527 p << " -> (" << getInitArgs().getTypes() << ')';
528 p << ' ';
529 if (Type t = getInductionVar().getType(); !t.isIndex())
530 p << " : " << t << ' ';
531 p.printRegion(getRegion(),
532 /*printEntryBlockArgs=*/false,
533 /*printBlockTerminators=*/!getInitArgs().empty());
534 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
535}
536
537ParseResult ForOp::parse(OpAsmParser &parser, OperationState &result) {
538 auto &builder = parser.getBuilder();
539 Type type;
540
541 OpAsmParser::Argument inductionVariable;
543
544 if (succeeded(parser.parseOptionalKeyword("unsigned")))
545 result.addAttribute(getUnsignedCmpAttrName(result.name),
546 builder.getUnitAttr());
547
548 // Parse the induction variable followed by '='.
549 if (parser.parseOperand(inductionVariable.ssaName) || parser.parseEqual() ||
550 // Parse loop bounds.
551 parser.parseOperand(lb) || parser.parseKeyword("to") ||
552 parser.parseOperand(ub) || parser.parseKeyword("step") ||
553 parser.parseOperand(step))
554 return failure();
555
556 // Parse the optional initial iteration arguments.
559 regionArgs.push_back(inductionVariable);
560
561 bool hasIterArgs = succeeded(parser.parseOptionalKeyword("iter_args"));
562 if (hasIterArgs) {
563 // Parse assignment list and results type list.
564 if (parser.parseAssignmentList(regionArgs, operands) ||
565 parser.parseArrowTypeList(result.types))
566 return failure();
567 }
568
569 if (regionArgs.size() != result.types.size() + 1)
570 return parser.emitError(
571 parser.getNameLoc(),
572 "mismatch in number of loop-carried values and defined values");
573
574 // Parse optional type, else assume Index.
575 if (parser.parseOptionalColon())
576 type = builder.getIndexType();
577 else if (parser.parseType(type))
578 return failure();
579
580 // Set block argument types, so that they are known when parsing the region.
581 regionArgs.front().type = type;
582 for (auto [iterArg, type] :
583 llvm::zip_equal(llvm::drop_begin(regionArgs), result.types))
584 iterArg.type = type;
585
586 // Parse the body region.
587 Region *body = result.addRegion();
588 if (parser.parseRegion(*body, regionArgs))
589 return failure();
590 ForOp::ensureTerminator(*body, builder, result.location);
591
592 // Resolve input operands. This should be done after parsing the region to
593 // catch invalid IR where operands were defined inside of the region.
594 if (parser.resolveOperand(lb, type, result.operands) ||
595 parser.resolveOperand(ub, type, result.operands) ||
596 parser.resolveOperand(step, type, result.operands))
597 return failure();
598 if (hasIterArgs) {
599 for (auto argOperandType : llvm::zip_equal(llvm::drop_begin(regionArgs),
600 operands, result.types)) {
601 Type type = std::get<2>(argOperandType);
602 std::get<0>(argOperandType).type = type;
603 if (parser.resolveOperand(std::get<1>(argOperandType), type,
604 result.operands))
605 return failure();
606 }
607 }
608
609 // Parse the optional attribute list.
610 if (parser.parseOptionalAttrDict(result.attributes))
611 return failure();
612
613 return success();
614}
615
616SmallVector<Region *> ForOp::getLoopRegions() { return {&getRegion()}; }
617
618Block::BlockArgListType ForOp::getRegionIterArgs() {
619 return getBody()->getArguments().drop_front(getNumInductionVars());
620}
621
622MutableArrayRef<OpOperand> ForOp::getInitsMutable() {
623 return getInitArgsMutable();
624}
625
626FailureOr<LoopLikeOpInterface>
627ForOp::replaceWithAdditionalYields(RewriterBase &rewriter,
628 ValueRange newInitOperands,
629 bool replaceInitOperandUsesInLoop,
630 const NewYieldValuesFn &newYieldValuesFn) {
631 // Create a new loop before the existing one, with the extra operands.
632 OpBuilder::InsertionGuard g(rewriter);
633 rewriter.setInsertionPoint(getOperation());
634 auto inits = llvm::to_vector(getInitArgs());
635 inits.append(newInitOperands.begin(), newInitOperands.end());
636 scf::ForOp newLoop = scf::ForOp::create(
637 rewriter, getLoc(), getLowerBound(), getUpperBound(), getStep(), inits,
638 [](OpBuilder &, Location, Value, ValueRange) {}, getUnsignedCmp());
639 newLoop->setDiscardableAttrs(getOperation()->getDiscardableAttrDictionary());
640
641 // Generate the new yield values and append them to the scf.yield operation.
642 auto yieldOp = cast<scf::YieldOp>(getBody()->getTerminator());
643 ArrayRef<BlockArgument> newIterArgs =
644 newLoop.getBody()->getArguments().take_back(newInitOperands.size());
645 {
646 OpBuilder::InsertionGuard g(rewriter);
647 rewriter.setInsertionPoint(yieldOp);
648 SmallVector<Value> newYieldedValues =
649 newYieldValuesFn(rewriter, getLoc(), newIterArgs);
650 assert(newInitOperands.size() == newYieldedValues.size() &&
651 "expected as many new yield values as new iter operands");
652 rewriter.modifyOpInPlace(yieldOp, [&]() {
653 yieldOp.getResultsMutable().append(newYieldedValues);
654 });
655 }
656
657 // Move the loop body to the new op.
658 rewriter.mergeBlocks(getBody(), newLoop.getBody(),
659 newLoop.getBody()->getArguments().take_front(
660 getBody()->getNumArguments()));
661
662 if (replaceInitOperandUsesInLoop) {
663 // Replace all uses of `newInitOperands` with the corresponding basic block
664 // arguments.
665 for (auto it : llvm::zip(newInitOperands, newIterArgs)) {
666 rewriter.replaceUsesWithIf(std::get<0>(it), std::get<1>(it),
667 [&](OpOperand &use) {
668 Operation *user = use.getOwner();
669 return newLoop->isProperAncestor(user);
670 });
671 }
672 }
673
674 // Replace the old loop.
675 rewriter.replaceOp(getOperation(),
676 newLoop->getResults().take_front(getNumResults()));
677 return cast<LoopLikeOpInterface>(newLoop.getOperation());
678}
679
681 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
682 if (!ivArg)
683 return ForOp();
684 assert(ivArg.getOwner() && "unlinked block argument");
685 auto *containingOp = ivArg.getOwner()->getParentOp();
686 return dyn_cast_or_null<ForOp>(containingOp);
687}
688
689OperandRange ForOp::getEntrySuccessorOperands(RegionSuccessor successor) {
690 return getInitArgs();
691}
692
693void ForOp::getSuccessorRegions(RegionBranchPoint point,
695 if (std::optional<APInt> tripCount = getStaticTripCount()) {
696 // The loop has a known static trip count.
697 if (point.isParent()) {
698 if (*tripCount == 0) {
699 // The loop has zero iterations. It branches directly back to the
700 // parent.
701 regions.push_back(RegionSuccessor(getOperation()));
702 } else {
703 // The loop has at least one iteration. It branches into the body.
704 regions.push_back(RegionSuccessor(&getRegion()));
705 }
706 return;
707 } else if (*tripCount == 1) {
708 // The loop has exactly 1 iteration. Therefore, it branches from the
709 // region to the parent. (No further iteration.)
710 regions.push_back(RegionSuccessor(getOperation()));
711 return;
712 }
713 }
714
715 // Both the operation itself and the region may be branching into the body or
716 // back into the operation itself. It is possible for loop not to enter the
717 // body.
718 regions.push_back(RegionSuccessor(&getRegion()));
719 regions.push_back(RegionSuccessor(getOperation()));
720}
721
722ValueRange ForOp::getSuccessorInputs(RegionSuccessor successor) {
723 return successor.isOperation() ? ValueRange(getResults())
724 : ValueRange(getRegionIterArgs());
725}
726
727SmallVector<Region *> ForallOp::getLoopRegions() { return {&getRegion()}; }
728
729/// Promotes the loop body of a forallOp to its containing block if it can be
730/// determined that the loop has a single iteration.
731LogicalResult scf::ForallOp::promoteIfSingleIteration(RewriterBase &rewriter) {
732 for (auto [lb, ub, step] :
733 llvm::zip(getMixedLowerBound(), getMixedUpperBound(), getMixedStep())) {
734 auto tripCount =
735 constantTripCount(lb, ub, step, /*isSigned=*/true, computeUbMinusLb);
736 if (!tripCount.has_value() || *tripCount != 1)
737 return failure();
738 }
739
740 promote(rewriter, *this);
741 return success();
742}
743
744Block::BlockArgListType ForallOp::getRegionIterArgs() {
745 return getBody()->getArguments().drop_front(getRank());
746}
747
748MutableArrayRef<OpOperand> ForallOp::getInitsMutable() {
749 return getOutputsMutable();
750}
751
752/// Promotes the loop body of a scf::ForallOp to its containing block.
753void mlir::scf::promote(RewriterBase &rewriter, scf::ForallOp forallOp) {
754 OpBuilder::InsertionGuard g(rewriter);
755 scf::InParallelOp terminator = forallOp.getTerminator();
756
757 // Replace block arguments with lower bounds (replacements for IVs) and
758 // outputs.
759 SmallVector<Value> bbArgReplacements = forallOp.getLowerBound(rewriter);
760 bbArgReplacements.append(forallOp.getOutputs().begin(),
761 forallOp.getOutputs().end());
762
763 // Move the loop body operations to the loop's containing block.
764 rewriter.inlineBlockBefore(forallOp.getBody(), forallOp->getBlock(),
765 forallOp->getIterator(), bbArgReplacements);
766
767 // Replace the terminator with tensor.insert_slice ops.
768 rewriter.setInsertionPointAfter(forallOp);
769 SmallVector<Value> results;
770 results.reserve(forallOp.getResults().size());
771 for (auto &yieldingOp : terminator.getYieldingOps()) {
772 auto parallelInsertSliceOp =
773 dyn_cast<tensor::ParallelInsertSliceOp>(yieldingOp);
774 if (!parallelInsertSliceOp)
775 continue;
776
777 Value dst = parallelInsertSliceOp.getDest();
778 Value src = parallelInsertSliceOp.getSource();
779 if (llvm::isa<TensorType>(src.getType())) {
780 results.push_back(tensor::InsertSliceOp::create(
781 rewriter, forallOp.getLoc(), dst.getType(), src, dst,
782 parallelInsertSliceOp.getOffsets(), parallelInsertSliceOp.getSizes(),
783 parallelInsertSliceOp.getStrides(),
784 parallelInsertSliceOp.getStaticOffsets(),
785 parallelInsertSliceOp.getStaticSizes(),
786 parallelInsertSliceOp.getStaticStrides()));
787 } else {
788 llvm_unreachable("unsupported terminator");
789 }
790 }
791 rewriter.replaceAllUsesWith(forallOp.getResults(), results);
792
793 // Erase the old terminator and the loop.
794 rewriter.eraseOp(terminator);
795 rewriter.eraseOp(forallOp);
796}
797
799 OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs,
800 ValueRange steps, ValueRange iterArgs,
802 bodyBuilder) {
803 assert(lbs.size() == ubs.size() &&
804 "expected the same number of lower and upper bounds");
805 assert(lbs.size() == steps.size() &&
806 "expected the same number of lower bounds and steps");
807
808 // If there are no bounds, call the body-building function and return early.
809 if (lbs.empty()) {
810 ValueVector results =
811 bodyBuilder ? bodyBuilder(builder, loc, ValueRange(), iterArgs)
812 : ValueVector();
813 assert(results.size() == iterArgs.size() &&
814 "loop nest body must return as many values as loop has iteration "
815 "arguments");
816 return LoopNest{{}, std::move(results)};
817 }
818
819 // First, create the loop structure iteratively using the body-builder
820 // callback of `ForOp::build`. Do not create `YieldOp`s yet.
821 OpBuilder::InsertionGuard guard(builder);
824 loops.reserve(lbs.size());
825 ivs.reserve(lbs.size());
826 ValueRange currentIterArgs = iterArgs;
827 Location currentLoc = loc;
828 for (unsigned i = 0, e = lbs.size(); i < e; ++i) {
829 auto loop = scf::ForOp::create(
830 builder, currentLoc, lbs[i], ubs[i], steps[i], currentIterArgs,
831 [&](OpBuilder &nestedBuilder, Location nestedLoc, Value iv,
832 ValueRange args) {
833 ivs.push_back(iv);
834 // It is safe to store ValueRange args because it points to block
835 // arguments of a loop operation that we also own.
836 currentIterArgs = args;
837 currentLoc = nestedLoc;
838 });
839 // Set the builder to point to the body of the newly created loop. We don't
840 // do this in the callback because the builder is reset when the callback
841 // returns.
842 builder.setInsertionPointToStart(loop.getBody());
843 loops.push_back(loop);
844 }
845
846 // For all loops but the innermost, yield the results of the nested loop.
847 for (unsigned i = 0, e = loops.size() - 1; i < e; ++i) {
848 builder.setInsertionPointToEnd(loops[i].getBody());
849 scf::YieldOp::create(builder, loc, loops[i + 1].getResults());
850 }
851
852 // In the body of the innermost loop, call the body building function if any
853 // and yield its results.
854 builder.setInsertionPointToStart(loops.back().getBody());
855 ValueVector results = bodyBuilder
856 ? bodyBuilder(builder, currentLoc, ivs,
857 loops.back().getRegionIterArgs())
858 : ValueVector();
859 assert(results.size() == iterArgs.size() &&
860 "loop nest body must return as many values as loop has iteration "
861 "arguments");
862 builder.setInsertionPointToEnd(loops.back().getBody());
863 scf::YieldOp::create(builder, loc, results);
864
865 // Return the loops.
866 ValueVector nestResults;
867 llvm::append_range(nestResults, loops.front().getResults());
868 return LoopNest{std::move(loops), std::move(nestResults)};
869}
870
872 OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs,
873 ValueRange steps,
874 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilder) {
875 // Delegate to the main function by wrapping the body builder.
876 return buildLoopNest(builder, loc, lbs, ubs, steps, {},
877 [&bodyBuilder](OpBuilder &nestedBuilder,
878 Location nestedLoc, ValueRange ivs,
880 if (bodyBuilder)
881 bodyBuilder(nestedBuilder, nestedLoc, ivs);
882 return {};
883 });
884}
885
888 OpOperand &operand, Value replacement,
889 const ValueTypeCastFnTy &castFn) {
890 assert(operand.getOwner() == forOp);
891 Type oldType = operand.get().getType(), newType = replacement.getType();
892
893 // 1. Create new iter operands, exactly 1 is replaced.
894 assert(operand.getOperandNumber() >= forOp.getNumControlOperands() &&
895 "expected an iter OpOperand");
896 assert(operand.get().getType() != replacement.getType() &&
897 "Expected a different type");
898 SmallVector<Value> newIterOperands;
899 for (OpOperand &opOperand : forOp.getInitArgsMutable()) {
900 if (opOperand.getOperandNumber() == operand.getOperandNumber()) {
901 newIterOperands.push_back(replacement);
902 continue;
903 }
904 newIterOperands.push_back(opOperand.get());
905 }
906
907 // 2. Create the new forOp shell.
908 scf::ForOp newForOp = scf::ForOp::create(
909 rewriter, forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
910 forOp.getStep(), newIterOperands, /*bodyBuilder=*/nullptr,
911 forOp.getUnsignedCmp());
912 newForOp->setDiscardableAttrs(
913 forOp->getDiscardableAttrDictionary().getValue());
914 Block &newBlock = newForOp.getRegion().front();
915 SmallVector<Value, 4> newBlockTransferArgs(newBlock.getArguments().begin(),
916 newBlock.getArguments().end());
917
918 // 3. Inject an incoming cast op at the beginning of the block for the bbArg
919 // corresponding to the `replacement` value.
920 OpBuilder::InsertionGuard g(rewriter);
921 rewriter.setInsertionPointToStart(&newBlock);
922 BlockArgument newRegionIterArg = newForOp.getTiedLoopRegionIterArg(
923 &newForOp->getOpOperand(operand.getOperandNumber()));
924 Value castIn = castFn(rewriter, newForOp.getLoc(), oldType, newRegionIterArg);
925 newBlockTransferArgs[newRegionIterArg.getArgNumber()] = castIn;
926
927 // 4. Steal the old block ops, mapping to the newBlockTransferArgs.
928 Block &oldBlock = forOp.getRegion().front();
929 rewriter.mergeBlocks(&oldBlock, &newBlock, newBlockTransferArgs);
930
931 // 5. Inject an outgoing cast op at the end of the block and yield it instead.
932 auto clonedYieldOp = cast<scf::YieldOp>(newBlock.getTerminator());
933 rewriter.setInsertionPoint(clonedYieldOp);
934 unsigned yieldIdx =
935 newRegionIterArg.getArgNumber() - forOp.getNumInductionVars();
936 Value castOut = castFn(rewriter, newForOp.getLoc(), newType,
937 clonedYieldOp.getOperand(yieldIdx));
938 SmallVector<Value> newYieldOperands = clonedYieldOp.getOperands();
939 newYieldOperands[yieldIdx] = castOut;
940 scf::YieldOp::create(rewriter, newForOp.getLoc(), newYieldOperands);
941 rewriter.eraseOp(clonedYieldOp);
942
943 // 6. Inject an outgoing cast op after the forOp.
944 rewriter.setInsertionPointAfter(newForOp);
945 SmallVector<Value> newResults = newForOp.getResults();
946 newResults[yieldIdx] =
947 castFn(rewriter, newForOp.getLoc(), oldType, newResults[yieldIdx]);
948
949 return newResults;
950}
951
952namespace {
953/// Fold scf.for iter_arg/result pairs that go through incoming/ougoing
954/// a tensor.cast op pair so as to pull the tensor.cast inside the scf.for:
955///
956/// ```
957/// %0 = tensor.cast %t0 : tensor<32x1024xf32> to tensor<?x?xf32>
958/// %1 = scf.for %i = %c0 to %c1024 step %c32 iter_args(%iter_t0 = %0)
959/// -> (tensor<?x?xf32>) {
960/// %2 = call @do(%iter_t0) : (tensor<?x?xf32>) -> tensor<?x?xf32>
961/// scf.yield %2 : tensor<?x?xf32>
962/// }
963/// use_of(%1)
964/// ```
965///
966/// folds into:
967///
968/// ```
969/// %0 = scf.for %arg2 = %c0 to %c1024 step %c32 iter_args(%arg3 = %arg0)
970/// -> (tensor<32x1024xf32>) {
971/// %2 = tensor.cast %arg3 : tensor<32x1024xf32> to tensor<?x?xf32>
972/// %3 = call @do(%2) : (tensor<?x?xf32>) -> tensor<?x?xf32>
973/// %4 = tensor.cast %3 : tensor<?x?xf32> to tensor<32x1024xf32>
974/// scf.yield %4 : tensor<32x1024xf32>
975/// }
976/// %1 = tensor.cast %0 : tensor<32x1024xf32> to tensor<?x?xf32>
977/// use_of(%1)
978/// ```
979struct ForOpTensorCastFolder : public OpRewritePattern<ForOp> {
981
982 LogicalResult matchAndRewrite(ForOp op,
983 PatternRewriter &rewriter) const override {
984 for (auto it : llvm::zip(op.getInitArgsMutable(), op.getResults())) {
985 OpOperand &iterOpOperand = std::get<0>(it);
986 auto incomingCast = iterOpOperand.get().getDefiningOp<tensor::CastOp>();
987 if (!incomingCast ||
988 incomingCast.getSource().getType() == incomingCast.getType())
989 continue;
990 // If the dest type of the cast does not preserve static information in
991 // the source type.
993 incomingCast.getDest().getType(),
994 incomingCast.getSource().getType()))
995 continue;
996 if (!std::get<1>(it).hasOneUse())
997 continue;
998
999 // Create a new ForOp with that iter operand replaced.
1000 rewriter.replaceOp(
1002 rewriter, op, iterOpOperand, incomingCast.getSource(),
1003 [](OpBuilder &b, Location loc, Type type, Value source) {
1004 return tensor::CastOp::create(b, loc, type, source);
1005 }));
1006 return success();
1007 }
1008 return failure();
1009 }
1010};
1011} // namespace
1012
1013void ForOp::getCanonicalizationPatterns(RewritePatternSet &results,
1014 MLIRContext *context) {
1015 results.add<ForOpTensorCastFolder>(context);
1017 results, ForOp::getOperationName());
1018 // Inline single-iteration loops before applying the generic region branch op
1019 // canonicalizations, which may otherwise remove tied iter_args and results
1020 // independently.
1022 results, ForOp::getOperationName(),
1023 /*replBuilderFn=*/
1024 [](OpBuilder &builder, Location loc, Value value) {
1025 // scf.for has only one non-successor input value: the loop induction
1026 // variable. In case of a single acyclic path through the op, the IV can
1027 // be safely replaced with the lower bound.
1028 auto blockArg = cast<BlockArgument>(value);
1029 assert(blockArg.getArgNumber() == 0 && "expected induction variable");
1030 auto forOp = cast<ForOp>(blockArg.getOwner()->getParentOp());
1031 return forOp.getLowerBound();
1032 },
1034 /*benefit=*/2);
1035}
1036
1037std::optional<APInt> ForOp::getConstantStep() {
1038 IntegerAttr step;
1039 if (matchPattern(getStep(), m_Constant(&step)))
1040 return step.getValue();
1041 return {};
1042}
1043
1044std::optional<MutableArrayRef<OpOperand>> ForOp::getYieldedValuesMutable() {
1045 return cast<scf::YieldOp>(getBody()->getTerminator()).getResultsMutable();
1046}
1047
1048Speculation::Speculatability ForOp::getSpeculatability() {
1049 // `scf.for (I = Start; I < End; I += 1)` terminates for all values of Start
1050 // and End.
1051 if (auto constantStep = getConstantStep())
1052 if (*constantStep == 1)
1054
1055 // For Step != 1, the loop may not terminate. We can add more smarts here if
1056 // needed.
1058}
1059
1060std::optional<APInt> ForOp::getStaticTripCount() {
1061 return constantTripCount(getLowerBound(), getUpperBound(), getStep(),
1062 /*isSigned=*/!getUnsignedCmp(), computeUbMinusLb);
1063}
1064
1065//===----------------------------------------------------------------------===//
1066// ForallOp
1067//===----------------------------------------------------------------------===//
1068
1069LogicalResult ForallOp::verify() {
1070 unsigned numLoops = getRank();
1071 // Check number of outputs.
1072 if (getNumResults() != getOutputs().size())
1073 return emitOpError("produces ")
1074 << getNumResults() << " results, but has only "
1075 << getOutputs().size() << " outputs";
1076
1077 // Check that the body defines block arguments for thread indices and outputs.
1078 auto *body = getBody();
1079 if (body->getNumArguments() != numLoops + getOutputs().size())
1080 return emitOpError("region expects ") << numLoops << " arguments";
1081 for (int64_t i = 0; i < numLoops; ++i)
1082 if (!body->getArgument(i).getType().isIndex())
1083 return emitOpError("expects ")
1084 << i << "-th block argument to be an index";
1085 for (unsigned i = 0; i < getOutputs().size(); ++i)
1086 if (body->getArgument(i + numLoops).getType() != getOutputs()[i].getType())
1087 return emitOpError("type mismatch between ")
1088 << i << "-th output and corresponding block argument";
1089 if (getMapping().has_value() && !getMapping()->empty()) {
1090 if (getDeviceMappingAttrs().size() != numLoops)
1091 return emitOpError() << "mapping attribute size must match op rank";
1092 if (failed(getDeviceMaskingAttr()))
1093 return emitOpError() << getMappingAttrName()
1094 << " supports at most one device masking attribute";
1095 }
1096
1097 // Verify mixed static/dynamic control variables.
1098 Operation *op = getOperation();
1099 if (failed(verifyListOfOperandsOrIntegers(op, "lower bound", numLoops,
1100 getStaticLowerBound(),
1101 getDynamicLowerBound())))
1102 return failure();
1103 if (failed(verifyListOfOperandsOrIntegers(op, "upper bound", numLoops,
1104 getStaticUpperBound(),
1105 getDynamicUpperBound())))
1106 return failure();
1107 if (failed(verifyListOfOperandsOrIntegers(op, "step", numLoops,
1108 getStaticStep(), getDynamicStep())))
1109 return failure();
1110
1111 return success();
1112}
1113
1114void ForallOp::print(OpAsmPrinter &p) {
1115 Operation *op = getOperation();
1116 p << " (" << getInductionVars();
1117 if (isNormalized()) {
1118 p << ") in ";
1119 printDynamicIndexList(p, op, getDynamicUpperBound(), getStaticUpperBound(),
1120 /*valueTypes=*/{}, /*scalables=*/{},
1122 } else {
1123 p << ") = ";
1124 printDynamicIndexList(p, op, getDynamicLowerBound(), getStaticLowerBound(),
1125 /*valueTypes=*/{}, /*scalables=*/{},
1127 p << " to ";
1128 printDynamicIndexList(p, op, getDynamicUpperBound(), getStaticUpperBound(),
1129 /*valueTypes=*/{}, /*scalables=*/{},
1131 p << " step ";
1132 printDynamicIndexList(p, op, getDynamicStep(), getStaticStep(),
1133 /*valueTypes=*/{}, /*scalables=*/{},
1135 }
1136 printInitializationList(p, getRegionOutArgs(), getOutputs(), " shared_outs");
1137 p << " ";
1138 if (!getRegionOutArgs().empty())
1139 p << "-> (" << getResultTypes() << ") ";
1140 p.printRegion(getRegion(),
1141 /*printEntryBlockArgs=*/false,
1142 /*printBlockTerminators=*/getNumResults() > 0);
1143 SmallVector<NamedAttribute> attrs(op->getDiscardableAttrs());
1144 if (ArrayAttr mapping = getMappingAttr())
1145 attrs.emplace_back(getMappingAttrName(), mapping);
1146 llvm::sort(attrs);
1147 p.printOptionalAttrDict(attrs);
1148}
1149
1150ParseResult ForallOp::parse(OpAsmParser &parser, OperationState &result) {
1151 OpBuilder b(parser.getContext());
1152 auto indexType = b.getIndexType();
1153
1154 // Parse an opening `(` followed by thread index variables followed by `)`
1155 // TODO: when we can refer to such "induction variable"-like handles from the
1156 // declarative assembly format, we can implement the parser as a custom hook.
1157 SmallVector<OpAsmParser::Argument, 4> ivs;
1159 return failure();
1160
1161 DenseI64ArrayAttr staticLbs, staticUbs, staticSteps;
1162 SmallVector<OpAsmParser::UnresolvedOperand> dynamicLbs, dynamicUbs,
1163 dynamicSteps;
1164 if (succeeded(parser.parseOptionalKeyword("in"))) {
1165 // Parse upper bounds.
1166 if (parseDynamicIndexList(parser, dynamicUbs, staticUbs,
1167 /*valueTypes=*/nullptr,
1169 parser.resolveOperands(dynamicUbs, indexType, result.operands))
1170 return failure();
1171
1172 unsigned numLoops = ivs.size();
1173 staticLbs = b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 0));
1174 staticSteps = b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 1));
1175 } else {
1176 // Parse lower bounds.
1177 if (parser.parseEqual() ||
1178 parseDynamicIndexList(parser, dynamicLbs, staticLbs,
1179 /*valueTypes=*/nullptr,
1181
1182 parser.resolveOperands(dynamicLbs, indexType, result.operands))
1183 return failure();
1184
1185 // Parse upper bounds.
1186 if (parser.parseKeyword("to") ||
1187 parseDynamicIndexList(parser, dynamicUbs, staticUbs,
1188 /*valueTypes=*/nullptr,
1190 parser.resolveOperands(dynamicUbs, indexType, result.operands))
1191 return failure();
1192
1193 // Parse step values.
1194 if (parser.parseKeyword("step") ||
1195 parseDynamicIndexList(parser, dynamicSteps, staticSteps,
1196 /*valueTypes=*/nullptr,
1198 parser.resolveOperands(dynamicSteps, indexType, result.operands))
1199 return failure();
1200 }
1201
1202 // Parse out operands and results.
1203 SmallVector<OpAsmParser::Argument, 4> regionOutArgs;
1204 SmallVector<OpAsmParser::UnresolvedOperand, 4> outOperands;
1205 SMLoc outOperandsLoc = parser.getCurrentLocation();
1206 if (succeeded(parser.parseOptionalKeyword("shared_outs"))) {
1207 if (outOperands.size() != result.types.size())
1208 return parser.emitError(outOperandsLoc,
1209 "mismatch between out operands and types");
1210 if (parser.parseAssignmentList(regionOutArgs, outOperands) ||
1211 parser.parseOptionalArrowTypeList(result.types) ||
1212 parser.resolveOperands(outOperands, result.types, outOperandsLoc,
1213 result.operands))
1214 return failure();
1215 }
1216
1217 // Parse region.
1218 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1219 std::unique_ptr<Region> region = std::make_unique<Region>();
1220 for (auto &iv : ivs) {
1221 iv.type = b.getIndexType();
1222 regionArgs.push_back(iv);
1223 }
1224 for (const auto &it : llvm::enumerate(regionOutArgs)) {
1225 auto &out = it.value();
1226 out.type = result.types[it.index()];
1227 regionArgs.push_back(out);
1228 }
1229 if (parser.parseRegion(*region, regionArgs))
1230 return failure();
1231
1232 // Ensure terminator and move region.
1233 ForallOp::ensureTerminator(*region, b, result.location);
1234 result.addRegion(std::move(region));
1235
1236 // Parse the optional attribute list.
1237 if (parser.parseOptionalAttrDict(result.attributes))
1238 return failure();
1239
1240 result.addAttribute("staticLowerBound", staticLbs);
1241 result.addAttribute("staticUpperBound", staticUbs);
1242 result.addAttribute("staticStep", staticSteps);
1243 result.addAttribute("operandSegmentSizes",
1245 {static_cast<int32_t>(dynamicLbs.size()),
1246 static_cast<int32_t>(dynamicUbs.size()),
1247 static_cast<int32_t>(dynamicSteps.size()),
1248 static_cast<int32_t>(outOperands.size())}));
1249 return success();
1250}
1251
1252// Builder that takes loop bounds.
1253void ForallOp::build(
1254 mlir::OpBuilder &b, mlir::OperationState &result,
1255 ArrayRef<OpFoldResult> lbs, ArrayRef<OpFoldResult> ubs,
1256 ArrayRef<OpFoldResult> steps, ValueRange outputs,
1257 std::optional<ArrayAttr> mapping,
1258 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {
1259 SmallVector<int64_t> staticLbs, staticUbs, staticSteps;
1260 SmallVector<Value> dynamicLbs, dynamicUbs, dynamicSteps;
1261 dispatchIndexOpFoldResults(lbs, dynamicLbs, staticLbs);
1262 dispatchIndexOpFoldResults(ubs, dynamicUbs, staticUbs);
1263 dispatchIndexOpFoldResults(steps, dynamicSteps, staticSteps);
1264
1265 result.addOperands(dynamicLbs);
1266 result.addOperands(dynamicUbs);
1267 result.addOperands(dynamicSteps);
1268 result.addOperands(outputs);
1269 result.addTypes(TypeRange(outputs));
1270
1271 result.addAttribute(getStaticLowerBoundAttrName(result.name),
1272 b.getDenseI64ArrayAttr(staticLbs));
1273 result.addAttribute(getStaticUpperBoundAttrName(result.name),
1274 b.getDenseI64ArrayAttr(staticUbs));
1275 result.addAttribute(getStaticStepAttrName(result.name),
1276 b.getDenseI64ArrayAttr(staticSteps));
1277 result.addAttribute(
1278 "operandSegmentSizes",
1279 b.getDenseI32ArrayAttr({static_cast<int32_t>(dynamicLbs.size()),
1280 static_cast<int32_t>(dynamicUbs.size()),
1281 static_cast<int32_t>(dynamicSteps.size()),
1282 static_cast<int32_t>(outputs.size())}));
1283 if (mapping.has_value()) {
1284 result.addAttribute(ForallOp::getMappingAttrName(result.name),
1285 mapping.value());
1286 }
1287
1288 Region *bodyRegion = result.addRegion();
1289 OpBuilder::InsertionGuard g(b);
1290 b.createBlock(bodyRegion);
1291 Block &bodyBlock = bodyRegion->front();
1292
1293 // Add block arguments for indices and outputs.
1294 bodyBlock.addArguments(
1295 SmallVector<Type>(lbs.size(), b.getIndexType()),
1296 SmallVector<Location>(staticLbs.size(), result.location));
1297 bodyBlock.addArguments(
1298 TypeRange(outputs),
1299 SmallVector<Location>(outputs.size(), result.location));
1300
1301 b.setInsertionPointToStart(&bodyBlock);
1302 if (!bodyBuilderFn) {
1303 ForallOp::ensureTerminator(*bodyRegion, b, result.location);
1304 return;
1305 }
1306 bodyBuilderFn(b, result.location, bodyBlock.getArguments());
1307}
1308
1309// Builder that takes loop bounds.
1310void ForallOp::build(
1311 mlir::OpBuilder &b, mlir::OperationState &result,
1312 ArrayRef<OpFoldResult> ubs, ValueRange outputs,
1313 std::optional<ArrayAttr> mapping,
1314 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {
1315 unsigned numLoops = ubs.size();
1316 SmallVector<OpFoldResult> lbs(numLoops, b.getIndexAttr(0));
1317 SmallVector<OpFoldResult> steps(numLoops, b.getIndexAttr(1));
1318 build(b, result, lbs, ubs, steps, outputs, mapping, bodyBuilderFn);
1319}
1320
1321// Checks if the lbs are zeros and steps are ones.
1322bool ForallOp::isNormalized() {
1323 auto allEqual = [](ArrayRef<OpFoldResult> results, int64_t val) {
1324 return llvm::all_of(results, [&](OpFoldResult ofr) {
1325 auto intValue = getConstantIntValue(ofr);
1326 return intValue.has_value() && intValue == val;
1327 });
1328 };
1329 return allEqual(getMixedLowerBound(), 0) && allEqual(getMixedStep(), 1);
1330}
1331
1332InParallelOp ForallOp::getTerminator() {
1333 return cast<InParallelOp>(getBody()->getTerminator());
1334}
1335
1336SmallVector<Operation *> ForallOp::getCombiningOps(BlockArgument bbArg) {
1337 SmallVector<Operation *> storeOps;
1338 for (Operation *user : bbArg.getUsers()) {
1339 if (auto parallelOp = dyn_cast<ParallelCombiningOpInterface>(user)) {
1340 storeOps.push_back(parallelOp);
1341 }
1342 }
1343 return storeOps;
1344}
1345
1346SmallVector<DeviceMappingAttrInterface> ForallOp::getDeviceMappingAttrs() {
1347 SmallVector<DeviceMappingAttrInterface> res;
1348 if (!getMapping())
1349 return res;
1350 for (auto attr : getMapping()->getValue()) {
1351 auto m = dyn_cast<DeviceMappingAttrInterface>(attr);
1352 if (m)
1353 res.push_back(m);
1354 }
1355 return res;
1356}
1357
1358FailureOr<DeviceMaskingAttrInterface> ForallOp::getDeviceMaskingAttr() {
1359 DeviceMaskingAttrInterface res;
1360 if (!getMapping())
1361 return res;
1362 for (auto attr : getMapping()->getValue()) {
1363 auto m = dyn_cast<DeviceMaskingAttrInterface>(attr);
1364 if (m && res)
1365 return failure();
1366 if (m)
1367 res = m;
1368 }
1369 return res;
1370}
1371
1372bool ForallOp::usesLinearMapping() {
1373 SmallVector<DeviceMappingAttrInterface> ifaces = getDeviceMappingAttrs();
1374 if (ifaces.empty())
1375 return false;
1376 return ifaces.front().isLinearMapping();
1377}
1378
1379std::optional<SmallVector<Value>> ForallOp::getLoopInductionVars() {
1380 return SmallVector<Value>{getBody()->getArguments().take_front(getRank())};
1381}
1382
1383// Get lower bounds as OpFoldResult.
1384std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopLowerBounds() {
1385 Builder b(getOperation()->getContext());
1386 return getMixedValues(getStaticLowerBound(), getDynamicLowerBound(), b);
1387}
1388
1389// Get upper bounds as OpFoldResult.
1390std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopUpperBounds() {
1391 Builder b(getOperation()->getContext());
1392 return getMixedValues(getStaticUpperBound(), getDynamicUpperBound(), b);
1393}
1394
1395// Get steps as OpFoldResult.
1396std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopSteps() {
1397 Builder b(getOperation()->getContext());
1398 return getMixedValues(getStaticStep(), getDynamicStep(), b);
1399}
1400
1402 auto tidxArg = llvm::dyn_cast<BlockArgument>(val);
1403 if (!tidxArg)
1404 return ForallOp();
1405 assert(tidxArg.getOwner() && "unlinked block argument");
1406 auto *containingOp = tidxArg.getOwner()->getParentOp();
1407 return dyn_cast<ForallOp>(containingOp);
1408}
1409
1410namespace {
1411/// Fold tensor.dim(forall shared_outs(... = %t)) to tensor.dim(%t).
1412struct DimOfForallOp : public OpRewritePattern<tensor::DimOp> {
1413 using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
1414
1415 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1416 PatternRewriter &rewriter) const final {
1417 auto forallOp = dimOp.getSource().getDefiningOp<ForallOp>();
1418 if (!forallOp)
1419 return failure();
1420 Value sharedOut =
1421 forallOp.getTiedOpOperand(llvm::cast<OpResult>(dimOp.getSource()))
1422 ->get();
1423 rewriter.modifyOpInPlace(
1424 dimOp, [&]() { dimOp.getSourceMutable().assign(sharedOut); });
1425 return success();
1426 }
1427};
1428
1429class ForallOpControlOperandsFolder : public OpRewritePattern<ForallOp> {
1430public:
1431 using OpRewritePattern<ForallOp>::OpRewritePattern;
1432
1433 LogicalResult matchAndRewrite(ForallOp op,
1434 PatternRewriter &rewriter) const override {
1435 SmallVector<OpFoldResult> mixedLowerBound(op.getMixedLowerBound());
1436 SmallVector<OpFoldResult> mixedUpperBound(op.getMixedUpperBound());
1437 SmallVector<OpFoldResult> mixedStep(op.getMixedStep());
1438 if (failed(foldDynamicIndexList(mixedLowerBound)) &&
1439 failed(foldDynamicIndexList(mixedUpperBound)) &&
1440 failed(foldDynamicIndexList(mixedStep)))
1441 return failure();
1442
1443 rewriter.modifyOpInPlace(op, [&]() {
1444 SmallVector<Value> dynamicLowerBound, dynamicUpperBound, dynamicStep;
1445 SmallVector<int64_t> staticLowerBound, staticUpperBound, staticStep;
1446 dispatchIndexOpFoldResults(mixedLowerBound, dynamicLowerBound,
1447 staticLowerBound);
1448 op.getDynamicLowerBoundMutable().assign(dynamicLowerBound);
1449 op.setStaticLowerBound(staticLowerBound);
1450
1451 dispatchIndexOpFoldResults(mixedUpperBound, dynamicUpperBound,
1452 staticUpperBound);
1453 op.getDynamicUpperBoundMutable().assign(dynamicUpperBound);
1454 op.setStaticUpperBound(staticUpperBound);
1455
1456 dispatchIndexOpFoldResults(mixedStep, dynamicStep, staticStep);
1457 op.getDynamicStepMutable().assign(dynamicStep);
1458 op.setStaticStep(staticStep);
1459
1460 op->setInherentAttr(
1461 rewriter.getStringAttr(ForallOp::getOperandSegmentSizeAttr()),
1462 rewriter.getDenseI32ArrayAttr(
1463 {static_cast<int32_t>(dynamicLowerBound.size()),
1464 static_cast<int32_t>(dynamicUpperBound.size()),
1465 static_cast<int32_t>(dynamicStep.size()),
1466 static_cast<int32_t>(op.getNumResults())}));
1467 });
1468 return success();
1469 }
1470};
1471
1472/// The following canonicalization pattern folds the iter arguments of
1473/// scf.forall op if :-
1474/// 1. The corresponding result has zero uses.
1475/// 2. The iter argument is NOT being modified within the loop body.
1476/// uses.
1477///
1478/// Example of first case :-
1479/// INPUT:
1480/// %res:3 = scf.forall ... shared_outs(%arg0 = %a, %arg1 = %b, %arg2 = %c)
1481/// {
1482/// ...
1483/// <SOME USE OF %arg0>
1484/// <SOME USE OF %arg1>
1485/// <SOME USE OF %arg2>
1486/// ...
1487/// scf.forall.in_parallel {
1488/// <STORE OP WITH DESTINATION %arg1>
1489/// <STORE OP WITH DESTINATION %arg0>
1490/// <STORE OP WITH DESTINATION %arg2>
1491/// }
1492/// }
1493/// return %res#1
1494///
1495/// OUTPUT:
1496/// %res:3 = scf.forall ... shared_outs(%new_arg0 = %b)
1497/// {
1498/// ...
1499/// <SOME USE OF %a>
1500/// <SOME USE OF %new_arg0>
1501/// <SOME USE OF %c>
1502/// ...
1503/// scf.forall.in_parallel {
1504/// <STORE OP WITH DESTINATION %new_arg0>
1505/// }
1506/// }
1507/// return %res
1508///
1509/// NOTE: 1. All uses of the folded shared_outs (iter argument) within the
1510/// scf.forall is replaced by their corresponding operands.
1511/// 2. Even if there are <STORE OP WITH DESTINATION *> ops within the body
1512/// of the scf.forall besides within scf.forall.in_parallel terminator,
1513/// this canonicalization remains valid. For more details, please refer
1514/// to :
1515/// https://github.com/llvm/llvm-project/pull/90189#discussion_r1589011124
1516/// 3. TODO(avarma): Generalize it for other store ops. Currently it
1517/// handles tensor.parallel_insert_slice ops only.
1518///
1519/// Example of second case :-
1520/// INPUT:
1521/// %res:2 = scf.forall ... shared_outs(%arg0 = %a, %arg1 = %b)
1522/// {
1523/// ...
1524/// <SOME USE OF %arg0>
1525/// <SOME USE OF %arg1>
1526/// ...
1527/// scf.forall.in_parallel {
1528/// <STORE OP WITH DESTINATION %arg1>
1529/// }
1530/// }
1531/// return %res#0, %res#1
1532///
1533/// OUTPUT:
1534/// %res = scf.forall ... shared_outs(%new_arg0 = %b)
1535/// {
1536/// ...
1537/// <SOME USE OF %a>
1538/// <SOME USE OF %new_arg0>
1539/// ...
1540/// scf.forall.in_parallel {
1541/// <STORE OP WITH DESTINATION %new_arg0>
1542/// }
1543/// }
1544/// return %a, %res
1545struct ForallOpIterArgsFolder : public OpRewritePattern<ForallOp> {
1546 using OpRewritePattern<ForallOp>::OpRewritePattern;
1547
1548 LogicalResult matchAndRewrite(ForallOp forallOp,
1549 PatternRewriter &rewriter) const final {
1550 // Step 1: For a given i-th result of scf.forall, check the following :-
1551 // a. If it has any use.
1552 // b. If the corresponding iter argument is being modified within
1553 // the loop, i.e. has at least one store op with the iter arg as
1554 // its destination operand. For this we use
1555 // ForallOp::getCombiningOps(iter_arg).
1556 //
1557 // Based on the check we maintain the following :-
1558 // a. op results, block arguments, outputs to delete
1559 // b. new outputs (i.e., outputs to retain)
1560 SmallVector<Value> resultsToDelete;
1561 SmallVector<Value> outsToDelete;
1562 SmallVector<BlockArgument> blockArgsToDelete;
1563 SmallVector<Value> newOuts;
1564 BitVector resultIndicesToDelete(forallOp.getNumResults(), false);
1565 BitVector blockIndicesToDelete(forallOp.getBody()->getNumArguments(),
1566 false);
1567 for (OpResult result : forallOp.getResults()) {
1568 OpOperand *opOperand = forallOp.getTiedOpOperand(result);
1569 BlockArgument blockArg = forallOp.getTiedBlockArgument(opOperand);
1570 if (result.use_empty() || forallOp.getCombiningOps(blockArg).empty()) {
1571 resultsToDelete.push_back(result);
1572 outsToDelete.push_back(opOperand->get());
1573 blockArgsToDelete.push_back(blockArg);
1574 resultIndicesToDelete[result.getResultNumber()] = true;
1575 blockIndicesToDelete[blockArg.getArgNumber()] = true;
1576 } else {
1577 newOuts.push_back(opOperand->get());
1578 }
1579 }
1580
1581 // Return early if all results of scf.forall have at least one use and being
1582 // modified within the loop.
1583 if (resultsToDelete.empty())
1584 return failure();
1585
1586 // Step 2: Erase combining ops and replace uses of deleted results and
1587 // block arguments with the corresponding outputs.
1588 for (auto blockArg : blockArgsToDelete) {
1589 SmallVector<Operation *> combiningOps =
1590 forallOp.getCombiningOps(blockArg);
1591 for (Operation *combiningOp : combiningOps)
1592 rewriter.eraseOp(combiningOp);
1593 }
1594 for (auto [blockArg, result, out] :
1595 llvm::zip_equal(blockArgsToDelete, resultsToDelete, outsToDelete)) {
1596 rewriter.replaceAllUsesWith(blockArg, out);
1597 rewriter.replaceAllUsesWith(result, out);
1598 }
1599 // TODO: There is no rewriter API for erasing block arguments.
1600 rewriter.modifyOpInPlace(forallOp, [&]() {
1601 forallOp.getBody()->eraseArguments(blockIndicesToDelete);
1602 });
1603
1604 // Step 3. Create a new scf.forall op with only the shared_outs/results
1605 // that should be retained.
1606 auto newForallOp = cast<scf::ForallOp>(
1607 rewriter.eraseOpResults(forallOp, resultIndicesToDelete));
1608 newForallOp.getOutputsMutable().assign(newOuts);
1609
1610 return success();
1611 }
1612};
1613
1614struct ForallOpSingleOrZeroIterationDimsFolder
1615 : public OpRewritePattern<ForallOp> {
1616 using OpRewritePattern<ForallOp>::OpRewritePattern;
1617
1618 LogicalResult matchAndRewrite(ForallOp op,
1619 PatternRewriter &rewriter) const override {
1620 // Do not fold dimensions if they are mapped to processing units.
1621 if (op.getMapping().has_value() && !op.getMapping()->empty())
1622 return failure();
1623 Location loc = op.getLoc();
1624
1625 // Compute new loop bounds that omit all single-iteration loop dimensions.
1626 SmallVector<OpFoldResult> newMixedLowerBounds, newMixedUpperBounds,
1627 newMixedSteps;
1628 IRMapping mapping;
1629 for (auto [lb, ub, step, iv] :
1630 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1631 op.getMixedStep(), op.getInductionVars())) {
1632 auto numIterations =
1633 constantTripCount(lb, ub, step, /*isSigned=*/true, computeUbMinusLb);
1634 if (numIterations.has_value()) {
1635 // Remove the loop if it performs zero iterations.
1636 if (*numIterations == 0) {
1637 rewriter.replaceOp(op, op.getOutputs());
1638 return success();
1639 }
1640 // Replace the loop induction variable by the lower bound if the loop
1641 // performs a single iteration. Otherwise, copy the loop bounds.
1642 if (*numIterations == 1) {
1643 mapping.map(iv, getValueOrCreateConstantIndexOp(rewriter, loc, lb));
1644 continue;
1645 }
1646 }
1647 newMixedLowerBounds.push_back(lb);
1648 newMixedUpperBounds.push_back(ub);
1649 newMixedSteps.push_back(step);
1650 }
1651
1652 // All of the loop dimensions perform a single iteration. Inline loop body.
1653 if (newMixedLowerBounds.empty()) {
1654 promote(rewriter, op);
1655 return success();
1656 }
1657
1658 // Exit if none of the loop dimensions perform a single iteration.
1659 if (newMixedLowerBounds.size() == static_cast<unsigned>(op.getRank())) {
1660 return rewriter.notifyMatchFailure(
1661 op, "no dimensions have 0 or 1 iterations");
1662 }
1663
1664 // Replace the loop by a lower-dimensional loop.
1665 ForallOp newOp;
1666 newOp = ForallOp::create(rewriter, loc, newMixedLowerBounds,
1667 newMixedUpperBounds, newMixedSteps,
1668 op.getOutputs(), std::nullopt, nullptr);
1669 newOp.getBodyRegion().getBlocks().clear();
1670 newOp.setMappingAttr(op.getMappingAttr());
1671 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
1672 rewriter.cloneRegionBefore(op.getRegion(), newOp.getRegion(),
1673 newOp.getRegion().begin(), mapping);
1674 rewriter.replaceOp(op, newOp.getResults());
1675 return success();
1676 }
1677};
1678
1679/// Replace all induction vars with a single trip count with their lower bound.
1680struct ForallOpReplaceConstantInductionVar : public OpRewritePattern<ForallOp> {
1681 using OpRewritePattern<ForallOp>::OpRewritePattern;
1682
1683 LogicalResult matchAndRewrite(ForallOp op,
1684 PatternRewriter &rewriter) const override {
1685 Location loc = op.getLoc();
1686 bool changed = false;
1687 for (auto [lb, ub, step, iv] :
1688 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1689 op.getMixedStep(), op.getInductionVars())) {
1690 if (iv.hasNUses(0))
1691 continue;
1692 auto numIterations =
1693 constantTripCount(lb, ub, step, /*isSigned=*/true, computeUbMinusLb);
1694 if (!numIterations.has_value() || numIterations.value() != 1) {
1695 continue;
1696 }
1697 rewriter.replaceAllUsesWith(
1698 iv, getValueOrCreateConstantIndexOp(rewriter, loc, lb));
1699 changed = true;
1700 }
1701 return success(changed);
1702 }
1703};
1704
1705struct FoldTensorCastOfOutputIntoForallOp
1706 : public OpRewritePattern<scf::ForallOp> {
1707 using OpRewritePattern<scf::ForallOp>::OpRewritePattern;
1708
1709 struct TypeCast {
1710 Type srcType;
1711 Type dstType;
1712 };
1713
1714 LogicalResult matchAndRewrite(scf::ForallOp forallOp,
1715 PatternRewriter &rewriter) const final {
1716 llvm::SmallMapVector<unsigned, TypeCast, 2> tensorCastProducers;
1717 llvm::SmallVector<Value> newOutputTensors = forallOp.getOutputs();
1718 for (auto en : llvm::enumerate(newOutputTensors)) {
1719 auto castOp = en.value().getDefiningOp<tensor::CastOp>();
1720 if (!castOp)
1721 continue;
1722
1723 // Only casts that that preserve static information, i.e. will make the
1724 // loop result type "more" static than before, will be folded.
1725 if (!tensor::preservesStaticInformation(castOp.getDest().getType(),
1726 castOp.getSource().getType())) {
1727 continue;
1728 }
1729
1730 tensorCastProducers[en.index()] =
1731 TypeCast{castOp.getSource().getType(), castOp.getType()};
1732 newOutputTensors[en.index()] = castOp.getSource();
1733 }
1734
1735 if (tensorCastProducers.empty())
1736 return failure();
1737
1738 // Create new loop.
1739 Location loc = forallOp.getLoc();
1740 auto newForallOp = ForallOp::create(
1741 rewriter, loc, forallOp.getMixedLowerBound(),
1742 forallOp.getMixedUpperBound(), forallOp.getMixedStep(),
1743 newOutputTensors, forallOp.getMapping(),
1744 [&](OpBuilder nestedBuilder, Location nestedLoc, ValueRange bbArgs) {
1745 auto castBlockArgs =
1746 llvm::to_vector(bbArgs.take_back(forallOp->getNumResults()));
1747 for (auto [index, cast] : tensorCastProducers) {
1748 Value &oldTypeBBArg = castBlockArgs[index];
1749 oldTypeBBArg = tensor::CastOp::create(nestedBuilder, nestedLoc,
1750 cast.dstType, oldTypeBBArg);
1751 }
1752
1753 // Move old body into new parallel loop.
1754 SmallVector<Value> ivsBlockArgs =
1755 llvm::to_vector(bbArgs.take_front(forallOp.getRank()));
1756 ivsBlockArgs.append(castBlockArgs);
1757 rewriter.mergeBlocks(forallOp.getBody(),
1758 bbArgs.front().getParentBlock(), ivsBlockArgs);
1759 });
1760
1761 // After `mergeBlocks` happened, the destinations in the terminator may be
1762 // mapped to tensor.cast values wrapping the new output bbArgs (introduced
1763 // for indices in `tensorCastProducers`). Update those destinations to
1764 // point directly to the output bbArgs, bypassing the casts.
1765 //
1766 // Note: we cannot zip yieldingOps with regionIterArgs by position because
1767 // a parallel_insert_slice inside in_parallel may write to any shared
1768 // output, not necessarily the one at the same position.
1769 llvm::SmallDenseSet<Value> newIterArgSet(
1770 newForallOp.getRegionIterArgs().begin(),
1771 newForallOp.getRegionIterArgs().end());
1772 auto terminator = newForallOp.getTerminator();
1773 for (auto &yieldingOp : terminator.getYieldingOps()) {
1774 auto parallelCombiningOp =
1775 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1776 if (!parallelCombiningOp)
1777 continue;
1778 for (OpOperand &dest : parallelCombiningOp.getUpdatedDestinations()) {
1779 auto castOp = dest.get().getDefiningOp<tensor::CastOp>();
1780 if (castOp && newIterArgSet.contains(castOp.getSource()))
1781 dest.set(castOp.getSource());
1782 }
1783 }
1784
1785 // Cast results back to the original types.
1786 rewriter.setInsertionPointAfter(newForallOp);
1787 SmallVector<Value> castResults = newForallOp.getResults();
1788 for (auto &item : tensorCastProducers) {
1789 Value &oldTypeResult = castResults[item.first];
1790 oldTypeResult = tensor::CastOp::create(rewriter, loc, item.second.dstType,
1791 oldTypeResult);
1792 }
1793 rewriter.replaceOp(forallOp, castResults);
1794 return success();
1795 }
1796};
1797
1798} // namespace
1799
1800void ForallOp::getCanonicalizationPatterns(RewritePatternSet &results,
1801 MLIRContext *context) {
1802 results.add<DimOfForallOp, FoldTensorCastOfOutputIntoForallOp,
1803 ForallOpControlOperandsFolder, ForallOpIterArgsFolder,
1804 ForallOpSingleOrZeroIterationDimsFolder,
1805 ForallOpReplaceConstantInductionVar>(context);
1806}
1807
1808void ForallOp::getSuccessorRegions(RegionBranchPoint point,
1809 SmallVectorImpl<RegionSuccessor> &regions) {
1810 // There are two region branch points:
1811 // 1. "parent": entering the forall op for the first time.
1812 // 2. scf.in_parallel terminator
1813 if (point.isParent()) {
1814 // When first entering the forall op, the control flow typically branches
1815 // into the forall body. (In parallel for multiple threads.)
1816 regions.push_back(RegionSuccessor(&getRegion()));
1817 // However, when there are 0 threads, the control flow may branch back to
1818 // the parent immediately.
1819 regions.push_back(RegionSuccessor(getOperation()));
1820 } else {
1821 // In accordance with the semantics of forall, its body is executed in
1822 // parallel by multiple threads. We should not expect to branch back into
1823 // the forall body after the region's execution is complete.
1824 regions.push_back(RegionSuccessor(getOperation()));
1825 }
1826}
1827
1828//===----------------------------------------------------------------------===//
1829// InParallelOp
1830//===----------------------------------------------------------------------===//
1831
1832// Build a InParallelOp with mixed static and dynamic entries.
1833void InParallelOp::build(OpBuilder &b, OperationState &result) {
1834 OpBuilder::InsertionGuard g(b);
1835 Region *bodyRegion = result.addRegion();
1836 b.createBlock(bodyRegion);
1837}
1838
1839LogicalResult InParallelOp::verify() {
1840 scf::ForallOp forallOp =
1841 dyn_cast<scf::ForallOp>(getOperation()->getParentOp());
1842 if (!forallOp)
1843 return this->emitOpError("expected forall op parent");
1844
1845 for (Operation &op : getRegion().front().getOperations()) {
1846 auto parallelCombiningOp = dyn_cast<ParallelCombiningOpInterface>(&op);
1847 if (!parallelCombiningOp) {
1848 return this->emitOpError("expected only ParallelCombiningOpInterface")
1849 << " ops";
1850 }
1851
1852 // Verify that inserts are into out block arguments.
1853 MutableOperandRange dests = parallelCombiningOp.getUpdatedDestinations();
1854 ArrayRef<BlockArgument> regionOutArgs = forallOp.getRegionOutArgs();
1855 for (OpOperand &dest : dests) {
1856 if (!llvm::is_contained(regionOutArgs, dest.get()))
1857 return op.emitOpError("may only insert into an output block argument");
1858 }
1859 }
1860
1861 return success();
1862}
1863
1864void InParallelOp::print(OpAsmPrinter &p) {
1865 p << " ";
1866 p.printRegion(getRegion(),
1867 /*printEntryBlockArgs=*/false,
1868 /*printBlockTerminators=*/false);
1870 getOperation()->getDiscardableAttrDictionary().getValue());
1871}
1872
1873ParseResult InParallelOp::parse(OpAsmParser &parser, OperationState &result) {
1874 auto &builder = parser.getBuilder();
1875
1876 SmallVector<OpAsmParser::Argument, 8> regionOperands;
1877 std::unique_ptr<Region> region = std::make_unique<Region>();
1878 if (parser.parseRegion(*region, regionOperands))
1879 return failure();
1880
1881 if (region->empty())
1882 OpBuilder(builder.getContext()).createBlock(region.get());
1883 result.addRegion(std::move(region));
1884
1885 // Parse the optional attribute list.
1886 if (parser.parseOptionalAttrDict(result.attributes))
1887 return failure();
1888 return success();
1889}
1890
1891OpResult InParallelOp::getParentResult(int64_t idx) {
1892 return getOperation()->getParentOp()->getResult(idx);
1893}
1894
1895SmallVector<BlockArgument> InParallelOp::getDests() {
1896 SmallVector<BlockArgument> updatedDests;
1897 for (Operation &yieldingOp : getYieldingOps()) {
1898 auto parallelCombiningOp =
1899 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1900 if (!parallelCombiningOp)
1901 continue;
1902 for (OpOperand &updatedOperand :
1903 parallelCombiningOp.getUpdatedDestinations())
1904 updatedDests.push_back(cast<BlockArgument>(updatedOperand.get()));
1905 }
1906 return updatedDests;
1907}
1908
1909llvm::iterator_range<Block::iterator> InParallelOp::getYieldingOps() {
1910 return getRegion().front().getOperations();
1911}
1912
1913//===----------------------------------------------------------------------===//
1914// IfOp
1915//===----------------------------------------------------------------------===//
1916
1918 assert(a && "expected non-empty operation");
1919 assert(b && "expected non-empty operation");
1920
1921 IfOp ifOp = a->getParentOfType<IfOp>();
1922 while (ifOp) {
1923 // Check if b is inside ifOp. (We already know that a is.)
1924 if (ifOp->isProperAncestor(b))
1925 // b is contained in ifOp. a and b are in mutually exclusive branches if
1926 // they are in different blocks of ifOp.
1927 return static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*a)) !=
1928 static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*b));
1929 // Check next enclosing IfOp.
1930 ifOp = ifOp->getParentOfType<IfOp>();
1931 }
1932
1933 // Could not find a common IfOp among a's and b's ancestors.
1934 return false;
1935}
1936
1937LogicalResult
1938IfOp::inferReturnTypes(MLIRContext *ctx, std::optional<Location> loc,
1939 IfOp::Adaptor adaptor,
1940 SmallVectorImpl<Type> &inferredReturnTypes) {
1941 if (adaptor.getRegions().empty())
1942 return failure();
1943 Region *r = &adaptor.getThenRegion();
1944 if (r->empty())
1945 return failure();
1946 Block &b = r->front();
1947 if (b.empty())
1948 return failure();
1949 auto yieldOp = llvm::dyn_cast<YieldOp>(b.back());
1950 if (!yieldOp)
1951 return failure();
1952 TypeRange types = yieldOp.getOperandTypes();
1953 llvm::append_range(inferredReturnTypes, types);
1954 return success();
1955}
1956
1957void IfOp::build(OpBuilder &builder, OperationState &result,
1958 TypeRange resultTypes, Value cond) {
1959 return build(builder, result, resultTypes, cond, /*addThenBlock=*/false,
1960 /*addElseBlock=*/false);
1961}
1962
1963void IfOp::build(OpBuilder &builder, OperationState &result,
1964 TypeRange resultTypes, Value cond, bool addThenBlock,
1965 bool addElseBlock) {
1966 assert((!addElseBlock || addThenBlock) &&
1967 "must not create else block w/o then block");
1968 result.addTypes(resultTypes);
1969 result.addOperands(cond);
1970
1971 // Add regions and blocks.
1972 OpBuilder::InsertionGuard guard(builder);
1973 Region *thenRegion = result.addRegion();
1974 if (addThenBlock)
1975 builder.createBlock(thenRegion);
1976 Region *elseRegion = result.addRegion();
1977 if (addElseBlock)
1978 builder.createBlock(elseRegion);
1979}
1980
1981void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
1982 bool withElseRegion) {
1983 build(builder, result, TypeRange{}, cond, withElseRegion);
1984}
1985
1986void IfOp::build(OpBuilder &builder, OperationState &result,
1987 TypeRange resultTypes, Value cond, bool withElseRegion) {
1988 result.addTypes(resultTypes);
1989 result.addOperands(cond);
1990
1991 // Build then region.
1992 OpBuilder::InsertionGuard guard(builder);
1993 Region *thenRegion = result.addRegion();
1994 builder.createBlock(thenRegion);
1995 if (resultTypes.empty())
1996 IfOp::ensureTerminator(*thenRegion, builder, result.location);
1997
1998 // Build else region.
1999 Region *elseRegion = result.addRegion();
2000 if (withElseRegion) {
2001 builder.createBlock(elseRegion);
2002 if (resultTypes.empty())
2003 IfOp::ensureTerminator(*elseRegion, builder, result.location);
2004 }
2005}
2006
2007void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
2008 function_ref<void(OpBuilder &, Location)> thenBuilder,
2009 function_ref<void(OpBuilder &, Location)> elseBuilder) {
2010 assert(thenBuilder && "the builder callback for 'then' must be present");
2011 result.addOperands(cond);
2012
2013 // Build then region.
2014 OpBuilder::InsertionGuard guard(builder);
2015 Region *thenRegion = result.addRegion();
2016 builder.createBlock(thenRegion);
2017 thenBuilder(builder, result.location);
2018
2019 // Build else region.
2020 Region *elseRegion = result.addRegion();
2021 if (elseBuilder) {
2022 builder.createBlock(elseRegion);
2023 elseBuilder(builder, result.location);
2024 }
2025
2026 // Infer result types.
2027 SmallVector<Type> inferredReturnTypes;
2028 MLIRContext *ctx = builder.getContext();
2029 auto attrDict = DictionaryAttr::get(ctx, result.attributes);
2030 if (succeeded(inferReturnTypes(ctx, std::nullopt, result.operands, attrDict,
2031 /*properties=*/PropertyRef{}, result.regions,
2032 inferredReturnTypes))) {
2033 result.addTypes(inferredReturnTypes);
2034 }
2035}
2036
2037LogicalResult IfOp::verify() {
2038 if (getNumResults() != 0 && getElseRegion().empty())
2039 return emitOpError("must have an else block if defining values");
2040 return success();
2041}
2042
2043ParseResult IfOp::parse(OpAsmParser &parser, OperationState &result) {
2044 // Create the regions for 'then'.
2045 result.regions.reserve(2);
2046 Region *thenRegion = result.addRegion();
2047 Region *elseRegion = result.addRegion();
2048
2049 auto &builder = parser.getBuilder();
2050 OpAsmParser::UnresolvedOperand cond;
2051 Type i1Type = builder.getIntegerType(1);
2052 if (parser.parseOperand(cond) ||
2053 parser.resolveOperand(cond, i1Type, result.operands))
2054 return failure();
2055 // Parse optional results type list.
2056 if (parser.parseOptionalArrowTypeList(result.types))
2057 return failure();
2058 // Parse the 'then' region.
2059 if (parser.parseRegion(*thenRegion, /*arguments=*/{}, /*argTypes=*/{}))
2060 return failure();
2061 IfOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location);
2062
2063 // If we find an 'else' keyword then parse the 'else' region.
2064 if (!parser.parseOptionalKeyword("else")) {
2065 if (parser.parseRegion(*elseRegion, /*arguments=*/{}, /*argTypes=*/{}))
2066 return failure();
2067 IfOp::ensureTerminator(*elseRegion, parser.getBuilder(), result.location);
2068 }
2069
2070 // Parse the optional attribute list.
2071 if (parser.parseOptionalAttrDict(result.attributes))
2072 return failure();
2073 return success();
2074}
2075
2076void IfOp::print(OpAsmPrinter &p) {
2077 bool printBlockTerminators = false;
2078
2079 p << " " << getCondition();
2080 if (!getResults().empty()) {
2081 p << " -> (" << getResultTypes() << ")";
2082 // Print yield explicitly if the op defines values.
2083 printBlockTerminators = true;
2084 }
2085 p << ' ';
2086 p.printRegion(getThenRegion(),
2087 /*printEntryBlockArgs=*/false,
2088 /*printBlockTerminators=*/printBlockTerminators);
2089
2090 // Print the 'else' regions if it exists and has a block.
2091 auto &elseRegion = getElseRegion();
2092 if (!elseRegion.empty()) {
2093 p << " else ";
2094 p.printRegion(elseRegion,
2095 /*printEntryBlockArgs=*/false,
2096 /*printBlockTerminators=*/printBlockTerminators);
2097 }
2098
2099 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
2100}
2101
2102void IfOp::getSuccessorRegions(RegionBranchPoint point,
2103 SmallVectorImpl<RegionSuccessor> &regions) {
2104 // The `then` and the `else` region branch back to the parent operation or one
2105 // of the recursive parent operations (early exit case).
2106 if (!point.isParent()) {
2107 regions.push_back(RegionSuccessor(getOperation()));
2108 return;
2109 }
2110
2111 regions.push_back(RegionSuccessor(&getThenRegion()));
2112
2113 // Don't consider the else region if it is empty.
2114 Region *elseRegion = &this->getElseRegion();
2115 if (elseRegion->empty())
2116 regions.push_back(RegionSuccessor(getOperation()));
2117 else
2118 regions.push_back(RegionSuccessor(elseRegion));
2119}
2120
2121ValueRange IfOp::getSuccessorInputs(RegionSuccessor successor) {
2122 return successor.isOperation() ? ValueRange(getOperation()->getResults())
2123 : ValueRange();
2124}
2125
2126void IfOp::getEntrySuccessorRegions(ArrayRef<Attribute> operands,
2127 SmallVectorImpl<RegionSuccessor> &regions) {
2128 FoldAdaptor adaptor(operands, *this);
2129 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
2130 if (!boolAttr || boolAttr.getValue())
2131 regions.emplace_back(&getThenRegion());
2132
2133 // If the else region is empty, execution continues after the parent op.
2134 if (!boolAttr || !boolAttr.getValue()) {
2135 if (!getElseRegion().empty())
2136 regions.emplace_back(&getElseRegion());
2137 else
2138 regions.emplace_back(RegionSuccessor(getOperation()));
2139 }
2140}
2141
2142LogicalResult IfOp::fold(FoldAdaptor adaptor,
2143 SmallVectorImpl<OpFoldResult> &results) {
2144 // if (!c) then A() else B() -> if c then B() else A()
2145 if (getElseRegion().empty())
2146 return failure();
2147
2148 arith::XOrIOp xorStmt = getCondition().getDefiningOp<arith::XOrIOp>();
2149 if (!xorStmt)
2150 return failure();
2151
2152 if (!matchPattern(xorStmt.getRhs(), m_One()))
2153 return failure();
2154
2155 getConditionMutable().assign(xorStmt.getLhs());
2156 Block *thenBlock = &getThenRegion().front();
2157 // It would be nicer to use iplist::swap, but that has no implemented
2158 // callbacks See: https://llvm.org/doxygen/ilist_8h_source.html#l00224
2159 getThenRegion().getBlocks().splice(getThenRegion().getBlocks().begin(),
2160 getElseRegion().getBlocks());
2161 getElseRegion().getBlocks().splice(getElseRegion().getBlocks().begin(),
2162 getThenRegion().getBlocks(), thenBlock);
2163 return success();
2164}
2165
2166void IfOp::getRegionInvocationBounds(
2167 ArrayRef<Attribute> operands,
2168 SmallVectorImpl<InvocationBounds> &invocationBounds) {
2169 if (auto cond = llvm::dyn_cast_or_null<BoolAttr>(operands[0])) {
2170 // If the condition is known, then one region is known to be executed once
2171 // and the other zero times.
2172 invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
2173 invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
2174 } else {
2175 // Non-constant condition. Each region may be executed 0 or 1 times.
2176 invocationBounds.assign(2, {0, 1});
2177 }
2178}
2179
2180namespace {
2181/// Hoist any yielded results whose operands are defined outside
2182/// the if, to a select instruction.
2183struct ConvertTrivialIfToSelect : public OpRewritePattern<IfOp> {
2184 using OpRewritePattern<IfOp>::OpRewritePattern;
2185
2186 LogicalResult matchAndRewrite(IfOp op,
2187 PatternRewriter &rewriter) const override {
2188 if (op->getNumResults() == 0)
2189 return failure();
2190
2191 auto cond = op.getCondition();
2192 auto thenYieldArgs = op.thenYield().getOperands();
2193 auto elseYieldArgs = op.elseYield().getOperands();
2194
2195 SmallVector<Type> nonHoistable;
2196 for (auto [trueVal, falseVal] : llvm::zip(thenYieldArgs, elseYieldArgs)) {
2197 if (&op.getThenRegion() == trueVal.getParentRegion() ||
2198 &op.getElseRegion() == falseVal.getParentRegion())
2199 nonHoistable.push_back(trueVal.getType());
2200 }
2201 // Early exit if there aren't any yielded values we can
2202 // hoist outside the if.
2203 if (nonHoistable.size() == op->getNumResults())
2204 return failure();
2205
2206 IfOp replacement = IfOp::create(rewriter, op.getLoc(), nonHoistable, cond,
2207 /*withElseRegion=*/false);
2208 if (replacement.thenBlock())
2209 rewriter.eraseBlock(replacement.thenBlock());
2210 replacement.getThenRegion().takeBody(op.getThenRegion());
2211 replacement.getElseRegion().takeBody(op.getElseRegion());
2212
2213 SmallVector<Value> results(op->getNumResults());
2214 assert(thenYieldArgs.size() == results.size());
2215 assert(elseYieldArgs.size() == results.size());
2216
2217 SmallVector<Value> trueYields;
2218 SmallVector<Value> falseYields;
2220 for (const auto &it :
2221 llvm::enumerate(llvm::zip(thenYieldArgs, elseYieldArgs))) {
2222 Value trueVal = std::get<0>(it.value());
2223 Value falseVal = std::get<1>(it.value());
2224 if (&replacement.getThenRegion() == trueVal.getParentRegion() ||
2225 &replacement.getElseRegion() == falseVal.getParentRegion()) {
2226 results[it.index()] = replacement.getResult(trueYields.size());
2227 trueYields.push_back(trueVal);
2228 falseYields.push_back(falseVal);
2229 } else if (trueVal == falseVal)
2230 results[it.index()] = trueVal;
2231 else
2232 results[it.index()] = arith::SelectOp::create(rewriter, op.getLoc(),
2233 cond, trueVal, falseVal);
2234 }
2235
2236 rewriter.setInsertionPointToEnd(replacement.thenBlock());
2237 rewriter.replaceOpWithNewOp<YieldOp>(replacement.thenYield(), trueYields);
2238
2239 rewriter.setInsertionPointToEnd(replacement.elseBlock());
2240 rewriter.replaceOpWithNewOp<YieldOp>(replacement.elseYield(), falseYields);
2241
2242 rewriter.replaceOp(op, results);
2243 return success();
2244 }
2245};
2246
2247/// Allow the true region of an if to assume the condition is true
2248/// and vice versa. For example:
2249///
2250/// scf.if %cmp {
2251/// print(%cmp)
2252/// }
2253///
2254/// becomes
2255///
2256/// scf.if %cmp {
2257/// print(true)
2258/// }
2259///
2260struct ConditionPropagation : public OpRewritePattern<IfOp> {
2261 using OpRewritePattern<IfOp>::OpRewritePattern;
2262
2263 /// Kind of parent region in the ancestor cache.
2264 enum class Parent { Then, Else, None };
2265
2266 /// Returns the kind of region ("then", "else", or "none") of the
2267 /// IfOp that the given region is transitively nested in. Updates
2268 /// the cache accordingly.
2269 static Parent getParentType(Region *toCheck, IfOp op,
2271 Region *endRegion) {
2272 SmallVector<Region *> seen;
2273 while (toCheck != endRegion) {
2274 auto found = cache.find(toCheck);
2275 if (found != cache.end())
2276 return found->second;
2277 seen.push_back(toCheck);
2278 if (&op.getThenRegion() == toCheck) {
2279 for (Region *region : seen)
2280 cache[region] = Parent::Then;
2281 return Parent::Then;
2282 }
2283 if (&op.getElseRegion() == toCheck) {
2284 for (Region *region : seen)
2285 cache[region] = Parent::Else;
2286 return Parent::Else;
2287 }
2288 toCheck = toCheck->getParentRegion();
2289 }
2290
2291 for (Region *region : seen)
2292 cache[region] = Parent::None;
2293 return Parent::None;
2294 }
2295
2296 LogicalResult matchAndRewrite(IfOp op,
2297 PatternRewriter &rewriter) const override {
2298 // Early exit if the condition is constant since replacing a constant
2299 // in the body with another constant isn't a simplification.
2300 if (matchPattern(op.getCondition(), m_Constant()))
2301 return failure();
2302
2303 bool changed = false;
2304 mlir::Type i1Ty = rewriter.getI1Type();
2305
2306 // These variables serve to prevent creating duplicate constants
2307 // and hold constant true or false values.
2308 Value constantTrue = nullptr;
2309 Value constantFalse = nullptr;
2310
2312 for (OpOperand &use :
2313 llvm::make_early_inc_range(op.getCondition().getUses())) {
2314 switch (getParentType(use.getOwner()->getParentRegion(), op, cache,
2315 op.getCondition().getParentRegion())) {
2316 case Parent::Then: {
2317 changed = true;
2318
2319 if (!constantTrue)
2320 constantTrue = arith::ConstantOp::create(
2321 rewriter, op.getLoc(), i1Ty, rewriter.getIntegerAttr(i1Ty, 1));
2322
2323 rewriter.modifyOpInPlace(use.getOwner(),
2324 [&]() { use.set(constantTrue); });
2325 break;
2326 }
2327 case Parent::Else: {
2328 changed = true;
2329
2330 if (!constantFalse)
2331 constantFalse = arith::ConstantOp::create(
2332 rewriter, op.getLoc(), i1Ty, rewriter.getIntegerAttr(i1Ty, 0));
2333
2334 rewriter.modifyOpInPlace(use.getOwner(),
2335 [&]() { use.set(constantFalse); });
2336 break;
2337 }
2338 case Parent::None:
2339 break;
2340 }
2341 }
2342
2343 return success(changed);
2344 }
2345};
2346
2347/// Remove any statements from an if that are equivalent to the condition
2348/// or its negation. For example:
2349///
2350/// %res:2 = scf.if %cmp {
2351/// yield something(), true
2352/// } else {
2353/// yield something2(), false
2354/// }
2355/// print(%res#1)
2356///
2357/// becomes
2358/// %res = scf.if %cmp {
2359/// yield something()
2360/// } else {
2361/// yield something2()
2362/// }
2363/// print(%cmp)
2364///
2365/// Additionally if both branches yield the same value, replace all uses
2366/// of the result with the yielded value.
2367///
2368/// %res:2 = scf.if %cmp {
2369/// yield something(), %arg1
2370/// } else {
2371/// yield something2(), %arg1
2372/// }
2373/// print(%res#1)
2374///
2375/// becomes
2376/// %res = scf.if %cmp {
2377/// yield something()
2378/// } else {
2379/// yield something2()
2380/// }
2381/// print(%arg1)
2382///
2383struct ReplaceIfYieldWithConditionOrValue : public OpRewritePattern<IfOp> {
2384 using OpRewritePattern<IfOp>::OpRewritePattern;
2385
2386 LogicalResult matchAndRewrite(IfOp op,
2387 PatternRewriter &rewriter) const override {
2388 // Early exit if there are no results that could be replaced.
2389 if (op.getNumResults() == 0)
2390 return failure();
2391
2392 auto trueYield =
2393 cast<scf::YieldOp>(op.getThenRegion().back().getTerminator());
2394 auto falseYield =
2395 cast<scf::YieldOp>(op.getElseRegion().back().getTerminator());
2396
2397 rewriter.setInsertionPoint(op->getBlock(),
2398 op.getOperation()->getIterator());
2399 bool changed = false;
2400 Type i1Ty = rewriter.getI1Type();
2401 for (auto [trueResult, falseResult, opResult] :
2402 llvm::zip(trueYield.getResults(), falseYield.getResults(),
2403 op.getResults())) {
2404 if (trueResult == falseResult) {
2405 if (!opResult.use_empty()) {
2406 opResult.replaceAllUsesWith(trueResult);
2407 changed = true;
2408 }
2409 continue;
2410 }
2411
2412 BoolAttr trueYield, falseYield;
2413 if (!matchPattern(trueResult, m_Constant(&trueYield)) ||
2414 !matchPattern(falseResult, m_Constant(&falseYield)))
2415 continue;
2416
2417 bool trueVal = trueYield.getValue();
2418 bool falseVal = falseYield.getValue();
2419 if (!trueVal && falseVal) {
2420 if (!opResult.use_empty()) {
2421 Dialect *constDialect = trueResult.getDefiningOp()->getDialect();
2422 Value notCond = arith::XOrIOp::create(
2423 rewriter, op.getLoc(), op.getCondition(),
2424 constDialect
2425 ->materializeConstant(rewriter,
2426 rewriter.getIntegerAttr(i1Ty, 1), i1Ty,
2427 op.getLoc())
2428 ->getResult(0));
2429 opResult.replaceAllUsesWith(notCond);
2430 changed = true;
2431 }
2432 }
2433 if (trueVal && !falseVal) {
2434 if (!opResult.use_empty()) {
2435 opResult.replaceAllUsesWith(op.getCondition());
2436 changed = true;
2437 }
2438 }
2439 }
2440 return success(changed);
2441 }
2442};
2443
2444/// Merge any consecutive scf.if's with the same condition.
2445///
2446/// scf.if %cond {
2447/// firstCodeTrue();...
2448/// } else {
2449/// firstCodeFalse();...
2450/// }
2451/// %res = scf.if %cond {
2452/// secondCodeTrue();...
2453/// } else {
2454/// secondCodeFalse();...
2455/// }
2456///
2457/// becomes
2458/// %res = scf.if %cmp {
2459/// firstCodeTrue();...
2460/// secondCodeTrue();...
2461/// } else {
2462/// firstCodeFalse();...
2463/// secondCodeFalse();...
2464/// }
2465struct CombineIfs : public OpRewritePattern<IfOp> {
2466 using OpRewritePattern<IfOp>::OpRewritePattern;
2467
2468 LogicalResult matchAndRewrite(IfOp nextIf,
2469 PatternRewriter &rewriter) const override {
2470 Block *parent = nextIf->getBlock();
2471 if (nextIf == &parent->front())
2472 return failure();
2473
2474 auto prevIf = dyn_cast<IfOp>(nextIf->getPrevNode());
2475 if (!prevIf)
2476 return failure();
2477
2478 // Determine the logical then/else blocks when prevIf's
2479 // condition is used. Null means the block does not exist
2480 // in that case (e.g. empty else). If neither of these
2481 // are set, the two conditions cannot be compared.
2482 Block *nextThen = nullptr;
2483 Block *nextElse = nullptr;
2484 if (nextIf.getCondition() == prevIf.getCondition()) {
2485 nextThen = nextIf.thenBlock();
2486 if (!nextIf.getElseRegion().empty())
2487 nextElse = nextIf.elseBlock();
2488 }
2489 if (arith::XOrIOp notv =
2490 nextIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2491 if (notv.getLhs() == prevIf.getCondition() &&
2492 matchPattern(notv.getRhs(), m_One())) {
2493 nextElse = nextIf.thenBlock();
2494 if (!nextIf.getElseRegion().empty())
2495 nextThen = nextIf.elseBlock();
2496 }
2497 }
2498 if (arith::XOrIOp notv =
2499 prevIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2500 if (notv.getLhs() == nextIf.getCondition() &&
2501 matchPattern(notv.getRhs(), m_One())) {
2502 nextElse = nextIf.thenBlock();
2503 if (!nextIf.getElseRegion().empty())
2504 nextThen = nextIf.elseBlock();
2505 }
2506 }
2507
2508 if (!nextThen && !nextElse)
2509 return failure();
2510
2511 SmallVector<Value> prevElseYielded;
2512 if (!prevIf.getElseRegion().empty())
2513 prevElseYielded = prevIf.elseYield().getOperands();
2514 // Replace all uses of return values of op within nextIf with the
2515 // corresponding yields
2516 for (auto it : llvm::zip(prevIf.getResults(),
2517 prevIf.thenYield().getOperands(), prevElseYielded))
2518 for (OpOperand &use :
2519 llvm::make_early_inc_range(std::get<0>(it).getUses())) {
2520 if (nextThen && nextThen->getParent()->isAncestor(
2521 use.getOwner()->getParentRegion())) {
2522 rewriter.startOpModification(use.getOwner());
2523 use.set(std::get<1>(it));
2524 rewriter.finalizeOpModification(use.getOwner());
2525 } else if (nextElse && nextElse->getParent()->isAncestor(
2526 use.getOwner()->getParentRegion())) {
2527 rewriter.startOpModification(use.getOwner());
2528 use.set(std::get<2>(it));
2529 rewriter.finalizeOpModification(use.getOwner());
2530 }
2531 }
2532
2533 SmallVector<Type> mergedTypes(prevIf.getResultTypes());
2534 llvm::append_range(mergedTypes, nextIf.getResultTypes());
2535
2536 IfOp combinedIf = IfOp::create(rewriter, nextIf.getLoc(), mergedTypes,
2537 prevIf.getCondition(), /*hasElse=*/false);
2538 rewriter.eraseBlock(&combinedIf.getThenRegion().back());
2539
2540 rewriter.inlineRegionBefore(prevIf.getThenRegion(),
2541 combinedIf.getThenRegion(),
2542 combinedIf.getThenRegion().begin());
2543
2544 if (nextThen) {
2545 YieldOp thenYield = combinedIf.thenYield();
2546 YieldOp thenYield2 = cast<YieldOp>(nextThen->getTerminator());
2547 rewriter.mergeBlocks(nextThen, combinedIf.thenBlock());
2548 rewriter.setInsertionPointToEnd(combinedIf.thenBlock());
2549
2550 SmallVector<Value> mergedYields(thenYield.getOperands());
2551 llvm::append_range(mergedYields, thenYield2.getOperands());
2552 YieldOp::create(rewriter, thenYield2.getLoc(), mergedYields);
2553 rewriter.eraseOp(thenYield);
2554 rewriter.eraseOp(thenYield2);
2555 }
2556
2557 rewriter.inlineRegionBefore(prevIf.getElseRegion(),
2558 combinedIf.getElseRegion(),
2559 combinedIf.getElseRegion().begin());
2560
2561 if (nextElse) {
2562 if (combinedIf.getElseRegion().empty()) {
2563 rewriter.inlineRegionBefore(*nextElse->getParent(),
2564 combinedIf.getElseRegion(),
2565 combinedIf.getElseRegion().begin());
2566 } else {
2567 YieldOp elseYield = combinedIf.elseYield();
2568 YieldOp elseYield2 = cast<YieldOp>(nextElse->getTerminator());
2569 rewriter.mergeBlocks(nextElse, combinedIf.elseBlock());
2570
2571 rewriter.setInsertionPointToEnd(combinedIf.elseBlock());
2572
2573 SmallVector<Value> mergedElseYields(elseYield.getOperands());
2574 llvm::append_range(mergedElseYields, elseYield2.getOperands());
2575
2576 YieldOp::create(rewriter, elseYield2.getLoc(), mergedElseYields);
2577 rewriter.eraseOp(elseYield);
2578 rewriter.eraseOp(elseYield2);
2579 }
2580 }
2581
2582 SmallVector<Value> prevValues;
2583 SmallVector<Value> nextValues;
2584 for (const auto &pair : llvm::enumerate(combinedIf.getResults())) {
2585 if (pair.index() < prevIf.getNumResults())
2586 prevValues.push_back(pair.value());
2587 else
2588 nextValues.push_back(pair.value());
2589 }
2590 rewriter.replaceOp(prevIf, prevValues);
2591 rewriter.replaceOp(nextIf, nextValues);
2592 return success();
2593 }
2594};
2595
2596/// Pattern to remove an empty else branch.
2597struct RemoveEmptyElseBranch : public OpRewritePattern<IfOp> {
2598 using OpRewritePattern<IfOp>::OpRewritePattern;
2599
2600 LogicalResult matchAndRewrite(IfOp ifOp,
2601 PatternRewriter &rewriter) const override {
2602 // Cannot remove else region when there are operation results.
2603 if (ifOp.getNumResults())
2604 return failure();
2605 Block *elseBlock = ifOp.elseBlock();
2606 if (!elseBlock || !llvm::hasSingleElement(*elseBlock))
2607 return failure();
2608 auto newIfOp = rewriter.cloneWithoutRegions(ifOp);
2609 rewriter.inlineRegionBefore(ifOp.getThenRegion(), newIfOp.getThenRegion(),
2610 newIfOp.getThenRegion().begin());
2611 rewriter.eraseOp(ifOp);
2612 return success();
2613 }
2614};
2615
2616/// Convert nested `if`s into `arith.andi` + single `if`.
2617///
2618/// scf.if %arg0 {
2619/// scf.if %arg1 {
2620/// ...
2621/// scf.yield
2622/// }
2623/// scf.yield
2624/// }
2625/// becomes
2626///
2627/// %0 = arith.andi %arg0, %arg1
2628/// scf.if %0 {
2629/// ...
2630/// scf.yield
2631/// }
2632struct CombineNestedIfs : public OpRewritePattern<IfOp> {
2633 using OpRewritePattern<IfOp>::OpRewritePattern;
2634
2635 LogicalResult matchAndRewrite(IfOp op,
2636 PatternRewriter &rewriter) const override {
2637 auto nestedOps = op.thenBlock()->without_terminator();
2638 // Nested `if` must be the only op in block.
2639 if (!llvm::hasSingleElement(nestedOps))
2640 return failure();
2641
2642 // If there is an else block, it can only yield
2643 if (op.elseBlock() && !llvm::hasSingleElement(*op.elseBlock()))
2644 return failure();
2645
2646 auto nestedIf = dyn_cast<IfOp>(*nestedOps.begin());
2647 if (!nestedIf)
2648 return failure();
2649
2650 if (nestedIf.elseBlock() && !llvm::hasSingleElement(*nestedIf.elseBlock()))
2651 return failure();
2652
2653 SmallVector<Value> thenYield(op.thenYield().getOperands());
2654 SmallVector<Value> elseYield;
2655 if (op.elseBlock())
2656 llvm::append_range(elseYield, op.elseYield().getOperands());
2657
2658 // A list of indices for which we should upgrade the value yielded
2659 // in the else to a select.
2660 SmallVector<unsigned> elseYieldsToUpgradeToSelect;
2661
2662 // If the outer scf.if yields a value produced by the inner scf.if,
2663 // only permit combining if the value yielded when the condition
2664 // is false in the outer scf.if is the same value yielded when the
2665 // inner scf.if condition is false.
2666 // Note that the array access to elseYield will not go out of bounds
2667 // since it must have the same length as thenYield, since they both
2668 // come from the same scf.if.
2669 for (const auto &tup : llvm::enumerate(thenYield)) {
2670 if (tup.value().getDefiningOp() == nestedIf) {
2671 auto nestedIdx = llvm::cast<OpResult>(tup.value()).getResultNumber();
2672 if (nestedIf.elseYield().getOperand(nestedIdx) !=
2673 elseYield[tup.index()]) {
2674 return failure();
2675 }
2676 // If the correctness test passes, we will yield
2677 // corresponding value from the inner scf.if
2678 thenYield[tup.index()] = nestedIf.thenYield().getOperand(nestedIdx);
2679 continue;
2680 }
2681
2682 // Otherwise, we need to ensure the else block of the combined
2683 // condition still returns the same value when the outer condition is
2684 // true and the inner condition is false. This can be accomplished if
2685 // the then value is defined outside the outer scf.if and we replace the
2686 // value with a select that considers just the outer condition. Since
2687 // the else region contains just the yield, its yielded value is
2688 // defined outside the scf.if, by definition.
2689
2690 // If the then value is defined within the scf.if, bail.
2691 if (tup.value().getParentRegion() == &op.getThenRegion()) {
2692 return failure();
2693 }
2694 elseYieldsToUpgradeToSelect.push_back(tup.index());
2695 }
2696
2697 Location loc = op.getLoc();
2698 Value newCondition = arith::AndIOp::create(rewriter, loc, op.getCondition(),
2699 nestedIf.getCondition());
2700 auto newIf = IfOp::create(rewriter, loc, op.getResultTypes(), newCondition);
2701 Block *newIfBlock = rewriter.createBlock(&newIf.getThenRegion());
2702
2703 SmallVector<Value> results;
2704 llvm::append_range(results, newIf.getResults());
2705 rewriter.setInsertionPoint(newIf);
2706
2707 for (auto idx : elseYieldsToUpgradeToSelect)
2708 results[idx] =
2709 arith::SelectOp::create(rewriter, op.getLoc(), op.getCondition(),
2710 thenYield[idx], elseYield[idx]);
2711
2712 rewriter.mergeBlocks(nestedIf.thenBlock(), newIfBlock);
2713 rewriter.setInsertionPointToEnd(newIf.thenBlock());
2714 rewriter.replaceOpWithNewOp<YieldOp>(newIf.thenYield(), thenYield);
2715 if (!elseYield.empty()) {
2716 rewriter.createBlock(&newIf.getElseRegion());
2717 rewriter.setInsertionPointToEnd(newIf.elseBlock());
2718 YieldOp::create(rewriter, loc, elseYield);
2719 }
2720 rewriter.replaceOp(op, results);
2721 return success();
2722 }
2723};
2724
2725} // namespace
2726
2727void IfOp::getCanonicalizationPatterns(RewritePatternSet &results,
2728 MLIRContext *context) {
2729 results.add<CombineIfs, CombineNestedIfs, ConditionPropagation,
2730 ConvertTrivialIfToSelect, RemoveEmptyElseBranch,
2731 ReplaceIfYieldWithConditionOrValue>(context);
2733 results, IfOp::getOperationName());
2735 IfOp::getOperationName());
2736}
2737
2738Block *IfOp::thenBlock() { return &getThenRegion().back(); }
2739YieldOp IfOp::thenYield() { return cast<YieldOp>(&thenBlock()->back()); }
2740Block *IfOp::elseBlock() {
2741 Region &r = getElseRegion();
2742 if (r.empty())
2743 return nullptr;
2744 return &r.back();
2745}
2746YieldOp IfOp::elseYield() { return cast<YieldOp>(&elseBlock()->back()); }
2747
2748//===----------------------------------------------------------------------===//
2749// ParallelOp
2750//===----------------------------------------------------------------------===//
2751
2752void ParallelOp::build(
2753 OpBuilder &builder, OperationState &result, ValueRange lowerBounds,
2754 ValueRange upperBounds, ValueRange steps, ValueRange initVals,
2755 function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
2756 bodyBuilderFn) {
2757 result.addOperands(lowerBounds);
2758 result.addOperands(upperBounds);
2759 result.addOperands(steps);
2760 result.addOperands(initVals);
2761 result.addAttribute(
2762 ParallelOp::getOperandSegmentSizeAttr(),
2763 builder.getDenseI32ArrayAttr({static_cast<int32_t>(lowerBounds.size()),
2764 static_cast<int32_t>(upperBounds.size()),
2765 static_cast<int32_t>(steps.size()),
2766 static_cast<int32_t>(initVals.size())}));
2767 result.addTypes(initVals.getTypes());
2768
2769 OpBuilder::InsertionGuard guard(builder);
2770 unsigned numIVs = steps.size();
2771 SmallVector<Type, 8> argTypes(numIVs, builder.getIndexType());
2772 SmallVector<Location, 8> argLocs(numIVs, result.location);
2773 Region *bodyRegion = result.addRegion();
2774 Block *bodyBlock = builder.createBlock(bodyRegion, {}, argTypes, argLocs);
2775
2776 if (bodyBuilderFn) {
2777 builder.setInsertionPointToStart(bodyBlock);
2778 bodyBuilderFn(builder, result.location,
2779 bodyBlock->getArguments().take_front(numIVs),
2780 bodyBlock->getArguments().drop_front(numIVs));
2781 }
2782 // Add terminator only if there are no reductions.
2783 if (initVals.empty())
2784 ParallelOp::ensureTerminator(*bodyRegion, builder, result.location);
2785}
2786
2787void ParallelOp::build(
2788 OpBuilder &builder, OperationState &result, ValueRange lowerBounds,
2789 ValueRange upperBounds, ValueRange steps,
2790 function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {
2791 // Only pass a non-null wrapper if bodyBuilderFn is non-null itself. Make sure
2792 // we don't capture a reference to a temporary by constructing the lambda at
2793 // function level.
2794 auto wrappedBuilderFn = [&bodyBuilderFn](OpBuilder &nestedBuilder,
2795 Location nestedLoc, ValueRange ivs,
2796 ValueRange) {
2797 bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
2798 };
2799 function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)> wrapper;
2800 if (bodyBuilderFn)
2801 wrapper = wrappedBuilderFn;
2802
2803 build(builder, result, lowerBounds, upperBounds, steps, ValueRange(),
2804 wrapper);
2805}
2806
2807LogicalResult ParallelOp::verify() {
2808 // Check that there is at least one value in lowerBound, upperBound and step.
2809 // It is sufficient to test only step, because it is ensured already that the
2810 // number of elements in lowerBound, upperBound and step are the same.
2811 Operation::operand_range stepValues = getStep();
2812 if (stepValues.empty())
2813 return emitOpError(
2814 "needs at least one tuple element for lowerBound, upperBound and step");
2815
2816 // Check whether all constant step values are positive.
2817 for (Value stepValue : stepValues)
2818 if (auto cst = getConstantIntValue(stepValue))
2819 if (*cst <= 0)
2820 return emitOpError("constant step operand must be positive");
2821
2822 // Check that the body defines the same number of block arguments as the
2823 // number of tuple elements in step.
2824 Block *body = getBody();
2825 if (body->getNumArguments() != stepValues.size())
2826 return emitOpError() << "expects the same number of induction variables: "
2827 << body->getNumArguments()
2828 << " as bound and step values: " << stepValues.size();
2829 for (auto arg : body->getArguments())
2830 if (!arg.getType().isIndex())
2831 return emitOpError(
2832 "expects arguments for the induction variable to be of index type");
2833
2834 // Check that the terminator is an scf.reduce op.
2836 *this, getRegion(), "expects body to terminate with 'scf.reduce'");
2837 if (!reduceOp)
2838 return failure();
2839
2840 // Check that the number of results is the same as the number of reductions.
2841 auto resultsSize = getResults().size();
2842 auto reductionsSize = reduceOp.getReductions().size();
2843 auto initValsSize = getInitVals().size();
2844 if (resultsSize != reductionsSize)
2845 return emitOpError() << "expects number of results: " << resultsSize
2846 << " to be the same as number of reductions: "
2847 << reductionsSize;
2848 if (resultsSize != initValsSize)
2849 return emitOpError() << "expects number of results: " << resultsSize
2850 << " to be the same as number of initial values: "
2851 << initValsSize;
2852 if (reduceOp.getNumOperands() != initValsSize)
2853 // Delegate error reporting to ReduceOp
2854 return success();
2855
2856 // Check that the types of the results and reductions are the same.
2857 for (int64_t i = 0; i < static_cast<int64_t>(reductionsSize); ++i) {
2858 auto resultType = getOperation()->getResult(i).getType();
2859 auto reductionOperandType = reduceOp.getOperands()[i].getType();
2860 if (resultType != reductionOperandType)
2861 return reduceOp.emitOpError()
2862 << "expects type of " << i
2863 << "-th reduction operand: " << reductionOperandType
2864 << " to be the same as the " << i
2865 << "-th result type: " << resultType;
2866 }
2867 return success();
2868}
2869
2870ParseResult ParallelOp::parse(OpAsmParser &parser, OperationState &result) {
2871 auto &builder = parser.getBuilder();
2872 // Parse an opening `(` followed by induction variables followed by `)`
2873 SmallVector<OpAsmParser::Argument, 4> ivs;
2875 return failure();
2876
2877 // Parse loop bounds.
2878 SmallVector<OpAsmParser::UnresolvedOperand, 4> lower;
2879 if (parser.parseEqual() ||
2880 parser.parseOperandList(lower, ivs.size(),
2882 parser.resolveOperands(lower, builder.getIndexType(), result.operands))
2883 return failure();
2884
2885 SmallVector<OpAsmParser::UnresolvedOperand, 4> upper;
2886 if (parser.parseKeyword("to") ||
2887 parser.parseOperandList(upper, ivs.size(),
2889 parser.resolveOperands(upper, builder.getIndexType(), result.operands))
2890 return failure();
2891
2892 // Parse step values.
2893 SmallVector<OpAsmParser::UnresolvedOperand, 4> steps;
2894 if (parser.parseKeyword("step") ||
2895 parser.parseOperandList(steps, ivs.size(),
2897 parser.resolveOperands(steps, builder.getIndexType(), result.operands))
2898 return failure();
2899
2900 // Parse init values.
2901 SmallVector<OpAsmParser::UnresolvedOperand, 4> initVals;
2902 if (succeeded(parser.parseOptionalKeyword("init"))) {
2903 if (parser.parseOperandList(initVals, OpAsmParser::Delimiter::Paren))
2904 return failure();
2905 }
2906
2907 // Parse optional results in case there is a reduce.
2908 if (parser.parseOptionalArrowTypeList(result.types))
2909 return failure();
2910
2911 // Now parse the body.
2912 Region *body = result.addRegion();
2913 for (auto &iv : ivs)
2914 iv.type = builder.getIndexType();
2915 if (parser.parseRegion(*body, ivs))
2916 return failure();
2917
2918 // Set `operandSegmentSizes` attribute.
2919 result.addAttribute(
2920 ParallelOp::getOperandSegmentSizeAttr(),
2921 builder.getDenseI32ArrayAttr({static_cast<int32_t>(lower.size()),
2922 static_cast<int32_t>(upper.size()),
2923 static_cast<int32_t>(steps.size()),
2924 static_cast<int32_t>(initVals.size())}));
2925
2926 // Parse attributes.
2927 if (parser.parseOptionalAttrDict(result.attributes) ||
2928 parser.resolveOperands(initVals, result.types, parser.getNameLoc(),
2929 result.operands))
2930 return failure();
2931
2932 // Add a terminator if none was parsed.
2933 ParallelOp::ensureTerminator(*body, builder, result.location);
2934 return success();
2935}
2936
2937void ParallelOp::print(OpAsmPrinter &p) {
2938 p << " (" << getBody()->getArguments() << ") = (" << getLowerBound()
2939 << ") to (" << getUpperBound() << ") step (" << getStep() << ")";
2940 if (!getInitVals().empty())
2941 p << " init (" << getInitVals() << ")";
2942 p.printOptionalArrowTypeList(getResultTypes());
2943 p << ' ';
2944 p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
2945 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
2946}
2947
2948SmallVector<Region *> ParallelOp::getLoopRegions() { return {&getRegion()}; }
2949
2950std::optional<SmallVector<Value>> ParallelOp::getLoopInductionVars() {
2951 return SmallVector<Value>{getBody()->getArguments()};
2952}
2953
2954std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopLowerBounds() {
2955 return getLowerBound();
2956}
2957
2958std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopUpperBounds() {
2959 return getUpperBound();
2960}
2961
2962std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopSteps() {
2963 return getStep();
2964}
2965
2967 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
2968 if (!ivArg)
2969 return ParallelOp();
2970 assert(ivArg.getOwner() && "unlinked block argument");
2971 auto *containingOp = ivArg.getOwner()->getParentOp();
2972 return dyn_cast<ParallelOp>(containingOp);
2973}
2974
2975namespace {
2976// Collapse loop dimensions that perform a single iteration.
2977struct ParallelOpSingleOrZeroIterationDimsFolder
2978 : public OpRewritePattern<ParallelOp> {
2979 using OpRewritePattern<ParallelOp>::OpRewritePattern;
2980
2981 LogicalResult matchAndRewrite(ParallelOp op,
2982 PatternRewriter &rewriter) const override {
2983 Location loc = op.getLoc();
2984
2985 // Compute new loop bounds that omit all single-iteration loop dimensions.
2986 SmallVector<Value> newLowerBounds, newUpperBounds, newSteps;
2987 IRMapping mapping;
2988 for (auto [lb, ub, step, iv] :
2989 llvm::zip(op.getLowerBound(), op.getUpperBound(), op.getStep(),
2990 op.getInductionVars())) {
2991 auto numIterations =
2992 constantTripCount(lb, ub, step, /*isSigned=*/true, computeUbMinusLb);
2993 if (numIterations.has_value()) {
2994 // Remove the loop if it performs zero iterations.
2995 if (*numIterations == 0) {
2996 rewriter.replaceOp(op, op.getInitVals());
2997 return success();
2998 }
2999 // Replace the loop induction variable by the lower bound if the loop
3000 // performs a single iteration. Otherwise, copy the loop bounds.
3001 if (*numIterations == 1) {
3002 mapping.map(iv, getValueOrCreateConstantIndexOp(rewriter, loc, lb));
3003 continue;
3004 }
3005 }
3006 newLowerBounds.push_back(lb);
3007 newUpperBounds.push_back(ub);
3008 newSteps.push_back(step);
3009 }
3010 // Exit if none of the loop dimensions perform a single iteration.
3011 if (newLowerBounds.size() == op.getLowerBound().size())
3012 return failure();
3013
3014 if (newLowerBounds.empty()) {
3015 // All of the loop dimensions perform a single iteration. Inline
3016 // loop body and nested ReduceOp's
3017 SmallVector<Value> results;
3018 results.reserve(op.getInitVals().size());
3019 for (auto &bodyOp : op.getBody()->without_terminator())
3020 rewriter.clone(bodyOp, mapping);
3021 auto reduceOp = cast<ReduceOp>(op.getBody()->getTerminator());
3022 for (int64_t i = 0, e = reduceOp.getReductions().size(); i < e; ++i) {
3023 Block &reduceBlock = reduceOp.getReductions()[i].front();
3024 auto initValIndex = results.size();
3025 mapping.map(reduceBlock.getArgument(0), op.getInitVals()[initValIndex]);
3026 mapping.map(reduceBlock.getArgument(1),
3027 mapping.lookupOrDefault(reduceOp.getOperands()[i]));
3028 for (auto &reduceBodyOp : reduceBlock.without_terminator())
3029 rewriter.clone(reduceBodyOp, mapping);
3030
3031 auto result = mapping.lookupOrDefault(
3032 cast<ReduceReturnOp>(reduceBlock.getTerminator()).getResult());
3033 results.push_back(result);
3034 }
3035
3036 rewriter.replaceOp(op, results);
3037 return success();
3038 }
3039 // Replace the parallel loop by lower-dimensional parallel loop.
3040 auto newOp =
3041 ParallelOp::create(rewriter, op.getLoc(), newLowerBounds,
3042 newUpperBounds, newSteps, op.getInitVals(), nullptr);
3043 // Erase the empty block that was inserted by the builder.
3044 rewriter.eraseBlock(newOp.getBody());
3045 // Clone the loop body and remap the block arguments of the collapsed loops
3046 // (inlining does not support a cancellable block argument mapping).
3047 rewriter.cloneRegionBefore(op.getRegion(), newOp.getRegion(),
3048 newOp.getRegion().begin(), mapping);
3049 rewriter.replaceOp(op, newOp.getResults());
3050 return success();
3051 }
3052};
3053
3054struct MergeNestedParallelLoops : public OpRewritePattern<ParallelOp> {
3055 using OpRewritePattern<ParallelOp>::OpRewritePattern;
3056
3057 LogicalResult matchAndRewrite(ParallelOp op,
3058 PatternRewriter &rewriter) const override {
3059 Block &outerBody = *op.getBody();
3060 if (!llvm::hasSingleElement(outerBody.without_terminator()))
3061 return failure();
3062
3063 auto innerOp = dyn_cast<ParallelOp>(outerBody.front());
3064 if (!innerOp)
3065 return failure();
3066
3067 for (auto val : outerBody.getArguments())
3068 if (llvm::is_contained(innerOp.getLowerBound(), val) ||
3069 llvm::is_contained(innerOp.getUpperBound(), val) ||
3070 llvm::is_contained(innerOp.getStep(), val))
3071 return failure();
3072
3073 // Reductions are not supported yet.
3074 if (!op.getInitVals().empty() || !innerOp.getInitVals().empty())
3075 return failure();
3076
3077 auto bodyBuilder = [&](OpBuilder &builder, Location /*loc*/,
3078 ValueRange iterVals, ValueRange) {
3079 Block &innerBody = *innerOp.getBody();
3080 assert(iterVals.size() ==
3081 (outerBody.getNumArguments() + innerBody.getNumArguments()));
3082 IRMapping mapping;
3083 mapping.map(outerBody.getArguments(),
3084 iterVals.take_front(outerBody.getNumArguments()));
3085 mapping.map(innerBody.getArguments(),
3086 iterVals.take_back(innerBody.getNumArguments()));
3087 for (Operation &op : innerBody.without_terminator())
3088 builder.clone(op, mapping);
3089 };
3090
3091 auto concatValues = [](const auto &first, const auto &second) {
3092 SmallVector<Value> ret;
3093 ret.reserve(first.size() + second.size());
3094 ret.assign(first.begin(), first.end());
3095 ret.append(second.begin(), second.end());
3096 return ret;
3097 };
3098
3099 auto newLowerBounds =
3100 concatValues(op.getLowerBound(), innerOp.getLowerBound());
3101 auto newUpperBounds =
3102 concatValues(op.getUpperBound(), innerOp.getUpperBound());
3103 auto newSteps = concatValues(op.getStep(), innerOp.getStep());
3104
3105 rewriter.replaceOpWithNewOp<ParallelOp>(op, newLowerBounds, newUpperBounds,
3106 newSteps, ValueRange(),
3107 bodyBuilder);
3108 return success();
3109 }
3110};
3111
3112} // namespace
3113
3114void ParallelOp::getCanonicalizationPatterns(RewritePatternSet &results,
3115 MLIRContext *context) {
3116 results
3117 .add<ParallelOpSingleOrZeroIterationDimsFolder, MergeNestedParallelLoops>(
3118 context);
3119}
3120
3121/// Given the region at `index`, or the parent operation if `index` is None,
3122/// return the successor regions. These are the regions that may be selected
3123/// during the flow of control. `operands` is a set of optional attributes that
3124/// correspond to a constant value for each operand, or null if that operand is
3125/// not a constant.
3126void ParallelOp::getSuccessorRegions(
3127 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
3128 // Both the operation itself and the region may be branching into the body or
3129 // back into the operation itself. It is possible for loop not to enter the
3130 // body.
3131 regions.push_back(RegionSuccessor(&getRegion()));
3132 regions.push_back(RegionSuccessor(getOperation()));
3133}
3134
3135//===----------------------------------------------------------------------===//
3136// ReduceOp
3137//===----------------------------------------------------------------------===//
3138
3139void ReduceOp::build(OpBuilder &builder, OperationState &result) {}
3140
3141void ReduceOp::build(OpBuilder &builder, OperationState &result,
3142 ValueRange operands) {
3143 result.addOperands(operands);
3144 for (Value v : operands) {
3145 OpBuilder::InsertionGuard guard(builder);
3146 Region *bodyRegion = result.addRegion();
3147 builder.createBlock(bodyRegion, {},
3148 ArrayRef<Type>{v.getType(), v.getType()},
3149 {result.location, result.location});
3150 }
3151}
3152
3153LogicalResult ReduceOp::verifyRegions() {
3154 if (getReductions().size() != getOperands().size())
3155 return emitOpError() << "expects number of reduction regions: "
3156 << getReductions().size()
3157 << " to be the same as number of reduction operands: "
3158 << getOperands().size();
3159 // The region of a ReduceOp has two arguments of the same type as its
3160 // corresponding operand.
3161 for (int64_t i = 0, e = getReductions().size(); i < e; ++i) {
3162 auto type = getOperands()[i].getType();
3163 Block &block = getReductions()[i].front();
3164 if (block.empty())
3165 return emitOpError() << i << "-th reduction has an empty body";
3166 if (block.getNumArguments() != 2 ||
3167 llvm::any_of(block.getArguments(), [&](const BlockArgument &arg) {
3168 return arg.getType() != type;
3169 }))
3170 return emitOpError() << "expected two block arguments with type " << type
3171 << " in the " << i << "-th reduction region";
3172
3173 // Check that the block is terminated by a ReduceReturnOp.
3174 if (!isa<ReduceReturnOp>(block.getTerminator()))
3175 return emitOpError("reduction bodies must be terminated with an "
3176 "'scf.reduce.return' op");
3177 }
3178
3179 return success();
3180}
3181
3182MutableOperandRange
3183ReduceOp::getMutableSuccessorOperands(RegionSuccessor point) {
3184 // No operands are forwarded to the next iteration.
3185 return MutableOperandRange(getOperation(), /*start=*/0, /*length=*/0);
3186}
3187
3188//===----------------------------------------------------------------------===//
3189// ReduceReturnOp
3190//===----------------------------------------------------------------------===//
3191
3192LogicalResult ReduceReturnOp::verify() {
3193 // The type of the return value should be the same type as the types of the
3194 // block arguments of the reduction body.
3195 Block *reductionBody = getOperation()->getBlock();
3196 // Should already be verified by an op trait.
3197 assert(isa<ReduceOp>(reductionBody->getParentOp()) && "expected scf.reduce");
3198 Type expectedResultType = reductionBody->getArgument(0).getType();
3199 if (expectedResultType != getResult().getType())
3200 return emitOpError() << "must have type " << expectedResultType
3201 << " (the type of the reduction inputs)";
3202 return success();
3203}
3204
3205//===----------------------------------------------------------------------===//
3206// WhileOp
3207//===----------------------------------------------------------------------===//
3208
3209void WhileOp::build(::mlir::OpBuilder &odsBuilder,
3210 ::mlir::OperationState &odsState, TypeRange resultTypes,
3211 ValueRange inits, BodyBuilderFn beforeBuilder,
3212 BodyBuilderFn afterBuilder) {
3213 odsState.addOperands(inits);
3214 odsState.addTypes(resultTypes);
3215
3216 OpBuilder::InsertionGuard guard(odsBuilder);
3217
3218 // Build before region.
3219 SmallVector<Location, 4> beforeArgLocs;
3220 beforeArgLocs.reserve(inits.size());
3221 for (Value operand : inits) {
3222 beforeArgLocs.push_back(operand.getLoc());
3223 }
3224
3225 Region *beforeRegion = odsState.addRegion();
3226 Block *beforeBlock = odsBuilder.createBlock(beforeRegion, /*insertPt=*/{},
3227 inits.getTypes(), beforeArgLocs);
3228 if (beforeBuilder)
3229 beforeBuilder(odsBuilder, odsState.location, beforeBlock->getArguments());
3230
3231 // Build after region.
3232 SmallVector<Location, 4> afterArgLocs(resultTypes.size(), odsState.location);
3233
3234 Region *afterRegion = odsState.addRegion();
3235 Block *afterBlock = odsBuilder.createBlock(afterRegion, /*insertPt=*/{},
3236 resultTypes, afterArgLocs);
3237
3238 if (afterBuilder)
3239 afterBuilder(odsBuilder, odsState.location, afterBlock->getArguments());
3240}
3241
3242ConditionOp WhileOp::getConditionOp() {
3243 return cast<ConditionOp>(getBeforeBody()->getTerminator());
3244}
3245
3246YieldOp WhileOp::getYieldOp() {
3247 return cast<YieldOp>(getAfterBody()->getTerminator());
3248}
3249
3250std::optional<MutableArrayRef<OpOperand>> WhileOp::getYieldedValuesMutable() {
3251 return getYieldOp().getResultsMutable();
3252}
3253
3254Block::BlockArgListType WhileOp::getBeforeArguments() {
3255 return getBeforeBody()->getArguments();
3256}
3257
3258Block::BlockArgListType WhileOp::getAfterArguments() {
3259 return getAfterBody()->getArguments();
3260}
3261
3262Block::BlockArgListType WhileOp::getRegionIterArgs() {
3263 return getBeforeArguments();
3264}
3265
3266OperandRange WhileOp::getEntrySuccessorOperands(RegionSuccessor successor) {
3267 assert(successor.getSuccessor() == &getBefore() &&
3268 "WhileOp is expected to branch only to the first region");
3269 return getInits();
3270}
3271
3272void WhileOp::getSuccessorRegions(RegionBranchPoint point,
3273 SmallVectorImpl<RegionSuccessor> &regions) {
3274 // The parent op always branches to the condition region.
3275 if (point.isParent()) {
3276 regions.emplace_back(&getBefore());
3277 return;
3278 }
3279
3280 assert(llvm::is_contained(
3281 {&getAfter(), &getBefore()},
3282 point.getTerminatorPredecessorOrNull()->getParentRegion()) &&
3283 "there are only two regions in a WhileOp");
3284 // The body region always branches back to the condition region.
3285 if (point.getTerminatorPredecessorOrNull()->getParentRegion() ==
3286 &getAfter()) {
3287 regions.emplace_back(&getBefore());
3288 return;
3289 }
3290
3291 regions.push_back(RegionSuccessor(getOperation()));
3292 regions.emplace_back(&getAfter());
3293}
3294
3295ValueRange WhileOp::getSuccessorInputs(RegionSuccessor successor) {
3296 if (successor.isOperation())
3297 return getOperation()->getResults();
3298 if (successor == &getBefore())
3299 return getBefore().getArguments();
3300 if (successor == &getAfter())
3301 return getAfter().getArguments();
3302 llvm_unreachable("invalid region successor");
3303}
3304
3305SmallVector<Region *> WhileOp::getLoopRegions() {
3306 return {&getBefore(), &getAfter()};
3307}
3308
3309/// Parses a `while` op.
3310///
3311/// op ::= `scf.while` assignments `:` function-type region `do` region
3312/// `attributes` attribute-dict
3313/// initializer ::= /* empty */ | `(` assignment-list `)`
3314/// assignment-list ::= assignment | assignment `,` assignment-list
3315/// assignment ::= ssa-value `=` ssa-value
3316ParseResult scf::WhileOp::parse(OpAsmParser &parser, OperationState &result) {
3317 SmallVector<OpAsmParser::Argument, 4> regionArgs;
3318 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
3319 Region *before = result.addRegion();
3320 Region *after = result.addRegion();
3321
3322 OptionalParseResult listResult =
3323 parser.parseOptionalAssignmentList(regionArgs, operands);
3324 if (listResult.has_value() && failed(listResult.value()))
3325 return failure();
3326
3327 FunctionType functionType;
3328 SMLoc typeLoc = parser.getCurrentLocation();
3329 if (failed(parser.parseColonType(functionType)))
3330 return failure();
3331
3332 result.addTypes(functionType.getResults());
3333
3334 if (functionType.getNumInputs() != operands.size()) {
3335 return parser.emitError(typeLoc)
3336 << "expected as many input types as operands " << "(expected "
3337 << operands.size() << " got " << functionType.getNumInputs() << ")";
3338 }
3339
3340 // Resolve input operands.
3341 if (failed(parser.resolveOperands(operands, functionType.getInputs(),
3342 parser.getCurrentLocation(),
3343 result.operands)))
3344 return failure();
3345
3346 // Propagate the types into the region arguments.
3347 for (size_t i = 0, e = regionArgs.size(); i != e; ++i)
3348 regionArgs[i].type = functionType.getInput(i);
3349
3350 return failure(parser.parseRegion(*before, regionArgs) ||
3351 parser.parseKeyword("do") || parser.parseRegion(*after) ||
3352 parser.parseOptionalAttrDictWithKeyword(result.attributes));
3353}
3354
3355/// Prints a `while` op.
3356void scf::WhileOp::print(OpAsmPrinter &p) {
3357 printInitializationList(p, getBeforeArguments(), getInits(), " ");
3358 p << " : ";
3359 p.printFunctionalType(getInits().getTypes(), getResults().getTypes());
3360 p << ' ';
3361 p.printRegion(getBefore(), /*printEntryBlockArgs=*/false);
3362 p << " do ";
3363 p.printRegion(getAfter());
3365 (*this)->getDiscardableAttrDictionary().getValue());
3366}
3367
3368LogicalResult scf::WhileOp::verify() {
3369 auto beforeTerminator = verifyAndGetTerminator<scf::ConditionOp>(
3370 *this, getBefore(),
3371 "expects the 'before' region to terminate with 'scf.condition'");
3372 if (!beforeTerminator)
3373 return failure();
3374
3375 auto afterTerminator = verifyAndGetTerminator<scf::YieldOp>(
3376 *this, getAfter(),
3377 "expects the 'after' region to terminate with 'scf.yield'");
3378 return success(afterTerminator != nullptr);
3379}
3380
3381namespace {
3382/// Move a scf.if op that is directly before the scf.condition op in the while
3383/// before region, and whose condition matches the condition of the
3384/// scf.condition op, down into the while after region.
3385///
3386/// scf.while (..) : (...) -> ... {
3387/// %additional_used_values = ...
3388/// %cond = ...
3389/// ...
3390/// %res = scf.if %cond -> (...) {
3391/// use(%additional_used_values)
3392/// ... // then block
3393/// scf.yield %then_value
3394/// } else {
3395/// scf.yield %else_value
3396/// }
3397/// scf.condition(%cond) %res, ...
3398/// } do {
3399/// ^bb0(%res_arg, ...):
3400/// use(%res_arg)
3401/// ...
3402///
3403/// becomes
3404/// scf.while (..) : (...) -> ... {
3405/// %additional_used_values = ...
3406/// %cond = ...
3407/// ...
3408/// scf.condition(%cond) %else_value, ..., %additional_used_values
3409/// } do {
3410/// ^bb0(%res_arg ..., %additional_args): :
3411/// use(%additional_args)
3412/// ... // if then block
3413/// use(%then_value)
3414/// ...
3415struct WhileMoveIfDown : public OpRewritePattern<scf::WhileOp> {
3416 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3417
3418 LogicalResult matchAndRewrite(scf::WhileOp op,
3419 PatternRewriter &rewriter) const override {
3420 auto conditionOp = op.getConditionOp();
3421
3422 // Only support ifOp right before the condition at the moment. Relaxing this
3423 // would require to:
3424 // - check that the body does not have side-effects conflicting with
3425 // operations between the if and the condition.
3426 // - check that results of the if operation are only used as arguments to
3427 // the condition.
3428 auto ifOp = dyn_cast_or_null<scf::IfOp>(conditionOp->getPrevNode());
3429
3430 // Check that the ifOp is directly before the conditionOp and that it
3431 // matches the condition of the conditionOp. Also ensure that the ifOp has
3432 // no else block with content, as that would complicate the transformation.
3433 // TODO: support else blocks with content.
3434 if (!ifOp || ifOp.getCondition() != conditionOp.getCondition() ||
3435 (ifOp.elseBlock() && !ifOp.elseBlock()->without_terminator().empty()))
3436 return failure();
3437
3438 assert((ifOp->use_empty() || (llvm::all_equal(ifOp->getUsers()) &&
3439 *ifOp->user_begin() == conditionOp)) &&
3440 "ifOp has unexpected uses");
3441
3442 Location loc = op.getLoc();
3443
3444 // Replace uses of ifOp results in the conditionOp with the yielded values
3445 // from the ifOp branches: the after-region argument takes the `then` value,
3446 // while the condition operand -- which becomes a while result once the
3447 // condition is false -- takes the `else` value.
3448 //
3449 // The same ifOp result may be forwarded to several condition operands, so
3450 // assign into the specific operand instead of replacing all uses of the
3451 // ifOp result, which would also rewrite the operands not yet visited.
3452 for (auto [idx, arg] : llvm::enumerate(conditionOp.getArgs())) {
3453 auto it = llvm::find(ifOp->getResults(), arg);
3454 if (it == ifOp->getResults().end())
3455 continue;
3456 size_t ifOpIdx = it.getIndex();
3457 rewriter.replaceAllUsesWith(op.getAfterArguments()[idx],
3458 ifOp.thenYield()->getOperand(ifOpIdx));
3459 unsigned argIdx = idx;
3460 Value elseValue = ifOp.elseYield()->getOperand(ifOpIdx);
3461 rewriter.modifyOpInPlace(conditionOp, [&] {
3462 conditionOp.getArgsMutable()[argIdx].assign(elseValue);
3463 });
3464 }
3465
3466 // Collect additional used values from before region.
3467 SetVector<Value> additionalUsedValuesSet;
3468 visitUsedValuesDefinedAbove(ifOp.getThenRegion(), [&](OpOperand *operand) {
3469 if (&op.getBefore() == operand->get().getParentRegion())
3470 additionalUsedValuesSet.insert(operand->get());
3471 });
3472
3473 // Create new whileOp with additional used values as results.
3474 auto additionalUsedValues = additionalUsedValuesSet.getArrayRef();
3475 auto additionalValueTypes = llvm::map_to_vector(
3476 additionalUsedValues, [](Value val) { return val.getType(); });
3477 size_t additionalValueSize = additionalUsedValues.size();
3478 SmallVector<Type> newResultTypes(op.getResultTypes());
3479 newResultTypes.append(additionalValueTypes);
3480
3481 auto newWhileOp =
3482 scf::WhileOp::create(rewriter, loc, newResultTypes, op.getInits());
3483
3484 rewriter.modifyOpInPlace(newWhileOp, [&] {
3485 newWhileOp.getBefore().takeBody(op.getBefore());
3486 newWhileOp.getAfter().takeBody(op.getAfter());
3487 newWhileOp.getAfter().addArguments(
3488 additionalValueTypes,
3489 SmallVector<Location>(additionalValueSize, loc));
3490 });
3491
3492 rewriter.modifyOpInPlace(conditionOp, [&] {
3493 conditionOp.getArgsMutable().append(additionalUsedValues);
3494 });
3495
3496 // Replace uses of additional used values inside the ifOp then region with
3497 // the whileOp after region arguments.
3498 rewriter.replaceUsesWithIf(
3499 additionalUsedValues,
3500 newWhileOp.getAfterArguments().take_back(additionalValueSize),
3501 [&](OpOperand &use) {
3502 return ifOp.getThenRegion().isAncestor(
3503 use.getOwner()->getParentRegion());
3504 });
3505
3506 // Inline ifOp then region into new whileOp after region.
3507 rewriter.eraseOp(ifOp.thenYield());
3508 rewriter.inlineBlockBefore(ifOp.thenBlock(), newWhileOp.getAfterBody(),
3509 newWhileOp.getAfterBody()->begin());
3510 rewriter.eraseOp(ifOp);
3511 rewriter.replaceOp(op,
3512 newWhileOp->getResults().drop_back(additionalValueSize));
3513 return success();
3514 }
3515};
3516
3517/// Replace uses of the condition within the do block with true, since otherwise
3518/// the block would not be evaluated.
3519///
3520/// scf.while (..) : (i1, ...) -> ... {
3521/// %condition = call @evaluate_condition() : () -> i1
3522/// scf.condition(%condition) %condition : i1, ...
3523/// } do {
3524/// ^bb0(%arg0: i1, ...):
3525/// use(%arg0)
3526/// ...
3527///
3528/// becomes
3529/// scf.while (..) : (i1, ...) -> ... {
3530/// %condition = call @evaluate_condition() : () -> i1
3531/// scf.condition(%condition) %condition : i1, ...
3532/// } do {
3533/// ^bb0(%arg0: i1, ...):
3534/// use(%true)
3535/// ...
3536struct WhileConditionTruth : public OpRewritePattern<WhileOp> {
3537 using OpRewritePattern<WhileOp>::OpRewritePattern;
3538
3539 LogicalResult matchAndRewrite(WhileOp op,
3540 PatternRewriter &rewriter) const override {
3541 auto term = op.getConditionOp();
3542
3543 // These variables serve to prevent creating duplicate constants
3544 // and hold constant true or false values.
3545 Value constantTrue = nullptr;
3546
3547 bool replaced = false;
3548 for (auto yieldedAndBlockArgs :
3549 llvm::zip(term.getArgs(), op.getAfterArguments())) {
3550 if (std::get<0>(yieldedAndBlockArgs) == term.getCondition()) {
3551 if (!std::get<1>(yieldedAndBlockArgs).use_empty()) {
3552 if (!constantTrue)
3553 constantTrue = arith::ConstantOp::create(
3554 rewriter, op.getLoc(), term.getCondition().getType(),
3555 rewriter.getBoolAttr(true));
3556
3557 rewriter.replaceAllUsesWith(std::get<1>(yieldedAndBlockArgs),
3558 constantTrue);
3559 replaced = true;
3560 }
3561 }
3562 }
3563 return success(replaced);
3564 }
3565};
3566
3567/// Replace operations equivalent to the condition in the do block with true,
3568/// since otherwise the block would not be evaluated.
3569///
3570/// scf.while (..) : (i32, ...) -> ... {
3571/// %z = ... : i32
3572/// %condition = cmpi pred %z, %a
3573/// scf.condition(%condition) %z : i32, ...
3574/// } do {
3575/// ^bb0(%arg0: i32, ...):
3576/// %condition2 = cmpi pred %arg0, %a
3577/// use(%condition2)
3578/// ...
3579///
3580/// becomes
3581/// scf.while (..) : (i32, ...) -> ... {
3582/// %z = ... : i32
3583/// %condition = cmpi pred %z, %a
3584/// scf.condition(%condition) %z : i32, ...
3585/// } do {
3586/// ^bb0(%arg0: i32, ...):
3587/// use(%true)
3588/// ...
3589struct WhileCmpCond : public OpRewritePattern<scf::WhileOp> {
3590 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3591
3592 LogicalResult matchAndRewrite(scf::WhileOp op,
3593 PatternRewriter &rewriter) const override {
3594 using namespace scf;
3595 auto cond = op.getConditionOp();
3596 auto cmp = cond.getCondition().getDefiningOp<arith::CmpIOp>();
3597 if (!cmp)
3598 return failure();
3599 bool changed = false;
3600 for (auto tup : llvm::zip(cond.getArgs(), op.getAfterArguments())) {
3601 for (size_t opIdx = 0; opIdx < 2; opIdx++) {
3602 if (std::get<0>(tup) != cmp.getOperand(opIdx))
3603 continue;
3604 for (OpOperand &u :
3605 llvm::make_early_inc_range(std::get<1>(tup).getUses())) {
3606 auto cmp2 = dyn_cast<arith::CmpIOp>(u.getOwner());
3607 if (!cmp2)
3608 continue;
3609 // For a binary operator 1-opIdx gets the other side.
3610 if (cmp2.getOperand(1 - opIdx) != cmp.getOperand(1 - opIdx))
3611 continue;
3612 bool samePredicate;
3613 if (cmp2.getPredicate() == cmp.getPredicate())
3614 samePredicate = true;
3615 else if (cmp2.getPredicate() ==
3616 arith::invertPredicate(cmp.getPredicate()))
3617 samePredicate = false;
3618 else
3619 continue;
3620
3621 rewriter.replaceOpWithNewOp<arith::ConstantIntOp>(cmp2, samePredicate,
3622 1);
3623 changed = true;
3624 }
3625 }
3626 }
3627 return success(changed);
3628 }
3629};
3630
3631/// If both ranges contain same values return mappping indices from args2 to
3632/// args1. Otherwise return std::nullopt.
3633static std::optional<SmallVector<unsigned>> getArgsMapping(ValueRange args1,
3634 ValueRange args2) {
3635 if (args1.size() != args2.size())
3636 return std::nullopt;
3637
3638 SmallVector<unsigned> ret(args1.size());
3639 for (auto &&[i, arg1] : llvm::enumerate(args1)) {
3640 auto it = llvm::find(args2, arg1);
3641 if (it == args2.end())
3642 return std::nullopt;
3643
3644 ret[std::distance(args2.begin(), it)] = static_cast<unsigned>(i);
3645 }
3646
3647 return ret;
3648}
3649
3650static bool hasDuplicates(ValueRange args) {
3651 llvm::SmallDenseSet<Value> set;
3652 for (Value arg : args) {
3653 if (!set.insert(arg).second)
3654 return true;
3655 }
3656 return false;
3657}
3658
3659/// If `before` block args are directly forwarded to `scf.condition`, rearrange
3660/// `scf.condition` args into same order as block args. Update `after` block
3661/// args and op result values accordingly.
3662/// Needed to simplify `scf.while` -> `scf.for` uplifting.
3663struct WhileOpAlignBeforeArgs : public OpRewritePattern<WhileOp> {
3665
3666 LogicalResult matchAndRewrite(WhileOp loop,
3667 PatternRewriter &rewriter) const override {
3668 auto *oldBefore = loop.getBeforeBody();
3669 ConditionOp oldTerm = loop.getConditionOp();
3670 ValueRange beforeArgs = oldBefore->getArguments();
3671 ValueRange termArgs = oldTerm.getArgs();
3672 if (beforeArgs == termArgs)
3673 return failure();
3674
3675 if (hasDuplicates(termArgs))
3676 return failure();
3677
3678 auto mapping = getArgsMapping(beforeArgs, termArgs);
3679 if (!mapping)
3680 return failure();
3681
3682 {
3683 OpBuilder::InsertionGuard g(rewriter);
3684 rewriter.setInsertionPoint(oldTerm);
3685 rewriter.replaceOpWithNewOp<ConditionOp>(oldTerm, oldTerm.getCondition(),
3686 beforeArgs);
3687 }
3688
3689 auto *oldAfter = loop.getAfterBody();
3690
3691 SmallVector<Type> newResultTypes(beforeArgs.size());
3692 for (auto &&[i, j] : llvm::enumerate(*mapping))
3693 newResultTypes[j] = loop.getResult(i).getType();
3694
3695 auto newLoop = WhileOp::create(
3696 rewriter, loop.getLoc(), newResultTypes, loop.getInits(),
3697 /*beforeBuilder=*/nullptr, /*afterBuilder=*/nullptr);
3698 auto *newBefore = newLoop.getBeforeBody();
3699 auto *newAfter = newLoop.getAfterBody();
3700
3701 SmallVector<Value> newResults(beforeArgs.size());
3702 SmallVector<Value> newAfterArgs(beforeArgs.size());
3703 for (auto &&[i, j] : llvm::enumerate(*mapping)) {
3704 newResults[i] = newLoop.getResult(j);
3705 newAfterArgs[i] = newAfter->getArgument(j);
3706 }
3707
3708 rewriter.inlineBlockBefore(oldBefore, newBefore, newBefore->begin(),
3709 newBefore->getArguments());
3710 rewriter.inlineBlockBefore(oldAfter, newAfter, newAfter->begin(),
3711 newAfterArgs);
3712
3713 rewriter.replaceOp(loop, newResults);
3714 return success();
3715 }
3716};
3717} // namespace
3718
3719void WhileOp::getCanonicalizationPatterns(RewritePatternSet &results,
3720 MLIRContext *context) {
3721 results.add<WhileConditionTruth, WhileCmpCond, WhileOpAlignBeforeArgs,
3722 WhileMoveIfDown>(context);
3724 results, WhileOp::getOperationName());
3726 WhileOp::getOperationName());
3727}
3728
3729//===----------------------------------------------------------------------===//
3730// IndexSwitchOp
3731//===----------------------------------------------------------------------===//
3732
3733/// Parse the case regions and values.
3734static ParseResult
3736 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
3737 SmallVector<int64_t> caseValues;
3738 while (succeeded(p.parseOptionalKeyword("case"))) {
3739 int64_t value;
3740 Region &region = *caseRegions.emplace_back(std::make_unique<Region>());
3741 if (p.parseInteger(value) || p.parseRegion(region, /*arguments=*/{}))
3742 return failure();
3743 caseValues.push_back(value);
3744 }
3745 cases = p.getBuilder().getDenseI64ArrayAttr(caseValues);
3746 return success();
3747}
3748
3749/// Print the case regions and values.
3751 DenseI64ArrayAttr cases, RegionRange caseRegions) {
3752 for (auto [value, region] : llvm::zip(cases.asArrayRef(), caseRegions)) {
3753 p.printNewline();
3754 p << "case " << value << ' ';
3755 p.printRegion(*region, /*printEntryBlockArgs=*/false);
3756 }
3757}
3758
3759LogicalResult scf::IndexSwitchOp::verify() {
3760 if (getCases().size() != getCaseRegions().size()) {
3761 return emitOpError("has ")
3762 << getCaseRegions().size() << " case regions but "
3763 << getCases().size() << " case values";
3764 }
3765
3766 DenseSet<int64_t> valueSet;
3767 for (int64_t value : getCases())
3768 if (!valueSet.insert(value).second)
3769 return emitOpError("has duplicate case value: ") << value;
3770 auto verifyRegion = [&](Region &region, const Twine &name) -> LogicalResult {
3771 auto yield = dyn_cast<YieldOp>(region.front().back());
3772 if (!yield)
3773 return emitOpError("expected region to end with scf.yield, but got ")
3774 << region.front().back().getName();
3775
3776 if (yield.getNumOperands() != getNumResults()) {
3777 return (emitOpError("expected each region to return ")
3778 << getNumResults() << " values, but " << name << " returns "
3779 << yield.getNumOperands())
3780 .attachNote(yield.getLoc())
3781 << "see yield operation here";
3782 }
3783 for (auto [idx, result, operand] :
3784 llvm::enumerate(getResultTypes(), yield.getOperands())) {
3785 if (!operand)
3786 return yield.emitOpError() << "operand " << idx << " is null\n";
3787 if (result == operand.getType())
3788 continue;
3789 return (emitOpError("expected result #")
3790 << idx << " of each region to be " << result)
3791 .attachNote(yield.getLoc())
3792 << name << " returns " << operand.getType() << " here";
3793 }
3794 return success();
3795 };
3796
3797 if (failed(verifyRegion(getDefaultRegion(), "default region")))
3798 return failure();
3799 for (auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
3800 if (failed(verifyRegion(caseRegion, "case region #" + Twine(idx))))
3801 return failure();
3802
3803 return success();
3804}
3805
3806unsigned scf::IndexSwitchOp::getNumCases() { return getCases().size(); }
3807
3808Block &scf::IndexSwitchOp::getDefaultBlock() {
3809 return getDefaultRegion().front();
3810}
3811
3812Block &scf::IndexSwitchOp::getCaseBlock(unsigned idx) {
3813 assert(idx < getNumCases() && "case index out-of-bounds");
3814 return getCaseRegions()[idx].front();
3815}
3816
3817void IndexSwitchOp::getSuccessorRegions(
3818 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
3819 // All regions branch back to the parent op.
3820 if (!point.isParent()) {
3821 successors.push_back(RegionSuccessor(getOperation()));
3822 return;
3823 }
3824
3825 llvm::append_range(successors, getRegions());
3826}
3827
3828ValueRange IndexSwitchOp::getSuccessorInputs(RegionSuccessor successor) {
3829 return successor.isOperation() ? ValueRange(getOperation()->getResults())
3830 : ValueRange();
3831}
3832
3833void IndexSwitchOp::getEntrySuccessorRegions(
3834 ArrayRef<Attribute> operands,
3835 SmallVectorImpl<RegionSuccessor> &successors) {
3836 FoldAdaptor adaptor(operands, *this);
3837
3838 // If a constant was not provided, all regions are possible successors.
3839 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
3840 if (!arg) {
3841 llvm::append_range(successors, getRegions());
3842 return;
3843 }
3844
3845 // Otherwise, try to find a case with a matching value. If not, the
3846 // default region is the only successor.
3847 for (auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
3848 if (caseValue == arg.getInt()) {
3849 successors.emplace_back(&caseRegion);
3850 return;
3851 }
3852 }
3853 successors.emplace_back(&getDefaultRegion());
3854}
3855
3856void IndexSwitchOp::getRegionInvocationBounds(
3857 ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
3858 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
3859 if (!operandValue) {
3860 // All regions are invoked at most once.
3861 bounds.append(getNumRegions(), InvocationBounds(/*lb=*/0, /*ub=*/1));
3862 return;
3863 }
3864
3865 unsigned liveIndex = getNumRegions() - 1;
3866 const auto *it = llvm::find(getCases(), operandValue.getInt());
3867 if (it != getCases().end())
3868 liveIndex = std::distance(getCases().begin(), it);
3869 for (unsigned i = 0, e = getNumRegions(); i < e; ++i)
3870 bounds.emplace_back(/*lb=*/0, /*ub=*/i == liveIndex);
3871}
3872
3873void IndexSwitchOp::getCanonicalizationPatterns(RewritePatternSet &results,
3874 MLIRContext *context) {
3876 results, IndexSwitchOp::getOperationName());
3878 results, IndexSwitchOp::getOperationName());
3879}
3880
3881//===----------------------------------------------------------------------===//
3882// TableGen'd op method definitions
3883//===----------------------------------------------------------------------===//
3884
3885#define GET_OP_CLASSES
3886#include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
return success()
static std::optional< int64_t > getUpperBound(Value iv)
Gets the constant upper bound on an affine.for iv.
static std::optional< int64_t > getLowerBound(Value iv)
Gets the constant lower bound on an iv.
static LogicalResult verifyRegion(emitc::SwitchOp op, Region &region, const Twine &name)
Definition EmitC.cpp:1523
static ParseResult parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse the case regions and values.
Definition EmitC.cpp:1498
static void printSwitchCases(OpAsmPrinter &p, Operation *op, DenseI64ArrayAttr cases, RegionRange caseRegions)
Print the case regions and values.
Definition EmitC.cpp:1514
static void printInitializationList(OpAsmPrinter &p, Block::BlockArgListType blocksArgs, ValueRange initializers, StringRef prefix="")
Prints the initialization list in the form of <prefix>(inner = outer, inner2 = outer2,...
Definition SCF.cpp:502
static TerminatorTy verifyAndGetTerminator(Operation *op, Region &region, StringRef errorMessage)
Verifies that the first block of the given region is terminated by a TerminatorTy.
Definition SCF.cpp:102
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
ArrayAttr()
b getContext())
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
@ None
static std::string diag(const llvm::Value &value)
@ Paren
Parens surrounding zero or more operands.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
virtual ParseResult parseArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an arrow followed by a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
void printOptionalArrowTypeList(TypeRange &&types)
Print an optional arrow followed by a type list.
virtual void printNewline()
Print a newline and indent the printer to the start of the current operation/attribute/type.
This class represents an argument of a Block.
Definition Value.h:306
unsigned getArgNumber() const
Returns the number of this argument.
Definition Value.h:318
Block represents an ordered list of Operations.
Definition Block.h:33
MutableArrayRef< BlockArgument > BlockArgListType
Definition Block.h:109
bool empty()
Definition Block.h:172
BlockArgument getArgument(unsigned i)
Definition Block.h:153
unsigned getNumArguments()
Definition Block.h:152
iterator_range< args_iterator > addArguments(TypeRange types, ArrayRef< Location > locs)
Add one argument to the argument list for each type specified in the list.
Definition Block.cpp:165
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
Operation & front()
Definition Block.h:177
Operation & back()
Definition Block.h:176
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
BlockArgListType getArguments()
Definition Block.h:111
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
Definition Block.h:236
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
bool getValue() const
Return the boolean value of this attribute.
UnitAttr getUnitAttr()
Definition Builders.cpp:106
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:171
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
Definition Builders.cpp:175
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:75
BoolAttr getBoolAttr(bool value)
Definition Builders.cpp:108
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
IntegerType getI1Type()
Definition Builders.cpp:61
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:59
virtual Operation * materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc)
Registered hook to materialize a single constant operation from a given attribute value with the desi...
Definition Dialect.h:83
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
Definition IRMapping.h:65
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
IRValueT get() const
Return the current value being used by this operand.
void set(IRValueT newValue)
Set the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class provides a mutable adaptor for a range of operands.
Definition ValueRange.h:119
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual OptionalParseResult parseOptionalAssignmentList(SmallVectorImpl< Argument > &lhs, SmallVectorImpl< UnresolvedOperand > &rhs)=0
virtual ParseResult parseRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult parseArgumentList(SmallVectorImpl< Argument > &result, Delimiter delimiter=Delimiter::None, bool allowType=false, bool allowAttrs=false)=0
Parse zero or more arguments with a specified surrounding delimiter.
ParseResult parseAssignmentList(SmallVectorImpl< Argument > &lhs, SmallVectorImpl< UnresolvedOperand > &rhs)
Parse a list of assignments of the form (x1 = y1, x2 = y2, ...)
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDictWithKeyword(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary prefixed with 'attribute...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
void printFunctionalType(Operation *op)
Print the complete type of an operation in functional form.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:581
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
void cloneRegionBefore(Region &region, Region &parent, Region::iterator before, IRMapping &mapping)
Clone the blocks that belong to "region" before the given position in another region "parent".
Definition Builders.cpp:608
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:415
Operation * cloneWithoutRegions(Operation &op, IRMapping &mapper)
Creates a deep copy of this operation but keep the operation regions empty.
Definition Builders.h:596
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:254
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Definition Value.cpp:226
Set of flags used to control the behavior of the various IR print methods (e.g.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition Operation.h:1169
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
OperandRange operand_range
Definition Operation.h:396
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
auto getDiscardableAttrs()
Return a range of all of discardable attributes on this operation.
Definition Operation.h:538
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
ParseResult value() const
Access the internal ParseResult value.
bool has_value() const
Returns true if we contain a valid ParseResult value.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
bool isParent() const
Returns true if branching from the parent op.
RegionBranchTerminatorOpInterface getTerminatorPredecessorOrNull() const
Returns the terminator if branching from a region.
This class provides an abstraction over the different types of ranges over Regions.
Definition Region.h:378
This class represents a successor of a region.
Region * getSuccessor() const
Return the given region successor.
bool isOperation() const
Return true if the successor is an operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Definition Region.cpp:45
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
Definition Region.h:249
Block & back()
Definition Region.h:64
bool empty()
Definition Region.h:60
unsigned getNumArguments()
Definition Region.h:136
iterator begin()
Definition Region.h:55
BlockArgument getArgument(unsigned i)
Definition Region.h:137
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
Block * splitBlock(Block *block, Block::iterator before)
Split the operations starting at "before" (inclusive) out of the given block into a new block,...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void finalizeOpModification(Operation *op)
This method is used to signal the end of an in-place modification of the given operation.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
Operation * eraseOpResults(Operation *op, const BitVector &eraseIndices)
Erase the specified results of the given operation.
virtual void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues={})
Inline the operations of block 'source' into block 'dest' before the given position.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
void inlineRegionBefore(Region &region, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
virtual void startOpModification(Operation *op)
This method is used to notify the rewriter that an in-place operation modification is about to happen...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
Definition Types.cpp:66
bool isIndex() const
Definition Types.cpp:56
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
type_range getTypes() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
user_range getUsers() const
Definition Value.h:218
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition Value.cpp:39
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
constexpr auto RecursivelySpeculatable
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto NotSpeculatable
static Value defaultReplBuilderFn(OpBuilder &builder, Location loc, Value value)
Default implementation of the non-successor-input replacement builder function.
static LogicalResult defaultMatcherFn(Operation *op)
Default implementation of the pattern matcher function.
StringRef getMappingAttrName()
Name of the mapping attribute produced by loop mappers.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:732
ParallelOp getParallelForInductionVarOwner(Value val)
Returns the parallel loop parent of an induction variable.
Definition SCF.cpp:2966
void buildTerminatedBody(OpBuilder &builder, Location loc)
Default callback for IfOp builders. Inserts a yield without arguments.
Definition SCF.cpp:95
LoopNest buildLoopNest(OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs, ValueRange steps, ValueRange iterArgs, function_ref< ValueVector(OpBuilder &, Location, ValueRange, ValueRange)> bodyBuilder=nullptr)
Creates a perfect nest of "for" loops, i.e.
Definition SCF.cpp:798
bool insideMutuallyExclusiveBranches(Operation *a, Operation *b)
Return true if ops a and b (or their ancestors) are in mutually exclusive regions/blocks of an IfOp.
Definition SCF.cpp:1917
void promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
Definition SCF.cpp:753
std::optional< llvm::APSInt > computeUbMinusLb(Value lb, Value ub, bool isSigned)
Helper function to compute the difference between two values.
Definition SCF.cpp:116
ForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
Definition SCF.cpp:680
SmallVector< Value > ValueVector
An owning vector of values, handy to return from functions.
Definition SCF.h:64
llvm::function_ref< Value(OpBuilder &, Location loc, Type, Value)> ValueTypeCastFnTy
Perform a replacement of one iter OpOperand of an scf.for to the replacement value with a different t...
Definition SCF.h:107
ForallOp getForallOpThreadIndexOwner(Value val)
Returns the ForallOp parent of an thread index variable.
Definition SCF.cpp:1401
SmallVector< Value > replaceAndCastForOpIterArg(RewriterBase &rewriter, scf::ForOp forOp, OpOperand &operand, Value replacement, const ValueTypeCastFnTy &castFn)
Definition SCF.cpp:887
bool preservesStaticInformation(Type source, Type target)
Returns true if target is a ranked tensor type that preserves static information available in the sou...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
Definition Matchers.h:527
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
std::function< SmallVector< Value >( OpBuilder &b, Location loc, ArrayRef< BlockArgument > newBbArgs)> NewYieldValuesFn
A function that returns the additional yielded values during replaceWithAdditionalYields.
ParseResult parseDynamicIndexList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &values, DenseI64ArrayAttr &integers, DenseBoolArrayAttr &scalableFlags, SmallVectorImpl< Type > *valueTypes=nullptr, AsmParser::Delimiter delimiter=AsmParser::Delimiter::Square)
Parser hooks for custom directive in assemblyFormat.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:310
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:122
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:125
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
Definition Matchers.h:478
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
void populateRegionBranchOpInterfaceInliningPattern(RewritePatternSet &patterns, StringRef opName, NonSuccessorInputReplacementBuilderFn replBuilderFn=detail::defaultReplBuilderFn, PatternMatcherFn matcherFn=detail::defaultMatcherFn, PatternBenefit benefit=1)
Populate a pattern that inlines the body of region branch ops when there is a single acyclic path thr...
LogicalResult verifyListOfOperandsOrIntegers(Operation *op, StringRef name, unsigned expectedNumElements, ArrayRef< int64_t > attr, ValueRange values)
Verify that a the values has as many elements as the number of entries in attr for which isDynamic ev...
void populateRegionBranchOpInterfaceCanonicalizationPatterns(RewritePatternSet &patterns, StringRef opName, PatternBenefit benefit=1)
Populate canonicalization patterns that simplify successor operands/inputs of region branch operation...
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
void visitUsedValuesDefinedAbove(Region &region, Region &limit, function_ref< void(OpOperand *)> callback)
Calls callback for each use of a value within region or its descendants that was defined at the ances...
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
void printDynamicIndexList(OpAsmPrinter &printer, Operation *op, OperandRange values, ArrayRef< int64_t > integers, ArrayRef< bool > scalableFlags, TypeRange valueTypes=TypeRange(), AsmParser::Delimiter delimiter=AsmParser::Delimiter::Square)
Printer hooks for custom directive in assemblyFormat.
std::optional< APInt > constantTripCount(OpFoldResult lb, OpFoldResult ub, OpFoldResult step, bool isSigned, llvm::function_ref< std::optional< llvm::APSInt >(Value, Value, bool)> computeUbMinusLb)
Return the number of iterations for a loop with a lower bound lb, upper bound ub and step step,...
LogicalResult foldDynamicIndexList(SmallVectorImpl< OpFoldResult > &ofrs, bool onlyNonNegative=false, bool onlyNonZero=false)
Returns "success" when any of the elements in ofrs is a constant value.
LogicalResult matchAndRewrite(ExecuteRegionOp op, PatternRewriter &rewriter) const override
Definition SCF.cpp:223
This is the representation of an operand reference.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addOperands(ValueRange newOperands)
void addTypes(ArrayRef< Type > newTypes)
Region * addRegion()
Create a region that should be attached to the operation.