26#include "llvm/ADT/MapVector.h"
27#include "llvm/Support/Debug.h"
28#include "llvm/Support/DebugLog.h"
29#include "llvm/Support/raw_ostream.h"
32#define DEBUG_TYPE "loop-utils"
37using llvm::SmallMapVector;
58 auto lbMap = forOp.getLowerBoundMap();
59 auto lb = AffineApplyOp::create(
b, forOp.getLoc(), lbMap,
60 forOp.getLowerBoundOperands());
69 int64_t step = forOp.getStepAsInt();
70 for (
unsigned i = 0, e = tripCountMap.
getNumResults(); i < e; i++) {
71 auto tripCountExpr = tripCountMap.
getResult(i);
72 bumpExprs[i] = (tripCountExpr - tripCountExpr % unrollFactor) * step;
76 AffineApplyOp::create(
b, forOp.getLoc(), bumpMap, tripCountOperands);
80 for (
unsigned i = 0, e = bumpExprs.size(); i < e; i++)
81 newUbExprs[i] =
b.getAffineDimExpr(0) +
b.getAffineDimExpr(i + 1);
83 cleanupLbOperands.clear();
84 cleanupLbOperands.push_back(lb);
85 cleanupLbOperands.append(bumpValues.begin(), bumpValues.end());
93 for (
auto v : bumpValues)
95 v.getDefiningOp()->erase();
107 AffineMap lbMap = forOp.getLowerBoundMap();
109 "expected single-result bounds");
117 {map.getResult(0), shiftedLbMap.getResult(0)}, forOp.getContext());
118 ValueRange lbOperands = forOp.getLowerBoundOperands();
120 operands.begin() + numDims);
121 llvm::append_range(maxOperands, lbOperands.take_front(numLbDims));
122 llvm::append_range(maxOperands,
124 llvm::append_range(maxOperands, lbOperands.drop_front(numLbDims));
132 if (
auto cst = dyn_cast<AffineConstantExpr>(diff); cst && cst.getValue() >= 0)
134 operands.assign(maxOperands.begin(), maxOperands.end());
142 auto iterOperands = forOp.getInits();
143 auto iterArgs = forOp.getRegionIterArgs();
144 for (
auto e : llvm::zip(iterOperands, iterArgs))
145 std::get<1>(e).replaceAllUsesWith(std::get<0>(e));
148 auto outerResults = forOp.getResults();
149 auto innerResults = forOp.getBody()->getTerminator()->getOperands();
150 for (
auto e : llvm::zip(outerResults, innerResults))
151 std::get<0>(e).replaceAllUsesWith(std::get<1>(e));
157 std::optional<APInt> tripCount = forOp.getStaticTripCount();
158 if (!tripCount || *tripCount != 1)
162 if (forOp.getLowerBoundMap().getNumResults() != 1)
166 auto iv = forOp.getInductionVar();
167 auto *parentBlock = forOp->getBlock();
168 if (!iv.use_empty()) {
169 if (forOp.hasConstantLowerBound()) {
170 auto func = forOp->getParentOfType<FunctionOpInterface>();
173 builder.setInsertionPointToStart(&
func.getFunctionBody().front());
175 builder.setInsertionPoint(forOp);
177 builder, forOp.getLoc(), forOp.getConstantLowerBound());
178 iv.replaceAllUsesWith(constOp);
180 auto lbOperands = forOp.getLowerBoundOperands();
181 auto lbMap = forOp.getLowerBoundMap();
185 iv.replaceAllUsesWith(lbOperands[0]);
188 AffineApplyOp::create(builder, forOp.getLoc(), lbMap, lbOperands);
189 iv.replaceAllUsesWith(affineApplyOp);
198 forOp.getBody()->back().erase();
200 forOp.getBody()->getOperations());
215 unsigned offset, AffineForOp srcForOp,
OpBuilder b) {
216 auto lbOperands = srcForOp.getLowerBoundOperands();
217 auto ubOperands = srcForOp.getUpperBoundOperands();
223 AffineForOp::create(
b, srcForOp.getLoc(), lbOperands, lbMap, ubOperands,
224 ubMap, srcForOp.getStepAsInt());
225 auto loopChunkIV = loopChunk.getInductionVar();
226 auto srcIV = srcForOp.getInductionVar();
231 for (
const auto &it : llvm::drop_begin(opGroupQueue, offset)) {
232 uint64_t shift = it.first;
233 auto ops = it.second;
238 if (!srcIV.use_empty() && shift != 0) {
239 auto ivRemap = AffineApplyOp::create(
240 bodyBuilder, srcForOp.getLoc(),
241 bodyBuilder.getSingleDimShiftAffineMap(
242 -
static_cast<int64_t>(srcForOp.getStepAsInt() * shift)),
244 operandMap.
map(srcIV, ivRemap);
246 operandMap.
map(srcIV, loopChunkIV);
249 bodyBuilder.clone(*op, operandMap);
252 return AffineForOp();
269 bool unrollPrologueEpilogue) {
270 assert(forOp.getBody()->getOperations().size() == shifts.size() &&
271 "too few/many shifts");
272 if (forOp.getBody()->begin() == std::prev(forOp.getBody()->end()))
279 auto mayBeConstTripCount = forOp.getStaticTripCount();
280 if (!mayBeConstTripCount) {
281 LLVM_DEBUG(forOp.emitRemark(
"non-constant trip count loop not handled"));
284 uint64_t tripCount = mayBeConstTripCount->getZExtValue();
287 "shifts will lead to an invalid transformation\n");
289 int64_t step = forOp.getStepAsInt();
291 unsigned numChildOps = shifts.size();
294 uint64_t maxShift = *llvm::max_element(shifts);
295 if (maxShift >= numChildOps) {
297 forOp.emitWarning(
"not shifting because shifts are unrealistically large");
304 std::vector<std::vector<Operation *>> sortedOpGroups(maxShift + 1);
306 for (
auto &op : forOp.getBody()->without_terminator()) {
307 auto shift = shifts[pos++];
308 sortedOpGroups[shift].push_back(&op);
316 AffineForOp prologue, epilogue;
321 std::vector<std::pair<uint64_t, ArrayRef<Operation *>>> opGroupQueue;
323 auto origLbMap = forOp.getLowerBoundMap();
324 uint64_t lbShift = 0;
326 for (uint64_t d = 0, e = sortedOpGroups.size(); d < e; ++d) {
328 if (sortedOpGroups[d].empty())
330 if (!opGroupQueue.empty()) {
332 "Queue expected to be empty when the first block is found");
337 if (lbShift + tripCount * step < d * step) {
339 b.getShiftedAffineMap(origLbMap, lbShift),
340 b.getShiftedAffineMap(origLbMap, lbShift + tripCount * step),
341 opGroupQueue, 0, forOp,
b);
343 opGroupQueue.clear();
344 lbShift += tripCount * step;
347 b.getShiftedAffineMap(origLbMap, d),
348 opGroupQueue, 0, forOp,
b);
355 AffineForOp::getCanonicalizationPatterns(patterns, res.
getContext());
358 res.getOperation(), std::move(patterns),
362 if (!erased && !prologue)
372 opGroupQueue.emplace_back(d, sortedOpGroups[d]);
377 for (
unsigned i = 0, e = opGroupQueue.size(); i < e; ++i) {
378 uint64_t ubShift = (opGroupQueue[i].first + tripCount) * step;
380 b.getShiftedAffineMap(origLbMap, ubShift),
381 opGroupQueue, i, forOp,
b);
390 if (unrollPrologueEpilogue && prologue)
392 if (unrollPrologueEpilogue && !epilogue && epilogue != prologue)
404 if (input.size() <= 1)
407 LDBG() <<
"Index set computation failed!";
411 LDBG() <<
"Non-hyperrectangular nests not supported for tiling!";
422 assert(input.size() == tileSizes.size() &&
"Too few/many tile sizes");
424 if (llvm::any_of(input,
425 [](AffineForOp op) {
return op.getNumResults() > 0; })) {
426 LDBG() <<
"Cannot tile nest where a loop has yield values";
432 LDBG() <<
"input loops not perfectly nested";
447 auto &ops = src.getBody()->getOperations();
448 dest.getBody()->getOperations().splice(loc, ops, ops.begin(),
449 std::prev(ops.end()));
461 AffineForOp rootAffineForOp,
unsigned width,
463 Location loc = rootAffineForOp.getLoc();
466 Operation *topLoop = rootAffineForOp.getOperation();
467 AffineForOp innermostPointLoop;
470 for (
unsigned i = 0; i < width; i++) {
473 AffineForOp pointLoop = AffineForOp::create(
b, loc, 0, 0);
474 pointLoop.getBody()->getOperations().splice(
477 tiledLoops[2 * width - 1 - i] = pointLoop;
478 topLoop = pointLoop.getOperation();
480 innermostPointLoop = pointLoop;
484 for (
unsigned i = width; i < 2 * width; i++) {
487 AffineForOp tileSpaceLoop = AffineForOp::create(
b, loc, 0, 0);
488 tileSpaceLoop.getBody()->getOperations().splice(
491 tiledLoops[2 * width - i - 1] = tileSpaceLoop;
492 topLoop = tileSpaceLoop.getOperation();
502 AffineForOp newInterTileLoop,
503 AffineForOp newIntraTileLoop,
513 assert(origLoop.hasConstantLowerBound() &&
514 "expected input loops to have constant lower bound.");
519 b.getAffineConstantExpr(origLoop.getConstantLowerBound());
526 ubOperands.reserve(
ub.getNumOperands() + 2);
532 ubOperands.push_back(
ub.getOperand(
j));
536 lbOperands.push_back(newInterTileLoop.getInductionVar());
537 ubOperands.push_back(newInterTileLoop.getInductionVar());
541 AffineExpr lbLoopIvExpr =
b.getAffineDimExpr(lbOperands.size() - 1);
542 AffineExpr ubLoopIvExpr =
b.getAffineDimExpr(ubOperands.size() - 1);
548 ubOperands.push_back(
ub.getOperand(origUbMap.
getNumDims() +
j));
551 lbOperands.push_back(tileSize);
552 ubOperands.push_back(tileSize);
564 lbBoundExprs.push_back(
565 ((lbLoopIvExpr - origLowerBoundExpr) * lbTileParameter) +
569 AffineExpr origLoopStep =
b.getAffineConstantExpr(origLoop.getStepAsInt());
573 ubBoundExprs.push_back(
574 ((ubLoopIvExpr - origLowerBoundExpr) * ubTileParameter) +
575 (ubTileParameter * origLoopStep) + origLowerBoundExpr);
577 ubBoundExprs.append(origUbMap.
getResults().begin(),
582 lbBoundExprs,
b.getContext());
583 newIntraTileLoop.setLowerBound(lbOperands, lbMap);
587 ubBoundExprs,
b.getContext());
588 newIntraTileLoop.setUpperBound(ubOperands, ubMap);
591 newIntraTileLoop.setStep(origLoop.getStepAsInt());
597 AffineForOp newLoop,
Value tileSize) {
598 OperandRange newLbOperands = origLoop.getLowerBoundOperands();
602 newLoop.setLowerBound(newLbOperands, origLoop.getLowerBoundMap());
613 assert(origLoop.hasConstantLowerBound() &&
614 "expected input loops to have constant lower bound.");
619 b.getAffineConstantExpr(origLoop.getConstantLowerBound());
624 ubOperands.reserve(
ub.getNumOperands() + 1);
627 ubOperands.push_back(
ub.getOperand(
j));
631 ubOperands.push_back(
ub.getOperand(origUbMap.
getNumDims() +
j));
634 ubOperands.push_back(tileSize);
646 if (origLoop.hasConstantUpperBound()) {
647 origUpperBound = origLoop.getConstantUpperBound();
650 origUpperBoundExpr =
b.getAffineConstantExpr(origUpperBound);
654 boundExprs.push_back(
656 (origUpperBoundExpr - origLowerBoundExpr).ceilDiv(tileParameter));
677 boundExprs.push_back(
679 (origUpperBoundExpr - origLowerBoundExpr).ceilDiv(tileParameter));
684 boundExprs,
b.getContext());
685 newLoop.setUpperBound(ubOperands, ubMap);
688 newLoop.setStep(origLoop.getStepAsInt());
700 assert(!origLoops.empty() &&
"expected atleast one loop in band");
701 assert(origLoops.size() == tileSizes.size() &&
702 "expected tiling parameter for each loop in band.");
705 unsigned width = origLoops.size();
708 for (
unsigned i = 0; i < width; ++i) {
713 for (
unsigned i = 0; i < width; ++i) {
715 newLoops[i + width], tileSizes[i]);
728 assert(!origLoops.empty());
729 assert(origLoops.size() == tileSizes.size());
732 unsigned width = origLoops.size();
735 for (
unsigned i = 0; i < width; i++) {
736 OperandRange newLbOperands = origLoops[i].getLowerBoundOperands();
737 OperandRange newUbOperands = origLoops[i].getUpperBoundOperands();
738 newLoops[i].setLowerBound(newLbOperands, origLoops[i].getLowerBoundMap());
739 newLoops[i].setUpperBound(newUbOperands, origLoops[i].getUpperBoundMap());
742 newLoops[i].setStep(tileSizes[i] * origLoops[i].getStepAsInt());
745 for (
unsigned i = 0; i < width; i++) {
747 AffineForOp forOp = origLoops[i];
748 std::optional<uint64_t> mayBeConstantCount = std::nullopt;
749 if (
auto staticTripCount = forOp.getStaticTripCount())
750 mayBeConstantCount = staticTripCount->getZExtValue();
753 newLoops[width + i].setLowerBound(
754 newLoops[i].getInductionVar(), lbMap);
756 newLoops[width + i].setStep(origLoops[i].getStepAsInt());
759 if (mayBeConstantCount && *mayBeConstantCount < tileSizes[i]) {
762 AffineMap ubMap =
b.getSingleDimShiftAffineMap(
763 *mayBeConstantCount * origLoops[i].getStepAsInt());
764 newLoops[width + i].setUpperBound(
765 newLoops[i].getInductionVar(), ubMap);
766 }
else if (largestDiv % tileSizes[i] != 0) {
776 ubOperands.reserve(
ub.getNumOperands() + 1);
779 ubOperands.push_back(
ub.getOperand(
j));
782 ubOperands.push_back(newLoops[i].getInductionVar());
786 ubOperands.push_back(
ub.getOperand(origUbMap.
getNumDims() +
j));
793 boundExprs.push_back(dim + tileSizes[i] * origLoops[i].getStepAsInt());
794 boundExprs.append(origUbMap.
getResults().begin(),
798 boundExprs,
b.getContext());
799 newLoops[width + i].setUpperBound(ubOperands, ubMap);
804 1, 0, dim + tileSizes[i] * origLoops[i].getStepAsInt());
805 newLoops[width + i].setUpperBound(newLoops[i].getInductionVar(), ubMap);
821 AffineForOp rootAffineForOp = origLoops[0];
824 unsigned width = input.size();
838 for (
unsigned i = 0; i < width; i++)
839 origLoopIVs[i].replaceAllUsesWith(tiledLoops[i + width].getInductionVar());
842 rootAffineForOp.erase();
845 *tiledNest = std::move(tiledLoops);
863 AffineForOp rootAffineForOp = origLoops[0];
864 unsigned width = input.size();
879 for (
unsigned i = 0; i < width; i++)
880 origLoopIVs[i].replaceAllUsesWith(tiledLoops[i + width].getInductionVar());
883 rootAffineForOp.erase();
886 *tiledNest = std::move(tiledLoops);
897 for (
unsigned i = 0; i < std::numeric_limits<unsigned>::max(); ++i) {
898 nestedLoops.push_back(root);
899 Block &body = root.getRegion().front();
900 if (body.
begin() != std::prev(body.
end(), 2))
903 root = dyn_cast<AffineForOp>(&body.
front());
911 std::optional<APInt> mayBeConstantTripCount = forOp.getStaticTripCount();
912 if (mayBeConstantTripCount.has_value()) {
913 uint64_t tripCount = mayBeConstantTripCount->getZExtValue();
926 uint64_t unrollFactor) {
927 std::optional<APInt> mayBeConstantTripCount = forOp.getStaticTripCount();
928 if (mayBeConstantTripCount.has_value() &&
929 mayBeConstantTripCount->ult(unrollFactor))
939 Block *loopBodyBlock,
Value forOpIV, uint64_t unrollFactor,
949 annotateFn = defaultAnnotateFn;
958 for (
unsigned i = 1; i < unrollFactor; i++) {
962 operandMap.
map(iterArgs, lastYielded);
967 Value ivUnroll = ivRemapFn(i, forOpIV, builder);
968 operandMap.
map(forOpIV, ivUnroll);
972 for (
auto it = loopBodyBlock->
begin(); it != std::next(srcBlockEnd); it++) {
974 annotateFn(i, clonedOp, builder);
981 for (
unsigned i = 0, e = lastYielded.size(); i < e; i++) {
982 Operation *defOp = yieldedValues[i].getDefiningOp();
983 if (defOp && defOp->
getBlock() == loopBodyBlock)
984 lastYielded[i] = operandMap.
lookup(yieldedValues[i]);
990 for (
auto it = loopBodyBlock->
begin(); it != std::next(srcBlockEnd); it++)
991 annotateFn(0, &*it, builder);
1000 uint64_t unrollFactor) {
1003 auto cleanupForOp = cast<AffineForOp>(builder.
clone(*forOp));
1007 auto results = forOp.getResults();
1008 auto cleanupResults = cleanupForOp.getResults();
1009 auto cleanupIterOperands = cleanupForOp.getInits();
1011 for (
auto e : llvm::zip(results, cleanupResults, cleanupIterOperands)) {
1012 std::get<0>(e).replaceAllUsesWith(std::get<1>(e));
1013 cleanupForOp->replaceUsesOfWith(std::get<2>(e), std::get<0>(e));
1027 cleanupForOp.setLowerBound(cleanupLbOperands, cleanupLbMap);
1033 forOp.setUpperBound(cleanupOperands, cleanupMap);
1040 AffineForOp forOp, uint64_t unrollFactor,
1042 bool cleanUpUnroll) {
1043 assert(unrollFactor > 0 &&
"unroll factor should be positive");
1045 std::optional<uint64_t> mayBeConstantTripCount = std::nullopt;
1046 if (
auto staticTripCount = forOp.getStaticTripCount())
1047 mayBeConstantTripCount = staticTripCount->getZExtValue();
1048 if (unrollFactor == 1) {
1055 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
1059 if (mayBeConstantTripCount && *mayBeConstantTripCount < unrollFactor) {
1060 if (cleanUpUnroll) {
1074 if (forOp.getLowerBoundMap().getNumResults() != 1 ||
1075 forOp.getUpperBoundMap().getNumResults() != 1)
1081 assert(
false &&
"cleanup loop lower bound map for single result lower "
1082 "and upper bound maps can always be determined");
1085 ValueRange iterArgs(forOp.getRegionIterArgs());
1086 auto yieldedValues = forOp.getBody()->getTerminator()->getOperands();
1089 int64_t step = forOp.getStepAsInt();
1090 forOp.setStep(step * unrollFactor);
1092 forOp.getBody(), forOp.getInductionVar(), unrollFactor,
1095 auto d0 = b.getAffineDimExpr(0);
1096 auto bumpMap = AffineMap::get(1, 0, d0 + i * step);
1097 return AffineApplyOp::create(b, forOp.getLoc(), bumpMap, iv);
1100 iterArgs, yieldedValues);
1108 uint64_t unrollJamFactor) {
1109 std::optional<APInt> mayBeConstantTripCount = forOp.getStaticTripCount();
1110 if (mayBeConstantTripCount.has_value() &&
1111 mayBeConstantTripCount->getZExtValue() < unrollJamFactor)
1119 auto walkResult = forOp.walk([&](AffineForOp aForOp) {
1120 for (
auto controlOperand : aForOp.getControlOperands()) {
1121 if (!forOp.isDefinedOutsideOfLoop(controlOperand))
1126 return !walkResult.wasInterrupted();
1131 uint64_t unrollJamFactor) {
1132 assert(unrollJamFactor > 0 &&
"unroll jam factor should be positive");
1134 std::optional<uint64_t> mayBeConstantTripCount = std::nullopt;
1135 if (
auto staticTripCount = forOp.getStaticTripCount())
1136 mayBeConstantTripCount = staticTripCount->getZExtValue();
1137 if (unrollJamFactor == 1) {
1144 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
1148 if (mayBeConstantTripCount && *mayBeConstantTripCount < unrollJamFactor) {
1149 LDBG() <<
"[failed] trip count < unroll-jam factor";
1165 forOp.walk([&](AffineForOp aForOp) {
1166 if (aForOp.getNumIterOperands() > 0)
1167 loopsWithIterArgs.push_back(aForOp);
1172 if (forOp.getNumIterOperands() > 0)
1182 if (forOp.getLowerBoundMap().getNumResults() != 1 ||
1183 forOp.getUpperBoundMap().getNumResults() != 1)
1186 assert(
false &&
"cleanup loop lower bound map for single result lower "
1187 "and upper bound maps can always be determined");
1199 for (AffineForOp oldForOp : loopsWithIterArgs) {
1201 ValueRange oldIterOperands = oldForOp.getInits();
1202 ValueRange oldIterArgs = oldForOp.getRegionIterArgs();
1204 cast<AffineYieldOp>(oldForOp.getBody()->getTerminator()).getOperands();
1207 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
1208 dupIterOperands.append(oldIterOperands.begin(), oldIterOperands.end());
1209 dupYieldOperands.append(oldYieldOperands.begin(), oldYieldOperands.end());
1213 bool forOpReplaced = oldForOp == forOp;
1214 AffineForOp newForOp =
1215 cast<AffineForOp>(*oldForOp.replaceWithAdditionalYields(
1216 rewriter, dupIterOperands,
false,
1218 return dupYieldOperands;
1220 newLoopsWithIterArgs.push_back(newForOp);
1225 ValueRange newIterArgs = newForOp.getRegionIterArgs();
1226 unsigned oldNumIterArgs = oldIterArgs.size();
1227 ValueRange newResults = newForOp.getResults();
1228 unsigned oldNumResults = newResults.size() / unrollJamFactor;
1229 assert(oldNumIterArgs == oldNumResults &&
1230 "oldNumIterArgs must be the same as oldNumResults");
1231 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
1232 for (
unsigned j = 0;
j < oldNumIterArgs; ++
j) {
1236 operandMaps[i - 1].map(newIterArgs[
j],
1237 newIterArgs[i * oldNumIterArgs +
j]);
1238 operandMaps[i - 1].map(newResults[
j],
1239 newResults[i * oldNumResults +
j]);
1245 int64_t step = forOp.getStepAsInt();
1246 forOp.setStep(step * unrollJamFactor);
1248 auto forOpIV = forOp.getInductionVar();
1250 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
1251 for (
auto &subBlock : subBlocks) {
1254 OpBuilder builder(subBlock.first->getBlock(), std::next(subBlock.second));
1263 AffineApplyOp::create(builder, forOp.getLoc(), bumpMap, forOpIV);
1264 operandMaps[i - 1].map(forOpIV, ivUnroll);
1267 for (
auto it = subBlock.first; it != std::next(subBlock.second); ++it)
1268 builder.
clone(*it, operandMaps[i - 1]);
1271 for (
auto newForOp : newLoopsWithIterArgs) {
1272 unsigned oldNumIterOperands =
1273 newForOp.getNumIterOperands() / unrollJamFactor;
1274 unsigned numControlOperands = newForOp.getNumControlOperands();
1275 auto yieldOp = cast<AffineYieldOp>(newForOp.getBody()->getTerminator());
1276 unsigned oldNumYieldOperands = yieldOp.getNumOperands() / unrollJamFactor;
1277 assert(oldNumIterOperands == oldNumYieldOperands &&
1278 "oldNumIterOperands must be the same as oldNumYieldOperands");
1279 for (
unsigned j = 0;
j < oldNumIterOperands; ++
j) {
1283 newForOp.setOperand(numControlOperands + i * oldNumIterOperands +
j,
1284 operandMaps[i - 1].lookupOrDefault(
1285 newForOp.getOperand(numControlOperands +
j)));
1287 i * oldNumYieldOperands +
j,
1288 operandMaps[i - 1].lookupOrDefault(yieldOp.getOperand(
j)));
1292 if (forOp.getNumResults() > 0) {
1298 auto loc = forOp.getLoc();
1299 unsigned oldNumResults = forOp.getNumResults() / unrollJamFactor;
1301 unsigned pos = reduction.iterArgPosition;
1302 Value lhs = forOp.getResult(pos);
1305 for (
unsigned i = unrollJamFactor - 1; i >= 1; --i) {
1306 rhs = forOp.getResult(i * oldNumResults + pos);
1312 assert(op &&
"Reduction op should have been created");
1316 forOp.getResult(pos).replaceAllUsesExcept(lhs, newOps);
1328 assert(&*forOpA.getBody()->begin() == forOpB.getOperation());
1329 auto &forOpABody = forOpA.getBody()->getOperations();
1330 auto &forOpBBody = forOpB.getBody()->getOperations();
1336 forOpABody, forOpABody.begin(),
1337 std::prev(forOpABody.end()));
1340 forOpABody.splice(forOpABody.begin(), forOpBBody, forOpBBody.begin(),
1341 std::prev(forOpBBody.end()));
1343 forOpBBody.splice(forOpBBody.begin(), forOpA->getBlock()->getOperations(),
1354 unsigned maxLoopDepth = loops.size();
1356 loopPermMapInv.resize(maxLoopDepth);
1357 for (
unsigned i = 0; i < maxLoopDepth; ++i)
1358 loopPermMapInv[loopPermMap[i]] = i;
1365 for (
const auto &depComps : depCompsVec) {
1366 assert(depComps.size() >= maxLoopDepth);
1369 for (
unsigned j = 0;
j < maxLoopDepth; ++
j) {
1370 unsigned permIndex = loopPermMapInv[
j];
1371 assert(depComps[permIndex].lb);
1372 int64_t depCompLb = *depComps[permIndex].lb;
1386 assert(loopPermMap.size() == loops.size() &&
"invalid loop perm map");
1387 unsigned maxLoopDepth = loops.size();
1388 if (maxLoopDepth == 1)
1394 if (llvm::any_of(loops, [](AffineForOp loop) {
1395 return loop.getNumIterOperands() > 0;
1401 std::vector<SmallVector<DependenceComponent, 2>> depCompsVec;
1408[[maybe_unused]]
bool
1410 assert(!loops.empty() &&
"no loops provided");
1413 auto hasTwoElements = [](
Block *block) {
1414 auto secondOpIt = std::next(block->begin());
1415 return secondOpIt != block->end() && &*secondOpIt == &block->back();
1418 auto enclosingLoop = loops.front();
1419 for (
auto loop : loops.drop_front()) {
1420 auto parentForOp = dyn_cast<AffineForOp>(loop->getParentOp());
1422 if (parentForOp != enclosingLoop || !hasTwoElements(parentForOp.getBody()))
1424 enclosingLoop = loop;
1433 assert(input.size() == permMap.size() &&
"invalid permutation map size");
1437 llvm::sort(checkPermMap);
1438 if (llvm::any_of(llvm::enumerate(checkPermMap),
1439 [](
const auto &en) {
return en.value() != en.index(); }))
1440 assert(
false &&
"invalid permutation map");
1443 if (input.size() < 2)
1451 for (
unsigned i = 0, e = input.size(); i < e; ++i)
1452 invPermMap.push_back({permMap[i], i});
1453 llvm::sort(invPermMap);
1457 if (permMap.back() != input.size() - 1) {
1458 Block *destBody = ((AffineForOp)input[invPermMap.back().second]).getBody();
1459 Block *srcBody = ((AffineForOp)input.back()).getBody();
1462 std::prev(srcBody->
end()));
1467 for (
int i = input.size() - 1; i >= 0; --i) {
1470 if (permMap[i] == 0) {
1475 auto *parentBlock = input[0]->getBlock();
1477 input[i]->getBlock()->getOperations(),
1484 unsigned parentPosInInput = invPermMap[permMap[i] - 1].second;
1485 if (i > 0 &&
static_cast<unsigned>(i - 1) == parentPosInInput)
1489 auto *destBody = ((AffineForOp)input[parentPosInInput]).getBody();
1490 destBody->getOperations().splice(destBody->begin(),
1491 input[i]->getBlock()->getOperations(),
1495 return invPermMap[0].second;
1504 if (loops.size() < 2)
1509 unsigned maxLoopDepth = loops.size();
1510 std::vector<SmallVector<DependenceComponent, 2>> depCompsVec;
1515 for (
auto &depComps : depCompsVec) {
1516 assert(depComps.size() >= maxLoopDepth);
1517 for (
unsigned j = 0;
j < maxLoopDepth; ++
j) {
1519 assert(depComp.
lb.has_value() && depComp.
ub.has_value());
1520 if (*depComp.
lb != 0 || *depComp.
ub != 0)
1521 isParallelLoop[
j] =
false;
1525 unsigned numParallelLoops = llvm::count(isParallelLoop,
true);
1530 unsigned nextSequentialLoop = numParallelLoops;
1531 unsigned nextParallelLoop = 0;
1532 for (
unsigned i = 0; i < maxLoopDepth; ++i) {
1533 if (isParallelLoop[i]) {
1534 loopPermMap[i] = nextParallelLoop++;
1536 loopPermMap[i] = nextSequentialLoop++;
1544 unsigned loopNestRootIndex =
permuteLoops(loops, loopPermMap);
1545 return loops[loopNestRootIndex];
1560 auto bounds = llvm::to_vector<4>(map->
getResults());
1561 bounds.push_back(
b.getAffineDimExpr(map->
getNumDims()) + offset);
1562 operands->insert(operands->begin() + map->
getNumDims(), iv);
1579 auto originalStep = forOp.getStepAsInt();
1580 auto scaledStep = originalStep * factor;
1581 forOp.setStep(scaledStep);
1586 auto lbMap = forOp.getLowerBoundMap();
1591 auto ubMap = forOp.getUpperBoundMap();
1596 auto iv = forOp.getInductionVar();
1598 for (
auto t : targets) {
1601 auto newForOp = AffineForOp::create(
b, t.getLoc(), lbOperands, lbMap,
1602 ubOperands, ubMap, originalStep);
1603 auto begin = t.getBody()->begin();
1605 auto nOps = t.getBody()->getOperations().size() - 2;
1606 newForOp.getBody()->getOperations().splice(
1607 newForOp.getBody()->getOperations().begin(),
1608 t.getBody()->getOperations(), begin, std::next(begin, nOps));
1610 newForOp.getRegion());
1611 innerLoops.push_back(newForOp);
1622 for (
auto it : llvm::zip(forOps, sizes)) {
1623 auto step =
stripmineSink(std::get<0>(it), std::get<1>(it), currentTargets);
1624 res.push_back(step);
1625 currentTargets = step;
1635 res.push_back(llvm::getSingleElement(loops));
1640 if (loops.size() < 2)
1643 AffineForOp innermost = loops.back();
1644 AffineForOp outermost = loops.front();
1649 for (AffineForOp loop : loops) {
1651 if (loop.getStepAsInt() != 1 || !loop.hasConstantLowerBound() ||
1652 loop.getConstantLowerBound() != 0)
1657 ub.getOperands().end());
1661 if (!llvm::hasSingleElement(origUbMap.
getResults()))
1662 prev = AffineMinOp::create(builder, loc, origUbMap, ubOperands);
1664 prev = AffineApplyOp::create(builder, loc, origUbMap, ubOperands);
1665 upperBoundSymbols.push_back(prev);
1669 for (AffineForOp loop : loops.drop_front()) {
1670 ub = loop.getUpperBound();
1671 origUbMap =
ub.getMap();
1672 ubOperands =
ub.getOperands();
1675 if (!llvm::hasSingleElement(origUbMap.
getResults()))
1676 upperBound = AffineMinOp::create(builder, loc, origUbMap, ubOperands);
1678 upperBound = AffineApplyOp::create(builder, loc, origUbMap, ubOperands);
1679 upperBoundSymbols.push_back(upperBound);
1681 operands.push_back(prev);
1682 operands.push_back(upperBound);
1684 prev = AffineApplyOp::create(
1688 builder.getAffineDimExpr(0) *
1689 builder.getAffineSymbolExpr(0)),
1695 1, builder.getAffineSymbolExpr(0), builder.getContext());
1696 outermost.setUpperBound(prev, newUbMap);
1698 builder.setInsertionPointToStart(outermost.getBody());
1708 Value previous = outermost.getInductionVar();
1709 for (
unsigned idx = loops.size(); idx > 0; --idx) {
1710 if (idx != loops.size()) {
1712 operands.push_back(previous);
1713 operands.push_back(upperBoundSymbols[idx]);
1714 previous = AffineApplyOp::create(builder, loc,
1717 builder.getAffineDimExpr(0).floorDiv(
1718 builder.getAffineSymbolExpr(0))),
1723 Value inductionVariable;
1725 inductionVariable = previous;
1728 applyOperands.push_back(previous);
1729 applyOperands.push_back(upperBoundSymbols[idx - 1]);
1730 inductionVariable = AffineApplyOp::create(
1734 builder.getAffineDimExpr(0) % builder.getAffineSymbolExpr(0)),
1738 inductionVariable, loops.back().getRegion());
1743 AffineForOp secondOutermostLoop = loops[1];
1744 innermost.getBody()->back().erase();
1745 outermost.getBody()->getOperations().splice(
1747 innermost.getBody()->getOperations());
1748 for (
auto [iter, init] :
1749 llvm::zip_equal(secondOutermostLoop.getRegionIterArgs(),
1750 secondOutermostLoop.getInits())) {
1751 iter.replaceAllUsesWith(init);
1754 secondOutermostLoop.erase();
1758void mlir::affine::mapLoopToProcessorIds(scf::ForOp forOp,
1761 assert(processorId.size() == numProcessors.size());
1762 if (processorId.empty())
1772 Value linearIndex = processorId.front();
1773 for (
unsigned i = 1, e = processorId.size(); i < e; ++i) {
1774 auto mulApplyOp = AffineApplyOp::create(
1775 b, loc, mulMap,
ValueRange{linearIndex, numProcessors[i]});
1776 linearIndex = AffineApplyOp::create(
b, loc, addMap,
1780 auto mulApplyOp = AffineApplyOp::create(
1781 b, loc, mulMap,
ValueRange{linearIndex, forOp.getStep()});
1782 Value lb = AffineApplyOp::create(
1783 b, loc, addMap,
ValueRange{mulApplyOp, forOp.getLowerBound()});
1784 forOp.setLowerBound(lb);
1786 Value step = forOp.getStep();
1787 for (
auto numProcs : numProcessors)
1788 step = AffineApplyOp::create(
b, loc, mulMap,
ValueRange{numProcs, step});
1789 forOp.setStep(step);
1800 Block **copyPlacementBlock,
1805 cst->getValues(cst->getNumDimVars(), cst->getNumDimAndSymbolVars(), &symbols);
1811 auto it = enclosingAffineOps.rbegin();
1812 AffineForOp lastInvariantFor;
1813 for (
auto e = enclosingAffineOps.rend(); it != e; ++it) {
1818 LDBG() <<
"memref definition will end up not dominating hoist location";
1822 auto affineFor = dyn_cast<AffineForOp>(enclosingOp);
1827 if (llvm::is_contained(symbols, affineFor.getInductionVar()))
1829 lastInvariantFor = affineFor;
1832 if (it != enclosingAffineOps.rbegin()) {
1834 *copyOutPlacementStart = std::next(*copyInPlacementStart);
1835 *copyPlacementBlock = lastInvariantFor->getBlock();
1837 *copyInPlacementStart = begin;
1838 *copyOutPlacementStart = end;
1839 *copyPlacementBlock = █
1857 if (bufferShape.size() <= 1)
1862 for (
int d = bufferShape.size() - 1; d >= 1; d--) {
1865 numEltPerStride *= bufferShape[d];
1869 if (bufferShape[d] < dimSize && bufferShape[d - 1] > 1) {
1870 strideInfos->push_back({stride, numEltPerStride});
1895 assert(llvm::all_of(lbMaps, [&](
AffineMap lbMap) {
1898 assert(llvm::all_of(ubMaps, [&](
AffineMap ubMap) {
1902 unsigned rank = cast<MemRefType>(
memref.getType()).getRank();
1904 assert(rank != 0 &&
"non-zero rank memref expected");
1905 assert(lbMaps.size() == rank &&
"wrong number of lb maps");
1906 assert(ubMaps.size() == rank &&
"wrong number of ub maps");
1913 for (
unsigned d = 0; d < rank; ++d) {
1914 auto forOp = createCanonicalizedAffineForOp(
b, loc, lbOperands, lbMaps[d],
1915 ubOperands, ubMaps[d]);
1921 auto fastBufOffsetMap =
1923 auto offset = AffineApplyOp::create(
b, loc, fastBufOffsetMap, lbOperands);
1928 b.getAffineDimExpr(2 * d));
1929 fastBufMapOperands.push_back(offset);
1930 fastBufMapOperands.push_back(forOp.getInductionVar());
1931 mayBeDeadApplys.push_back(offset);
1934 memIndices.push_back(forOp.getInductionVar());
1944 for (
auto applyOp : mayBeDeadApplys)
1945 if (applyOp.use_empty())
1950 auto load = AffineLoadOp::create(
b, loc,
memref, memIndices);
1952 fastBufMapOperands);
1958 AffineLoadOp::create(
b, loc, fastMemRef,
fastBufMap, fastBufMapOperands);
1959 AffineStoreOp::create(
b, loc,
load,
memref, memIndices);
1991 auto f = begin->getParentOfType<FunctionOpInterface>();
1992 OpBuilder topBuilder(f.getFunctionBody());
2004 Operation *lastCopyOp = end->getPrevNode();
2008 bool isCopyOutAtEndOfBlock = (end == copyOutPlacementStart);
2011 OpBuilder prologue(copyPlacementBlock, copyInPlacementStart);
2013 OpBuilder epilogue(copyPlacementBlock, copyOutPlacementStart);
2021 auto loc = region.
loc;
2023 auto memRefType = cast<MemRefType>(
memref.getType());
2025 if (!memRefType.getLayout().isIdentity()) {
2026 LDBG() <<
"Non-identity layout map not yet supported";
2036 unsigned rank = memRefType.getRank();
2038 LDBG() <<
"Non-zero ranked memrefs supported";
2047 std::optional<int64_t> numElements =
2050 LDBG() <<
"Non-constant region size not supported";
2056 LDBG() <<
"Max lower bound for memref region start not supported";
2060 if (*numElements == 0) {
2061 LDBG() <<
"Nothing to copy";
2066 for (
unsigned i = 0; i < rank; ++i) {
2068 if (lbMaps[i].getNumResults() == 0 || ubMaps[i].getNumResults() == 0) {
2069 LDBG() <<
"Missing lower or upper bound for region along dimension: "
2089 fastBufOffsets.reserve(rank);
2090 for (
unsigned d = 0; d < rank; d++) {
2091 assert(lbs[d].getNumSymbols() == cst->
getNumCols() - rank - 1 &&
2092 "incorrect bound size");
2096 if (lbs[d].isSingleConstant()) {
2097 auto indexVal = lbs[d].getSingleConstantResult();
2098 if (indexVal == 0) {
2099 memIndices.push_back(zeroIndex);
2101 memIndices.push_back(
2111 for (
unsigned i = 0, e = lbs[d].getNumSymbols(); i < e; ++i)
2112 symReplacements[i] = top.getAffineDimExpr(i);
2113 lbs[d] = lbs[d].replaceDimsAndSymbols(
2114 {}, symReplacements, lbs[d].getNumSymbols(),
2116 memIndices.push_back(
2117 AffineApplyOp::create(
b, loc, lbs[d], regionSymbols));
2120 bufIndices.push_back(zeroIndex);
2124 fastBufOffsets.push_back(lbs[d].getResult(0));
2131 bool existingBuf = fastBufferMap.count(
memref) > 0;
2133 AffineMap fastBufferLayout =
b.getMultiDimIdentityMap(rank);
2134 auto fastMemRefType =
2135 MemRefType::get(fastBufferShape, memRefType.getElementType(),
2141 memref::AllocOp::create(prologue, loc, fastMemRefType).getResult();
2143 fastBufferMap[
memref] = fastMemRef;
2147 *sizeInBytes = maySizeInBytes.value_or(0);
2150 <<
"Creating fast buffer of type " << fastMemRefType
2151 <<
" and size " << llvm::divideCeil(*sizeInBytes, 1024)
2155 fastMemRef = fastBufferMap[
memref];
2161 Value numEltPerDmaStride;
2168 if (dmaStrideInfos.size() > 1) {
2169 LDBG() <<
"Only up to one level of stride supported";
2173 if (!dmaStrideInfos.empty()) {
2177 top, loc, dmaStrideInfos[0].numEltPerStride);
2182 auto memAffineMap =
b.getMultiDimIdentityMap(memIndices.size());
2184 auto bufAffineMap =
b.getMultiDimIdentityMap(bufIndices.size());
2190 generatePointWiseCopy(loc,
memref, fastMemRef, lbMaps,
2191 regionSymbols, ubMaps,
2192 regionSymbols, fastBufOffsets,
2196 copyNests.insert(copyNest);
2200 if (region.
isWrite() && isCopyOutAtEndOfBlock)
2205 auto tagMemRefType = MemRefType::get({1}, top.getIntegerType(32), {},
2207 auto tagMemRef = memref::AllocOp::create(prologue, loc, tagMemRefType);
2210 auto tagAffineMap =
b.getMultiDimIdentityMap(tagIndices.size());
2214 AffineDmaStartOp::create(
b, loc,
memref, memAffineMap, memIndices,
2215 fastMemRef, bufAffineMap, bufIndices, tagMemRef,
2216 tagAffineMap, tagIndices, numElementsSSA,
2217 dmaStride, numEltPerDmaStride);
2220 auto op = AffineDmaStartOp::create(
2221 b, loc, fastMemRef, bufAffineMap, bufIndices,
memref, memAffineMap,
2222 memIndices, tagMemRef, tagAffineMap, tagIndices, numElementsSSA,
2223 dmaStride, numEltPerDmaStride);
2226 if (isCopyOutAtEndOfBlock)
2231 AffineDmaWaitOp::create(
b, loc, tagMemRef, tagAffineMap, zeroIndex,
2235 auto tagDeallocOp = memref::DeallocOp::create(epilogue, loc, tagMemRef);
2236 if (*nEnd == end && isCopyOutAtEndOfBlock)
2244 auto bufDeallocOp = memref::DeallocOp::create(epilogue, loc, fastMemRef);
2247 if (!copyOptions.
generateDma && *nEnd == end && isCopyOutAtEndOfBlock)
2259 remapExprs.reserve(rank);
2260 for (
unsigned i = 0; i < rank; i++) {
2264 auto dimExpr =
b.getAffineDimExpr(regionSymbols.size() + i);
2265 remapExprs.push_back(dimExpr - fastBufOffsets[i]);
2267 auto indexRemap =
AffineMap::get(regionSymbols.size() + rank, 0, remapExprs,
2272 bool isBeginAtStartOfBlock = (begin == block->
begin());
2273 if (!isBeginAtStartOfBlock)
2274 prevOfBegin = std::prev(begin);
2276 auto userFilterFn = [&](
Operation *user) {
2283 (
void)replaceAllMemRefUsesWith(
memref, fastMemRef,
2288 *nBegin = isBeginAtStartOfBlock ? block->
begin() : std::next(prevOfBegin);
2300 if (
auto loadOp = dyn_cast<AffineLoadOp>(op)) {
2301 rank = loadOp.getMemRefType().getRank();
2302 region->
memref = loadOp.getMemRef();
2304 }
else if (
auto storeOp = dyn_cast<AffineStoreOp>(op)) {
2305 rank = storeOp.getMemRefType().getRank();
2306 region->
memref = storeOp.getMemRef();
2309 assert(
false &&
"expected load or store op");
2312 auto memRefType = cast<MemRefType>(region->
memref.
getType());
2313 if (!memRefType.hasStaticShape())
2322 ivs.resize(numParamLoopIVs);
2326 regionCst->setValues(rank, rank + numParamLoopIVs, symbols);
2329 for (
unsigned d = 0; d < rank; d++) {
2330 auto dimSize = memRefType.getDimSize(d);
2331 assert(dimSize > 0 &&
"filtered dynamic shapes above");
2332 regionCst->addBound(BoundType::LB, d, 0);
2333 regionCst->addBound(BoundType::UB, d, dimSize - 1);
2341 std::optional<Value> filterMemRef,
2346 assert(begin->getBlock() == std::prev(end)->getBlock() &&
2347 "Inconsistent block begin/end args");
2348 assert(end != end->getBlock()->end() &&
"end can't be the block terminator");
2350 Block *block = begin->getBlock();
2356 LDBG() <<
"Generating copies at depth " << copyDepth;
2357 LDBG() <<
"from begin: "
2359 LDBG() <<
"to inclusive end: "
2365 SmallMapVector<Value, std::unique_ptr<MemRefRegion>, 4> readRegions;
2366 SmallMapVector<Value, std::unique_ptr<MemRefRegion>, 4> writeRegions;
2378 MemRefType memrefType;
2380 if (
auto loadOp = dyn_cast<AffineLoadOp>(opInst)) {
2381 memref = loadOp.getMemRef();
2382 memrefType = loadOp.getMemRefType();
2383 }
else if (
auto storeOp = dyn_cast<AffineStoreOp>(opInst)) {
2384 memref = storeOp.getMemRef();
2385 memrefType = storeOp.getMemRefType();
2391 if ((filterMemRef.has_value() && filterMemRef !=
memref) ||
2392 (isa_and_nonnull<IntegerAttr>(memrefType.getMemorySpace()) &&
2397 LDBG() <<
"memref definition is inside of the depth at "
2398 <<
"which copy-in/copy-out would happen";
2403 auto region = std::make_unique<MemRefRegion>(opInst->
getLoc());
2404 if (
failed(region->compute(opInst, copyDepth,
nullptr,
2406 LDBG() <<
"Error obtaining memory region: semi-affine maps?";
2407 LDBG() <<
"over-approximating to the entire memref";
2409 LDBG() <<
"non-constant memref sizes not yet supported";
2430 [&](
const SmallMapVector<Value, std::unique_ptr<MemRefRegion>, 4>
2432 const auto *
const it = targetRegions.find(region->memref);
2433 if (it == targetRegions.end())
2437 if (
failed(it->second->unionBoundingBox(*region))) {
2438 LDBG() <<
"Memory region bounding box failed; "
2439 <<
"over-approximating to the entire memref";
2442 LDBG() <<
"non-constant memref sizes not yet supported";
2446 it->second->getConstraints()->clearAndCopyFrom(
2447 *region->getConstraints());
2450 region->getConstraints()->clearAndCopyFrom(
2451 *it->second->getConstraints());
2456 bool existsInRead = updateRegion(readRegions);
2459 bool existsInWrite = updateRegion(writeRegions);
2464 if (region->isWrite() && !existsInWrite) {
2465 writeRegions[region->memref] = std::move(region);
2466 }
else if (!region->isWrite() && !existsInRead) {
2467 readRegions[region->memref] = std::move(region);
2472 LDBG() <<
"copy generation failed for one or more memref's in this block";
2476 uint64_t totalCopyBuffersSizeInBytes = 0;
2478 auto processRegions =
2479 [&](
const SmallMapVector<Value, std::unique_ptr<MemRefRegion>, 4>
2481 for (
const auto ®ionEntry : regions) {
2485 Block *copyPlacementBlock;
2487 *regionEntry.second, *block, begin, end, ©PlacementBlock,
2488 ©InPlacementStart, ©OutPlacementStart);
2490 uint64_t sizeInBytes;
2493 *regionEntry.second, block, begin, end, copyPlacementBlock,
2494 copyInPlacementStart, copyOutPlacementStart, copyOptions,
2495 fastBufferMap, copyNests, &sizeInBytes, &nBegin, &nEnd);
2496 if (succeeded(iRet)) {
2500 totalCopyBuffersSizeInBytes += sizeInBytes;
2502 ret = ret & succeeded(iRet);
2505 processRegions(readRegions);
2506 processRegions(writeRegions);
2509 LDBG() <<
"copy generation failed for one or more memref's in this block";
2515 if (llvm::DebugFlag && (forOp = dyn_cast<AffineForOp>(&*begin))) {
2516 LLVM_DEBUG(forOp.emitRemark()
2517 << llvm::divideCeil(totalCopyBuffersSizeInBytes, 1024)
2518 <<
" KiB of copy buffers in fast memory space for this block");
2523 "total size of all copy buffers' for this block exceeds fast memory "
2532LogicalResult mlir::affine::affineDataCopyGenerate(
2535 return affineDataCopyGenerate(forOp.getBody()->begin(),
2536 std::prev(forOp.getBody()->end()), copyOptions,
2537 filterMemRef, copyNests);
2540LogicalResult mlir::affine::generateCopyForMemRegion(
2544 auto begin = analyzedOp->getIterator();
2545 auto end = std::next(begin);
2549 auto err =
generateCopy(memrefRegion, block, begin, end, block, begin, end,
2550 copyOptions, fastBufferMap, copyNests,
2551 &
result.sizeInBytes, &begin, &end);
2555 const auto &en = fastBufferMap.find(memrefRegion.
memref);
2557 if (en == fastBufferMap.end())
2559 result.alloc = en->second.getDefiningOp();
2560 assert(
result.alloc &&
"fast buffer expected to be locally allocated");
2561 assert(copyNests.size() <= 1 &&
"At most one copy nest is expected.");
2562 result.copyNest = copyNests.empty() ?
nullptr : *copyNests.begin();
2571 assert(currLoopDepth <= depthToLoops.size() &&
"Unexpected currLoopDepth");
2572 if (currLoopDepth == depthToLoops.size())
2573 depthToLoops.emplace_back();
2575 for (
auto &op : *block) {
2576 if (
auto forOp = dyn_cast<AffineForOp>(op)) {
2577 depthToLoops[currLoopDepth].push_back(forOp);
2584void mlir::affine::gatherLoops(
2586 for (
auto &block :
func)
2590 if (!depthToLoops.empty()) {
2591 assert(depthToLoops.back().empty() &&
"Last loop level is not empty?");
2592 depthToLoops.pop_back();
2596AffineForOp mlir::affine::createCanonicalizedAffineForOp(
2609 return AffineForOp::create(
b, loc, lowerOperands, lbMap, upperOperands, ubMap,
2623 auto *context = loops[0].getContext();
2627 llvm::append_range(ops, loops);
2637 for (
auto loop : loops) {
2640 assert(loop.getStepAsInt() == 1 &&
"point loop step expected to be one");
2644 unsigned fullTileLbPos, fullTileUbPos;
2646 .getConstantBoundOnDimSize(0,
nullptr,
2648 nullptr, &fullTileLbPos,
2650 LDBG() <<
"Can't get constant diff pair for a loop";
2659 fullTileLb.assign(fLb.begin(), fLb.end());
2660 fullTileUb.assign(fUb.begin(), fUb.end());
2663 for (
auto lbIndex : lbIndices)
2664 for (
unsigned i = 0, e = cst.
getNumCols(); i < e; ++i)
2665 cst.
atIneq(lbIndex, i) = fullTileLb[i] - cst.
atIneq(lbIndex, i);
2668 for (
auto ubIndex : ubIndices)
2669 for (
unsigned i = 0, e = cst.
getNumCols(); i < e; ++i)
2670 cst.
atIneq(ubIndex, i) -= fullTileUb[i];
2693 return AffineIfOp::create(
b, loops[0].getLoc(), ifCondSet, setOperands,
2701 fullTileLoops.reserve(inputNest.size());
2706 for (
auto loop : inputNest) {
2708 if (loop.getStepAsInt() != 1) {
2709 LDBG() <<
"[tile separation] non-unit stride not implemented";
2717 unsigned lbPos, ubPos;
2719 .getConstantBoundOnDimSize(0,
nullptr,
2721 nullptr, &lbPos, &ubPos) ||
2723 LDBG() <<
"[tile separation] Can't get constant diff / "
2724 <<
"equalities not yet handled";
2733 cst.getIneqAsAffineValueMap(0, lbPos, lbVmap,
b.getContext());
2734 cst.getIneqAsAffineValueMap(0, ubPos, ubVmap,
b.getContext());
2735 AffineForOp fullTileLoop = createCanonicalizedAffineForOp(
2739 fullTileLoops.push_back(fullTileLoop);
2744 for (
const auto &loopEn : llvm::enumerate(inputNest))
2745 operandMap.
map(loopEn.value().getInductionVar(),
2746 fullTileLoops[loopEn.index()].getInductionVar());
2748 for (
auto &op : inputNest.back().getBody()->without_terminator())
2749 b.clone(op, operandMap);
2756 if (inputNest.empty())
2759 auto firstLoop = inputNest[0];
2762 auto prevLoop = firstLoop;
2763 for (
auto loop : inputNest.drop_front(1)) {
2764 assert(loop->getParentOp() == prevLoop &&
"input not contiguously nested");
2772 if (!fullTileLoops.empty())
2773 fullTileLoops.front().erase();
2781 fullTileLoops.front().erase();
2782 LDBG() <<
"All tiles are full tiles, or failure creating "
2783 <<
"separation condition";
2788 Block *thenBlock = ifOp.getThenBlock();
2789 AffineForOp outermostFullTileLoop = fullTileLoops[0];
2791 std::prev(thenBlock->
end()),
2792 outermostFullTileLoop->getBlock()->getOperations(),
2797 Block *elseBlock = ifOp.getElseBlock();
2799 firstLoop->getBlock()->getOperations(),
2803 *fullTileNest = std::move(fullTileLoops);
2808LogicalResult affine::coalescePerfectlyNestedAffineLoops(AffineForOp op) {
2809 LogicalResult
result(failure());
2812 if (loops.size() <= 1)
2820 for (
unsigned i = 0, e = loops.size(); i < e; ++i) {
2821 operandsDefinedAbove[i] = i;
2822 for (
unsigned j = 0;
j < i; ++
j) {
2824 operandsDefinedAbove[i] =
j;
2833 for (
unsigned end = loops.size(); end > 0; --end) {
2835 for (; start < end - 1; ++start) {
2837 *std::max_element(std::next(operandsDefinedAbove.begin(), start),
2838 std::next(operandsDefinedAbove.begin(), end));
2841 assert(maxPos == start &&
2842 "expected loop bounds to be known at the start of the band");
2850 if (start != end - 1)
2859 while (
auto loopOp = currentOp->
getParentOfType<LoopLikeOpInterface>()) {
2860 if (!loopOp.isDefinedOutsideOfLoop(operand.
get()))
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its the output argument nEnd is set *to the new end sizeInBytes is set to the size of the fast buffer *allocated *static LogicalResult generateCopy(const MemRefRegion ®ion, Block *block, Block::iterator begin, Block::iterator end, Block *copyPlacementBlock, Block::iterator copyInPlacementStart, Block::iterator copyOutPlacementStart, const AffineCopyOptions ©Options, DenseMap< Value, Value > &fastBufferMap, DenseSet< Operation * > ©Nests, uint64_t *sizeInBytes, Block::iterator *nBegin, Block::iterator *nEnd)
static LogicalResult performPreTilingChecks(MutableArrayRef< AffineForOp > input, ArrayRef< t > tileSizes)
Check if the input nest is supported for tiling and whether tiling would be legal or not.
static void constructParametricallyTiledIndexSetHyperRect(MutableArrayRef< AffineForOp > origLoops, MutableArrayRef< AffineForOp > newLoops, ArrayRef< Value > tileSizes)
Constructs and sets new loop bounds after tiling for the case of hyper-rectangular index sets,...
fullyComposeAffineMapAndOperands & fastBufMap
static InFlightDiagnostic emitRemarkForBlock(Block &block)
static void constructTiledLoopNest(MutableArrayRef< AffineForOp > origLoops, AffineForOp rootAffineForOp, unsigned width, MutableArrayRef< AffineForOp > tiledLoops)
Constructs tiled loop nest, without setting the loop bounds and move the body of the original loop ne...
static bool getFullMemRefAsRegion(Operation *op, unsigned numParamLoopIVs, MemRefRegion *region)
Construct the memref region to just include the entire memref.
static bool checkLoopInterchangeDependences(const std::vector< SmallVector< DependenceComponent, 2 > > &depCompsVec, ArrayRef< AffineForOp > loops, ArrayRef< unsigned > loopPermMap)
static LogicalResult checkIfHyperRectangular(MutableArrayRef< AffineForOp > input)
Checks whether a loop nest is hyper-rectangular or not.
static void findHighestBlockForPlacement(const MemRefRegion ®ion, Block &block, Block::iterator &begin, Block::iterator &end, Block **copyPlacementBlock, Block::iterator *copyInPlacementStart, Block::iterator *copyOutPlacementStart)
Given a memref region, determine the lowest depth at which transfers can be placed for it,...
static void moveLoopBodyImpl(AffineForOp src, AffineForOp dest, Block::iterator loc)
Move the loop body of AffineForOp 'src' from 'src' into the specified location in destination's body,...
static void setInterTileBoundsParametric(OpBuilder &b, AffineForOp origLoop, AffineForOp newLoop, Value tileSize)
Set lower and upper bounds of inter-tile loops for parametric tiling.
static void setIntraTileBoundsParametric(OpBuilder &b, AffineForOp origLoop, AffineForOp newInterTileLoop, AffineForOp newIntraTileLoop, Value tileSize)
Set lower and upper bounds of intra-tile loops for parametric tiling.
static AffineMap getMaxWithLowerBound(AffineForOp forOp, AffineMap map, SmallVectorImpl< Value > &operands)
Returns the max of the bound given by map applied to operands and the lower bound of forOp,...
static LogicalResult createFullTiles(MutableArrayRef< AffineForOp > inputNest, SmallVectorImpl< AffineForOp > &fullTileLoops, OpBuilder b)
Create the full tile loop nest (along with its body).
static AffineForOp generateShiftedLoop(AffineMap lbMap, AffineMap ubMap, const std::vector< std::pair< uint64_t, ArrayRef< Operation * > > > &opGroupQueue, unsigned offset, AffineForOp srcForOp, OpBuilder b)
Generates an affine.for op with the specified lower and upper bounds while generating the right IV re...
static void getMultiLevelStrides(const MemRefRegion ®ion, ArrayRef< int64_t > bufferShape, SmallVectorImpl< StrideInfo > *strideInfos)
Returns striding information for a copy/transfer of this region with potentially multiple striding le...
static void constructTiledIndexSetHyperRect(MutableArrayRef< AffineForOp > origLoops, MutableArrayRef< AffineForOp > newLoops, ArrayRef< unsigned > tileSizes)
Constructs and sets new loop bounds after tiling for the case of hyper-rectangular index sets,...
static LogicalResult generateCleanupLoopForUnroll(AffineForOp forOp, uint64_t unrollFactor)
Helper to generate cleanup loop for unroll or unroll-and-jam when the trip count is not a multiple of...
static bool areInnerBoundsInvariant(AffineForOp forOp)
Check if all control operands of all loops are defined outside of forOp and return false if not.
static void moveLoopBody(AffineForOp src, AffineForOp dest)
Move the loop body of AffineForOp 'src' from 'src' to the start of dest body.
static AffineIfOp createSeparationCondition(MutableArrayRef< AffineForOp > loops, OpBuilder b)
Creates an AffineIfOp that encodes the conditional to choose between the constant trip count version ...
static void gatherLoopsInBlock(Block *block, unsigned currLoopDepth, std::vector< SmallVector< AffineForOp, 2 > > &depthToLoops)
Gathers all AffineForOps in 'block' at 'currLoopDepth' in 'depthToLoops'.
static void getCleanupLoopLowerBound(AffineForOp forOp, unsigned unrollFactor, AffineMap &cleanupLbMap, SmallVectorImpl< Value > &cleanupLbOperands)
Computes the cleanup loop lower bound of the loop being unrolled with the specified unroll factor; th...
static void augmentMapAndBounds(OpBuilder &b, Value iv, AffineMap *map, SmallVector< Value, 4 > *operands, int64_t offset=0)
static SmallVector< AffineForOp, 8 > stripmineSink(AffineForOp forOp, uint64_t factor, ArrayRef< AffineForOp > targets)
static void replaceIterArgsAndYieldResults(AffineForOp forOp)
Helper to replace uses of loop carried values (iter_args) and loop yield values while promoting singl...
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
AffineMap shiftDims(unsigned shift, unsigned offset=0) const
Replace dims[offset ... numDims) by dims[offset + shift ... shift + numDims).
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
unsigned getNumInputs() const
AffineMap shiftSymbols(unsigned shift, unsigned offset=0) const
Replace symbols[offset ... numSymbols) by symbols[offset + shift ... shift + numSymbols).
AffineExpr getResult(unsigned idx) const
Block represents an ordered list of Operations.
OpListType::iterator iterator
Operation * findAncestorOpInBlock(Operation &op)
Returns 'op' if 'op' lies in this block, or otherwise finds the ancestor operation of 'op' that lies ...
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
OpListType & getOperations()
RetT walk(FnT &&callback)
Walk all nested operations, blocks (including this block) or regions, depending on the type of callba...
Operation * getTerminator()
Get the terminator operation of this block.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
AffineMap getDimIdentityMap()
AffineExpr getAffineDimExpr(unsigned position)
IntegerSet getAsIntegerSet(MLIRContext *context) const
Returns the constraint system as an integer set.
void getValues(unsigned start, unsigned end, SmallVectorImpl< Value > *values) const
Returns the Values associated with variables in range [start, end).
This class allows control over how the GreedyPatternRewriteDriver works.
This is a utility class for mapping one set of IR entities to another.
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'.
IRValueT get() const
Return the current value being used by this operand.
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
This class represents a diagnostic that is inflight and set to be reported.
An integer set representing a conjunction of one or more affine equalities and inequalities.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
static OpBuilder atBlockTerminator(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the block terminator.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents an operand of an operation.
Set of flags used to control the behavior of the various IR print methods (e.g.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
InFlightDiagnostic emitWarning(const Twine &message={})
Emit a warning about this operation, reporting up to any diagnostic handlers that may be listening.
Block * getBlock()
Returns the operation block that contains this operation.
Location getLoc()
The source location the operation was defined or derived from.
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
void setOperands(ValueRange operands)
Replace the current operands of this operation with the ones provided in 'operands'.
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...
Region * getParentRegion()
Returns the region to which the instruction belongs.
InFlightDiagnostic emitRemark(const Twine &message={})
Emit a remark about this operation, reporting up to any diagnostic handlers that may be listening.
ParentT getParentOfType()
Find the first parent operation of the given type, or nullptr if there is no ancestor operation.
MLIRContext * getContext() const
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.
static WalkResult advance()
static WalkResult interrupt()
AffineBound represents a lower or upper bound in the for operation.
Value getOperand(unsigned idx)
unsigned getNumOperands()
An AffineValueMap is an affine map plus its ML value operands and results for analysis purposes.
ArrayRef< Value > getOperands() const
AffineMap getAffineMap() const
FlatAffineValueConstraints is an extension of FlatLinearValueConstraints with helper functions for Af...
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Operation * getOwner() const
Return the owner of this operand.
An IntegerRelation represents the set of points from a PresburgerSpace that satisfy a list of affine ...
void removeIndependentConstraints(unsigned pos, unsigned num)
Removes constraints that are independent of (i.e., do not have a coefficient) variables in the range ...
void removeTrivialRedundancy()
Removes duplicate constraints, trivially true constraints, and constraints that can be detected as re...
ArrayRef< DynamicAPInt > getInequality(unsigned idx) const
DynamicAPInt atIneq(unsigned i, unsigned j) const
Returns the value at the specified inequality row and column.
bool isHyperRectangular(unsigned pos, unsigned num) const
Returns true if the set can be trivially detected as being hyper-rectangular on the specified contigu...
unsigned getNumVars() const
void setDimSymbolSeparation(unsigned newSymbolCount)
Changes the partition between dimensions and symbols.
unsigned getNumDimAndSymbolVars() const
unsigned getNumCols() const
Returns the number of columns in the constraint system.
void getLowerAndUpperBoundIndices(unsigned pos, SmallVectorImpl< unsigned > *lbIndices, SmallVectorImpl< unsigned > *ubIndices, SmallVectorImpl< unsigned > *eqIndices=nullptr, unsigned offset=0, unsigned num=0) const
Gather positions of all lower and upper bounds of the variable at pos, and optionally any equalities ...
void removeVar(VarKind kind, unsigned pos)
Removes variables of the specified kind with the specified pos (or within the specified range) from t...
LogicalResult loopUnrollFull(AffineForOp forOp)
Unrolls this for operation completely if the trip count is known to be constant.
LogicalResult promoteIfSingleIteration(AffineForOp forOp)
Promotes the loop body of a AffineForOp to its containing block if the loop was known to have a singl...
LogicalResult loopUnrollJamUpToFactor(AffineForOp forOp, uint64_t unrollJamFactor)
Unrolls and jams this loop by the specified factor or by the trip count (if constant),...
void extractForInductionVars(ArrayRef< AffineForOp > forInsts, SmallVectorImpl< Value > *ivs)
Extracts the induction variables from a list of AffineForOps and places them in the output argument i...
LogicalResult loopUnrollByFactor(AffineForOp forOp, uint64_t unrollFactor, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn=nullptr, bool cleanUpUnroll=false)
Unrolls this for operation by the specified unroll factor.
void getEnclosingAffineOps(Operation &op, SmallVectorImpl< Operation * > *ops)
Populates 'ops' with affine operations enclosing op ordered from outermost to innermost while stoppin...
LogicalResult getIndexSet(MutableArrayRef< Operation * > ops, FlatAffineValueConstraints *domain)
Builds a system of constraints with dimensional variables corresponding to the loop IVs of the forOps...
void getPerfectlyNestedLoops(SmallVectorImpl< AffineForOp > &nestedLoops, AffineForOp root)
Get perfectly nested sequence of loops starting at root of loop nest (the first op being another Affi...
LogicalResult affineForOpBodySkew(AffineForOp forOp, ArrayRef< uint64_t > shifts, bool unrollPrologueEpilogue=false)
Skew the operations in an affine.for's body with the specified operation-wise shifts.
void getTripCountMapAndOperands(AffineForOp forOp, AffineMap *map, SmallVectorImpl< Value > *operands)
Returns the trip count of the loop as an affine map with its corresponding operands if the latter is ...
bool isValidLoopInterchangePermutation(ArrayRef< AffineForOp > loops, ArrayRef< unsigned > loopPermMap)
Checks if the loop interchange permutation 'loopPermMap', of the perfectly nested sequence of loops i...
void getSupportedReductions(AffineForOp forOp, SmallVectorImpl< LoopReduction > &supportedReductions)
Populate supportedReductions with descriptors of the supported reductions.
void canonicalizeMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Modifies both map and operands in-place so as to:
LogicalResult loopUnrollUpToFactor(AffineForOp forOp, uint64_t unrollFactor)
Unrolls this loop by the specified unroll factor or its trip count, whichever is lower.
void getDependenceComponents(AffineForOp forOp, unsigned maxLoopDepth, std::vector< SmallVector< DependenceComponent, 2 > > *depCompsVec)
Returns in 'depCompsVec', dependence components for dependences between all load and store ops in loo...
unsigned permuteLoops(ArrayRef< AffineForOp > inputNest, ArrayRef< unsigned > permMap)
Performs a loop permutation on a perfectly nested loop nest inputNest (where the contained loops appe...
LogicalResult loopUnrollJamByFactor(AffineForOp forOp, uint64_t unrollJamFactor)
Unrolls and jams this loop by the specified factor.
LogicalResult tilePerfectlyNestedParametric(MutableArrayRef< AffineForOp > input, ArrayRef< Value > tileSizes, SmallVectorImpl< AffineForOp > *tiledNest=nullptr)
Tiles the specified band of perfectly nested loops creating tile-space loops and intra-tile loops,...
void fullyComposeAffineMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands, bool composeAffineMin=false)
Given an affine map map and its input operands, this method composes into map, maps of AffineApplyOps...
void canonicalizeSetAndOperands(IntegerSet *set, SmallVectorImpl< Value > *operands)
Canonicalizes an integer set the same way canonicalizeMapAndOperands does for affine maps.
bool isPerfectlyNested(ArrayRef< AffineForOp > loops)
Returns true if loops is a perfectly nested loop nest, where loops appear in it from outermost to inn...
void getAffineForIVs(Operation &op, SmallVectorImpl< AffineForOp > *loops)
Populates 'loops' with IVs of the affine.for ops surrounding 'op' ordered from the outermost 'affine....
uint64_t getLargestDivisorOfTripCount(AffineForOp forOp)
Returns the greatest known integral divisor of the trip count.
std::optional< uint64_t > getIntOrFloatMemRefSizeInBytes(MemRefType memRefType)
Returns the size of a memref with element type int or float in bytes if it's statically shaped,...
int64_t numEnclosingInvariantLoops(OpOperand &operand)
Performs explicit copying for the contiguous sequence of operations in the block iterator range [‘beg...
unsigned getNestingDepth(Operation *op)
Returns the nesting depth of this operation, i.e., the number of loops surrounding this operation.
bool isOpwiseShiftValid(AffineForOp forOp, ArrayRef< uint64_t > shifts)
Checks where SSA dominance would be violated if a for op's body operations are shifted by the specifi...
SmallVector< SmallVector< AffineForOp, 8 >, 8 > tile(ArrayRef< AffineForOp > forOps, ArrayRef< uint64_t > sizes, ArrayRef< AffineForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
AffineForOp sinkSequentialLoops(AffineForOp forOp)
LogicalResult tilePerfectlyNested(MutableArrayRef< AffineForOp > input, ArrayRef< unsigned > tileSizes, SmallVectorImpl< AffineForOp > *tiledNest=nullptr)
Tiles the specified band of perfectly nested loops creating tile-space loops and intra-tile loops.
void interchangeLoops(AffineForOp forOpA, AffineForOp forOpB)
Performs loop interchange on 'forOpA' and 'forOpB'.
Value getReductionOp(AtomicRMWKind op, OpBuilder &builder, Location loc, Value lhs, Value rhs)
Returns the value obtained by applying the reduction operation kind associated with a binary AtomicRM...
Include the generated interface declarations.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region ®ion)
Replace all uses of orig within the given region with replacement.
AffineMap removeDuplicateExprs(AffineMap map)
Returns a map with the same dimension and symbol count as map, but whose results are the unique affin...
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...
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
LogicalResult applyOpPatternsGreedily(ArrayRef< Operation * > ops, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr, bool *allErased=nullptr)
Rewrite the specified ops by repeatedly applying the highest benefit patterns in a greedy worklist dr...
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
bool areValuesDefinedAbove(Range values, Region &limit)
Check if all values in the provided range are defined above the limit region.
@ ExistingAndNewOps
Only pre-existing and newly created ops are processed.
AffineExpr simplifyAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols)
Simplify an affine expression by flattening and some amount of simple analysis.
LogicalResult coalesceLoops(MutableArrayRef< scf::ForOp > loops)
Replace a perfect nest of "for" loops with a single linearized loop.
llvm::function_ref< Fn > function_ref
SmallVector< std::pair< Block::iterator, Block::iterator > > subBlocks
Explicit copy / DMA generation options for mlir::affineDataCopyGenerate.
uint64_t fastMemCapacityBytes
std::optional< int64_t > ub
std::optional< int64_t > lb
A description of a (parallelizable) reduction in an affine loop.
A region of a memref's data space; this is typically constructed by analyzing load/store op's on this...
std::optional< int64_t > getConstantBoundingSizeAndShape(SmallVectorImpl< int64_t > *shape=nullptr, SmallVectorImpl< AffineMap > *lbs=nullptr) const
Returns a constant upper bound on the number of elements in this region if bounded by a known constan...
void getLowerAndUpperBound(unsigned pos, AffineMap &lbMap, AffineMap &ubMap) const
Gets the lower and upper bound map for the dimensional variable at pos.
FlatAffineValueConstraints * getConstraints()
Value memref
Memref that this region corresponds to.
Location loc
If there is more than one load/store op associated with the region, the location information would co...
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.