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);
517 Value unrollFactorCst = arith::ConstantOp::create(
521 arith::RemSIOp::create(boundsBuilder, loc, tripCount, unrollFactorCst);
523 Value tripCountEvenMultiple =
524 arith::SubIOp::create(boundsBuilder, loc, tripCount, tripCountRem);
526 upperBoundUnrolled = arith::AddIOp::create(
527 boundsBuilder, loc, lowerBound,
528 arith::MulIOp::create(boundsBuilder, loc, tripCountEvenMultiple, step));
531 arith::MulIOp::create(boundsBuilder, loc, step, unrollFactorCst);
537 if (generateEpilogueLoop) {
539 if (failed(splitLoops))
541 forOp = splitLoops->first;
542 scf::ForOp epilogueForOp = splitLoops->second;
543 if (!shouldPromoteIfSingleIteration ||
544 epilogueForOp.promoteIfSingleIteration(rewriter).failed())
547 forOp.setUpperBound(upperBoundUnrolled);
551 forOp.setStep(stepUnrolled);
553 auto iterArgs =
ValueRange(forOp.getRegionIterArgs());
554 auto yieldedValues = forOp.getBody()->getTerminator()->getOperands();
557 forOp.getBody(), forOp.getInductionVar(), unrollFactor,
560 auto stride = arith::MulIOp::create(
562 arith::ConstantOp::create(b, loc,
563 b.getIntegerAttr(iv.getType(), i)));
564 return arith::AddIOp::create(b, loc, iv, stride);
566 annotateFn, iterArgs, yieldedValues);
569 if (!shouldPromoteIfSingleIteration ||
570 forOp.promoteIfSingleIteration(rewriter).failed())
578 std::optional<APInt> mayBeConstantTripCount = forOp.getStaticTripCount();
579 if (!mayBeConstantTripCount.has_value())
581 const APInt &tripCount = *mayBeConstantTripCount;
582 if (tripCount.isZero())
584 if (tripCount.isOne())
585 return forOp.promoteIfSingleIteration(rewriter);
592 auto walkResult = forOp.walk([&](scf::ForOp innerForOp) {
593 if (!forOp.isDefinedOutsideOfLoop(innerForOp.getLowerBound()) ||
594 !forOp.isDefinedOutsideOfLoop(innerForOp.getUpperBound()) ||
595 !forOp.isDefinedOutsideOfLoop(innerForOp.getStep()))
600 return !walkResult.wasInterrupted();
605 uint64_t unrollJamFactor) {
606 assert(unrollJamFactor > 0 &&
"unroll jam factor should be positive");
608 if (unrollJamFactor == 1)
614 LDBG() <<
"failed to unroll and jam: inner bounds are not invariant";
619 if (forOp->getNumResults() > 0) {
620 LDBG() <<
"failed to unroll and jam: unsupported loop with results";
626 std::optional<APInt> tripCount = forOp.getStaticTripCount();
627 if (!tripCount.has_value()) {
629 LDBG() <<
"failed to unroll and jam: trip count could not be determined";
632 uint64_t tripCountValue = tripCount->getZExtValue();
633 if (tripCountValue == 0)
635 if (unrollJamFactor > tripCountValue) {
636 LDBG() <<
"unroll and jam factor is greater than trip count, set factor to "
639 unrollJamFactor = tripCountValue;
640 }
else if (tripCountValue % unrollJamFactor != 0) {
641 LDBG() <<
"failed to unroll and jam: unsupported trip count that is not a "
642 "multiple of unroll jam factor";
647 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
657 forOp.walk([&](scf::ForOp innerForOp) { innerLoops.push_back(innerForOp); });
668 for (scf::ForOp oldForOp : innerLoops) {
670 ValueRange oldIterOperands = oldForOp.getInits();
671 ValueRange oldIterArgs = oldForOp.getRegionIterArgs();
673 cast<scf::YieldOp>(oldForOp.getBody()->getTerminator()).getOperands();
676 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
677 dupIterOperands.append(oldIterOperands.begin(), oldIterOperands.end());
678 dupYieldOperands.append(oldYieldOperands.begin(), oldYieldOperands.end());
682 bool forOpReplaced = oldForOp == forOp;
683 scf::ForOp newForOp =
684 cast<scf::ForOp>(*oldForOp.replaceWithAdditionalYields(
685 rewriter, dupIterOperands,
false,
687 return dupYieldOperands;
689 newInnerLoops.push_back(newForOp);
694 ValueRange newIterArgs = newForOp.getRegionIterArgs();
695 unsigned oldNumIterArgs = oldIterArgs.size();
696 ValueRange newResults = newForOp.getResults();
697 unsigned oldNumResults = newResults.size() / unrollJamFactor;
698 assert(oldNumIterArgs == oldNumResults &&
699 "oldNumIterArgs must be the same as oldNumResults");
700 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
701 for (
unsigned j = 0;
j < oldNumIterArgs; ++
j) {
705 operandMaps[i - 1].map(newIterArgs[
j],
706 newIterArgs[i * oldNumIterArgs +
j]);
707 operandMaps[i - 1].map(newResults[
j],
708 newResults[i * oldNumResults +
j]);
715 int64_t step = forOp.getConstantStep()->getSExtValue();
717 forOp.getLoc(), forOp.getStep(),
719 forOp.getLoc(), rewriter.
getIndexAttr(unrollJamFactor)));
720 forOp.setStep(newStep);
721 auto forOpIV = forOp.getInductionVar();
724 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
725 for (
auto &subBlock : subBlocks) {
728 OpBuilder builder(subBlock.first->getBlock(), std::next(subBlock.second));
732 if (!forOpIV.use_empty()) {
737 builder.
createOrFold<arith::AddIOp>(forOp.getLoc(), forOpIV, ivTag);
738 operandMaps[i - 1].map(forOpIV, ivUnroll);
741 for (
auto it = subBlock.first; it != std::next(subBlock.second); ++it)
742 builder.
clone(*it, operandMaps[i - 1]);
745 for (
auto newForOp : newInnerLoops) {
746 unsigned oldNumIterOperands =
747 newForOp.getNumRegionIterArgs() / unrollJamFactor;
748 unsigned numControlOperands = newForOp.getNumControlOperands();
749 auto yieldOp = cast<scf::YieldOp>(newForOp.getBody()->getTerminator());
750 unsigned oldNumYieldOperands = yieldOp.getNumOperands() / unrollJamFactor;
751 assert(oldNumIterOperands == oldNumYieldOperands &&
752 "oldNumIterOperands must be the same as oldNumYieldOperands");
753 for (
unsigned j = 0;
j < oldNumIterOperands; ++
j) {
757 newForOp.setOperand(numControlOperands + i * oldNumIterOperands +
j,
758 operandMaps[i - 1].lookupOrDefault(
759 newForOp.getOperand(numControlOperands +
j)));
761 i * oldNumYieldOperands +
j,
762 operandMaps[i - 1].lookupOrDefault(yieldOp.getOperand(
j)));
768 (
void)forOp.promoteIfSingleIteration(rewriter);
776 Range normalizedLoopBounds;
782 normalizedLoopBounds.
size =
784 return normalizedLoopBounds;
796 bool isZeroBased =
false;
798 isZeroBased = lbCst.value() == 0;
800 bool isStepOne =
false;
802 isStepOne = stepCst.value() == 1;
806 "expected matching types");
811 if (isZeroBased && isStepOne)
812 return {lb,
ub, step};
822 newUpperBound = rewriter.
createOrFold<arith::CeilDivSIOp>(
830 return {newLowerBound, newUpperBound, newStep};
844 Value denormalizedIvVal =
851 if (
Operation *preservedUse = denormalizedIvVal.getDefiningOp()) {
852 preservedUses.insert(preservedUse);
861 if (
getType(origLb).isIndex()) {
865 Value denormalizedIv;
870 Value scaled = normalizedIv;
872 Value origStepValue =
874 scaled = arith::MulIOp::create(rewriter, loc, normalizedIv, origStepValue);
877 denormalizedIv = scaled;
880 denormalizedIv = arith::AddIOp::create(rewriter, loc, scaled, origLbValue);
889 assert(!values.empty() &&
"unexecpted empty array");
894 for (
auto v : values) {
904 assert(!values.empty() &&
"unexpected empty list");
910 std::optional<Value> productOf;
911 for (
auto v : values) {
913 if (vOne && vOne.value() == 1)
916 productOf = arith::MulIOp::create(rewriter, loc, productOf.value(), v)
922 productOf = arith::ConstantOp::create(
926 return productOf.value();
942 Operation *delinearizedOp = affine::AffineDelinearizeIndexOp::create(
943 rewriter, loc, linearizedIv, ubs);
944 auto resultVals = llvm::map_to_vector(
952 llvm::BitVector isUbOne(ubs.size());
953 for (
auto [
index,
ub] : llvm::enumerate(ubs)) {
955 if (ubCst && ubCst.value() == 1)
960 unsigned numLeadingOneUbs = 0;
961 for (
auto [
index,
ub] : llvm::enumerate(ubs)) {
962 if (!isUbOne.test(
index)) {
965 delinearizedIvs[
index] = arith::ConstantOp::create(
970 Value previous = linearizedIv;
971 for (
unsigned i = numLeadingOneUbs, e = ubs.size(); i < e; ++i) {
972 unsigned idx = ubs.size() - (i - numLeadingOneUbs) - 1;
973 if (i != numLeadingOneUbs && !isUbOne.test(idx + 1)) {
974 previous = arith::DivSIOp::create(rewriter, loc, previous, ubs[idx + 1]);
979 if (!isUbOne.test(idx)) {
980 iv = arith::RemSIOp::create(rewriter, loc, previous, ubs[idx]);
983 iv = arith::ConstantOp::create(
984 rewriter, loc, rewriter.
getZeroAttr(ubs[idx].getType()));
987 delinearizedIvs[idx] = iv;
989 return {delinearizedIvs, preservedUsers};
994 if (loops.size() < 2)
997 scf::ForOp innermost = loops.back();
998 scf::ForOp outermost = loops.front();
1002 for (
auto loop : loops) {
1004 if (step.value() == 0) {
1011 for (
auto loop : loops) {
1014 Value lb = loop.getLowerBound();
1015 Value ub = loop.getUpperBound();
1016 Value step = loop.getStep();
1022 newLoopRange.offset));
1024 newLoopRange.size));
1026 newLoopRange.stride));
1030 loop.getInductionVar(), lb, step);
1039 loops, [](
auto loop) {
return loop.getUpperBound(); });
1041 outermost.setUpperBound(upperBound);
1046 rewriter, loc, outermost.getInductionVar(), upperBounds);
1050 for (
int i = loops.size() - 1; i > 0; --i) {
1051 auto outerLoop = loops[i - 1];
1052 auto innerLoop = loops[i];
1054 Operation *innerTerminator = innerLoop.getBody()->getTerminator();
1055 auto yieldedVals = llvm::to_vector(innerTerminator->
getOperands());
1056 assert(llvm::equal(outerLoop.getRegionIterArgs(), innerLoop.getInitArgs()));
1057 for (
Value &yieldedVal : yieldedVals) {
1061 if (yieldedVal == innerLoop.getInductionVar()) {
1062 yieldedVal = delinearizeIvs[i];
1067 auto iter = llvm::find(innerLoop.getRegionIterArgs(), yieldedVal);
1068 if (iter != innerLoop.getRegionIterArgs().end()) {
1069 unsigned iterArgIndex = iter - innerLoop.getRegionIterArgs().begin();
1071 assert(iterArgIndex < innerLoop.getInitArgs().size());
1072 yieldedVal = innerLoop.getInitArgs()[iterArgIndex];
1075 rewriter.
eraseOp(innerTerminator);
1078 innerBlockArgs.push_back(delinearizeIvs[i]);
1079 llvm::append_range(innerBlockArgs, outerLoop.getRegionIterArgs());
1082 rewriter.
replaceOp(innerLoop, yieldedVals);
1088 if (loops.empty()) {
1091 IRRewriter rewriter(loops.front().getContext());
1096 LogicalResult
result(failure());
1106 for (
unsigned i = 0, e = loops.size(); i < e; ++i) {
1107 operandsDefinedAbove[i] = i;
1108 for (
unsigned j = 0;
j < i; ++
j) {
1110 loops[i].getUpperBound(),
1111 loops[i].getStep()};
1113 operandsDefinedAbove[i] =
j;
1124 iterArgChainStart[0] = 0;
1125 for (
unsigned i = 1, e = loops.size(); i < e; ++i) {
1127 iterArgChainStart[i] = i;
1128 auto outerloop = loops[i - 1];
1129 auto innerLoop = loops[i];
1130 if (outerloop.getNumRegionIterArgs() != innerLoop.getNumRegionIterArgs()) {
1133 if (!llvm::equal(outerloop.getRegionIterArgs(), innerLoop.getInitArgs())) {
1136 auto outerloopTerminator = outerloop.getBody()->getTerminator();
1137 if (!llvm::equal(outerloopTerminator->getOperands(),
1138 innerLoop.getResults())) {
1141 iterArgChainStart[i] = iterArgChainStart[i - 1];
1147 for (
unsigned end = loops.size(); end > 0; --end) {
1149 for (; start < end - 1; ++start) {
1151 *std::max_element(std::next(operandsDefinedAbove.begin(), start),
1152 std::next(operandsDefinedAbove.begin(), end));
1155 if (iterArgChainStart[end - 1] > start)
1164 if (start != end - 1)
1172 ArrayRef<std::vector<unsigned>> combinedDimensions) {
1178 auto sortedDimensions = llvm::to_vector<3>(combinedDimensions);
1179 for (
auto &dims : sortedDimensions)
1184 for (
unsigned i = 0, e = loops.getNumLoops(); i < e; ++i) {
1187 Value lb = loops.getLowerBound()[i];
1188 Value ub = loops.getUpperBound()[i];
1189 Value step = loops.getStep()[i];
1192 rewriter, loops.getLoc(), newLoopRange.size));
1203 for (
auto &sortedDimension : sortedDimensions) {
1205 for (
auto idx : sortedDimension) {
1206 newUpperBound = arith::MulIOp::create(rewriter, loc, newUpperBound,
1207 normalizedUpperBounds[idx]);
1209 lowerBounds.push_back(cst0);
1210 steps.push_back(cst1);
1211 upperBounds.push_back(newUpperBound);
1220 auto newPloop = scf::ParallelOp::create(
1221 rewriter, loc, lowerBounds, upperBounds, steps,
1223 for (
unsigned i = 0, e = combinedDimensions.size(); i < e; ++i) {
1224 Value previous = ploopIVs[i];
1225 unsigned numberCombinedDimensions = combinedDimensions[i].size();
1227 for (
unsigned j = numberCombinedDimensions - 1;
j > 0; --
j) {
1228 unsigned idx = combinedDimensions[i][
j];
1231 Value iv = arith::RemSIOp::create(insideBuilder, loc, previous,
1232 normalizedUpperBounds[idx]);
1238 previous = arith::DivSIOp::create(insideBuilder, loc, previous,
1239 normalizedUpperBounds[idx]);
1243 unsigned idx = combinedDimensions[i][0];
1245 previous, loops.getRegion());
1250 loops.getBody()->back().erase();
1251 newPloop.getBody()->getOperations().splice(
1253 loops.getBody()->getOperations());
1266 return op != inner.getOperation();
1269 LogicalResult status =
success();
1271 for (
auto &op : outer.getBody()->without_terminator()) {
1273 if (&op == inner.getOperation())
1276 if (forwardSlice.count(&op) > 0) {
1281 if (isa<scf::ForOp>(op))
1284 if (op.getNumRegions() > 0) {
1294 toHoist.push_back(&op);
1296 auto *outerForOp = outer.getOperation();
1297 for (
auto *op : toHoist)
1298 op->moveBefore(outerForOp);
1307 LogicalResult status =
success();
1308 const Loops &interTile = tileLoops.first;
1309 const Loops &intraTile = tileLoops.second;
1310 auto size = interTile.size();
1311 assert(size == intraTile.size());
1314 for (
unsigned s = 1; s < size; ++s)
1315 status = succeeded(status) ?
hoistOpsBetween(intraTile[0], intraTile[s])
1317 for (
unsigned s = 1; s < size; ++s)
1318 status = succeeded(status) ?
hoistOpsBetween(interTile[0], interTile[s])
1327template <
typename T>
1330 unsigned maxLoops = std::numeric_limits<unsigned>::max()) {
1331 for (
unsigned i = 0; i < maxLoops; ++i) {
1332 forOps.push_back(rootForOp);
1333 Block &body = rootForOp.getRegion().front();
1334 if (body.
begin() != std::prev(body.
end(), 2))
1337 rootForOp = dyn_cast<T>(&body.
front());
1345 assert(!forOp.getUnsignedCmp() &&
"unsigned loops are not supported");
1346 auto originalStep = forOp.getStep();
1347 auto iv = forOp.getInductionVar();
1350 forOp.setStep(arith::MulIOp::create(
b, forOp.getLoc(), originalStep, factor));
1353 for (
auto t : targets) {
1354 assert(!t.getUnsignedCmp() &&
"unsigned loops are not supported");
1357 auto begin = t.getBody()->begin();
1358 auto nOps = t.getBody()->getOperations().size();
1362 Value stepped = arith::AddIOp::create(
b, t.getLoc(), iv, forOp.getStep());
1364 arith::MinSIOp::create(
b, t.getLoc(), forOp.getUpperBound(), stepped);
1367 auto newForOp = scf::ForOp::create(
b, t.getLoc(), iv,
ub, originalStep);
1368 newForOp.getBody()->getOperations().splice(
1369 newForOp.getBody()->getOperations().begin(),
1370 t.getBody()->getOperations(), begin, std::next(begin, nOps - 1));
1372 newForOp.getRegion());
1374 innerLoops.push_back(newForOp);
1385 for (
auto it : llvm::zip(forOps, sizes)) {
1386 auto step =
stripmineSink(std::get<0>(it), std::get<1>(it), currentTargets);
1387 res.push_back(step);
1388 currentTargets = step;
1397 res.push_back(llvm::getSingleElement(loops));
1405 forOps.reserve(sizes.size());
1407 if (forOps.size() < sizes.size())
1408 sizes = sizes.take_front(forOps.size());
1410 return ::tile(forOps, sizes, forOps.back());
1423 forOps.reserve(sizes.size());
1425 if (forOps.size() < sizes.size())
1426 sizes = sizes.take_front(forOps.size());
1433 if (llvm::any_of(forOps,
1434 [](scf::ForOp op) {
return !op.getInitArgs().empty(); }))
1442 tileSizes.reserve(sizes.size());
1443 for (
unsigned i = 0, e = sizes.size(); i < e; ++i) {
1444 assert(sizes[i] > 0 &&
"expected strictly positive size for strip-mining");
1446 auto forOp = forOps[i];
1448 auto loc = forOp.getLoc();
1449 Value diff = arith::SubIOp::create(builder, loc, forOp.getUpperBound(),
1450 forOp.getLowerBound());
1452 Value iterationsPerBlock =
1454 tileSizes.push_back(iterationsPerBlock);
1458 auto intraTile =
tile(forOps, tileSizes, forOps.back());
1459 TileLoops tileLoops = std::make_pair(forOps, intraTile);
1470 scf::ForallOp source,
1472 unsigned numTargetOuts =
target.getNumResults();
1473 unsigned numSourceOuts = source.getNumResults();
1477 llvm::append_range(fusedOuts,
target.getOutputs());
1478 llvm::append_range(fusedOuts, source.getOutputs());
1482 scf::ForallOp fusedLoop = scf::ForallOp::create(
1483 rewriter, source.getLoc(), source.getMixedLowerBound(),
1484 source.getMixedUpperBound(), source.getMixedStep(), fusedOuts,
1485 source.getMapping());
1489 mapping.
map(
target.getInductionVars(), fusedLoop.getInductionVars());
1490 mapping.
map(source.getInductionVars(), fusedLoop.getInductionVars());
1494 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1495 mapping.
map(source.getRegionIterArgs(),
1496 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1501 rewriter.
clone(op, mapping);
1502 for (
Operation &op : source.getBody()->without_terminator())
1503 rewriter.
clone(op, mapping);
1506 scf::InParallelOp targetTerm =
target.getTerminator();
1507 scf::InParallelOp sourceTerm = source.getTerminator();
1508 scf::InParallelOp fusedTerm = fusedLoop.getTerminator();
1510 for (
Operation &op : targetTerm.getYieldingOps())
1511 rewriter.
clone(op, mapping);
1512 for (
Operation &op : sourceTerm.getYieldingOps())
1513 rewriter.
clone(op, mapping);
1516 rewriter.
replaceOp(
target, fusedLoop.getResults().take_front(numTargetOuts));
1517 rewriter.
replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1525 assert(source.getUnsignedCmp() ==
target.getUnsignedCmp() &&
1526 "incompatible signedness");
1527 unsigned numTargetOuts =
target.getNumResults();
1528 unsigned numSourceOuts = source.getNumResults();
1532 llvm::append_range(fusedInitArgs,
target.getInitArgs());
1533 llvm::append_range(fusedInitArgs, source.getInitArgs());
1538 scf::ForOp fusedLoop = scf::ForOp::create(
1539 rewriter, source.getLoc(), source.getLowerBound(), source.getUpperBound(),
1540 source.getStep(), fusedInitArgs,
nullptr,
1541 source.getUnsignedCmp());
1545 mapping.
map(
target.getInductionVar(), fusedLoop.getInductionVar());
1547 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1548 mapping.
map(source.getInductionVar(), fusedLoop.getInductionVar());
1549 mapping.
map(source.getRegionIterArgs(),
1550 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1555 rewriter.
clone(op, mapping);
1556 for (
Operation &op : source.getBody()->without_terminator())
1557 rewriter.
clone(op, mapping);
1561 for (
Value operand :
target.getBody()->getTerminator()->getOperands())
1563 for (
Value operand : source.getBody()->getTerminator()->getOperands())
1565 if (!yieldResults.empty())
1566 scf::YieldOp::create(rewriter, source.getLoc(), yieldResults);
1569 rewriter.
replaceOp(
target, fusedLoop.getResults().take_front(numTargetOuts));
1570 rewriter.
replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1576 scf::ForallOp forallOp) {
1581 if (forallOp.isNormalized())
1585 auto loc = forallOp.getLoc();
1588 for (
auto [lb,
ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
1589 Range normalizedLoopParams =
1591 newUbs.push_back(normalizedLoopParams.
size);
1597 auto normalizedForallOp = scf::ForallOp::create(
1598 rewriter, loc, newUbs, forallOp.getOutputs(), forallOp.getMapping(),
1602 normalizedForallOp.getBodyRegion(),
1603 normalizedForallOp.getBodyRegion().begin());
1605 rewriter.
eraseBlock(&normalizedForallOp.getBodyRegion().back());
1609 for (
auto [idx, iv] :
1610 llvm::enumerate(normalizedForallOp.getInductionVars())) {
1616 rewriter.
replaceOp(forallOp, normalizedForallOp);
1617 return normalizedForallOp;
1622 assert(!loops.empty() &&
"unexpected empty loop nest");
1623 if (loops.size() == 1)
1624 return isa_and_nonnull<scf::ForOp>(loops.front().getOperation());
1625 for (
auto [outerLoop, innerLoop] :
1626 llvm::zip_equal(loops.drop_back(), loops.drop_front())) {
1627 auto outerFor = dyn_cast_or_null<scf::ForOp>(outerLoop.getOperation());
1628 auto innerFor = dyn_cast_or_null<scf::ForOp>(innerLoop.getOperation());
1629 if (!outerFor || !innerFor)
1631 auto outerBBArgs = outerFor.getRegionIterArgs();
1632 auto innerIterArgs = innerFor.getInitArgs();
1633 if (outerBBArgs.size() != innerIterArgs.size())
1636 for (
auto [outerBBArg, innerIterArg] :
1637 llvm::zip_equal(outerBBArgs, innerIterArgs)) {
1638 if (!llvm::hasSingleElement(outerBBArg.getUses()) ||
1639 innerIterArg != outerBBArg)
1644 cast<scf::YieldOp>(outerFor.getBody()->getTerminator())->getOperands();
1645 ValueRange innerResults = innerFor.getResults();
1646 if (outerYields.size() != innerResults.size())
1648 for (
auto [outerYield, innerResult] :
1649 llvm::zip_equal(outerYields, innerResults)) {
1650 if (!llvm::hasSingleElement(innerResult.getUses()) ||
1651 outerYield != innerResult)
1660 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1661 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1662 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1663 if (!loBnds || !upBnds || !steps)
1666 for (
auto [lb,
ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1670 if (!lbCst || !ubCst || !stepCst)
1672 loopRanges.emplace_back(*lbCst, *ubCst, *stepCst);
1679 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1680 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1681 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1682 if (!loBnds || !upBnds || !steps)
1685 for (
auto [lb,
ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1691 tripCounts.push_back(*numIter);
1701 const unsigned numLoops = op.getNumLoops();
1702 assert(llvm::none_of(unrollFactors, [](uint64_t f) {
return f == 0; }) &&
1703 "Expected positive unroll factors");
1704 assert((!unrollFactors.empty() && (unrollFactors.size() <= numLoops)) &&
1705 "Expected non-empty unroll factors of size <= to the number of loops");
1708 if (llvm::all_of(unrollFactors, [](uint64_t f) {
return f == 1; }))
1710 op,
"Unrolling not applied if all factors are 1");
1713 if (llvm::hasSingleElement(op.getBody()->getOperations()))
1718 const unsigned firstLoopDimIdx = numLoops - unrollFactors.size();
1723 if (tripCounts.empty())
1725 op,
"Failed to compute constant trip counts for the loop. Note that "
1726 "dynamic loop sizes are not supported.");
1728 for (
unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1729 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1730 if (tripCounts[dimIdx].urem(unrollFactor) != 0)
1732 op,
"Unroll factors don't divide the iteration space evenly");
1735 std::optional<SmallVector<OpFoldResult>> maybeFoldSteps = op.getLoopSteps();
1736 if (!maybeFoldSteps)
1739 for (
auto step : *maybeFoldSteps)
1742 for (
unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1743 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1744 if (unrollFactor == 1)
1746 const size_t origStep = steps[dimIdx];
1747 const int64_t newStep = origStep * unrollFactor;
1751 auto yieldedValues = op.getBody()->getTerminator()->getOperands();
1754 op.getBody(), op.getInductionVars()[dimIdx], unrollFactor,
1757 const AffineExpr expr = b.getAffineDimExpr(0) + (origStep * i);
1759 b.getDimIdentityMap().dropResult(0).insertResult(expr, 0);
1760 return affine::AffineApplyOp::create(b, iv.getLoc(), map,
1763 annotateFn, iterArgs, yieldedValues, &clonedToSrcOpsMap);
1768 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
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.