31#include "llvm/ADT/MapVector.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/ADT/SmallPtrSet.h"
34#include "llvm/Support/Casting.h"
35#include "llvm/Support/DebugLog.h"
41#include "mlir/Dialect/SCF/IR/SCFOpsDialect.cpp.inc"
48struct SCFInlinerInterface :
public DialectInlinerInterface {
49 using DialectInlinerInterface::DialectInlinerInterface;
53 IRMapping &valueMapping)
const final {
58 bool isLegalToInline(Operation *, Region *,
bool, IRMapping &)
const final {
63 void handleTerminator(Operation *op,
ValueRange valuesToRepl)
const final {
64 auto retValOp = dyn_cast<scf::YieldOp>(op);
68 for (
auto retValue : llvm::zip(valuesToRepl, retValOp.getOperands())) {
69 std::get<0>(retValue).replaceAllUsesWith(std::get<1>(retValue));
79void SCFDialect::initialize() {
82#include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
84 addInterfaces<SCFInlinerInterface>();
85 declarePromisedInterface<ConvertToEmitCPatternInterface, SCFDialect>();
86 declarePromisedInterfaces<bufferization::BufferDeallocationOpInterface,
87 InParallelOp, ReduceReturnOp>();
88 declarePromisedInterfaces<bufferization::BufferizableOpInterface, ConditionOp,
89 ExecuteRegionOp, ForOp, IfOp, IndexSwitchOp,
90 ForallOp, InParallelOp, WhileOp, YieldOp>();
91 declarePromisedInterface<ValueBoundsOpInterface, ForOp>();
96 scf::YieldOp::create(builder, loc);
101template <
typename TerminatorTy>
103 StringRef errorMessage) {
104 Operation *terminatorOperation =
nullptr;
106 terminatorOperation = ®ion.
front().
back();
107 if (
auto yield = dyn_cast_or_null<TerminatorTy>(terminatorOperation))
111 if (terminatorOperation)
112 diag.attachNote(terminatorOperation->
getLoc()) <<
"terminator here";
119 auto addOp =
ub.getDefiningOp<arith::AddIOp>();
122 if ((isSigned && !addOp.hasNoSignedWrap()) ||
123 (!isSigned && !addOp.hasNoUnsignedWrap()))
126 if (addOp.getLhs() != lb ||
147ParseResult ExecuteRegionOp::parse(
OpAsmParser &parser,
175LogicalResult ExecuteRegionOp::verify() {
176 if (getRegion().empty())
177 return emitOpError(
"region needs to have at least one block");
178 if (getRegion().front().getNumArguments() > 0)
179 return emitOpError(
"region cannot have any arguments");
225 if (op.getNoInline())
227 if (!isa<FunctionOpInterface, ExecuteRegionOp>(op->getParentOp()))
230 Block *prevBlock = op->getBlock();
234 cf::BranchOp::create(rewriter, op.getLoc(), &op.getRegion().front());
236 for (
Block &blk : op.getRegion()) {
237 if (YieldOp yieldOp = dyn_cast<YieldOp>(blk.getTerminator())) {
239 cf::BranchOp::create(rewriter, yieldOp.getLoc(), postBlock,
240 yieldOp.getResults());
248 for (
auto res : op.getResults())
249 blockArgs.push_back(postBlock->
addArgument(res.getType(), res.getLoc()));
260 results, ExecuteRegionOp::getOperationName());
263 results, ExecuteRegionOp::getOperationName(),
265 return failure(cast<ExecuteRegionOp>(op).getNoInline());
269void ExecuteRegionOp::getSuccessorRegions(
281void ExecuteRegionOp::getRegionInvocationBounds(
283 bounds.emplace_back(1, 1);
299 "condition op can only exit the loop or branch to the after"
302 return getArgsMutable();
305void ConditionOp::getSuccessorRegions(
307 FoldAdaptor adaptor(operands, *
this);
309 WhileOp whileOp = getParentOp();
313 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
314 if (!boolAttr || boolAttr.getValue())
315 regions.emplace_back(&whileOp.getAfter());
316 if (!boolAttr || !boolAttr.getValue())
326 BodyBuilderFn bodyBuilder,
bool unsignedCmp) {
330 result.addAttribute(getUnsignedCmpAttrName(
result.name),
333 result.addOperands(initArgs);
334 for (
Value v : initArgs)
335 result.addTypes(v.getType());
340 for (
Value v : initArgs)
346 if (initArgs.empty() && !bodyBuilder) {
347 ForOp::ensureTerminator(*bodyRegion, builder,
result.location);
348 }
else if (bodyBuilder) {
356LogicalResult ForOp::verify() {
361 if (getBody()->getNumArguments() < getNumInductionVars())
362 return emitOpError(
"expected body to have at least ")
363 << getNumInductionVars()
364 <<
" argument(s) for the induction variable, but got "
365 << getBody()->getNumArguments();
368 if (getInitArgs().size() != getNumResults())
370 "mismatch in number of loop-carried values and defined values");
375LogicalResult ForOp::verifyRegions() {
377 if (getBody()->getNumArguments() < getNumInductionVars())
378 return emitOpError(
"expected body to have at least ")
379 << getNumInductionVars() <<
" argument(s) for the induction "
380 <<
"variable, but got " << getBody()->getNumArguments();
386 "expected induction variable to be same type as bounds and step");
388 if (getNumRegionIterArgs() != getNumResults())
390 "mismatch in number of basic block args and defined values");
392 auto initArgs = getInitArgs();
393 auto iterArgs = getRegionIterArgs();
394 auto opResults = getResults();
396 for (
auto e : llvm::zip(initArgs, iterArgs, opResults)) {
398 return emitOpError() <<
"types mismatch between " << i
399 <<
"th iter operand and defined value";
401 return emitOpError() <<
"types mismatch between " << i
402 <<
"th iter region arg and defined value";
409std::optional<SmallVector<Value>> ForOp::getLoopInductionVars() {
413std::optional<SmallVector<OpFoldResult>> ForOp::getLoopLowerBounds() {
417std::optional<SmallVector<OpFoldResult>> ForOp::getLoopSteps() {
421std::optional<SmallVector<OpFoldResult>> ForOp::getLoopUpperBounds() {
425bool ForOp::isValidInductionVarType(
Type type) {
430 if (bounds.size() != 1)
432 if (
auto val = dyn_cast<Value>(bounds[0])) {
440 if (bounds.size() != 1)
442 if (
auto val = dyn_cast<Value>(bounds[0])) {
450 if (steps.size() != 1)
452 if (
auto val = dyn_cast<Value>(steps[0])) {
459std::optional<ResultRange> ForOp::getLoopResults() {
return getResults(); }
463LogicalResult ForOp::promoteIfSingleIteration(
RewriterBase &rewriter) {
464 std::optional<APInt> tripCount = getStaticTripCount();
465 LDBG() <<
"promoteIfSingleIteration tripCount is " << tripCount
468 if (!tripCount.has_value() || tripCount->getZExtValue() > 1)
471 if (*tripCount == 0) {
478 auto yieldOp = cast<scf::YieldOp>(getBody()->getTerminator());
485 llvm::append_range(bbArgReplacements, getInitArgs());
489 getOperation()->getIterator(), bbArgReplacements);
505 StringRef prefix =
"") {
506 assert(blocksArgs.size() == initializers.size() &&
507 "expected same length of arguments and initializers");
508 if (initializers.empty())
512 llvm::interleaveComma(llvm::zip(blocksArgs, initializers), p, [&](
auto it) {
513 p << std::get<0>(it) <<
" = " << std::get<1>(it);
519 if (getUnsignedCmp())
522 p <<
" " << getInductionVar() <<
" = " <<
getLowerBound() <<
" to "
526 if (!getInitArgs().empty())
527 p <<
" -> (" << getInitArgs().getTypes() <<
')';
530 p <<
" : " << t <<
' ';
533 !getInitArgs().empty());
545 result.addAttribute(getUnsignedCmpAttrName(
result.name),
559 regionArgs.push_back(inductionVariable);
569 if (regionArgs.size() !=
result.types.size() + 1)
572 "mismatch in number of loop-carried values and defined values");
581 regionArgs.front().type = type;
582 for (
auto [iterArg, type] :
583 llvm::zip_equal(llvm::drop_begin(regionArgs),
result.types))
590 ForOp::ensureTerminator(*body, builder,
result.location);
599 for (
auto argOperandType : llvm::zip_equal(llvm::drop_begin(regionArgs),
600 operands,
result.types)) {
601 Type type = std::get<2>(argOperandType);
602 std::get<0>(argOperandType).type = type;
619 return getBody()->getArguments().drop_front(getNumInductionVars());
623 return getInitArgsMutable();
626FailureOr<LoopLikeOpInterface>
627ForOp::replaceWithAdditionalYields(
RewriterBase &rewriter,
629 bool replaceInitOperandUsesInLoop,
634 auto inits = llvm::to_vector(getInitArgs());
635 inits.append(newInitOperands.begin(), newInitOperands.end());
636 scf::ForOp newLoop = scf::ForOp::create(
639 newLoop->setDiscardableAttrs(getOperation()->getDiscardableAttrDictionary());
642 auto yieldOp = cast<scf::YieldOp>(getBody()->getTerminator());
644 newLoop.getBody()->getArguments().take_back(newInitOperands.size());
649 newYieldValuesFn(rewriter, getLoc(), newIterArgs);
650 assert(newInitOperands.size() == newYieldedValues.size() &&
651 "expected as many new yield values as new iter operands");
653 yieldOp.getResultsMutable().append(newYieldedValues);
659 newLoop.getBody()->getArguments().take_front(
660 getBody()->getNumArguments()));
662 if (replaceInitOperandUsesInLoop) {
665 for (
auto it : llvm::zip(newInitOperands, newIterArgs)) {
676 newLoop->getResults().take_front(getNumResults()));
677 return cast<LoopLikeOpInterface>(newLoop.getOperation());
681 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
684 assert(ivArg.getOwner() &&
"unlinked block argument");
685 auto *containingOp = ivArg.getOwner()->getParentOp();
686 return dyn_cast_or_null<ForOp>(containingOp);
690 return getInitArgs();
695 if (std::optional<APInt> tripCount = getStaticTripCount()) {
698 if (*tripCount == 0) {
707 }
else if (*tripCount == 1) {
731LogicalResult scf::ForallOp::promoteIfSingleIteration(RewriterBase &rewriter) {
732 for (
auto [lb, ub, step] :
733 llvm::zip(getMixedLowerBound(), getMixedUpperBound(), getMixedStep())) {
736 if (!tripCount.has_value() || *tripCount != 1)
745 return getBody()->getArguments().drop_front(getRank());
748MutableArrayRef<OpOperand> ForallOp::getInitsMutable() {
749 return getOutputsMutable();
755 scf::InParallelOp terminator = forallOp.getTerminator();
760 bbArgReplacements.append(forallOp.getOutputs().begin(),
761 forallOp.getOutputs().end());
765 forallOp->getIterator(), bbArgReplacements);
770 results.reserve(forallOp.getResults().size());
771 for (
auto &yieldingOp : terminator.getYieldingOps()) {
772 auto parallelInsertSliceOp =
773 dyn_cast<tensor::ParallelInsertSliceOp>(yieldingOp);
774 if (!parallelInsertSliceOp)
777 Value dst = parallelInsertSliceOp.getDest();
778 Value src = parallelInsertSliceOp.getSource();
779 if (llvm::isa<TensorType>(src.
getType())) {
780 results.push_back(tensor::InsertSliceOp::create(
781 rewriter, forallOp.getLoc(), dst.
getType(), src, dst,
782 parallelInsertSliceOp.getOffsets(), parallelInsertSliceOp.getSizes(),
783 parallelInsertSliceOp.getStrides(),
784 parallelInsertSliceOp.getStaticOffsets(),
785 parallelInsertSliceOp.getStaticSizes(),
786 parallelInsertSliceOp.getStaticStrides()));
788 llvm_unreachable(
"unsupported terminator");
803 assert(lbs.size() == ubs.size() &&
804 "expected the same number of lower and upper bounds");
805 assert(lbs.size() == steps.size() &&
806 "expected the same number of lower bounds and steps");
811 bodyBuilder ? bodyBuilder(builder, loc,
ValueRange(), iterArgs)
813 assert(results.size() == iterArgs.size() &&
814 "loop nest body must return as many values as loop has iteration "
816 return LoopNest{{}, std::move(results)};
824 loops.reserve(lbs.size());
825 ivs.reserve(lbs.size());
828 for (
unsigned i = 0, e = lbs.size(); i < e; ++i) {
829 auto loop = scf::ForOp::create(
830 builder, currentLoc, lbs[i], ubs[i], steps[i], currentIterArgs,
836 currentIterArgs = args;
837 currentLoc = nestedLoc;
843 loops.push_back(loop);
847 for (
unsigned i = 0, e = loops.size() - 1; i < e; ++i) {
849 scf::YieldOp::create(builder, loc, loops[i + 1].getResults());
856 ? bodyBuilder(builder, currentLoc, ivs,
857 loops.back().getRegionIterArgs())
859 assert(results.size() == iterArgs.size() &&
860 "loop nest body must return as many values as loop has iteration "
863 scf::YieldOp::create(builder, loc, results);
867 llvm::append_range(nestResults, loops.front().getResults());
868 return LoopNest{std::move(loops), std::move(nestResults)};
881 bodyBuilder(nestedBuilder, nestedLoc, ivs);
890 assert(operand.
getOwner() == forOp);
895 "expected an iter OpOperand");
897 "Expected a different type");
899 for (
OpOperand &opOperand : forOp.getInitArgsMutable()) {
904 newIterOperands.push_back(opOperand.get());
908 scf::ForOp newForOp = scf::ForOp::create(
909 rewriter, forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
910 forOp.getStep(), newIterOperands,
nullptr,
911 forOp.getUnsignedCmp());
912 newForOp->setDiscardableAttrs(
913 forOp->getDiscardableAttrDictionary().getValue());
914 Block &newBlock = newForOp.getRegion().
front();
922 BlockArgument newRegionIterArg = newForOp.getTiedLoopRegionIterArg(
924 Value castIn = castFn(rewriter, newForOp.getLoc(), oldType, newRegionIterArg);
925 newBlockTransferArgs[newRegionIterArg.
getArgNumber()] = castIn;
929 rewriter.
mergeBlocks(&oldBlock, &newBlock, newBlockTransferArgs);
932 auto clonedYieldOp = cast<scf::YieldOp>(newBlock.
getTerminator());
935 newRegionIterArg.
getArgNumber() - forOp.getNumInductionVars();
936 Value castOut = castFn(rewriter, newForOp.getLoc(), newType,
937 clonedYieldOp.getOperand(yieldIdx));
939 newYieldOperands[yieldIdx] = castOut;
940 scf::YieldOp::create(rewriter, newForOp.getLoc(), newYieldOperands);
941 rewriter.
eraseOp(clonedYieldOp);
946 newResults[yieldIdx] =
947 castFn(rewriter, newForOp.getLoc(), oldType, newResults[yieldIdx]);
982 LogicalResult matchAndRewrite(ForOp op,
984 for (
auto it : llvm::zip(op.getInitArgsMutable(), op.getResults())) {
985 OpOperand &iterOpOperand = std::get<0>(it);
988 incomingCast.getSource().getType() == incomingCast.getType())
993 incomingCast.getDest().getType(),
994 incomingCast.getSource().getType()))
996 if (!std::get<1>(it).hasOneUse())
1002 rewriter, op, iterOpOperand, incomingCast.getSource(),
1004 return tensor::CastOp::create(b, loc, type, source);
1013void ForOp::getCanonicalizationPatterns(RewritePatternSet &results,
1014 MLIRContext *context) {
1015 results.
add<ForOpTensorCastFolder>(context);
1017 results, ForOp::getOperationName());
1022 results, ForOp::getOperationName(),
1024 [](OpBuilder &builder, Location loc, Value value) {
1028 auto blockArg = cast<BlockArgument>(value);
1029 assert(blockArg.getArgNumber() == 0 &&
"expected induction variable");
1030 auto forOp = cast<ForOp>(blockArg.getOwner()->getParentOp());
1031 return forOp.getLowerBound();
1037std::optional<APInt> ForOp::getConstantStep() {
1040 return step.getValue();
1044std::optional<MutableArrayRef<OpOperand>> ForOp::getYieldedValuesMutable() {
1045 return cast<scf::YieldOp>(getBody()->getTerminator()).getResultsMutable();
1051 if (
auto constantStep = getConstantStep())
1052 if (*constantStep == 1)
1060std::optional<APInt> ForOp::getStaticTripCount() {
1069LogicalResult ForallOp::verify() {
1070 unsigned numLoops = getRank();
1072 if (getNumResults() != getOutputs().size())
1073 return emitOpError(
"produces ")
1074 << getNumResults() <<
" results, but has only "
1075 << getOutputs().size() <<
" outputs";
1078 auto *body = getBody();
1080 return emitOpError(
"region expects ") << numLoops <<
" arguments";
1081 for (int64_t i = 0; i < numLoops; ++i)
1083 return emitOpError(
"expects ")
1084 << i <<
"-th block argument to be an index";
1085 for (
unsigned i = 0; i < getOutputs().size(); ++i)
1087 return emitOpError(
"type mismatch between ")
1088 << i <<
"-th output and corresponding block argument";
1089 if (getMapping().has_value() && !getMapping()->empty()) {
1090 if (getDeviceMappingAttrs().size() != numLoops)
1091 return emitOpError() <<
"mapping attribute size must match op rank";
1092 if (
failed(getDeviceMaskingAttr()))
1094 <<
" supports at most one device masking attribute";
1098 Operation *op = getOperation();
1100 getStaticLowerBound(),
1101 getDynamicLowerBound())))
1104 getStaticUpperBound(),
1105 getDynamicUpperBound())))
1108 getStaticStep(), getDynamicStep())))
1114void ForallOp::print(OpAsmPrinter &p) {
1115 Operation *op = getOperation();
1116 p <<
" (" << getInductionVars();
1117 if (isNormalized()) {
1138 if (!getRegionOutArgs().empty())
1139 p <<
"-> (" << getResultTypes() <<
") ";
1140 p.printRegion(getRegion(),
1142 getNumResults() > 0);
1144 if (
ArrayAttr mapping = getMappingAttr())
1147 p.printOptionalAttrDict(attrs);
1150ParseResult ForallOp::parse(OpAsmParser &parser, OperationState &
result) {
1152 auto indexType =
b.getIndexType();
1157 SmallVector<OpAsmParser::Argument, 4> ivs;
1162 SmallVector<OpAsmParser::UnresolvedOperand> dynamicLbs, dynamicUbs,
1172 unsigned numLoops = ivs.size();
1173 staticLbs =
b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 0));
1174 staticSteps =
b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 1));
1203 SmallVector<OpAsmParser::Argument, 4> regionOutArgs;
1204 SmallVector<OpAsmParser::UnresolvedOperand, 4> outOperands;
1207 if (outOperands.size() !=
result.types.size())
1209 "mismatch between out operands and types");
1218 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1219 std::unique_ptr<Region> region = std::make_unique<Region>();
1220 for (
auto &iv : ivs) {
1221 iv.type =
b.getIndexType();
1222 regionArgs.push_back(iv);
1224 for (
const auto &it : llvm::enumerate(regionOutArgs)) {
1225 auto &out = it.value();
1226 out.type =
result.types[it.index()];
1227 regionArgs.push_back(out);
1233 ForallOp::ensureTerminator(*region,
b,
result.location);
1234 result.addRegion(std::move(region));
1240 result.addAttribute(
"staticLowerBound", staticLbs);
1241 result.addAttribute(
"staticUpperBound", staticUbs);
1242 result.addAttribute(
"staticStep", staticSteps);
1243 result.addAttribute(
"operandSegmentSizes",
1245 {static_cast<int32_t>(dynamicLbs.size()),
1246 static_cast<int32_t>(dynamicUbs.size()),
1247 static_cast<int32_t>(dynamicSteps.size()),
1248 static_cast<int32_t>(outOperands.size())}));
1253void ForallOp::build(
1254 mlir::OpBuilder &
b, mlir::OperationState &
result,
1255 ArrayRef<OpFoldResult> lbs, ArrayRef<OpFoldResult> ubs,
1256 ArrayRef<OpFoldResult> steps,
ValueRange outputs,
1257 std::optional<ArrayAttr> mapping,
1259 SmallVector<int64_t> staticLbs, staticUbs, staticSteps;
1260 SmallVector<Value> dynamicLbs, dynamicUbs, dynamicSteps;
1265 result.addOperands(dynamicLbs);
1266 result.addOperands(dynamicUbs);
1267 result.addOperands(dynamicSteps);
1268 result.addOperands(outputs);
1271 result.addAttribute(getStaticLowerBoundAttrName(
result.name),
1272 b.getDenseI64ArrayAttr(staticLbs));
1273 result.addAttribute(getStaticUpperBoundAttrName(
result.name),
1274 b.getDenseI64ArrayAttr(staticUbs));
1275 result.addAttribute(getStaticStepAttrName(
result.name),
1276 b.getDenseI64ArrayAttr(staticSteps));
1278 "operandSegmentSizes",
1279 b.getDenseI32ArrayAttr({static_cast<int32_t>(dynamicLbs.size()),
1280 static_cast<int32_t>(dynamicUbs.size()),
1281 static_cast<int32_t>(dynamicSteps.size()),
1282 static_cast<int32_t>(outputs.size())}));
1283 if (mapping.has_value()) {
1284 result.addAttribute(ForallOp::getMappingAttrName(
result.name),
1288 Region *bodyRegion =
result.addRegion();
1289 OpBuilder::InsertionGuard g(
b);
1290 b.createBlock(bodyRegion);
1295 SmallVector<Type>(lbs.size(),
b.getIndexType()),
1296 SmallVector<Location>(staticLbs.size(),
result.location));
1299 SmallVector<Location>(outputs.size(),
result.location));
1301 b.setInsertionPointToStart(&bodyBlock);
1302 if (!bodyBuilderFn) {
1303 ForallOp::ensureTerminator(*bodyRegion,
b,
result.location);
1310void ForallOp::build(
1311 mlir::OpBuilder &
b, mlir::OperationState &
result,
1312 ArrayRef<OpFoldResult> ubs,
ValueRange outputs,
1313 std::optional<ArrayAttr> mapping,
1315 unsigned numLoops = ubs.size();
1316 SmallVector<OpFoldResult> lbs(numLoops,
b.getIndexAttr(0));
1317 SmallVector<OpFoldResult> steps(numLoops,
b.getIndexAttr(1));
1318 build(
b,
result, lbs, ubs, steps, outputs, mapping, bodyBuilderFn);
1322bool ForallOp::isNormalized() {
1323 auto allEqual = [](ArrayRef<OpFoldResult> results, int64_t val) {
1324 return llvm::all_of(results, [&](OpFoldResult ofr) {
1326 return intValue.has_value() && intValue == val;
1329 return allEqual(getMixedLowerBound(), 0) && allEqual(getMixedStep(), 1);
1332InParallelOp ForallOp::getTerminator() {
1333 return cast<InParallelOp>(getBody()->getTerminator());
1336SmallVector<Operation *> ForallOp::getCombiningOps(BlockArgument bbArg) {
1337 SmallVector<Operation *> storeOps;
1338 for (Operation *user : bbArg.
getUsers()) {
1339 if (
auto parallelOp = dyn_cast<ParallelCombiningOpInterface>(user)) {
1340 storeOps.push_back(parallelOp);
1346SmallVector<DeviceMappingAttrInterface> ForallOp::getDeviceMappingAttrs() {
1347 SmallVector<DeviceMappingAttrInterface> res;
1350 for (
auto attr : getMapping()->getValue()) {
1351 auto m = dyn_cast<DeviceMappingAttrInterface>(attr);
1358FailureOr<DeviceMaskingAttrInterface> ForallOp::getDeviceMaskingAttr() {
1359 DeviceMaskingAttrInterface res;
1362 for (
auto attr : getMapping()->getValue()) {
1363 auto m = dyn_cast<DeviceMaskingAttrInterface>(attr);
1372bool ForallOp::usesLinearMapping() {
1373 SmallVector<DeviceMappingAttrInterface> ifaces = getDeviceMappingAttrs();
1376 return ifaces.front().isLinearMapping();
1379std::optional<SmallVector<Value>> ForallOp::getLoopInductionVars() {
1380 return SmallVector<Value>{getBody()->getArguments().take_front(getRank())};
1384std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopLowerBounds() {
1386 return getMixedValues(getStaticLowerBound(), getDynamicLowerBound(),
b);
1390std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopUpperBounds() {
1392 return getMixedValues(getStaticUpperBound(), getDynamicUpperBound(),
b);
1396std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopSteps() {
1402 auto tidxArg = llvm::dyn_cast<BlockArgument>(val);
1405 assert(tidxArg.getOwner() &&
"unlinked block argument");
1406 auto *containingOp = tidxArg.getOwner()->getParentOp();
1407 return dyn_cast<ForallOp>(containingOp);
1415 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1417 auto forallOp = dimOp.getSource().getDefiningOp<ForallOp>();
1421 forallOp.getTiedOpOperand(llvm::cast<OpResult>(dimOp.getSource()))
1424 dimOp, [&]() { dimOp.getSourceMutable().assign(sharedOut); });
1429class ForallOpControlOperandsFolder :
public OpRewritePattern<ForallOp> {
1431 using OpRewritePattern<ForallOp>::OpRewritePattern;
1433 LogicalResult matchAndRewrite(ForallOp op,
1434 PatternRewriter &rewriter)
const override {
1435 SmallVector<OpFoldResult> mixedLowerBound(op.getMixedLowerBound());
1436 SmallVector<OpFoldResult> mixedUpperBound(op.getMixedUpperBound());
1437 SmallVector<OpFoldResult> mixedStep(op.getMixedStep());
1444 SmallVector<Value> dynamicLowerBound, dynamicUpperBound, dynamicStep;
1445 SmallVector<int64_t> staticLowerBound, staticUpperBound, staticStep;
1448 op.getDynamicLowerBoundMutable().assign(dynamicLowerBound);
1449 op.setStaticLowerBound(staticLowerBound);
1453 op.getDynamicUpperBoundMutable().assign(dynamicUpperBound);
1454 op.setStaticUpperBound(staticUpperBound);
1457 op.getDynamicStepMutable().assign(dynamicStep);
1458 op.setStaticStep(staticStep);
1460 op->setInherentAttr(
1461 rewriter.
getStringAttr(ForallOp::getOperandSegmentSizeAttr()),
1463 {static_cast<int32_t>(dynamicLowerBound.size()),
1464 static_cast<int32_t>(dynamicUpperBound.size()),
1465 static_cast<int32_t>(dynamicStep.size()),
1466 static_cast<int32_t>(op.getNumResults())}));
1545struct ForallOpIterArgsFolder :
public OpRewritePattern<ForallOp> {
1546 using OpRewritePattern<ForallOp>::OpRewritePattern;
1548 LogicalResult matchAndRewrite(ForallOp forallOp,
1549 PatternRewriter &rewriter)
const final {
1560 SmallVector<Value> resultsToDelete;
1561 SmallVector<Value> outsToDelete;
1562 SmallVector<BlockArgument> blockArgsToDelete;
1563 SmallVector<Value> newOuts;
1564 BitVector resultIndicesToDelete(forallOp.getNumResults(),
false);
1565 BitVector blockIndicesToDelete(forallOp.getBody()->getNumArguments(),
1567 for (OpResult
result : forallOp.getResults()) {
1568 OpOperand *opOperand = forallOp.getTiedOpOperand(
result);
1569 BlockArgument blockArg = forallOp.getTiedBlockArgument(opOperand);
1570 if (
result.use_empty() || forallOp.getCombiningOps(blockArg).empty()) {
1571 resultsToDelete.push_back(
result);
1572 outsToDelete.push_back(opOperand->
get());
1573 blockArgsToDelete.push_back(blockArg);
1574 resultIndicesToDelete[
result.getResultNumber()] =
true;
1577 newOuts.push_back(opOperand->
get());
1583 if (resultsToDelete.empty())
1588 for (
auto blockArg : blockArgsToDelete) {
1589 SmallVector<Operation *> combiningOps =
1590 forallOp.getCombiningOps(blockArg);
1591 for (Operation *combiningOp : combiningOps)
1592 rewriter.
eraseOp(combiningOp);
1594 for (
auto [blockArg,
result, out] :
1595 llvm::zip_equal(blockArgsToDelete, resultsToDelete, outsToDelete)) {
1601 forallOp.getBody()->eraseArguments(blockIndicesToDelete);
1606 auto newForallOp = cast<scf::ForallOp>(
1608 newForallOp.getOutputsMutable().assign(newOuts);
1614struct ForallOpSingleOrZeroIterationDimsFolder
1615 :
public OpRewritePattern<ForallOp> {
1616 using OpRewritePattern<ForallOp>::OpRewritePattern;
1618 LogicalResult matchAndRewrite(ForallOp op,
1619 PatternRewriter &rewriter)
const override {
1621 if (op.getMapping().has_value() && !op.getMapping()->empty())
1623 Location loc = op.getLoc();
1626 SmallVector<OpFoldResult> newMixedLowerBounds, newMixedUpperBounds,
1629 for (
auto [lb, ub, step, iv] :
1630 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1631 op.getMixedStep(), op.getInductionVars())) {
1632 auto numIterations =
1634 if (numIterations.has_value()) {
1636 if (*numIterations == 0) {
1637 rewriter.
replaceOp(op, op.getOutputs());
1642 if (*numIterations == 1) {
1647 newMixedLowerBounds.push_back(lb);
1648 newMixedUpperBounds.push_back(ub);
1649 newMixedSteps.push_back(step);
1653 if (newMixedLowerBounds.empty()) {
1659 if (newMixedLowerBounds.size() ==
static_cast<unsigned>(op.getRank())) {
1661 op,
"no dimensions have 0 or 1 iterations");
1666 newOp = ForallOp::create(rewriter, loc, newMixedLowerBounds,
1667 newMixedUpperBounds, newMixedSteps,
1668 op.getOutputs(), std::nullopt,
nullptr);
1669 newOp.getBodyRegion().getBlocks().clear();
1670 newOp.setMappingAttr(op.getMappingAttr());
1671 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
1673 newOp.getRegion().begin(), mapping);
1674 rewriter.
replaceOp(op, newOp.getResults());
1680struct ForallOpReplaceConstantInductionVar :
public OpRewritePattern<ForallOp> {
1681 using OpRewritePattern<ForallOp>::OpRewritePattern;
1683 LogicalResult matchAndRewrite(ForallOp op,
1684 PatternRewriter &rewriter)
const override {
1685 Location loc = op.getLoc();
1686 bool changed =
false;
1687 for (
auto [lb, ub, step, iv] :
1688 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1689 op.getMixedStep(), op.getInductionVars())) {
1692 auto numIterations =
1694 if (!numIterations.has_value() || numIterations.value() != 1) {
1705struct FoldTensorCastOfOutputIntoForallOp
1706 :
public OpRewritePattern<scf::ForallOp> {
1707 using OpRewritePattern<scf::ForallOp>::OpRewritePattern;
1714 LogicalResult matchAndRewrite(scf::ForallOp forallOp,
1715 PatternRewriter &rewriter)
const final {
1716 llvm::SmallMapVector<unsigned, TypeCast, 2> tensorCastProducers;
1717 llvm::SmallVector<Value> newOutputTensors = forallOp.getOutputs();
1718 for (
auto en : llvm::enumerate(newOutputTensors)) {
1719 auto castOp = en.value().getDefiningOp<tensor::CastOp>();
1726 castOp.getSource().getType())) {
1730 tensorCastProducers[en.index()] =
1731 TypeCast{castOp.getSource().getType(), castOp.getType()};
1732 newOutputTensors[en.index()] = castOp.getSource();
1735 if (tensorCastProducers.empty())
1739 Location loc = forallOp.getLoc();
1740 auto newForallOp = ForallOp::create(
1741 rewriter, loc, forallOp.getMixedLowerBound(),
1742 forallOp.getMixedUpperBound(), forallOp.getMixedStep(),
1743 newOutputTensors, forallOp.getMapping(),
1744 [&](OpBuilder nestedBuilder, Location nestedLoc,
ValueRange bbArgs) {
1745 auto castBlockArgs =
1746 llvm::to_vector(bbArgs.take_back(forallOp->getNumResults()));
1747 for (auto [index, cast] : tensorCastProducers) {
1748 Value &oldTypeBBArg = castBlockArgs[index];
1749 oldTypeBBArg = tensor::CastOp::create(nestedBuilder, nestedLoc,
1750 cast.dstType, oldTypeBBArg);
1754 SmallVector<Value> ivsBlockArgs =
1755 llvm::to_vector(bbArgs.take_front(forallOp.getRank()));
1756 ivsBlockArgs.append(castBlockArgs);
1758 bbArgs.front().getParentBlock(), ivsBlockArgs);
1769 llvm::SmallDenseSet<Value> newIterArgSet(
1770 newForallOp.getRegionIterArgs().begin(),
1771 newForallOp.getRegionIterArgs().end());
1772 auto terminator = newForallOp.getTerminator();
1773 for (
auto &yieldingOp : terminator.getYieldingOps()) {
1774 auto parallelCombiningOp =
1775 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1776 if (!parallelCombiningOp)
1778 for (OpOperand &dest : parallelCombiningOp.getUpdatedDestinations()) {
1779 auto castOp = dest.get().getDefiningOp<tensor::CastOp>();
1780 if (castOp && newIterArgSet.contains(castOp.getSource()))
1781 dest.set(castOp.getSource());
1787 SmallVector<Value> castResults = newForallOp.getResults();
1788 for (
auto &item : tensorCastProducers) {
1789 Value &oldTypeResult = castResults[item.first];
1790 oldTypeResult = tensor::CastOp::create(rewriter, loc, item.second.dstType,
1793 rewriter.
replaceOp(forallOp, castResults);
1800void ForallOp::getCanonicalizationPatterns(RewritePatternSet &results,
1801 MLIRContext *context) {
1802 results.
add<DimOfForallOp, FoldTensorCastOfOutputIntoForallOp,
1803 ForallOpControlOperandsFolder, ForallOpIterArgsFolder,
1804 ForallOpSingleOrZeroIterationDimsFolder,
1805 ForallOpReplaceConstantInductionVar>(context);
1808void ForallOp::getSuccessorRegions(RegionBranchPoint point,
1809 SmallVectorImpl<RegionSuccessor> ®ions) {
1816 regions.push_back(RegionSuccessor(&getRegion()));
1819 regions.push_back(RegionSuccessor(getOperation()));
1824 regions.push_back(RegionSuccessor(getOperation()));
1833void InParallelOp::build(OpBuilder &
b, OperationState &
result) {
1834 OpBuilder::InsertionGuard g(
b);
1835 Region *bodyRegion =
result.addRegion();
1836 b.createBlock(bodyRegion);
1839LogicalResult InParallelOp::verify() {
1840 scf::ForallOp forallOp =
1841 dyn_cast<scf::ForallOp>(getOperation()->getParentOp());
1843 return this->emitOpError(
"expected forall op parent");
1845 for (Operation &op : getRegion().front().getOperations()) {
1846 auto parallelCombiningOp = dyn_cast<ParallelCombiningOpInterface>(&op);
1847 if (!parallelCombiningOp) {
1848 return this->emitOpError(
"expected only ParallelCombiningOpInterface")
1853 MutableOperandRange dests = parallelCombiningOp.getUpdatedDestinations();
1854 ArrayRef<BlockArgument> regionOutArgs = forallOp.getRegionOutArgs();
1855 for (OpOperand &dest : dests) {
1856 if (!llvm::is_contained(regionOutArgs, dest.get()))
1857 return op.emitOpError(
"may only insert into an output block argument");
1864void InParallelOp::print(OpAsmPrinter &p) {
1870 getOperation()->getDiscardableAttrDictionary().getValue());
1873ParseResult InParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
1876 SmallVector<OpAsmParser::Argument, 8> regionOperands;
1877 std::unique_ptr<Region> region = std::make_unique<Region>();
1881 if (region->empty())
1882 OpBuilder(builder.
getContext()).createBlock(region.get());
1883 result.addRegion(std::move(region));
1891OpResult InParallelOp::getParentResult(int64_t idx) {
1892 return getOperation()->getParentOp()->getResult(idx);
1895SmallVector<BlockArgument> InParallelOp::getDests() {
1896 SmallVector<BlockArgument> updatedDests;
1897 for (Operation &yieldingOp : getYieldingOps()) {
1898 auto parallelCombiningOp =
1899 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1900 if (!parallelCombiningOp)
1902 for (OpOperand &updatedOperand :
1903 parallelCombiningOp.getUpdatedDestinations())
1904 updatedDests.push_back(cast<BlockArgument>(updatedOperand.get()));
1906 return updatedDests;
1909llvm::iterator_range<Block::iterator> InParallelOp::getYieldingOps() {
1910 return getRegion().front().getOperations();
1918 assert(a &&
"expected non-empty operation");
1919 assert(
b &&
"expected non-empty operation");
1924 if (ifOp->isProperAncestor(
b))
1927 return static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*a)) !=
1928 static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*
b));
1930 ifOp = ifOp->getParentOfType<IfOp>();
1938IfOp::inferReturnTypes(
MLIRContext *ctx, std::optional<Location> loc,
1939 IfOp::Adaptor adaptor,
1941 if (adaptor.getRegions().empty())
1943 Region *r = &adaptor.getThenRegion();
1949 auto yieldOp = llvm::dyn_cast<YieldOp>(
b.back());
1952 TypeRange types = yieldOp.getOperandTypes();
1953 llvm::append_range(inferredReturnTypes, types);
1959 return build(builder,
result, resultTypes, cond,
false,
1963void IfOp::build(OpBuilder &builder, OperationState &
result,
1964 TypeRange resultTypes, Value cond,
bool addThenBlock,
1965 bool addElseBlock) {
1966 assert((!addElseBlock || addThenBlock) &&
1967 "must not create else block w/o then block");
1968 result.addTypes(resultTypes);
1969 result.addOperands(cond);
1972 OpBuilder::InsertionGuard guard(builder);
1973 Region *thenRegion =
result.addRegion();
1976 Region *elseRegion =
result.addRegion();
1981void IfOp::build(OpBuilder &builder, OperationState &
result, Value cond,
1982 bool withElseRegion) {
1986void IfOp::build(OpBuilder &builder, OperationState &
result,
1987 TypeRange resultTypes, Value cond,
bool withElseRegion) {
1988 result.addTypes(resultTypes);
1989 result.addOperands(cond);
1992 OpBuilder::InsertionGuard guard(builder);
1993 Region *thenRegion =
result.addRegion();
1995 if (resultTypes.empty())
1996 IfOp::ensureTerminator(*thenRegion, builder,
result.location);
1999 Region *elseRegion =
result.addRegion();
2000 if (withElseRegion) {
2002 if (resultTypes.empty())
2003 IfOp::ensureTerminator(*elseRegion, builder,
result.location);
2007void IfOp::build(OpBuilder &builder, OperationState &
result, Value cond,
2009 function_ref<
void(OpBuilder &, Location)> elseBuilder) {
2010 assert(thenBuilder &&
"the builder callback for 'then' must be present");
2011 result.addOperands(cond);
2014 OpBuilder::InsertionGuard guard(builder);
2015 Region *thenRegion =
result.addRegion();
2017 thenBuilder(builder,
result.location);
2020 Region *elseRegion =
result.addRegion();
2023 elseBuilder(builder,
result.location);
2027 SmallVector<Type> inferredReturnTypes;
2029 auto attrDict = DictionaryAttr::get(ctx,
result.attributes);
2030 if (succeeded(inferReturnTypes(ctx, std::nullopt,
result.operands, attrDict,
2031 PropertyRef{},
result.regions,
2032 inferredReturnTypes))) {
2033 result.addTypes(inferredReturnTypes);
2037LogicalResult IfOp::verify() {
2038 if (getNumResults() != 0 && getElseRegion().empty())
2039 return emitOpError(
"must have an else block if defining values");
2043ParseResult IfOp::parse(OpAsmParser &parser, OperationState &
result) {
2045 result.regions.reserve(2);
2046 Region *thenRegion =
result.addRegion();
2047 Region *elseRegion =
result.addRegion();
2050 OpAsmParser::UnresolvedOperand cond;
2076void IfOp::print(OpAsmPrinter &p) {
2077 bool printBlockTerminators =
false;
2079 p <<
" " << getCondition();
2080 if (!getResults().empty()) {
2081 p <<
" -> (" << getResultTypes() <<
")";
2083 printBlockTerminators =
true;
2088 printBlockTerminators);
2091 auto &elseRegion = getElseRegion();
2092 if (!elseRegion.
empty()) {
2096 printBlockTerminators);
2102void IfOp::getSuccessorRegions(RegionBranchPoint point,
2103 SmallVectorImpl<RegionSuccessor> ®ions) {
2107 regions.push_back(RegionSuccessor(getOperation()));
2111 regions.push_back(RegionSuccessor(&getThenRegion()));
2114 Region *elseRegion = &this->getElseRegion();
2115 if (elseRegion->
empty())
2116 regions.push_back(RegionSuccessor(getOperation()));
2118 regions.push_back(RegionSuccessor(elseRegion));
2121ValueRange IfOp::getSuccessorInputs(RegionSuccessor successor) {
2126void IfOp::getEntrySuccessorRegions(ArrayRef<Attribute> operands,
2127 SmallVectorImpl<RegionSuccessor> ®ions) {
2128 FoldAdaptor adaptor(operands, *
this);
2129 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
2130 if (!boolAttr || boolAttr.getValue())
2131 regions.emplace_back(&getThenRegion());
2134 if (!boolAttr || !boolAttr.getValue()) {
2135 if (!getElseRegion().empty())
2136 regions.emplace_back(&getElseRegion());
2138 regions.emplace_back(RegionSuccessor(getOperation()));
2142LogicalResult IfOp::fold(FoldAdaptor adaptor,
2143 SmallVectorImpl<OpFoldResult> &results) {
2145 if (getElseRegion().empty())
2148 arith::XOrIOp xorStmt = getCondition().getDefiningOp<arith::XOrIOp>();
2155 getConditionMutable().assign(xorStmt.getLhs());
2156 Block *thenBlock = &getThenRegion().front();
2159 getThenRegion().getBlocks().splice(getThenRegion().getBlocks().begin(),
2160 getElseRegion().getBlocks());
2161 getElseRegion().getBlocks().splice(getElseRegion().getBlocks().begin(),
2162 getThenRegion().getBlocks(), thenBlock);
2166void IfOp::getRegionInvocationBounds(
2167 ArrayRef<Attribute> operands,
2168 SmallVectorImpl<InvocationBounds> &invocationBounds) {
2169 if (
auto cond = llvm::dyn_cast_or_null<BoolAttr>(operands[0])) {
2172 invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
2173 invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
2176 invocationBounds.assign(2, {0, 1});
2183struct ConvertTrivialIfToSelect :
public OpRewritePattern<IfOp> {
2184 using OpRewritePattern<IfOp>::OpRewritePattern;
2186 LogicalResult matchAndRewrite(IfOp op,
2187 PatternRewriter &rewriter)
const override {
2188 if (op->getNumResults() == 0)
2191 auto cond = op.getCondition();
2192 auto thenYieldArgs = op.thenYield().getOperands();
2193 auto elseYieldArgs = op.elseYield().getOperands();
2195 SmallVector<Type> nonHoistable;
2196 for (
auto [trueVal, falseVal] : llvm::zip(thenYieldArgs, elseYieldArgs)) {
2197 if (&op.getThenRegion() == trueVal.getParentRegion() ||
2198 &op.getElseRegion() == falseVal.getParentRegion())
2199 nonHoistable.push_back(trueVal.getType());
2203 if (nonHoistable.size() == op->getNumResults())
2206 IfOp
replacement = IfOp::create(rewriter, op.getLoc(), nonHoistable, cond,
2210 replacement.getThenRegion().takeBody(op.getThenRegion());
2211 replacement.getElseRegion().takeBody(op.getElseRegion());
2213 SmallVector<Value> results(op->getNumResults());
2214 assert(thenYieldArgs.size() == results.size());
2215 assert(elseYieldArgs.size() == results.size());
2217 SmallVector<Value> trueYields;
2218 SmallVector<Value> falseYields;
2220 for (
const auto &it :
2221 llvm::enumerate(llvm::zip(thenYieldArgs, elseYieldArgs))) {
2222 Value trueVal = std::get<0>(it.value());
2223 Value falseVal = std::get<1>(it.value());
2226 results[it.index()] =
replacement.getResult(trueYields.size());
2227 trueYields.push_back(trueVal);
2228 falseYields.push_back(falseVal);
2229 }
else if (trueVal == falseVal)
2230 results[it.index()] = trueVal;
2232 results[it.index()] = arith::SelectOp::create(rewriter, op.getLoc(),
2233 cond, trueVal, falseVal);
2260struct ConditionPropagation :
public OpRewritePattern<IfOp> {
2261 using OpRewritePattern<IfOp>::OpRewritePattern;
2264 enum class Parent { Then, Else,
None };
2269 static Parent getParentType(Region *toCheck, IfOp op,
2271 Region *endRegion) {
2272 SmallVector<Region *> seen;
2273 while (toCheck != endRegion) {
2274 auto found = cache.find(toCheck);
2275 if (found != cache.end())
2276 return found->second;
2277 seen.push_back(toCheck);
2278 if (&op.getThenRegion() == toCheck) {
2279 for (Region *region : seen)
2280 cache[region] = Parent::Then;
2281 return Parent::Then;
2283 if (&op.getElseRegion() == toCheck) {
2284 for (Region *region : seen)
2285 cache[region] = Parent::Else;
2286 return Parent::Else;
2291 for (Region *region : seen)
2292 cache[region] = Parent::None;
2293 return Parent::None;
2296 LogicalResult matchAndRewrite(IfOp op,
2297 PatternRewriter &rewriter)
const override {
2303 bool changed =
false;
2308 Value constantTrue =
nullptr;
2309 Value constantFalse =
nullptr;
2312 for (OpOperand &use :
2313 llvm::make_early_inc_range(op.getCondition().getUses())) {
2316 case Parent::Then: {
2320 constantTrue = arith::ConstantOp::create(
2324 [&]() { use.set(constantTrue); });
2327 case Parent::Else: {
2331 constantFalse = arith::ConstantOp::create(
2335 [&]() { use.set(constantFalse); });
2383struct ReplaceIfYieldWithConditionOrValue :
public OpRewritePattern<IfOp> {
2384 using OpRewritePattern<IfOp>::OpRewritePattern;
2386 LogicalResult matchAndRewrite(IfOp op,
2387 PatternRewriter &rewriter)
const override {
2389 if (op.getNumResults() == 0)
2393 cast<scf::YieldOp>(op.getThenRegion().back().getTerminator());
2395 cast<scf::YieldOp>(op.getElseRegion().back().getTerminator());
2398 op.getOperation()->getIterator());
2399 bool changed =
false;
2401 for (
auto [trueResult, falseResult, opResult] :
2402 llvm::zip(trueYield.getResults(), falseYield.getResults(),
2404 if (trueResult == falseResult) {
2405 if (!opResult.use_empty()) {
2406 opResult.replaceAllUsesWith(trueResult);
2412 BoolAttr trueYield, falseYield;
2417 bool trueVal = trueYield.
getValue();
2418 bool falseVal = falseYield.
getValue();
2419 if (!trueVal && falseVal) {
2420 if (!opResult.use_empty()) {
2421 Dialect *constDialect = trueResult.getDefiningOp()->getDialect();
2422 Value notCond = arith::XOrIOp::create(
2423 rewriter, op.getLoc(), op.getCondition(),
2429 opResult.replaceAllUsesWith(notCond);
2433 if (trueVal && !falseVal) {
2434 if (!opResult.use_empty()) {
2435 opResult.replaceAllUsesWith(op.getCondition());
2465struct CombineIfs :
public OpRewritePattern<IfOp> {
2466 using OpRewritePattern<IfOp>::OpRewritePattern;
2468 LogicalResult matchAndRewrite(IfOp nextIf,
2469 PatternRewriter &rewriter)
const override {
2470 Block *parent = nextIf->getBlock();
2471 if (nextIf == &parent->
front())
2474 auto prevIf = dyn_cast<IfOp>(nextIf->getPrevNode());
2482 Block *nextThen =
nullptr;
2483 Block *nextElse =
nullptr;
2484 if (nextIf.getCondition() == prevIf.getCondition()) {
2485 nextThen = nextIf.thenBlock();
2486 if (!nextIf.getElseRegion().empty())
2487 nextElse = nextIf.elseBlock();
2489 if (arith::XOrIOp notv =
2490 nextIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2491 if (notv.getLhs() == prevIf.getCondition() &&
2493 nextElse = nextIf.thenBlock();
2494 if (!nextIf.getElseRegion().empty())
2495 nextThen = nextIf.elseBlock();
2498 if (arith::XOrIOp notv =
2499 prevIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2500 if (notv.getLhs() == nextIf.getCondition() &&
2502 nextElse = nextIf.thenBlock();
2503 if (!nextIf.getElseRegion().empty())
2504 nextThen = nextIf.elseBlock();
2508 if (!nextThen && !nextElse)
2511 SmallVector<Value> prevElseYielded;
2512 if (!prevIf.getElseRegion().empty())
2513 prevElseYielded = prevIf.elseYield().getOperands();
2516 for (
auto it : llvm::zip(prevIf.getResults(),
2517 prevIf.thenYield().getOperands(), prevElseYielded))
2518 for (OpOperand &use :
2519 llvm::make_early_inc_range(std::get<0>(it).getUses())) {
2523 use.
set(std::get<1>(it));
2528 use.
set(std::get<2>(it));
2533 SmallVector<Type> mergedTypes(prevIf.getResultTypes());
2534 llvm::append_range(mergedTypes, nextIf.getResultTypes());
2536 IfOp combinedIf = IfOp::create(rewriter, nextIf.getLoc(), mergedTypes,
2537 prevIf.getCondition(),
false);
2538 rewriter.
eraseBlock(&combinedIf.getThenRegion().back());
2541 combinedIf.getThenRegion(),
2542 combinedIf.getThenRegion().begin());
2545 YieldOp thenYield = combinedIf.thenYield();
2546 YieldOp thenYield2 = cast<YieldOp>(nextThen->
getTerminator());
2547 rewriter.
mergeBlocks(nextThen, combinedIf.thenBlock());
2550 SmallVector<Value> mergedYields(thenYield.getOperands());
2551 llvm::append_range(mergedYields, thenYield2.getOperands());
2552 YieldOp::create(rewriter, thenYield2.getLoc(), mergedYields);
2558 combinedIf.getElseRegion(),
2559 combinedIf.getElseRegion().begin());
2562 if (combinedIf.getElseRegion().empty()) {
2564 combinedIf.getElseRegion(),
2565 combinedIf.getElseRegion().
begin());
2567 YieldOp elseYield = combinedIf.elseYield();
2568 YieldOp elseYield2 = cast<YieldOp>(nextElse->
getTerminator());
2569 rewriter.
mergeBlocks(nextElse, combinedIf.elseBlock());
2573 SmallVector<Value> mergedElseYields(elseYield.getOperands());
2574 llvm::append_range(mergedElseYields, elseYield2.getOperands());
2576 YieldOp::create(rewriter, elseYield2.getLoc(), mergedElseYields);
2582 SmallVector<Value> prevValues;
2583 SmallVector<Value> nextValues;
2584 for (
const auto &pair : llvm::enumerate(combinedIf.getResults())) {
2585 if (pair.index() < prevIf.getNumResults())
2586 prevValues.push_back(pair.value());
2588 nextValues.push_back(pair.value());
2597struct RemoveEmptyElseBranch :
public OpRewritePattern<IfOp> {
2598 using OpRewritePattern<IfOp>::OpRewritePattern;
2600 LogicalResult matchAndRewrite(IfOp ifOp,
2601 PatternRewriter &rewriter)
const override {
2603 if (ifOp.getNumResults())
2605 Block *elseBlock = ifOp.elseBlock();
2606 if (!elseBlock || !llvm::hasSingleElement(*elseBlock))
2610 newIfOp.getThenRegion().begin());
2632struct CombineNestedIfs :
public OpRewritePattern<IfOp> {
2633 using OpRewritePattern<IfOp>::OpRewritePattern;
2635 LogicalResult matchAndRewrite(IfOp op,
2636 PatternRewriter &rewriter)
const override {
2637 auto nestedOps = op.thenBlock()->without_terminator();
2639 if (!llvm::hasSingleElement(nestedOps))
2643 if (op.elseBlock() && !llvm::hasSingleElement(*op.elseBlock()))
2646 auto nestedIf = dyn_cast<IfOp>(*nestedOps.begin());
2650 if (nestedIf.elseBlock() && !llvm::hasSingleElement(*nestedIf.elseBlock()))
2653 SmallVector<Value> thenYield(op.thenYield().getOperands());
2654 SmallVector<Value> elseYield;
2656 llvm::append_range(elseYield, op.elseYield().getOperands());
2660 SmallVector<unsigned> elseYieldsToUpgradeToSelect;
2669 for (
const auto &tup : llvm::enumerate(thenYield)) {
2670 if (tup.value().getDefiningOp() == nestedIf) {
2671 auto nestedIdx = llvm::cast<OpResult>(tup.value()).getResultNumber();
2672 if (nestedIf.elseYield().getOperand(nestedIdx) !=
2673 elseYield[tup.index()]) {
2678 thenYield[tup.index()] = nestedIf.thenYield().getOperand(nestedIdx);
2691 if (tup.value().getParentRegion() == &op.getThenRegion()) {
2694 elseYieldsToUpgradeToSelect.push_back(tup.index());
2697 Location loc = op.getLoc();
2698 Value newCondition = arith::AndIOp::create(rewriter, loc, op.getCondition(),
2699 nestedIf.getCondition());
2700 auto newIf = IfOp::create(rewriter, loc, op.getResultTypes(), newCondition);
2703 SmallVector<Value> results;
2704 llvm::append_range(results, newIf.getResults());
2707 for (
auto idx : elseYieldsToUpgradeToSelect)
2709 arith::SelectOp::create(rewriter, op.getLoc(), op.getCondition(),
2710 thenYield[idx], elseYield[idx]);
2712 rewriter.
mergeBlocks(nestedIf.thenBlock(), newIfBlock);
2715 if (!elseYield.empty()) {
2718 YieldOp::create(rewriter, loc, elseYield);
2727void IfOp::getCanonicalizationPatterns(RewritePatternSet &results,
2728 MLIRContext *context) {
2729 results.
add<CombineIfs, CombineNestedIfs, ConditionPropagation,
2730 ConvertTrivialIfToSelect, RemoveEmptyElseBranch,
2731 ReplaceIfYieldWithConditionOrValue>(context);
2733 results, IfOp::getOperationName());
2735 IfOp::getOperationName());
2738Block *IfOp::thenBlock() {
return &getThenRegion().back(); }
2739YieldOp IfOp::thenYield() {
return cast<YieldOp>(&thenBlock()->back()); }
2740Block *IfOp::elseBlock() {
2741 Region &r = getElseRegion();
2746YieldOp IfOp::elseYield() {
return cast<YieldOp>(&elseBlock()->back()); }
2752void ParallelOp::build(
2757 result.addOperands(lowerBounds);
2758 result.addOperands(upperBounds);
2759 result.addOperands(steps);
2760 result.addOperands(initVals);
2762 ParallelOp::getOperandSegmentSizeAttr(),
2764 static_cast<int32_t>(upperBounds.size()),
2765 static_cast<int32_t>(steps.size()),
2766 static_cast<int32_t>(initVals.size())}));
2769 OpBuilder::InsertionGuard guard(builder);
2770 unsigned numIVs = steps.size();
2771 SmallVector<Type, 8> argTypes(numIVs, builder.
getIndexType());
2772 SmallVector<Location, 8> argLocs(numIVs,
result.location);
2773 Region *bodyRegion =
result.addRegion();
2776 if (bodyBuilderFn) {
2778 bodyBuilderFn(builder,
result.location,
2783 if (initVals.empty())
2784 ParallelOp::ensureTerminator(*bodyRegion, builder,
result.location);
2787void ParallelOp::build(
2794 auto wrappedBuilderFn = [&bodyBuilderFn](OpBuilder &nestedBuilder,
2797 bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
2801 wrapper = wrappedBuilderFn;
2807LogicalResult ParallelOp::verify() {
2812 if (stepValues.empty())
2814 "needs at least one tuple element for lowerBound, upperBound and step");
2817 for (Value stepValue : stepValues)
2820 return emitOpError(
"constant step operand must be positive");
2824 Block *body = getBody();
2826 return emitOpError() <<
"expects the same number of induction variables: "
2828 <<
" as bound and step values: " << stepValues.size();
2830 if (!arg.getType().isIndex())
2832 "expects arguments for the induction variable to be of index type");
2836 *
this, getRegion(),
"expects body to terminate with 'scf.reduce'");
2841 auto resultsSize = getResults().size();
2842 auto reductionsSize = reduceOp.getReductions().size();
2843 auto initValsSize = getInitVals().size();
2844 if (resultsSize != reductionsSize)
2845 return emitOpError() <<
"expects number of results: " << resultsSize
2846 <<
" to be the same as number of reductions: "
2848 if (resultsSize != initValsSize)
2849 return emitOpError() <<
"expects number of results: " << resultsSize
2850 <<
" to be the same as number of initial values: "
2852 if (reduceOp.getNumOperands() != initValsSize)
2857 for (int64_t i = 0; i < static_cast<int64_t>(reductionsSize); ++i) {
2858 auto resultType = getOperation()->getResult(i).getType();
2859 auto reductionOperandType = reduceOp.getOperands()[i].getType();
2860 if (resultType != reductionOperandType)
2861 return reduceOp.emitOpError()
2862 <<
"expects type of " << i
2863 <<
"-th reduction operand: " << reductionOperandType
2864 <<
" to be the same as the " << i
2865 <<
"-th result type: " << resultType;
2870ParseResult ParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
2873 SmallVector<OpAsmParser::Argument, 4> ivs;
2878 SmallVector<OpAsmParser::UnresolvedOperand, 4> lower;
2885 SmallVector<OpAsmParser::UnresolvedOperand, 4> upper;
2893 SmallVector<OpAsmParser::UnresolvedOperand, 4> steps;
2901 SmallVector<OpAsmParser::UnresolvedOperand, 4> initVals;
2912 Region *body =
result.addRegion();
2913 for (
auto &iv : ivs)
2920 ParallelOp::getOperandSegmentSizeAttr(),
2922 static_cast<int32_t>(upper.size()),
2923 static_cast<int32_t>(steps.size()),
2924 static_cast<int32_t>(initVals.size())}));
2933 ParallelOp::ensureTerminator(*body, builder,
result.location);
2937void ParallelOp::print(OpAsmPrinter &p) {
2938 p <<
" (" << getBody()->getArguments() <<
") = (" <<
getLowerBound()
2939 <<
") to (" <<
getUpperBound() <<
") step (" << getStep() <<
")";
2940 if (!getInitVals().empty())
2941 p <<
" init (" << getInitVals() <<
")";
2948SmallVector<Region *> ParallelOp::getLoopRegions() {
return {&getRegion()}; }
2950std::optional<SmallVector<Value>> ParallelOp::getLoopInductionVars() {
2951 return SmallVector<Value>{getBody()->getArguments()};
2954std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopLowerBounds() {
2958std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopUpperBounds() {
2962std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopSteps() {
2967 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
2969 return ParallelOp();
2970 assert(ivArg.getOwner() &&
"unlinked block argument");
2971 auto *containingOp = ivArg.getOwner()->getParentOp();
2972 return dyn_cast<ParallelOp>(containingOp);
2977struct ParallelOpSingleOrZeroIterationDimsFolder
2981 LogicalResult matchAndRewrite(ParallelOp op,
2988 for (
auto [lb,
ub, step, iv] :
2989 llvm::zip(op.getLowerBound(), op.getUpperBound(), op.getStep(),
2990 op.getInductionVars())) {
2991 auto numIterations =
2993 if (numIterations.has_value()) {
2995 if (*numIterations == 0) {
2996 rewriter.
replaceOp(op, op.getInitVals());
3001 if (*numIterations == 1) {
3006 newLowerBounds.push_back(lb);
3007 newUpperBounds.push_back(ub);
3008 newSteps.push_back(step);
3011 if (newLowerBounds.size() == op.getLowerBound().size())
3014 if (newLowerBounds.empty()) {
3017 SmallVector<Value> results;
3018 results.reserve(op.getInitVals().size());
3019 for (
auto &bodyOp : op.getBody()->without_terminator())
3020 rewriter.
clone(bodyOp, mapping);
3021 auto reduceOp = cast<ReduceOp>(op.getBody()->getTerminator());
3022 for (int64_t i = 0, e = reduceOp.getReductions().size(); i < e; ++i) {
3023 Block &reduceBlock = reduceOp.getReductions()[i].front();
3024 auto initValIndex = results.size();
3025 mapping.
map(reduceBlock.
getArgument(0), op.getInitVals()[initValIndex]);
3029 rewriter.
clone(reduceBodyOp, mapping);
3032 cast<ReduceReturnOp>(reduceBlock.
getTerminator()).getResult());
3033 results.push_back(
result);
3041 ParallelOp::create(rewriter, op.getLoc(), newLowerBounds,
3042 newUpperBounds, newSteps, op.getInitVals(),
nullptr);
3048 newOp.getRegion().begin(), mapping);
3049 rewriter.
replaceOp(op, newOp.getResults());
3054struct MergeNestedParallelLoops :
public OpRewritePattern<ParallelOp> {
3055 using OpRewritePattern<ParallelOp>::OpRewritePattern;
3057 LogicalResult matchAndRewrite(ParallelOp op,
3058 PatternRewriter &rewriter)
const override {
3059 Block &outerBody = *op.getBody();
3063 auto innerOp = dyn_cast<ParallelOp>(outerBody.
front());
3068 if (llvm::is_contained(innerOp.getLowerBound(), val) ||
3069 llvm::is_contained(innerOp.getUpperBound(), val) ||
3070 llvm::is_contained(innerOp.getStep(), val))
3074 if (!op.getInitVals().empty() || !innerOp.getInitVals().empty())
3077 auto bodyBuilder = [&](OpBuilder &builder, Location ,
3079 Block &innerBody = *innerOp.getBody();
3080 assert(iterVals.size() ==
3088 builder.
clone(op, mapping);
3091 auto concatValues = [](
const auto &first,
const auto &second) {
3092 SmallVector<Value> ret;
3093 ret.reserve(first.size() + second.size());
3094 ret.assign(first.begin(), first.end());
3095 ret.append(second.begin(), second.end());
3099 auto newLowerBounds =
3100 concatValues(op.getLowerBound(), innerOp.getLowerBound());
3101 auto newUpperBounds =
3102 concatValues(op.getUpperBound(), innerOp.getUpperBound());
3103 auto newSteps = concatValues(op.getStep(), innerOp.getStep());
3114void ParallelOp::getCanonicalizationPatterns(RewritePatternSet &results,
3115 MLIRContext *context) {
3117 .
add<ParallelOpSingleOrZeroIterationDimsFolder, MergeNestedParallelLoops>(
3126void ParallelOp::getSuccessorRegions(
3127 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
3131 regions.push_back(RegionSuccessor(&getRegion()));
3132 regions.push_back(RegionSuccessor(getOperation()));
3139void ReduceOp::build(OpBuilder &builder, OperationState &
result) {}
3141void ReduceOp::build(OpBuilder &builder, OperationState &
result,
3143 result.addOperands(operands);
3144 for (Value v : operands) {
3145 OpBuilder::InsertionGuard guard(builder);
3146 Region *bodyRegion =
result.addRegion();
3148 ArrayRef<Type>{v.getType(), v.getType()},
3153LogicalResult ReduceOp::verifyRegions() {
3154 if (getReductions().size() != getOperands().size())
3155 return emitOpError() <<
"expects number of reduction regions: "
3156 << getReductions().size()
3157 <<
" to be the same as number of reduction operands: "
3158 << getOperands().size();
3161 for (int64_t i = 0, e = getReductions().size(); i < e; ++i) {
3162 auto type = getOperands()[i].getType();
3163 Block &block = getReductions()[i].front();
3165 return emitOpError() << i <<
"-th reduction has an empty body";
3167 llvm::any_of(block.
getArguments(), [&](
const BlockArgument &arg) {
3168 return arg.getType() != type;
3170 return emitOpError() <<
"expected two block arguments with type " << type
3171 <<
" in the " << i <<
"-th reduction region";
3175 return emitOpError(
"reduction bodies must be terminated with an "
3176 "'scf.reduce.return' op");
3183ReduceOp::getMutableSuccessorOperands(RegionSuccessor point) {
3185 return MutableOperandRange(getOperation(), 0, 0);
3192LogicalResult ReduceReturnOp::verify() {
3195 Block *reductionBody = getOperation()->getBlock();
3197 assert(isa<ReduceOp>(reductionBody->
getParentOp()) &&
"expected scf.reduce");
3199 if (expectedResultType != getResult().
getType())
3200 return emitOpError() <<
"must have type " << expectedResultType
3201 <<
" (the type of the reduction inputs)";
3209void WhileOp::build(::mlir::OpBuilder &odsBuilder,
3210 ::mlir::OperationState &odsState,
TypeRange resultTypes,
3211 ValueRange inits, BodyBuilderFn beforeBuilder,
3212 BodyBuilderFn afterBuilder) {
3216 OpBuilder::InsertionGuard guard(odsBuilder);
3219 SmallVector<Location, 4> beforeArgLocs;
3220 beforeArgLocs.reserve(inits.size());
3221 for (Value operand : inits) {
3222 beforeArgLocs.push_back(operand.getLoc());
3225 Region *beforeRegion = odsState.
addRegion();
3227 inits.getTypes(), beforeArgLocs);
3232 SmallVector<Location, 4> afterArgLocs(resultTypes.size(), odsState.
location);
3234 Region *afterRegion = odsState.
addRegion();
3236 resultTypes, afterArgLocs);
3242ConditionOp WhileOp::getConditionOp() {
3243 return cast<ConditionOp>(getBeforeBody()->getTerminator());
3246YieldOp WhileOp::getYieldOp() {
3247 return cast<YieldOp>(getAfterBody()->getTerminator());
3250std::optional<MutableArrayRef<OpOperand>> WhileOp::getYieldedValuesMutable() {
3251 return getYieldOp().getResultsMutable();
3255 return getBeforeBody()->getArguments();
3259 return getAfterBody()->getArguments();
3263 return getBeforeArguments();
3266OperandRange WhileOp::getEntrySuccessorOperands(RegionSuccessor successor) {
3268 "WhileOp is expected to branch only to the first region");
3272void WhileOp::getSuccessorRegions(RegionBranchPoint point,
3273 SmallVectorImpl<RegionSuccessor> ®ions) {
3276 regions.emplace_back(&getBefore());
3280 assert(llvm::is_contained(
3281 {&getAfter(), &getBefore()},
3283 "there are only two regions in a WhileOp");
3287 regions.emplace_back(&getBefore());
3291 regions.push_back(RegionSuccessor(getOperation()));
3292 regions.emplace_back(&getAfter());
3295ValueRange WhileOp::getSuccessorInputs(RegionSuccessor successor) {
3297 return getOperation()->getResults();
3298 if (successor == &getBefore())
3299 return getBefore().getArguments();
3300 if (successor == &getAfter())
3301 return getAfter().getArguments();
3302 llvm_unreachable(
"invalid region successor");
3305SmallVector<Region *> WhileOp::getLoopRegions() {
3306 return {&getBefore(), &getAfter()};
3316ParseResult scf::WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
3317 SmallVector<OpAsmParser::Argument, 4> regionArgs;
3318 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
3319 Region *before =
result.addRegion();
3320 Region *after =
result.addRegion();
3322 OptionalParseResult listResult =
3327 FunctionType functionType;
3332 result.addTypes(functionType.getResults());
3334 if (functionType.getNumInputs() != operands.size()) {
3336 <<
"expected as many input types as operands " <<
"(expected "
3337 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
3347 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
3348 regionArgs[i].type = functionType.getInput(i);
3350 return failure(parser.
parseRegion(*before, regionArgs) ||
3356void scf::WhileOp::print(OpAsmPrinter &p) {
3365 (*this)->getDiscardableAttrDictionary().getValue());
3368LogicalResult scf::WhileOp::verify() {
3371 "expects the 'before' region to terminate with 'scf.condition'");
3372 if (!beforeTerminator)
3377 "expects the 'after' region to terminate with 'scf.yield'");
3378 return success(afterTerminator !=
nullptr);
3415struct WhileMoveIfDown :
public OpRewritePattern<scf::WhileOp> {
3416 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3418 LogicalResult matchAndRewrite(scf::WhileOp op,
3419 PatternRewriter &rewriter)
const override {
3420 auto conditionOp = op.getConditionOp();
3428 auto ifOp = dyn_cast_or_null<scf::IfOp>(conditionOp->getPrevNode());
3434 if (!ifOp || ifOp.getCondition() != conditionOp.getCondition() ||
3435 (ifOp.elseBlock() && !ifOp.elseBlock()->without_terminator().empty()))
3438 assert((ifOp->use_empty() || (llvm::all_equal(ifOp->getUsers()) &&
3439 *ifOp->user_begin() == conditionOp)) &&
3440 "ifOp has unexpected uses");
3442 Location loc = op.getLoc();
3452 for (
auto [idx, arg] : llvm::enumerate(conditionOp.getArgs())) {
3453 auto it = llvm::find(ifOp->getResults(), arg);
3454 if (it == ifOp->getResults().end())
3456 size_t ifOpIdx = it.getIndex();
3458 ifOp.thenYield()->getOperand(ifOpIdx));
3459 unsigned argIdx = idx;
3460 Value elseValue = ifOp.elseYield()->getOperand(ifOpIdx);
3462 conditionOp.getArgsMutable()[argIdx].assign(elseValue);
3469 if (&op.getBefore() == operand->get().getParentRegion())
3470 additionalUsedValuesSet.insert(operand->get());
3474 auto additionalUsedValues = additionalUsedValuesSet.getArrayRef();
3475 auto additionalValueTypes = llvm::map_to_vector(
3476 additionalUsedValues, [](Value val) {
return val.
getType(); });
3477 size_t additionalValueSize = additionalUsedValues.size();
3478 SmallVector<Type> newResultTypes(op.getResultTypes());
3479 newResultTypes.append(additionalValueTypes);
3482 scf::WhileOp::create(rewriter, loc, newResultTypes, op.getInits());
3485 newWhileOp.getBefore().takeBody(op.getBefore());
3486 newWhileOp.getAfter().takeBody(op.getAfter());
3487 newWhileOp.getAfter().addArguments(
3488 additionalValueTypes,
3489 SmallVector<Location>(additionalValueSize, loc));
3493 conditionOp.getArgsMutable().append(additionalUsedValues);
3499 additionalUsedValues,
3500 newWhileOp.getAfterArguments().take_back(additionalValueSize),
3501 [&](OpOperand &use) {
3502 return ifOp.getThenRegion().isAncestor(
3503 use.getOwner()->getParentRegion());
3507 rewriter.
eraseOp(ifOp.thenYield());
3509 newWhileOp.getAfterBody()->begin());
3512 newWhileOp->getResults().drop_back(additionalValueSize));
3536struct WhileConditionTruth :
public OpRewritePattern<WhileOp> {
3537 using OpRewritePattern<WhileOp>::OpRewritePattern;
3539 LogicalResult matchAndRewrite(WhileOp op,
3540 PatternRewriter &rewriter)
const override {
3541 auto term = op.getConditionOp();
3545 Value constantTrue =
nullptr;
3547 bool replaced =
false;
3548 for (
auto yieldedAndBlockArgs :
3549 llvm::zip(term.getArgs(), op.getAfterArguments())) {
3550 if (std::get<0>(yieldedAndBlockArgs) == term.getCondition()) {
3551 if (!std::get<1>(yieldedAndBlockArgs).use_empty()) {
3553 constantTrue = arith::ConstantOp::create(
3554 rewriter, op.getLoc(), term.getCondition().getType(),
3589struct WhileCmpCond :
public OpRewritePattern<scf::WhileOp> {
3590 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3592 LogicalResult matchAndRewrite(scf::WhileOp op,
3593 PatternRewriter &rewriter)
const override {
3594 using namespace scf;
3595 auto cond = op.getConditionOp();
3596 auto cmp = cond.getCondition().getDefiningOp<arith::CmpIOp>();
3599 bool changed =
false;
3600 for (
auto tup : llvm::zip(cond.getArgs(), op.getAfterArguments())) {
3601 for (
size_t opIdx = 0; opIdx < 2; opIdx++) {
3602 if (std::get<0>(tup) != cmp.getOperand(opIdx))
3605 llvm::make_early_inc_range(std::get<1>(tup).getUses())) {
3606 auto cmp2 = dyn_cast<arith::CmpIOp>(u.getOwner());
3610 if (cmp2.getOperand(1 - opIdx) != cmp.getOperand(1 - opIdx))
3613 if (cmp2.getPredicate() == cmp.getPredicate())
3614 samePredicate =
true;
3615 else if (cmp2.getPredicate() ==
3616 arith::invertPredicate(cmp.getPredicate()))
3617 samePredicate =
false;
3633static std::optional<SmallVector<unsigned>> getArgsMapping(
ValueRange args1,
3635 if (args1.size() != args2.size())
3636 return std::nullopt;
3638 SmallVector<unsigned> ret(args1.size());
3639 for (
auto &&[i, arg1] : llvm::enumerate(args1)) {
3640 auto it = llvm::find(args2, arg1);
3641 if (it == args2.end())
3642 return std::nullopt;
3644 ret[std::distance(args2.begin(), it)] =
static_cast<unsigned>(i);
3651 llvm::SmallDenseSet<Value> set;
3652 for (Value arg : args) {
3653 if (!set.insert(arg).second)
3663struct WhileOpAlignBeforeArgs :
public OpRewritePattern<WhileOp> {
3666 LogicalResult matchAndRewrite(WhileOp loop,
3667 PatternRewriter &rewriter)
const override {
3668 auto *oldBefore = loop.getBeforeBody();
3669 ConditionOp oldTerm = loop.getConditionOp();
3670 ValueRange beforeArgs = oldBefore->getArguments();
3672 if (beforeArgs == termArgs)
3675 if (hasDuplicates(termArgs))
3678 auto mapping = getArgsMapping(beforeArgs, termArgs);
3683 OpBuilder::InsertionGuard g(rewriter);
3689 auto *oldAfter = loop.getAfterBody();
3691 SmallVector<Type> newResultTypes(beforeArgs.size());
3692 for (
auto &&[i, j] : llvm::enumerate(*mapping))
3693 newResultTypes[j] = loop.getResult(i).getType();
3695 auto newLoop = WhileOp::create(
3696 rewriter, loop.getLoc(), newResultTypes, loop.getInits(),
3698 auto *newBefore = newLoop.getBeforeBody();
3699 auto *newAfter = newLoop.getAfterBody();
3701 SmallVector<Value> newResults(beforeArgs.size());
3702 SmallVector<Value> newAfterArgs(beforeArgs.size());
3703 for (
auto &&[i, j] : llvm::enumerate(*mapping)) {
3704 newResults[i] = newLoop.getResult(j);
3705 newAfterArgs[i] = newAfter->getArgument(j);
3709 newBefore->getArguments());
3719void WhileOp::getCanonicalizationPatterns(RewritePatternSet &results,
3720 MLIRContext *context) {
3721 results.
add<WhileConditionTruth, WhileCmpCond, WhileOpAlignBeforeArgs,
3722 WhileMoveIfDown>(context);
3724 results, WhileOp::getOperationName());
3726 WhileOp::getOperationName());
3740 Region ®ion = *caseRegions.emplace_back(std::make_unique<Region>());
3743 caseValues.push_back(value);
3752 for (
auto [value, region] : llvm::zip(cases.
asArrayRef(), caseRegions)) {
3754 p <<
"case " << value <<
' ';
3759LogicalResult scf::IndexSwitchOp::verify() {
3760 if (getCases().size() != getCaseRegions().size()) {
3761 return emitOpError(
"has ")
3762 << getCaseRegions().size() <<
" case regions but "
3763 << getCases().size() <<
" case values";
3767 for (int64_t value : getCases())
3768 if (!valueSet.insert(value).second)
3769 return emitOpError(
"has duplicate case value: ") << value;
3770 auto verifyRegion = [&](Region ®ion,
const Twine &name) -> LogicalResult {
3771 auto yield = dyn_cast<YieldOp>(region.
front().
back());
3773 return emitOpError(
"expected region to end with scf.yield, but got ")
3776 if (yield.getNumOperands() != getNumResults()) {
3777 return (emitOpError(
"expected each region to return ")
3778 << getNumResults() <<
" values, but " << name <<
" returns "
3779 << yield.getNumOperands())
3780 .attachNote(yield.getLoc())
3781 <<
"see yield operation here";
3783 for (
auto [idx,
result, operand] :
3784 llvm::enumerate(getResultTypes(), yield.getOperands())) {
3786 return yield.emitOpError() <<
"operand " << idx <<
" is null\n";
3787 if (
result == operand.getType())
3789 return (emitOpError(
"expected result #")
3790 << idx <<
" of each region to be " <<
result)
3791 .attachNote(yield.getLoc())
3792 << name <<
" returns " << operand.getType() <<
" here";
3799 for (
auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
3806unsigned scf::IndexSwitchOp::getNumCases() {
return getCases().size(); }
3808Block &scf::IndexSwitchOp::getDefaultBlock() {
3809 return getDefaultRegion().front();
3812Block &scf::IndexSwitchOp::getCaseBlock(
unsigned idx) {
3813 assert(idx < getNumCases() &&
"case index out-of-bounds");
3814 return getCaseRegions()[idx].front();
3817void IndexSwitchOp::getSuccessorRegions(
3818 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
3821 successors.push_back(RegionSuccessor(getOperation()));
3825 llvm::append_range(successors, getRegions());
3828ValueRange IndexSwitchOp::getSuccessorInputs(RegionSuccessor successor) {
3833void IndexSwitchOp::getEntrySuccessorRegions(
3834 ArrayRef<Attribute> operands,
3835 SmallVectorImpl<RegionSuccessor> &successors) {
3836 FoldAdaptor adaptor(operands, *
this);
3839 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
3841 llvm::append_range(successors, getRegions());
3847 for (
auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
3848 if (caseValue == arg.getInt()) {
3849 successors.emplace_back(&caseRegion);
3853 successors.emplace_back(&getDefaultRegion());
3856void IndexSwitchOp::getRegionInvocationBounds(
3857 ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
3858 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
3859 if (!operandValue) {
3861 bounds.append(getNumRegions(), InvocationBounds(0, 1));
3865 unsigned liveIndex = getNumRegions() - 1;
3866 const auto *it = llvm::find(getCases(), operandValue.getInt());
3867 if (it != getCases().end())
3868 liveIndex = std::distance(getCases().begin(), it);
3869 for (
unsigned i = 0, e = getNumRegions(); i < e; ++i)
3870 bounds.emplace_back(0, i == liveIndex);
3873void IndexSwitchOp::getCanonicalizationPatterns(RewritePatternSet &results,
3874 MLIRContext *context) {
3876 results, IndexSwitchOp::getOperationName());
3878 results, IndexSwitchOp::getOperationName());
3885#define GET_OP_CLASSES
3886#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`
static std::string diag(const llvm::Value &value)
@ Paren
Parens surrounding zero or more operands.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
virtual ParseResult parseArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an arrow followed by a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
void printOptionalArrowTypeList(TypeRange &&types)
Print an optional arrow followed by a type list.
virtual void printNewline()
Print a newline and indent the printer to the start of the current operation/attribute/type.
This class represents an argument of a Block.
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
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.
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 replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues={})
Inline the operations of block 'source' into block 'dest' before the given position.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
void inlineRegionBefore(Region ®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...
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.
void promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
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.
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)
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.