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);
367FailureOr<std::pair<scf::ForOp, scf::ForOp>>
370 if (splitPoint.
getType() != forOp.getLowerBound().getType())
374 bool isUnsigned = forOp.getUnsignedCmp();
375 Value lbVal = forOp.getLowerBound();
376 Value ubVal = forOp.getUpperBound();
377 Value stepVal = forOp.getStep();
378 auto checkSplitPoint = [&](
auto getBound) -> LogicalResult {
379 auto lb = getBound(lbVal);
380 auto ub = getBound(ubVal);
381 auto step = getBound(stepVal);
382 auto split = getBound(splitPoint);
383 if ((lb && split && *lb > *split) || (split &&
ub && *split >= *
ub) ||
384 (step && *step <= 0))
386 if (lb && step && split && (*split - *lb) % *step != 0)
396 auto firstForOp = cast<scf::ForOp>(rewriter.
clone(*forOp));
397 auto secondForOp = cast<scf::ForOp>(rewriter.
clone(*forOp));
399 [&] { firstForOp.setUpperBound(splitPoint); });
401 [&] { secondForOp.setLowerBound(splitPoint); });
408 secondForOp->setOperands(secondForOp.getNumControlOperands(),
409 secondForOp.getInitArgs().size(),
410 firstForOp.getResults());
412 rewriter.
replaceOp(forOp, secondForOp.getResults());
414 return std::pair<scf::ForOp, scf::ForOp>{firstForOp, secondForOp};
420 scf::ForOp forOp, uint64_t unrollFactor,
422 bool shouldPromoteIfSingleIteration) {
423 assert(unrollFactor > 0 &&
"expected positive unroll factor");
426 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
433 auto loc = forOp.getLoc();
434 Value step = forOp.getStep();
435 Value upperBoundUnrolled;
437 bool generateEpilogueLoop =
true;
439 std::optional<APInt> constTripCount = forOp.getStaticTripCount();
449 constTripCount = std::nullopt;
450 if (constTripCount) {
452 bool isUnsignedLoop = forOp.getUnsignedCmp();
458 if (isUnsignedLoop) {
459 if (
auto intTy = dyn_cast<IntegerType>(forOp.getUpperBound().getType()))
460 if (intTy.getWidth() >= 64)
465 assert(apInt &&
"expected constant loop bound");
466 return isUnsignedLoop ?
static_cast<int64_t>(apInt->first.getZExtValue())
467 : apInt->first.getSExtValue();
469 int64_t lbCst = getLoopBound(forOp.getLowerBound());
470 int64_t ubCst = getLoopBound(forOp.getUpperBound());
471 int64_t stepCst = getLoopBound(step);
472 if (unrollFactor == 1) {
473 if (shouldPromoteIfSingleIteration && constTripCount->isOne() &&
474 failed(forOp.promoteIfSingleIteration(rewriter)))
479 uint64_t tripCount = constTripCount->getZExtValue();
480 uint64_t tripCountEvenMultiple = tripCount - tripCount % unrollFactor;
481 int64_t upperBoundUnrolledCst = lbCst + tripCountEvenMultiple * stepCst;
482 int64_t stepUnrolledCst = stepCst * unrollFactor;
485 generateEpilogueLoop = upperBoundUnrolledCst < ubCst;
486 if (generateEpilogueLoop)
487 upperBoundUnrolled = arith::ConstantOp::create(
490 upperBoundUnrolledCst));
492 upperBoundUnrolled = forOp.getUpperBound();
500 bool mainLoopHasNoIter = (tripCountEvenMultiple == 0);
501 bool stepUnchanged = (stepCst == stepUnrolledCst);
503 (mainLoopHasNoIter || stepUnchanged)
505 : arith::ConstantOp::create(boundsBuilder, loc,
507 step.
getType(), stepUnrolledCst));
512 auto lowerBound = forOp.getLowerBound();
513 auto upperBound = forOp.getUpperBound();
515 arith::SubIOp::create(boundsBuilder, loc, upperBound, lowerBound);
516 Value zero = arith::ConstantOp::create(
518 Value nonEmpty = arith::CmpIOp::create(boundsBuilder, loc,
519 forOp.getUnsignedCmp()
520 ? arith::CmpIPredicate::ult
521 : arith::CmpIPredicate::slt,
522 lowerBound, upperBound);
523 diff = arith::SelectOp::create(boundsBuilder, loc, nonEmpty, diff, zero);
526 Value unrollFactorCst = arith::ConstantOp::create(
530 arith::RemSIOp::create(boundsBuilder, loc, tripCount, unrollFactorCst);
532 Value tripCountEvenMultiple =
533 arith::SubIOp::create(boundsBuilder, loc, tripCount, tripCountRem);
535 upperBoundUnrolled = arith::AddIOp::create(
536 boundsBuilder, loc, lowerBound,
537 arith::MulIOp::create(boundsBuilder, loc, tripCountEvenMultiple, step));
540 arith::MulIOp::create(boundsBuilder, loc, step, unrollFactorCst);
546 if (generateEpilogueLoop) {
548 if (failed(splitLoops))
550 forOp = splitLoops->first;
551 scf::ForOp epilogueForOp = splitLoops->second;
552 if (!shouldPromoteIfSingleIteration ||
553 epilogueForOp.promoteIfSingleIteration(rewriter).failed())
556 forOp.setUpperBound(upperBoundUnrolled);
560 forOp.setStep(stepUnrolled);
562 auto iterArgs =
ValueRange(forOp.getRegionIterArgs());
563 auto yieldedValues = forOp.getBody()->getTerminator()->getOperands();
566 forOp.getBody(), forOp.getInductionVar(), unrollFactor,
569 auto stride = arith::MulIOp::create(
571 arith::ConstantOp::create(b, loc,
572 b.getIntegerAttr(iv.getType(), i)));
573 return arith::AddIOp::create(b, loc, iv, stride);
575 annotateFn, iterArgs, yieldedValues);
578 if (!shouldPromoteIfSingleIteration ||
579 forOp.promoteIfSingleIteration(rewriter).failed())
587 std::optional<APInt> mayBeConstantTripCount = forOp.getStaticTripCount();
588 if (!mayBeConstantTripCount.has_value())
590 const APInt &tripCount = *mayBeConstantTripCount;
591 if (tripCount.isZero())
593 if (tripCount.isOne())
594 return forOp.promoteIfSingleIteration(rewriter);
601 auto walkResult = forOp.walk([&](scf::ForOp innerForOp) {
602 if (!forOp.isDefinedOutsideOfLoop(innerForOp.getLowerBound()) ||
603 !forOp.isDefinedOutsideOfLoop(innerForOp.getUpperBound()) ||
604 !forOp.isDefinedOutsideOfLoop(innerForOp.getStep()))
609 return !walkResult.wasInterrupted();
614 uint64_t unrollJamFactor) {
615 assert(unrollJamFactor > 0 &&
"unroll jam factor should be positive");
617 if (unrollJamFactor == 1)
623 LDBG() <<
"failed to unroll and jam: inner bounds are not invariant";
628 if (forOp->getNumResults() > 0) {
629 LDBG() <<
"failed to unroll and jam: unsupported loop with results";
635 std::optional<APInt> tripCount = forOp.getStaticTripCount();
636 if (!tripCount.has_value()) {
638 LDBG() <<
"failed to unroll and jam: trip count could not be determined";
641 uint64_t tripCountValue = tripCount->getZExtValue();
642 if (tripCountValue == 0)
644 if (unrollJamFactor > tripCountValue) {
645 LDBG() <<
"unroll and jam factor is greater than trip count, set factor to "
648 unrollJamFactor = tripCountValue;
649 }
else if (tripCountValue % unrollJamFactor != 0) {
650 LDBG() <<
"failed to unroll and jam: unsupported trip count that is not a "
651 "multiple of unroll jam factor";
656 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
666 forOp.walk([&](scf::ForOp innerForOp) { innerLoops.push_back(innerForOp); });
677 for (scf::ForOp oldForOp : innerLoops) {
679 ValueRange oldIterOperands = oldForOp.getInits();
680 ValueRange oldIterArgs = oldForOp.getRegionIterArgs();
682 cast<scf::YieldOp>(oldForOp.getBody()->getTerminator()).getOperands();
685 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
686 dupIterOperands.append(oldIterOperands.begin(), oldIterOperands.end());
687 dupYieldOperands.append(oldYieldOperands.begin(), oldYieldOperands.end());
691 bool forOpReplaced = oldForOp == forOp;
692 scf::ForOp newForOp =
693 cast<scf::ForOp>(*oldForOp.replaceWithAdditionalYields(
694 rewriter, dupIterOperands,
false,
696 return dupYieldOperands;
698 newInnerLoops.push_back(newForOp);
703 ValueRange newIterArgs = newForOp.getRegionIterArgs();
704 unsigned oldNumIterArgs = oldIterArgs.size();
705 ValueRange newResults = newForOp.getResults();
706 unsigned oldNumResults = newResults.size() / unrollJamFactor;
707 assert(oldNumIterArgs == oldNumResults &&
708 "oldNumIterArgs must be the same as oldNumResults");
709 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
710 for (
unsigned j = 0;
j < oldNumIterArgs; ++
j) {
714 operandMaps[i - 1].map(newIterArgs[
j],
715 newIterArgs[i * oldNumIterArgs +
j]);
716 operandMaps[i - 1].map(newResults[
j],
717 newResults[i * oldNumResults +
j]);
724 int64_t step = forOp.getConstantStep()->getSExtValue();
726 forOp.getLoc(), forOp.getStep(),
728 forOp.getLoc(), rewriter.
getIndexAttr(unrollJamFactor)));
729 forOp.setStep(newStep);
730 auto forOpIV = forOp.getInductionVar();
733 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
734 for (
auto &subBlock : subBlocks) {
737 OpBuilder builder(subBlock.first->getBlock(), std::next(subBlock.second));
741 if (!forOpIV.use_empty()) {
746 builder.
createOrFold<arith::AddIOp>(forOp.getLoc(), forOpIV, ivTag);
747 operandMaps[i - 1].map(forOpIV, ivUnroll);
750 for (
auto it = subBlock.first; it != std::next(subBlock.second); ++it)
751 builder.
clone(*it, operandMaps[i - 1]);
754 for (
auto newForOp : newInnerLoops) {
755 unsigned oldNumIterOperands =
756 newForOp.getNumRegionIterArgs() / unrollJamFactor;
757 unsigned numControlOperands = newForOp.getNumControlOperands();
758 auto yieldOp = cast<scf::YieldOp>(newForOp.getBody()->getTerminator());
759 unsigned oldNumYieldOperands = yieldOp.getNumOperands() / unrollJamFactor;
760 assert(oldNumIterOperands == oldNumYieldOperands &&
761 "oldNumIterOperands must be the same as oldNumYieldOperands");
762 for (
unsigned j = 0;
j < oldNumIterOperands; ++
j) {
766 newForOp.setOperand(numControlOperands + i * oldNumIterOperands +
j,
767 operandMaps[i - 1].lookupOrDefault(
768 newForOp.getOperand(numControlOperands +
j)));
770 i * oldNumYieldOperands +
j,
771 operandMaps[i - 1].lookupOrDefault(yieldOp.getOperand(
j)));
777 (
void)forOp.promoteIfSingleIteration(rewriter);
785 Range normalizedLoopBounds;
791 normalizedLoopBounds.
size =
793 return normalizedLoopBounds;
805 bool isZeroBased =
false;
807 isZeroBased = lbCst.value() == 0;
809 bool isStepOne =
false;
811 isStepOne = stepCst.value() == 1;
815 "expected matching types");
820 if (isZeroBased && isStepOne)
821 return {lb,
ub, step};
831 newUpperBound = rewriter.
createOrFold<arith::CeilDivSIOp>(
839 return {newLowerBound, newUpperBound, newStep};
853 Value denormalizedIvVal =
860 if (
Operation *preservedUse = denormalizedIvVal.getDefiningOp()) {
861 preservedUses.insert(preservedUse);
870 if (
getType(origLb).isIndex()) {
874 Value denormalizedIv;
879 Value scaled = normalizedIv;
881 Value origStepValue =
883 scaled = arith::MulIOp::create(rewriter, loc, normalizedIv, origStepValue);
886 denormalizedIv = scaled;
889 denormalizedIv = arith::AddIOp::create(rewriter, loc, scaled, origLbValue);
898 assert(!values.empty() &&
"unexecpted empty array");
903 for (
auto v : values) {
913 assert(!values.empty() &&
"unexpected empty list");
919 std::optional<Value> productOf;
920 for (
auto v : values) {
922 if (vOne && vOne.value() == 1)
925 productOf = arith::MulIOp::create(rewriter, loc, productOf.value(), v)
931 productOf = arith::ConstantOp::create(
935 return productOf.value();
951 Operation *delinearizedOp = affine::AffineDelinearizeIndexOp::create(
952 rewriter, loc, linearizedIv, ubs);
953 auto resultVals = llvm::map_to_vector(
961 llvm::BitVector isUbOne(ubs.size());
962 for (
auto [
index,
ub] : llvm::enumerate(ubs)) {
964 if (ubCst && ubCst.value() == 1)
969 unsigned numLeadingOneUbs = 0;
970 for (
auto [
index,
ub] : llvm::enumerate(ubs)) {
971 if (!isUbOne.test(
index)) {
974 delinearizedIvs[
index] = arith::ConstantOp::create(
979 Value previous = linearizedIv;
980 for (
unsigned i = numLeadingOneUbs, e = ubs.size(); i < e; ++i) {
981 unsigned idx = ubs.size() - (i - numLeadingOneUbs) - 1;
982 if (i != numLeadingOneUbs && !isUbOne.test(idx + 1)) {
983 previous = arith::DivSIOp::create(rewriter, loc, previous, ubs[idx + 1]);
988 if (!isUbOne.test(idx)) {
989 iv = arith::RemSIOp::create(rewriter, loc, previous, ubs[idx]);
992 iv = arith::ConstantOp::create(
993 rewriter, loc, rewriter.
getZeroAttr(ubs[idx].getType()));
996 delinearizedIvs[idx] = iv;
998 return {delinearizedIvs, preservedUsers};
1003 if (loops.size() < 2)
1006 scf::ForOp innermost = loops.back();
1007 scf::ForOp outermost = loops.front();
1011 for (
auto loop : loops) {
1013 if (step.value() == 0) {
1020 for (
auto loop : loops) {
1023 Value lb = loop.getLowerBound();
1024 Value ub = loop.getUpperBound();
1025 Value step = loop.getStep();
1031 newLoopRange.offset));
1033 newLoopRange.size));
1035 newLoopRange.stride));
1039 loop.getInductionVar(), lb, step);
1048 loops, [](
auto loop) {
return loop.getUpperBound(); });
1050 outermost.setUpperBound(upperBound);
1055 rewriter, loc, outermost.getInductionVar(), upperBounds);
1059 for (
int i = loops.size() - 1; i > 0; --i) {
1060 auto outerLoop = loops[i - 1];
1061 auto innerLoop = loops[i];
1063 Operation *innerTerminator = innerLoop.getBody()->getTerminator();
1064 auto yieldedVals = llvm::to_vector(innerTerminator->
getOperands());
1065 assert(llvm::equal(outerLoop.getRegionIterArgs(), innerLoop.getInitArgs()));
1066 for (
Value &yieldedVal : yieldedVals) {
1070 if (yieldedVal == innerLoop.getInductionVar()) {
1071 yieldedVal = delinearizeIvs[i];
1076 auto iter = llvm::find(innerLoop.getRegionIterArgs(), yieldedVal);
1077 if (iter != innerLoop.getRegionIterArgs().end()) {
1078 unsigned iterArgIndex = iter - innerLoop.getRegionIterArgs().begin();
1080 assert(iterArgIndex < innerLoop.getInitArgs().size());
1081 yieldedVal = innerLoop.getInitArgs()[iterArgIndex];
1084 rewriter.
eraseOp(innerTerminator);
1087 innerBlockArgs.push_back(delinearizeIvs[i]);
1088 llvm::append_range(innerBlockArgs, outerLoop.getRegionIterArgs());
1091 rewriter.
replaceOp(innerLoop, yieldedVals);
1097 if (loops.empty()) {
1100 IRRewriter rewriter(loops.front().getContext());
1105 LogicalResult
result(failure());
1115 for (
unsigned i = 0, e = loops.size(); i < e; ++i) {
1116 operandsDefinedAbove[i] = i;
1117 for (
unsigned j = 0;
j < i; ++
j) {
1119 loops[i].getUpperBound(),
1120 loops[i].getStep()};
1122 operandsDefinedAbove[i] =
j;
1133 iterArgChainStart[0] = 0;
1134 for (
unsigned i = 1, e = loops.size(); i < e; ++i) {
1136 iterArgChainStart[i] = i;
1137 auto outerloop = loops[i - 1];
1138 auto innerLoop = loops[i];
1139 if (outerloop.getNumRegionIterArgs() != innerLoop.getNumRegionIterArgs()) {
1142 if (!llvm::equal(outerloop.getRegionIterArgs(), innerLoop.getInitArgs())) {
1145 auto outerloopTerminator = outerloop.getBody()->getTerminator();
1146 if (!llvm::equal(outerloopTerminator->getOperands(),
1147 innerLoop.getResults())) {
1150 iterArgChainStart[i] = iterArgChainStart[i - 1];
1156 for (
unsigned end = loops.size(); end > 0; --end) {
1158 for (; start < end - 1; ++start) {
1160 *std::max_element(std::next(operandsDefinedAbove.begin(), start),
1161 std::next(operandsDefinedAbove.begin(), end));
1164 if (iterArgChainStart[end - 1] > start)
1173 if (start != end - 1)
1181 ArrayRef<std::vector<unsigned>> combinedDimensions) {
1187 auto sortedDimensions = llvm::to_vector<3>(combinedDimensions);
1188 for (
auto &dims : sortedDimensions)
1193 for (
unsigned i = 0, e = loops.getNumLoops(); i < e; ++i) {
1196 Value lb = loops.getLowerBound()[i];
1197 Value ub = loops.getUpperBound()[i];
1198 Value step = loops.getStep()[i];
1201 rewriter, loops.getLoc(), newLoopRange.size));
1212 for (
auto &sortedDimension : sortedDimensions) {
1214 for (
auto idx : sortedDimension) {
1215 newUpperBound = arith::MulIOp::create(rewriter, loc, newUpperBound,
1216 normalizedUpperBounds[idx]);
1218 lowerBounds.push_back(cst0);
1219 steps.push_back(cst1);
1220 upperBounds.push_back(newUpperBound);
1229 auto newPloop = scf::ParallelOp::create(
1230 rewriter, loc, lowerBounds, upperBounds, steps,
1232 for (
unsigned i = 0, e = combinedDimensions.size(); i < e; ++i) {
1233 Value previous = ploopIVs[i];
1234 unsigned numberCombinedDimensions = combinedDimensions[i].size();
1236 for (
unsigned j = numberCombinedDimensions - 1;
j > 0; --
j) {
1237 unsigned idx = combinedDimensions[i][
j];
1240 Value iv = arith::RemSIOp::create(insideBuilder, loc, previous,
1241 normalizedUpperBounds[idx]);
1247 previous = arith::DivSIOp::create(insideBuilder, loc, previous,
1248 normalizedUpperBounds[idx]);
1252 unsigned idx = combinedDimensions[i][0];
1254 previous, loops.getRegion());
1259 loops.getBody()->back().erase();
1260 newPloop.getBody()->getOperations().splice(
1262 loops.getBody()->getOperations());
1275 return op != inner.getOperation();
1278 LogicalResult status =
success();
1280 for (
auto &op : outer.getBody()->without_terminator()) {
1282 if (&op == inner.getOperation())
1285 if (forwardSlice.count(&op) > 0) {
1290 if (isa<scf::ForOp>(op))
1293 if (op.getNumRegions() > 0) {
1303 toHoist.push_back(&op);
1305 auto *outerForOp = outer.getOperation();
1306 for (
auto *op : toHoist)
1307 op->moveBefore(outerForOp);
1316 LogicalResult status =
success();
1317 const Loops &interTile = tileLoops.first;
1318 const Loops &intraTile = tileLoops.second;
1319 auto size = interTile.size();
1320 assert(size == intraTile.size());
1323 for (
unsigned s = 1; s < size; ++s)
1324 status = succeeded(status) ?
hoistOpsBetween(intraTile[0], intraTile[s])
1326 for (
unsigned s = 1; s < size; ++s)
1327 status = succeeded(status) ?
hoistOpsBetween(interTile[0], interTile[s])
1336template <
typename T>
1339 unsigned maxLoops = std::numeric_limits<unsigned>::max()) {
1340 for (
unsigned i = 0; i < maxLoops; ++i) {
1341 forOps.push_back(rootForOp);
1342 Block &body = rootForOp.getRegion().front();
1343 if (body.
begin() != std::prev(body.
end(), 2))
1346 rootForOp = dyn_cast<T>(&body.
front());
1354 assert(!forOp.getUnsignedCmp() &&
"unsigned loops are not supported");
1355 auto originalStep = forOp.getStep();
1356 auto iv = forOp.getInductionVar();
1359 forOp.setStep(arith::MulIOp::create(
b, forOp.getLoc(), originalStep, factor));
1362 for (
auto t : targets) {
1363 assert(!t.getUnsignedCmp() &&
"unsigned loops are not supported");
1366 auto begin = t.getBody()->begin();
1367 auto nOps = t.getBody()->getOperations().size();
1371 Value stepped = arith::AddIOp::create(
b, t.getLoc(), iv, forOp.getStep());
1373 arith::MinSIOp::create(
b, t.getLoc(), forOp.getUpperBound(), stepped);
1376 auto newForOp = scf::ForOp::create(
b, t.getLoc(), iv,
ub, originalStep);
1377 newForOp.getBody()->getOperations().splice(
1378 newForOp.getBody()->getOperations().begin(),
1379 t.getBody()->getOperations(), begin, std::next(begin, nOps - 1));
1381 newForOp.getRegion());
1383 innerLoops.push_back(newForOp);
1394 for (
auto it : llvm::zip(forOps, sizes)) {
1395 auto step =
stripmineSink(std::get<0>(it), std::get<1>(it), currentTargets);
1396 res.push_back(step);
1397 currentTargets = step;
1406 res.push_back(llvm::getSingleElement(loops));
1414 forOps.reserve(sizes.size());
1416 if (forOps.size() < sizes.size())
1417 sizes = sizes.take_front(forOps.size());
1419 return ::tile(forOps, sizes, forOps.back());
1432 forOps.reserve(sizes.size());
1434 if (forOps.size() < sizes.size())
1435 sizes = sizes.take_front(forOps.size());
1442 if (llvm::any_of(forOps,
1443 [](scf::ForOp op) {
return !op.getInitArgs().empty(); }))
1451 tileSizes.reserve(sizes.size());
1452 for (
unsigned i = 0, e = sizes.size(); i < e; ++i) {
1453 assert(sizes[i] > 0 &&
"expected strictly positive size for strip-mining");
1455 auto forOp = forOps[i];
1457 auto loc = forOp.getLoc();
1458 Value diff = arith::SubIOp::create(builder, loc, forOp.getUpperBound(),
1459 forOp.getLowerBound());
1461 Value iterationsPerBlock =
1463 tileSizes.push_back(iterationsPerBlock);
1467 auto intraTile =
tile(forOps, tileSizes, forOps.back());
1468 TileLoops tileLoops = std::make_pair(forOps, intraTile);
1479 scf::ForallOp source,
1481 unsigned numTargetOuts =
target.getNumResults();
1482 unsigned numSourceOuts = source.getNumResults();
1486 llvm::append_range(fusedOuts,
target.getOutputs());
1487 llvm::append_range(fusedOuts, source.getOutputs());
1491 scf::ForallOp fusedLoop = scf::ForallOp::create(
1492 rewriter, source.getLoc(), source.getMixedLowerBound(),
1493 source.getMixedUpperBound(), source.getMixedStep(), fusedOuts,
1494 source.getMapping());
1498 mapping.
map(
target.getInductionVars(), fusedLoop.getInductionVars());
1499 mapping.
map(source.getInductionVars(), fusedLoop.getInductionVars());
1503 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1504 mapping.
map(source.getRegionIterArgs(),
1505 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1510 rewriter.
clone(op, mapping);
1511 for (
Operation &op : source.getBody()->without_terminator())
1512 rewriter.
clone(op, mapping);
1515 scf::InParallelOp targetTerm =
target.getTerminator();
1516 scf::InParallelOp sourceTerm = source.getTerminator();
1517 scf::InParallelOp fusedTerm = fusedLoop.getTerminator();
1519 for (
Operation &op : targetTerm.getYieldingOps())
1520 rewriter.
clone(op, mapping);
1521 for (
Operation &op : sourceTerm.getYieldingOps())
1522 rewriter.
clone(op, mapping);
1525 rewriter.
replaceOp(
target, fusedLoop.getResults().take_front(numTargetOuts));
1526 rewriter.
replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1534 assert(source.getUnsignedCmp() ==
target.getUnsignedCmp() &&
1535 "incompatible signedness");
1536 unsigned numTargetOuts =
target.getNumResults();
1537 unsigned numSourceOuts = source.getNumResults();
1541 llvm::append_range(fusedInitArgs,
target.getInitArgs());
1542 llvm::append_range(fusedInitArgs, source.getInitArgs());
1547 scf::ForOp fusedLoop = scf::ForOp::create(
1548 rewriter, source.getLoc(), source.getLowerBound(), source.getUpperBound(),
1549 source.getStep(), fusedInitArgs,
nullptr,
1550 source.getUnsignedCmp());
1554 mapping.
map(
target.getInductionVar(), fusedLoop.getInductionVar());
1556 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1557 mapping.
map(source.getInductionVar(), fusedLoop.getInductionVar());
1558 mapping.
map(source.getRegionIterArgs(),
1559 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1564 rewriter.
clone(op, mapping);
1565 for (
Operation &op : source.getBody()->without_terminator())
1566 rewriter.
clone(op, mapping);
1570 for (
Value operand :
target.getBody()->getTerminator()->getOperands())
1572 for (
Value operand : source.getBody()->getTerminator()->getOperands())
1574 if (!yieldResults.empty())
1575 scf::YieldOp::create(rewriter, source.getLoc(), yieldResults);
1578 rewriter.
replaceOp(
target, fusedLoop.getResults().take_front(numTargetOuts));
1579 rewriter.
replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1585 scf::ForallOp forallOp) {
1590 if (forallOp.isNormalized())
1594 auto loc = forallOp.getLoc();
1597 for (
auto [lb,
ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
1598 Range normalizedLoopParams =
1600 newUbs.push_back(normalizedLoopParams.
size);
1606 auto normalizedForallOp = scf::ForallOp::create(
1607 rewriter, loc, newUbs, forallOp.getOutputs(), forallOp.getMapping(),
1611 normalizedForallOp.getBodyRegion(),
1612 normalizedForallOp.getBodyRegion().begin());
1614 rewriter.
eraseBlock(&normalizedForallOp.getBodyRegion().back());
1618 for (
auto [idx, iv] :
1619 llvm::enumerate(normalizedForallOp.getInductionVars())) {
1625 rewriter.
replaceOp(forallOp, normalizedForallOp);
1626 return normalizedForallOp;
1631 assert(!loops.empty() &&
"unexpected empty loop nest");
1632 if (loops.size() == 1)
1633 return isa_and_nonnull<scf::ForOp>(loops.front().getOperation());
1634 for (
auto [outerLoop, innerLoop] :
1635 llvm::zip_equal(loops.drop_back(), loops.drop_front())) {
1636 auto outerFor = dyn_cast_or_null<scf::ForOp>(outerLoop.getOperation());
1637 auto innerFor = dyn_cast_or_null<scf::ForOp>(innerLoop.getOperation());
1638 if (!outerFor || !innerFor)
1640 auto outerBBArgs = outerFor.getRegionIterArgs();
1641 auto innerIterArgs = innerFor.getInitArgs();
1642 if (outerBBArgs.size() != innerIterArgs.size())
1645 for (
auto [outerBBArg, innerIterArg] :
1646 llvm::zip_equal(outerBBArgs, innerIterArgs)) {
1647 if (!llvm::hasSingleElement(outerBBArg.getUses()) ||
1648 innerIterArg != outerBBArg)
1653 cast<scf::YieldOp>(outerFor.getBody()->getTerminator())->getOperands();
1654 ValueRange innerResults = innerFor.getResults();
1655 if (outerYields.size() != innerResults.size())
1657 for (
auto [outerYield, innerResult] :
1658 llvm::zip_equal(outerYields, innerResults)) {
1659 if (!llvm::hasSingleElement(innerResult.getUses()) ||
1660 outerYield != innerResult)
1669 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1670 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1671 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1672 if (!loBnds || !upBnds || !steps)
1675 for (
auto [lb,
ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1679 if (!lbCst || !ubCst || !stepCst)
1681 loopRanges.emplace_back(*lbCst, *ubCst, *stepCst);
1688 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1689 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1690 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1691 if (!loBnds || !upBnds || !steps)
1694 for (
auto [lb,
ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1700 tripCounts.push_back(*numIter);
1710 const unsigned numLoops = op.getNumLoops();
1711 assert(llvm::none_of(unrollFactors, [](uint64_t f) {
return f == 0; }) &&
1712 "Expected positive unroll factors");
1713 assert((!unrollFactors.empty() && (unrollFactors.size() <= numLoops)) &&
1714 "Expected non-empty unroll factors of size <= to the number of loops");
1717 if (llvm::all_of(unrollFactors, [](uint64_t f) {
return f == 1; }))
1719 op,
"Unrolling not applied if all factors are 1");
1722 if (llvm::hasSingleElement(op.getBody()->getOperations()))
1727 const unsigned firstLoopDimIdx = numLoops - unrollFactors.size();
1732 if (tripCounts.empty())
1734 op,
"Failed to compute constant trip counts for the loop. Note that "
1735 "dynamic loop sizes are not supported.");
1737 for (
unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1738 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1739 if (tripCounts[dimIdx].urem(unrollFactor) != 0)
1741 op,
"Unroll factors don't divide the iteration space evenly");
1744 std::optional<SmallVector<OpFoldResult>> maybeFoldSteps = op.getLoopSteps();
1745 if (!maybeFoldSteps)
1748 for (
auto step : *maybeFoldSteps)
1751 for (
unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1752 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1753 if (unrollFactor == 1)
1755 const size_t origStep = steps[dimIdx];
1756 const int64_t newStep = origStep * unrollFactor;
1760 auto yieldedValues = op.getBody()->getTerminator()->getOperands();
1763 op.getBody(), op.getInductionVars()[dimIdx], unrollFactor,
1766 const AffineExpr expr = b.getAffineDimExpr(0) + (origStep * i);
1768 b.getDimIdentityMap().dropResult(0).insertResult(expr, 0);
1769 return affine::AffineApplyOp::create(b, iv.getLoc(), map,
1772 annotateFn, iterArgs, yieldedValues, &clonedToSrcOpsMap);
1777 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.
Type getType() const
Return the type of this value.
bool replaceUsesWithIf(Value newValue, function_ref< bool(OpOperand &)> shouldReplace)
Replace all uses of 'this' value with 'newValue' if the given callback returns true.
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
FailureOr< std::pair< scf::ForOp, scf::ForOp > > splitForOpAtPoint(RewriterBase &rewriter, scf::ForOp forOp, Value splitPoint)
Splits forOp into two consecutive loops at splitPoint: first: [lowerBound, splitPoint) second: [split...
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.
std::optional< uint64_t > getConstantUIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer zero-extended to 64 bits.
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.