25#include "llvm/ADT/APInt.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/SmallVector.h"
28#include "llvm/ADT/SmallVectorExtras.h"
29#include "llvm/Support/DebugLog.h"
34#define DEBUG_TYPE "scf-utils"
39 bool replaceIterOperandsUsesInLoop) {
45 assert(loopNest.size() <= 10 &&
46 "exceeded recursion limit when yielding value from loop nest");
78 if (loopNest.size() == 1) {
80 cast<scf::ForOp>(*loopNest.back().replaceWithAdditionalYields(
81 rewriter, newIterOperands, replaceIterOperandsUsesInLoop,
83 return {innerMostLoop};
93 innerNewBBArgs, newYieldValuesFn,
94 replaceIterOperandsUsesInLoop);
95 return llvm::map_to_vector(
96 newLoopNest.front().getResults().take_back(innerNewBBArgs.size()),
99 scf::ForOp outerMostLoop =
100 cast<scf::ForOp>(*loopNest.front().replaceWithAdditionalYields(
101 rewriter, newIterOperands, replaceIterOperandsUsesInLoop, fn));
102 newLoopNest.insert(newLoopNest.begin(), outerMostLoop);
119 func::CallOp *callOp) {
120 assert(!funcName.empty() &&
"funcName cannot be empty");
134 ValueRange outlinedValues(captures.getArrayRef());
141 outlinedFuncArgTypes.push_back(arg.getType());
142 outlinedFuncArgLocs.push_back(arg.getLoc());
144 for (
Value value : outlinedValues) {
145 outlinedFuncArgTypes.push_back(value.getType());
146 outlinedFuncArgLocs.push_back(value.getLoc());
148 FunctionType outlinedFuncType =
149 FunctionType::get(rewriter.
getContext(), outlinedFuncArgTypes,
152 func::FuncOp::create(rewriter, loc, funcName, outlinedFuncType);
153 Block *outlinedFuncBody = outlinedFunc.addEntryBlock();
158 auto outlinedFuncBlockArgs = outlinedFuncBody->
getArguments();
163 originalBlock, outlinedFuncBody,
164 outlinedFuncBlockArgs.take_front(numOriginalBlockArguments));
167 func::ReturnOp::create(rewriter, loc, originalTerminator->
getResultTypes(),
174 ®ion, region.
begin(),
175 TypeRange{outlinedFuncArgTypes}.take_front(numOriginalBlockArguments),
177 .take_front(numOriginalBlockArguments));
182 llvm::append_range(callValues, newBlock->
getArguments());
183 llvm::append_range(callValues, outlinedValues);
184 auto call = func::CallOp::create(rewriter, loc, outlinedFunc, callValues);
193 rewriter.
clone(*originalTerminator, bvm);
194 rewriter.
eraseOp(originalTerminator);
199 for (
auto it : llvm::zip(outlinedValues, outlinedFuncBlockArgs.take_back(
200 outlinedValues.size()))) {
201 Value orig = std::get<0>(it);
202 Value repl = std::get<1>(it);
211 return outlinedFunc->isProperAncestor(opOperand.
getOwner());
219 func::FuncOp *thenFn, StringRef thenFnName,
220 func::FuncOp *elseFn, StringRef elseFnName) {
223 FailureOr<func::FuncOp> outlinedFuncOpOrFailure;
224 if (thenFn && !ifOp.getThenRegion().empty()) {
226 rewriter, loc, ifOp.getThenRegion(), thenFnName);
227 if (failed(outlinedFuncOpOrFailure))
229 *thenFn = *outlinedFuncOpOrFailure;
231 if (elseFn && !ifOp.getElseRegion().empty()) {
233 rewriter, loc, ifOp.getElseRegion(), elseFnName);
234 if (failed(outlinedFuncOpOrFailure))
236 *elseFn = *outlinedFuncOpOrFailure;
243 assert(rootOp !=
nullptr &&
"Root operation must not be a nullptr.");
244 bool rootEnclosesPloops =
false;
246 for (
Block &block : region.getBlocks()) {
249 rootEnclosesPloops |= enclosesPloops;
250 if (
auto ploop = dyn_cast<scf::ParallelOp>(op)) {
251 rootEnclosesPloops =
true;
260 return rootEnclosesPloops;
268 assert(divisor > 0 &&
"expected positive divisor");
270 "expected integer or index-typed value");
272 Value divisorMinusOneCst = arith::ConstantOp::create(
274 Value divisorCst = arith::ConstantOp::create(
276 Value sum = arith::AddIOp::create(builder, loc, dividend, divisorMinusOneCst);
277 return arith::DivUIOp::create(builder, loc, sum, divisorCst);
287 "expected integer or index-typed value");
288 Value cstOne = arith::ConstantOp::create(
290 Value divisorMinusOne = arith::SubIOp::create(builder, loc, divisor, cstOne);
291 Value sum = arith::AddIOp::create(builder, loc, dividend, divisorMinusOne);
292 return arith::DivUIOp::create(builder, loc, sum, divisor);
296 Block *loopBodyBlock,
Value iv, uint64_t unrollFactor,
304 auto findOriginalSrcOp =
309 while (srcOp && clonedToSrcOpsMap.
contains(srcOp))
310 srcOp = clonedToSrcOpsMap.
lookup(srcOp);
320 annotateFn = noopAnnotateFn;
330 for (
unsigned i = 1; i < unrollFactor; i++) {
333 operandMap.
map(iterArgs, lastYielded);
338 Value ivUnroll = ivRemapFn(i, iv, builder);
339 operandMap.
map(iv, ivUnroll);
343 for (
auto it = loopBodyBlock->
begin(); it != std::next(srcBlockEnd); it++) {
346 annotateFn(i, clonedOp, builder);
347 if (clonedToSrcOpsMap)
348 clonedToSrcOpsMap->
map(clonedOp,
349 findOriginalSrcOp(srcOp, *clonedToSrcOpsMap));
353 for (
unsigned i = 0, e = lastYielded.size(); i < e; i++)
359 for (
auto it = loopBodyBlock->
begin(); it != std::next(srcBlockEnd); it++)
360 annotateFn(0, &*it, builder);
369 scf::ForOp forOp, uint64_t unrollFactor,
371 bool shouldPromoteIfSingleIteration) {
372 assert(unrollFactor > 0 &&
"expected positive unroll factor");
375 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
382 auto loc = forOp.getLoc();
383 Value step = forOp.getStep();
384 Value upperBoundUnrolled;
386 bool generateEpilogueLoop =
true;
388 std::optional<APInt> constTripCount = forOp.getStaticTripCount();
389 if (constTripCount) {
391 bool isUnsignedLoop = forOp.getUnsignedCmp();
397 if (isUnsignedLoop) {
398 if (
auto intTy = dyn_cast<IntegerType>(forOp.getUpperBound().getType()))
399 if (intTy.getWidth() >= 64)
404 assert(apInt &&
"expected constant loop bound");
405 return isUnsignedLoop ?
static_cast<int64_t>(apInt->first.getZExtValue())
406 : apInt->first.getSExtValue();
408 int64_t lbCst = getLoopBound(forOp.getLowerBound());
409 int64_t ubCst = getLoopBound(forOp.getUpperBound());
410 int64_t stepCst = getLoopBound(step);
411 if (unrollFactor == 1) {
412 if (shouldPromoteIfSingleIteration && constTripCount->isOne() &&
413 failed(forOp.promoteIfSingleIteration(rewriter)))
418 uint64_t tripCount = constTripCount->getZExtValue();
419 uint64_t tripCountEvenMultiple = tripCount - tripCount % unrollFactor;
420 int64_t upperBoundUnrolledCst = lbCst + tripCountEvenMultiple * stepCst;
421 int64_t stepUnrolledCst = stepCst * unrollFactor;
424 generateEpilogueLoop = upperBoundUnrolledCst < ubCst;
425 if (generateEpilogueLoop)
426 upperBoundUnrolled = arith::ConstantOp::create(
429 upperBoundUnrolledCst));
431 upperBoundUnrolled = forOp.getUpperBound();
439 bool mainLoopHasNoIter = (tripCountEvenMultiple == 0);
440 bool stepUnchanged = (stepCst == stepUnrolledCst);
442 (mainLoopHasNoIter || stepUnchanged)
444 : arith::ConstantOp::create(boundsBuilder, loc,
446 step.
getType(), stepUnrolledCst));
451 auto lowerBound = forOp.getLowerBound();
452 auto upperBound = forOp.getUpperBound();
454 arith::SubIOp::create(boundsBuilder, loc, upperBound, lowerBound);
456 Value unrollFactorCst = arith::ConstantOp::create(
460 arith::RemSIOp::create(boundsBuilder, loc, tripCount, unrollFactorCst);
462 Value tripCountEvenMultiple =
463 arith::SubIOp::create(boundsBuilder, loc, tripCount, tripCountRem);
465 upperBoundUnrolled = arith::AddIOp::create(
466 boundsBuilder, loc, lowerBound,
467 arith::MulIOp::create(boundsBuilder, loc, tripCountEvenMultiple, step));
470 arith::MulIOp::create(boundsBuilder, loc, step, unrollFactorCst);
476 if (generateEpilogueLoop) {
477 OpBuilder epilogueBuilder(forOp->getContext());
479 auto epilogueForOp = cast<scf::ForOp>(epilogueBuilder.
clone(*forOp));
480 epilogueForOp.setLowerBound(upperBoundUnrolled);
483 auto results = forOp.getResults();
484 auto epilogueResults = epilogueForOp.getResults();
486 for (
auto e : llvm::zip(results, epilogueResults)) {
487 std::get<0>(e).replaceAllUsesWith(std::get<1>(e));
489 epilogueForOp->setOperands(epilogueForOp.getNumControlOperands(),
490 epilogueForOp.getInitArgs().size(), results);
491 if (!shouldPromoteIfSingleIteration ||
492 epilogueForOp.promoteIfSingleIteration(rewriter).failed())
497 forOp.setUpperBound(upperBoundUnrolled);
498 forOp.setStep(stepUnrolled);
500 auto iterArgs =
ValueRange(forOp.getRegionIterArgs());
501 auto yieldedValues = forOp.getBody()->getTerminator()->getOperands();
504 forOp.getBody(), forOp.getInductionVar(), unrollFactor,
507 auto stride = arith::MulIOp::create(
509 arith::ConstantOp::create(b, loc,
510 b.getIntegerAttr(iv.getType(), i)));
511 return arith::AddIOp::create(b, loc, iv, stride);
513 annotateFn, iterArgs, yieldedValues);
516 if (!shouldPromoteIfSingleIteration ||
517 forOp.promoteIfSingleIteration(rewriter).failed())
525 std::optional<APInt> mayBeConstantTripCount = forOp.getStaticTripCount();
526 if (!mayBeConstantTripCount.has_value())
528 const APInt &tripCount = *mayBeConstantTripCount;
529 if (tripCount.isZero())
531 if (tripCount.isOne())
532 return forOp.promoteIfSingleIteration(rewriter);
539 auto walkResult = forOp.walk([&](scf::ForOp innerForOp) {
540 if (!forOp.isDefinedOutsideOfLoop(innerForOp.getLowerBound()) ||
541 !forOp.isDefinedOutsideOfLoop(innerForOp.getUpperBound()) ||
542 !forOp.isDefinedOutsideOfLoop(innerForOp.getStep()))
547 return !walkResult.wasInterrupted();
552 uint64_t unrollJamFactor) {
553 assert(unrollJamFactor > 0 &&
"unroll jam factor should be positive");
555 if (unrollJamFactor == 1)
561 LDBG() <<
"failed to unroll and jam: inner bounds are not invariant";
566 if (forOp->getNumResults() > 0) {
567 LDBG() <<
"failed to unroll and jam: unsupported loop with results";
573 std::optional<APInt> tripCount = forOp.getStaticTripCount();
574 if (!tripCount.has_value()) {
576 LDBG() <<
"failed to unroll and jam: trip count could not be determined";
579 uint64_t tripCountValue = tripCount->getZExtValue();
580 if (tripCountValue == 0)
582 if (unrollJamFactor > tripCountValue) {
583 LDBG() <<
"unroll and jam factor is greater than trip count, set factor to "
586 unrollJamFactor = tripCountValue;
587 }
else if (tripCountValue % unrollJamFactor != 0) {
588 LDBG() <<
"failed to unroll and jam: unsupported trip count that is not a "
589 "multiple of unroll jam factor";
594 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
604 forOp.walk([&](scf::ForOp innerForOp) { innerLoops.push_back(innerForOp); });
615 for (scf::ForOp oldForOp : innerLoops) {
617 ValueRange oldIterOperands = oldForOp.getInits();
618 ValueRange oldIterArgs = oldForOp.getRegionIterArgs();
620 cast<scf::YieldOp>(oldForOp.getBody()->getTerminator()).getOperands();
623 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
624 dupIterOperands.append(oldIterOperands.begin(), oldIterOperands.end());
625 dupYieldOperands.append(oldYieldOperands.begin(), oldYieldOperands.end());
629 bool forOpReplaced = oldForOp == forOp;
630 scf::ForOp newForOp =
631 cast<scf::ForOp>(*oldForOp.replaceWithAdditionalYields(
632 rewriter, dupIterOperands,
false,
634 return dupYieldOperands;
636 newInnerLoops.push_back(newForOp);
641 ValueRange newIterArgs = newForOp.getRegionIterArgs();
642 unsigned oldNumIterArgs = oldIterArgs.size();
643 ValueRange newResults = newForOp.getResults();
644 unsigned oldNumResults = newResults.size() / unrollJamFactor;
645 assert(oldNumIterArgs == oldNumResults &&
646 "oldNumIterArgs must be the same as oldNumResults");
647 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
648 for (
unsigned j = 0;
j < oldNumIterArgs; ++
j) {
652 operandMaps[i - 1].map(newIterArgs[
j],
653 newIterArgs[i * oldNumIterArgs +
j]);
654 operandMaps[i - 1].map(newResults[
j],
655 newResults[i * oldNumResults +
j]);
662 int64_t step = forOp.getConstantStep()->getSExtValue();
664 forOp.getLoc(), forOp.getStep(),
666 forOp.getLoc(), rewriter.
getIndexAttr(unrollJamFactor)));
667 forOp.setStep(newStep);
668 auto forOpIV = forOp.getInductionVar();
671 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
672 for (
auto &subBlock : subBlocks) {
675 OpBuilder builder(subBlock.first->getBlock(), std::next(subBlock.second));
679 if (!forOpIV.use_empty()) {
684 builder.
createOrFold<arith::AddIOp>(forOp.getLoc(), forOpIV, ivTag);
685 operandMaps[i - 1].map(forOpIV, ivUnroll);
688 for (
auto it = subBlock.first; it != std::next(subBlock.second); ++it)
689 builder.
clone(*it, operandMaps[i - 1]);
692 for (
auto newForOp : newInnerLoops) {
693 unsigned oldNumIterOperands =
694 newForOp.getNumRegionIterArgs() / unrollJamFactor;
695 unsigned numControlOperands = newForOp.getNumControlOperands();
696 auto yieldOp = cast<scf::YieldOp>(newForOp.getBody()->getTerminator());
697 unsigned oldNumYieldOperands = yieldOp.getNumOperands() / unrollJamFactor;
698 assert(oldNumIterOperands == oldNumYieldOperands &&
699 "oldNumIterOperands must be the same as oldNumYieldOperands");
700 for (
unsigned j = 0;
j < oldNumIterOperands; ++
j) {
704 newForOp.setOperand(numControlOperands + i * oldNumIterOperands +
j,
705 operandMaps[i - 1].lookupOrDefault(
706 newForOp.getOperand(numControlOperands +
j)));
708 i * oldNumYieldOperands +
j,
709 operandMaps[i - 1].lookupOrDefault(yieldOp.getOperand(
j)));
715 (
void)forOp.promoteIfSingleIteration(rewriter);
723 Range normalizedLoopBounds;
729 normalizedLoopBounds.
size =
731 return normalizedLoopBounds;
743 bool isZeroBased =
false;
745 isZeroBased = lbCst.value() == 0;
747 bool isStepOne =
false;
749 isStepOne = stepCst.value() == 1;
753 "expected matching types");
758 if (isZeroBased && isStepOne)
759 return {lb,
ub, step};
769 newUpperBound = rewriter.
createOrFold<arith::CeilDivSIOp>(
777 return {newLowerBound, newUpperBound, newStep};
791 Value denormalizedIvVal =
798 if (
Operation *preservedUse = denormalizedIvVal.getDefiningOp()) {
799 preservedUses.insert(preservedUse);
808 if (
getType(origLb).isIndex()) {
812 Value denormalizedIv;
817 Value scaled = normalizedIv;
819 Value origStepValue =
821 scaled = arith::MulIOp::create(rewriter, loc, normalizedIv, origStepValue);
824 denormalizedIv = scaled;
827 denormalizedIv = arith::AddIOp::create(rewriter, loc, scaled, origLbValue);
836 assert(!values.empty() &&
"unexecpted empty array");
841 for (
auto v : values) {
851 assert(!values.empty() &&
"unexpected empty list");
857 std::optional<Value> productOf;
858 for (
auto v : values) {
860 if (vOne && vOne.value() == 1)
863 productOf = arith::MulIOp::create(rewriter, loc, productOf.value(), v)
869 productOf = arith::ConstantOp::create(
873 return productOf.value();
889 Operation *delinearizedOp = affine::AffineDelinearizeIndexOp::create(
890 rewriter, loc, linearizedIv, ubs);
891 auto resultVals = llvm::map_to_vector(
899 llvm::BitVector isUbOne(ubs.size());
900 for (
auto [
index,
ub] : llvm::enumerate(ubs)) {
902 if (ubCst && ubCst.value() == 1)
907 unsigned numLeadingOneUbs = 0;
908 for (
auto [
index,
ub] : llvm::enumerate(ubs)) {
909 if (!isUbOne.test(
index)) {
912 delinearizedIvs[
index] = arith::ConstantOp::create(
917 Value previous = linearizedIv;
918 for (
unsigned i = numLeadingOneUbs, e = ubs.size(); i < e; ++i) {
919 unsigned idx = ubs.size() - (i - numLeadingOneUbs) - 1;
920 if (i != numLeadingOneUbs && !isUbOne.test(idx + 1)) {
921 previous = arith::DivSIOp::create(rewriter, loc, previous, ubs[idx + 1]);
926 if (!isUbOne.test(idx)) {
927 iv = arith::RemSIOp::create(rewriter, loc, previous, ubs[idx]);
930 iv = arith::ConstantOp::create(
931 rewriter, loc, rewriter.
getZeroAttr(ubs[idx].getType()));
934 delinearizedIvs[idx] = iv;
936 return {delinearizedIvs, preservedUsers};
941 if (loops.size() < 2)
944 scf::ForOp innermost = loops.back();
945 scf::ForOp outermost = loops.front();
949 for (
auto loop : loops) {
951 if (step.value() == 0) {
958 for (
auto loop : loops) {
961 Value lb = loop.getLowerBound();
962 Value ub = loop.getUpperBound();
963 Value step = loop.getStep();
969 newLoopRange.offset));
973 newLoopRange.stride));
977 loop.getInductionVar(), lb, step);
986 loops, [](
auto loop) {
return loop.getUpperBound(); });
988 outermost.setUpperBound(upperBound);
993 rewriter, loc, outermost.getInductionVar(), upperBounds);
997 for (
int i = loops.size() - 1; i > 0; --i) {
998 auto outerLoop = loops[i - 1];
999 auto innerLoop = loops[i];
1001 Operation *innerTerminator = innerLoop.getBody()->getTerminator();
1002 auto yieldedVals = llvm::to_vector(innerTerminator->
getOperands());
1003 assert(llvm::equal(outerLoop.getRegionIterArgs(), innerLoop.getInitArgs()));
1004 for (
Value &yieldedVal : yieldedVals) {
1008 if (yieldedVal == innerLoop.getInductionVar()) {
1009 yieldedVal = delinearizeIvs[i];
1014 auto iter = llvm::find(innerLoop.getRegionIterArgs(), yieldedVal);
1015 if (iter != innerLoop.getRegionIterArgs().end()) {
1016 unsigned iterArgIndex = iter - innerLoop.getRegionIterArgs().begin();
1018 assert(iterArgIndex < innerLoop.getInitArgs().size());
1019 yieldedVal = innerLoop.getInitArgs()[iterArgIndex];
1022 rewriter.
eraseOp(innerTerminator);
1025 innerBlockArgs.push_back(delinearizeIvs[i]);
1026 llvm::append_range(innerBlockArgs, outerLoop.getRegionIterArgs());
1029 rewriter.
replaceOp(innerLoop, yieldedVals);
1035 if (loops.empty()) {
1038 IRRewriter rewriter(loops.front().getContext());
1043 LogicalResult
result(failure());
1053 for (
unsigned i = 0, e = loops.size(); i < e; ++i) {
1054 operandsDefinedAbove[i] = i;
1055 for (
unsigned j = 0;
j < i; ++
j) {
1057 loops[i].getUpperBound(),
1058 loops[i].getStep()};
1060 operandsDefinedAbove[i] =
j;
1071 iterArgChainStart[0] = 0;
1072 for (
unsigned i = 1, e = loops.size(); i < e; ++i) {
1074 iterArgChainStart[i] = i;
1075 auto outerloop = loops[i - 1];
1076 auto innerLoop = loops[i];
1077 if (outerloop.getNumRegionIterArgs() != innerLoop.getNumRegionIterArgs()) {
1080 if (!llvm::equal(outerloop.getRegionIterArgs(), innerLoop.getInitArgs())) {
1083 auto outerloopTerminator = outerloop.getBody()->getTerminator();
1084 if (!llvm::equal(outerloopTerminator->getOperands(),
1085 innerLoop.getResults())) {
1088 iterArgChainStart[i] = iterArgChainStart[i - 1];
1094 for (
unsigned end = loops.size(); end > 0; --end) {
1096 for (; start < end - 1; ++start) {
1098 *std::max_element(std::next(operandsDefinedAbove.begin(), start),
1099 std::next(operandsDefinedAbove.begin(), end));
1102 if (iterArgChainStart[end - 1] > start)
1111 if (start != end - 1)
1119 ArrayRef<std::vector<unsigned>> combinedDimensions) {
1125 auto sortedDimensions = llvm::to_vector<3>(combinedDimensions);
1126 for (
auto &dims : sortedDimensions)
1131 for (
unsigned i = 0, e = loops.getNumLoops(); i < e; ++i) {
1134 Value lb = loops.getLowerBound()[i];
1135 Value ub = loops.getUpperBound()[i];
1136 Value step = loops.getStep()[i];
1139 rewriter, loops.getLoc(), newLoopRange.size));
1150 for (
auto &sortedDimension : sortedDimensions) {
1152 for (
auto idx : sortedDimension) {
1153 newUpperBound = arith::MulIOp::create(rewriter, loc, newUpperBound,
1154 normalizedUpperBounds[idx]);
1156 lowerBounds.push_back(cst0);
1157 steps.push_back(cst1);
1158 upperBounds.push_back(newUpperBound);
1167 auto newPloop = scf::ParallelOp::create(
1168 rewriter, loc, lowerBounds, upperBounds, steps,
1170 for (
unsigned i = 0, e = combinedDimensions.size(); i < e; ++i) {
1171 Value previous = ploopIVs[i];
1172 unsigned numberCombinedDimensions = combinedDimensions[i].size();
1174 for (
unsigned j = numberCombinedDimensions - 1;
j > 0; --
j) {
1175 unsigned idx = combinedDimensions[i][
j];
1178 Value iv = arith::RemSIOp::create(insideBuilder, loc, previous,
1179 normalizedUpperBounds[idx]);
1185 previous = arith::DivSIOp::create(insideBuilder, loc, previous,
1186 normalizedUpperBounds[idx]);
1190 unsigned idx = combinedDimensions[i][0];
1192 previous, loops.getRegion());
1197 loops.getBody()->back().erase();
1198 newPloop.getBody()->getOperations().splice(
1200 loops.getBody()->getOperations());
1213 return op != inner.getOperation();
1216 LogicalResult status =
success();
1218 for (
auto &op : outer.getBody()->without_terminator()) {
1220 if (&op == inner.getOperation())
1223 if (forwardSlice.count(&op) > 0) {
1228 if (isa<scf::ForOp>(op))
1231 if (op.getNumRegions() > 0) {
1241 toHoist.push_back(&op);
1243 auto *outerForOp = outer.getOperation();
1244 for (
auto *op : toHoist)
1245 op->moveBefore(outerForOp);
1254 LogicalResult status =
success();
1255 const Loops &interTile = tileLoops.first;
1256 const Loops &intraTile = tileLoops.second;
1257 auto size = interTile.size();
1258 assert(size == intraTile.size());
1261 for (
unsigned s = 1; s < size; ++s)
1262 status = succeeded(status) ?
hoistOpsBetween(intraTile[0], intraTile[s])
1264 for (
unsigned s = 1; s < size; ++s)
1265 status = succeeded(status) ?
hoistOpsBetween(interTile[0], interTile[s])
1274template <
typename T>
1277 unsigned maxLoops = std::numeric_limits<unsigned>::max()) {
1278 for (
unsigned i = 0; i < maxLoops; ++i) {
1279 forOps.push_back(rootForOp);
1280 Block &body = rootForOp.getRegion().front();
1281 if (body.
begin() != std::prev(body.
end(), 2))
1284 rootForOp = dyn_cast<T>(&body.
front());
1292 assert(!forOp.getUnsignedCmp() &&
"unsigned loops are not supported");
1293 auto originalStep = forOp.getStep();
1294 auto iv = forOp.getInductionVar();
1297 forOp.setStep(arith::MulIOp::create(
b, forOp.getLoc(), originalStep, factor));
1300 for (
auto t : targets) {
1301 assert(!t.getUnsignedCmp() &&
"unsigned loops are not supported");
1304 auto begin = t.getBody()->begin();
1305 auto nOps = t.getBody()->getOperations().size();
1309 Value stepped = arith::AddIOp::create(
b, t.getLoc(), iv, forOp.getStep());
1311 arith::MinSIOp::create(
b, t.getLoc(), forOp.getUpperBound(), stepped);
1314 auto newForOp = scf::ForOp::create(
b, t.getLoc(), iv,
ub, originalStep);
1315 newForOp.getBody()->getOperations().splice(
1316 newForOp.getBody()->getOperations().begin(),
1317 t.getBody()->getOperations(), begin, std::next(begin, nOps - 1));
1319 newForOp.getRegion());
1321 innerLoops.push_back(newForOp);
1329template <
typename SizeType>
1337 assert(res.size() == 1 &&
"Expected 1 inner forOp");
1346 for (
auto it : llvm::zip(forOps, sizes)) {
1347 auto step =
stripmineSink(std::get<0>(it), std::get<1>(it), currentTargets);
1348 res.push_back(step);
1349 currentTargets = step;
1358 res.push_back(llvm::getSingleElement(loops));
1366 forOps.reserve(sizes.size());
1368 if (forOps.size() < sizes.size())
1369 sizes = sizes.take_front(forOps.size());
1371 return ::tile(forOps, sizes, forOps.back());
1384 forOps.reserve(sizes.size());
1386 if (forOps.size() < sizes.size())
1387 sizes = sizes.take_front(forOps.size());
1394 if (llvm::any_of(forOps,
1395 [](scf::ForOp op) {
return !op.getInitArgs().empty(); }))
1403 tileSizes.reserve(sizes.size());
1404 for (
unsigned i = 0, e = sizes.size(); i < e; ++i) {
1405 assert(sizes[i] > 0 &&
"expected strictly positive size for strip-mining");
1407 auto forOp = forOps[i];
1409 auto loc = forOp.getLoc();
1410 Value diff = arith::SubIOp::create(builder, loc, forOp.getUpperBound(),
1411 forOp.getLowerBound());
1413 Value iterationsPerBlock =
1415 tileSizes.push_back(iterationsPerBlock);
1419 auto intraTile =
tile(forOps, tileSizes, forOps.back());
1420 TileLoops tileLoops = std::make_pair(forOps, intraTile);
1431 scf::ForallOp source,
1433 unsigned numTargetOuts =
target.getNumResults();
1434 unsigned numSourceOuts = source.getNumResults();
1438 llvm::append_range(fusedOuts,
target.getOutputs());
1439 llvm::append_range(fusedOuts, source.getOutputs());
1443 scf::ForallOp fusedLoop = scf::ForallOp::create(
1444 rewriter, source.getLoc(), source.getMixedLowerBound(),
1445 source.getMixedUpperBound(), source.getMixedStep(), fusedOuts,
1446 source.getMapping());
1450 mapping.
map(
target.getInductionVars(), fusedLoop.getInductionVars());
1451 mapping.
map(source.getInductionVars(), fusedLoop.getInductionVars());
1455 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1456 mapping.
map(source.getRegionIterArgs(),
1457 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1462 rewriter.
clone(op, mapping);
1463 for (
Operation &op : source.getBody()->without_terminator())
1464 rewriter.
clone(op, mapping);
1467 scf::InParallelOp targetTerm =
target.getTerminator();
1468 scf::InParallelOp sourceTerm = source.getTerminator();
1469 scf::InParallelOp fusedTerm = fusedLoop.getTerminator();
1471 for (
Operation &op : targetTerm.getYieldingOps())
1472 rewriter.
clone(op, mapping);
1473 for (
Operation &op : sourceTerm.getYieldingOps())
1474 rewriter.
clone(op, mapping);
1477 rewriter.
replaceOp(
target, fusedLoop.getResults().take_front(numTargetOuts));
1478 rewriter.
replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1486 assert(source.getUnsignedCmp() ==
target.getUnsignedCmp() &&
1487 "incompatible signedness");
1488 unsigned numTargetOuts =
target.getNumResults();
1489 unsigned numSourceOuts = source.getNumResults();
1493 llvm::append_range(fusedInitArgs,
target.getInitArgs());
1494 llvm::append_range(fusedInitArgs, source.getInitArgs());
1499 scf::ForOp fusedLoop = scf::ForOp::create(
1500 rewriter, source.getLoc(), source.getLowerBound(), source.getUpperBound(),
1501 source.getStep(), fusedInitArgs,
nullptr,
1502 source.getUnsignedCmp());
1506 mapping.
map(
target.getInductionVar(), fusedLoop.getInductionVar());
1508 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1509 mapping.
map(source.getInductionVar(), fusedLoop.getInductionVar());
1510 mapping.
map(source.getRegionIterArgs(),
1511 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1516 rewriter.
clone(op, mapping);
1517 for (
Operation &op : source.getBody()->without_terminator())
1518 rewriter.
clone(op, mapping);
1522 for (
Value operand :
target.getBody()->getTerminator()->getOperands())
1524 for (
Value operand : source.getBody()->getTerminator()->getOperands())
1526 if (!yieldResults.empty())
1527 scf::YieldOp::create(rewriter, source.getLoc(), yieldResults);
1530 rewriter.
replaceOp(
target, fusedLoop.getResults().take_front(numTargetOuts));
1531 rewriter.
replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1537 scf::ForallOp forallOp) {
1542 if (forallOp.isNormalized())
1546 auto loc = forallOp.getLoc();
1549 for (
auto [lb,
ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
1550 Range normalizedLoopParams =
1552 newUbs.push_back(normalizedLoopParams.
size);
1558 auto normalizedForallOp = scf::ForallOp::create(
1559 rewriter, loc, newUbs, forallOp.getOutputs(), forallOp.getMapping(),
1563 normalizedForallOp.getBodyRegion(),
1564 normalizedForallOp.getBodyRegion().begin());
1566 rewriter.
eraseBlock(&normalizedForallOp.getBodyRegion().back());
1570 for (
auto [idx, iv] :
1571 llvm::enumerate(normalizedForallOp.getInductionVars())) {
1577 rewriter.
replaceOp(forallOp, normalizedForallOp);
1578 return normalizedForallOp;
1583 assert(!loops.empty() &&
"unexpected empty loop nest");
1584 if (loops.size() == 1)
1585 return isa_and_nonnull<scf::ForOp>(loops.front().getOperation());
1586 for (
auto [outerLoop, innerLoop] :
1587 llvm::zip_equal(loops.drop_back(), loops.drop_front())) {
1588 auto outerFor = dyn_cast_or_null<scf::ForOp>(outerLoop.getOperation());
1589 auto innerFor = dyn_cast_or_null<scf::ForOp>(innerLoop.getOperation());
1590 if (!outerFor || !innerFor)
1592 auto outerBBArgs = outerFor.getRegionIterArgs();
1593 auto innerIterArgs = innerFor.getInitArgs();
1594 if (outerBBArgs.size() != innerIterArgs.size())
1597 for (
auto [outerBBArg, innerIterArg] :
1598 llvm::zip_equal(outerBBArgs, innerIterArgs)) {
1599 if (!llvm::hasSingleElement(outerBBArg.getUses()) ||
1600 innerIterArg != outerBBArg)
1605 cast<scf::YieldOp>(outerFor.getBody()->getTerminator())->getOperands();
1606 ValueRange innerResults = innerFor.getResults();
1607 if (outerYields.size() != innerResults.size())
1609 for (
auto [outerYield, innerResult] :
1610 llvm::zip_equal(outerYields, innerResults)) {
1611 if (!llvm::hasSingleElement(innerResult.getUses()) ||
1612 outerYield != innerResult)
1621 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1622 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1623 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1624 if (!loBnds || !upBnds || !steps)
1627 for (
auto [lb,
ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1631 if (!lbCst || !ubCst || !stepCst)
1633 loopRanges.emplace_back(*lbCst, *ubCst, *stepCst);
1640 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1641 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1642 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1643 if (!loBnds || !upBnds || !steps)
1646 for (
auto [lb,
ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1652 tripCounts.push_back(*numIter);
1662 const unsigned numLoops = op.getNumLoops();
1663 assert(llvm::none_of(unrollFactors, [](uint64_t f) {
return f == 0; }) &&
1664 "Expected positive unroll factors");
1665 assert((!unrollFactors.empty() && (unrollFactors.size() <= numLoops)) &&
1666 "Expected non-empty unroll factors of size <= to the number of loops");
1669 if (llvm::all_of(unrollFactors, [](uint64_t f) {
return f == 1; }))
1671 op,
"Unrolling not applied if all factors are 1");
1674 if (llvm::hasSingleElement(op.getBody()->getOperations()))
1679 const unsigned firstLoopDimIdx = numLoops - unrollFactors.size();
1684 if (tripCounts.empty())
1686 op,
"Failed to compute constant trip counts for the loop. Note that "
1687 "dynamic loop sizes are not supported.");
1689 for (
unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1690 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1691 if (tripCounts[dimIdx].urem(unrollFactor) != 0)
1693 op,
"Unroll factors don't divide the iteration space evenly");
1696 std::optional<SmallVector<OpFoldResult>> maybeFoldSteps = op.getLoopSteps();
1697 if (!maybeFoldSteps)
1700 for (
auto step : *maybeFoldSteps)
1703 for (
unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1704 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1705 if (unrollFactor == 1)
1707 const size_t origStep = steps[dimIdx];
1708 const int64_t newStep = origStep * unrollFactor;
1712 auto yieldedValues = op.getBody()->getTerminator()->getOperands();
1715 op.getBody(), op.getInductionVars()[dimIdx], unrollFactor,
1718 const AffineExpr expr = b.getAffineDimExpr(0) + (origStep * i);
1720 b.getDimIdentityMap().dropResult(0).insertResult(expr, 0);
1721 return affine::AffineApplyOp::create(b, iv.getLoc(), map,
1724 annotateFn, iterArgs, yieldedValues, &clonedToSrcOpsMap);
1729 op.getStepMutable()[dimIdx].assign(
static OpFoldResult getProductOfIndexes(RewriterBase &rewriter, Location loc, ArrayRef< OpFoldResult > values)
static LogicalResult tryIsolateBands(const TileLoops &tileLoops)
static void getPerfectlyNestedLoopsImpl(SmallVectorImpl< T > &forOps, T rootForOp, unsigned maxLoops=std::numeric_limits< unsigned >::max())
Collect perfectly nested loops starting from rootForOps.
static LogicalResult hoistOpsBetween(scf::ForOp outer, scf::ForOp inner)
static Range emitNormalizedLoopBoundsForIndexType(RewriterBase &rewriter, Location loc, OpFoldResult lb, OpFoldResult ub, OpFoldResult step)
static Loops stripmineSink(scf::ForOp forOp, Value factor, ArrayRef< scf::ForOp > targets)
static Value ceilDivPositive(OpBuilder &builder, Location loc, Value dividend, int64_t divisor)
static Value getProductOfIntsOrIndexes(RewriterBase &rewriter, Location loc, ArrayRef< Value > values)
Helper function to multiply a sequence of values.
static std::pair< SmallVector< Value >, SmallPtrSet< Operation *, 2 > > delinearizeInductionVariable(RewriterBase &rewriter, Location loc, Value linearizedIv, ArrayRef< Value > ubs)
For each original loop, the value of the induction variable can be obtained by dividing the induction...
static void denormalizeInductionVariableForIndexType(RewriterBase &rewriter, Location loc, Value normalizedIv, OpFoldResult origLb, OpFoldResult origStep)
static bool areInnerBoundsInvariant(scf::ForOp forOp)
Check if bounds of all inner loops are defined outside of forOp and return false if not.
static int64_t product(ArrayRef< int64_t > vals)
static llvm::ManagedStatic< PassManagerOptions > options
Base type for affine expression.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
OpListType::iterator iterator
unsigned getNumArguments()
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgListType getArguments()
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
TypedAttr getZeroAttr(Type type)
MLIRContext * getContext() const
TypedAttr getOneAttr(Type type)
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.
auto lookup(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
bool contains(T from) const
Checks to see if a mapping for 'from' exists.
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
InsertPoint saveInsertionPoint() const
Return a saved insertion point.
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.
static OpBuilder atBlockTerminator(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the block terminator.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
void restoreInsertionPoint(InsertPoint ip)
Restore the insert point to a previously saved point.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
This class represents an operand of an operation.
This is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
operand_type_range getOperandTypes()
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
void setOperands(ValueRange operands)
Replace the current operands of this operation with the ones provided in 'operands'.
result_range getResults()
Operation * clone(IRMapping &mapper, const CloneOptions &options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
BlockArgListType getArguments()
ParentT getParentOfType()
Find the first parent operation of the given type, or nullptr if there is no ancestor operation.
bool hasOneBlock()
Return true if this region has exactly one block.
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.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
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".
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 isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
bool use_empty() const
Returns true if this value has no uses.
void replaceUsesWithIf(Value newValue, function_ref< bool(OpOperand &)> shouldReplace)
Replace all uses of 'this' value with 'newValue' if the given callback returns true.
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static WalkResult advance()
static WalkResult interrupt()
Specialization of arith.constant op that returns an integer of index type.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Operation * getOwner() const
Return the owner of this operand.
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
std::optional< llvm::APSInt > computeUbMinusLb(Value lb, Value ub, bool isSigned)
Helper function to compute the difference between two values.
Include the generated interface declarations.
void getPerfectlyNestedLoops(SmallVectorImpl< scf::ForOp > &nestedLoops, scf::ForOp root)
Get perfectly nested sequence of loops starting at root of loop nest (the first op being another Affi...
bool isPerfectlyNestedForLoops(MutableArrayRef< LoopLikeOpInterface > loops)
Check if the provided loops are perfectly nested for-loops.
FailureOr< UnrolledLoopInfo > loopUnrollByFactor(scf::ForOp forOp, uint64_t unrollFactor, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn=nullptr, bool shouldPromoteIfSingleIteration=true)
Unrolls this for operation by the specified unroll factor.
LogicalResult outlineIfOp(RewriterBase &b, scf::IfOp ifOp, func::FuncOp *thenFn, StringRef thenFnName, func::FuncOp *elseFn, StringRef elseFnName)
Outline the then and/or else regions of ifOp as follows:
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region ®ion)
Replace all uses of orig within the given region with replacement.
SmallVector< scf::ForOp > replaceLoopNestWithNewYields(RewriterBase &rewriter, MutableArrayRef< scf::ForOp > loopNest, ValueRange newIterOperands, const NewYieldValuesFn &newYieldValuesFn, bool replaceIterOperandsUsesInLoop=true)
Update a perfectly nested loop nest to yield new values from the innermost loop and propagating it up...
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.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
LogicalResult coalescePerfectlyNestedSCFForLoops(scf::ForOp op)
Walk an affine.for to find a band to coalesce.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
void generateUnrolledLoop(Block *loopBodyBlock, Value iv, uint64_t unrollFactor, function_ref< Value(unsigned, Value, OpBuilder)> ivRemapFn, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn, ValueRange iterArgs, ValueRange yieldedValues, IRMapping *clonedToSrcOpsMap=nullptr)
Generate unrolled copies of an scf loop's 'loopBodyBlock', with 'iterArgs' and 'yieldedValues' as the...
Value getValueOrCreateConstantIntOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
LogicalResult loopUnrollFull(scf::ForOp forOp)
Unrolls this loop completely.
llvm::SmallVector< llvm::APInt > getConstLoopTripCounts(mlir::LoopLikeOpInterface loopOp)
Get constant trip counts for each of the induction variables of the given loop operation.
std::pair< Loops, Loops > TileLoops
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
llvm::SmallVector< std::tuple< int64_t, int64_t, int64_t > > getConstLoopBounds(mlir::LoopLikeOpInterface loopOp)
Get constant loop bounds and steps for each of the induction variables of the given loop operation,...
void collapseParallelLoops(RewriterBase &rewriter, scf::ParallelOp loops, ArrayRef< std::vector< unsigned > > combinedDimensions)
Take the ParallelLoop and for each set of dimension indices, combine them into a single dimension.
llvm::SetVector< T, Vector, Set, N > SetVector
std::optional< std::pair< APInt, bool > > getConstantAPIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
SliceOptions ForwardSliceOptions
Loops tilePerfectlyNested(scf::ForOp rootForOp, ArrayRef< Value > sizes)
Tile a nest of scf::ForOp loops rooted at rootForOp with the given (parametric) sizes.
LogicalResult loopUnrollJamByFactor(scf::ForOp forOp, uint64_t unrollFactor)
Unrolls and jams this scf.for operation by the specified unroll factor.
bool getInnermostParallelLoops(Operation *rootOp, SmallVectorImpl< scf::ParallelOp > &result)
Get a list of innermost parallel loops contained in rootOp.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
FailureOr< scf::ParallelOp > parallelLoopUnrollByFactors(scf::ParallelOp op, ArrayRef< uint64_t > unrollFactors, RewriterBase &rewriter, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn=nullptr, IRMapping *clonedToSrcOpsMap=nullptr)
Unroll this scf::Parallel loop by the specified unroll factors.
void getUsedValuesDefinedAbove(Region ®ion, Region &limit, SetVector< Value > &values)
Fill values with a list of values defined at the ancestors of the limit region and used within region...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
SmallVector< Loops, 8 > tile(ArrayRef< scf::ForOp > forOps, ArrayRef< Value > sizes, ArrayRef< scf::ForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
FailureOr< func::FuncOp > outlineSingleBlockRegion(RewriterBase &rewriter, Location loc, Region ®ion, StringRef funcName, func::CallOp *callOp=nullptr)
Outline a region with a single block into a new FuncOp.
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
bool areValuesDefinedAbove(Range values, Region &limit)
Check if all values in the provided range are defined above the limit region.
void denormalizeInductionVariable(RewriterBase &rewriter, Location loc, Value normalizedIv, OpFoldResult origLb, OpFoldResult origStep)
Get back the original induction variable values after loop normalization.
scf::ForallOp fuseIndependentSiblingForallLoops(scf::ForallOp target, scf::ForallOp source, RewriterBase &rewriter)
Given two scf.forall loops, target and source, fuses target into source.
LogicalResult coalesceLoops(MutableArrayRef< scf::ForOp > loops)
Replace a perfect nest of "for" loops with a single linearized loop.
scf::ForOp fuseIndependentSiblingForLoops(scf::ForOp target, scf::ForOp source, RewriterBase &rewriter)
Given two scf.for loops, target and source, fuses target into source.
llvm::function_ref< Fn > function_ref
TileLoops extractFixedOuterLoops(scf::ForOp rootFOrOp, ArrayRef< int64_t > sizes)
Range emitNormalizedLoopBounds(RewriterBase &rewriter, Location loc, OpFoldResult lb, OpFoldResult ub, OpFoldResult step)
Materialize bounds and step of a zero-based and unit-step loop derived by normalizing the specified b...
SmallVector< scf::ForOp, 8 > Loops
Tile a nest of standard for loops rooted at rootForOp by finding such parametric tile sizes that the ...
bool isOneInteger(OpFoldResult v)
Return true if v is an IntegerAttr with value 1.
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.
FailureOr< scf::ForallOp > normalizeForallOp(RewriterBase &rewriter, scf::ForallOp forallOp)
Normalize an scf.forall operation.
void getForwardSlice(Operation *op, SetVector< Operation * > *forwardSlice, const ForwardSliceOptions &options={})
Fills forwardSlice with the computed forward slice (i.e.
SmallVector< std::pair< Block::iterator, Block::iterator > > subBlocks
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...
std::optional< scf::ForOp > epilogueLoopOp
std::optional< scf::ForOp > mainLoopOp
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.