30#include "llvm/ADT/MapVector.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/SmallPtrSet.h"
33#include "llvm/Support/Casting.h"
34#include "llvm/Support/DebugLog.h"
40#include "mlir/Dialect/SCF/IR/SCFOpsDialect.cpp.inc"
47struct SCFInlinerInterface :
public DialectInlinerInterface {
48 using DialectInlinerInterface::DialectInlinerInterface;
52 IRMapping &valueMapping)
const final {
57 bool isLegalToInline(Operation *, Region *,
bool, IRMapping &)
const final {
62 void handleTerminator(Operation *op,
ValueRange valuesToRepl)
const final {
63 auto retValOp = dyn_cast<scf::YieldOp>(op);
67 for (
auto retValue : llvm::zip(valuesToRepl, retValOp.getOperands())) {
68 std::get<0>(retValue).replaceAllUsesWith(std::get<1>(retValue));
78void SCFDialect::initialize() {
81#include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
83 addInterfaces<SCFInlinerInterface>();
84 declarePromisedInterface<ConvertToEmitCPatternInterface, SCFDialect>();
85 declarePromisedInterfaces<bufferization::BufferDeallocationOpInterface,
86 InParallelOp, ReduceReturnOp>();
87 declarePromisedInterfaces<bufferization::BufferizableOpInterface, ConditionOp,
88 ExecuteRegionOp, ForOp, IfOp, IndexSwitchOp,
89 ForallOp, InParallelOp, WhileOp, YieldOp>();
90 declarePromisedInterface<ValueBoundsOpInterface, ForOp>();
95 scf::YieldOp::create(builder, loc);
100template <
typename TerminatorTy>
102 StringRef errorMessage) {
103 Operation *terminatorOperation =
nullptr;
105 terminatorOperation = ®ion.
front().
back();
106 if (
auto yield = dyn_cast_or_null<TerminatorTy>(terminatorOperation))
110 if (terminatorOperation)
111 diag.attachNote(terminatorOperation->
getLoc()) <<
"terminator here";
118 auto addOp =
ub.getDefiningOp<arith::AddIOp>();
121 if ((isSigned && !addOp.hasNoSignedWrap()) ||
122 (!isSigned && !addOp.hasNoUnsignedWrap()))
125 if (addOp.getLhs() != lb ||
146ParseResult ExecuteRegionOp::parse(
OpAsmParser &parser,
174LogicalResult ExecuteRegionOp::verify() {
175 if (getRegion().empty())
176 return emitOpError(
"region needs to have at least one block");
177 if (getRegion().front().getNumArguments() > 0)
178 return emitOpError(
"region cannot have any arguments");
224 if (op.getNoInline())
226 if (!isa<FunctionOpInterface, ExecuteRegionOp>(op->getParentOp()))
229 Block *prevBlock = op->getBlock();
233 cf::BranchOp::create(rewriter, op.getLoc(), &op.getRegion().front());
235 for (
Block &blk : op.getRegion()) {
236 if (YieldOp yieldOp = dyn_cast<YieldOp>(blk.getTerminator())) {
238 cf::BranchOp::create(rewriter, yieldOp.getLoc(), postBlock,
239 yieldOp.getResults());
247 for (
auto res : op.getResults())
248 blockArgs.push_back(postBlock->
addArgument(res.getType(), res.getLoc()));
259 results, ExecuteRegionOp::getOperationName());
262 results, ExecuteRegionOp::getOperationName(),
264 return failure(cast<ExecuteRegionOp>(op).getNoInline());
268void ExecuteRegionOp::getSuccessorRegions(
280void ExecuteRegionOp::getRegionInvocationBounds(
282 bounds.emplace_back(1, 1);
298 "condition op can only exit the loop or branch to the after"
301 return getArgsMutable();
304void ConditionOp::getSuccessorRegions(
306 FoldAdaptor adaptor(operands, *
this);
308 WhileOp whileOp = getParentOp();
312 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
313 if (!boolAttr || boolAttr.getValue())
314 regions.emplace_back(&whileOp.getAfter());
315 if (!boolAttr || !boolAttr.getValue())
325 BodyBuilderFn bodyBuilder,
bool unsignedCmp) {
329 result.addAttribute(getUnsignedCmpAttrName(
result.name),
332 result.addOperands(initArgs);
333 for (
Value v : initArgs)
334 result.addTypes(v.getType());
339 for (
Value v : initArgs)
345 if (initArgs.empty() && !bodyBuilder) {
346 ForOp::ensureTerminator(*bodyRegion, builder,
result.location);
347 }
else if (bodyBuilder) {
355LogicalResult ForOp::verify() {
360 if (getBody()->getNumArguments() < getNumInductionVars())
361 return emitOpError(
"expected body to have at least ")
362 << getNumInductionVars()
363 <<
" argument(s) for the induction variable, but got "
364 << getBody()->getNumArguments();
367 if (getInitArgs().size() != getNumResults())
369 "mismatch in number of loop-carried values and defined values");
374LogicalResult ForOp::verifyRegions() {
376 if (getBody()->getNumArguments() < getNumInductionVars())
377 return emitOpError(
"expected body to have at least ")
378 << getNumInductionVars() <<
" argument(s) for the induction "
379 <<
"variable, but got " << getBody()->getNumArguments();
385 "expected induction variable to be same type as bounds and step");
387 if (getNumRegionIterArgs() != getNumResults())
389 "mismatch in number of basic block args and defined values");
391 auto initArgs = getInitArgs();
392 auto iterArgs = getRegionIterArgs();
393 auto opResults = getResults();
395 for (
auto e : llvm::zip(initArgs, iterArgs, opResults)) {
397 return emitOpError() <<
"types mismatch between " << i
398 <<
"th iter operand and defined value";
400 return emitOpError() <<
"types mismatch between " << i
401 <<
"th iter region arg and defined value";
408std::optional<SmallVector<Value>> ForOp::getLoopInductionVars() {
412std::optional<SmallVector<OpFoldResult>> ForOp::getLoopLowerBounds() {
416std::optional<SmallVector<OpFoldResult>> ForOp::getLoopSteps() {
420std::optional<SmallVector<OpFoldResult>> ForOp::getLoopUpperBounds() {
424bool ForOp::isValidInductionVarType(
Type type) {
429 if (bounds.size() != 1)
431 if (
auto val = dyn_cast<Value>(bounds[0])) {
439 if (bounds.size() != 1)
441 if (
auto val = dyn_cast<Value>(bounds[0])) {
449 if (steps.size() != 1)
451 if (
auto val = dyn_cast<Value>(steps[0])) {
458std::optional<ResultRange> ForOp::getLoopResults() {
return getResults(); }
462LogicalResult ForOp::promoteIfSingleIteration(
RewriterBase &rewriter) {
463 std::optional<APInt> tripCount = getStaticTripCount();
464 LDBG() <<
"promoteIfSingleIteration tripCount is " << tripCount
467 if (!tripCount.has_value() || tripCount->getZExtValue() > 1)
470 if (*tripCount == 0) {
477 auto yieldOp = cast<scf::YieldOp>(getBody()->getTerminator());
484 llvm::append_range(bbArgReplacements, getInitArgs());
488 getOperation()->getIterator(), bbArgReplacements);
504 StringRef prefix =
"") {
505 assert(blocksArgs.size() == initializers.size() &&
506 "expected same length of arguments and initializers");
507 if (initializers.empty())
511 llvm::interleaveComma(llvm::zip(blocksArgs, initializers), p, [&](
auto it) {
512 p << std::get<0>(it) <<
" = " << std::get<1>(it);
518 if (getUnsignedCmp())
521 p <<
" " << getInductionVar() <<
" = " <<
getLowerBound() <<
" to "
525 if (!getInitArgs().empty())
526 p <<
" -> (" << getInitArgs().getTypes() <<
')';
529 p <<
" : " << t <<
' ';
532 !getInitArgs().empty());
534 getUnsignedCmpAttrName().strref());
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(
642 auto yieldOp = cast<scf::YieldOp>(getBody()->getTerminator());
644 newLoop.getBody()->getArguments().take_back(newInitOperands.size());
649 newYieldValuesFn(rewriter, getLoc(), newIterArgs);
650 assert(newInitOperands.size() == newYieldedValues.size() &&
651 "expected as many new yield values as new iter operands");
653 yieldOp.getResultsMutable().append(newYieldedValues);
659 newLoop.getBody()->getArguments().take_front(
660 getBody()->getNumArguments()));
662 if (replaceInitOperandUsesInLoop) {
665 for (
auto it : llvm::zip(newInitOperands, newIterArgs)) {
676 newLoop->getResults().take_front(getNumResults()));
677 return cast<LoopLikeOpInterface>(newLoop.getOperation());
681 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
684 assert(ivArg.getOwner() &&
"unlinked block argument");
685 auto *containingOp = ivArg.getOwner()->getParentOp();
686 return dyn_cast_or_null<ForOp>(containingOp);
690 return getInitArgs();
695 if (std::optional<APInt> tripCount = getStaticTripCount()) {
698 if (*tripCount == 0) {
707 }
else if (*tripCount == 1) {
731LogicalResult scf::ForallOp::promoteIfSingleIteration(RewriterBase &rewriter) {
732 for (
auto [lb, ub, step] :
733 llvm::zip(getMixedLowerBound(), getMixedUpperBound(), getMixedStep())) {
736 if (!tripCount.has_value() || *tripCount != 1)
745 return getBody()->getArguments().drop_front(getRank());
748MutableArrayRef<OpOperand> ForallOp::getInitsMutable() {
749 return getOutputsMutable();
755 scf::InParallelOp terminator = forallOp.getTerminator();
760 bbArgReplacements.append(forallOp.getOutputs().begin(),
761 forallOp.getOutputs().end());
765 forallOp->getIterator(), bbArgReplacements);
770 results.reserve(forallOp.getResults().size());
771 for (
auto &yieldingOp : terminator.getYieldingOps()) {
772 auto parallelInsertSliceOp =
773 dyn_cast<tensor::ParallelInsertSliceOp>(yieldingOp);
774 if (!parallelInsertSliceOp)
777 Value dst = parallelInsertSliceOp.getDest();
778 Value src = parallelInsertSliceOp.getSource();
779 if (llvm::isa<TensorType>(src.
getType())) {
780 results.push_back(tensor::InsertSliceOp::create(
781 rewriter, forallOp.getLoc(), dst.
getType(), src, dst,
782 parallelInsertSliceOp.getOffsets(), parallelInsertSliceOp.getSizes(),
783 parallelInsertSliceOp.getStrides(),
784 parallelInsertSliceOp.getStaticOffsets(),
785 parallelInsertSliceOp.getStaticSizes(),
786 parallelInsertSliceOp.getStaticStrides()));
788 llvm_unreachable(
"unsupported terminator");
803 assert(lbs.size() == ubs.size() &&
804 "expected the same number of lower and upper bounds");
805 assert(lbs.size() == steps.size() &&
806 "expected the same number of lower bounds and steps");
811 bodyBuilder ? bodyBuilder(builder, loc,
ValueRange(), iterArgs)
813 assert(results.size() == iterArgs.size() &&
814 "loop nest body must return as many values as loop has iteration "
816 return LoopNest{{}, std::move(results)};
824 loops.reserve(lbs.size());
825 ivs.reserve(lbs.size());
828 for (
unsigned i = 0, e = lbs.size(); i < e; ++i) {
829 auto loop = scf::ForOp::create(
830 builder, currentLoc, lbs[i], ubs[i], steps[i], currentIterArgs,
836 currentIterArgs = args;
837 currentLoc = nestedLoc;
843 loops.push_back(loop);
847 for (
unsigned i = 0, e = loops.size() - 1; i < e; ++i) {
849 scf::YieldOp::create(builder, loc, loops[i + 1].getResults());
856 ? bodyBuilder(builder, currentLoc, ivs,
857 loops.back().getRegionIterArgs())
859 assert(results.size() == iterArgs.size() &&
860 "loop nest body must return as many values as loop has iteration "
863 scf::YieldOp::create(builder, loc, results);
867 llvm::append_range(nestResults, loops.front().getResults());
868 return LoopNest{std::move(loops), std::move(nestResults)};
881 bodyBuilder(nestedBuilder, nestedLoc, ivs);
890 assert(operand.
getOwner() == forOp);
895 "expected an iter OpOperand");
897 "Expected a different type");
899 for (
OpOperand &opOperand : forOp.getInitArgsMutable()) {
904 newIterOperands.push_back(opOperand.get());
908 scf::ForOp newForOp = scf::ForOp::create(
909 rewriter, forOp.getLoc(), forOp.getLowerBound(), forOp.getUpperBound(),
910 forOp.getStep(), newIterOperands,
nullptr,
911 forOp.getUnsignedCmp());
912 newForOp->setAttrs(forOp->getAttrs());
913 Block &newBlock = newForOp.getRegion().
front();
921 BlockArgument newRegionIterArg = newForOp.getTiedLoopRegionIterArg(
923 Value castIn = castFn(rewriter, newForOp.getLoc(), oldType, newRegionIterArg);
924 newBlockTransferArgs[newRegionIterArg.
getArgNumber()] = castIn;
928 rewriter.
mergeBlocks(&oldBlock, &newBlock, newBlockTransferArgs);
931 auto clonedYieldOp = cast<scf::YieldOp>(newBlock.
getTerminator());
934 newRegionIterArg.
getArgNumber() - forOp.getNumInductionVars();
935 Value castOut = castFn(rewriter, newForOp.getLoc(), newType,
936 clonedYieldOp.getOperand(yieldIdx));
938 newYieldOperands[yieldIdx] = castOut;
939 scf::YieldOp::create(rewriter, newForOp.getLoc(), newYieldOperands);
940 rewriter.
eraseOp(clonedYieldOp);
945 newResults[yieldIdx] =
946 castFn(rewriter, newForOp.getLoc(), oldType, newResults[yieldIdx]);
981 LogicalResult matchAndRewrite(ForOp op,
983 for (
auto it : llvm::zip(op.getInitArgsMutable(), op.getResults())) {
984 OpOperand &iterOpOperand = std::get<0>(it);
987 incomingCast.getSource().getType() == incomingCast.getType())
992 incomingCast.getDest().getType(),
993 incomingCast.getSource().getType()))
995 if (!std::get<1>(it).hasOneUse())
1001 rewriter, op, iterOpOperand, incomingCast.getSource(),
1003 return tensor::CastOp::create(b, loc, type, source);
1012void ForOp::getCanonicalizationPatterns(RewritePatternSet &results,
1013 MLIRContext *context) {
1014 results.
add<ForOpTensorCastFolder>(context);
1016 results, ForOp::getOperationName());
1021 results, ForOp::getOperationName(),
1023 [](OpBuilder &builder, Location loc, Value value) {
1027 auto blockArg = cast<BlockArgument>(value);
1028 assert(blockArg.getArgNumber() == 0 &&
"expected induction variable");
1029 auto forOp = cast<ForOp>(blockArg.getOwner()->getParentOp());
1030 return forOp.getLowerBound();
1036std::optional<APInt> ForOp::getConstantStep() {
1039 return step.getValue();
1043std::optional<MutableArrayRef<OpOperand>> ForOp::getYieldedValuesMutable() {
1044 return cast<scf::YieldOp>(getBody()->getTerminator()).getResultsMutable();
1050 if (
auto constantStep = getConstantStep())
1051 if (*constantStep == 1)
1059std::optional<APInt> ForOp::getStaticTripCount() {
1068LogicalResult ForallOp::verify() {
1069 unsigned numLoops = getRank();
1071 if (getNumResults() != getOutputs().size())
1073 << getNumResults() <<
" results, but has only "
1074 << getOutputs().size() <<
" outputs";
1077 auto *body = getBody();
1079 return emitOpError(
"region expects ") << numLoops <<
" arguments";
1080 for (int64_t i = 0; i < numLoops; ++i)
1083 << i <<
"-th block argument to be an index";
1084 for (
unsigned i = 0; i < getOutputs().size(); ++i)
1087 << i <<
"-th output and corresponding block argument";
1088 if (getMapping().has_value() && !getMapping()->empty()) {
1089 if (getDeviceMappingAttrs().size() != numLoops)
1090 return emitOpError() <<
"mapping attribute size must match op rank";
1091 if (
failed(getDeviceMaskingAttr()))
1093 <<
" supports at most one device masking attribute";
1097 Operation *op = getOperation();
1099 getStaticLowerBound(),
1100 getDynamicLowerBound())))
1103 getStaticUpperBound(),
1104 getDynamicUpperBound())))
1107 getStaticStep(), getDynamicStep())))
1113void ForallOp::print(OpAsmPrinter &p) {
1114 Operation *op = getOperation();
1115 p <<
" (" << getInductionVars();
1116 if (isNormalized()) {
1137 if (!getRegionOutArgs().empty())
1138 p <<
"-> (" << getResultTypes() <<
") ";
1139 p.printRegion(getRegion(),
1141 getNumResults() > 0);
1142 p.printOptionalAttrDict(op->
getAttrs(), {getOperandSegmentSizesAttrName(),
1143 getStaticLowerBoundAttrName(),
1144 getStaticUpperBoundAttrName(),
1145 getStaticStepAttrName()});
1148ParseResult ForallOp::parse(OpAsmParser &parser, OperationState &
result) {
1150 auto indexType =
b.getIndexType();
1155 SmallVector<OpAsmParser::Argument, 4> ivs;
1160 SmallVector<OpAsmParser::UnresolvedOperand> dynamicLbs, dynamicUbs,
1170 unsigned numLoops = ivs.size();
1171 staticLbs =
b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 0));
1172 staticSteps =
b.getDenseI64ArrayAttr(SmallVector<int64_t>(numLoops, 1));
1201 SmallVector<OpAsmParser::Argument, 4> regionOutArgs;
1202 SmallVector<OpAsmParser::UnresolvedOperand, 4> outOperands;
1205 if (outOperands.size() !=
result.types.size())
1207 "mismatch between out operands and types");
1216 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1217 std::unique_ptr<Region> region = std::make_unique<Region>();
1218 for (
auto &iv : ivs) {
1219 iv.type =
b.getIndexType();
1220 regionArgs.push_back(iv);
1222 for (
const auto &it : llvm::enumerate(regionOutArgs)) {
1223 auto &out = it.value();
1224 out.type =
result.types[it.index()];
1225 regionArgs.push_back(out);
1231 ForallOp::ensureTerminator(*region,
b,
result.location);
1232 result.addRegion(std::move(region));
1238 result.addAttribute(
"staticLowerBound", staticLbs);
1239 result.addAttribute(
"staticUpperBound", staticUbs);
1240 result.addAttribute(
"staticStep", staticSteps);
1241 result.addAttribute(
"operandSegmentSizes",
1243 {static_cast<int32_t>(dynamicLbs.size()),
1244 static_cast<int32_t>(dynamicUbs.size()),
1245 static_cast<int32_t>(dynamicSteps.size()),
1246 static_cast<int32_t>(outOperands.size())}));
1251void ForallOp::build(
1252 mlir::OpBuilder &
b, mlir::OperationState &
result,
1253 ArrayRef<OpFoldResult> lbs, ArrayRef<OpFoldResult> ubs,
1254 ArrayRef<OpFoldResult> steps,
ValueRange outputs,
1255 std::optional<ArrayAttr> mapping,
1257 SmallVector<int64_t> staticLbs, staticUbs, staticSteps;
1258 SmallVector<Value> dynamicLbs, dynamicUbs, dynamicSteps;
1263 result.addOperands(dynamicLbs);
1264 result.addOperands(dynamicUbs);
1265 result.addOperands(dynamicSteps);
1266 result.addOperands(outputs);
1269 result.addAttribute(getStaticLowerBoundAttrName(
result.name),
1270 b.getDenseI64ArrayAttr(staticLbs));
1271 result.addAttribute(getStaticUpperBoundAttrName(
result.name),
1272 b.getDenseI64ArrayAttr(staticUbs));
1273 result.addAttribute(getStaticStepAttrName(
result.name),
1274 b.getDenseI64ArrayAttr(staticSteps));
1276 "operandSegmentSizes",
1277 b.getDenseI32ArrayAttr({static_cast<int32_t>(dynamicLbs.size()),
1278 static_cast<int32_t>(dynamicUbs.size()),
1279 static_cast<int32_t>(dynamicSteps.size()),
1280 static_cast<int32_t>(outputs.size())}));
1281 if (mapping.has_value()) {
1282 result.addAttribute(ForallOp::getMappingAttrName(
result.name),
1286 Region *bodyRegion =
result.addRegion();
1287 OpBuilder::InsertionGuard g(
b);
1288 b.createBlock(bodyRegion);
1293 SmallVector<Type>(lbs.size(),
b.getIndexType()),
1294 SmallVector<Location>(staticLbs.size(),
result.location));
1297 SmallVector<Location>(outputs.size(),
result.location));
1299 b.setInsertionPointToStart(&bodyBlock);
1300 if (!bodyBuilderFn) {
1301 ForallOp::ensureTerminator(*bodyRegion,
b,
result.location);
1308void ForallOp::build(
1309 mlir::OpBuilder &
b, mlir::OperationState &
result,
1310 ArrayRef<OpFoldResult> ubs,
ValueRange outputs,
1311 std::optional<ArrayAttr> mapping,
1313 unsigned numLoops = ubs.size();
1314 SmallVector<OpFoldResult> lbs(numLoops,
b.getIndexAttr(0));
1315 SmallVector<OpFoldResult> steps(numLoops,
b.getIndexAttr(1));
1316 build(
b,
result, lbs, ubs, steps, outputs, mapping, bodyBuilderFn);
1320bool ForallOp::isNormalized() {
1321 auto allEqual = [](ArrayRef<OpFoldResult> results, int64_t val) {
1322 return llvm::all_of(results, [&](OpFoldResult ofr) {
1324 return intValue.has_value() && intValue == val;
1327 return allEqual(getMixedLowerBound(), 0) && allEqual(getMixedStep(), 1);
1330InParallelOp ForallOp::getTerminator() {
1331 return cast<InParallelOp>(getBody()->getTerminator());
1334SmallVector<Operation *> ForallOp::getCombiningOps(BlockArgument bbArg) {
1335 SmallVector<Operation *> storeOps;
1336 for (Operation *user : bbArg.
getUsers()) {
1337 if (
auto parallelOp = dyn_cast<ParallelCombiningOpInterface>(user)) {
1338 storeOps.push_back(parallelOp);
1344SmallVector<DeviceMappingAttrInterface> ForallOp::getDeviceMappingAttrs() {
1345 SmallVector<DeviceMappingAttrInterface> res;
1348 for (
auto attr : getMapping()->getValue()) {
1349 auto m = dyn_cast<DeviceMappingAttrInterface>(attr);
1356FailureOr<DeviceMaskingAttrInterface> ForallOp::getDeviceMaskingAttr() {
1357 DeviceMaskingAttrInterface res;
1360 for (
auto attr : getMapping()->getValue()) {
1361 auto m = dyn_cast<DeviceMaskingAttrInterface>(attr);
1370bool ForallOp::usesLinearMapping() {
1371 SmallVector<DeviceMappingAttrInterface> ifaces = getDeviceMappingAttrs();
1374 return ifaces.front().isLinearMapping();
1377std::optional<SmallVector<Value>> ForallOp::getLoopInductionVars() {
1378 return SmallVector<Value>{getBody()->getArguments().take_front(getRank())};
1382std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopLowerBounds() {
1384 return getMixedValues(getStaticLowerBound(), getDynamicLowerBound(),
b);
1388std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopUpperBounds() {
1390 return getMixedValues(getStaticUpperBound(), getDynamicUpperBound(),
b);
1394std::optional<SmallVector<OpFoldResult>> ForallOp::getLoopSteps() {
1400 auto tidxArg = llvm::dyn_cast<BlockArgument>(val);
1403 assert(tidxArg.getOwner() &&
"unlinked block argument");
1404 auto *containingOp = tidxArg.getOwner()->getParentOp();
1405 return dyn_cast<ForallOp>(containingOp);
1413 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1415 auto forallOp = dimOp.getSource().getDefiningOp<ForallOp>();
1419 forallOp.getTiedOpOperand(llvm::cast<OpResult>(dimOp.getSource()))
1422 dimOp, [&]() { dimOp.getSourceMutable().assign(sharedOut); });
1427class ForallOpControlOperandsFolder :
public OpRewritePattern<ForallOp> {
1429 using OpRewritePattern<ForallOp>::OpRewritePattern;
1431 LogicalResult matchAndRewrite(ForallOp op,
1432 PatternRewriter &rewriter)
const override {
1433 SmallVector<OpFoldResult> mixedLowerBound(op.getMixedLowerBound());
1434 SmallVector<OpFoldResult> mixedUpperBound(op.getMixedUpperBound());
1435 SmallVector<OpFoldResult> mixedStep(op.getMixedStep());
1442 SmallVector<Value> dynamicLowerBound, dynamicUpperBound, dynamicStep;
1443 SmallVector<int64_t> staticLowerBound, staticUpperBound, staticStep;
1446 op.getDynamicLowerBoundMutable().assign(dynamicLowerBound);
1447 op.setStaticLowerBound(staticLowerBound);
1451 op.getDynamicUpperBoundMutable().assign(dynamicUpperBound);
1452 op.setStaticUpperBound(staticUpperBound);
1455 op.getDynamicStepMutable().assign(dynamicStep);
1456 op.setStaticStep(staticStep);
1458 op->setAttr(ForallOp::getOperandSegmentSizeAttr(),
1460 {static_cast<int32_t>(dynamicLowerBound.size()),
1461 static_cast<int32_t>(dynamicUpperBound.size()),
1462 static_cast<int32_t>(dynamicStep.size()),
1463 static_cast<int32_t>(op.getNumResults())}));
1542struct ForallOpIterArgsFolder :
public OpRewritePattern<ForallOp> {
1543 using OpRewritePattern<ForallOp>::OpRewritePattern;
1545 LogicalResult matchAndRewrite(ForallOp forallOp,
1546 PatternRewriter &rewriter)
const final {
1557 SmallVector<Value> resultsToDelete;
1558 SmallVector<Value> outsToDelete;
1559 SmallVector<BlockArgument> blockArgsToDelete;
1560 SmallVector<Value> newOuts;
1561 BitVector resultIndicesToDelete(forallOp.getNumResults(),
false);
1562 BitVector blockIndicesToDelete(forallOp.getBody()->getNumArguments(),
1564 for (OpResult
result : forallOp.getResults()) {
1565 OpOperand *opOperand = forallOp.getTiedOpOperand(
result);
1566 BlockArgument blockArg = forallOp.getTiedBlockArgument(opOperand);
1567 if (
result.use_empty() || forallOp.getCombiningOps(blockArg).empty()) {
1568 resultsToDelete.push_back(
result);
1569 outsToDelete.push_back(opOperand->
get());
1570 blockArgsToDelete.push_back(blockArg);
1571 resultIndicesToDelete[
result.getResultNumber()] =
true;
1574 newOuts.push_back(opOperand->
get());
1580 if (resultsToDelete.empty())
1585 for (
auto blockArg : blockArgsToDelete) {
1586 SmallVector<Operation *> combiningOps =
1587 forallOp.getCombiningOps(blockArg);
1588 for (Operation *combiningOp : combiningOps)
1589 rewriter.
eraseOp(combiningOp);
1591 for (
auto [blockArg,
result, out] :
1592 llvm::zip_equal(blockArgsToDelete, resultsToDelete, outsToDelete)) {
1598 forallOp.getBody()->eraseArguments(blockIndicesToDelete);
1603 auto newForallOp = cast<scf::ForallOp>(
1605 newForallOp.getOutputsMutable().assign(newOuts);
1611struct ForallOpSingleOrZeroIterationDimsFolder
1612 :
public OpRewritePattern<ForallOp> {
1613 using OpRewritePattern<ForallOp>::OpRewritePattern;
1615 LogicalResult matchAndRewrite(ForallOp op,
1616 PatternRewriter &rewriter)
const override {
1618 if (op.getMapping().has_value() && !op.getMapping()->empty())
1620 Location loc = op.getLoc();
1623 SmallVector<OpFoldResult> newMixedLowerBounds, newMixedUpperBounds,
1626 for (
auto [lb, ub, step, iv] :
1627 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1628 op.getMixedStep(), op.getInductionVars())) {
1629 auto numIterations =
1631 if (numIterations.has_value()) {
1633 if (*numIterations == 0) {
1634 rewriter.
replaceOp(op, op.getOutputs());
1639 if (*numIterations == 1) {
1644 newMixedLowerBounds.push_back(lb);
1645 newMixedUpperBounds.push_back(ub);
1646 newMixedSteps.push_back(step);
1650 if (newMixedLowerBounds.empty()) {
1656 if (newMixedLowerBounds.size() ==
static_cast<unsigned>(op.getRank())) {
1658 op,
"no dimensions have 0 or 1 iterations");
1663 newOp = ForallOp::create(rewriter, loc, newMixedLowerBounds,
1664 newMixedUpperBounds, newMixedSteps,
1665 op.getOutputs(), std::nullopt,
nullptr);
1666 newOp.getBodyRegion().getBlocks().clear();
1670 SmallVector<StringAttr> elidedAttrs{newOp.getOperandSegmentSizesAttrName(),
1671 newOp.getStaticLowerBoundAttrName(),
1672 newOp.getStaticUpperBoundAttrName(),
1673 newOp.getStaticStepAttrName()};
1674 for (
const auto &namedAttr : op->getAttrs()) {
1675 if (llvm::is_contained(elidedAttrs, namedAttr.getName()))
1678 newOp->setAttr(namedAttr.getName(), namedAttr.getValue());
1682 newOp.getRegion().begin(), mapping);
1683 rewriter.
replaceOp(op, newOp.getResults());
1689struct ForallOpReplaceConstantInductionVar :
public OpRewritePattern<ForallOp> {
1690 using OpRewritePattern<ForallOp>::OpRewritePattern;
1692 LogicalResult matchAndRewrite(ForallOp op,
1693 PatternRewriter &rewriter)
const override {
1694 Location loc = op.getLoc();
1695 bool changed =
false;
1696 for (
auto [lb, ub, step, iv] :
1697 llvm::zip(op.getMixedLowerBound(), op.getMixedUpperBound(),
1698 op.getMixedStep(), op.getInductionVars())) {
1701 auto numIterations =
1703 if (!numIterations.has_value() || numIterations.value() != 1) {
1714struct FoldTensorCastOfOutputIntoForallOp
1715 :
public OpRewritePattern<scf::ForallOp> {
1716 using OpRewritePattern<scf::ForallOp>::OpRewritePattern;
1723 LogicalResult matchAndRewrite(scf::ForallOp forallOp,
1724 PatternRewriter &rewriter)
const final {
1725 llvm::SmallMapVector<unsigned, TypeCast, 2> tensorCastProducers;
1726 llvm::SmallVector<Value> newOutputTensors = forallOp.getOutputs();
1727 for (
auto en : llvm::enumerate(newOutputTensors)) {
1728 auto castOp = en.value().getDefiningOp<tensor::CastOp>();
1735 castOp.getSource().getType())) {
1739 tensorCastProducers[en.index()] =
1740 TypeCast{castOp.getSource().getType(), castOp.getType()};
1741 newOutputTensors[en.index()] = castOp.getSource();
1744 if (tensorCastProducers.empty())
1748 Location loc = forallOp.getLoc();
1749 auto newForallOp = ForallOp::create(
1750 rewriter, loc, forallOp.getMixedLowerBound(),
1751 forallOp.getMixedUpperBound(), forallOp.getMixedStep(),
1752 newOutputTensors, forallOp.getMapping(),
1753 [&](OpBuilder nestedBuilder, Location nestedLoc,
ValueRange bbArgs) {
1754 auto castBlockArgs =
1755 llvm::to_vector(bbArgs.take_back(forallOp->getNumResults()));
1756 for (auto [index, cast] : tensorCastProducers) {
1757 Value &oldTypeBBArg = castBlockArgs[index];
1758 oldTypeBBArg = tensor::CastOp::create(nestedBuilder, nestedLoc,
1759 cast.dstType, oldTypeBBArg);
1763 SmallVector<Value> ivsBlockArgs =
1764 llvm::to_vector(bbArgs.take_front(forallOp.getRank()));
1765 ivsBlockArgs.append(castBlockArgs);
1767 bbArgs.front().getParentBlock(), ivsBlockArgs);
1778 llvm::SmallDenseSet<Value> newIterArgSet(
1779 newForallOp.getRegionIterArgs().begin(),
1780 newForallOp.getRegionIterArgs().end());
1781 auto terminator = newForallOp.getTerminator();
1782 for (
auto &yieldingOp : terminator.getYieldingOps()) {
1783 auto parallelCombiningOp =
1784 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1785 if (!parallelCombiningOp)
1787 for (OpOperand &dest : parallelCombiningOp.getUpdatedDestinations()) {
1788 auto castOp = dest.get().getDefiningOp<tensor::CastOp>();
1789 if (castOp && newIterArgSet.contains(castOp.getSource()))
1790 dest.set(castOp.getSource());
1796 SmallVector<Value> castResults = newForallOp.getResults();
1797 for (
auto &item : tensorCastProducers) {
1798 Value &oldTypeResult = castResults[item.first];
1799 oldTypeResult = tensor::CastOp::create(rewriter, loc, item.second.dstType,
1802 rewriter.
replaceOp(forallOp, castResults);
1809void ForallOp::getCanonicalizationPatterns(RewritePatternSet &results,
1810 MLIRContext *context) {
1811 results.
add<DimOfForallOp, FoldTensorCastOfOutputIntoForallOp,
1812 ForallOpControlOperandsFolder, ForallOpIterArgsFolder,
1813 ForallOpSingleOrZeroIterationDimsFolder,
1814 ForallOpReplaceConstantInductionVar>(context);
1817void ForallOp::getSuccessorRegions(RegionBranchPoint point,
1818 SmallVectorImpl<RegionSuccessor> ®ions) {
1825 regions.push_back(RegionSuccessor(&getRegion()));
1828 regions.push_back(RegionSuccessor(getOperation()));
1833 regions.push_back(RegionSuccessor(getOperation()));
1842void InParallelOp::build(OpBuilder &
b, OperationState &
result) {
1843 OpBuilder::InsertionGuard g(
b);
1844 Region *bodyRegion =
result.addRegion();
1845 b.createBlock(bodyRegion);
1848LogicalResult InParallelOp::verify() {
1849 scf::ForallOp forallOp =
1850 dyn_cast<scf::ForallOp>(getOperation()->getParentOp());
1852 return this->
emitOpError(
"expected forall op parent");
1854 for (Operation &op : getRegion().front().getOperations()) {
1855 auto parallelCombiningOp = dyn_cast<ParallelCombiningOpInterface>(&op);
1856 if (!parallelCombiningOp) {
1857 return this->
emitOpError(
"expected only ParallelCombiningOpInterface")
1862 MutableOperandRange dests = parallelCombiningOp.getUpdatedDestinations();
1863 ArrayRef<BlockArgument> regionOutArgs = forallOp.getRegionOutArgs();
1864 for (OpOperand &dest : dests) {
1865 if (!llvm::is_contained(regionOutArgs, dest.get()))
1866 return op.emitOpError(
"may only insert into an output block argument");
1873void InParallelOp::print(OpAsmPrinter &p) {
1881ParseResult InParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
1884 SmallVector<OpAsmParser::Argument, 8> regionOperands;
1885 std::unique_ptr<Region> region = std::make_unique<Region>();
1889 if (region->empty())
1890 OpBuilder(builder.
getContext()).createBlock(region.get());
1891 result.addRegion(std::move(region));
1899OpResult InParallelOp::getParentResult(int64_t idx) {
1900 return getOperation()->getParentOp()->getResult(idx);
1903SmallVector<BlockArgument> InParallelOp::getDests() {
1904 SmallVector<BlockArgument> updatedDests;
1905 for (Operation &yieldingOp : getYieldingOps()) {
1906 auto parallelCombiningOp =
1907 dyn_cast<ParallelCombiningOpInterface>(&yieldingOp);
1908 if (!parallelCombiningOp)
1910 for (OpOperand &updatedOperand :
1911 parallelCombiningOp.getUpdatedDestinations())
1912 updatedDests.push_back(cast<BlockArgument>(updatedOperand.get()));
1914 return updatedDests;
1917llvm::iterator_range<Block::iterator> InParallelOp::getYieldingOps() {
1918 return getRegion().front().getOperations();
1926 assert(a &&
"expected non-empty operation");
1927 assert(
b &&
"expected non-empty operation");
1932 if (ifOp->isProperAncestor(
b))
1935 return static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*a)) !=
1936 static_cast<bool>(ifOp.thenBlock()->findAncestorOpInBlock(*
b));
1938 ifOp = ifOp->getParentOfType<IfOp>();
1946IfOp::inferReturnTypes(
MLIRContext *ctx, std::optional<Location> loc,
1947 IfOp::Adaptor adaptor,
1949 if (adaptor.getRegions().empty())
1951 Region *r = &adaptor.getThenRegion();
1957 auto yieldOp = llvm::dyn_cast<YieldOp>(
b.back());
1960 TypeRange types = yieldOp.getOperandTypes();
1961 llvm::append_range(inferredReturnTypes, types);
1967 return build(builder,
result, resultTypes, cond,
false,
1971void IfOp::build(OpBuilder &builder, OperationState &
result,
1972 TypeRange resultTypes, Value cond,
bool addThenBlock,
1973 bool addElseBlock) {
1974 assert((!addElseBlock || addThenBlock) &&
1975 "must not create else block w/o then block");
1976 result.addTypes(resultTypes);
1977 result.addOperands(cond);
1980 OpBuilder::InsertionGuard guard(builder);
1981 Region *thenRegion =
result.addRegion();
1984 Region *elseRegion =
result.addRegion();
1989void IfOp::build(OpBuilder &builder, OperationState &
result, Value cond,
1990 bool withElseRegion) {
1994void IfOp::build(OpBuilder &builder, OperationState &
result,
1995 TypeRange resultTypes, Value cond,
bool withElseRegion) {
1996 result.addTypes(resultTypes);
1997 result.addOperands(cond);
2000 OpBuilder::InsertionGuard guard(builder);
2001 Region *thenRegion =
result.addRegion();
2003 if (resultTypes.empty())
2004 IfOp::ensureTerminator(*thenRegion, builder,
result.location);
2007 Region *elseRegion =
result.addRegion();
2008 if (withElseRegion) {
2010 if (resultTypes.empty())
2011 IfOp::ensureTerminator(*elseRegion, builder,
result.location);
2015void IfOp::build(OpBuilder &builder, OperationState &
result, Value cond,
2017 function_ref<
void(OpBuilder &, Location)> elseBuilder) {
2018 assert(thenBuilder &&
"the builder callback for 'then' must be present");
2019 result.addOperands(cond);
2022 OpBuilder::InsertionGuard guard(builder);
2023 Region *thenRegion =
result.addRegion();
2025 thenBuilder(builder,
result.location);
2028 Region *elseRegion =
result.addRegion();
2031 elseBuilder(builder,
result.location);
2035 SmallVector<Type> inferredReturnTypes;
2037 auto attrDict = DictionaryAttr::get(ctx,
result.attributes);
2038 if (succeeded(inferReturnTypes(ctx, std::nullopt,
result.operands, attrDict,
2039 PropertyRef{},
result.regions,
2040 inferredReturnTypes))) {
2041 result.addTypes(inferredReturnTypes);
2045LogicalResult IfOp::verify() {
2046 if (getNumResults() != 0 && getElseRegion().empty())
2047 return emitOpError(
"must have an else block if defining values");
2051ParseResult IfOp::parse(OpAsmParser &parser, OperationState &
result) {
2053 result.regions.reserve(2);
2054 Region *thenRegion =
result.addRegion();
2055 Region *elseRegion =
result.addRegion();
2058 OpAsmParser::UnresolvedOperand cond;
2084void IfOp::print(OpAsmPrinter &p) {
2085 bool printBlockTerminators =
false;
2087 p <<
" " << getCondition();
2088 if (!getResults().empty()) {
2089 p <<
" -> (" << getResultTypes() <<
")";
2091 printBlockTerminators =
true;
2096 printBlockTerminators);
2099 auto &elseRegion = getElseRegion();
2100 if (!elseRegion.
empty()) {
2104 printBlockTerminators);
2110void IfOp::getSuccessorRegions(RegionBranchPoint point,
2111 SmallVectorImpl<RegionSuccessor> ®ions) {
2115 regions.push_back(RegionSuccessor(getOperation()));
2119 regions.push_back(RegionSuccessor(&getThenRegion()));
2122 Region *elseRegion = &this->getElseRegion();
2123 if (elseRegion->
empty())
2124 regions.push_back(RegionSuccessor(getOperation()));
2126 regions.push_back(RegionSuccessor(elseRegion));
2129ValueRange IfOp::getSuccessorInputs(RegionSuccessor successor) {
2134void IfOp::getEntrySuccessorRegions(ArrayRef<Attribute> operands,
2135 SmallVectorImpl<RegionSuccessor> ®ions) {
2136 FoldAdaptor adaptor(operands, *
this);
2137 auto boolAttr = dyn_cast_or_null<BoolAttr>(adaptor.getCondition());
2138 if (!boolAttr || boolAttr.getValue())
2139 regions.emplace_back(&getThenRegion());
2142 if (!boolAttr || !boolAttr.getValue()) {
2143 if (!getElseRegion().empty())
2144 regions.emplace_back(&getElseRegion());
2146 regions.emplace_back(RegionSuccessor(getOperation()));
2150LogicalResult IfOp::fold(FoldAdaptor adaptor,
2151 SmallVectorImpl<OpFoldResult> &results) {
2153 if (getElseRegion().empty())
2156 arith::XOrIOp xorStmt = getCondition().getDefiningOp<arith::XOrIOp>();
2163 getConditionMutable().assign(xorStmt.getLhs());
2164 Block *thenBlock = &getThenRegion().front();
2167 getThenRegion().getBlocks().splice(getThenRegion().getBlocks().begin(),
2168 getElseRegion().getBlocks());
2169 getElseRegion().getBlocks().splice(getElseRegion().getBlocks().begin(),
2170 getThenRegion().getBlocks(), thenBlock);
2174void IfOp::getRegionInvocationBounds(
2175 ArrayRef<Attribute> operands,
2176 SmallVectorImpl<InvocationBounds> &invocationBounds) {
2177 if (
auto cond = llvm::dyn_cast_or_null<BoolAttr>(operands[0])) {
2180 invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0);
2181 invocationBounds.emplace_back(0, cond.getValue() ? 0 : 1);
2184 invocationBounds.assign(2, {0, 1});
2191struct ConvertTrivialIfToSelect :
public OpRewritePattern<IfOp> {
2192 using OpRewritePattern<IfOp>::OpRewritePattern;
2194 LogicalResult matchAndRewrite(IfOp op,
2195 PatternRewriter &rewriter)
const override {
2196 if (op->getNumResults() == 0)
2199 auto cond = op.getCondition();
2200 auto thenYieldArgs = op.thenYield().getOperands();
2201 auto elseYieldArgs = op.elseYield().getOperands();
2203 SmallVector<Type> nonHoistable;
2204 for (
auto [trueVal, falseVal] : llvm::zip(thenYieldArgs, elseYieldArgs)) {
2205 if (&op.getThenRegion() == trueVal.getParentRegion() ||
2206 &op.getElseRegion() == falseVal.getParentRegion())
2207 nonHoistable.push_back(trueVal.getType());
2211 if (nonHoistable.size() == op->getNumResults())
2214 IfOp
replacement = IfOp::create(rewriter, op.getLoc(), nonHoistable, cond,
2218 replacement.getThenRegion().takeBody(op.getThenRegion());
2219 replacement.getElseRegion().takeBody(op.getElseRegion());
2221 SmallVector<Value> results(op->getNumResults());
2222 assert(thenYieldArgs.size() == results.size());
2223 assert(elseYieldArgs.size() == results.size());
2225 SmallVector<Value> trueYields;
2226 SmallVector<Value> falseYields;
2228 for (
const auto &it :
2229 llvm::enumerate(llvm::zip(thenYieldArgs, elseYieldArgs))) {
2230 Value trueVal = std::get<0>(it.value());
2231 Value falseVal = std::get<1>(it.value());
2234 results[it.index()] =
replacement.getResult(trueYields.size());
2235 trueYields.push_back(trueVal);
2236 falseYields.push_back(falseVal);
2237 }
else if (trueVal == falseVal)
2238 results[it.index()] = trueVal;
2240 results[it.index()] = arith::SelectOp::create(rewriter, op.getLoc(),
2241 cond, trueVal, falseVal);
2268struct ConditionPropagation :
public OpRewritePattern<IfOp> {
2269 using OpRewritePattern<IfOp>::OpRewritePattern;
2272 enum class Parent { Then, Else,
None };
2277 static Parent getParentType(Region *toCheck, IfOp op,
2279 Region *endRegion) {
2280 SmallVector<Region *> seen;
2281 while (toCheck != endRegion) {
2282 auto found = cache.find(toCheck);
2283 if (found != cache.end())
2284 return found->second;
2285 seen.push_back(toCheck);
2286 if (&op.getThenRegion() == toCheck) {
2287 for (Region *region : seen)
2288 cache[region] = Parent::Then;
2289 return Parent::Then;
2291 if (&op.getElseRegion() == toCheck) {
2292 for (Region *region : seen)
2293 cache[region] = Parent::Else;
2294 return Parent::Else;
2299 for (Region *region : seen)
2300 cache[region] = Parent::None;
2301 return Parent::None;
2304 LogicalResult matchAndRewrite(IfOp op,
2305 PatternRewriter &rewriter)
const override {
2311 bool changed =
false;
2316 Value constantTrue =
nullptr;
2317 Value constantFalse =
nullptr;
2320 for (OpOperand &use :
2321 llvm::make_early_inc_range(op.getCondition().getUses())) {
2324 case Parent::Then: {
2328 constantTrue = arith::ConstantOp::create(
2332 [&]() { use.set(constantTrue); });
2335 case Parent::Else: {
2339 constantFalse = arith::ConstantOp::create(
2343 [&]() { use.set(constantFalse); });
2391struct ReplaceIfYieldWithConditionOrValue :
public OpRewritePattern<IfOp> {
2392 using OpRewritePattern<IfOp>::OpRewritePattern;
2394 LogicalResult matchAndRewrite(IfOp op,
2395 PatternRewriter &rewriter)
const override {
2397 if (op.getNumResults() == 0)
2401 cast<scf::YieldOp>(op.getThenRegion().back().getTerminator());
2403 cast<scf::YieldOp>(op.getElseRegion().back().getTerminator());
2406 op.getOperation()->getIterator());
2407 bool changed =
false;
2409 for (
auto [trueResult, falseResult, opResult] :
2410 llvm::zip(trueYield.getResults(), falseYield.getResults(),
2412 if (trueResult == falseResult) {
2413 if (!opResult.use_empty()) {
2414 opResult.replaceAllUsesWith(trueResult);
2420 BoolAttr trueYield, falseYield;
2425 bool trueVal = trueYield.
getValue();
2426 bool falseVal = falseYield.
getValue();
2427 if (!trueVal && falseVal) {
2428 if (!opResult.use_empty()) {
2429 Dialect *constDialect = trueResult.getDefiningOp()->getDialect();
2430 Value notCond = arith::XOrIOp::create(
2431 rewriter, op.getLoc(), op.getCondition(),
2437 opResult.replaceAllUsesWith(notCond);
2441 if (trueVal && !falseVal) {
2442 if (!opResult.use_empty()) {
2443 opResult.replaceAllUsesWith(op.getCondition());
2473struct CombineIfs :
public OpRewritePattern<IfOp> {
2474 using OpRewritePattern<IfOp>::OpRewritePattern;
2476 LogicalResult matchAndRewrite(IfOp nextIf,
2477 PatternRewriter &rewriter)
const override {
2478 Block *parent = nextIf->getBlock();
2479 if (nextIf == &parent->
front())
2482 auto prevIf = dyn_cast<IfOp>(nextIf->getPrevNode());
2490 Block *nextThen =
nullptr;
2491 Block *nextElse =
nullptr;
2492 if (nextIf.getCondition() == prevIf.getCondition()) {
2493 nextThen = nextIf.thenBlock();
2494 if (!nextIf.getElseRegion().empty())
2495 nextElse = nextIf.elseBlock();
2497 if (arith::XOrIOp notv =
2498 nextIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2499 if (notv.getLhs() == prevIf.getCondition() &&
2501 nextElse = nextIf.thenBlock();
2502 if (!nextIf.getElseRegion().empty())
2503 nextThen = nextIf.elseBlock();
2506 if (arith::XOrIOp notv =
2507 prevIf.getCondition().getDefiningOp<arith::XOrIOp>()) {
2508 if (notv.getLhs() == nextIf.getCondition() &&
2510 nextElse = nextIf.thenBlock();
2511 if (!nextIf.getElseRegion().empty())
2512 nextThen = nextIf.elseBlock();
2516 if (!nextThen && !nextElse)
2519 SmallVector<Value> prevElseYielded;
2520 if (!prevIf.getElseRegion().empty())
2521 prevElseYielded = prevIf.elseYield().getOperands();
2524 for (
auto it : llvm::zip(prevIf.getResults(),
2525 prevIf.thenYield().getOperands(), prevElseYielded))
2526 for (OpOperand &use :
2527 llvm::make_early_inc_range(std::get<0>(it).getUses())) {
2531 use.
set(std::get<1>(it));
2536 use.
set(std::get<2>(it));
2541 SmallVector<Type> mergedTypes(prevIf.getResultTypes());
2542 llvm::append_range(mergedTypes, nextIf.getResultTypes());
2544 IfOp combinedIf = IfOp::create(rewriter, nextIf.getLoc(), mergedTypes,
2545 prevIf.getCondition(),
false);
2546 rewriter.
eraseBlock(&combinedIf.getThenRegion().back());
2549 combinedIf.getThenRegion(),
2550 combinedIf.getThenRegion().begin());
2553 YieldOp thenYield = combinedIf.thenYield();
2554 YieldOp thenYield2 = cast<YieldOp>(nextThen->
getTerminator());
2555 rewriter.
mergeBlocks(nextThen, combinedIf.thenBlock());
2558 SmallVector<Value> mergedYields(thenYield.getOperands());
2559 llvm::append_range(mergedYields, thenYield2.getOperands());
2560 YieldOp::create(rewriter, thenYield2.getLoc(), mergedYields);
2566 combinedIf.getElseRegion(),
2567 combinedIf.getElseRegion().begin());
2570 if (combinedIf.getElseRegion().empty()) {
2572 combinedIf.getElseRegion(),
2573 combinedIf.getElseRegion().
begin());
2575 YieldOp elseYield = combinedIf.elseYield();
2576 YieldOp elseYield2 = cast<YieldOp>(nextElse->
getTerminator());
2577 rewriter.
mergeBlocks(nextElse, combinedIf.elseBlock());
2581 SmallVector<Value> mergedElseYields(elseYield.getOperands());
2582 llvm::append_range(mergedElseYields, elseYield2.getOperands());
2584 YieldOp::create(rewriter, elseYield2.getLoc(), mergedElseYields);
2590 SmallVector<Value> prevValues;
2591 SmallVector<Value> nextValues;
2592 for (
const auto &pair : llvm::enumerate(combinedIf.getResults())) {
2593 if (pair.index() < prevIf.getNumResults())
2594 prevValues.push_back(pair.value());
2596 nextValues.push_back(pair.value());
2605struct RemoveEmptyElseBranch :
public OpRewritePattern<IfOp> {
2606 using OpRewritePattern<IfOp>::OpRewritePattern;
2608 LogicalResult matchAndRewrite(IfOp ifOp,
2609 PatternRewriter &rewriter)
const override {
2611 if (ifOp.getNumResults())
2613 Block *elseBlock = ifOp.elseBlock();
2614 if (!elseBlock || !llvm::hasSingleElement(*elseBlock))
2618 newIfOp.getThenRegion().begin());
2640struct CombineNestedIfs :
public OpRewritePattern<IfOp> {
2641 using OpRewritePattern<IfOp>::OpRewritePattern;
2643 LogicalResult matchAndRewrite(IfOp op,
2644 PatternRewriter &rewriter)
const override {
2645 auto nestedOps = op.thenBlock()->without_terminator();
2647 if (!llvm::hasSingleElement(nestedOps))
2651 if (op.elseBlock() && !llvm::hasSingleElement(*op.elseBlock()))
2654 auto nestedIf = dyn_cast<IfOp>(*nestedOps.begin());
2658 if (nestedIf.elseBlock() && !llvm::hasSingleElement(*nestedIf.elseBlock()))
2661 SmallVector<Value> thenYield(op.thenYield().getOperands());
2662 SmallVector<Value> elseYield;
2664 llvm::append_range(elseYield, op.elseYield().getOperands());
2668 SmallVector<unsigned> elseYieldsToUpgradeToSelect;
2677 for (
const auto &tup : llvm::enumerate(thenYield)) {
2678 if (tup.value().getDefiningOp() == nestedIf) {
2679 auto nestedIdx = llvm::cast<OpResult>(tup.value()).getResultNumber();
2680 if (nestedIf.elseYield().getOperand(nestedIdx) !=
2681 elseYield[tup.index()]) {
2686 thenYield[tup.index()] = nestedIf.thenYield().getOperand(nestedIdx);
2699 if (tup.value().getParentRegion() == &op.getThenRegion()) {
2702 elseYieldsToUpgradeToSelect.push_back(tup.index());
2705 Location loc = op.getLoc();
2706 Value newCondition = arith::AndIOp::create(rewriter, loc, op.getCondition(),
2707 nestedIf.getCondition());
2708 auto newIf = IfOp::create(rewriter, loc, op.getResultTypes(), newCondition);
2711 SmallVector<Value> results;
2712 llvm::append_range(results, newIf.getResults());
2715 for (
auto idx : elseYieldsToUpgradeToSelect)
2717 arith::SelectOp::create(rewriter, op.getLoc(), op.getCondition(),
2718 thenYield[idx], elseYield[idx]);
2720 rewriter.
mergeBlocks(nestedIf.thenBlock(), newIfBlock);
2723 if (!elseYield.empty()) {
2726 YieldOp::create(rewriter, loc, elseYield);
2735void IfOp::getCanonicalizationPatterns(RewritePatternSet &results,
2736 MLIRContext *context) {
2737 results.
add<CombineIfs, CombineNestedIfs, ConditionPropagation,
2738 ConvertTrivialIfToSelect, RemoveEmptyElseBranch,
2739 ReplaceIfYieldWithConditionOrValue>(context);
2741 results, IfOp::getOperationName());
2743 IfOp::getOperationName());
2746Block *IfOp::thenBlock() {
return &getThenRegion().back(); }
2747YieldOp IfOp::thenYield() {
return cast<YieldOp>(&thenBlock()->back()); }
2748Block *IfOp::elseBlock() {
2749 Region &r = getElseRegion();
2754YieldOp IfOp::elseYield() {
return cast<YieldOp>(&elseBlock()->back()); }
2760void ParallelOp::build(
2765 result.addOperands(lowerBounds);
2766 result.addOperands(upperBounds);
2767 result.addOperands(steps);
2768 result.addOperands(initVals);
2770 ParallelOp::getOperandSegmentSizeAttr(),
2772 static_cast<int32_t>(upperBounds.size()),
2773 static_cast<int32_t>(steps.size()),
2774 static_cast<int32_t>(initVals.size())}));
2777 OpBuilder::InsertionGuard guard(builder);
2778 unsigned numIVs = steps.size();
2779 SmallVector<Type, 8> argTypes(numIVs, builder.
getIndexType());
2780 SmallVector<Location, 8> argLocs(numIVs,
result.location);
2781 Region *bodyRegion =
result.addRegion();
2784 if (bodyBuilderFn) {
2786 bodyBuilderFn(builder,
result.location,
2791 if (initVals.empty())
2792 ParallelOp::ensureTerminator(*bodyRegion, builder,
result.location);
2795void ParallelOp::build(
2802 auto wrappedBuilderFn = [&bodyBuilderFn](OpBuilder &nestedBuilder,
2805 bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
2809 wrapper = wrappedBuilderFn;
2815LogicalResult ParallelOp::verify() {
2820 if (stepValues.empty())
2822 "needs at least one tuple element for lowerBound, upperBound and step");
2825 for (Value stepValue : stepValues)
2828 return emitOpError(
"constant step operand must be positive");
2832 Block *body = getBody();
2834 return emitOpError() <<
"expects the same number of induction variables: "
2836 <<
" as bound and step values: " << stepValues.size();
2838 if (!arg.getType().isIndex())
2840 "expects arguments for the induction variable to be of index type");
2844 *
this, getRegion(),
"expects body to terminate with 'scf.reduce'");
2849 auto resultsSize = getResults().size();
2850 auto reductionsSize = reduceOp.getReductions().size();
2851 auto initValsSize = getInitVals().size();
2852 if (resultsSize != reductionsSize)
2853 return emitOpError() <<
"expects number of results: " << resultsSize
2854 <<
" to be the same as number of reductions: "
2856 if (resultsSize != initValsSize)
2857 return emitOpError() <<
"expects number of results: " << resultsSize
2858 <<
" to be the same as number of initial values: "
2860 if (reduceOp.getNumOperands() != initValsSize)
2865 for (int64_t i = 0; i < static_cast<int64_t>(reductionsSize); ++i) {
2866 auto resultType = getOperation()->getResult(i).getType();
2867 auto reductionOperandType = reduceOp.getOperands()[i].getType();
2868 if (resultType != reductionOperandType)
2869 return reduceOp.emitOpError()
2870 <<
"expects type of " << i
2871 <<
"-th reduction operand: " << reductionOperandType
2872 <<
" to be the same as the " << i
2873 <<
"-th result type: " << resultType;
2878ParseResult ParallelOp::parse(OpAsmParser &parser, OperationState &
result) {
2881 SmallVector<OpAsmParser::Argument, 4> ivs;
2886 SmallVector<OpAsmParser::UnresolvedOperand, 4> lower;
2893 SmallVector<OpAsmParser::UnresolvedOperand, 4> upper;
2901 SmallVector<OpAsmParser::UnresolvedOperand, 4> steps;
2909 SmallVector<OpAsmParser::UnresolvedOperand, 4> initVals;
2920 Region *body =
result.addRegion();
2921 for (
auto &iv : ivs)
2928 ParallelOp::getOperandSegmentSizeAttr(),
2930 static_cast<int32_t>(upper.size()),
2931 static_cast<int32_t>(steps.size()),
2932 static_cast<int32_t>(initVals.size())}));
2941 ParallelOp::ensureTerminator(*body, builder,
result.location);
2945void ParallelOp::print(OpAsmPrinter &p) {
2946 p <<
" (" << getBody()->getArguments() <<
") = (" <<
getLowerBound()
2947 <<
") to (" <<
getUpperBound() <<
") step (" << getStep() <<
")";
2948 if (!getInitVals().empty())
2949 p <<
" init (" << getInitVals() <<
")";
2954 (*this)->getAttrs(),
2955 ParallelOp::getOperandSegmentSizeAttr());
2958SmallVector<Region *> ParallelOp::getLoopRegions() {
return {&getRegion()}; }
2960std::optional<SmallVector<Value>> ParallelOp::getLoopInductionVars() {
2961 return SmallVector<Value>{getBody()->getArguments()};
2964std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopLowerBounds() {
2968std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopUpperBounds() {
2972std::optional<SmallVector<OpFoldResult>> ParallelOp::getLoopSteps() {
2977 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
2979 return ParallelOp();
2980 assert(ivArg.getOwner() &&
"unlinked block argument");
2981 auto *containingOp = ivArg.getOwner()->getParentOp();
2982 return dyn_cast<ParallelOp>(containingOp);
2987struct ParallelOpSingleOrZeroIterationDimsFolder
2991 LogicalResult matchAndRewrite(ParallelOp op,
2998 for (
auto [lb,
ub, step, iv] :
2999 llvm::zip(op.getLowerBound(), op.getUpperBound(), op.getStep(),
3000 op.getInductionVars())) {
3001 auto numIterations =
3003 if (numIterations.has_value()) {
3005 if (*numIterations == 0) {
3006 rewriter.
replaceOp(op, op.getInitVals());
3011 if (*numIterations == 1) {
3016 newLowerBounds.push_back(lb);
3017 newUpperBounds.push_back(ub);
3018 newSteps.push_back(step);
3021 if (newLowerBounds.size() == op.getLowerBound().size())
3024 if (newLowerBounds.empty()) {
3027 SmallVector<Value> results;
3028 results.reserve(op.getInitVals().size());
3029 for (
auto &bodyOp : op.getBody()->without_terminator())
3030 rewriter.
clone(bodyOp, mapping);
3031 auto reduceOp = cast<ReduceOp>(op.getBody()->getTerminator());
3032 for (int64_t i = 0, e = reduceOp.getReductions().size(); i < e; ++i) {
3033 Block &reduceBlock = reduceOp.getReductions()[i].front();
3034 auto initValIndex = results.size();
3035 mapping.
map(reduceBlock.
getArgument(0), op.getInitVals()[initValIndex]);
3039 rewriter.
clone(reduceBodyOp, mapping);
3042 cast<ReduceReturnOp>(reduceBlock.
getTerminator()).getResult());
3043 results.push_back(
result);
3051 ParallelOp::create(rewriter, op.getLoc(), newLowerBounds,
3052 newUpperBounds, newSteps, op.getInitVals(),
nullptr);
3058 newOp.getRegion().begin(), mapping);
3059 rewriter.
replaceOp(op, newOp.getResults());
3064struct MergeNestedParallelLoops :
public OpRewritePattern<ParallelOp> {
3065 using OpRewritePattern<ParallelOp>::OpRewritePattern;
3067 LogicalResult matchAndRewrite(ParallelOp op,
3068 PatternRewriter &rewriter)
const override {
3069 Block &outerBody = *op.getBody();
3073 auto innerOp = dyn_cast<ParallelOp>(outerBody.
front());
3078 if (llvm::is_contained(innerOp.getLowerBound(), val) ||
3079 llvm::is_contained(innerOp.getUpperBound(), val) ||
3080 llvm::is_contained(innerOp.getStep(), val))
3084 if (!op.getInitVals().empty() || !innerOp.getInitVals().empty())
3087 auto bodyBuilder = [&](OpBuilder &builder, Location ,
3089 Block &innerBody = *innerOp.getBody();
3090 assert(iterVals.size() ==
3098 builder.
clone(op, mapping);
3101 auto concatValues = [](
const auto &first,
const auto &second) {
3102 SmallVector<Value> ret;
3103 ret.reserve(first.size() + second.size());
3104 ret.assign(first.begin(), first.end());
3105 ret.append(second.begin(), second.end());
3109 auto newLowerBounds =
3110 concatValues(op.getLowerBound(), innerOp.getLowerBound());
3111 auto newUpperBounds =
3112 concatValues(op.getUpperBound(), innerOp.getUpperBound());
3113 auto newSteps = concatValues(op.getStep(), innerOp.getStep());
3124void ParallelOp::getCanonicalizationPatterns(RewritePatternSet &results,
3125 MLIRContext *context) {
3127 .
add<ParallelOpSingleOrZeroIterationDimsFolder, MergeNestedParallelLoops>(
3136void ParallelOp::getSuccessorRegions(
3137 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
3141 regions.push_back(RegionSuccessor(&getRegion()));
3142 regions.push_back(RegionSuccessor(getOperation()));
3149void ReduceOp::build(OpBuilder &builder, OperationState &
result) {}
3151void ReduceOp::build(OpBuilder &builder, OperationState &
result,
3153 result.addOperands(operands);
3154 for (Value v : operands) {
3155 OpBuilder::InsertionGuard guard(builder);
3156 Region *bodyRegion =
result.addRegion();
3158 ArrayRef<Type>{v.getType(), v.getType()},
3163LogicalResult ReduceOp::verifyRegions() {
3164 if (getReductions().size() != getOperands().size())
3165 return emitOpError() <<
"expects number of reduction regions: "
3166 << getReductions().size()
3167 <<
" to be the same as number of reduction operands: "
3168 << getOperands().size();
3171 for (int64_t i = 0, e = getReductions().size(); i < e; ++i) {
3172 auto type = getOperands()[i].getType();
3173 Block &block = getReductions()[i].front();
3175 return emitOpError() << i <<
"-th reduction has an empty body";
3177 llvm::any_of(block.
getArguments(), [&](
const BlockArgument &arg) {
3178 return arg.getType() != type;
3180 return emitOpError() <<
"expected two block arguments with type " << type
3181 <<
" in the " << i <<
"-th reduction region";
3185 return emitOpError(
"reduction bodies must be terminated with an "
3186 "'scf.reduce.return' op");
3193ReduceOp::getMutableSuccessorOperands(RegionSuccessor point) {
3195 return MutableOperandRange(getOperation(), 0, 0);
3202LogicalResult ReduceReturnOp::verify() {
3205 Block *reductionBody = getOperation()->getBlock();
3207 assert(isa<ReduceOp>(reductionBody->
getParentOp()) &&
"expected scf.reduce");
3209 if (expectedResultType != getResult().
getType())
3210 return emitOpError() <<
"must have type " << expectedResultType
3211 <<
" (the type of the reduction inputs)";
3219void WhileOp::build(::mlir::OpBuilder &odsBuilder,
3220 ::mlir::OperationState &odsState,
TypeRange resultTypes,
3221 ValueRange inits, BodyBuilderFn beforeBuilder,
3222 BodyBuilderFn afterBuilder) {
3226 OpBuilder::InsertionGuard guard(odsBuilder);
3229 SmallVector<Location, 4> beforeArgLocs;
3230 beforeArgLocs.reserve(inits.size());
3231 for (Value operand : inits) {
3232 beforeArgLocs.push_back(operand.getLoc());
3235 Region *beforeRegion = odsState.
addRegion();
3237 inits.getTypes(), beforeArgLocs);
3242 SmallVector<Location, 4> afterArgLocs(resultTypes.size(), odsState.
location);
3244 Region *afterRegion = odsState.
addRegion();
3246 resultTypes, afterArgLocs);
3252ConditionOp WhileOp::getConditionOp() {
3253 return cast<ConditionOp>(getBeforeBody()->getTerminator());
3256YieldOp WhileOp::getYieldOp() {
3257 return cast<YieldOp>(getAfterBody()->getTerminator());
3260std::optional<MutableArrayRef<OpOperand>> WhileOp::getYieldedValuesMutable() {
3261 return getYieldOp().getResultsMutable();
3265 return getBeforeBody()->getArguments();
3269 return getAfterBody()->getArguments();
3273 return getBeforeArguments();
3276OperandRange WhileOp::getEntrySuccessorOperands(RegionSuccessor successor) {
3278 "WhileOp is expected to branch only to the first region");
3282void WhileOp::getSuccessorRegions(RegionBranchPoint point,
3283 SmallVectorImpl<RegionSuccessor> ®ions) {
3286 regions.emplace_back(&getBefore());
3290 assert(llvm::is_contained(
3291 {&getAfter(), &getBefore()},
3293 "there are only two regions in a WhileOp");
3297 regions.emplace_back(&getBefore());
3301 regions.push_back(RegionSuccessor(getOperation()));
3302 regions.emplace_back(&getAfter());
3305ValueRange WhileOp::getSuccessorInputs(RegionSuccessor successor) {
3307 return getOperation()->getResults();
3308 if (successor == &getBefore())
3309 return getBefore().getArguments();
3310 if (successor == &getAfter())
3311 return getAfter().getArguments();
3312 llvm_unreachable(
"invalid region successor");
3315SmallVector<Region *> WhileOp::getLoopRegions() {
3316 return {&getBefore(), &getAfter()};
3326ParseResult scf::WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
3327 SmallVector<OpAsmParser::Argument, 4> regionArgs;
3328 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
3329 Region *before =
result.addRegion();
3330 Region *after =
result.addRegion();
3332 OptionalParseResult listResult =
3337 FunctionType functionType;
3342 result.addTypes(functionType.getResults());
3344 if (functionType.getNumInputs() != operands.size()) {
3346 <<
"expected as many input types as operands " <<
"(expected "
3347 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
3357 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
3358 regionArgs[i].type = functionType.getInput(i);
3360 return failure(parser.
parseRegion(*before, regionArgs) ||
3366void scf::WhileOp::print(OpAsmPrinter &p) {
3380template <
typename OpTy>
3383 if (left.size() != right.size())
3384 return op.emitOpError(
"expects the same number of ") << message;
3386 for (
unsigned i = 0, e = left.size(); i < e; ++i) {
3387 if (left[i] != right[i]) {
3390 diag.attachNote() <<
"for argument " << i <<
", found " << left[i]
3391 <<
" and " << right[i];
3399LogicalResult scf::WhileOp::verify() {
3402 "expects the 'before' region to terminate with 'scf.condition'");
3403 if (!beforeTerminator)
3408 "expects the 'after' region to terminate with 'scf.yield'");
3409 return success(afterTerminator !=
nullptr);
3446struct WhileMoveIfDown :
public OpRewritePattern<scf::WhileOp> {
3447 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3449 LogicalResult matchAndRewrite(scf::WhileOp op,
3450 PatternRewriter &rewriter)
const override {
3451 auto conditionOp = op.getConditionOp();
3459 auto ifOp = dyn_cast_or_null<scf::IfOp>(conditionOp->getPrevNode());
3465 if (!ifOp || ifOp.getCondition() != conditionOp.getCondition() ||
3466 (ifOp.elseBlock() && !ifOp.elseBlock()->without_terminator().empty()))
3469 assert((ifOp->use_empty() || (llvm::all_equal(ifOp->getUsers()) &&
3470 *ifOp->user_begin() == conditionOp)) &&
3471 "ifOp has unexpected uses");
3473 Location loc = op.getLoc();
3477 for (
auto [idx, arg] : llvm::enumerate(conditionOp.getArgs())) {
3478 auto it = llvm::find(ifOp->getResults(), arg);
3479 if (it != ifOp->getResults().end()) {
3480 size_t ifOpIdx = it.getIndex();
3481 Value thenValue = ifOp.thenYield()->getOperand(ifOpIdx);
3482 Value elseValue = ifOp.elseYield()->getOperand(ifOpIdx);
3492 if (&op.getBefore() == operand->get().getParentRegion())
3493 additionalUsedValuesSet.insert(operand->get());
3497 auto additionalUsedValues = additionalUsedValuesSet.getArrayRef();
3498 auto additionalValueTypes = llvm::map_to_vector(
3499 additionalUsedValues, [](Value val) {
return val.
getType(); });
3500 size_t additionalValueSize = additionalUsedValues.size();
3501 SmallVector<Type> newResultTypes(op.getResultTypes());
3502 newResultTypes.append(additionalValueTypes);
3505 scf::WhileOp::create(rewriter, loc, newResultTypes, op.getInits());
3508 newWhileOp.getBefore().takeBody(op.getBefore());
3509 newWhileOp.getAfter().takeBody(op.getAfter());
3510 newWhileOp.getAfter().addArguments(
3511 additionalValueTypes,
3512 SmallVector<Location>(additionalValueSize, loc));
3516 conditionOp.getArgsMutable().append(additionalUsedValues);
3522 additionalUsedValues,
3523 newWhileOp.getAfterArguments().take_back(additionalValueSize),
3524 [&](OpOperand &use) {
3525 return ifOp.getThenRegion().isAncestor(
3526 use.getOwner()->getParentRegion());
3530 rewriter.
eraseOp(ifOp.thenYield());
3532 newWhileOp.getAfterBody()->begin());
3535 newWhileOp->getResults().drop_back(additionalValueSize));
3559struct WhileConditionTruth :
public OpRewritePattern<WhileOp> {
3560 using OpRewritePattern<WhileOp>::OpRewritePattern;
3562 LogicalResult matchAndRewrite(WhileOp op,
3563 PatternRewriter &rewriter)
const override {
3564 auto term = op.getConditionOp();
3568 Value constantTrue =
nullptr;
3570 bool replaced =
false;
3571 for (
auto yieldedAndBlockArgs :
3572 llvm::zip(term.getArgs(), op.getAfterArguments())) {
3573 if (std::get<0>(yieldedAndBlockArgs) == term.getCondition()) {
3574 if (!std::get<1>(yieldedAndBlockArgs).use_empty()) {
3576 constantTrue = arith::ConstantOp::create(
3577 rewriter, op.getLoc(), term.getCondition().getType(),
3612struct WhileCmpCond :
public OpRewritePattern<scf::WhileOp> {
3613 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
3615 LogicalResult matchAndRewrite(scf::WhileOp op,
3616 PatternRewriter &rewriter)
const override {
3617 using namespace scf;
3618 auto cond = op.getConditionOp();
3619 auto cmp = cond.getCondition().getDefiningOp<arith::CmpIOp>();
3622 bool changed =
false;
3623 for (
auto tup : llvm::zip(cond.getArgs(), op.getAfterArguments())) {
3624 for (
size_t opIdx = 0; opIdx < 2; opIdx++) {
3625 if (std::get<0>(tup) != cmp.getOperand(opIdx))
3628 llvm::make_early_inc_range(std::get<1>(tup).getUses())) {
3629 auto cmp2 = dyn_cast<arith::CmpIOp>(u.getOwner());
3633 if (cmp2.getOperand(1 - opIdx) != cmp.getOperand(1 - opIdx))
3636 if (cmp2.getPredicate() == cmp.getPredicate())
3637 samePredicate =
true;
3638 else if (cmp2.getPredicate() ==
3639 arith::invertPredicate(cmp.getPredicate()))
3640 samePredicate =
false;
3656static std::optional<SmallVector<unsigned>> getArgsMapping(
ValueRange args1,
3658 if (args1.size() != args2.size())
3659 return std::nullopt;
3661 SmallVector<unsigned> ret(args1.size());
3662 for (
auto &&[i, arg1] : llvm::enumerate(args1)) {
3663 auto it = llvm::find(args2, arg1);
3664 if (it == args2.end())
3665 return std::nullopt;
3667 ret[std::distance(args2.begin(), it)] =
static_cast<unsigned>(i);
3674 llvm::SmallDenseSet<Value> set;
3675 for (Value arg : args) {
3676 if (!set.insert(arg).second)
3686struct WhileOpAlignBeforeArgs :
public OpRewritePattern<WhileOp> {
3689 LogicalResult matchAndRewrite(WhileOp loop,
3690 PatternRewriter &rewriter)
const override {
3691 auto *oldBefore = loop.getBeforeBody();
3692 ConditionOp oldTerm = loop.getConditionOp();
3693 ValueRange beforeArgs = oldBefore->getArguments();
3695 if (beforeArgs == termArgs)
3698 if (hasDuplicates(termArgs))
3701 auto mapping = getArgsMapping(beforeArgs, termArgs);
3706 OpBuilder::InsertionGuard g(rewriter);
3712 auto *oldAfter = loop.getAfterBody();
3714 SmallVector<Type> newResultTypes(beforeArgs.size());
3715 for (
auto &&[i, j] : llvm::enumerate(*mapping))
3716 newResultTypes[j] = loop.getResult(i).getType();
3718 auto newLoop = WhileOp::create(
3719 rewriter, loop.getLoc(), newResultTypes, loop.getInits(),
3721 auto *newBefore = newLoop.getBeforeBody();
3722 auto *newAfter = newLoop.getAfterBody();
3724 SmallVector<Value> newResults(beforeArgs.size());
3725 SmallVector<Value> newAfterArgs(beforeArgs.size());
3726 for (
auto &&[i, j] : llvm::enumerate(*mapping)) {
3727 newResults[i] = newLoop.getResult(j);
3728 newAfterArgs[i] = newAfter->getArgument(j);
3732 newBefore->getArguments());
3742void WhileOp::getCanonicalizationPatterns(RewritePatternSet &results,
3743 MLIRContext *context) {
3744 results.
add<WhileConditionTruth, WhileCmpCond, WhileOpAlignBeforeArgs,
3745 WhileMoveIfDown>(context);
3747 results, WhileOp::getOperationName());
3749 WhileOp::getOperationName());
3763 Region ®ion = *caseRegions.emplace_back(std::make_unique<Region>());
3766 caseValues.push_back(value);
3775 for (
auto [value, region] : llvm::zip(cases.
asArrayRef(), caseRegions)) {
3777 p <<
"case " << value <<
' ';
3782LogicalResult scf::IndexSwitchOp::verify() {
3783 if (getCases().size() != getCaseRegions().size()) {
3785 << getCaseRegions().size() <<
" case regions but "
3786 << getCases().size() <<
" case values";
3790 for (int64_t value : getCases())
3791 if (!valueSet.insert(value).second)
3792 return emitOpError(
"has duplicate case value: ") << value;
3793 auto verifyRegion = [&](Region ®ion,
const Twine &name) -> LogicalResult {
3794 auto yield = dyn_cast<YieldOp>(region.
front().
back());
3796 return emitOpError(
"expected region to end with scf.yield, but got ")
3799 if (yield.getNumOperands() != getNumResults()) {
3800 return (
emitOpError(
"expected each region to return ")
3801 << getNumResults() <<
" values, but " << name <<
" returns "
3802 << yield.getNumOperands())
3803 .attachNote(yield.getLoc())
3804 <<
"see yield operation here";
3806 for (
auto [idx,
result, operand] :
3807 llvm::enumerate(getResultTypes(), yield.getOperands())) {
3809 return yield.emitOpError() <<
"operand " << idx <<
" is null\n";
3810 if (
result == operand.getType())
3813 << idx <<
" of each region to be " <<
result)
3814 .attachNote(yield.getLoc())
3815 << name <<
" returns " << operand.getType() <<
" here";
3822 for (
auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
3829unsigned scf::IndexSwitchOp::getNumCases() {
return getCases().size(); }
3831Block &scf::IndexSwitchOp::getDefaultBlock() {
3832 return getDefaultRegion().front();
3835Block &scf::IndexSwitchOp::getCaseBlock(
unsigned idx) {
3836 assert(idx < getNumCases() &&
"case index out-of-bounds");
3837 return getCaseRegions()[idx].front();
3840void IndexSwitchOp::getSuccessorRegions(
3841 RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
3844 successors.push_back(RegionSuccessor(getOperation()));
3848 llvm::append_range(successors, getRegions());
3851ValueRange IndexSwitchOp::getSuccessorInputs(RegionSuccessor successor) {
3856void IndexSwitchOp::getEntrySuccessorRegions(
3857 ArrayRef<Attribute> operands,
3858 SmallVectorImpl<RegionSuccessor> &successors) {
3859 FoldAdaptor adaptor(operands, *
this);
3862 auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
3864 llvm::append_range(successors, getRegions());
3870 for (
auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
3871 if (caseValue == arg.getInt()) {
3872 successors.emplace_back(&caseRegion);
3876 successors.emplace_back(&getDefaultRegion());
3879void IndexSwitchOp::getRegionInvocationBounds(
3880 ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
3881 auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
3882 if (!operandValue) {
3884 bounds.append(getNumRegions(), InvocationBounds(0, 1));
3888 unsigned liveIndex = getNumRegions() - 1;
3889 const auto *it = llvm::find(getCases(), operandValue.getInt());
3890 if (it != getCases().end())
3891 liveIndex = std::distance(getCases().begin(), it);
3892 for (
unsigned i = 0, e = getNumRegions(); i < e; ++i)
3893 bounds.emplace_back(0, i == liveIndex);
3896void IndexSwitchOp::getCanonicalizationPatterns(RewritePatternSet &results,
3897 MLIRContext *context) {
3899 results, IndexSwitchOp::getOperationName());
3901 results, IndexSwitchOp::getOperationName());
3908#define GET_OP_CLASSES
3909#include "mlir/Dialect/SCF/IR/SCFOps.cpp.inc"
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
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 LogicalResult verifyTypeRangesMatch(OpTy op, TypeRange left, TypeRange right, StringRef message)
Verifies that two ranges of types match, i.e.
static void printInitializationList(OpAsmPrinter &p, Block::BlockArgListType blocksArgs, ValueRange initializers, StringRef prefix="")
Prints the initialization list in the form of <prefix>(inner = outer, inner2 = outer2,...
static TerminatorTy verifyAndGetTerminator(Operation *op, Region ®ion, StringRef errorMessage)
Verifies that the first block of the given region is terminated by a TerminatorTy.
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static std::string diag(const llvm::Value &value)
@ Paren
Parens surrounding zero or more operands.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
virtual ParseResult parseArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an arrow followed by a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
void printOptionalArrowTypeList(TypeRange &&types)
Print an optional arrow followed by a type list.
virtual void printNewline()
Print a newline and indent the printer to the start of the current operation/attribute/type.
This class represents an argument of a Block.
unsigned getArgNumber() const
Returns the number of this argument.
Block represents an ordered list of Operations.
MutableArrayRef< BlockArgument > BlockArgListType
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
iterator_range< args_iterator > addArguments(TypeRange types, ArrayRef< Location > locs)
Add one argument to the argument list for each type specified in the list.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
BlockArgListType getArguments()
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
bool getValue() const
Return the boolean value of this attribute.
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
IntegerType getIntegerType(unsigned width)
BoolAttr getBoolAttr(bool value)
MLIRContext * getContext() const
virtual Operation * materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc)
Registered hook to materialize a single constant operation from a given attribute value with the desi...
This is a utility class for mapping one set of IR entities to another.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
IRValueT get() const
Return the current value being used by this operand.
void set(IRValueT newValue)
Set the current value being used by this operand.
This class represents a diagnostic that is inflight and set to be reported.
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.
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
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'.
OperationName getName()
The name of an operation is the key identifier for it.
Region * getParentRegion()
Returns the region to which the instruction belongs.
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
ParseResult value() const
Access the internal ParseResult value.
bool has_value() const
Returns true if we contain a valid ParseResult value.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
bool isParent() const
Returns true if branching from the parent op.
RegionBranchTerminatorOpInterface getTerminatorPredecessorOrNull() const
Returns the terminator if branching from a region.
This class provides an abstraction over the different types of ranges over Regions.
This class represents a successor of a region.
Region * getSuccessor() const
Return the given region successor.
bool isOperation() const
Return true if the successor is an operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
unsigned getNumArguments()
BlockArgument getArgument(unsigned i)
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
Block * splitBlock(Block *block, Block::iterator before)
Split the operations starting at "before" (inclusive) out of the given block into a new block,...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void finalizeOpModification(Operation *op)
This method is used to signal the end of an in-place modification of the given operation.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
Operation * eraseOpResults(Operation *op, const BitVector &eraseIndices)
Erase the specified results of the given operation.
virtual void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues={})
Inline the operations of block 'source' into block 'dest' before the given position.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
void inlineRegionBefore(Region ®ion, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
virtual void startOpModification(Operation *op)
This method is used to notify the rewriter that an in-place operation modification is about to happen...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
This class provides an abstraction over the different types of ranges over Values.
type_range getTypes() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
user_range getUsers() const
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Region * getParentRegion()
Return the Region in which this Value is defined.
ArrayRef< T > asArrayRef() const
Operation * getOwner() const
Return the owner of this operand.
constexpr auto RecursivelySpeculatable
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto NotSpeculatable
static Value defaultReplBuilderFn(OpBuilder &builder, Location loc, Value value)
Default implementation of the non-successor-input replacement builder function.
static LogicalResult defaultMatcherFn(Operation *op)
Default implementation of the pattern matcher function.
StringRef getMappingAttrName()
Name of the mapping attribute produced by loop mappers.
ParallelOp getParallelForInductionVarOwner(Value val)
Returns the parallel loop parent of an induction variable.
void buildTerminatedBody(OpBuilder &builder, Location loc)
Default callback for IfOp builders. Inserts a yield without arguments.
LoopNest buildLoopNest(OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs, ValueRange steps, ValueRange iterArgs, function_ref< ValueVector(OpBuilder &, Location, ValueRange, ValueRange)> bodyBuilder=nullptr)
Creates a perfect nest of "for" loops, i.e.
bool insideMutuallyExclusiveBranches(Operation *a, Operation *b)
Return true if ops a and b (or their ancestors) are in mutually exclusive regions/blocks of an IfOp.
void promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
std::optional< llvm::APSInt > computeUbMinusLb(Value lb, Value ub, bool isSigned)
Helper function to compute the difference between two values.
ForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
SmallVector< Value > ValueVector
An owning vector of values, handy to return from functions.
llvm::function_ref< Value(OpBuilder &, Location loc, Type, Value)> ValueTypeCastFnTy
Perform a replacement of one iter OpOperand of an scf.for to the replacement value with a different t...
ForallOp getForallOpThreadIndexOwner(Value val)
Returns the ForallOp parent of an thread index variable.
SmallVector< Value > replaceAndCastForOpIterArg(RewriterBase &rewriter, scf::ForOp forOp, OpOperand &operand, Value replacement, const ValueTypeCastFnTy &castFn)
bool preservesStaticInformation(Type source, Type target)
Returns true if target is a ranked tensor type that preserves static information available in the sou...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
std::function< SmallVector< Value >( OpBuilder &b, Location loc, ArrayRef< BlockArgument > newBbArgs)> NewYieldValuesFn
A function that returns the additional yielded values during replaceWithAdditionalYields.
ParseResult parseDynamicIndexList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &values, DenseI64ArrayAttr &integers, DenseBoolArrayAttr &scalableFlags, SmallVectorImpl< Type > *valueTypes=nullptr, AsmParser::Delimiter delimiter=AsmParser::Delimiter::Square)
Parser hooks for custom directive in assemblyFormat.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
llvm::SetVector< T, Vector, Set, N > SetVector
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
void populateRegionBranchOpInterfaceInliningPattern(RewritePatternSet &patterns, StringRef opName, NonSuccessorInputReplacementBuilderFn replBuilderFn=detail::defaultReplBuilderFn, PatternMatcherFn matcherFn=detail::defaultMatcherFn, PatternBenefit benefit=1)
Populate a pattern that inlines the body of region branch ops when there is a single acyclic path thr...
LogicalResult verifyListOfOperandsOrIntegers(Operation *op, StringRef name, unsigned expectedNumElements, ArrayRef< int64_t > attr, ValueRange values)
Verify that a the values has as many elements as the number of entries in attr for which isDynamic ev...
void populateRegionBranchOpInterfaceCanonicalizationPatterns(RewritePatternSet &patterns, StringRef opName, PatternBenefit benefit=1)
Populate canonicalization patterns that simplify successor operands/inputs of region branch operation...
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
void visitUsedValuesDefinedAbove(Region ®ion, Region &limit, function_ref< void(OpOperand *)> callback)
Calls callback for each use of a value within region or its descendants that was defined at the ances...
llvm::function_ref< Fn > function_ref
SmallVector< NamedAttribute > getPrunedAttributeList(Operation *op, ArrayRef< StringRef > elidedAttrs)
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.