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(
2758 result.addOperands(lowerBounds);
2759 result.addOperands(upperBounds);
2760 result.addOperands(steps);
2761 result.addOperands(initVals);
2763 result.addAttribute(getUnsignedCmpAttrName(
result.name),
2766 ParallelOp::getOperandSegmentSizeAttr(),
2768 static_cast<int32_t>(upperBounds.size()),
2769 static_cast<int32_t>(steps.size()),
2770 static_cast<int32_t>(initVals.size())}));
2773 OpBuilder::InsertionGuard guard(builder);
2774 unsigned numIVs = steps.size();
2775 SmallVector<Type, 8> argTypes(numIVs, builder.
getIndexType());
2776 SmallVector<Location, 8> argLocs(numIVs,
result.location);
2777 Region *bodyRegion =
result.addRegion();
2780 if (bodyBuilderFn) {
2782 bodyBuilderFn(builder,
result.location,
2787 if (initVals.empty())
2788 ParallelOp::ensureTerminator(*bodyRegion, builder,
result.location);
2791void ParallelOp::build(
2799 auto wrappedBuilderFn = [&bodyBuilderFn](OpBuilder &nestedBuilder,
2802 bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
2806 wrapper = wrappedBuilderFn;
2808 build(builder,
result, lowerBounds, upperBounds, steps,
ValueRange(), wrapper,
2812LogicalResult ParallelOp::verify() {
2817 if (stepValues.empty())
2819 "needs at least one tuple element for lowerBound, upperBound and step");
2822 for (Value stepValue : stepValues)
2825 return emitOpError(
"constant step operand must be positive");
2829 Block *body = getBody();
2831 return emitOpError() <<
"expects the same number of induction variables: "
2833 <<
" as bound and step values: " << stepValues.size();
2835 if (!arg.getType().isIndex())
2837 "expects arguments for the induction variable to be of index type");
2841 *
this, getRegion(),
"expects body to terminate with 'scf.reduce'");
2846 auto resultsSize = getResults().size();
2847 auto reductionsSize = reduceOp.getReductions().size();
2848 auto initValsSize = getInitVals().size();
2849 if (resultsSize != reductionsSize)
2850 return emitOpError() <<
"expects number of results: " << resultsSize
2851 <<
" to be the same as number of reductions: "
2853 if (resultsSize != initValsSize)
2854 return emitOpError() <<
"expects number of results: " << resultsSize
2855 <<
" to be the same as number of initial values: "
2857 if (reduceOp.getNumOperands() != initValsSize)
2862 for (int64_t i = 0; i < static_cast<int64_t>(reductionsSize); ++i) {
2863 auto resultType = getOperation()->getResult(i).getType();
2864 auto reductionOperandType = reduceOp.getOperands()[i].getType();
2865 if (resultType != reductionOperandType)
2866 return reduceOp.emitOpError()
2867 <<
"expects type of " << i
2868 <<
"-th reduction operand: " << reductionOperandType
2869 <<
" to be the same as the " << i
2870 <<
"-th result type: " << resultType;
2875ParseResult ParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
2878 result.addAttribute(getUnsignedCmpAttrName(
result.name),
2882 SmallVector<OpAsmParser::Argument, 4> ivs;
2887 SmallVector<OpAsmParser::UnresolvedOperand, 4> lower;
2894 SmallVector<OpAsmParser::UnresolvedOperand, 4> upper;
2902 SmallVector<OpAsmParser::UnresolvedOperand, 4> steps;
2910 SmallVector<OpAsmParser::UnresolvedOperand, 4> initVals;
2921 Region *body =
result.addRegion();
2922 for (
auto &iv : ivs)
2929 ParallelOp::getOperandSegmentSizeAttr(),
2931 static_cast<int32_t>(upper.size()),
2932 static_cast<int32_t>(steps.size()),
2933 static_cast<int32_t>(initVals.size())}));
2942 ParallelOp::ensureTerminator(*body, builder,
result.location);
2946void ParallelOp::print(OpAsmPrinter &p) {
2947 if (getUnsignedCmp())
2949 p <<
" (" << getBody()->getArguments() <<
") = (" <<
getLowerBound()
2950 <<
") to (" <<
getUpperBound() <<
") step (" << getStep() <<
")";
2951 if (!getInitVals().empty())
2952 p <<
" init (" << getInitVals() <<
")";
2959SmallVector<Region *> ParallelOp::getLoopRegions() {
return {&getRegion()}; }
2961std::optional<SmallVector<Value>> ParallelOp::getLoopInductionVars() {
2962 return SmallVector<Value>{getBody()->getArguments()};
2965std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopLowerBounds() {
2969std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopUpperBounds() {
2973std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopSteps() {
2978 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
2980 return ParallelOp();
2981 assert(ivArg.getOwner() &&
"unlinked block argument");
2982 auto *containingOp = ivArg.getOwner()->getParentOp();
2983 return dyn_cast<ParallelOp>(containingOp);
2988struct ParallelOpSingleOrZeroIterationDimsFolder
2992 LogicalResult matchAndRewrite(ParallelOp op,
2999 for (
auto [lb,
ub, step, iv] :
3000 llvm::zip(op.getLowerBound(), op.getUpperBound(), op.getStep(),
3001 op.getInductionVars())) {
3002 auto numIterations =
3004 if (numIterations.has_value()) {
3006 if (*numIterations == 0) {
3007 rewriter.
replaceOp(op, op.getInitVals());
3012 if (*numIterations == 1) {
3017 newLowerBounds.push_back(lb);
3018 newUpperBounds.push_back(ub);
3019 newSteps.push_back(step);
3022 if (newLowerBounds.size() == op.getLowerBound().size())
3025 if (newLowerBounds.empty()) {
3028 SmallVector<Value> results;
3029 results.reserve(op.getInitVals().size());
3030 for (
auto &bodyOp : op.getBody()->without_terminator())
3031 rewriter.
clone(bodyOp, mapping);
3032 auto reduceOp = cast<ReduceOp>(op.getBody()->getTerminator());
3033 for (int64_t i = 0, e = reduceOp.getReductions().size(); i < e; ++i) {
3034 Block &reduceBlock = reduceOp.getReductions()[i].front();
3035 auto initValIndex = results.size();
3036 mapping.
map(reduceBlock.
getArgument(0), op.getInitVals()[initValIndex]);
3040 rewriter.
clone(reduceBodyOp, mapping);
3043 cast<ReduceReturnOp>(reduceBlock.
getTerminator()).getResult());
3044 results.push_back(
result);
3052 ParallelOp::create(rewriter, op.getLoc(), newLowerBounds,
3053 newUpperBounds, newSteps, op.getInitVals(),
nullptr);
3059 newOp.getRegion().begin(), mapping);
3060 rewriter.
replaceOp(op, newOp.getResults());
3065struct MergeNestedParallelLoops :
public OpRewritePattern<ParallelOp> {
3066 using OpRewritePattern<ParallelOp>::OpRewritePattern;
3068 LogicalResult matchAndRewrite(ParallelOp op,
3069 PatternRewriter &rewriter)
const override {
3070 Block &outerBody = *op.getBody();
3074 auto innerOp = dyn_cast<ParallelOp>(outerBody.
front());
3079 if (llvm::is_contained(innerOp.getLowerBound(), val) ||
3080 llvm::is_contained(innerOp.getUpperBound(), val) ||
3081 llvm::is_contained(innerOp.getStep(), val))
3085 if (!op.getInitVals().empty() || !innerOp.getInitVals().empty())
3088 auto bodyBuilder = [&](OpBuilder &builder, Location ,
3090 Block &innerBody = *innerOp.getBody();
3091 assert(iterVals.size() ==
3099 builder.
clone(op, mapping);
3102 auto concatValues = [](
const auto &first,
const auto &second) {
3103 SmallVector<Value> ret;
3104 ret.reserve(first.size() + second.size());
3105 ret.assign(first.begin(), first.end());
3106 ret.append(second.begin(), second.end());
3110 auto newLowerBounds =
3111 concatValues(op.getLowerBound(), innerOp.getLowerBound());
3112 auto newUpperBounds =
3113 concatValues(op.getUpperBound(), innerOp.getUpperBound());
3114 auto newSteps = concatValues(op.getStep(), innerOp.getStep());
3125void ParallelOp::getCanonicalizationPatterns(RewritePatternSet &results,
3126 MLIRContext *context) {
3128 .
add<ParallelOpSingleOrZeroIterationDimsFolder, MergeNestedParallelLoops>(
3137void ParallelOp::getSuccessorRegions(
3138 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
3142 regions.push_back(RegionSuccessor(&getRegion()));
3143 regions.push_back(RegionSuccessor(getOperation()));
3150void ReduceOp::build(OpBuilder &builder, OperationState &
result) {}
3152void ReduceOp::build(OpBuilder &builder, OperationState &
result,
3154 result.addOperands(operands);
3155 for (Value v : operands) {
3156 OpBuilder::InsertionGuard guard(builder);
3157 Region *bodyRegion =
result.addRegion();
3159 ArrayRef<Type>{v.getType(), v.getType()},
3164LogicalResult ReduceOp::verifyRegions() {
3165 if (getReductions().size() != getOperands().size())
3166 return emitOpError() <<
"expects number of reduction regions: "
3167 << getReductions().size()
3168 <<
" to be the same as number of reduction operands: "
3169 << getOperands().size();
3172 for (int64_t i = 0, e = getReductions().size(); i < e; ++i) {
3173 auto type = getOperands()[i].getType();
3174 Block &block = getReductions()[i].front();
3176 return emitOpError() << i <<
"-th reduction has an empty body";
3178 llvm::any_of(block.
getArguments(), [&](
const BlockArgument &arg) {
3179 return arg.getType() != type;
3181 return emitOpError() <<
"expected two block arguments with type " << type
3182 <<
" in the " << i <<
"-th reduction region";
3186 return emitOpError(
"reduction bodies must be terminated with an "
3187 "'scf.reduce.return' op");
3194ReduceOp::getMutableSuccessorOperands(RegionSuccessor point) {
3196 return MutableOperandRange(getOperation(), 0, 0);
3203LogicalResult ReduceReturnOp::verify() {
3206 Block *reductionBody = getOperation()->getBlock();
3208 assert(isa<ReduceOp>(reductionBody->
getParentOp()) &&
"expected scf.reduce");
3210 if (expectedResultType != getResult().
getType())
3211 return emitOpError() <<
"must have type " << expectedResultType
3212 <<
" (the type of the reduction inputs)";
3220void WhileOp::build(::mlir::OpBuilder &odsBuilder,
3221 ::mlir::OperationState &odsState,
TypeRange resultTypes,
3222 ValueRange inits, BodyBuilderFn beforeBuilder,
3223 BodyBuilderFn afterBuilder) {
3227 OpBuilder::InsertionGuard guard(odsBuilder);
3230 SmallVector<Location, 4> beforeArgLocs;
3231 beforeArgLocs.reserve(inits.size());
3232 for (Value operand : inits) {
3233 beforeArgLocs.push_back(operand.getLoc());
3236 Region *beforeRegion = odsState.
addRegion();
3238 inits.getTypes(), beforeArgLocs);
3243 SmallVector<Location, 4> afterArgLocs(resultTypes.size(), odsState.
location);
3245 Region *afterRegion = odsState.
addRegion();
3247 resultTypes, afterArgLocs);
3253ConditionOp WhileOp::getConditionOp() {
3254 return cast<ConditionOp>(getBeforeBody()->getTerminator());
3257YieldOp WhileOp::getYieldOp() {
3258 return cast<YieldOp>(getAfterBody()->getTerminator());
3261std::optional<MutableArrayRef<OpOperand>> WhileOp::getYieldedValuesMutable() {
3262 return getYieldOp().getResultsMutable();
3266 return getBeforeBody()->getArguments();
3270 return getAfterBody()->getArguments();
3274 return getBeforeArguments();
3277OperandRange WhileOp::getEntrySuccessorOperands(RegionSuccessor successor) {
3279 "WhileOp is expected to branch only to the first region");
3283void WhileOp::getSuccessorRegions(RegionBranchPoint point,
3284 SmallVectorImpl<RegionSuccessor> ®ions) {
3287 regions.emplace_back(&getBefore());
3291 assert(llvm::is_contained(
3292 {&getAfter(), &getBefore()},
3294 "there are only two regions in a WhileOp");
3298 regions.emplace_back(&getBefore());
3302 regions.push_back(RegionSuccessor(getOperation()));
3303 regions.emplace_back(&getAfter());
3306ValueRange WhileOp::getSuccessorInputs(RegionSuccessor successor) {
3308 return getOperation()->getResults();
3309 if (successor == &getBefore())
3310 return getBefore().getArguments();
3311 if (successor == &getAfter())
3312 return getAfter().getArguments();
3313 llvm_unreachable(
"invalid region successor");
3316SmallVector<Region *> WhileOp::getLoopRegions() {
3317 return {&getBefore(), &getAfter()};
3327ParseResult scf::WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
3328 SmallVector<OpAsmParser::Argument, 4> regionArgs;
3329 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
3330 Region *before =
result.addRegion();
3331 Region *after =
result.addRegion();
3333 OptionalParseResult listResult =
3338 FunctionType functionType;
3343 result.addTypes(functionType.getResults());
3345 if (functionType.getNumInputs() != operands.size()) {
3347 <<
"expected as many input types as operands " <<
"(expected "
3348 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
3358 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
3359 regionArgs[i].type = functionType.getInput(i);
3361 return failure(parser.
parseRegion(*before, regionArgs) ||
3367void scf::WhileOp::print(OpAsmPrinter &p) {
3376 (*this)->getDiscardableAttrDictionary().getValue());
3379LogicalResult scf::WhileOp::verify() {
3382 "expects the 'before' region to terminate with 'scf.condition'");
3383 if (!beforeTerminator)
3388 "expects the 'after' region to terminate with 'scf.yield'");
3389 return success(afterTerminator !=
nullptr);
3426struct WhileMoveIfDown :
public OpRewritePattern<scf::WhileOp> {
3427 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3429 LogicalResult matchAndRewrite(scf::WhileOp op,
3430 PatternRewriter &rewriter)
const override {
3431 auto conditionOp = op.getConditionOp();
3439 auto ifOp = dyn_cast_or_null<scf::IfOp>(conditionOp->getPrevNode());
3445 if (!ifOp || ifOp.getCondition() != conditionOp.getCondition() ||
3446 (ifOp.elseBlock() && !ifOp.elseBlock()->without_terminator().empty()))
3449 assert((ifOp->use_empty() || (llvm::all_equal(ifOp->getUsers()) &&
3450 *ifOp->user_begin() == conditionOp)) &&
3451 "ifOp has unexpected uses");
3453 Location loc = op.getLoc();
3463 for (
auto [idx, arg] : llvm::enumerate(conditionOp.getArgs())) {
3464 auto it = llvm::find(ifOp->getResults(), arg);
3465 if (it == ifOp->getResults().end())
3467 size_t ifOpIdx = it.getIndex();
3469 ifOp.thenYield()->getOperand(ifOpIdx));
3470 unsigned argIdx = idx;
3471 Value elseValue = ifOp.elseYield()->getOperand(ifOpIdx);
3473 conditionOp.getArgsMutable()[argIdx].assign(elseValue);
3480 if (&op.getBefore() == operand->get().getParentRegion())
3481 additionalUsedValuesSet.insert(operand->get());
3485 auto additionalUsedValues = additionalUsedValuesSet.getArrayRef();
3486 auto additionalValueTypes = llvm::map_to_vector(
3487 additionalUsedValues, [](Value val) {
return val.
getType(); });
3488 size_t additionalValueSize = additionalUsedValues.size();
3489 SmallVector<Type> newResultTypes(op.getResultTypes());
3490 newResultTypes.append(additionalValueTypes);
3493 scf::WhileOp::create(rewriter, loc, newResultTypes, op.getInits());
3496 newWhileOp.getBefore().takeBody(op.getBefore());
3497 newWhileOp.getAfter().takeBody(op.getAfter());
3498 newWhileOp.getAfter().addArguments(
3499 additionalValueTypes,
3500 SmallVector<Location>(additionalValueSize, loc));
3504 conditionOp.getArgsMutable().append(additionalUsedValues);
3510 additionalUsedValues,
3511 newWhileOp.getAfterArguments().take_back(additionalValueSize),
3512 [&](OpOperand &use) {
3513 return ifOp.getThenRegion().isAncestor(
3514 use.getOwner()->getParentRegion());
3518 rewriter.
eraseOp(ifOp.thenYield());
3520 newWhileOp.getAfterBody()->begin());
3523 newWhileOp->getResults().drop_back(additionalValueSize));
3547struct WhileConditionTruth :
public OpRewritePattern<WhileOp> {
3548 using OpRewritePattern<WhileOp>::OpRewritePattern;
3550 LogicalResult matchAndRewrite(WhileOp op,
3551 PatternRewriter &rewriter)
const override {
3552 auto term = op.getConditionOp();
3556 Value constantTrue =
nullptr;
3558 bool replaced =
false;
3559 for (
auto yieldedAndBlockArgs :
3560 llvm::zip(term.getArgs(), op.getAfterArguments())) {
3561 if (std::get<0>(yieldedAndBlockArgs) == term.getCondition()) {
3562 if (!std::get<1>(yieldedAndBlockArgs).use_empty()) {
3564 constantTrue = arith::ConstantOp::create(
3565 rewriter, op.getLoc(), term.getCondition().getType(),
3600struct WhileCmpCond :
public OpRewritePattern<scf::WhileOp> {
3601 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3603 LogicalResult matchAndRewrite(scf::WhileOp op,
3604 PatternRewriter &rewriter)
const override {
3605 using namespace scf;
3606 auto cond = op.getConditionOp();
3607 auto cmp = cond.getCondition().getDefiningOp<arith::CmpIOp>();
3610 bool changed =
false;
3611 for (
auto tup : llvm::zip(cond.getArgs(), op.getAfterArguments())) {
3612 for (
size_t opIdx = 0; opIdx < 2; opIdx++) {
3613 if (std::get<0>(tup) != cmp.getOperand(opIdx))
3616 llvm::make_early_inc_range(std::get<1>(tup).getUses())) {
3617 auto cmp2 = dyn_cast<arith::CmpIOp>(u.getOwner());
3621 if (cmp2.getOperand(1 - opIdx) != cmp.getOperand(1 - opIdx))
3624 if (cmp2.getPredicate() == cmp.getPredicate())
3625 samePredicate =
true;
3626 else if (cmp2.getPredicate() ==
3627 arith::invertPredicate(cmp.getPredicate()))
3628 samePredicate =
false;
3644static std::optional<SmallVector<unsigned>> getArgsMapping(
ValueRange args1,
3646 if (args1.size() != args2.size())
3647 return std::nullopt;
3649 SmallVector<unsigned> ret(args1.size());
3650 for (
auto &&[i, arg1] : llvm::enumerate(args1)) {
3651 auto it = llvm::find(args2, arg1);
3652 if (it == args2.end())
3653 return std::nullopt;
3655 ret[std::distance(args2.begin(), it)] =
static_cast<unsigned>(i);
3662 llvm::SmallDenseSet<Value> set;
3663 for (Value arg : args) {
3664 if (!set.insert(arg).second)
3674struct WhileOpAlignBeforeArgs :
public OpRewritePattern<WhileOp> {
3677 LogicalResult matchAndRewrite(WhileOp loop,
3678 PatternRewriter &rewriter)
const override {
3679 auto *oldBefore = loop.getBeforeBody();
3680 ConditionOp oldTerm = loop.getConditionOp();
3681 ValueRange beforeArgs = oldBefore->getArguments();
3683 if (beforeArgs == termArgs)
3686 if (hasDuplicates(termArgs))
3689 auto mapping = getArgsMapping(beforeArgs, termArgs);
3694 OpBuilder::InsertionGuard g(rewriter);
3700 auto *oldAfter = loop.getAfterBody();
3702 SmallVector<Type> newResultTypes(beforeArgs.size());
3703 for (
auto &&[i, j] : llvm::enumerate(*mapping))
3704 newResultTypes[j] = loop.getResult(i).getType();
3706 auto newLoop = WhileOp::create(
3707 rewriter, loop.getLoc(), newResultTypes, loop.getInits(),
3709 auto *newBefore = newLoop.getBeforeBody();
3710 auto *newAfter = newLoop.getAfterBody();
3712 SmallVector<Value> newResults(beforeArgs.size());
3713 SmallVector<Value> newAfterArgs(beforeArgs.size());
3714 for (
auto &&[i, j] : llvm::enumerate(*mapping)) {
3715 newResults[i] = newLoop.getResult(j);
3716 newAfterArgs[i] = newAfter->getArgument(j);
3720 newBefore->getArguments());
3730void WhileOp::getCanonicalizationPatterns(RewritePatternSet &results,
3731 MLIRContext *context) {
3732 results.
add<WhileConditionTruth, WhileCmpCond, WhileOpAlignBeforeArgs,
3733 WhileMoveIfDown>(context);
3735 results, WhileOp::getOperationName());
3737 WhileOp::getOperationName());
3751 Region ®ion = *caseRegions.emplace_back(std::make_unique<Region>());
3754 caseValues.push_back(value);
3763 for (
auto [value, region] : llvm::zip(cases.
asArrayRef(), caseRegions)) {
3765 p <<
"case " << value <<
' ';
3770LogicalResult scf::IndexSwitchOp::verify() {
3771 if (getCases().size() != getCaseRegions().size()) {
3772 return emitOpError(
"has ")
3773 << getCaseRegions().size() <<
" case regions but "
3774 << getCases().size() <<
" case values";
3778 for (int64_t value : getCases())
3779 if (!valueSet.insert(value).second)
3780 return emitOpError(
"has duplicate case value: ") << value;
3781 auto verifyRegion = [&](Region ®ion,
const Twine &name) -> LogicalResult {
3782 auto yield = dyn_cast<YieldOp>(region.
front().
back());
3784 return emitOpError(
"expected region to end with scf.yield, but got ")
3787 if (yield.getNumOperands() != getNumResults()) {
3788 return (emitOpError(
"expected each region to return ")
3789 << getNumResults() <<
" values, but " << name <<
" returns "
3790 << yield.getNumOperands())
3791 .attachNote(yield.getLoc())
3792 <<
"see yield operation here";
3794 for (
auto [idx,
result, operand] :
3795 llvm::enumerate(getResultTypes(), yield.getOperands())) {
3797 return yield.emitOpError() <<
"operand " << idx <<
" is null\n";
3798 if (
result == operand.getType())
3800 return (emitOpError(
"expected result #")
3801 << idx <<
" of each region to be " <<
result)
3802 .attachNote(yield.getLoc())
3803 << name <<
" returns " << operand.getType() <<
" here";
3810 for (
auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
3817unsigned scf::IndexSwitchOp::getNumCases() {
return getCases().size(); }
3819Block &scf::IndexSwitchOp::getDefaultBlock() {
3820 return getDefaultRegion().front();
3823Block &scf::IndexSwitchOp::getCaseBlock(
unsigned idx) {
3824 assert(idx < getNumCases() &&
"case index out-of-bounds");
3825 return getCaseRegions()[idx].front();
3828void IndexSwitchOp::getSuccessorRegions(
3829 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
3832 successors.push_back(RegionSuccessor(getOperation()));
3836 llvm::append_range(successors, getRegions());
3839ValueRange IndexSwitchOp::getSuccessorInputs(RegionSuccessor successor) {
3844void IndexSwitchOp::getEntrySuccessorRegions(
3845 ArrayRef<Attribute> operands,
3846 SmallVectorImpl<RegionSuccessor> &successors) {
3847 FoldAdaptor adaptor(operands, *
this);
3850 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
3852 llvm::append_range(successors, getRegions());
3858 for (
auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
3859 if (caseValue == arg.getInt()) {
3860 successors.emplace_back(&caseRegion);
3864 successors.emplace_back(&getDefaultRegion());
3867void IndexSwitchOp::getRegionInvocationBounds(
3868 ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
3869 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
3870 if (!operandValue) {
3872 bounds.append(getNumRegions(), InvocationBounds(0, 1));
3876 unsigned liveIndex = getNumRegions() - 1;
3877 const auto *it = llvm::find(getCases(), operandValue.getInt());
3878 if (it != getCases().end())
3879 liveIndex = std::distance(getCases().begin(), it);
3880 for (
unsigned i = 0, e = getNumRegions(); i < e; ++i)
3881 bounds.emplace_back(0, i == liveIndex);
3884void IndexSwitchOp::getCanonicalizationPatterns(RewritePatternSet &results,
3885 MLIRContext *context) {
3887 results, IndexSwitchOp::getOperationName());
3889 results, IndexSwitchOp::getOperationName());
3896#define GET_OP_CLASSES
3897#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
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.