31#include "llvm/ADT/MapVector.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/ADT/SmallPtrSet.h"
34#include "llvm/ADT/TypeSwitch.h"
35#include "llvm/Support/Casting.h"
36#include "llvm/Support/DebugLog.h"
42#include "mlir/Dialect/SCF/IR/SCFOpsDialect.cpp.inc"
49struct SCFInlinerInterface :
public DialectInlinerInterface {
50 using DialectInlinerInterface::DialectInlinerInterface;
54 IRMapping &valueMapping)
const final {
59 bool isLegalToInline(Operation *, Region *,
bool, IRMapping &)
const final {
64 void handleTerminator(Operation *op,
ValueRange valuesToRepl)
const final {
65 auto retValOp = dyn_cast<scf::YieldOp>(op);
69 for (
auto retValue : llvm::zip(valuesToRepl, retValOp.getOperands())) {
70 std::get<0>(retValue).replaceAllUsesWith(std::get<1>(retValue));
80void SCFDialect::initialize() {
83#include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
85 addInterfaces<SCFInlinerInterface>();
86 declarePromisedInterface<ConvertToEmitCPatternInterface, SCFDialect>();
87 declarePromisedInterfaces<bufferization::BufferDeallocationOpInterface,
88 InParallelOp, ReduceReturnOp>();
89 declarePromisedInterfaces<bufferization::BufferizableOpInterface, ConditionOp,
90 ExecuteRegionOp, ForOp, IfOp, IndexSwitchOp,
91 ForallOp, InParallelOp, WhileOp, YieldOp>();
92 declarePromisedInterface<ValueBoundsOpInterface, ForOp>();
97 scf::YieldOp::create(builder, loc);
102template <
typename TerminatorTy>
104 StringRef errorMessage) {
105 Operation *terminatorOperation =
nullptr;
107 terminatorOperation = ®ion.
front().
back();
108 if (
auto yield = dyn_cast_or_null<TerminatorTy>(terminatorOperation))
112 if (terminatorOperation)
113 diag.attachNote(terminatorOperation->
getLoc()) <<
"terminator here";
120 auto addOp =
ub.getDefiningOp<arith::AddIOp>();
123 if ((isSigned && !addOp.hasNoSignedWrap()) ||
124 (!isSigned && !addOp.hasNoUnsignedWrap()))
127 if (addOp.getLhs() != lb ||
148ParseResult ExecuteRegionOp::parse(
OpAsmParser &parser,
176LogicalResult ExecuteRegionOp::verify() {
177 if (getRegion().empty())
178 return emitOpError(
"region needs to have at least one block");
179 if (getRegion().front().getNumArguments() > 0)
180 return emitOpError(
"region cannot have any arguments");
226 if (op.getNoInline())
228 if (!isa<FunctionOpInterface, ExecuteRegionOp>(op->getParentOp()))
231 Block *prevBlock = op->getBlock();
235 cf::BranchOp::create(rewriter, op.getLoc(), &op.getRegion().front());
237 for (
Block &blk : op.getRegion()) {
238 if (YieldOp yieldOp = dyn_cast<YieldOp>(blk.getTerminator())) {
240 cf::BranchOp::create(rewriter, yieldOp.getLoc(), postBlock,
241 yieldOp.getResults());
249 for (
auto res : op.getResults())
250 blockArgs.push_back(postBlock->
addArgument(res.getType(), res.getLoc()));
261 results, ExecuteRegionOp::getOperationName());
264 results, ExecuteRegionOp::getOperationName(),
266 return failure(cast<ExecuteRegionOp>(op).getNoInline());
270void ExecuteRegionOp::getSuccessorRegions(
282void ExecuteRegionOp::getRegionInvocationBounds(
284 bounds.emplace_back(1, 1);
300 "condition op can only exit the loop or branch to the after"
303 return getArgsMutable();
306void ConditionOp::getSuccessorRegions(
308 FoldAdaptor adaptor(operands, *
this);
310 WhileOp whileOp = getParentOp();
314 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
315 if (!boolAttr || boolAttr.getValue())
316 regions.emplace_back(&whileOp.getAfter());
317 if (!boolAttr || !boolAttr.getValue())
327 BodyBuilderFn bodyBuilder,
bool unsignedCmp) {
331 result.addAttribute(getUnsignedCmpAttrName(
result.name),
334 result.addOperands(initArgs);
335 for (
Value v : initArgs)
336 result.addTypes(v.getType());
341 for (
Value v : initArgs)
347 if (initArgs.empty() && !bodyBuilder) {
348 ForOp::ensureTerminator(*bodyRegion, builder,
result.location);
349 }
else if (bodyBuilder) {
357LogicalResult ForOp::verify() {
362 if (getBody()->getNumArguments() < getNumInductionVars())
363 return emitOpError(
"expected body to have at least ")
364 << getNumInductionVars()
365 <<
" argument(s) for the induction variable, but got "
366 << getBody()->getNumArguments();
369 if (getInitArgs().size() != getNumResults())
371 "mismatch in number of loop-carried values and defined values");
376LogicalResult ForOp::verifyRegions() {
378 if (getBody()->getNumArguments() < getNumInductionVars())
379 return emitOpError(
"expected body to have at least ")
380 << getNumInductionVars() <<
" argument(s) for the induction "
381 <<
"variable, but got " << getBody()->getNumArguments();
387 "expected induction variable to be same type as bounds and step");
389 if (getNumRegionIterArgs() != getNumResults())
391 "mismatch in number of basic block args and defined values");
393 auto initArgs = getInitArgs();
394 auto iterArgs = getRegionIterArgs();
395 auto opResults = getResults();
397 for (
auto e : llvm::zip(initArgs, iterArgs, opResults)) {
399 return emitOpError() <<
"types mismatch between " << i
400 <<
"th iter operand and defined value";
402 return emitOpError() <<
"types mismatch between " << i
403 <<
"th iter region arg and defined value";
410std::optional<SmallVector<Value>> ForOp::getLoopInductionVars() {
414std::optional<SmallVector<OpFoldResult>> ForOp::getLoopLowerBounds() {
418std::optional<SmallVector<OpFoldResult>> ForOp::getLoopSteps() {
422std::optional<SmallVector<OpFoldResult>> ForOp::getLoopUpperBounds() {
426bool ForOp::isValidInductionVarType(
Type type) {
431 if (bounds.size() != 1)
433 if (
auto val = dyn_cast<Value>(bounds[0])) {
441 if (bounds.size() != 1)
443 if (
auto val = dyn_cast<Value>(bounds[0])) {
451 if (steps.size() != 1)
453 if (
auto val = dyn_cast<Value>(steps[0])) {
460std::optional<ResultRange> ForOp::getLoopResults() {
return getResults(); }
464LogicalResult ForOp::promoteIfSingleIteration(
RewriterBase &rewriter) {
465 std::optional<APInt> tripCount = getStaticTripCount();
466 LDBG() <<
"promoteIfSingleIteration tripCount is " << tripCount
469 if (!tripCount.has_value() || tripCount->getZExtValue() > 1)
472 if (*tripCount == 0) {
479 auto yieldOp = cast<scf::YieldOp>(getBody()->getTerminator());
486 llvm::append_range(bbArgReplacements, getInitArgs());
490 getOperation()->getIterator(), bbArgReplacements);
506 StringRef prefix =
"") {
507 assert(blocksArgs.size() == initializers.size() &&
508 "expected same length of arguments and initializers");
509 if (initializers.empty())
513 llvm::interleaveComma(llvm::zip(blocksArgs, initializers), p, [&](
auto it) {
514 p << std::get<0>(it) <<
" = " << std::get<1>(it);
520 if (getUnsignedCmp())
523 p <<
" " << getInductionVar() <<
" = " <<
getLowerBound() <<
" to "
527 if (!getInitArgs().empty())
528 p <<
" -> (" << getInitArgs().getTypes() <<
')';
531 p <<
" : " << t <<
' ';
534 !getInitArgs().empty());
546 result.addAttribute(getUnsignedCmpAttrName(
result.name),
560 regionArgs.push_back(inductionVariable);
570 if (regionArgs.size() !=
result.types.size() + 1)
573 "mismatch in number of loop-carried values and defined values");
582 regionArgs.front().type = type;
583 for (
auto [iterArg, type] :
584 llvm::zip_equal(llvm::drop_begin(regionArgs),
result.types))
591 ForOp::ensureTerminator(*body, builder,
result.location);
600 for (
auto argOperandType : llvm::zip_equal(llvm::drop_begin(regionArgs),
601 operands,
result.types)) {
602 Type type = std::get<2>(argOperandType);
603 std::get<0>(argOperandType).type = type;
620 return getBody()->getArguments().drop_front(getNumInductionVars());
624 return getInitArgsMutable();
627FailureOr<LoopLikeOpInterface>
628ForOp::replaceWithAdditionalYields(
RewriterBase &rewriter,
630 bool replaceInitOperandUsesInLoop,
635 auto inits = llvm::to_vector(getInitArgs());
636 inits.append(newInitOperands.begin(), newInitOperands.end());
637 scf::ForOp newLoop = scf::ForOp::create(
640 newLoop->setDiscardableAttrs(getOperation()->getDiscardableAttrDictionary());
643 auto yieldOp = cast<scf::YieldOp>(getBody()->getTerminator());
645 newLoop.getBody()->getArguments().take_back(newInitOperands.size());
650 newYieldValuesFn(rewriter, getLoc(), newIterArgs);
651 assert(newInitOperands.size() == newYieldedValues.size() &&
652 "expected as many new yield values as new iter operands");
654 yieldOp.getResultsMutable().append(newYieldedValues);
660 newLoop.getBody()->getArguments().take_front(
661 getBody()->getNumArguments()));
663 if (replaceInitOperandUsesInLoop) {
666 for (
auto it : llvm::zip(newInitOperands, newIterArgs)) {
677 newLoop->getResults().take_front(getNumResults()));
678 return cast<LoopLikeOpInterface>(newLoop.getOperation());
682 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
685 assert(ivArg.getOwner() &&
"unlinked block argument");
686 auto *containingOp = ivArg.getOwner()->getParentOp();
687 return dyn_cast_or_null<ForOp>(containingOp);
691 return getInitArgs();
696 if (std::optional<APInt> tripCount = getStaticTripCount()) {
699 if (*tripCount == 0) {
708 }
else if (*tripCount == 1) {
732LogicalResult scf::ForallOp::promoteIfSingleIteration(RewriterBase &rewriter) {
733 for (
auto [lb, ub, step] :
734 llvm::zip(getMixedLowerBound(), getMixedUpperBound(), getMixedStep())) {
737 if (!tripCount.has_value() || *tripCount != 1)
741 return promote(rewriter, *
this);
745 return getBody()->getArguments().drop_front(getRank());
748MutableArrayRef<OpOperand> ForallOp::getInitsMutable() {
749 return getOutputsMutable();
754 scf::ForallOp forallOp) {
756 scf::InParallelOp terminator = forallOp.getTerminator();
760 for (
Operation &op : terminator.getYieldingOps()) {
761 if (!isa<tensor::ParallelInsertSliceOp>(op))
763 diag <<
"unsupported parallel combining op: " << op.
getName();
770 bbArgReplacements.append(forallOp.getOutputs().begin(),
771 forallOp.getOutputs().end());
776 yieldingOps.reserve(forallOp.getNumResults());
778 for (
BlockArgument outputArg : forallOp.getRegionIterArgs()) {
779 auto &outputOps = yieldingOps.emplace_back();
781 for (
Operation *op : forallOp.getCombiningOps(outputArg)) {
782 auto combiningOp = cast<ParallelCombiningOpInterface>(op);
784 if (combiningOp->getParentOp() != terminator.getOperation() ||
785 combiningOp.getUpdatedDestinations()[0].get() != outputArg)
788 outputOps.push_back(combiningOp);
794 forallOp->getIterator(), bbArgReplacements);
799 results.reserve(forallOp.getResults().size());
800 for (
auto [output, combiningOps] :
801 llvm::zip_equal(forallOp.getOutputs(), yieldingOps)) {
804 for (ParallelCombiningOpInterface combiningOp : combiningOps) {
805 auto insertOp = cast<tensor::ParallelInsertSliceOp>(combiningOp);
806 result = tensor::InsertSliceOp::create(
807 rewriter, forallOp.getLoc(),
result.getType(), insertOp.getSource(),
808 result, insertOp.getOffsets(), insertOp.getSizes(),
809 insertOp.getStrides(), insertOp.getStaticOffsets(),
810 insertOp.getStaticSizes(), insertOp.getStaticStrides());
813 results.push_back(
result);
829 assert(lbs.size() == ubs.size() &&
830 "expected the same number of lower and upper bounds");
831 assert(lbs.size() == steps.size() &&
832 "expected the same number of lower bounds and steps");
837 bodyBuilder ? bodyBuilder(builder, loc,
ValueRange(), iterArgs)
839 assert(results.size() == iterArgs.size() &&
840 "loop nest body must return as many values as loop has iteration "
842 return LoopNest{{}, std::move(results)};
850 loops.reserve(lbs.size());
851 ivs.reserve(lbs.size());
854 for (
unsigned i = 0, e = lbs.size(); i < e; ++i) {
855 auto loop = scf::ForOp::create(
856 builder, currentLoc, lbs[i], ubs[i], steps[i], currentIterArgs,
862 currentIterArgs = args;
863 currentLoc = nestedLoc;
869 loops.push_back(loop);
873 for (
unsigned i = 0, e = loops.size() - 1; i < e; ++i) {
875 scf::YieldOp::create(builder, loc, loops[i + 1].getResults());
882 ? bodyBuilder(builder, currentLoc, ivs,
883 loops.back().getRegionIterArgs())
885 assert(results.size() == iterArgs.size() &&
886 "loop nest body must return as many values as loop has iteration "
889 scf::YieldOp::create(builder, loc, results);
893 llvm::append_range(nestResults, loops.front().getResults());
894 return LoopNest{std::move(loops), std::move(nestResults)};
907 bodyBuilder(nestedBuilder, nestedLoc, ivs);
916 assert(operand.
getOwner() == forOp);
921 "expected an iter OpOperand");
923 "Expected a different type");
925 for (
OpOperand &opOperand : forOp.getInitArgsMutable()) {
930 newIterOperands.push_back(opOperand.get());
934 scf::ForOp newForOp = scf::ForOp::create(
935 rewriter, forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
936 forOp.getStep(), newIterOperands,
nullptr,
937 forOp.getUnsignedCmp());
938 newForOp->setDiscardableAttrs(
939 forOp->getDiscardableAttrDictionary().getValue());
940 Block &newBlock = newForOp.getRegion().
front();
948 BlockArgument newRegionIterArg = newForOp.getTiedLoopRegionIterArg(
950 Value castIn = castFn(rewriter, newForOp.getLoc(), oldType, newRegionIterArg);
951 newBlockTransferArgs[newRegionIterArg.
getArgNumber()] = castIn;
955 rewriter.
mergeBlocks(&oldBlock, &newBlock, newBlockTransferArgs);
958 auto clonedYieldOp = cast<scf::YieldOp>(newBlock.
getTerminator());
961 newRegionIterArg.
getArgNumber() - forOp.getNumInductionVars();
962 Value castOut = castFn(rewriter, newForOp.getLoc(), newType,
963 clonedYieldOp.getOperand(yieldIdx));
965 newYieldOperands[yieldIdx] = castOut;
966 scf::YieldOp::create(rewriter, newForOp.getLoc(), newYieldOperands);
967 rewriter.
eraseOp(clonedYieldOp);
972 newResults[yieldIdx] =
973 castFn(rewriter, newForOp.getLoc(), oldType, newResults[yieldIdx]);
979 assert(arg <
static_cast<int64_t>(forOp.getNumResults()) &&
980 "arg is out of bounds");
981 Value value = forOp.getYieldedValues()[arg];
983 if (value == forOp.getRegionIterArgs()[arg])
985 OpResult opResult = dyn_cast<OpResult>(value);
990 .Case([&](DestinationStyleOpInterface op) {
991 return op.getTiedOpOperand(opResult)->get();
993 .Case([&](ForOp forOp) {
1033 LogicalResult matchAndRewrite(ForOp op,
1035 for (
auto it : llvm::zip(op.getInitArgsMutable(), op.getResults())) {
1036 OpOperand &iterOpOperand = std::get<0>(it);
1038 if (!incomingCast ||
1039 incomingCast.getSource().getType() == incomingCast.getType())
1044 incomingCast.getDest().getType(),
1045 incomingCast.getSource().getType()))
1047 if (!std::get<1>(it).hasOneUse())
1053 int64_t resultNumber = std::get<1>(it).getResultNumber();
1055 op.getYieldedValues()[resultNumber].getDefiningOp<tensor::CastOp>();
1056 bool yieldsStaticType =
1057 yieldedCast && yieldedCast.getSource().getType() ==
1058 incomingCast.getSource().getType();
1065 rewriter, op, iterOpOperand, incomingCast.getSource(),
1067 return tensor::CastOp::create(b, loc, type, source);
1076void ForOp::getCanonicalizationPatterns(RewritePatternSet &results,
1077 MLIRContext *context) {
1078 results.
add<ForOpTensorCastFolder>(context);
1080 results, ForOp::getOperationName());
1085 results, ForOp::getOperationName(),
1087 [](OpBuilder &builder, Location loc, Value value) {
1091 auto blockArg = cast<BlockArgument>(value);
1092 assert(blockArg.getArgNumber() == 0 &&
"expected induction variable");
1093 auto forOp = cast<ForOp>(blockArg.getOwner()->getParentOp());
1094 return forOp.getLowerBound();
1100std::optional<APInt> ForOp::getConstantStep() {
1103 return step.getValue();
1107std::optional<MutableArrayRef<OpOperand>> ForOp::getYieldedValuesMutable() {
1108 return cast<scf::YieldOp>(getBody()->getTerminator()).getResultsMutable();
1114 if (
auto constantStep = getConstantStep())
1115 if (*constantStep == 1)
1123std::optional<APInt> ForOp::getStaticTripCount() {
1132LogicalResult ForallOp::verify() {
1133 unsigned numLoops = getRank();
1135 if (getNumResults() != getOutputs().size())
1136 return emitOpError(
"produces ")
1137 << getNumResults() <<
" results, but has only "
1138 << getOutputs().size() <<
" outputs";
1141 auto *body = getBody();
1143 return emitOpError(
"region expects ") << numLoops <<
" arguments";
1144 for (int64_t i = 0; i < numLoops; ++i)
1146 return emitOpError(
"expects ")
1147 << i <<
"-th block argument to be an index";
1148 for (
unsigned i = 0; i < getOutputs().size(); ++i)
1150 return emitOpError(
"type mismatch between ")
1151 << i <<
"-th output and corresponding block argument";
1152 if (getMapping().has_value() && !getMapping()->empty()) {
1153 if (getDeviceMappingAttrs().size() != numLoops)
1154 return emitOpError() <<
"mapping attribute size must match op rank";
1155 if (
failed(getDeviceMaskingAttr()))
1157 <<
" supports at most one device masking attribute";
1161 Operation *op = getOperation();
1163 getStaticLowerBound(),
1164 getDynamicLowerBound())))
1167 getStaticUpperBound(),
1168 getDynamicUpperBound())))
1171 getStaticStep(), getDynamicStep())))
1177void ForallOp::print(OpAsmPrinter &p) {
1178 Operation *op = getOperation();
1179 p <<
" (" << getInductionVars();
1180 if (isNormalized()) {
1201 if (!getRegionOutArgs().empty())
1202 p <<
"-> (" << getResultTypes() <<
") ";
1203 p.printRegion(getRegion(),
1205 getNumResults() > 0);
1207 if (
ArrayAttr mapping = getMappingAttr())
1210 p.printOptionalAttrDict(attrs);
1213ParseResult ForallOp::parse(OpAsmParser &parser, OperationState &
result) {
1215 auto indexType =
b.getIndexType();
1220 SmallVector<OpAsmParser::Argument, 4> ivs;
1225 SmallVector<OpAsmParser::UnresolvedOperand> dynamicLbs, dynamicUbs,
1235 unsigned numLoops = ivs.size();
1236 staticLbs =
b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 0));
1237 staticSteps =
b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 1));
1266 SmallVector<OpAsmParser::Argument, 4> regionOutArgs;
1267 SmallVector<OpAsmParser::UnresolvedOperand, 4> outOperands;
1270 if (outOperands.size() !=
result.types.size())
1272 "mismatch between out operands and types");
1281 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1282 std::unique_ptr<Region> region = std::make_unique<Region>();
1283 for (
auto &iv : ivs) {
1284 iv.type =
b.getIndexType();
1285 regionArgs.push_back(iv);
1287 for (
const auto &it : llvm::enumerate(regionOutArgs)) {
1288 auto &out = it.value();
1289 out.type =
result.types[it.index()];
1290 regionArgs.push_back(out);
1296 ForallOp::ensureTerminator(*region,
b,
result.location);
1297 result.addRegion(std::move(region));
1303 result.addAttribute(
"staticLowerBound", staticLbs);
1304 result.addAttribute(
"staticUpperBound", staticUbs);
1305 result.addAttribute(
"staticStep", staticSteps);
1306 result.addAttribute(
"operandSegmentSizes",
1308 {static_cast<int32_t>(dynamicLbs.size()),
1309 static_cast<int32_t>(dynamicUbs.size()),
1310 static_cast<int32_t>(dynamicSteps.size()),
1311 static_cast<int32_t>(outOperands.size())}));
1316void ForallOp::build(
1317 mlir::OpBuilder &
b, mlir::OperationState &
result,
1318 ArrayRef<OpFoldResult> lbs, ArrayRef<OpFoldResult> ubs,
1319 ArrayRef<OpFoldResult> steps,
ValueRange outputs,
1320 std::optional<ArrayAttr> mapping,
1322 SmallVector<int64_t> staticLbs, staticUbs, staticSteps;
1323 SmallVector<Value> dynamicLbs, dynamicUbs, dynamicSteps;
1328 result.addOperands(dynamicLbs);
1329 result.addOperands(dynamicUbs);
1330 result.addOperands(dynamicSteps);
1331 result.addOperands(outputs);
1334 result.addAttribute(getStaticLowerBoundAttrName(
result.name),
1335 b.getDenseI64ArrayAttr(staticLbs));
1336 result.addAttribute(getStaticUpperBoundAttrName(
result.name),
1337 b.getDenseI64ArrayAttr(staticUbs));
1338 result.addAttribute(getStaticStepAttrName(
result.name),
1339 b.getDenseI64ArrayAttr(staticSteps));
1341 "operandSegmentSizes",
1342 b.getDenseI32ArrayAttr({static_cast<int32_t>(dynamicLbs.size()),
1343 static_cast<int32_t>(dynamicUbs.size()),
1344 static_cast<int32_t>(dynamicSteps.size()),
1345 static_cast<int32_t>(outputs.size())}));
1346 if (mapping.has_value()) {
1347 result.addAttribute(ForallOp::getMappingAttrName(
result.name),
1351 Region *bodyRegion =
result.addRegion();
1352 OpBuilder::InsertionGuard g(
b);
1353 b.createBlock(bodyRegion);
1358 SmallVector<Type>(lbs.size(),
b.getIndexType()),
1359 SmallVector<Location>(staticLbs.size(),
result.location));
1362 SmallVector<Location>(outputs.size(),
result.location));
1364 b.setInsertionPointToStart(&bodyBlock);
1365 if (!bodyBuilderFn) {
1366 ForallOp::ensureTerminator(*bodyRegion,
b,
result.location);
1373void ForallOp::build(
1374 mlir::OpBuilder &
b, mlir::OperationState &
result,
1375 ArrayRef<OpFoldResult> ubs,
ValueRange outputs,
1376 std::optional<ArrayAttr> mapping,
1378 unsigned numLoops = ubs.size();
1379 SmallVector<OpFoldResult> lbs(numLoops,
b.getIndexAttr(0));
1380 SmallVector<OpFoldResult> steps(numLoops,
b.getIndexAttr(1));
1381 build(
b,
result, lbs, ubs, steps, outputs, mapping, bodyBuilderFn);
1385bool ForallOp::isNormalized() {
1386 auto allEqual = [](ArrayRef<OpFoldResult> results, int64_t val) {
1387 return llvm::all_of(results, [&](OpFoldResult ofr) {
1389 return intValue.has_value() && intValue == val;
1392 return allEqual(getMixedLowerBound(), 0) && allEqual(getMixedStep(), 1);
1395InParallelOp ForallOp::getTerminator() {
1396 return cast<InParallelOp>(getBody()->getTerminator());
1399SmallVector<Operation *> ForallOp::getCombiningOps(BlockArgument bbArg) {
1400 SmallVector<Operation *> storeOps;
1401 for (Operation *user : bbArg.
getUsers()) {
1402 if (
auto parallelOp = dyn_cast<ParallelCombiningOpInterface>(user)) {
1403 storeOps.push_back(parallelOp);
1409SmallVector<DeviceMappingAttrInterface> ForallOp::getDeviceMappingAttrs() {
1410 SmallVector<DeviceMappingAttrInterface> res;
1413 for (
auto attr : getMapping()->getValue()) {
1414 auto m = dyn_cast<DeviceMappingAttrInterface>(attr);
1421FailureOr<DeviceMaskingAttrInterface> ForallOp::getDeviceMaskingAttr() {
1422 DeviceMaskingAttrInterface res;
1425 for (
auto attr : getMapping()->getValue()) {
1426 auto m = dyn_cast<DeviceMaskingAttrInterface>(attr);
1435bool ForallOp::usesLinearMapping() {
1436 SmallVector<DeviceMappingAttrInterface> ifaces = getDeviceMappingAttrs();
1439 return ifaces.front().isLinearMapping();
1442std::optional<SmallVector<Value>> ForallOp::getLoopInductionVars() {
1443 return SmallVector<Value>{getBody()->getArguments().take_front(getRank())};
1447std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopLowerBounds() {
1449 return getMixedValues(getStaticLowerBound(), getDynamicLowerBound(),
b);
1453std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopUpperBounds() {
1455 return getMixedValues(getStaticUpperBound(), getDynamicUpperBound(),
b);
1459std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopSteps() {
1465 auto tidxArg = llvm::dyn_cast<BlockArgument>(val);
1468 assert(tidxArg.getOwner() &&
"unlinked block argument");
1469 auto *containingOp = tidxArg.getOwner()->getParentOp();
1470 return dyn_cast<ForallOp>(containingOp);
1478 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1480 auto forallOp = dimOp.getSource().getDefiningOp<ForallOp>();
1484 forallOp.getTiedOpOperand(llvm::cast<OpResult>(dimOp.getSource()))
1487 dimOp, [&]() { dimOp.getSourceMutable().assign(sharedOut); });
1492class ForallOpControlOperandsFolder :
public OpRewritePattern<ForallOp> {
1494 using OpRewritePattern<ForallOp>::OpRewritePattern;
1496 LogicalResult matchAndRewrite(ForallOp op,
1497 PatternRewriter &rewriter)
const override {
1498 SmallVector<OpFoldResult> mixedLowerBound(op.getMixedLowerBound());
1499 SmallVector<OpFoldResult> mixedUpperBound(op.getMixedUpperBound());
1500 SmallVector<OpFoldResult> mixedStep(op.getMixedStep());
1507 SmallVector<Value> dynamicLowerBound, dynamicUpperBound, dynamicStep;
1508 SmallVector<int64_t> staticLowerBound, staticUpperBound, staticStep;
1511 op.getDynamicLowerBoundMutable().assign(dynamicLowerBound);
1512 op.setStaticLowerBound(staticLowerBound);
1516 op.getDynamicUpperBoundMutable().assign(dynamicUpperBound);
1517 op.setStaticUpperBound(staticUpperBound);
1520 op.getDynamicStepMutable().assign(dynamicStep);
1521 op.setStaticStep(staticStep);
1523 op->setInherentAttr(
1524 rewriter.
getStringAttr(ForallOp::getOperandSegmentSizeAttr()),
1526 {static_cast<int32_t>(dynamicLowerBound.size()),
1527 static_cast<int32_t>(dynamicUpperBound.size()),
1528 static_cast<int32_t>(dynamicStep.size()),
1529 static_cast<int32_t>(op.getNumResults())}));
1608struct ForallOpIterArgsFolder :
public OpRewritePattern<ForallOp> {
1609 using OpRewritePattern<ForallOp>::OpRewritePattern;
1611 LogicalResult matchAndRewrite(ForallOp forallOp,
1612 PatternRewriter &rewriter)
const final {
1623 SmallVector<Value> resultsToDelete;
1624 SmallVector<Value> outsToDelete;
1625 SmallVector<BlockArgument> blockArgsToDelete;
1626 SmallVector<Value> newOuts;
1627 BitVector resultIndicesToDelete(forallOp.getNumResults(),
false);
1628 BitVector blockIndicesToDelete(forallOp.getBody()->getNumArguments(),
1630 for (OpResult
result : forallOp.getResults()) {
1631 OpOperand *opOperand = forallOp.getTiedOpOperand(
result);
1632 BlockArgument blockArg = forallOp.getTiedBlockArgument(opOperand);
1633 if (
result.use_empty() || forallOp.getCombiningOps(blockArg).empty()) {
1634 resultsToDelete.push_back(
result);
1635 outsToDelete.push_back(opOperand->
get());
1636 blockArgsToDelete.push_back(blockArg);
1637 resultIndicesToDelete[
result.getResultNumber()] =
true;
1640 newOuts.push_back(opOperand->
get());
1646 if (resultsToDelete.empty())
1651 for (
auto blockArg : blockArgsToDelete) {
1652 SmallVector<Operation *> combiningOps =
1653 forallOp.getCombiningOps(blockArg);
1654 for (Operation *combiningOp : combiningOps)
1655 rewriter.
eraseOp(combiningOp);
1657 for (
auto [blockArg,
result, out] :
1658 llvm::zip_equal(blockArgsToDelete, resultsToDelete, outsToDelete)) {
1664 forallOp.getBody()->eraseArguments(blockIndicesToDelete);
1669 auto newForallOp = cast<scf::ForallOp>(
1671 newForallOp.getOutputsMutable().assign(newOuts);
1677struct ForallOpSingleOrZeroIterationDimsFolder
1678 :
public OpRewritePattern<ForallOp> {
1679 using OpRewritePattern<ForallOp>::OpRewritePattern;
1681 LogicalResult matchAndRewrite(ForallOp op,
1682 PatternRewriter &rewriter)
const override {
1684 if (op.getMapping().has_value() && !op.getMapping()->empty())
1686 Location loc = op.getLoc();
1689 SmallVector<OpFoldResult> newMixedLowerBounds, newMixedUpperBounds,
1691 SmallVector<std::pair<Value, OpFoldResult>> singleIterationIVs;
1692 for (
auto [lb, ub, step, iv] :
1693 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1694 op.getMixedStep(), op.getInductionVars())) {
1695 auto numIterations =
1697 if (numIterations.has_value()) {
1699 if (*numIterations == 0) {
1700 rewriter.
replaceOp(op, op.getOutputs());
1705 if (*numIterations == 1) {
1706 singleIterationIVs.emplace_back(iv, lb);
1710 newMixedLowerBounds.push_back(lb);
1711 newMixedUpperBounds.push_back(ub);
1712 newMixedSteps.push_back(step);
1716 if (newMixedLowerBounds.empty())
1720 if (newMixedLowerBounds.size() ==
static_cast<unsigned>(op.getRank())) {
1722 op,
"no dimensions have 0 or 1 iterations");
1728 for (
auto [iv, lb] : singleIterationIVs)
1733 newOp = ForallOp::create(rewriter, loc, newMixedLowerBounds,
1734 newMixedUpperBounds, newMixedSteps,
1735 op.getOutputs(), std::nullopt,
nullptr);
1736 newOp.getBodyRegion().getBlocks().clear();
1737 newOp.setMappingAttr(op.getMappingAttr());
1738 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
1740 newOp.getRegion().begin(), mapping);
1741 rewriter.
replaceOp(op, newOp.getResults());
1747struct ForallOpReplaceConstantInductionVar :
public OpRewritePattern<ForallOp> {
1748 using OpRewritePattern<ForallOp>::OpRewritePattern;
1750 LogicalResult matchAndRewrite(ForallOp op,
1751 PatternRewriter &rewriter)
const override {
1752 Location loc = op.getLoc();
1753 bool changed =
false;
1754 for (
auto [lb, ub, step, iv] :
1755 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1756 op.getMixedStep(), op.getInductionVars())) {
1759 auto numIterations =
1761 if (!numIterations.has_value() || numIterations.value() != 1) {
1772struct FoldTensorCastOfOutputIntoForallOp
1773 :
public OpRewritePattern<scf::ForallOp> {
1774 using OpRewritePattern<scf::ForallOp>::OpRewritePattern;
1781 LogicalResult matchAndRewrite(scf::ForallOp forallOp,
1782 PatternRewriter &rewriter)
const final {
1783 llvm::SmallMapVector<unsigned, TypeCast, 2> tensorCastProducers;
1784 llvm::SmallVector<Value> newOutputTensors = forallOp.getOutputs();
1785 for (
auto en : llvm::enumerate(newOutputTensors)) {
1786 auto castOp = en.value().getDefiningOp<tensor::CastOp>();
1793 castOp.getSource().getType())) {
1797 tensorCastProducers[en.index()] =
1798 TypeCast{castOp.getSource().getType(), castOp.getType()};
1799 newOutputTensors[en.index()] = castOp.getSource();
1802 if (tensorCastProducers.empty())
1806 Location loc = forallOp.getLoc();
1807 auto newForallOp = ForallOp::create(
1808 rewriter, loc, forallOp.getMixedLowerBound(),
1809 forallOp.getMixedUpperBound(), forallOp.getMixedStep(),
1810 newOutputTensors, forallOp.getMapping(),
1811 [&](OpBuilder nestedBuilder, Location nestedLoc,
ValueRange bbArgs) {
1812 auto castBlockArgs =
1813 llvm::to_vector(bbArgs.take_back(forallOp->getNumResults()));
1814 for (auto [index, cast] : tensorCastProducers) {
1815 Value &oldTypeBBArg = castBlockArgs[index];
1816 oldTypeBBArg = tensor::CastOp::create(nestedBuilder, nestedLoc,
1817 cast.dstType, oldTypeBBArg);
1821 SmallVector<Value> ivsBlockArgs =
1822 llvm::to_vector(bbArgs.take_front(forallOp.getRank()));
1823 ivsBlockArgs.append(castBlockArgs);
1825 bbArgs.front().getParentBlock(), ivsBlockArgs);
1836 llvm::SmallDenseSet<Value> newIterArgSet(
1837 newForallOp.getRegionIterArgs().begin(),
1838 newForallOp.getRegionIterArgs().end());
1839 auto terminator = newForallOp.getTerminator();
1840 for (
auto &yieldingOp : terminator.getYieldingOps()) {
1841 auto parallelCombiningOp =
1842 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1843 if (!parallelCombiningOp)
1845 for (OpOperand &dest : parallelCombiningOp.getUpdatedDestinations()) {
1846 auto castOp = dest.get().getDefiningOp<tensor::CastOp>();
1847 if (castOp && newIterArgSet.contains(castOp.getSource()))
1848 dest.set(castOp.getSource());
1854 SmallVector<Value> castResults = newForallOp.getResults();
1855 for (
auto &item : tensorCastProducers) {
1856 Value &oldTypeResult = castResults[item.first];
1857 oldTypeResult = tensor::CastOp::create(rewriter, loc, item.second.dstType,
1860 rewriter.
replaceOp(forallOp, castResults);
1867void ForallOp::getCanonicalizationPatterns(RewritePatternSet &results,
1868 MLIRContext *context) {
1869 results.
add<DimOfForallOp, FoldTensorCastOfOutputIntoForallOp,
1870 ForallOpControlOperandsFolder, ForallOpIterArgsFolder,
1871 ForallOpSingleOrZeroIterationDimsFolder,
1872 ForallOpReplaceConstantInductionVar>(context);
1875void ForallOp::getSuccessorRegions(RegionBranchPoint point,
1876 SmallVectorImpl<RegionSuccessor> ®ions) {
1883 regions.push_back(RegionSuccessor(&getRegion()));
1886 regions.push_back(RegionSuccessor(getOperation()));
1891 regions.push_back(RegionSuccessor(getOperation()));
1900void InParallelOp::build(OpBuilder &
b, OperationState &
result) {
1901 OpBuilder::InsertionGuard g(
b);
1902 Region *bodyRegion =
result.addRegion();
1903 b.createBlock(bodyRegion);
1906LogicalResult InParallelOp::verify() {
1907 scf::ForallOp forallOp =
1908 dyn_cast<scf::ForallOp>(getOperation()->getParentOp());
1910 return this->emitOpError(
"expected forall op parent");
1912 for (Operation &op : getRegion().front().getOperations()) {
1913 auto parallelCombiningOp = dyn_cast<ParallelCombiningOpInterface>(&op);
1914 if (!parallelCombiningOp) {
1915 return this->emitOpError(
"expected only ParallelCombiningOpInterface")
1920 MutableOperandRange dests = parallelCombiningOp.getUpdatedDestinations();
1921 ArrayRef<BlockArgument> regionOutArgs = forallOp.getRegionOutArgs();
1922 for (OpOperand &dest : dests) {
1923 if (!llvm::is_contained(regionOutArgs, dest.get()))
1924 return op.emitOpError(
"may only insert into an output block argument");
1931void InParallelOp::print(OpAsmPrinter &p) {
1937 getOperation()->getDiscardableAttrDictionary().getValue());
1940ParseResult InParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
1943 SmallVector<OpAsmParser::Argument, 8> regionOperands;
1944 std::unique_ptr<Region> region = std::make_unique<Region>();
1948 if (region->empty())
1949 OpBuilder(builder.
getContext()).createBlock(region.get());
1950 result.addRegion(std::move(region));
1958OpResult InParallelOp::getParentResult(int64_t idx) {
1959 return getOperation()->getParentOp()->getResult(idx);
1962SmallVector<BlockArgument> InParallelOp::getDests() {
1963 SmallVector<BlockArgument> updatedDests;
1964 for (Operation &yieldingOp : getYieldingOps()) {
1965 auto parallelCombiningOp =
1966 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1967 if (!parallelCombiningOp)
1969 for (OpOperand &updatedOperand :
1970 parallelCombiningOp.getUpdatedDestinations())
1971 updatedDests.push_back(cast<BlockArgument>(updatedOperand.get()));
1973 return updatedDests;
1976llvm::iterator_range<Block::iterator> InParallelOp::getYieldingOps() {
1977 return getRegion().front().getOperations();
1985 assert(a &&
"expected non-empty operation");
1986 assert(
b &&
"expected non-empty operation");
1991 if (ifOp->isProperAncestor(
b))
1994 return static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*a)) !=
1995 static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*
b));
1997 ifOp = ifOp->getParentOfType<IfOp>();
2005IfOp::inferReturnTypes(
MLIRContext *ctx, std::optional<Location> loc,
2006 IfOp::Adaptor adaptor,
2008 if (adaptor.getRegions().empty())
2010 Region *r = &adaptor.getThenRegion();
2016 auto yieldOp = llvm::dyn_cast<YieldOp>(
b.back());
2019 TypeRange types = yieldOp.getOperandTypes();
2020 llvm::append_range(inferredReturnTypes, types);
2026 return build(builder,
result, resultTypes, cond,
false,
2030void IfOp::build(OpBuilder &builder, OperationState &
result,
2031 TypeRange resultTypes, Value cond,
bool addThenBlock,
2032 bool addElseBlock) {
2033 assert((!addElseBlock || addThenBlock) &&
2034 "must not create else block w/o then block");
2035 result.addTypes(resultTypes);
2036 result.addOperands(cond);
2039 OpBuilder::InsertionGuard guard(builder);
2040 Region *thenRegion =
result.addRegion();
2043 Region *elseRegion =
result.addRegion();
2048void IfOp::build(OpBuilder &builder, OperationState &
result, Value cond,
2049 bool withElseRegion) {
2053void IfOp::build(OpBuilder &builder, OperationState &
result,
2054 TypeRange resultTypes, Value cond,
bool withElseRegion) {
2055 result.addTypes(resultTypes);
2056 result.addOperands(cond);
2059 OpBuilder::InsertionGuard guard(builder);
2060 Region *thenRegion =
result.addRegion();
2062 if (resultTypes.empty())
2063 IfOp::ensureTerminator(*thenRegion, builder,
result.location);
2066 Region *elseRegion =
result.addRegion();
2067 if (withElseRegion) {
2069 if (resultTypes.empty())
2070 IfOp::ensureTerminator(*elseRegion, builder,
result.location);
2074void IfOp::build(OpBuilder &builder, OperationState &
result, Value cond,
2076 function_ref<
void(OpBuilder &, Location)> elseBuilder) {
2077 assert(thenBuilder &&
"the builder callback for 'then' must be present");
2078 result.addOperands(cond);
2081 OpBuilder::InsertionGuard guard(builder);
2082 Region *thenRegion =
result.addRegion();
2084 thenBuilder(builder,
result.location);
2087 Region *elseRegion =
result.addRegion();
2090 elseBuilder(builder,
result.location);
2094 SmallVector<Type> inferredReturnTypes;
2096 auto attrDict = DictionaryAttr::get(ctx,
result.attributes);
2097 if (succeeded(inferReturnTypes(ctx, std::nullopt,
result.operands, attrDict,
2098 PropertyRef{},
result.regions,
2099 inferredReturnTypes))) {
2100 result.addTypes(inferredReturnTypes);
2104LogicalResult IfOp::verify() {
2105 if (getNumResults() != 0 && getElseRegion().empty())
2106 return emitOpError(
"must have an else block if defining values");
2110ParseResult IfOp::parse(OpAsmParser &parser, OperationState &
result) {
2112 result.regions.reserve(2);
2113 Region *thenRegion =
result.addRegion();
2114 Region *elseRegion =
result.addRegion();
2117 OpAsmParser::UnresolvedOperand cond;
2143void IfOp::print(OpAsmPrinter &p) {
2144 bool printBlockTerminators =
false;
2146 p <<
" " << getCondition();
2147 if (!getResults().empty()) {
2148 p <<
" -> (" << getResultTypes() <<
")";
2150 printBlockTerminators =
true;
2155 printBlockTerminators);
2158 auto &elseRegion = getElseRegion();
2159 if (!elseRegion.
empty()) {
2163 printBlockTerminators);
2169void IfOp::getSuccessorRegions(RegionBranchPoint point,
2170 SmallVectorImpl<RegionSuccessor> ®ions) {
2174 regions.push_back(RegionSuccessor(getOperation()));
2178 regions.push_back(RegionSuccessor(&getThenRegion()));
2181 Region *elseRegion = &this->getElseRegion();
2182 if (elseRegion->
empty())
2183 regions.push_back(RegionSuccessor(getOperation()));
2185 regions.push_back(RegionSuccessor(elseRegion));
2188ValueRange IfOp::getSuccessorInputs(RegionSuccessor successor) {
2193void IfOp::getEntrySuccessorRegions(ArrayRef<Attribute> operands,
2194 SmallVectorImpl<RegionSuccessor> ®ions) {
2195 FoldAdaptor adaptor(operands, *
this);
2196 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
2197 if (!boolAttr || boolAttr.getValue())
2198 regions.emplace_back(&getThenRegion());
2201 if (!boolAttr || !boolAttr.getValue()) {
2202 if (!getElseRegion().empty())
2203 regions.emplace_back(&getElseRegion());
2205 regions.emplace_back(RegionSuccessor(getOperation()));
2209LogicalResult IfOp::fold(FoldAdaptor adaptor,
2210 SmallVectorImpl<OpFoldResult> &results) {
2212 if (getElseRegion().empty())
2215 arith::XOrIOp xorStmt = getCondition().getDefiningOp<arith::XOrIOp>();
2222 getConditionMutable().assign(xorStmt.getLhs());
2223 Block *thenBlock = &getThenRegion().front();
2226 getThenRegion().getBlocks().splice(getThenRegion().getBlocks().begin(),
2227 getElseRegion().getBlocks());
2228 getElseRegion().getBlocks().splice(getElseRegion().getBlocks().begin(),
2229 getThenRegion().getBlocks(), thenBlock);
2233void IfOp::getRegionInvocationBounds(
2234 ArrayRef<Attribute> operands,
2235 SmallVectorImpl<InvocationBounds> &invocationBounds) {
2236 if (
auto cond = llvm::dyn_cast_or_null<BoolAttr>(operands[0])) {
2239 invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
2240 invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
2243 invocationBounds.assign(2, {0, 1});
2250struct ConvertTrivialIfToSelect :
public OpRewritePattern<IfOp> {
2251 using OpRewritePattern<IfOp>::OpRewritePattern;
2253 LogicalResult matchAndRewrite(IfOp op,
2254 PatternRewriter &rewriter)
const override {
2255 if (op->getNumResults() == 0)
2258 auto cond = op.getCondition();
2259 auto thenYieldArgs = op.thenYield().getOperands();
2260 auto elseYieldArgs = op.elseYield().getOperands();
2262 SmallVector<Type> nonHoistable;
2263 for (
auto [trueVal, falseVal] : llvm::zip(thenYieldArgs, elseYieldArgs)) {
2264 if (&op.getThenRegion() == trueVal.getParentRegion() ||
2265 &op.getElseRegion() == falseVal.getParentRegion())
2266 nonHoistable.push_back(trueVal.getType());
2270 if (nonHoistable.size() == op->getNumResults())
2273 IfOp
replacement = IfOp::create(rewriter, op.getLoc(), nonHoistable, cond,
2277 replacement.getThenRegion().takeBody(op.getThenRegion());
2278 replacement.getElseRegion().takeBody(op.getElseRegion());
2280 SmallVector<Value> results(op->getNumResults());
2281 assert(thenYieldArgs.size() == results.size());
2282 assert(elseYieldArgs.size() == results.size());
2284 SmallVector<Value> trueYields;
2285 SmallVector<Value> falseYields;
2287 for (
const auto &it :
2288 llvm::enumerate(llvm::zip(thenYieldArgs, elseYieldArgs))) {
2289 Value trueVal = std::get<0>(it.value());
2290 Value falseVal = std::get<1>(it.value());
2293 results[it.index()] =
replacement.getResult(trueYields.size());
2294 trueYields.push_back(trueVal);
2295 falseYields.push_back(falseVal);
2296 }
else if (trueVal == falseVal)
2297 results[it.index()] = trueVal;
2299 results[it.index()] = arith::SelectOp::create(rewriter, op.getLoc(),
2300 cond, trueVal, falseVal);
2327struct ConditionPropagation :
public OpRewritePattern<IfOp> {
2328 using OpRewritePattern<IfOp>::OpRewritePattern;
2331 enum class Parent { Then, Else,
None };
2336 static Parent getParentType(Region *toCheck, IfOp op,
2338 Region *endRegion) {
2339 SmallVector<Region *> seen;
2340 while (toCheck != endRegion) {
2341 auto found = cache.find(toCheck);
2342 if (found != cache.end())
2343 return found->second;
2344 seen.push_back(toCheck);
2345 if (&op.getThenRegion() == toCheck) {
2346 for (Region *region : seen)
2347 cache[region] = Parent::Then;
2348 return Parent::Then;
2350 if (&op.getElseRegion() == toCheck) {
2351 for (Region *region : seen)
2352 cache[region] = Parent::Else;
2353 return Parent::Else;
2358 for (Region *region : seen)
2359 cache[region] = Parent::None;
2360 return Parent::None;
2363 LogicalResult matchAndRewrite(IfOp op,
2364 PatternRewriter &rewriter)
const override {
2370 bool changed =
false;
2375 Value constantTrue =
nullptr;
2376 Value constantFalse =
nullptr;
2379 for (OpOperand &use :
2380 llvm::make_early_inc_range(op.getCondition().getUses())) {
2383 case Parent::Then: {
2387 constantTrue = arith::ConstantOp::create(
2391 [&]() { use.set(constantTrue); });
2394 case Parent::Else: {
2398 constantFalse = arith::ConstantOp::create(
2402 [&]() { use.set(constantFalse); });
2405 case Parent::None: {
2406 auto selectOp = dyn_cast<arith::SelectOp>(use.
getOwner());
2413 selectOp.getResult(), selectOp.getTrueValue(),
2414 [&](OpOperand &selectUse) {
2415 return getParentType(
2416 selectUse.getOwner()->getParentRegion(), op, cache,
2417 op.getCondition().getParentRegion()) == Parent::Then;
2420 selectOp.getResult(), selectOp.getFalseValue(),
2421 [&](OpOperand &selectUse) {
2422 return getParentType(
2423 selectUse.getOwner()->getParentRegion(), op, cache,
2424 op.getCondition().getParentRegion()) == Parent::Else;
2471struct ReplaceIfYieldWithConditionOrValue :
public OpRewritePattern<IfOp> {
2472 using OpRewritePattern<IfOp>::OpRewritePattern;
2474 LogicalResult matchAndRewrite(IfOp op,
2475 PatternRewriter &rewriter)
const override {
2477 if (op.getNumResults() == 0)
2481 cast<scf::YieldOp>(op.getThenRegion().back().getTerminator());
2483 cast<scf::YieldOp>(op.getElseRegion().back().getTerminator());
2486 op.getOperation()->getIterator());
2487 bool changed =
false;
2489 for (
auto [trueResult, falseResult, opResult] :
2490 llvm::zip(trueYield.getResults(), falseYield.getResults(),
2492 if (trueResult == falseResult) {
2493 if (!opResult.use_empty()) {
2494 opResult.replaceAllUsesWith(trueResult);
2500 BoolAttr trueYield, falseYield;
2505 bool trueVal = trueYield.
getValue();
2506 bool falseVal = falseYield.
getValue();
2507 if (!trueVal && falseVal) {
2508 if (!opResult.use_empty()) {
2509 Dialect *constDialect = trueResult.getDefiningOp()->getDialect();
2510 Value notCond = arith::XOrIOp::create(
2511 rewriter, op.getLoc(), op.getCondition(),
2517 opResult.replaceAllUsesWith(notCond);
2521 if (trueVal && !falseVal) {
2522 if (!opResult.use_empty()) {
2523 opResult.replaceAllUsesWith(op.getCondition());
2553struct CombineIfs :
public OpRewritePattern<IfOp> {
2554 using OpRewritePattern<IfOp>::OpRewritePattern;
2556 LogicalResult matchAndRewrite(IfOp nextIf,
2557 PatternRewriter &rewriter)
const override {
2558 Block *parent = nextIf->getBlock();
2559 if (nextIf == &parent->
front())
2562 auto prevIf = dyn_cast<IfOp>(nextIf->getPrevNode());
2570 Block *nextThen =
nullptr;
2571 Block *nextElse =
nullptr;
2572 if (nextIf.getCondition() == prevIf.getCondition()) {
2573 nextThen = nextIf.thenBlock();
2574 if (!nextIf.getElseRegion().empty())
2575 nextElse = nextIf.elseBlock();
2577 if (arith::XOrIOp notv =
2578 nextIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2579 if (notv.getLhs() == prevIf.getCondition() &&
2581 nextElse = nextIf.thenBlock();
2582 if (!nextIf.getElseRegion().empty())
2583 nextThen = nextIf.elseBlock();
2586 if (arith::XOrIOp notv =
2587 prevIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2588 if (notv.getLhs() == nextIf.getCondition() &&
2590 nextElse = nextIf.thenBlock();
2591 if (!nextIf.getElseRegion().empty())
2592 nextThen = nextIf.elseBlock();
2596 if (!nextThen && !nextElse)
2599 SmallVector<Value> prevElseYielded;
2600 if (!prevIf.getElseRegion().empty())
2601 prevElseYielded = prevIf.elseYield().getOperands();
2604 for (
auto it : llvm::zip(prevIf.getResults(),
2605 prevIf.thenYield().getOperands(), prevElseYielded))
2606 for (OpOperand &use :
2607 llvm::make_early_inc_range(std::get<0>(it).getUses())) {
2611 use.
set(std::get<1>(it));
2616 use.
set(std::get<2>(it));
2621 SmallVector<Type> mergedTypes(prevIf.getResultTypes());
2622 llvm::append_range(mergedTypes, nextIf.getResultTypes());
2624 IfOp combinedIf = IfOp::create(rewriter, nextIf.getLoc(), mergedTypes,
2625 prevIf.getCondition(),
false);
2626 rewriter.
eraseBlock(&combinedIf.getThenRegion().back());
2629 combinedIf.getThenRegion(),
2630 combinedIf.getThenRegion().begin());
2633 YieldOp thenYield = combinedIf.thenYield();
2634 YieldOp thenYield2 = cast<YieldOp>(nextThen->
getTerminator());
2635 rewriter.
mergeBlocks(nextThen, combinedIf.thenBlock());
2638 SmallVector<Value> mergedYields(thenYield.getOperands());
2639 llvm::append_range(mergedYields, thenYield2.getOperands());
2640 YieldOp::create(rewriter, thenYield2.getLoc(), mergedYields);
2646 combinedIf.getElseRegion(),
2647 combinedIf.getElseRegion().begin());
2650 if (combinedIf.getElseRegion().empty()) {
2652 combinedIf.getElseRegion(),
2653 combinedIf.getElseRegion().
begin());
2655 YieldOp elseYield = combinedIf.elseYield();
2656 YieldOp elseYield2 = cast<YieldOp>(nextElse->
getTerminator());
2657 rewriter.
mergeBlocks(nextElse, combinedIf.elseBlock());
2661 SmallVector<Value> mergedElseYields(elseYield.getOperands());
2662 llvm::append_range(mergedElseYields, elseYield2.getOperands());
2664 YieldOp::create(rewriter, elseYield2.getLoc(), mergedElseYields);
2670 SmallVector<Value> prevValues;
2671 SmallVector<Value> nextValues;
2672 for (
const auto &pair : llvm::enumerate(combinedIf.getResults())) {
2673 if (pair.index() < prevIf.getNumResults())
2674 prevValues.push_back(pair.value());
2676 nextValues.push_back(pair.value());
2685struct RemoveEmptyElseBranch :
public OpRewritePattern<IfOp> {
2686 using OpRewritePattern<IfOp>::OpRewritePattern;
2688 LogicalResult matchAndRewrite(IfOp ifOp,
2689 PatternRewriter &rewriter)
const override {
2691 if (ifOp.getNumResults())
2693 Block *elseBlock = ifOp.elseBlock();
2694 if (!elseBlock || !llvm::hasSingleElement(*elseBlock))
2698 newIfOp.getThenRegion().begin());
2720struct CombineNestedIfs :
public OpRewritePattern<IfOp> {
2721 using OpRewritePattern<IfOp>::OpRewritePattern;
2723 LogicalResult matchAndRewrite(IfOp op,
2724 PatternRewriter &rewriter)
const override {
2725 auto nestedOps = op.thenBlock()->without_terminator();
2727 if (!llvm::hasSingleElement(nestedOps))
2731 if (op.elseBlock() && !llvm::hasSingleElement(*op.elseBlock()))
2734 auto nestedIf = dyn_cast<IfOp>(*nestedOps.begin());
2738 if (nestedIf.elseBlock() && !llvm::hasSingleElement(*nestedIf.elseBlock()))
2741 SmallVector<Value> thenYield(op.thenYield().getOperands());
2742 SmallVector<Value> elseYield;
2744 llvm::append_range(elseYield, op.elseYield().getOperands());
2748 SmallVector<unsigned> elseYieldsToUpgradeToSelect;
2757 for (
const auto &tup : llvm::enumerate(thenYield)) {
2758 if (tup.value().getDefiningOp() == nestedIf) {
2759 auto nestedIdx = llvm::cast<OpResult>(tup.value()).getResultNumber();
2760 if (nestedIf.elseYield().getOperand(nestedIdx) !=
2761 elseYield[tup.index()]) {
2766 thenYield[tup.index()] = nestedIf.thenYield().getOperand(nestedIdx);
2779 if (tup.value().getParentRegion() == &op.getThenRegion()) {
2782 elseYieldsToUpgradeToSelect.push_back(tup.index());
2785 Location loc = op.getLoc();
2786 Value newCondition = arith::AndIOp::create(rewriter, loc, op.getCondition(),
2787 nestedIf.getCondition());
2788 auto newIf = IfOp::create(rewriter, loc, op.getResultTypes(), newCondition);
2791 SmallVector<Value> results;
2792 llvm::append_range(results, newIf.getResults());
2795 for (
auto idx : elseYieldsToUpgradeToSelect)
2797 arith::SelectOp::create(rewriter, op.getLoc(), op.getCondition(),
2798 thenYield[idx], elseYield[idx]);
2800 rewriter.
mergeBlocks(nestedIf.thenBlock(), newIfBlock);
2803 if (!elseYield.empty()) {
2806 YieldOp::create(rewriter, loc, elseYield);
2815void IfOp::getCanonicalizationPatterns(RewritePatternSet &results,
2816 MLIRContext *context) {
2817 results.
add<CombineIfs, CombineNestedIfs, ConditionPropagation,
2818 ConvertTrivialIfToSelect, RemoveEmptyElseBranch,
2819 ReplaceIfYieldWithConditionOrValue>(context);
2821 results, IfOp::getOperationName());
2823 IfOp::getOperationName());
2826Block *IfOp::thenBlock() {
return &getThenRegion().back(); }
2827YieldOp IfOp::thenYield() {
return cast<YieldOp>(&thenBlock()->back()); }
2828Block *IfOp::elseBlock() {
2829 Region &r = getElseRegion();
2834YieldOp IfOp::elseYield() {
return cast<YieldOp>(&elseBlock()->back()); }
2840void ParallelOp::build(
2846 result.addOperands(lowerBounds);
2847 result.addOperands(upperBounds);
2848 result.addOperands(steps);
2849 result.addOperands(initVals);
2851 result.addAttribute(getUnsignedCmpAttrName(
result.name),
2854 ParallelOp::getOperandSegmentSizeAttr(),
2856 static_cast<int32_t>(upperBounds.size()),
2857 static_cast<int32_t>(steps.size()),
2858 static_cast<int32_t>(initVals.size())}));
2861 OpBuilder::InsertionGuard guard(builder);
2862 unsigned numIVs = steps.size();
2863 SmallVector<Type, 8> argTypes(numIVs, builder.
getIndexType());
2864 SmallVector<Location, 8> argLocs(numIVs,
result.location);
2865 Region *bodyRegion =
result.addRegion();
2868 if (bodyBuilderFn) {
2870 bodyBuilderFn(builder,
result.location,
2875 if (initVals.empty())
2876 ParallelOp::ensureTerminator(*bodyRegion, builder,
result.location);
2879void ParallelOp::build(
2887 auto wrappedBuilderFn = [&bodyBuilderFn](OpBuilder &nestedBuilder,
2890 bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
2894 wrapper = wrappedBuilderFn;
2896 build(builder,
result, lowerBounds, upperBounds, steps,
ValueRange(), wrapper,
2900LogicalResult ParallelOp::verify() {
2905 if (stepValues.empty())
2907 "needs at least one tuple element for lowerBound, upperBound and step");
2910 for (Value stepValue : stepValues)
2913 return emitOpError(
"constant step operand must be positive");
2917 Block *body = getBody();
2919 return emitOpError() <<
"expects the same number of induction variables: "
2921 <<
" as bound and step values: " << stepValues.size();
2923 if (!arg.getType().isIndex())
2925 "expects arguments for the induction variable to be of index type");
2929 *
this, getRegion(),
"expects body to terminate with 'scf.reduce'");
2934 auto resultsSize = getResults().size();
2935 auto reductionsSize = reduceOp.getReductions().size();
2936 auto initValsSize = getInitVals().size();
2937 if (resultsSize != reductionsSize)
2938 return emitOpError() <<
"expects number of results: " << resultsSize
2939 <<
" to be the same as number of reductions: "
2941 if (resultsSize != initValsSize)
2942 return emitOpError() <<
"expects number of results: " << resultsSize
2943 <<
" to be the same as number of initial values: "
2945 if (reduceOp.getNumOperands() != initValsSize)
2950 for (int64_t i = 0; i < static_cast<int64_t>(reductionsSize); ++i) {
2951 auto resultType = getOperation()->getResult(i).getType();
2952 auto reductionOperandType = reduceOp.getOperands()[i].getType();
2953 if (resultType != reductionOperandType)
2954 return reduceOp.emitOpError()
2955 <<
"expects type of " << i
2956 <<
"-th reduction operand: " << reductionOperandType
2957 <<
" to be the same as the " << i
2958 <<
"-th result type: " << resultType;
2963ParseResult ParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
2966 result.addAttribute(getUnsignedCmpAttrName(
result.name),
2970 SmallVector<OpAsmParser::Argument, 4> ivs;
2975 SmallVector<OpAsmParser::UnresolvedOperand, 4> lower;
2982 SmallVector<OpAsmParser::UnresolvedOperand, 4> upper;
2990 SmallVector<OpAsmParser::UnresolvedOperand, 4> steps;
2998 SmallVector<OpAsmParser::UnresolvedOperand, 4> initVals;
3009 Region *body =
result.addRegion();
3010 for (
auto &iv : ivs)
3017 ParallelOp::getOperandSegmentSizeAttr(),
3019 static_cast<int32_t>(upper.size()),
3020 static_cast<int32_t>(steps.size()),
3021 static_cast<int32_t>(initVals.size())}));
3030 ParallelOp::ensureTerminator(*body, builder,
result.location);
3034void ParallelOp::print(OpAsmPrinter &p) {
3035 if (getUnsignedCmp())
3037 p <<
" (" << getBody()->getArguments() <<
") = (" <<
getLowerBound()
3038 <<
") to (" <<
getUpperBound() <<
") step (" << getStep() <<
")";
3039 if (!getInitVals().empty())
3040 p <<
" init (" << getInitVals() <<
")";
3047SmallVector<Region *> ParallelOp::getLoopRegions() {
return {&getRegion()}; }
3049std::optional<SmallVector<Value>> ParallelOp::getLoopInductionVars() {
3050 return SmallVector<Value>{getBody()->getArguments()};
3053std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopLowerBounds() {
3057std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopUpperBounds() {
3061std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopSteps() {
3066 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
3068 return ParallelOp();
3069 assert(ivArg.getOwner() &&
"unlinked block argument");
3070 auto *containingOp = ivArg.getOwner()->getParentOp();
3071 return dyn_cast<ParallelOp>(containingOp);
3076struct ParallelOpSingleOrZeroIterationDimsFolder
3080 LogicalResult matchAndRewrite(ParallelOp op,
3087 for (
auto [lb,
ub, step, iv] :
3088 llvm::zip(op.getLowerBound(), op.getUpperBound(), op.getStep(),
3089 op.getInductionVars())) {
3092 if (numIterations.has_value()) {
3094 if (*numIterations == 0) {
3095 rewriter.
replaceOp(op, op.getInitVals());
3100 if (*numIterations == 1) {
3105 newLowerBounds.push_back(lb);
3106 newUpperBounds.push_back(ub);
3107 newSteps.push_back(step);
3110 if (newLowerBounds.size() == op.getLowerBound().size())
3113 if (newLowerBounds.empty()) {
3116 SmallVector<Value> results;
3117 results.reserve(op.getInitVals().size());
3118 for (
auto &bodyOp : op.getBody()->without_terminator())
3119 rewriter.
clone(bodyOp, mapping);
3120 auto reduceOp = cast<ReduceOp>(op.getBody()->getTerminator());
3121 for (int64_t i = 0, e = reduceOp.getReductions().size(); i < e; ++i) {
3122 Block &reduceBlock = reduceOp.getReductions()[i].front();
3123 auto initValIndex = results.size();
3124 mapping.
map(reduceBlock.
getArgument(0), op.getInitVals()[initValIndex]);
3128 rewriter.
clone(reduceBodyOp, mapping);
3131 cast<ReduceReturnOp>(reduceBlock.
getTerminator()).getResult());
3132 results.push_back(
result);
3139 auto newOp = ParallelOp::create(rewriter, op.getLoc(), newLowerBounds,
3140 newUpperBounds, newSteps, op.getInitVals(),
3141 nullptr, op.getUnsignedCmp());
3147 newOp.getRegion().begin(), mapping);
3148 rewriter.
replaceOp(op, newOp.getResults());
3153struct MergeNestedParallelLoops :
public OpRewritePattern<ParallelOp> {
3154 using OpRewritePattern<ParallelOp>::OpRewritePattern;
3156 LogicalResult matchAndRewrite(ParallelOp op,
3157 PatternRewriter &rewriter)
const override {
3158 Block &outerBody = *op.getBody();
3162 auto innerOp = dyn_cast<ParallelOp>(outerBody.
front());
3166 if (op.getUnsignedCmp() != innerOp.getUnsignedCmp())
3170 if (llvm::is_contained(innerOp.getLowerBound(), val) ||
3171 llvm::is_contained(innerOp.getUpperBound(), val) ||
3172 llvm::is_contained(innerOp.getStep(), val))
3176 if (!op.getInitVals().empty() || !innerOp.getInitVals().empty())
3179 auto bodyBuilder = [&](OpBuilder &builder, Location ,
3181 Block &innerBody = *innerOp.getBody();
3182 assert(iterVals.size() ==
3190 builder.
clone(op, mapping);
3193 auto concatValues = [](
const auto &first,
const auto &second) {
3194 SmallVector<Value> ret;
3195 ret.reserve(first.size() + second.size());
3196 ret.assign(first.begin(), first.end());
3197 ret.append(second.begin(), second.end());
3201 auto newLowerBounds =
3202 concatValues(op.getLowerBound(), innerOp.getLowerBound());
3203 auto newUpperBounds =
3204 concatValues(op.getUpperBound(), innerOp.getUpperBound());
3205 auto newSteps = concatValues(op.getStep(), innerOp.getStep());
3216void ParallelOp::getCanonicalizationPatterns(RewritePatternSet &results,
3217 MLIRContext *context) {
3219 .
add<ParallelOpSingleOrZeroIterationDimsFolder, MergeNestedParallelLoops>(
3228void ParallelOp::getSuccessorRegions(
3229 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
3233 regions.push_back(RegionSuccessor(&getRegion()));
3234 regions.push_back(RegionSuccessor(getOperation()));
3241void ReduceOp::build(OpBuilder &builder, OperationState &
result) {}
3243void ReduceOp::build(OpBuilder &builder, OperationState &
result,
3245 result.addOperands(operands);
3246 for (Value v : operands) {
3247 OpBuilder::InsertionGuard guard(builder);
3248 Region *bodyRegion =
result.addRegion();
3250 ArrayRef<Type>{v.getType(), v.getType()},
3255LogicalResult ReduceOp::verifyRegions() {
3256 if (getReductions().size() != getOperands().size())
3257 return emitOpError() <<
"expects number of reduction regions: "
3258 << getReductions().size()
3259 <<
" to be the same as number of reduction operands: "
3260 << getOperands().size();
3263 for (int64_t i = 0, e = getReductions().size(); i < e; ++i) {
3264 auto type = getOperands()[i].getType();
3265 Block &block = getReductions()[i].front();
3267 return emitOpError() << i <<
"-th reduction has an empty body";
3269 llvm::any_of(block.
getArguments(), [&](
const BlockArgument &arg) {
3270 return arg.getType() != type;
3272 return emitOpError() <<
"expected two block arguments with type " << type
3273 <<
" in the " << i <<
"-th reduction region";
3277 return emitOpError(
"reduction bodies must be terminated with an "
3278 "'scf.reduce.return' op");
3285ReduceOp::getMutableSuccessorOperands(RegionSuccessor point) {
3287 return MutableOperandRange(getOperation(), 0, 0);
3294LogicalResult ReduceReturnOp::verify() {
3297 Block *reductionBody = getOperation()->getBlock();
3299 assert(isa<ReduceOp>(reductionBody->
getParentOp()) &&
"expected scf.reduce");
3301 if (expectedResultType != getResult().
getType())
3302 return emitOpError() <<
"must have type " << expectedResultType
3303 <<
" (the type of the reduction inputs)";
3311void WhileOp::build(::mlir::OpBuilder &odsBuilder,
3312 ::mlir::OperationState &odsState,
TypeRange resultTypes,
3313 ValueRange inits, BodyBuilderFn beforeBuilder,
3314 BodyBuilderFn afterBuilder) {
3318 OpBuilder::InsertionGuard guard(odsBuilder);
3321 SmallVector<Location, 4> beforeArgLocs;
3322 beforeArgLocs.reserve(inits.size());
3323 for (Value operand : inits) {
3324 beforeArgLocs.push_back(operand.getLoc());
3327 Region *beforeRegion = odsState.
addRegion();
3329 inits.getTypes(), beforeArgLocs);
3334 SmallVector<Location, 4> afterArgLocs(resultTypes.size(), odsState.
location);
3336 Region *afterRegion = odsState.
addRegion();
3338 resultTypes, afterArgLocs);
3344ConditionOp WhileOp::getConditionOp() {
3345 return cast<ConditionOp>(getBeforeBody()->getTerminator());
3348YieldOp WhileOp::getYieldOp() {
3349 return cast<YieldOp>(getAfterBody()->getTerminator());
3352std::optional<MutableArrayRef<OpOperand>> WhileOp::getYieldedValuesMutable() {
3353 return getYieldOp().getResultsMutable();
3357 return getBeforeBody()->getArguments();
3361 return getAfterBody()->getArguments();
3365 return getBeforeArguments();
3368OperandRange WhileOp::getEntrySuccessorOperands(RegionSuccessor successor) {
3370 "WhileOp is expected to branch only to the first region");
3374void WhileOp::getSuccessorRegions(RegionBranchPoint point,
3375 SmallVectorImpl<RegionSuccessor> ®ions) {
3378 regions.emplace_back(&getBefore());
3382 assert(llvm::is_contained(
3383 {&getAfter(), &getBefore()},
3385 "there are only two regions in a WhileOp");
3389 regions.emplace_back(&getBefore());
3393 regions.push_back(RegionSuccessor(getOperation()));
3394 regions.emplace_back(&getAfter());
3397ValueRange WhileOp::getSuccessorInputs(RegionSuccessor successor) {
3399 return getOperation()->getResults();
3400 if (successor == &getBefore())
3401 return getBefore().getArguments();
3402 if (successor == &getAfter())
3403 return getAfter().getArguments();
3404 llvm_unreachable(
"invalid region successor");
3407SmallVector<Region *> WhileOp::getLoopRegions() {
3408 return {&getBefore(), &getAfter()};
3418ParseResult scf::WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
3419 SmallVector<OpAsmParser::Argument, 4> regionArgs;
3420 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
3421 Region *before =
result.addRegion();
3422 Region *after =
result.addRegion();
3424 OptionalParseResult listResult =
3429 FunctionType functionType;
3434 result.addTypes(functionType.getResults());
3436 if (functionType.getNumInputs() != operands.size()) {
3438 <<
"expected as many input types as operands " <<
"(expected "
3439 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
3449 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
3450 regionArgs[i].type = functionType.getInput(i);
3452 return failure(parser.
parseRegion(*before, regionArgs) ||
3458void scf::WhileOp::print(OpAsmPrinter &p) {
3467 (*this)->getDiscardableAttrDictionary().getValue());
3470LogicalResult scf::WhileOp::verify() {
3473 "expects the 'before' region to terminate with 'scf.condition'");
3474 if (!beforeTerminator)
3479 "expects the 'after' region to terminate with 'scf.yield'");
3480 return success(afterTerminator !=
nullptr);
3517struct WhileMoveIfDown :
public OpRewritePattern<scf::WhileOp> {
3518 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3520 LogicalResult matchAndRewrite(scf::WhileOp op,
3521 PatternRewriter &rewriter)
const override {
3522 auto conditionOp = op.getConditionOp();
3530 auto ifOp = dyn_cast_or_null<scf::IfOp>(conditionOp->getPrevNode());
3536 if (!ifOp || ifOp.getCondition() != conditionOp.getCondition() ||
3537 (ifOp.elseBlock() && !ifOp.elseBlock()->without_terminator().empty()))
3540 assert((ifOp->use_empty() || (llvm::all_equal(ifOp->getUsers()) &&
3541 *ifOp->user_begin() == conditionOp)) &&
3542 "ifOp has unexpected uses");
3544 Location loc = op.getLoc();
3554 for (
auto [idx, arg] : llvm::enumerate(conditionOp.getArgs())) {
3555 auto it = llvm::find(ifOp->getResults(), arg);
3556 if (it == ifOp->getResults().end())
3558 size_t ifOpIdx = it.getIndex();
3560 ifOp.thenYield()->getOperand(ifOpIdx));
3561 unsigned argIdx = idx;
3562 Value elseValue = ifOp.elseYield()->getOperand(ifOpIdx);
3564 conditionOp.getArgsMutable()[argIdx].assign(elseValue);
3571 if (&op.getBefore() == operand->get().getParentRegion())
3572 additionalUsedValuesSet.insert(operand->get());
3576 auto additionalUsedValues = additionalUsedValuesSet.getArrayRef();
3577 auto additionalValueTypes = llvm::map_to_vector(
3578 additionalUsedValues, [](Value val) {
return val.
getType(); });
3579 size_t additionalValueSize = additionalUsedValues.size();
3580 SmallVector<Type> newResultTypes(op.getResultTypes());
3581 newResultTypes.append(additionalValueTypes);
3584 scf::WhileOp::create(rewriter, loc, newResultTypes, op.getInits());
3587 newWhileOp.getBefore().takeBody(op.getBefore());
3588 newWhileOp.getAfter().takeBody(op.getAfter());
3589 newWhileOp.getAfter().addArguments(
3590 additionalValueTypes,
3591 SmallVector<Location>(additionalValueSize, loc));
3595 conditionOp.getArgsMutable().append(additionalUsedValues);
3601 additionalUsedValues,
3602 newWhileOp.getAfterArguments().take_back(additionalValueSize),
3603 [&](OpOperand &use) {
3604 return ifOp.getThenRegion().isAncestor(
3605 use.getOwner()->getParentRegion());
3609 rewriter.
eraseOp(ifOp.thenYield());
3611 newWhileOp.getAfterBody()->begin());
3614 newWhileOp->getResults().drop_back(additionalValueSize));
3638struct WhileConditionTruth :
public OpRewritePattern<WhileOp> {
3639 using OpRewritePattern<WhileOp>::OpRewritePattern;
3641 LogicalResult matchAndRewrite(WhileOp op,
3642 PatternRewriter &rewriter)
const override {
3643 auto term = op.getConditionOp();
3647 Value constantTrue =
nullptr;
3649 bool replaced =
false;
3650 for (
auto yieldedAndBlockArgs :
3651 llvm::zip(term.getArgs(), op.getAfterArguments())) {
3652 if (std::get<0>(yieldedAndBlockArgs) == term.getCondition()) {
3653 if (!std::get<1>(yieldedAndBlockArgs).use_empty()) {
3655 constantTrue = arith::ConstantOp::create(
3656 rewriter, op.getLoc(), term.getCondition().getType(),
3691struct WhileCmpCond :
public OpRewritePattern<scf::WhileOp> {
3692 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3694 LogicalResult matchAndRewrite(scf::WhileOp op,
3695 PatternRewriter &rewriter)
const override {
3696 using namespace scf;
3697 auto cond = op.getConditionOp();
3698 auto cmp = cond.getCondition().getDefiningOp<arith::CmpIOp>();
3701 bool changed =
false;
3702 for (
auto tup : llvm::zip(cond.getArgs(), op.getAfterArguments())) {
3703 for (
size_t opIdx = 0; opIdx < 2; opIdx++) {
3704 if (std::get<0>(tup) != cmp.getOperand(opIdx))
3707 llvm::make_early_inc_range(std::get<1>(tup).getUses())) {
3708 auto cmp2 = dyn_cast<arith::CmpIOp>(u.getOwner());
3712 if (cmp2.getOperand(1 - opIdx) != cmp.getOperand(1 - opIdx))
3715 if (cmp2.getPredicate() == cmp.getPredicate())
3716 samePredicate =
true;
3717 else if (cmp2.getPredicate() ==
3718 arith::invertPredicate(cmp.getPredicate()))
3719 samePredicate =
false;
3735static std::optional<SmallVector<unsigned>> getArgsMapping(
ValueRange args1,
3737 if (args1.size() != args2.size())
3738 return std::nullopt;
3740 SmallVector<unsigned> ret(args1.size());
3741 for (
auto &&[i, arg1] : llvm::enumerate(args1)) {
3742 auto it = llvm::find(args2, arg1);
3743 if (it == args2.end())
3744 return std::nullopt;
3746 ret[std::distance(args2.begin(), it)] =
static_cast<unsigned>(i);
3753 llvm::SmallDenseSet<Value> set;
3754 for (Value arg : args) {
3755 if (!set.insert(arg).second)
3765struct WhileOpAlignBeforeArgs :
public OpRewritePattern<WhileOp> {
3768 LogicalResult matchAndRewrite(WhileOp loop,
3769 PatternRewriter &rewriter)
const override {
3770 auto *oldBefore = loop.getBeforeBody();
3771 ConditionOp oldTerm = loop.getConditionOp();
3772 ValueRange beforeArgs = oldBefore->getArguments();
3774 if (beforeArgs == termArgs)
3777 if (hasDuplicates(termArgs))
3780 auto mapping = getArgsMapping(beforeArgs, termArgs);
3785 OpBuilder::InsertionGuard g(rewriter);
3791 auto *oldAfter = loop.getAfterBody();
3793 SmallVector<Type> newResultTypes(beforeArgs.size());
3794 for (
auto &&[i, j] : llvm::enumerate(*mapping))
3795 newResultTypes[j] = loop.getResult(i).getType();
3797 auto newLoop = WhileOp::create(
3798 rewriter, loop.getLoc(), newResultTypes, loop.getInits(),
3800 auto *newBefore = newLoop.getBeforeBody();
3801 auto *newAfter = newLoop.getAfterBody();
3803 SmallVector<Value> newResults(beforeArgs.size());
3804 SmallVector<Value> newAfterArgs(beforeArgs.size());
3805 for (
auto &&[i, j] : llvm::enumerate(*mapping)) {
3806 newResults[i] = newLoop.getResult(j);
3807 newAfterArgs[i] = newAfter->getArgument(j);
3811 newBefore->getArguments());
3821void WhileOp::getCanonicalizationPatterns(RewritePatternSet &results,
3822 MLIRContext *context) {
3823 results.
add<WhileConditionTruth, WhileCmpCond, WhileOpAlignBeforeArgs,
3824 WhileMoveIfDown>(context);
3826 results, WhileOp::getOperationName());
3828 WhileOp::getOperationName());
3842 Region ®ion = *caseRegions.emplace_back(std::make_unique<Region>());
3845 caseValues.push_back(value);
3854 for (
auto [value, region] : llvm::zip(cases.
asArrayRef(), caseRegions)) {
3856 p <<
"case " << value <<
' ';
3861LogicalResult scf::IndexSwitchOp::verify() {
3862 if (getCases().size() != getCaseRegions().size()) {
3863 return emitOpError(
"has ")
3864 << getCaseRegions().size() <<
" case regions but "
3865 << getCases().size() <<
" case values";
3869 for (int64_t value : getCases())
3870 if (!valueSet.insert(value).second)
3871 return emitOpError(
"has duplicate case value: ") << value;
3872 auto verifyRegion = [&](Region ®ion,
const Twine &name) -> LogicalResult {
3873 auto yield = dyn_cast<YieldOp>(region.
front().
back());
3875 return emitOpError(
"expected region to end with scf.yield, but got ")
3878 if (yield.getNumOperands() != getNumResults()) {
3879 return (emitOpError(
"expected each region to return ")
3880 << getNumResults() <<
" values, but " << name <<
" returns "
3881 << yield.getNumOperands())
3882 .attachNote(yield.getLoc())
3883 <<
"see yield operation here";
3885 for (
auto [idx,
result, operand] :
3886 llvm::enumerate(getResultTypes(), yield.getOperands())) {
3888 return yield.emitOpError() <<
"operand " << idx <<
" is null\n";
3889 if (
result == operand.getType())
3891 return (emitOpError(
"expected result #")
3892 << idx <<
" of each region to be " <<
result)
3893 .attachNote(yield.getLoc())
3894 << name <<
" returns " << operand.getType() <<
" here";
3901 for (
auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
3908unsigned scf::IndexSwitchOp::getNumCases() {
return getCases().size(); }
3910Block &scf::IndexSwitchOp::getDefaultBlock() {
3911 return getDefaultRegion().front();
3914Block &scf::IndexSwitchOp::getCaseBlock(
unsigned idx) {
3915 assert(idx < getNumCases() &&
"case index out-of-bounds");
3916 return getCaseRegions()[idx].front();
3919void IndexSwitchOp::getSuccessorRegions(
3920 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
3923 successors.push_back(RegionSuccessor(getOperation()));
3927 llvm::append_range(successors, getRegions());
3930ValueRange IndexSwitchOp::getSuccessorInputs(RegionSuccessor successor) {
3935void IndexSwitchOp::getEntrySuccessorRegions(
3936 ArrayRef<Attribute> operands,
3937 SmallVectorImpl<RegionSuccessor> &successors) {
3938 FoldAdaptor adaptor(operands, *
this);
3941 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
3943 llvm::append_range(successors, getRegions());
3949 for (
auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
3950 if (caseValue == arg.getInt()) {
3951 successors.emplace_back(&caseRegion);
3955 successors.emplace_back(&getDefaultRegion());
3958void IndexSwitchOp::getRegionInvocationBounds(
3959 ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
3960 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
3961 if (!operandValue) {
3963 bounds.append(getNumRegions(), InvocationBounds(0, 1));
3967 unsigned liveIndex = getNumRegions() - 1;
3968 const auto *it = llvm::find(getCases(), operandValue.getInt());
3969 if (it != getCases().end())
3970 liveIndex = std::distance(getCases().begin(), it);
3971 for (
unsigned i = 0, e = getNumRegions(); i < e; ++i)
3972 bounds.emplace_back(0, i == liveIndex);
3975void IndexSwitchOp::getCanonicalizationPatterns(RewritePatternSet &results,
3976 MLIRContext *context) {
3978 results, IndexSwitchOp::getOperationName());
3980 results, IndexSwitchOp::getOperationName());
3987#define GET_OP_CLASSES
3988#include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
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 ®ion, const Twine &name)
static ParseResult parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse the case regions and values.
static void printSwitchCases(OpAsmPrinter &p, Operation *op, DenseI64ArrayAttr cases, RegionRange caseRegions)
Print the case regions and values.
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,...
static TerminatorTy verifyAndGetTerminator(Operation *op, Region ®ion, StringRef errorMessage)
Verifies that the first block of the given region is terminated by a TerminatorTy.
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.
*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`
@ 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.
unsigned getArgNumber() const
Returns the number of this argument.
Block represents an ordered list of Operations.
MutableArrayRef< BlockArgument > BlockArgListType
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
iterator_range< args_iterator > addArguments(TypeRange types, ArrayRef< Location > locs)
Add one argument to the argument list for each type specified in the list.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
BlockArgListType getArguments()
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
bool getValue() const
Return the boolean value of this attribute.
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
IntegerType getIntegerType(unsigned width)
BoolAttr getBoolAttr(bool value)
StringAttr getStringAttr(const Twine &bytes)
MLIRContext * getContext() const
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
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...
This is a utility class for mapping one set of IR entities to another.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
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...
MLIRContext is the top-level object for a collection of MLIR operations.
This class provides a mutable adaptor for a range of operands.
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 ®ion, 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.
This class helps build Operations.
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.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
void cloneRegionBefore(Region ®ion, Region &parent, Region::iterator before, IRMapping &mapping)
Clone the blocks that belong to "region" before the given position in another region "parent".
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Operation * cloneWithoutRegions(Operation &op, IRMapping &mapper)
Creates a deep copy of this operation but keep the operation regions empty.
This class represents a single result from folding an operation.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Set of flags used to control the behavior of the various IR print methods (e.g.
This is a value defined by a result of an operation.
Operation * getOwner() const
Returns the operation that owns this result.
unsigned getResultNumber() const
Returns the number of this result.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
OperandRange operand_range
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
auto getDiscardableAttrs()
Return a range of all of discardable attributes on this operation.
OperationName getName()
The name of an operation is the key identifier for it.
Region * getParentRegion()
Returns the region to which the instruction belongs.
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.
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.
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
unsigned getNumArguments()
BlockArgument getArgument(unsigned i)
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 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 ®ion, 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...
virtual bool 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.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
This class provides an abstraction over the different types of ranges over Values.
type_range getTypes() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
user_range getUsers() const
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Region * getParentRegion()
Return the Region in which this Value is defined.
ArrayRef< T > asArrayRef() const
Operation * getOwner() const
Return the owner of this operand.
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.
ParallelOp getParallelForInductionVarOwner(Value val)
Returns the parallel loop parent of an induction variable.
void buildTerminatedBody(OpBuilder &builder, Location loc)
Default callback for IfOp builders. Inserts a yield without arguments.
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.
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.
std::optional< llvm::APSInt > computeUbMinusLb(Value lb, Value ub, bool isSigned)
Helper function to compute the difference between two values.
ForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
SmallVector< Value > ValueVector
An owning vector of values, handy to return from functions.
bool isShapePreserving(ForOp forOp, int64_t arg)
A simple, conservative analysis to determine if the loop is shape preserving.
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...
ForallOp getForallOpThreadIndexOwner(Value val)
Returns the ForallOp parent of an thread index variable.
SmallVector< Value > replaceAndCastForOpIterArg(RewriterBase &rewriter, scf::ForOp forOp, OpOperand &operand, Value replacement, const ValueTypeCastFnTy &castFn)
LogicalResult promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
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.
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...
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.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
llvm::SetVector< T, Vector, Set, N > SetVector
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
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.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
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.
void visitUsedValuesDefinedAbove(Region ®ion, 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
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
UnresolvedOperand ssaName
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.