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)
740 return promote(rewriter, *
this);
744 return getBody()->getArguments().drop_front(getRank());
747MutableArrayRef<OpOperand> ForallOp::getInitsMutable() {
748 return getOutputsMutable();
753 scf::ForallOp forallOp) {
755 scf::InParallelOp terminator = forallOp.getTerminator();
759 for (
Operation &op : terminator.getYieldingOps()) {
760 if (!isa<tensor::ParallelInsertSliceOp>(op))
762 diag <<
"unsupported parallel combining op: " << op.
getName();
769 bbArgReplacements.append(forallOp.getOutputs().begin(),
770 forallOp.getOutputs().end());
775 yieldingOps.reserve(forallOp.getNumResults());
777 for (
BlockArgument outputArg : forallOp.getRegionIterArgs()) {
778 auto &outputOps = yieldingOps.emplace_back();
780 for (
Operation *op : forallOp.getCombiningOps(outputArg)) {
781 auto combiningOp = cast<ParallelCombiningOpInterface>(op);
783 if (combiningOp->getParentOp() != terminator.getOperation() ||
784 combiningOp.getUpdatedDestinations()[0].get() != outputArg)
787 outputOps.push_back(combiningOp);
793 forallOp->getIterator(), bbArgReplacements);
798 results.reserve(forallOp.getResults().size());
799 for (
auto [output, combiningOps] :
800 llvm::zip_equal(forallOp.getOutputs(), yieldingOps)) {
803 for (ParallelCombiningOpInterface combiningOp : combiningOps) {
804 auto insertOp = cast<tensor::ParallelInsertSliceOp>(combiningOp);
805 result = tensor::InsertSliceOp::create(
806 rewriter, forallOp.getLoc(),
result.getType(), insertOp.getSource(),
807 result, insertOp.getOffsets(), insertOp.getSizes(),
808 insertOp.getStrides(), insertOp.getStaticOffsets(),
809 insertOp.getStaticSizes(), insertOp.getStaticStrides());
812 results.push_back(
result);
828 assert(lbs.size() == ubs.size() &&
829 "expected the same number of lower and upper bounds");
830 assert(lbs.size() == steps.size() &&
831 "expected the same number of lower bounds and steps");
836 bodyBuilder ? bodyBuilder(builder, loc,
ValueRange(), iterArgs)
838 assert(results.size() == iterArgs.size() &&
839 "loop nest body must return as many values as loop has iteration "
841 return LoopNest{{}, std::move(results)};
849 loops.reserve(lbs.size());
850 ivs.reserve(lbs.size());
853 for (
unsigned i = 0, e = lbs.size(); i < e; ++i) {
854 auto loop = scf::ForOp::create(
855 builder, currentLoc, lbs[i], ubs[i], steps[i], currentIterArgs,
861 currentIterArgs = args;
862 currentLoc = nestedLoc;
868 loops.push_back(loop);
872 for (
unsigned i = 0, e = loops.size() - 1; i < e; ++i) {
874 scf::YieldOp::create(builder, loc, loops[i + 1].getResults());
881 ? bodyBuilder(builder, currentLoc, ivs,
882 loops.back().getRegionIterArgs())
884 assert(results.size() == iterArgs.size() &&
885 "loop nest body must return as many values as loop has iteration "
888 scf::YieldOp::create(builder, loc, results);
892 llvm::append_range(nestResults, loops.front().getResults());
893 return LoopNest{std::move(loops), std::move(nestResults)};
906 bodyBuilder(nestedBuilder, nestedLoc, ivs);
915 assert(operand.
getOwner() == forOp);
920 "expected an iter OpOperand");
922 "Expected a different type");
924 for (
OpOperand &opOperand : forOp.getInitArgsMutable()) {
929 newIterOperands.push_back(opOperand.get());
933 scf::ForOp newForOp = scf::ForOp::create(
934 rewriter, forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
935 forOp.getStep(), newIterOperands,
nullptr,
936 forOp.getUnsignedCmp());
937 newForOp->setDiscardableAttrs(
938 forOp->getDiscardableAttrDictionary().getValue());
939 Block &newBlock = newForOp.getRegion().
front();
947 BlockArgument newRegionIterArg = newForOp.getTiedLoopRegionIterArg(
949 Value castIn = castFn(rewriter, newForOp.getLoc(), oldType, newRegionIterArg);
950 newBlockTransferArgs[newRegionIterArg.
getArgNumber()] = castIn;
954 rewriter.
mergeBlocks(&oldBlock, &newBlock, newBlockTransferArgs);
957 auto clonedYieldOp = cast<scf::YieldOp>(newBlock.
getTerminator());
960 newRegionIterArg.
getArgNumber() - forOp.getNumInductionVars();
961 Value castOut = castFn(rewriter, newForOp.getLoc(), newType,
962 clonedYieldOp.getOperand(yieldIdx));
964 newYieldOperands[yieldIdx] = castOut;
965 scf::YieldOp::create(rewriter, newForOp.getLoc(), newYieldOperands);
966 rewriter.
eraseOp(clonedYieldOp);
971 newResults[yieldIdx] =
972 castFn(rewriter, newForOp.getLoc(), oldType, newResults[yieldIdx]);
1007 LogicalResult matchAndRewrite(ForOp op,
1009 for (
auto it : llvm::zip(op.getInitArgsMutable(), op.getResults())) {
1010 OpOperand &iterOpOperand = std::get<0>(it);
1012 if (!incomingCast ||
1013 incomingCast.getSource().getType() == incomingCast.getType())
1018 incomingCast.getDest().getType(),
1019 incomingCast.getSource().getType()))
1021 if (!std::get<1>(it).hasOneUse())
1027 rewriter, op, iterOpOperand, incomingCast.getSource(),
1029 return tensor::CastOp::create(b, loc, type, source);
1038void ForOp::getCanonicalizationPatterns(RewritePatternSet &results,
1039 MLIRContext *context) {
1040 results.
add<ForOpTensorCastFolder>(context);
1042 results, ForOp::getOperationName());
1047 results, ForOp::getOperationName(),
1049 [](OpBuilder &builder, Location loc, Value value) {
1053 auto blockArg = cast<BlockArgument>(value);
1054 assert(blockArg.getArgNumber() == 0 &&
"expected induction variable");
1055 auto forOp = cast<ForOp>(blockArg.getOwner()->getParentOp());
1056 return forOp.getLowerBound();
1062std::optional<APInt> ForOp::getConstantStep() {
1065 return step.getValue();
1069std::optional<MutableArrayRef<OpOperand>> ForOp::getYieldedValuesMutable() {
1070 return cast<scf::YieldOp>(getBody()->getTerminator()).getResultsMutable();
1076 if (
auto constantStep = getConstantStep())
1077 if (*constantStep == 1)
1085std::optional<APInt> ForOp::getStaticTripCount() {
1094LogicalResult ForallOp::verify() {
1095 unsigned numLoops = getRank();
1097 if (getNumResults() != getOutputs().size())
1098 return emitOpError(
"produces ")
1099 << getNumResults() <<
" results, but has only "
1100 << getOutputs().size() <<
" outputs";
1103 auto *body = getBody();
1105 return emitOpError(
"region expects ") << numLoops <<
" arguments";
1106 for (int64_t i = 0; i < numLoops; ++i)
1108 return emitOpError(
"expects ")
1109 << i <<
"-th block argument to be an index";
1110 for (
unsigned i = 0; i < getOutputs().size(); ++i)
1112 return emitOpError(
"type mismatch between ")
1113 << i <<
"-th output and corresponding block argument";
1114 if (getMapping().has_value() && !getMapping()->empty()) {
1115 if (getDeviceMappingAttrs().size() != numLoops)
1116 return emitOpError() <<
"mapping attribute size must match op rank";
1117 if (
failed(getDeviceMaskingAttr()))
1119 <<
" supports at most one device masking attribute";
1123 Operation *op = getOperation();
1125 getStaticLowerBound(),
1126 getDynamicLowerBound())))
1129 getStaticUpperBound(),
1130 getDynamicUpperBound())))
1133 getStaticStep(), getDynamicStep())))
1139void ForallOp::print(OpAsmPrinter &p) {
1140 Operation *op = getOperation();
1141 p <<
" (" << getInductionVars();
1142 if (isNormalized()) {
1163 if (!getRegionOutArgs().empty())
1164 p <<
"-> (" << getResultTypes() <<
") ";
1165 p.printRegion(getRegion(),
1167 getNumResults() > 0);
1169 if (
ArrayAttr mapping = getMappingAttr())
1172 p.printOptionalAttrDict(attrs);
1175ParseResult ForallOp::parse(OpAsmParser &parser, OperationState &
result) {
1177 auto indexType =
b.getIndexType();
1182 SmallVector<OpAsmParser::Argument, 4> ivs;
1187 SmallVector<OpAsmParser::UnresolvedOperand> dynamicLbs, dynamicUbs,
1197 unsigned numLoops = ivs.size();
1198 staticLbs =
b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 0));
1199 staticSteps =
b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 1));
1228 SmallVector<OpAsmParser::Argument, 4> regionOutArgs;
1229 SmallVector<OpAsmParser::UnresolvedOperand, 4> outOperands;
1232 if (outOperands.size() !=
result.types.size())
1234 "mismatch between out operands and types");
1243 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1244 std::unique_ptr<Region> region = std::make_unique<Region>();
1245 for (
auto &iv : ivs) {
1246 iv.type =
b.getIndexType();
1247 regionArgs.push_back(iv);
1249 for (
const auto &it : llvm::enumerate(regionOutArgs)) {
1250 auto &out = it.value();
1251 out.type =
result.types[it.index()];
1252 regionArgs.push_back(out);
1258 ForallOp::ensureTerminator(*region,
b,
result.location);
1259 result.addRegion(std::move(region));
1265 result.addAttribute(
"staticLowerBound", staticLbs);
1266 result.addAttribute(
"staticUpperBound", staticUbs);
1267 result.addAttribute(
"staticStep", staticSteps);
1268 result.addAttribute(
"operandSegmentSizes",
1270 {static_cast<int32_t>(dynamicLbs.size()),
1271 static_cast<int32_t>(dynamicUbs.size()),
1272 static_cast<int32_t>(dynamicSteps.size()),
1273 static_cast<int32_t>(outOperands.size())}));
1278void ForallOp::build(
1279 mlir::OpBuilder &
b, mlir::OperationState &
result,
1280 ArrayRef<OpFoldResult> lbs, ArrayRef<OpFoldResult> ubs,
1281 ArrayRef<OpFoldResult> steps,
ValueRange outputs,
1282 std::optional<ArrayAttr> mapping,
1284 SmallVector<int64_t> staticLbs, staticUbs, staticSteps;
1285 SmallVector<Value> dynamicLbs, dynamicUbs, dynamicSteps;
1290 result.addOperands(dynamicLbs);
1291 result.addOperands(dynamicUbs);
1292 result.addOperands(dynamicSteps);
1293 result.addOperands(outputs);
1296 result.addAttribute(getStaticLowerBoundAttrName(
result.name),
1297 b.getDenseI64ArrayAttr(staticLbs));
1298 result.addAttribute(getStaticUpperBoundAttrName(
result.name),
1299 b.getDenseI64ArrayAttr(staticUbs));
1300 result.addAttribute(getStaticStepAttrName(
result.name),
1301 b.getDenseI64ArrayAttr(staticSteps));
1303 "operandSegmentSizes",
1304 b.getDenseI32ArrayAttr({static_cast<int32_t>(dynamicLbs.size()),
1305 static_cast<int32_t>(dynamicUbs.size()),
1306 static_cast<int32_t>(dynamicSteps.size()),
1307 static_cast<int32_t>(outputs.size())}));
1308 if (mapping.has_value()) {
1309 result.addAttribute(ForallOp::getMappingAttrName(
result.name),
1313 Region *bodyRegion =
result.addRegion();
1314 OpBuilder::InsertionGuard g(
b);
1315 b.createBlock(bodyRegion);
1320 SmallVector<Type>(lbs.size(),
b.getIndexType()),
1321 SmallVector<Location>(staticLbs.size(),
result.location));
1324 SmallVector<Location>(outputs.size(),
result.location));
1326 b.setInsertionPointToStart(&bodyBlock);
1327 if (!bodyBuilderFn) {
1328 ForallOp::ensureTerminator(*bodyRegion,
b,
result.location);
1335void ForallOp::build(
1336 mlir::OpBuilder &
b, mlir::OperationState &
result,
1337 ArrayRef<OpFoldResult> ubs,
ValueRange outputs,
1338 std::optional<ArrayAttr> mapping,
1340 unsigned numLoops = ubs.size();
1341 SmallVector<OpFoldResult> lbs(numLoops,
b.getIndexAttr(0));
1342 SmallVector<OpFoldResult> steps(numLoops,
b.getIndexAttr(1));
1343 build(
b,
result, lbs, ubs, steps, outputs, mapping, bodyBuilderFn);
1347bool ForallOp::isNormalized() {
1348 auto allEqual = [](ArrayRef<OpFoldResult> results, int64_t val) {
1349 return llvm::all_of(results, [&](OpFoldResult ofr) {
1351 return intValue.has_value() && intValue == val;
1354 return allEqual(getMixedLowerBound(), 0) && allEqual(getMixedStep(), 1);
1357InParallelOp ForallOp::getTerminator() {
1358 return cast<InParallelOp>(getBody()->getTerminator());
1361SmallVector<Operation *> ForallOp::getCombiningOps(BlockArgument bbArg) {
1362 SmallVector<Operation *> storeOps;
1363 for (Operation *user : bbArg.
getUsers()) {
1364 if (
auto parallelOp = dyn_cast<ParallelCombiningOpInterface>(user)) {
1365 storeOps.push_back(parallelOp);
1371SmallVector<DeviceMappingAttrInterface> ForallOp::getDeviceMappingAttrs() {
1372 SmallVector<DeviceMappingAttrInterface> res;
1375 for (
auto attr : getMapping()->getValue()) {
1376 auto m = dyn_cast<DeviceMappingAttrInterface>(attr);
1383FailureOr<DeviceMaskingAttrInterface> ForallOp::getDeviceMaskingAttr() {
1384 DeviceMaskingAttrInterface res;
1387 for (
auto attr : getMapping()->getValue()) {
1388 auto m = dyn_cast<DeviceMaskingAttrInterface>(attr);
1397bool ForallOp::usesLinearMapping() {
1398 SmallVector<DeviceMappingAttrInterface> ifaces = getDeviceMappingAttrs();
1401 return ifaces.front().isLinearMapping();
1404std::optional<SmallVector<Value>> ForallOp::getLoopInductionVars() {
1405 return SmallVector<Value>{getBody()->getArguments().take_front(getRank())};
1409std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopLowerBounds() {
1411 return getMixedValues(getStaticLowerBound(), getDynamicLowerBound(),
b);
1415std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopUpperBounds() {
1417 return getMixedValues(getStaticUpperBound(), getDynamicUpperBound(),
b);
1421std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopSteps() {
1427 auto tidxArg = llvm::dyn_cast<BlockArgument>(val);
1430 assert(tidxArg.getOwner() &&
"unlinked block argument");
1431 auto *containingOp = tidxArg.getOwner()->getParentOp();
1432 return dyn_cast<ForallOp>(containingOp);
1440 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1442 auto forallOp = dimOp.getSource().getDefiningOp<ForallOp>();
1446 forallOp.getTiedOpOperand(llvm::cast<OpResult>(dimOp.getSource()))
1449 dimOp, [&]() { dimOp.getSourceMutable().assign(sharedOut); });
1454class ForallOpControlOperandsFolder :
public OpRewritePattern<ForallOp> {
1456 using OpRewritePattern<ForallOp>::OpRewritePattern;
1458 LogicalResult matchAndRewrite(ForallOp op,
1459 PatternRewriter &rewriter)
const override {
1460 SmallVector<OpFoldResult> mixedLowerBound(op.getMixedLowerBound());
1461 SmallVector<OpFoldResult> mixedUpperBound(op.getMixedUpperBound());
1462 SmallVector<OpFoldResult> mixedStep(op.getMixedStep());
1469 SmallVector<Value> dynamicLowerBound, dynamicUpperBound, dynamicStep;
1470 SmallVector<int64_t> staticLowerBound, staticUpperBound, staticStep;
1473 op.getDynamicLowerBoundMutable().assign(dynamicLowerBound);
1474 op.setStaticLowerBound(staticLowerBound);
1478 op.getDynamicUpperBoundMutable().assign(dynamicUpperBound);
1479 op.setStaticUpperBound(staticUpperBound);
1482 op.getDynamicStepMutable().assign(dynamicStep);
1483 op.setStaticStep(staticStep);
1485 op->setInherentAttr(
1486 rewriter.
getStringAttr(ForallOp::getOperandSegmentSizeAttr()),
1488 {static_cast<int32_t>(dynamicLowerBound.size()),
1489 static_cast<int32_t>(dynamicUpperBound.size()),
1490 static_cast<int32_t>(dynamicStep.size()),
1491 static_cast<int32_t>(op.getNumResults())}));
1570struct ForallOpIterArgsFolder :
public OpRewritePattern<ForallOp> {
1571 using OpRewritePattern<ForallOp>::OpRewritePattern;
1573 LogicalResult matchAndRewrite(ForallOp forallOp,
1574 PatternRewriter &rewriter)
const final {
1585 SmallVector<Value> resultsToDelete;
1586 SmallVector<Value> outsToDelete;
1587 SmallVector<BlockArgument> blockArgsToDelete;
1588 SmallVector<Value> newOuts;
1589 BitVector resultIndicesToDelete(forallOp.getNumResults(),
false);
1590 BitVector blockIndicesToDelete(forallOp.getBody()->getNumArguments(),
1592 for (OpResult
result : forallOp.getResults()) {
1593 OpOperand *opOperand = forallOp.getTiedOpOperand(
result);
1594 BlockArgument blockArg = forallOp.getTiedBlockArgument(opOperand);
1595 if (
result.use_empty() || forallOp.getCombiningOps(blockArg).empty()) {
1596 resultsToDelete.push_back(
result);
1597 outsToDelete.push_back(opOperand->
get());
1598 blockArgsToDelete.push_back(blockArg);
1599 resultIndicesToDelete[
result.getResultNumber()] =
true;
1602 newOuts.push_back(opOperand->
get());
1608 if (resultsToDelete.empty())
1613 for (
auto blockArg : blockArgsToDelete) {
1614 SmallVector<Operation *> combiningOps =
1615 forallOp.getCombiningOps(blockArg);
1616 for (Operation *combiningOp : combiningOps)
1617 rewriter.
eraseOp(combiningOp);
1619 for (
auto [blockArg,
result, out] :
1620 llvm::zip_equal(blockArgsToDelete, resultsToDelete, outsToDelete)) {
1626 forallOp.getBody()->eraseArguments(blockIndicesToDelete);
1631 auto newForallOp = cast<scf::ForallOp>(
1633 newForallOp.getOutputsMutable().assign(newOuts);
1639struct ForallOpSingleOrZeroIterationDimsFolder
1640 :
public OpRewritePattern<ForallOp> {
1641 using OpRewritePattern<ForallOp>::OpRewritePattern;
1643 LogicalResult matchAndRewrite(ForallOp op,
1644 PatternRewriter &rewriter)
const override {
1646 if (op.getMapping().has_value() && !op.getMapping()->empty())
1648 Location loc = op.getLoc();
1651 SmallVector<OpFoldResult> newMixedLowerBounds, newMixedUpperBounds,
1653 SmallVector<std::pair<Value, OpFoldResult>> singleIterationIVs;
1654 for (
auto [lb, ub, step, iv] :
1655 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1656 op.getMixedStep(), op.getInductionVars())) {
1657 auto numIterations =
1659 if (numIterations.has_value()) {
1661 if (*numIterations == 0) {
1662 rewriter.
replaceOp(op, op.getOutputs());
1667 if (*numIterations == 1) {
1668 singleIterationIVs.emplace_back(iv, lb);
1672 newMixedLowerBounds.push_back(lb);
1673 newMixedUpperBounds.push_back(ub);
1674 newMixedSteps.push_back(step);
1678 if (newMixedLowerBounds.empty())
1682 if (newMixedLowerBounds.size() ==
static_cast<unsigned>(op.getRank())) {
1684 op,
"no dimensions have 0 or 1 iterations");
1690 for (
auto [iv, lb] : singleIterationIVs)
1695 newOp = ForallOp::create(rewriter, loc, newMixedLowerBounds,
1696 newMixedUpperBounds, newMixedSteps,
1697 op.getOutputs(), std::nullopt,
nullptr);
1698 newOp.getBodyRegion().getBlocks().clear();
1699 newOp.setMappingAttr(op.getMappingAttr());
1700 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
1702 newOp.getRegion().begin(), mapping);
1703 rewriter.
replaceOp(op, newOp.getResults());
1709struct ForallOpReplaceConstantInductionVar :
public OpRewritePattern<ForallOp> {
1710 using OpRewritePattern<ForallOp>::OpRewritePattern;
1712 LogicalResult matchAndRewrite(ForallOp op,
1713 PatternRewriter &rewriter)
const override {
1714 Location loc = op.getLoc();
1715 bool changed =
false;
1716 for (
auto [lb, ub, step, iv] :
1717 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1718 op.getMixedStep(), op.getInductionVars())) {
1721 auto numIterations =
1723 if (!numIterations.has_value() || numIterations.value() != 1) {
1734struct FoldTensorCastOfOutputIntoForallOp
1735 :
public OpRewritePattern<scf::ForallOp> {
1736 using OpRewritePattern<scf::ForallOp>::OpRewritePattern;
1743 LogicalResult matchAndRewrite(scf::ForallOp forallOp,
1744 PatternRewriter &rewriter)
const final {
1745 llvm::SmallMapVector<unsigned, TypeCast, 2> tensorCastProducers;
1746 llvm::SmallVector<Value> newOutputTensors = forallOp.getOutputs();
1747 for (
auto en : llvm::enumerate(newOutputTensors)) {
1748 auto castOp = en.value().getDefiningOp<tensor::CastOp>();
1755 castOp.getSource().getType())) {
1759 tensorCastProducers[en.index()] =
1760 TypeCast{castOp.getSource().getType(), castOp.getType()};
1761 newOutputTensors[en.index()] = castOp.getSource();
1764 if (tensorCastProducers.empty())
1768 Location loc = forallOp.getLoc();
1769 auto newForallOp = ForallOp::create(
1770 rewriter, loc, forallOp.getMixedLowerBound(),
1771 forallOp.getMixedUpperBound(), forallOp.getMixedStep(),
1772 newOutputTensors, forallOp.getMapping(),
1773 [&](OpBuilder nestedBuilder, Location nestedLoc,
ValueRange bbArgs) {
1774 auto castBlockArgs =
1775 llvm::to_vector(bbArgs.take_back(forallOp->getNumResults()));
1776 for (auto [index, cast] : tensorCastProducers) {
1777 Value &oldTypeBBArg = castBlockArgs[index];
1778 oldTypeBBArg = tensor::CastOp::create(nestedBuilder, nestedLoc,
1779 cast.dstType, oldTypeBBArg);
1783 SmallVector<Value> ivsBlockArgs =
1784 llvm::to_vector(bbArgs.take_front(forallOp.getRank()));
1785 ivsBlockArgs.append(castBlockArgs);
1787 bbArgs.front().getParentBlock(), ivsBlockArgs);
1798 llvm::SmallDenseSet<Value> newIterArgSet(
1799 newForallOp.getRegionIterArgs().begin(),
1800 newForallOp.getRegionIterArgs().end());
1801 auto terminator = newForallOp.getTerminator();
1802 for (
auto &yieldingOp : terminator.getYieldingOps()) {
1803 auto parallelCombiningOp =
1804 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1805 if (!parallelCombiningOp)
1807 for (OpOperand &dest : parallelCombiningOp.getUpdatedDestinations()) {
1808 auto castOp = dest.get().getDefiningOp<tensor::CastOp>();
1809 if (castOp && newIterArgSet.contains(castOp.getSource()))
1810 dest.set(castOp.getSource());
1816 SmallVector<Value> castResults = newForallOp.getResults();
1817 for (
auto &item : tensorCastProducers) {
1818 Value &oldTypeResult = castResults[item.first];
1819 oldTypeResult = tensor::CastOp::create(rewriter, loc, item.second.dstType,
1822 rewriter.
replaceOp(forallOp, castResults);
1829void ForallOp::getCanonicalizationPatterns(RewritePatternSet &results,
1830 MLIRContext *context) {
1831 results.
add<DimOfForallOp, FoldTensorCastOfOutputIntoForallOp,
1832 ForallOpControlOperandsFolder, ForallOpIterArgsFolder,
1833 ForallOpSingleOrZeroIterationDimsFolder,
1834 ForallOpReplaceConstantInductionVar>(context);
1837void ForallOp::getSuccessorRegions(RegionBranchPoint point,
1838 SmallVectorImpl<RegionSuccessor> ®ions) {
1845 regions.push_back(RegionSuccessor(&getRegion()));
1848 regions.push_back(RegionSuccessor(getOperation()));
1853 regions.push_back(RegionSuccessor(getOperation()));
1862void InParallelOp::build(OpBuilder &
b, OperationState &
result) {
1863 OpBuilder::InsertionGuard g(
b);
1864 Region *bodyRegion =
result.addRegion();
1865 b.createBlock(bodyRegion);
1868LogicalResult InParallelOp::verify() {
1869 scf::ForallOp forallOp =
1870 dyn_cast<scf::ForallOp>(getOperation()->getParentOp());
1872 return this->emitOpError(
"expected forall op parent");
1874 for (Operation &op : getRegion().front().getOperations()) {
1875 auto parallelCombiningOp = dyn_cast<ParallelCombiningOpInterface>(&op);
1876 if (!parallelCombiningOp) {
1877 return this->emitOpError(
"expected only ParallelCombiningOpInterface")
1882 MutableOperandRange dests = parallelCombiningOp.getUpdatedDestinations();
1883 ArrayRef<BlockArgument> regionOutArgs = forallOp.getRegionOutArgs();
1884 for (OpOperand &dest : dests) {
1885 if (!llvm::is_contained(regionOutArgs, dest.get()))
1886 return op.emitOpError(
"may only insert into an output block argument");
1893void InParallelOp::print(OpAsmPrinter &p) {
1899 getOperation()->getDiscardableAttrDictionary().getValue());
1902ParseResult InParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
1905 SmallVector<OpAsmParser::Argument, 8> regionOperands;
1906 std::unique_ptr<Region> region = std::make_unique<Region>();
1910 if (region->empty())
1911 OpBuilder(builder.
getContext()).createBlock(region.get());
1912 result.addRegion(std::move(region));
1920OpResult InParallelOp::getParentResult(int64_t idx) {
1921 return getOperation()->getParentOp()->getResult(idx);
1924SmallVector<BlockArgument> InParallelOp::getDests() {
1925 SmallVector<BlockArgument> updatedDests;
1926 for (Operation &yieldingOp : getYieldingOps()) {
1927 auto parallelCombiningOp =
1928 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1929 if (!parallelCombiningOp)
1931 for (OpOperand &updatedOperand :
1932 parallelCombiningOp.getUpdatedDestinations())
1933 updatedDests.push_back(cast<BlockArgument>(updatedOperand.get()));
1935 return updatedDests;
1938llvm::iterator_range<Block::iterator> InParallelOp::getYieldingOps() {
1939 return getRegion().front().getOperations();
1947 assert(a &&
"expected non-empty operation");
1948 assert(
b &&
"expected non-empty operation");
1953 if (ifOp->isProperAncestor(
b))
1956 return static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*a)) !=
1957 static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*
b));
1959 ifOp = ifOp->getParentOfType<IfOp>();
1967IfOp::inferReturnTypes(
MLIRContext *ctx, std::optional<Location> loc,
1968 IfOp::Adaptor adaptor,
1970 if (adaptor.getRegions().empty())
1972 Region *r = &adaptor.getThenRegion();
1978 auto yieldOp = llvm::dyn_cast<YieldOp>(
b.back());
1981 TypeRange types = yieldOp.getOperandTypes();
1982 llvm::append_range(inferredReturnTypes, types);
1988 return build(builder,
result, resultTypes, cond,
false,
1992void IfOp::build(OpBuilder &builder, OperationState &
result,
1993 TypeRange resultTypes, Value cond,
bool addThenBlock,
1994 bool addElseBlock) {
1995 assert((!addElseBlock || addThenBlock) &&
1996 "must not create else block w/o then block");
1997 result.addTypes(resultTypes);
1998 result.addOperands(cond);
2001 OpBuilder::InsertionGuard guard(builder);
2002 Region *thenRegion =
result.addRegion();
2005 Region *elseRegion =
result.addRegion();
2010void IfOp::build(OpBuilder &builder, OperationState &
result, Value cond,
2011 bool withElseRegion) {
2015void IfOp::build(OpBuilder &builder, OperationState &
result,
2016 TypeRange resultTypes, Value cond,
bool withElseRegion) {
2017 result.addTypes(resultTypes);
2018 result.addOperands(cond);
2021 OpBuilder::InsertionGuard guard(builder);
2022 Region *thenRegion =
result.addRegion();
2024 if (resultTypes.empty())
2025 IfOp::ensureTerminator(*thenRegion, builder,
result.location);
2028 Region *elseRegion =
result.addRegion();
2029 if (withElseRegion) {
2031 if (resultTypes.empty())
2032 IfOp::ensureTerminator(*elseRegion, builder,
result.location);
2036void IfOp::build(OpBuilder &builder, OperationState &
result, Value cond,
2038 function_ref<
void(OpBuilder &, Location)> elseBuilder) {
2039 assert(thenBuilder &&
"the builder callback for 'then' must be present");
2040 result.addOperands(cond);
2043 OpBuilder::InsertionGuard guard(builder);
2044 Region *thenRegion =
result.addRegion();
2046 thenBuilder(builder,
result.location);
2049 Region *elseRegion =
result.addRegion();
2052 elseBuilder(builder,
result.location);
2056 SmallVector<Type> inferredReturnTypes;
2058 auto attrDict = DictionaryAttr::get(ctx,
result.attributes);
2059 if (succeeded(inferReturnTypes(ctx, std::nullopt,
result.operands, attrDict,
2060 PropertyRef{},
result.regions,
2061 inferredReturnTypes))) {
2062 result.addTypes(inferredReturnTypes);
2066LogicalResult IfOp::verify() {
2067 if (getNumResults() != 0 && getElseRegion().empty())
2068 return emitOpError(
"must have an else block if defining values");
2072ParseResult IfOp::parse(OpAsmParser &parser, OperationState &
result) {
2074 result.regions.reserve(2);
2075 Region *thenRegion =
result.addRegion();
2076 Region *elseRegion =
result.addRegion();
2079 OpAsmParser::UnresolvedOperand cond;
2105void IfOp::print(OpAsmPrinter &p) {
2106 bool printBlockTerminators =
false;
2108 p <<
" " << getCondition();
2109 if (!getResults().empty()) {
2110 p <<
" -> (" << getResultTypes() <<
")";
2112 printBlockTerminators =
true;
2117 printBlockTerminators);
2120 auto &elseRegion = getElseRegion();
2121 if (!elseRegion.
empty()) {
2125 printBlockTerminators);
2131void IfOp::getSuccessorRegions(RegionBranchPoint point,
2132 SmallVectorImpl<RegionSuccessor> ®ions) {
2136 regions.push_back(RegionSuccessor(getOperation()));
2140 regions.push_back(RegionSuccessor(&getThenRegion()));
2143 Region *elseRegion = &this->getElseRegion();
2144 if (elseRegion->
empty())
2145 regions.push_back(RegionSuccessor(getOperation()));
2147 regions.push_back(RegionSuccessor(elseRegion));
2150ValueRange IfOp::getSuccessorInputs(RegionSuccessor successor) {
2155void IfOp::getEntrySuccessorRegions(ArrayRef<Attribute> operands,
2156 SmallVectorImpl<RegionSuccessor> ®ions) {
2157 FoldAdaptor adaptor(operands, *
this);
2158 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
2159 if (!boolAttr || boolAttr.getValue())
2160 regions.emplace_back(&getThenRegion());
2163 if (!boolAttr || !boolAttr.getValue()) {
2164 if (!getElseRegion().empty())
2165 regions.emplace_back(&getElseRegion());
2167 regions.emplace_back(RegionSuccessor(getOperation()));
2171LogicalResult IfOp::fold(FoldAdaptor adaptor,
2172 SmallVectorImpl<OpFoldResult> &results) {
2174 if (getElseRegion().empty())
2177 arith::XOrIOp xorStmt = getCondition().getDefiningOp<arith::XOrIOp>();
2184 getConditionMutable().assign(xorStmt.getLhs());
2185 Block *thenBlock = &getThenRegion().front();
2188 getThenRegion().getBlocks().splice(getThenRegion().getBlocks().begin(),
2189 getElseRegion().getBlocks());
2190 getElseRegion().getBlocks().splice(getElseRegion().getBlocks().begin(),
2191 getThenRegion().getBlocks(), thenBlock);
2195void IfOp::getRegionInvocationBounds(
2196 ArrayRef<Attribute> operands,
2197 SmallVectorImpl<InvocationBounds> &invocationBounds) {
2198 if (
auto cond = llvm::dyn_cast_or_null<BoolAttr>(operands[0])) {
2201 invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
2202 invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
2205 invocationBounds.assign(2, {0, 1});
2212struct ConvertTrivialIfToSelect :
public OpRewritePattern<IfOp> {
2213 using OpRewritePattern<IfOp>::OpRewritePattern;
2215 LogicalResult matchAndRewrite(IfOp op,
2216 PatternRewriter &rewriter)
const override {
2217 if (op->getNumResults() == 0)
2220 auto cond = op.getCondition();
2221 auto thenYieldArgs = op.thenYield().getOperands();
2222 auto elseYieldArgs = op.elseYield().getOperands();
2224 SmallVector<Type> nonHoistable;
2225 for (
auto [trueVal, falseVal] : llvm::zip(thenYieldArgs, elseYieldArgs)) {
2226 if (&op.getThenRegion() == trueVal.getParentRegion() ||
2227 &op.getElseRegion() == falseVal.getParentRegion())
2228 nonHoistable.push_back(trueVal.getType());
2232 if (nonHoistable.size() == op->getNumResults())
2235 IfOp
replacement = IfOp::create(rewriter, op.getLoc(), nonHoistable, cond,
2239 replacement.getThenRegion().takeBody(op.getThenRegion());
2240 replacement.getElseRegion().takeBody(op.getElseRegion());
2242 SmallVector<Value> results(op->getNumResults());
2243 assert(thenYieldArgs.size() == results.size());
2244 assert(elseYieldArgs.size() == results.size());
2246 SmallVector<Value> trueYields;
2247 SmallVector<Value> falseYields;
2249 for (
const auto &it :
2250 llvm::enumerate(llvm::zip(thenYieldArgs, elseYieldArgs))) {
2251 Value trueVal = std::get<0>(it.value());
2252 Value falseVal = std::get<1>(it.value());
2255 results[it.index()] =
replacement.getResult(trueYields.size());
2256 trueYields.push_back(trueVal);
2257 falseYields.push_back(falseVal);
2258 }
else if (trueVal == falseVal)
2259 results[it.index()] = trueVal;
2261 results[it.index()] = arith::SelectOp::create(rewriter, op.getLoc(),
2262 cond, trueVal, falseVal);
2289struct ConditionPropagation :
public OpRewritePattern<IfOp> {
2290 using OpRewritePattern<IfOp>::OpRewritePattern;
2293 enum class Parent { Then, Else,
None };
2298 static Parent getParentType(Region *toCheck, IfOp op,
2300 Region *endRegion) {
2301 SmallVector<Region *> seen;
2302 while (toCheck != endRegion) {
2303 auto found = cache.find(toCheck);
2304 if (found != cache.end())
2305 return found->second;
2306 seen.push_back(toCheck);
2307 if (&op.getThenRegion() == toCheck) {
2308 for (Region *region : seen)
2309 cache[region] = Parent::Then;
2310 return Parent::Then;
2312 if (&op.getElseRegion() == toCheck) {
2313 for (Region *region : seen)
2314 cache[region] = Parent::Else;
2315 return Parent::Else;
2320 for (Region *region : seen)
2321 cache[region] = Parent::None;
2322 return Parent::None;
2325 LogicalResult matchAndRewrite(IfOp op,
2326 PatternRewriter &rewriter)
const override {
2332 bool changed =
false;
2337 Value constantTrue =
nullptr;
2338 Value constantFalse =
nullptr;
2341 for (OpOperand &use :
2342 llvm::make_early_inc_range(op.getCondition().getUses())) {
2345 case Parent::Then: {
2349 constantTrue = arith::ConstantOp::create(
2353 [&]() { use.set(constantTrue); });
2356 case Parent::Else: {
2360 constantFalse = arith::ConstantOp::create(
2364 [&]() { use.set(constantFalse); });
2412struct ReplaceIfYieldWithConditionOrValue :
public OpRewritePattern<IfOp> {
2413 using OpRewritePattern<IfOp>::OpRewritePattern;
2415 LogicalResult matchAndRewrite(IfOp op,
2416 PatternRewriter &rewriter)
const override {
2418 if (op.getNumResults() == 0)
2422 cast<scf::YieldOp>(op.getThenRegion().back().getTerminator());
2424 cast<scf::YieldOp>(op.getElseRegion().back().getTerminator());
2427 op.getOperation()->getIterator());
2428 bool changed =
false;
2430 for (
auto [trueResult, falseResult, opResult] :
2431 llvm::zip(trueYield.getResults(), falseYield.getResults(),
2433 if (trueResult == falseResult) {
2434 if (!opResult.use_empty()) {
2435 opResult.replaceAllUsesWith(trueResult);
2441 BoolAttr trueYield, falseYield;
2446 bool trueVal = trueYield.
getValue();
2447 bool falseVal = falseYield.
getValue();
2448 if (!trueVal && falseVal) {
2449 if (!opResult.use_empty()) {
2450 Dialect *constDialect = trueResult.getDefiningOp()->getDialect();
2451 Value notCond = arith::XOrIOp::create(
2452 rewriter, op.getLoc(), op.getCondition(),
2458 opResult.replaceAllUsesWith(notCond);
2462 if (trueVal && !falseVal) {
2463 if (!opResult.use_empty()) {
2464 opResult.replaceAllUsesWith(op.getCondition());
2494struct CombineIfs :
public OpRewritePattern<IfOp> {
2495 using OpRewritePattern<IfOp>::OpRewritePattern;
2497 LogicalResult matchAndRewrite(IfOp nextIf,
2498 PatternRewriter &rewriter)
const override {
2499 Block *parent = nextIf->getBlock();
2500 if (nextIf == &parent->
front())
2503 auto prevIf = dyn_cast<IfOp>(nextIf->getPrevNode());
2511 Block *nextThen =
nullptr;
2512 Block *nextElse =
nullptr;
2513 if (nextIf.getCondition() == prevIf.getCondition()) {
2514 nextThen = nextIf.thenBlock();
2515 if (!nextIf.getElseRegion().empty())
2516 nextElse = nextIf.elseBlock();
2518 if (arith::XOrIOp notv =
2519 nextIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2520 if (notv.getLhs() == prevIf.getCondition() &&
2522 nextElse = nextIf.thenBlock();
2523 if (!nextIf.getElseRegion().empty())
2524 nextThen = nextIf.elseBlock();
2527 if (arith::XOrIOp notv =
2528 prevIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2529 if (notv.getLhs() == nextIf.getCondition() &&
2531 nextElse = nextIf.thenBlock();
2532 if (!nextIf.getElseRegion().empty())
2533 nextThen = nextIf.elseBlock();
2537 if (!nextThen && !nextElse)
2540 SmallVector<Value> prevElseYielded;
2541 if (!prevIf.getElseRegion().empty())
2542 prevElseYielded = prevIf.elseYield().getOperands();
2545 for (
auto it : llvm::zip(prevIf.getResults(),
2546 prevIf.thenYield().getOperands(), prevElseYielded))
2547 for (OpOperand &use :
2548 llvm::make_early_inc_range(std::get<0>(it).getUses())) {
2552 use.
set(std::get<1>(it));
2557 use.
set(std::get<2>(it));
2562 SmallVector<Type> mergedTypes(prevIf.getResultTypes());
2563 llvm::append_range(mergedTypes, nextIf.getResultTypes());
2565 IfOp combinedIf = IfOp::create(rewriter, nextIf.getLoc(), mergedTypes,
2566 prevIf.getCondition(),
false);
2567 rewriter.
eraseBlock(&combinedIf.getThenRegion().back());
2570 combinedIf.getThenRegion(),
2571 combinedIf.getThenRegion().begin());
2574 YieldOp thenYield = combinedIf.thenYield();
2575 YieldOp thenYield2 = cast<YieldOp>(nextThen->
getTerminator());
2576 rewriter.
mergeBlocks(nextThen, combinedIf.thenBlock());
2579 SmallVector<Value> mergedYields(thenYield.getOperands());
2580 llvm::append_range(mergedYields, thenYield2.getOperands());
2581 YieldOp::create(rewriter, thenYield2.getLoc(), mergedYields);
2587 combinedIf.getElseRegion(),
2588 combinedIf.getElseRegion().begin());
2591 if (combinedIf.getElseRegion().empty()) {
2593 combinedIf.getElseRegion(),
2594 combinedIf.getElseRegion().
begin());
2596 YieldOp elseYield = combinedIf.elseYield();
2597 YieldOp elseYield2 = cast<YieldOp>(nextElse->
getTerminator());
2598 rewriter.
mergeBlocks(nextElse, combinedIf.elseBlock());
2602 SmallVector<Value> mergedElseYields(elseYield.getOperands());
2603 llvm::append_range(mergedElseYields, elseYield2.getOperands());
2605 YieldOp::create(rewriter, elseYield2.getLoc(), mergedElseYields);
2611 SmallVector<Value> prevValues;
2612 SmallVector<Value> nextValues;
2613 for (
const auto &pair : llvm::enumerate(combinedIf.getResults())) {
2614 if (pair.index() < prevIf.getNumResults())
2615 prevValues.push_back(pair.value());
2617 nextValues.push_back(pair.value());
2626struct RemoveEmptyElseBranch :
public OpRewritePattern<IfOp> {
2627 using OpRewritePattern<IfOp>::OpRewritePattern;
2629 LogicalResult matchAndRewrite(IfOp ifOp,
2630 PatternRewriter &rewriter)
const override {
2632 if (ifOp.getNumResults())
2634 Block *elseBlock = ifOp.elseBlock();
2635 if (!elseBlock || !llvm::hasSingleElement(*elseBlock))
2639 newIfOp.getThenRegion().begin());
2661struct CombineNestedIfs :
public OpRewritePattern<IfOp> {
2662 using OpRewritePattern<IfOp>::OpRewritePattern;
2664 LogicalResult matchAndRewrite(IfOp op,
2665 PatternRewriter &rewriter)
const override {
2666 auto nestedOps = op.thenBlock()->without_terminator();
2668 if (!llvm::hasSingleElement(nestedOps))
2672 if (op.elseBlock() && !llvm::hasSingleElement(*op.elseBlock()))
2675 auto nestedIf = dyn_cast<IfOp>(*nestedOps.begin());
2679 if (nestedIf.elseBlock() && !llvm::hasSingleElement(*nestedIf.elseBlock()))
2682 SmallVector<Value> thenYield(op.thenYield().getOperands());
2683 SmallVector<Value> elseYield;
2685 llvm::append_range(elseYield, op.elseYield().getOperands());
2689 SmallVector<unsigned> elseYieldsToUpgradeToSelect;
2698 for (
const auto &tup : llvm::enumerate(thenYield)) {
2699 if (tup.value().getDefiningOp() == nestedIf) {
2700 auto nestedIdx = llvm::cast<OpResult>(tup.value()).getResultNumber();
2701 if (nestedIf.elseYield().getOperand(nestedIdx) !=
2702 elseYield[tup.index()]) {
2707 thenYield[tup.index()] = nestedIf.thenYield().getOperand(nestedIdx);
2720 if (tup.value().getParentRegion() == &op.getThenRegion()) {
2723 elseYieldsToUpgradeToSelect.push_back(tup.index());
2726 Location loc = op.getLoc();
2727 Value newCondition = arith::AndIOp::create(rewriter, loc, op.getCondition(),
2728 nestedIf.getCondition());
2729 auto newIf = IfOp::create(rewriter, loc, op.getResultTypes(), newCondition);
2732 SmallVector<Value> results;
2733 llvm::append_range(results, newIf.getResults());
2736 for (
auto idx : elseYieldsToUpgradeToSelect)
2738 arith::SelectOp::create(rewriter, op.getLoc(), op.getCondition(),
2739 thenYield[idx], elseYield[idx]);
2741 rewriter.
mergeBlocks(nestedIf.thenBlock(), newIfBlock);
2744 if (!elseYield.empty()) {
2747 YieldOp::create(rewriter, loc, elseYield);
2756void IfOp::getCanonicalizationPatterns(RewritePatternSet &results,
2757 MLIRContext *context) {
2758 results.
add<CombineIfs, CombineNestedIfs, ConditionPropagation,
2759 ConvertTrivialIfToSelect, RemoveEmptyElseBranch,
2760 ReplaceIfYieldWithConditionOrValue>(context);
2762 results, IfOp::getOperationName());
2764 IfOp::getOperationName());
2767Block *IfOp::thenBlock() {
return &getThenRegion().back(); }
2768YieldOp IfOp::thenYield() {
return cast<YieldOp>(&thenBlock()->back()); }
2769Block *IfOp::elseBlock() {
2770 Region &r = getElseRegion();
2775YieldOp IfOp::elseYield() {
return cast<YieldOp>(&elseBlock()->back()); }
2781void ParallelOp::build(
2787 result.addOperands(lowerBounds);
2788 result.addOperands(upperBounds);
2789 result.addOperands(steps);
2790 result.addOperands(initVals);
2792 result.addAttribute(getUnsignedCmpAttrName(
result.name),
2795 ParallelOp::getOperandSegmentSizeAttr(),
2797 static_cast<int32_t>(upperBounds.size()),
2798 static_cast<int32_t>(steps.size()),
2799 static_cast<int32_t>(initVals.size())}));
2802 OpBuilder::InsertionGuard guard(builder);
2803 unsigned numIVs = steps.size();
2804 SmallVector<Type, 8> argTypes(numIVs, builder.
getIndexType());
2805 SmallVector<Location, 8> argLocs(numIVs,
result.location);
2806 Region *bodyRegion =
result.addRegion();
2809 if (bodyBuilderFn) {
2811 bodyBuilderFn(builder,
result.location,
2816 if (initVals.empty())
2817 ParallelOp::ensureTerminator(*bodyRegion, builder,
result.location);
2820void ParallelOp::build(
2828 auto wrappedBuilderFn = [&bodyBuilderFn](OpBuilder &nestedBuilder,
2831 bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
2835 wrapper = wrappedBuilderFn;
2837 build(builder,
result, lowerBounds, upperBounds, steps,
ValueRange(), wrapper,
2841LogicalResult ParallelOp::verify() {
2846 if (stepValues.empty())
2848 "needs at least one tuple element for lowerBound, upperBound and step");
2851 for (Value stepValue : stepValues)
2854 return emitOpError(
"constant step operand must be positive");
2858 Block *body = getBody();
2860 return emitOpError() <<
"expects the same number of induction variables: "
2862 <<
" as bound and step values: " << stepValues.size();
2864 if (!arg.getType().isIndex())
2866 "expects arguments for the induction variable to be of index type");
2870 *
this, getRegion(),
"expects body to terminate with 'scf.reduce'");
2875 auto resultsSize = getResults().size();
2876 auto reductionsSize = reduceOp.getReductions().size();
2877 auto initValsSize = getInitVals().size();
2878 if (resultsSize != reductionsSize)
2879 return emitOpError() <<
"expects number of results: " << resultsSize
2880 <<
" to be the same as number of reductions: "
2882 if (resultsSize != initValsSize)
2883 return emitOpError() <<
"expects number of results: " << resultsSize
2884 <<
" to be the same as number of initial values: "
2886 if (reduceOp.getNumOperands() != initValsSize)
2891 for (int64_t i = 0; i < static_cast<int64_t>(reductionsSize); ++i) {
2892 auto resultType = getOperation()->getResult(i).getType();
2893 auto reductionOperandType = reduceOp.getOperands()[i].getType();
2894 if (resultType != reductionOperandType)
2895 return reduceOp.emitOpError()
2896 <<
"expects type of " << i
2897 <<
"-th reduction operand: " << reductionOperandType
2898 <<
" to be the same as the " << i
2899 <<
"-th result type: " << resultType;
2904ParseResult ParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
2907 result.addAttribute(getUnsignedCmpAttrName(
result.name),
2911 SmallVector<OpAsmParser::Argument, 4> ivs;
2916 SmallVector<OpAsmParser::UnresolvedOperand, 4> lower;
2923 SmallVector<OpAsmParser::UnresolvedOperand, 4> upper;
2931 SmallVector<OpAsmParser::UnresolvedOperand, 4> steps;
2939 SmallVector<OpAsmParser::UnresolvedOperand, 4> initVals;
2950 Region *body =
result.addRegion();
2951 for (
auto &iv : ivs)
2958 ParallelOp::getOperandSegmentSizeAttr(),
2960 static_cast<int32_t>(upper.size()),
2961 static_cast<int32_t>(steps.size()),
2962 static_cast<int32_t>(initVals.size())}));
2971 ParallelOp::ensureTerminator(*body, builder,
result.location);
2975void ParallelOp::print(OpAsmPrinter &p) {
2976 if (getUnsignedCmp())
2978 p <<
" (" << getBody()->getArguments() <<
") = (" <<
getLowerBound()
2979 <<
") to (" <<
getUpperBound() <<
") step (" << getStep() <<
")";
2980 if (!getInitVals().empty())
2981 p <<
" init (" << getInitVals() <<
")";
2988SmallVector<Region *> ParallelOp::getLoopRegions() {
return {&getRegion()}; }
2990std::optional<SmallVector<Value>> ParallelOp::getLoopInductionVars() {
2991 return SmallVector<Value>{getBody()->getArguments()};
2994std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopLowerBounds() {
2998std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopUpperBounds() {
3002std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopSteps() {
3007 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
3009 return ParallelOp();
3010 assert(ivArg.getOwner() &&
"unlinked block argument");
3011 auto *containingOp = ivArg.getOwner()->getParentOp();
3012 return dyn_cast<ParallelOp>(containingOp);
3017struct ParallelOpSingleOrZeroIterationDimsFolder
3021 LogicalResult matchAndRewrite(ParallelOp op,
3028 for (
auto [lb,
ub, step, iv] :
3029 llvm::zip(op.getLowerBound(), op.getUpperBound(), op.getStep(),
3030 op.getInductionVars())) {
3031 auto numIterations =
3033 if (numIterations.has_value()) {
3035 if (*numIterations == 0) {
3036 rewriter.
replaceOp(op, op.getInitVals());
3041 if (*numIterations == 1) {
3046 newLowerBounds.push_back(lb);
3047 newUpperBounds.push_back(ub);
3048 newSteps.push_back(step);
3051 if (newLowerBounds.size() == op.getLowerBound().size())
3054 if (newLowerBounds.empty()) {
3057 SmallVector<Value> results;
3058 results.reserve(op.getInitVals().size());
3059 for (
auto &bodyOp : op.getBody()->without_terminator())
3060 rewriter.
clone(bodyOp, mapping);
3061 auto reduceOp = cast<ReduceOp>(op.getBody()->getTerminator());
3062 for (int64_t i = 0, e = reduceOp.getReductions().size(); i < e; ++i) {
3063 Block &reduceBlock = reduceOp.getReductions()[i].front();
3064 auto initValIndex = results.size();
3065 mapping.
map(reduceBlock.
getArgument(0), op.getInitVals()[initValIndex]);
3069 rewriter.
clone(reduceBodyOp, mapping);
3072 cast<ReduceReturnOp>(reduceBlock.
getTerminator()).getResult());
3073 results.push_back(
result);
3081 ParallelOp::create(rewriter, op.getLoc(), newLowerBounds,
3082 newUpperBounds, newSteps, op.getInitVals(),
nullptr);
3088 newOp.getRegion().begin(), mapping);
3089 rewriter.
replaceOp(op, newOp.getResults());
3094struct MergeNestedParallelLoops :
public OpRewritePattern<ParallelOp> {
3095 using OpRewritePattern<ParallelOp>::OpRewritePattern;
3097 LogicalResult matchAndRewrite(ParallelOp op,
3098 PatternRewriter &rewriter)
const override {
3099 Block &outerBody = *op.getBody();
3103 auto innerOp = dyn_cast<ParallelOp>(outerBody.
front());
3108 if (llvm::is_contained(innerOp.getLowerBound(), val) ||
3109 llvm::is_contained(innerOp.getUpperBound(), val) ||
3110 llvm::is_contained(innerOp.getStep(), val))
3114 if (!op.getInitVals().empty() || !innerOp.getInitVals().empty())
3117 auto bodyBuilder = [&](OpBuilder &builder, Location ,
3119 Block &innerBody = *innerOp.getBody();
3120 assert(iterVals.size() ==
3128 builder.
clone(op, mapping);
3131 auto concatValues = [](
const auto &first,
const auto &second) {
3132 SmallVector<Value> ret;
3133 ret.reserve(first.size() + second.size());
3134 ret.assign(first.begin(), first.end());
3135 ret.append(second.begin(), second.end());
3139 auto newLowerBounds =
3140 concatValues(op.getLowerBound(), innerOp.getLowerBound());
3141 auto newUpperBounds =
3142 concatValues(op.getUpperBound(), innerOp.getUpperBound());
3143 auto newSteps = concatValues(op.getStep(), innerOp.getStep());
3154void ParallelOp::getCanonicalizationPatterns(RewritePatternSet &results,
3155 MLIRContext *context) {
3157 .
add<ParallelOpSingleOrZeroIterationDimsFolder, MergeNestedParallelLoops>(
3166void ParallelOp::getSuccessorRegions(
3167 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
3171 regions.push_back(RegionSuccessor(&getRegion()));
3172 regions.push_back(RegionSuccessor(getOperation()));
3179void ReduceOp::build(OpBuilder &builder, OperationState &
result) {}
3181void ReduceOp::build(OpBuilder &builder, OperationState &
result,
3183 result.addOperands(operands);
3184 for (Value v : operands) {
3185 OpBuilder::InsertionGuard guard(builder);
3186 Region *bodyRegion =
result.addRegion();
3188 ArrayRef<Type>{v.getType(), v.getType()},
3193LogicalResult ReduceOp::verifyRegions() {
3194 if (getReductions().size() != getOperands().size())
3195 return emitOpError() <<
"expects number of reduction regions: "
3196 << getReductions().size()
3197 <<
" to be the same as number of reduction operands: "
3198 << getOperands().size();
3201 for (int64_t i = 0, e = getReductions().size(); i < e; ++i) {
3202 auto type = getOperands()[i].getType();
3203 Block &block = getReductions()[i].front();
3205 return emitOpError() << i <<
"-th reduction has an empty body";
3207 llvm::any_of(block.
getArguments(), [&](
const BlockArgument &arg) {
3208 return arg.getType() != type;
3210 return emitOpError() <<
"expected two block arguments with type " << type
3211 <<
" in the " << i <<
"-th reduction region";
3215 return emitOpError(
"reduction bodies must be terminated with an "
3216 "'scf.reduce.return' op");
3223ReduceOp::getMutableSuccessorOperands(RegionSuccessor point) {
3225 return MutableOperandRange(getOperation(), 0, 0);
3232LogicalResult ReduceReturnOp::verify() {
3235 Block *reductionBody = getOperation()->getBlock();
3237 assert(isa<ReduceOp>(reductionBody->
getParentOp()) &&
"expected scf.reduce");
3239 if (expectedResultType != getResult().
getType())
3240 return emitOpError() <<
"must have type " << expectedResultType
3241 <<
" (the type of the reduction inputs)";
3249void WhileOp::build(::mlir::OpBuilder &odsBuilder,
3250 ::mlir::OperationState &odsState,
TypeRange resultTypes,
3251 ValueRange inits, BodyBuilderFn beforeBuilder,
3252 BodyBuilderFn afterBuilder) {
3256 OpBuilder::InsertionGuard guard(odsBuilder);
3259 SmallVector<Location, 4> beforeArgLocs;
3260 beforeArgLocs.reserve(inits.size());
3261 for (Value operand : inits) {
3262 beforeArgLocs.push_back(operand.getLoc());
3265 Region *beforeRegion = odsState.
addRegion();
3267 inits.getTypes(), beforeArgLocs);
3272 SmallVector<Location, 4> afterArgLocs(resultTypes.size(), odsState.
location);
3274 Region *afterRegion = odsState.
addRegion();
3276 resultTypes, afterArgLocs);
3282ConditionOp WhileOp::getConditionOp() {
3283 return cast<ConditionOp>(getBeforeBody()->getTerminator());
3286YieldOp WhileOp::getYieldOp() {
3287 return cast<YieldOp>(getAfterBody()->getTerminator());
3290std::optional<MutableArrayRef<OpOperand>> WhileOp::getYieldedValuesMutable() {
3291 return getYieldOp().getResultsMutable();
3295 return getBeforeBody()->getArguments();
3299 return getAfterBody()->getArguments();
3303 return getBeforeArguments();
3306OperandRange WhileOp::getEntrySuccessorOperands(RegionSuccessor successor) {
3308 "WhileOp is expected to branch only to the first region");
3312void WhileOp::getSuccessorRegions(RegionBranchPoint point,
3313 SmallVectorImpl<RegionSuccessor> ®ions) {
3316 regions.emplace_back(&getBefore());
3320 assert(llvm::is_contained(
3321 {&getAfter(), &getBefore()},
3323 "there are only two regions in a WhileOp");
3327 regions.emplace_back(&getBefore());
3331 regions.push_back(RegionSuccessor(getOperation()));
3332 regions.emplace_back(&getAfter());
3335ValueRange WhileOp::getSuccessorInputs(RegionSuccessor successor) {
3337 return getOperation()->getResults();
3338 if (successor == &getBefore())
3339 return getBefore().getArguments();
3340 if (successor == &getAfter())
3341 return getAfter().getArguments();
3342 llvm_unreachable(
"invalid region successor");
3345SmallVector<Region *> WhileOp::getLoopRegions() {
3346 return {&getBefore(), &getAfter()};
3356ParseResult scf::WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
3357 SmallVector<OpAsmParser::Argument, 4> regionArgs;
3358 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
3359 Region *before =
result.addRegion();
3360 Region *after =
result.addRegion();
3362 OptionalParseResult listResult =
3367 FunctionType functionType;
3372 result.addTypes(functionType.getResults());
3374 if (functionType.getNumInputs() != operands.size()) {
3376 <<
"expected as many input types as operands " <<
"(expected "
3377 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
3387 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
3388 regionArgs[i].type = functionType.getInput(i);
3390 return failure(parser.
parseRegion(*before, regionArgs) ||
3396void scf::WhileOp::print(OpAsmPrinter &p) {
3405 (*this)->getDiscardableAttrDictionary().getValue());
3408LogicalResult scf::WhileOp::verify() {
3411 "expects the 'before' region to terminate with 'scf.condition'");
3412 if (!beforeTerminator)
3417 "expects the 'after' region to terminate with 'scf.yield'");
3418 return success(afterTerminator !=
nullptr);
3455struct WhileMoveIfDown :
public OpRewritePattern<scf::WhileOp> {
3456 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3458 LogicalResult matchAndRewrite(scf::WhileOp op,
3459 PatternRewriter &rewriter)
const override {
3460 auto conditionOp = op.getConditionOp();
3468 auto ifOp = dyn_cast_or_null<scf::IfOp>(conditionOp->getPrevNode());
3474 if (!ifOp || ifOp.getCondition() != conditionOp.getCondition() ||
3475 (ifOp.elseBlock() && !ifOp.elseBlock()->without_terminator().empty()))
3478 assert((ifOp->use_empty() || (llvm::all_equal(ifOp->getUsers()) &&
3479 *ifOp->user_begin() == conditionOp)) &&
3480 "ifOp has unexpected uses");
3482 Location loc = op.getLoc();
3492 for (
auto [idx, arg] : llvm::enumerate(conditionOp.getArgs())) {
3493 auto it = llvm::find(ifOp->getResults(), arg);
3494 if (it == ifOp->getResults().end())
3496 size_t ifOpIdx = it.getIndex();
3498 ifOp.thenYield()->getOperand(ifOpIdx));
3499 unsigned argIdx = idx;
3500 Value elseValue = ifOp.elseYield()->getOperand(ifOpIdx);
3502 conditionOp.getArgsMutable()[argIdx].assign(elseValue);
3509 if (&op.getBefore() == operand->get().getParentRegion())
3510 additionalUsedValuesSet.insert(operand->get());
3514 auto additionalUsedValues = additionalUsedValuesSet.getArrayRef();
3515 auto additionalValueTypes = llvm::map_to_vector(
3516 additionalUsedValues, [](Value val) {
return val.
getType(); });
3517 size_t additionalValueSize = additionalUsedValues.size();
3518 SmallVector<Type> newResultTypes(op.getResultTypes());
3519 newResultTypes.append(additionalValueTypes);
3522 scf::WhileOp::create(rewriter, loc, newResultTypes, op.getInits());
3525 newWhileOp.getBefore().takeBody(op.getBefore());
3526 newWhileOp.getAfter().takeBody(op.getAfter());
3527 newWhileOp.getAfter().addArguments(
3528 additionalValueTypes,
3529 SmallVector<Location>(additionalValueSize, loc));
3533 conditionOp.getArgsMutable().append(additionalUsedValues);
3539 additionalUsedValues,
3540 newWhileOp.getAfterArguments().take_back(additionalValueSize),
3541 [&](OpOperand &use) {
3542 return ifOp.getThenRegion().isAncestor(
3543 use.getOwner()->getParentRegion());
3547 rewriter.
eraseOp(ifOp.thenYield());
3549 newWhileOp.getAfterBody()->begin());
3552 newWhileOp->getResults().drop_back(additionalValueSize));
3576struct WhileConditionTruth :
public OpRewritePattern<WhileOp> {
3577 using OpRewritePattern<WhileOp>::OpRewritePattern;
3579 LogicalResult matchAndRewrite(WhileOp op,
3580 PatternRewriter &rewriter)
const override {
3581 auto term = op.getConditionOp();
3585 Value constantTrue =
nullptr;
3587 bool replaced =
false;
3588 for (
auto yieldedAndBlockArgs :
3589 llvm::zip(term.getArgs(), op.getAfterArguments())) {
3590 if (std::get<0>(yieldedAndBlockArgs) == term.getCondition()) {
3591 if (!std::get<1>(yieldedAndBlockArgs).use_empty()) {
3593 constantTrue = arith::ConstantOp::create(
3594 rewriter, op.getLoc(), term.getCondition().getType(),
3629struct WhileCmpCond :
public OpRewritePattern<scf::WhileOp> {
3630 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3632 LogicalResult matchAndRewrite(scf::WhileOp op,
3633 PatternRewriter &rewriter)
const override {
3634 using namespace scf;
3635 auto cond = op.getConditionOp();
3636 auto cmp = cond.getCondition().getDefiningOp<arith::CmpIOp>();
3639 bool changed =
false;
3640 for (
auto tup : llvm::zip(cond.getArgs(), op.getAfterArguments())) {
3641 for (
size_t opIdx = 0; opIdx < 2; opIdx++) {
3642 if (std::get<0>(tup) != cmp.getOperand(opIdx))
3645 llvm::make_early_inc_range(std::get<1>(tup).getUses())) {
3646 auto cmp2 = dyn_cast<arith::CmpIOp>(u.getOwner());
3650 if (cmp2.getOperand(1 - opIdx) != cmp.getOperand(1 - opIdx))
3653 if (cmp2.getPredicate() == cmp.getPredicate())
3654 samePredicate =
true;
3655 else if (cmp2.getPredicate() ==
3656 arith::invertPredicate(cmp.getPredicate()))
3657 samePredicate =
false;
3673static std::optional<SmallVector<unsigned>> getArgsMapping(
ValueRange args1,
3675 if (args1.size() != args2.size())
3676 return std::nullopt;
3678 SmallVector<unsigned> ret(args1.size());
3679 for (
auto &&[i, arg1] : llvm::enumerate(args1)) {
3680 auto it = llvm::find(args2, arg1);
3681 if (it == args2.end())
3682 return std::nullopt;
3684 ret[std::distance(args2.begin(), it)] =
static_cast<unsigned>(i);
3691 llvm::SmallDenseSet<Value> set;
3692 for (Value arg : args) {
3693 if (!set.insert(arg).second)
3703struct WhileOpAlignBeforeArgs :
public OpRewritePattern<WhileOp> {
3706 LogicalResult matchAndRewrite(WhileOp loop,
3707 PatternRewriter &rewriter)
const override {
3708 auto *oldBefore = loop.getBeforeBody();
3709 ConditionOp oldTerm = loop.getConditionOp();
3710 ValueRange beforeArgs = oldBefore->getArguments();
3712 if (beforeArgs == termArgs)
3715 if (hasDuplicates(termArgs))
3718 auto mapping = getArgsMapping(beforeArgs, termArgs);
3723 OpBuilder::InsertionGuard g(rewriter);
3729 auto *oldAfter = loop.getAfterBody();
3731 SmallVector<Type> newResultTypes(beforeArgs.size());
3732 for (
auto &&[i, j] : llvm::enumerate(*mapping))
3733 newResultTypes[j] = loop.getResult(i).getType();
3735 auto newLoop = WhileOp::create(
3736 rewriter, loop.getLoc(), newResultTypes, loop.getInits(),
3738 auto *newBefore = newLoop.getBeforeBody();
3739 auto *newAfter = newLoop.getAfterBody();
3741 SmallVector<Value> newResults(beforeArgs.size());
3742 SmallVector<Value> newAfterArgs(beforeArgs.size());
3743 for (
auto &&[i, j] : llvm::enumerate(*mapping)) {
3744 newResults[i] = newLoop.getResult(j);
3745 newAfterArgs[i] = newAfter->getArgument(j);
3749 newBefore->getArguments());
3759void WhileOp::getCanonicalizationPatterns(RewritePatternSet &results,
3760 MLIRContext *context) {
3761 results.
add<WhileConditionTruth, WhileCmpCond, WhileOpAlignBeforeArgs,
3762 WhileMoveIfDown>(context);
3764 results, WhileOp::getOperationName());
3766 WhileOp::getOperationName());
3780 Region ®ion = *caseRegions.emplace_back(std::make_unique<Region>());
3783 caseValues.push_back(value);
3792 for (
auto [value, region] : llvm::zip(cases.
asArrayRef(), caseRegions)) {
3794 p <<
"case " << value <<
' ';
3799LogicalResult scf::IndexSwitchOp::verify() {
3800 if (getCases().size() != getCaseRegions().size()) {
3801 return emitOpError(
"has ")
3802 << getCaseRegions().size() <<
" case regions but "
3803 << getCases().size() <<
" case values";
3807 for (int64_t value : getCases())
3808 if (!valueSet.insert(value).second)
3809 return emitOpError(
"has duplicate case value: ") << value;
3810 auto verifyRegion = [&](Region ®ion,
const Twine &name) -> LogicalResult {
3811 auto yield = dyn_cast<YieldOp>(region.
front().
back());
3813 return emitOpError(
"expected region to end with scf.yield, but got ")
3816 if (yield.getNumOperands() != getNumResults()) {
3817 return (emitOpError(
"expected each region to return ")
3818 << getNumResults() <<
" values, but " << name <<
" returns "
3819 << yield.getNumOperands())
3820 .attachNote(yield.getLoc())
3821 <<
"see yield operation here";
3823 for (
auto [idx,
result, operand] :
3824 llvm::enumerate(getResultTypes(), yield.getOperands())) {
3826 return yield.emitOpError() <<
"operand " << idx <<
" is null\n";
3827 if (
result == operand.getType())
3829 return (emitOpError(
"expected result #")
3830 << idx <<
" of each region to be " <<
result)
3831 .attachNote(yield.getLoc())
3832 << name <<
" returns " << operand.getType() <<
" here";
3839 for (
auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
3846unsigned scf::IndexSwitchOp::getNumCases() {
return getCases().size(); }
3848Block &scf::IndexSwitchOp::getDefaultBlock() {
3849 return getDefaultRegion().front();
3852Block &scf::IndexSwitchOp::getCaseBlock(
unsigned idx) {
3853 assert(idx < getNumCases() &&
"case index out-of-bounds");
3854 return getCaseRegions()[idx].front();
3857void IndexSwitchOp::getSuccessorRegions(
3858 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
3861 successors.push_back(RegionSuccessor(getOperation()));
3865 llvm::append_range(successors, getRegions());
3868ValueRange IndexSwitchOp::getSuccessorInputs(RegionSuccessor successor) {
3873void IndexSwitchOp::getEntrySuccessorRegions(
3874 ArrayRef<Attribute> operands,
3875 SmallVectorImpl<RegionSuccessor> &successors) {
3876 FoldAdaptor adaptor(operands, *
this);
3879 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
3881 llvm::append_range(successors, getRegions());
3887 for (
auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
3888 if (caseValue == arg.getInt()) {
3889 successors.emplace_back(&caseRegion);
3893 successors.emplace_back(&getDefaultRegion());
3896void IndexSwitchOp::getRegionInvocationBounds(
3897 ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
3898 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
3899 if (!operandValue) {
3901 bounds.append(getNumRegions(), InvocationBounds(0, 1));
3905 unsigned liveIndex = getNumRegions() - 1;
3906 const auto *it = llvm::find(getCases(), operandValue.getInt());
3907 if (it != getCases().end())
3908 liveIndex = std::distance(getCases().begin(), it);
3909 for (
unsigned i = 0, e = getNumRegions(); i < e; ++i)
3910 bounds.emplace_back(0, i == liveIndex);
3913void IndexSwitchOp::getCanonicalizationPatterns(RewritePatternSet &results,
3914 MLIRContext *context) {
3916 results, IndexSwitchOp::getOperationName());
3918 results, IndexSwitchOp::getOperationName());
3925#define GET_OP_CLASSES
3926#include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
static std::optional< int64_t > getUpperBound(Value iv)
Gets the constant upper bound on an affine.for iv.
static std::optional< int64_t > getLowerBound(Value iv)
Gets the constant lower bound on an iv.
static LogicalResult verifyRegion(emitc::SwitchOp op, Region ®ion, const Twine &name)
static ParseResult parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse the case regions and values.
static void printSwitchCases(OpAsmPrinter &p, Operation *op, DenseI64ArrayAttr cases, RegionRange caseRegions)
Print the case regions and values.
static void printInitializationList(OpAsmPrinter &p, Block::BlockArgListType blocksArgs, ValueRange initializers, StringRef prefix="")
Prints the initialization list in the form of <prefix>(inner = outer, inner2 = outer2,...
static TerminatorTy verifyAndGetTerminator(Operation *op, Region ®ion, StringRef errorMessage)
Verifies that the first block of the given region is terminated by a TerminatorTy.
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
@ Paren
Parens surrounding zero or more operands.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
virtual ParseResult parseArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an arrow followed by a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
void printOptionalArrowTypeList(TypeRange &&types)
Print an optional arrow followed by a type list.
virtual void printNewline()
Print a newline and indent the printer to the start of the current operation/attribute/type.
This class represents an argument of a Block.
unsigned getArgNumber() const
Returns the number of this argument.
Block represents an ordered list of Operations.
MutableArrayRef< BlockArgument > BlockArgListType
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
iterator_range< args_iterator > addArguments(TypeRange types, ArrayRef< Location > locs)
Add one argument to the argument list for each type specified in the list.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
BlockArgListType getArguments()
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
bool getValue() const
Return the boolean value of this attribute.
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
IntegerType getIntegerType(unsigned width)
BoolAttr getBoolAttr(bool value)
StringAttr getStringAttr(const Twine &bytes)
MLIRContext * getContext() const
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
virtual Operation * materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc)
Registered hook to materialize a single constant operation from a given attribute value with the desi...
This is a utility class for mapping one set of IR entities to another.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
IRValueT get() const
Return the current value being used by this operand.
void set(IRValueT newValue)
Set the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This class provides a mutable adaptor for a range of operands.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual OptionalParseResult parseOptionalAssignmentList(SmallVectorImpl< Argument > &lhs, SmallVectorImpl< UnresolvedOperand > &rhs)=0
virtual ParseResult parseRegion(Region ®ion, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult parseArgumentList(SmallVectorImpl< Argument > &result, Delimiter delimiter=Delimiter::None, bool allowType=false, bool allowAttrs=false)=0
Parse zero or more arguments with a specified surrounding delimiter.
ParseResult parseAssignmentList(SmallVectorImpl< Argument > &lhs, SmallVectorImpl< UnresolvedOperand > &rhs)
Parse a list of assignments of the form (x1 = y1, x2 = y2, ...)
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDictWithKeyword(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary prefixed with 'attribute...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
void printFunctionalType(Operation *op)
Print the complete type of an operation in functional form.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
void cloneRegionBefore(Region ®ion, Region &parent, Region::iterator before, IRMapping &mapping)
Clone the blocks that belong to "region" before the given position in another region "parent".
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Operation * cloneWithoutRegions(Operation &op, IRMapping &mapper)
Creates a deep copy of this operation but keep the operation regions empty.
This class represents a single result from folding an operation.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Set of flags used to control the behavior of the various IR print methods (e.g.
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.
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)
LogicalResult promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
bool preservesStaticInformation(Type source, Type target)
Returns true if target is a ranked tensor type that preserves static information available in the sou...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
std::function< SmallVector< Value >( OpBuilder &b, Location loc, ArrayRef< BlockArgument > newBbArgs)> NewYieldValuesFn
A function that returns the additional yielded values during replaceWithAdditionalYields.
ParseResult parseDynamicIndexList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &values, DenseI64ArrayAttr &integers, DenseBoolArrayAttr &scalableFlags, SmallVectorImpl< Type > *valueTypes=nullptr, AsmParser::Delimiter delimiter=AsmParser::Delimiter::Square)
Parser hooks for custom directive in assemblyFormat.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
llvm::SetVector< T, Vector, Set, N > SetVector
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
void populateRegionBranchOpInterfaceInliningPattern(RewritePatternSet &patterns, StringRef opName, NonSuccessorInputReplacementBuilderFn replBuilderFn=detail::defaultReplBuilderFn, PatternMatcherFn matcherFn=detail::defaultMatcherFn, PatternBenefit benefit=1)
Populate a pattern that inlines the body of region branch ops when there is a single acyclic path thr...
LogicalResult verifyListOfOperandsOrIntegers(Operation *op, StringRef name, unsigned expectedNumElements, ArrayRef< int64_t > attr, ValueRange values)
Verify that a the values has as many elements as the number of entries in attr for which isDynamic ev...
void populateRegionBranchOpInterfaceCanonicalizationPatterns(RewritePatternSet &patterns, StringRef opName, PatternBenefit benefit=1)
Populate canonicalization patterns that simplify successor operands/inputs of region branch operation...
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
void visitUsedValuesDefinedAbove(Region ®ion, Region &limit, function_ref< void(OpOperand *)> callback)
Calls callback for each use of a value within region or its descendants that was defined at the ances...
llvm::function_ref< Fn > function_ref
void printDynamicIndexList(OpAsmPrinter &printer, Operation *op, OperandRange values, ArrayRef< int64_t > integers, ArrayRef< bool > scalableFlags, TypeRange valueTypes=TypeRange(), AsmParser::Delimiter delimiter=AsmParser::Delimiter::Square)
Printer hooks for custom directive in assemblyFormat.
std::optional< APInt > constantTripCount(OpFoldResult lb, OpFoldResult ub, OpFoldResult step, bool isSigned, llvm::function_ref< std::optional< llvm::APSInt >(Value, Value, bool)> computeUbMinusLb)
Return the number of iterations for a loop with a lower bound lb, upper bound ub and step step,...
LogicalResult foldDynamicIndexList(SmallVectorImpl< OpFoldResult > &ofrs, bool onlyNonNegative=false, bool onlyNonZero=false)
Returns "success" when any of the elements in ofrs is a constant value.
LogicalResult matchAndRewrite(ExecuteRegionOp op, PatternRewriter &rewriter) const override
UnresolvedOperand ssaName
This is the representation of an operand reference.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addOperands(ValueRange newOperands)
void addTypes(ArrayRef< Type > newTypes)
Region * addRegion()
Create a region that should be attached to the operation.