29#include "llvm/ADT/SmallVectorExtras.h"
32#define DEBUG_TYPE "affine-utils"
42class AffineApplyExpander
49 : builder(builder), dimValues(dimValues), symbolValues(symbolValues),
52 template <
typename OpTy>
54 arith::IntegerOverflowFlags overflowFlags =
55 arith::IntegerOverflowFlags::none) {
60 auto op = OpTy::create(builder, loc,
lhs,
rhs, overflowFlags);
61 return op.getResult();
65 return buildBinaryExpr<arith::AddIOp>(expr);
69 return buildBinaryExpr<arith::MulIOp>(expr,
70 arith::IntegerOverflowFlags::nsw);
83 if (
auto rhsConst = dyn_cast<AffineConstantExpr>(expr.
getRHS())) {
84 if (rhsConst.getValue() <= 0) {
85 emitError(loc,
"modulo by non-positive value is not supported");
92 assert(
lhs &&
rhs &&
"unexpected affine expr lowering failure");
94 Value remainder = arith::RemSIOp::create(builder, loc,
lhs,
rhs);
96 Value isRemainderNegative = arith::CmpIOp::create(
97 builder, loc, arith::CmpIPredicate::slt, remainder, zeroCst);
98 Value correctedRemainder =
99 arith::AddIOp::create(builder, loc, remainder,
rhs);
100 Value result = arith::SelectOp::create(builder, loc, isRemainderNegative,
101 correctedRemainder, remainder);
123 if (
auto rhsConst = dyn_cast<AffineConstantExpr>(expr.
getRHS())) {
124 if (rhsConst.getValue() <= 0) {
125 emitError(loc,
"division by non-positive value is not supported");
131 assert(
lhs &&
rhs &&
"unexpected affine expr lowering failure");
135 Value negative = arith::CmpIOp::create(
136 builder, loc, arith::CmpIPredicate::slt,
lhs, zeroCst);
137 Value negatedDecremented =
138 arith::SubIOp::create(builder, loc, noneCst,
lhs);
139 Value dividend = arith::SelectOp::create(builder, loc, negative,
140 negatedDecremented,
lhs);
141 Value quotient = arith::DivSIOp::create(builder, loc, dividend,
rhs);
142 Value correctedQuotient =
143 arith::SubIOp::create(builder, loc, noneCst, quotient);
144 Value result = arith::SelectOp::create(builder, loc, negative,
145 correctedQuotient, quotient);
163 if (
auto rhsConst = dyn_cast<AffineConstantExpr>(expr.
getRHS())) {
164 if (rhsConst.getValue() <= 0) {
165 emitError(loc,
"division by non-positive value is not supported");
171 assert(
lhs &&
rhs &&
"unexpected affine expr lowering failure");
175 Value nonPositive = arith::CmpIOp::create(
176 builder, loc, arith::CmpIPredicate::sle,
lhs, zeroCst);
177 Value negated = arith::SubIOp::create(builder, loc, zeroCst,
lhs);
178 Value decremented = arith::SubIOp::create(builder, loc,
lhs, oneCst);
179 Value dividend = arith::SelectOp::create(builder, loc, nonPositive, negated,
181 Value quotient = arith::DivSIOp::create(builder, loc, dividend,
rhs);
182 Value negatedQuotient =
183 arith::SubIOp::create(builder, loc, zeroCst, quotient);
184 Value incrementedQuotient =
185 arith::AddIOp::create(builder, loc, quotient, oneCst);
187 builder, loc, nonPositive, negatedQuotient, incrementedQuotient);
193 return op.getResult();
198 "affine dim position out of range");
204 "symbol dim position out of range");
223 return AffineApplyExpander(builder, dimValues, symbolValues, loc).visit(expr);
228std::optional<SmallVector<Value, 8>>
232 auto expanded = llvm::map_to_vector<8>(
234 [numDims, &builder, loc, operands](
AffineExpr expr) {
235 return expandAffineExpr(builder, loc, expr,
236 operands.take_front(numDims),
237 operands.drop_front(numDims));
239 if (llvm::all_of(expanded, [](
Value v) {
return v; }))
249 assert(ifOp.hasElse() &&
"else block expected");
251 Block *destBlock = ifOp->getBlock();
252 Block *srcBlock = elseBlock ? ifOp.getElseBlock() : ifOp.getThenBlock();
255 std::prev(srcBlock->
end()));
269 if (
auto forOp = dyn_cast<AffineForOp>(parentOp)) {
270 if (llvm::is_contained(ifOperands, forOp.getInductionVar()))
272 }
else if (
auto parallelOp = dyn_cast<AffineParallelOp>(parentOp)) {
273 if (llvm::any_of(parallelOp.getIVs(), [&](
Value iv) {
274 return llvm::is_contained(ifOperands, iv);
277 }
else if (!isa<AffineIfOp>(parentOp)) {
292 if (hoistOverOp == ifOp)
302 auto hoistedIfOp = AffineIfOp::create(
b, ifOp.getLoc(), ifOp.getIntegerSet(),
311 StringAttr idForIfOp =
b.getStringAttr(
"__mlir_if_hoisting");
313 b.setInsertionPointAfter(hoistOverOp);
315 ifOp->setAttr(idForIfOp,
b.getBoolAttr(
true));
316 hoistOverOpClone =
b.clone(*hoistOverOp, operandMap);
322 auto *thenBlock = hoistedIfOp.getThenBlock();
323 thenBlock->getOperations().splice(thenBlock->begin(),
328 AffineIfOp ifCloneInElse;
329 hoistOverOpClone->
walk([&](AffineIfOp ifClone) {
330 if (!ifClone->getAttr(idForIfOp))
332 ifCloneInElse = ifClone;
335 assert(ifCloneInElse &&
"if op clone should exist");
338 if (!ifCloneInElse.hasElse())
339 ifCloneInElse.erase();
344 auto *elseBlock = hoistedIfOp.getElseBlock();
345 elseBlock->getOperations().splice(
355 AffineParallelOp *resOp) {
357 unsigned numReductions = parallelReductions.size();
358 if (numReductions != forOp.getNumIterOperands())
363 AffineMap lowerBoundMap = forOp.getLowerBoundMap();
364 ValueRange lowerBoundOperands = forOp.getLowerBoundOperands();
365 AffineMap upperBoundMap = forOp.getUpperBoundMap();
366 ValueRange upperBoundOperands = forOp.getUpperBoundOperands();
369 auto reducedValues = llvm::map_to_vector<4>(
371 auto reductionKinds = llvm::map_to_vector<4>(
373 AffineParallelOp newPloop = AffineParallelOp::create(
374 outsideBuilder, loc,
ValueRange(reducedValues).getTypes(), reductionKinds,
379 newPloop.getRegion().takeBody(forOp.getRegion());
380 Operation *yieldOp = &newPloop.getBody()->back();
385 newResults.reserve(numReductions);
386 for (
unsigned i = 0; i < numReductions; ++i) {
387 Value init = forOp.getInits()[i];
392 assert(reductionOp &&
"yielded value is expected to be produced by an op");
396 reductionOp->
setOperands({init, newPloop->getResult(i)});
397 forOp->getResult(i).replaceAllUsesWith(reductionOp->
getResult(0));
406 newPloop.getBody()->eraseArguments(numIVs, numReductions);
415LogicalResult mlir::affine::hoistAffineIfOp(AffineIfOp ifOp,
bool *folded) {
418 if (ifOp.getNumResults() != 0)
427 AffineIfOp::getCanonicalizationPatterns(
patterns, ifOp.getContext());
431 ifOp.getOperation(), frozenPatterns,
443 assert(llvm::all_of(ifOp.getOperands(),
445 return isTopLevelValue(v) || isAffineInductionVar(v);
447 "operands not composed");
455 if (hoistedIfOp == ifOp)
472 return positivePath ?
min :
max;
473 if (
auto bin = dyn_cast<AffineBinaryOpExpr>(e)) {
477 return substWithMin(
lhs, dim,
min,
max, positivePath) +
478 substWithMin(
rhs, dim,
min,
max, positivePath);
480 auto c1 = dyn_cast<AffineConstantExpr>(bin.getLHS());
481 auto c2 = dyn_cast<AffineConstantExpr>(bin.getRHS());
482 if (c1 && c1.getValue() < 0)
484 bin.getKind(), c1, substWithMin(
rhs, dim,
min,
max, !positivePath));
485 if (c2 && c2.getValue() < 0)
487 bin.getKind(), substWithMin(
lhs, dim,
min,
max, !positivePath), c2);
489 bin.getKind(), substWithMin(
lhs, dim,
min,
max, positivePath),
490 substWithMin(
rhs, dim,
min,
max, positivePath));
495void mlir::affine::normalizeAffineParallel(AffineParallelOp op) {
497 if (op.hasMinMaxBounds())
500 AffineMap lbMap = op.getLowerBoundsMap();
503 bool isAlreadyNormalized =
504 llvm::all_of(llvm::zip(steps, lbMap.
getResults()), [](
auto tuple) {
505 int64_t step = std::get<0>(tuple);
506 auto lbExpr = dyn_cast<AffineConstantExpr>(std::get<1>(tuple));
507 return lbExpr && lbExpr.getValue() == 0 && step == 1;
509 if (isAlreadyNormalized)
514 op.getLowerBoundsValueMap(), &ranges);
519 for (
unsigned i = 0, e = steps.size(); i < e; ++i) {
523 lbExprs.push_back(zeroExpr);
527 ubExprs.push_back(ubExpr);
540 OperandRange dimOperands = lbOperands.take_front(nDims);
541 OperandRange symbolOperands = lbOperands.drop_front(nDims);
543 applyOperands.push_back(iv);
544 applyOperands.append(symbolOperands.begin(), symbolOperands.end());
546 AffineApplyOp::create(builder, op.getLoc(), map, applyOperands);
551 op.setSteps(newSteps);
553 0, 0, lbExprs, op.getContext());
554 op.setLowerBounds({}, newLowerMap);
556 ubExprs, op.getContext());
557 op.setUpperBounds(ranges.
getOperands(), newUpperMap);
560LogicalResult mlir::affine::normalizeAffineFor(AffineForOp op,
561 bool promoteSingleIter) {
566 if (op.hasConstantLowerBound() && (op.getConstantLowerBound() == 0) &&
574 if (op.getLowerBoundMap().getNumResults() != 1)
579 int64_t origLoopStep = op.getStepAsInt();
582 AffineMap oldLbMap = op.getLowerBoundMap();
589 op.getLowerBoundMap().getResult(0));
594 AffineValueMap paddedLbValueMap(paddedLbMap, op.getLowerBoundOperands());
595 AffineValueMap ubValueMap(op.getUpperBoundMap(), op.getUpperBoundOperands());
604 for (
unsigned i = 0; i < numResult; ++i)
605 scaleDownExprs[i] = opBuilder.getAffineDimExpr(i).ceilDiv(origLoopStep);
613 op.setUpperBound(newUbValueMap.
getOperands(), newUbMap);
614 op.setLowerBound({}, opBuilder.getConstantAffineMap(0));
619 opBuilder.setInsertionPointToStart(op.getBody());
622 AffineMap::get(1, 0, -opBuilder.getAffineDimExpr(0) * origLoopStep);
626 (
void)newIvToOldIvMap.canonicalize();
628 AffineApplyOp::create(opBuilder, loc, newIvToOldIvMap.getAffineMap(),
629 newIvToOldIvMap.getOperands());
630 op.getInductionVar().replaceAllUsesExcept(newIV->getResult(0), newIV);
656 unsigned minSurroundingLoops) {
670 for (
unsigned d = nsLoops + 1; d > minSurroundingLoops; d--) {
672 srcAccess, destAccess, d, &dependenceConstraints,
687template <
typename EffectType,
typename T>
693 bool hasSideEffect =
false;
702 if (
auto memEffect = dyn_cast<MemoryEffectOpInterface>(op)) {
704 memEffect.getEffects(effects);
706 bool opMayHaveEffect =
false;
707 for (
auto effect : effects) {
710 if (isa<EffectType>(effect.getEffect())) {
711 if (effect.getValue() && effect.getValue() !=
memref &&
714 opMayHaveEffect =
true;
719 if (!opMayHaveEffect)
724 if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) {
731 unsigned minSurroundingLoops =
734 hasSideEffect =
true;
740 hasSideEffect =
true;
747 for (
Region ®ion : op->getRegions())
748 for (
Block &block : region)
756 hasSideEffect =
true;
767 checkOperation(parent);
776 "Checking for side effect between two operations without a common "
784 until(untilOp->getParentOp(), untilOp);
796 for (
auto iter = ++from->getIterator(), end = from->
getBlock()->
end();
797 iter != end && &*iter != untilOp; ++iter) {
798 checkOperation(&*iter);
803 if (untilOp->getBlock() != from->
getBlock())
805 todoBlocks.push_back(succ);
810 while (!todoBlocks.empty()) {
811 Block *blk = todoBlocks.pop_back_val();
815 for (
auto &op : *blk) {
821 todoBlocks.push_back(succ);
826 return !hasSideEffect;
845 for (
auto *user : loadOp.getMemRef().getUsers()) {
846 auto storeOp = dyn_cast<AffineWriteOpInterface>(user);
860 if (srcAccess != destAccess)
880 assert(lastWriteStoreOp ==
nullptr &&
881 "multiple simultaneous replacement stores");
882 lastWriteStoreOp = storeOp;
885 if (!lastWriteStoreOp)
890 cast<AffineWriteOpInterface>(lastWriteStoreOp).getValueToStore();
893 if (storeVal.
getType() != loadOp.getValue().getType())
897 memrefsToErase.insert(loadOp.getMemRef());
899 loadOpsToErase.push_back(loadOp);
904 affine::AffineReadOpInterface>(
920 auto writeB = dyn_cast<AffineWriteOpInterface>(user);
925 if (writeB == writeA)
929 if (writeB->getParentRegion() != writeA->getParentRegion())
936 if (srcAccess != destAccess)
949 opsToErase.push_back(writeA);
960static void loadCSE(AffineReadOpInterface loadA,
965 for (
auto *user : loadA.getMemRef().getUsers()) {
966 auto loadB = dyn_cast<AffineReadOpInterface>(user);
967 if (!loadB || loadB == loadA)
974 if (srcAccess != destAccess) {
984 loadB.getOperation(), loadA,
mayAlias))
989 if (loadB.getValue().getType() != loadA.getValue().getType())
992 loadCandidates.push_back(loadB);
998 for (AffineReadOpInterface option : loadCandidates) {
999 if (llvm::all_of(loadCandidates, [&](AffineReadOpInterface depStore) {
1000 return depStore == option ||
1001 domInfo.
dominates(option.getOperation(),
1002 depStore.getOperation());
1004 loadB = option.getValue();
1010 loadA.getValue().replaceAllUsesWith(loadB);
1012 loadOpsToErase.push_back(loadA);
1041void mlir::affine::affineScalarReplace(func::FuncOp f,
DominanceInfo &domInfo,
1051 return !aliasAnalysis.
alias(val1, val2).
isNo();
1055 f.walk([&](AffineReadOpInterface loadOp) {
1058 for (
auto *op : opsToErase)
1063 f.walk([&](AffineWriteOpInterface storeOp) {
1066 for (
auto *op : opsToErase)
1074 for (
auto memref : memrefsToErase) {
1082 return !isa<AffineWriteOpInterface>(ownerOp) &&
1083 !hasSingleEffect<MemoryEffects::Free>(ownerOp, memref);
1088 for (
auto *user : llvm::make_early_inc_range(
memref.getUsers()))
1096 f.walk([&](AffineReadOpInterface loadOp) {
1099 for (
auto *op : opsToErase)
1106 return isa<AffineMapAccessInterface, memref::LoadOp, memref::StoreOp>(op);
1110LogicalResult mlir::affine::replaceAllMemRefUsesWith(
1114 bool allowNonDereferencingOps) {
1115 unsigned newMemRefRank = cast<MemRefType>(newMemRef.
getType()).getRank();
1116 (
void)newMemRefRank;
1117 unsigned oldMemRefRank = cast<MemRefType>(oldMemRef.
getType()).getRank();
1118 (
void)oldMemRefRank;
1120 assert(indexRemap.
getNumSymbols() == symbolOperands.size() &&
1121 "symbolic operand count mismatch");
1123 extraOperands.size() + oldMemRefRank + symbolOperands.size());
1124 assert(indexRemap.
getNumResults() + extraIndices.size() == newMemRefRank);
1126 assert(oldMemRefRank + extraIndices.size() == newMemRefRank);
1130 assert(cast<MemRefType>(oldMemRef.
getType()).getElementType() ==
1131 cast<MemRefType>(newMemRef.
getType()).getElementType());
1134 for (
const auto &opEntry : llvm::enumerate(op->
getOperands())) {
1135 if (opEntry.value() == oldMemRef)
1136 usePositions.push_back(opEntry.index());
1140 if (usePositions.empty())
1143 unsigned memRefOperandPos = usePositions.front();
1149 if (!allowNonDereferencingOps) {
1155 for (
unsigned pos : usePositions)
1160 if (usePositions.size() > 1) {
1162 LLVM_DEBUG(llvm::dbgs()
1163 <<
"multiple dereferencing uses in a single op not supported");
1170 unsigned oldMemRefNumIndices = oldMemRefRank;
1172 auto affMapAccInterface = dyn_cast<AffineMapAccessInterface>(op);
1173 if (affMapAccInterface) {
1178 affMapAccInterface.getAffineMapAttrForMemRef(oldMemRef);
1179 oldMap = cast<AffineMapAttr>(oldMapAttrPair.
getValue()).getValue();
1182 oldMapOperands.assign(startIdx, startIdx + oldMemRefNumIndices);
1187 oldMemRefOperands.reserve(oldMemRefRank);
1188 if (affMapAccInterface &&
1190 for (
auto resultExpr : oldMap.
getResults()) {
1193 auto afOp = AffineApplyOp::create(builder, op->
getLoc(), singleResMap,
1195 oldMemRefOperands.push_back(afOp);
1196 affineApplyOps.push_back(afOp);
1199 oldMemRefOperands.assign(oldMapOperands.begin(), oldMapOperands.end());
1206 remapOperands.reserve(extraOperands.size() + oldMemRefRank +
1207 symbolOperands.size());
1208 remapOperands.append(extraOperands.begin(), extraOperands.end());
1209 remapOperands.append(oldMemRefOperands.begin(), oldMemRefOperands.end());
1210 remapOperands.append(symbolOperands.begin(), symbolOperands.end());
1213 remapOutputs.reserve(oldMemRefRank);
1217 for (
auto resultExpr : indexRemap.
getResults()) {
1220 auto afOp = AffineApplyOp::create(builder, op->
getLoc(), singleResMap,
1222 remapOutputs.push_back(afOp);
1223 affineApplyOps.push_back(afOp);
1227 remapOutputs.assign(remapOperands.begin(), remapOperands.end());
1230 newMapOperands.reserve(newMemRefRank);
1233 for (
Value extraIndex : extraIndices) {
1235 "invalid memory op index");
1236 newMapOperands.push_back(extraIndex);
1240 newMapOperands.append(remapOutputs.begin(), remapOutputs.end());
1243 assert(newMapOperands.size() == newMemRefRank);
1249 for (
Value value : affineApplyOps)
1250 if (value.use_empty())
1251 value.getDefiningOp()->erase();
1255 state.operands.reserve(op->
getNumOperands() + extraIndices.size());
1260 state.operands.push_back(newMemRef);
1263 if (affMapAccInterface) {
1264 state.operands.append(newMapOperands.begin(), newMapOperands.end());
1269 for (
unsigned i = 0; i < newMemRefRank; i++) {
1270 state.operands.push_back(AffineApplyOp::create(
1273 newMap.getResult(i)),
1279 unsigned oldMapNumInputs = oldMapOperands.size();
1280 state.operands.append(op->
operand_begin() + memRefOperandPos + 1 +
1286 state.types.push_back(
result.getType());
1289 auto newMapAttr = AffineMapAttr::get(newMap);
1290 for (
auto namedAttr : op->
getAttrs()) {
1291 if (affMapAccInterface &&
1292 namedAttr.getName() ==
1293 affMapAccInterface.getAffineMapAttrForMemRef(oldMemRef).getName())
1294 state.attributes.push_back({namedAttr.getName(), newMapAttr});
1296 state.attributes.push_back(namedAttr);
1300 auto *repOp = builder.
create(state);
1307LogicalResult mlir::affine::replaceAllMemRefUsesWith(
1312 bool allowNonDereferencingOps,
bool replaceInDeallocOp) {
1313 unsigned newMemRefRank = cast<MemRefType>(newMemRef.
getType()).getRank();
1314 (
void)newMemRefRank;
1315 unsigned oldMemRefRank = cast<MemRefType>(oldMemRef.
getType()).getRank();
1316 (
void)oldMemRefRank;
1318 assert(indexRemap.
getNumSymbols() == symbolOperands.size() &&
1319 "symbol operand count mismatch");
1321 extraOperands.size() + oldMemRefRank + symbolOperands.size());
1322 assert(indexRemap.
getNumResults() + extraIndices.size() == newMemRefRank);
1324 assert(oldMemRefRank + extraIndices.size() == newMemRefRank);
1328 assert(cast<MemRefType>(oldMemRef.
getType()).getElementType() ==
1329 cast<MemRefType>(newMemRef.
getType()).getElementType());
1335 for (
auto *user : oldMemRef.
getUsers()) {
1337 if (userFilterFn && !userFilterFn(user))
1343 !replaceInDeallocOp)
1349 if (!isa<AffineMapAccessInterface>(*user)) {
1350 if (!allowNonDereferencingOps) {
1353 <<
"Memref replacement failed: non-deferencing memref user: \n"
1360 LLVM_DEBUG(llvm::dbgs() <<
"Memref replacement failed: use without a "
1361 "memrefs normalizable trait: \n"
1370 opsToReplace.insert(user);
1373 for (
auto *user : opsToReplace) {
1374 if (
failed(replaceAllMemRefUsesWith(
1375 oldMemRef, newMemRef, user, extraIndices, indexRemap, extraOperands,
1376 symbolOperands, allowNonDereferencingOps)))
1377 llvm_unreachable(
"memref replacement guaranteed to succeed here");
1411void mlir::affine::createAffineComputationSlice(
1417 if (isa_and_nonnull<AffineApplyOp>(operand.getDefiningOp()))
1418 subOperands.push_back(operand);
1424 if (affineApplyOps.empty())
1429 bool localized =
true;
1430 for (
auto *op : affineApplyOps) {
1432 for (
auto *user :
result.getUsers()) {
1433 if (user != opInst) {
1449 sliceOps->reserve(composedMap.getNumResults());
1450 for (
auto resultExpr : composedMap.getResults()) {
1452 composedMap.getNumSymbols(), resultExpr);
1453 sliceOps->push_back(AffineApplyOp::create(
1454 builder, opInst->
getLoc(), singleResMap, composedOpOperands));
1462 for (
Value &operand : newOperands) {
1465 for (
j = 0, f = subOperands.size();
j < f;
j++) {
1466 if (operand == subOperands[
j])
1469 if (
j < subOperands.size())
1470 operand = (*sliceOps)[
j];
1472 for (
unsigned idx = 0, e = newOperands.size(); idx < e; idx++)
1495 SmallVectorImpl<std::tuple<AffineExpr, unsigned, unsigned>> &tileSizePos) {
1506 if (isa<AffineConstantExpr>(binaryExpr.
getRHS()))
1507 floordivExprs.emplace_back(
1508 std::make_tuple(binaryExpr.
getLHS(), binaryExpr.
getRHS(), pos));
1513 if (floordivExprs.empty()) {
1520 for (std::tuple<AffineExpr, AffineExpr, unsigned> fexpr : floordivExprs) {
1521 AffineExpr floordivExprLHS = std::get<0>(fexpr);
1522 AffineExpr floordivExprRHS = std::get<1>(fexpr);
1523 unsigned floordivPos = std::get<2>(fexpr);
1535 bool notTiled =
false;
1536 if (pos != floordivPos) {
1538 if (e == floordivExprLHS) {
1542 if (floordivExprLHS == binaryExpr.
getLHS() &&
1543 floordivExprRHS == binaryExpr.
getRHS()) {
1547 tileSizePos.emplace_back(
1548 std::make_tuple(binaryExpr.
getRHS(), floordivPos, pos));
1600 if (isa<AffineDimExpr>(e) &&
1601 llvm::any_of(inMemrefTypeDynDims, [&](
unsigned dim) {
1620 binaryExpr = cast<AffineBinaryOpExpr>(oldMapOutput);
1621 newMapOutput = binaryExpr.
getRHS();
1624 binaryExpr = cast<AffineBinaryOpExpr>(oldMapOutput);
1629 newMapOutput = oldMapOutput;
1631 return newMapOutput;
1666template <
typename AllocLikeOp>
1668 MemRefType newMemRefType,
AffineMap map,
1674 unsigned dynIdx = 0;
1675 for (
unsigned d = 0; d < oldMemRefType.getRank(); ++d) {
1676 if (oldMemRefShape[d] < 0) {
1678 inAffineApply.emplace_back(allocOp.getDynamicSizes()[dynIdx]);
1682 auto constantAttr =
b.getIntegerAttr(
b.getIndexType(), oldMemRefShape[d]);
1683 inAffineApply.emplace_back(
1684 arith::ConstantOp::create(
b, allocOp.getLoc(), constantAttr));
1690 unsigned newDimIdx = 0;
1695 if (newMemRefShape[newDimIdx] < 0) {
1698 for (
auto pos : tileSizePos) {
1699 if (newDimIdx == std::get<1>(pos))
1701 else if (newDimIdx == std::get<2>(pos))
1708 AffineApplyOp::create(
b, allocOp.getLoc(), newMap, inAffineApply);
1709 newDynamicSizes.emplace_back(affineApp);
1715template <
typename AllocLikeOp>
1716LogicalResult mlir::affine::normalizeMemRef(AllocLikeOp allocOp) {
1717 MemRefType memrefType = allocOp.getType();
1722 MemRefType newMemRefType = normalizeMemRefType(memrefType);
1723 if (newMemRefType == memrefType)
1728 Value oldMemRef = allocOp.getResult();
1731 AffineMap layoutMap = memrefType.getLayout().getAffineMap();
1732 AllocLikeOp newAlloc;
1737 if (newMemRefType.getNumDynamicDims() > 0 && !tileSizePos.empty()) {
1738 auto oldMemRefType = cast<MemRefType>(oldMemRef.
getType());
1743 newAlloc = AllocLikeOp::create(
b, allocOp.getLoc(), newMemRefType,
1744 newDynamicSizes, allocOp.getAlignmentAttr());
1746 newAlloc = AllocLikeOp::create(
b, allocOp.getLoc(), newMemRefType,
1747 allocOp.getAlignmentAttr());
1750 if (
failed(replaceAllMemRefUsesWith(oldMemRef, newAlloc,
1764 return hasSingleEffect<MemoryEffects::Free>(op, oldMemRef);
1772mlir::affine::normalizeMemRef(memref::ReinterpretCastOp reinterpretCastOp) {
1773 MemRefType memrefType = reinterpretCastOp.getType();
1774 AffineMap oldLayoutMap = memrefType.getLayout().getAffineMap();
1775 Value oldMemRef = reinterpretCastOp.getResult();
1783 MemRefType newMemRefType = normalizeMemRefType(memrefType);
1784 if (newMemRefType == memrefType)
1788 uint64_t newRank = newMemRefType.getRank();
1792 Location loc = reinterpretCastOp.getLoc();
1797 ValueRange oldSizes = reinterpretCastOp.getSizes();
1802 for (
unsigned i = 0, e = memrefType.getRank(); i < e; i++) {
1803 if (memrefType.isDynamicDim(i))
1805 arith::SubIOp::create(
b, loc, oldSizes[0].
getType(), oldSizes[idx++],
1810 for (
unsigned i = 0, e = oldStrides.size(); i < e; i++)
1811 mapOperands[memrefType.getRank() + i] = oldStrides[i];
1815 for (
unsigned i = 0; i < newRank; i++) {
1816 if (!newMemRefType.isDynamicDim(i))
1818 newSizes.push_back(AffineApplyOp::create(
1824 for (
unsigned i = 0, e = newSizes.size(); i < e; i++) {
1826 arith::AddIOp::create(
b, loc, newSizes[i].
getType(), newSizes[i],
1830 auto newReinterpretCast = memref::ReinterpretCastOp::create(
1831 b, loc, newMemRefType, reinterpretCastOp.getSource(),
1839 if (
failed(replaceAllMemRefUsesWith(oldMemRef,
1848 newReinterpretCast.erase();
1853 reinterpretCastOp.erase();
1857template LogicalResult
1858mlir::affine::normalizeMemRef<memref::AllocaOp>(memref::AllocaOp op);
1859template LogicalResult
1860mlir::affine::normalizeMemRef<memref::AllocOp>(memref::AllocOp op);
1862MemRefType mlir::affine::normalizeMemRefType(MemRefType memrefType) {
1863 unsigned rank = memrefType.getRank();
1867 if (memrefType.getLayout().isIdentity()) {
1872 AffineMap layoutMap = memrefType.getLayout().getAffineMap();
1883 if (memrefType.getNumDynamicDims() > 0 && tileSizePos.empty())
1892 for (
unsigned d = 0; d < rank; ++d) {
1895 fac.addBound(BoundType::LB, d, 0);
1896 fac.addBound(BoundType::UB, d,
shape[d] - 1);
1898 memrefTypeDynDims.emplace_back(d);
1904 if (
failed(fac.composeMatchingMap(layoutMap)))
1908 fac.projectOut(newRank, fac.getNumVars() - newRank - fac.getNumLocalVars());
1911 for (
unsigned d = 0; d < newRank; ++d) {
1914 newShape[d] = ShapedType::kDynamic;
1918 std::optional<int64_t> ubConst = fac.getConstantBound64(BoundType::UB, d);
1923 if (!ubConst.has_value() || *ubConst < 0) {
1924 LLVM_DEBUG(llvm::dbgs()
1925 <<
"can't normalize map due to unknown/invalid upper bound");
1929 newShape[d] = *ubConst + 1;
1933 auto newMemRefType =
1938 return newMemRefType;
1964FailureOr<SmallVector<Value>>
1968 basis = basis.drop_front();
1974 FailureOr<OpFoldResult> nextProd =
1978 basisProd = *nextProd;
1983 results.reserve(divisors.size() + 1);
1984 Value residual = linearIndex;
1985 for (
Value divisor : llvm::reverse(divisors)) {
1986 DivModValue divMod = getDivMod(
b, loc, residual, divisor);
1987 results.push_back(divMod.quotient);
1988 residual = divMod.remainder;
1990 results.push_back(residual);
1994FailureOr<SmallVector<Value>>
1997 bool hasOuterBound) {
1999 basis = basis.drop_front();
2005 FailureOr<OpFoldResult> nextProd =
2009 basisProd = *nextProd;
2014 results.reserve(divisors.size() + 1);
2015 Value residual = linearIndex;
2016 for (
Value divisor : llvm::reverse(divisors)) {
2017 DivModValue divMod = getDivMod(
b, loc, residual, divisor);
2018 results.push_back(divMod.quotient);
2019 residual = divMod.remainder;
2021 results.push_back(residual);
2034 assert(multiIndex.size() == basis.size() ||
2035 multiIndex.size() == basis.size() + 1);
2040 if (multiIndex.size() == basis.size() + 1)
2043 for (
size_t i = 0; i < basis.size(); ++i) {
2049 strides.reserve(stridesAffine.size());
2050 llvm::transform(stridesAffine, std::back_inserter(strides),
2051 [&builder, &basis, loc](
AffineExpr strideExpr) {
2053 builder, loc, strideExpr, basis);
2059 multiIndexAndStrides);
static bool mayHaveEffect(Operation *srcMemOp, Operation *destMemOp, unsigned minSurroundingLoops)
Returns true if srcMemOp may have an effect on destMemOp within the scope of the outermost minSurroun...
static AffineIfOp hoistAffineIfOp(AffineIfOp ifOp, Operation *hoistOverOp)
A helper for the mechanics of mlir::hoistAffineIfOp.
static void createNewDynamicSizes(MemRefType oldMemRefType, MemRefType newMemRefType, AffineMap map, AllocLikeOp allocOp, OpBuilder b, SmallVectorImpl< Value > &newDynamicSizes)
Create new maps to calculate each dimension size of newMemRefType, and create newDynamicSizes from th...
static bool isDereferencingOp(Operation *op)
static LogicalResult getTileSizePos(AffineMap map, SmallVectorImpl< std::tuple< AffineExpr, unsigned, unsigned > > &tileSizePos)
Check if map is a tiled layout.
TileExprPattern
Enum to set patterns of affine expr in tiled-layout map.
static void promoteIfBlock(AffineIfOp ifOp, bool elseBlock)
Promotes the then or the else block of ifOp (depending on whether elseBlock is false or true) into if...
static bool isNormalizedMemRefDynamicDim(unsigned dim, AffineMap layoutMap, SmallVectorImpl< unsigned > &inMemrefTypeDynDims)
Check if dim dimension of memrefType with layoutMap becomes dynamic after normalization.
static FailureOr< OpFoldResult > composedAffineMultiply(OpBuilder &b, Location loc, OpFoldResult lhs, OpFoldResult rhs)
Create an affine map that computes lhs * rhs, composing in any other affine maps.
static void loadCSE(AffineReadOpInterface loadA, SmallVectorImpl< Operation * > &loadOpsToErase, DominanceInfo &domInfo, llvm::function_ref< bool(Value, Value)> mayAlias)
static AffineExpr createDimSizeExprForTiledLayout(AffineExpr oldMapOutput, TileExprPattern pat)
Create affine expr to calculate dimension size for a tiled-layout map.
static void findUnusedStore(AffineWriteOpInterface writeA, SmallVectorImpl< Operation * > &opsToErase, PostDominanceInfo &postDominanceInfo, llvm::function_ref< bool(Value, Value)> mayAlias)
static bool mustReachAtInnermost(const MemRefAccess &srcAccess, const MemRefAccess &destAccess)
Returns true if the memory operation of destAccess depends on srcAccess inside of the innermost commo...
static void forwardStoreToLoad(AffineReadOpInterface loadOp, SmallVectorImpl< Operation * > &loadOpsToErase, SmallPtrSetImpl< Value > &memrefsToErase, DominanceInfo &domInfo, llvm::function_ref< bool(Value, Value)> mayAlias)
Attempt to eliminate loadOp by replacing it with a value stored into memory which the load is guarant...
static Operation * getOutermostInvariantForOp(AffineIfOp ifOp)
Returns the outermost affine.for/parallel op that the ifOp is invariant on.
static void visit(Operation *op, DenseSet< Operation * > &visited)
Visits all the pdl.operand(s), pdl.result(s), and pdl.operation(s) connected to the given operation.
static bool mayAlias(Value first, Value second)
Returns true if two values may be referencing aliasing memory.
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
Affine binary operation expression.
AffineExpr getLHS() const
AffineExpr getRHS() const
An integer constant appearing in affine expression.
A dimensional identifier appearing in an affine expression.
unsigned getPosition() const
See documentation for AffineExprVisitorBase.
Base type for affine expression.
AffineExpr floorDiv(uint64_t v) const
RetT walk(FnT &&callback) const
Walk all of the AffineExpr's in this expression in postorder.
AffineExprKind getKind() const
Return the classification for this type.
AffineExpr ceilDiv(uint64_t v) const
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
MLIRContext * getContext() const
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
bool isIdentity() const
Returns true if this affine map is an identity affine map.
A symbolic identifier appearing in an affine expression.
unsigned getPosition() const
This class represents the main alias analysis interface in MLIR.
AliasResult alias(Value lhs, Value rhs)
Given two values, return their aliasing behavior.
bool isNo() const
Returns if this result indicates no possibility of aliasing.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
OpListType::iterator iterator
OpListType & getOperations()
SuccessorRange getSuccessors()
Operation * getTerminator()
Get the terminator operation of this block.
IntegerAttr getIndexAttr(int64_t value)
AffineMap getMultiDimIdentityMap(unsigned rank)
AffineExpr getAffineConstantExpr(int64_t constant)
AffineExpr getAffineDimExpr(unsigned position)
MLIRContext * getContext() const
A class for computing basic dominance information.
bool dominates(Operation *a, Operation *b) const
Return true if operation A dominates operation B, i.e.
This class represents a frozen set of patterns that can be processed by a pattern applicator.
This class allows control over how the GreedyPatternRewriteDriver works.
This is a utility class for mapping one set of IR entities to another.
void clear()
Clears all mappings held by the mapper.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
Location getLoc() const
Accessors for the implied location.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This is a builder type that keeps local references to arguments.
Builder & setShape(ArrayRef< int64_t > newShape)
Builder & setLayout(MemRefLayoutAttrInterface newLayout)
NamedAttribute represents a combination of a name and an Attribute value.
Attribute getValue() const
Return the value of the attribute.
This class helps build Operations.
static OpBuilder atBlockBegin(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the first operation in the block but still ins...
Block::iterator getInsertionPoint() const
Returns the current insertion point of the builder.
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
This class represents a single result from folding an operation.
This trait indicates that the memory effects of an operation includes the effects of operations neste...
This class provides the API for ops that are known to be isolated from above.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
void setOperand(unsigned idx, Value value)
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
operand_iterator operand_begin()
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
unsigned getNumOperands()
operand_iterator operand_end()
OperationName getName()
The name of an operation is the key identifier for it.
operand_range getOperands()
Returns an iterator on the underlying Value's.
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
void replaceAllUsesWith(ValuesT &&values)
Replace all uses of results of this operation with the provided 'values'.
void setOperands(ValueRange operands)
Replace the current operands of this operation with the ones provided in 'operands'.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
user_range getUsers()
Returns a range of all users.
result_range getResults()
Region * getParentRegion()
Returns the region to which the instruction belongs.
void erase()
Remove this operation from its parent block and delete it.
unsigned getNumResults()
Return the number of results held by this operation.
A class for computing basic postdominance information.
bool postDominates(Operation *a, Operation *b) const
Return true if operation A postdominates operation B.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
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...
Type getType() const
Return the type of this value.
void replaceAllUsesExcept(Value newValue, const SmallPtrSetImpl< Operation * > &exceptions)
Replace all uses of 'this' value with 'newValue', updating anything in the IR that uses 'this' to use...
void replaceAllUsesWith(Value newValue)
Replace all uses of 'this' value with the new value, updating anything in the IR that uses 'this' to ...
user_range getUsers() const
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static WalkResult advance()
static WalkResult interrupt()
An AffineValueMap is an affine map plus its ML value operands and results for analysis purposes.
LogicalResult canonicalize()
Attempts to canonicalize the map and operands.
unsigned getNumSymbols() const
ArrayRef< Value > getOperands() const
unsigned getNumDims() const
AffineExpr getResult(unsigned i)
AffineMap getAffineMap() const
unsigned getNumResults() const
static void difference(const AffineValueMap &a, const AffineValueMap &b, AffineValueMap *res)
Return the value map that is the difference of value maps 'a' and 'b', represented as an affine map a...
FlatAffineValueConstraints is an extension of FlatLinearValueConstraints with helper functions for Af...
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
LogicalResult promoteIfSingleIteration(AffineForOp forOp)
Promotes the loop body of a AffineForOp to its containing block if the loop was known to have a singl...
bool isValidDim(Value value)
Returns true if the given Value can be used as a dimension id in the region of the closest surroundin...
unsigned getNumCommonSurroundingLoops(Operation &a, Operation &b)
Returns the number of surrounding loops common to both A and B.
DependenceResult checkMemrefAccessDependence(const MemRefAccess &srcAccess, const MemRefAccess &dstAccess, unsigned loopDepth, FlatAffineValueConstraints *dependenceConstraints=nullptr, SmallVector< DependenceComponent, 2 > *dependenceComponents=nullptr, bool allowRAR=false)
LogicalResult affineParallelize(AffineForOp forOp, ArrayRef< LoopReduction > parallelReductions={}, AffineParallelOp *resOp=nullptr)
Replaces a parallel affine.for op with a 1-d affine.parallel op.
void canonicalizeMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Modifies both map and operands in-place so as to:
void getReachableAffineApplyOps(ArrayRef< Value > operands, SmallVectorImpl< Operation * > &affineApplyOps)
Returns in affineApplyOps, the sequence of those AffineApplyOp Operations that are reachable via a se...
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...
Region * getAffineAnalysisScope(Operation *op)
Returns the closest region enclosing op that is held by a non-affine operation; nullptr if there is n...
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...
bool isValidSymbol(Value value)
Returns true if the given value can be used as a symbol in the region of the closest surrounding op t...
bool hasDependence(DependenceResult result)
Utility function that returns true if the provided DependenceResult corresponds to a dependence resul...
bool hasNoInterveningEffect(Operation *start, T memOp, llvm::function_ref< bool(Value, Value)> mayAlias)
Hoists out affine.if/else to as high as possible, i.e., past all invariant affine....
bool noDependence(DependenceResult result)
Returns true if the provided DependenceResult corresponds to the absence of a dependence.
Value linearizeIndex(ValueRange indices, ArrayRef< int64_t > strides, int64_t offset, Type integerType, Location loc, OpBuilder &builder)
Generates IR to perform index linearization with the given indices and their corresponding strides,...
Include the generated interface declarations.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
SmallVector< int64_t > computeStrides(ArrayRef< int64_t > sizes)
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
std::pair< AffineExpr, SmallVector< OpFoldResult > > computeLinearIndex(OpFoldResult sourceOffset, ArrayRef< OpFoldResult > strides, ArrayRef< OpFoldResult > indices)
Compute linear index from provided strides and indices, assuming strided layout.
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
bool hasSingleEffect(Operation *op)
Returns "true" if op has only an effect of type EffectTy.
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...
@ CeilDiv
RHS of ceildiv is always a constant or a symbolic expression.
@ Mod
RHS of mod is always a constant or a symbolic expression with a positive value.
@ FloorDiv
RHS of floordiv is always a constant or a symbolic expression.
AffineExpr getAffineBinaryOpExpr(AffineExprKind kind, AffineExpr lhs, AffineExpr rhs)
const FrozenRewritePatternSet & patterns
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
@ ExistingOps
Only pre-existing ops are processed.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
The following effect indicates that the operation reads from some resource.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Checks whether two accesses to the same memref access the same element.
A description of a (parallelizable) reduction in an affine loop.
arith::AtomicRMWKind kind
Reduction kind.
Value value
The value being reduced.
Encapsulates a memref load or store access information.
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.