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>(
921 auto writeB = dyn_cast<AffineWriteOpInterface>(user);
926 if (writeB == writeA)
930 if (writeB->getParentRegion() != writeA->getParentRegion())
937 if (srcAccess != destAccess)
946 if (writeA.getValueToStore().getType() !=
947 writeB.getValueToStore().getType())
960 opsToErase.push_back(writeA);
971static void loadCSE(AffineReadOpInterface loadA,
976 for (
auto *user : loadA.getMemRef().getUsers()) {
977 auto loadB = dyn_cast<AffineReadOpInterface>(user);
978 if (!loadB || loadB == loadA)
985 if (srcAccess != destAccess) {
995 loadB.getOperation(), loadA,
mayAlias))
1000 if (loadB.getValue().getType() != loadA.getValue().getType())
1003 loadCandidates.push_back(loadB);
1009 for (AffineReadOpInterface option : loadCandidates) {
1010 if (llvm::all_of(loadCandidates, [&](AffineReadOpInterface depStore) {
1011 return depStore == option ||
1012 domInfo.
dominates(option.getOperation(),
1013 depStore.getOperation());
1015 loadB = option.getValue();
1021 loadA.getValue().replaceAllUsesWith(loadB);
1023 loadOpsToErase.push_back(loadA);
1052void mlir::affine::affineScalarReplace(func::FuncOp f,
DominanceInfo &domInfo,
1062 return !aliasAnalysis.
alias(val1, val2).
isNo();
1066 f.walk([&](AffineReadOpInterface loadOp) {
1069 for (
auto *op : opsToErase)
1074 f.walk([&](AffineWriteOpInterface storeOp) {
1077 for (
auto *op : opsToErase)
1085 for (
auto memref : memrefsToErase) {
1093 return !isa<AffineWriteOpInterface>(ownerOp) &&
1094 !hasSingleEffect<MemoryEffects::Free>(ownerOp, memref);
1099 for (
auto *user : llvm::make_early_inc_range(
memref.getUsers()))
1107 f.walk([&](AffineReadOpInterface loadOp) {
1110 for (
auto *op : opsToErase)
1117 return isa<AffineMapAccessInterface, memref::LoadOp, memref::StoreOp>(op);
1121LogicalResult mlir::affine::replaceAllMemRefUsesWith(
1125 bool allowNonDereferencingOps) {
1126 unsigned newMemRefRank = cast<MemRefType>(newMemRef.
getType()).getRank();
1127 (
void)newMemRefRank;
1128 unsigned oldMemRefRank = cast<MemRefType>(oldMemRef.
getType()).getRank();
1129 (
void)oldMemRefRank;
1131 assert(indexRemap.
getNumSymbols() == symbolOperands.size() &&
1132 "symbolic operand count mismatch");
1134 extraOperands.size() + oldMemRefRank + symbolOperands.size());
1135 assert(indexRemap.
getNumResults() + extraIndices.size() == newMemRefRank);
1137 assert(oldMemRefRank + extraIndices.size() == newMemRefRank);
1141 assert(cast<MemRefType>(oldMemRef.
getType()).getElementType() ==
1142 cast<MemRefType>(newMemRef.
getType()).getElementType());
1145 for (
const auto &opEntry : llvm::enumerate(op->
getOperands())) {
1146 if (opEntry.value() == oldMemRef)
1147 usePositions.push_back(opEntry.index());
1151 if (usePositions.empty())
1154 unsigned memRefOperandPos = usePositions.front();
1160 if (!allowNonDereferencingOps) {
1166 for (
unsigned pos : usePositions)
1171 if (usePositions.size() > 1) {
1173 LLVM_DEBUG(llvm::dbgs()
1174 <<
"multiple dereferencing uses in a single op not supported");
1181 unsigned oldMemRefNumIndices = oldMemRefRank;
1183 auto affMapAccInterface = dyn_cast<AffineMapAccessInterface>(op);
1184 if (affMapAccInterface) {
1189 affMapAccInterface.getAffineMapAttrForMemRef(oldMemRef);
1190 oldMap = cast<AffineMapAttr>(oldMapAttrPair.
getValue()).getValue();
1193 oldMapOperands.assign(startIdx, startIdx + oldMemRefNumIndices);
1198 oldMemRefOperands.reserve(oldMemRefRank);
1199 if (affMapAccInterface &&
1201 for (
auto resultExpr : oldMap.
getResults()) {
1204 auto afOp = AffineApplyOp::create(builder, op->
getLoc(), singleResMap,
1206 oldMemRefOperands.push_back(afOp);
1207 affineApplyOps.push_back(afOp);
1210 oldMemRefOperands.assign(oldMapOperands.begin(), oldMapOperands.end());
1217 remapOperands.reserve(extraOperands.size() + oldMemRefRank +
1218 symbolOperands.size());
1219 remapOperands.append(extraOperands.begin(), extraOperands.end());
1220 remapOperands.append(oldMemRefOperands.begin(), oldMemRefOperands.end());
1221 remapOperands.append(symbolOperands.begin(), symbolOperands.end());
1224 remapOutputs.reserve(oldMemRefRank);
1228 for (
auto resultExpr : indexRemap.
getResults()) {
1231 auto afOp = AffineApplyOp::create(builder, op->
getLoc(), singleResMap,
1233 remapOutputs.push_back(afOp);
1234 affineApplyOps.push_back(afOp);
1238 remapOutputs.assign(remapOperands.begin(), remapOperands.end());
1241 newMapOperands.reserve(newMemRefRank);
1244 for (
Value extraIndex : extraIndices) {
1246 "invalid memory op index");
1247 newMapOperands.push_back(extraIndex);
1251 newMapOperands.append(remapOutputs.begin(), remapOutputs.end());
1254 assert(newMapOperands.size() == newMemRefRank);
1260 for (
Value value : affineApplyOps)
1261 if (value.use_empty())
1262 value.getDefiningOp()->erase();
1266 state.operands.reserve(op->
getNumOperands() + extraIndices.size());
1271 state.operands.push_back(newMemRef);
1274 if (affMapAccInterface) {
1275 state.operands.append(newMapOperands.begin(), newMapOperands.end());
1280 for (
unsigned i = 0; i < newMemRefRank; i++) {
1281 state.operands.push_back(AffineApplyOp::create(
1284 newMap.getResult(i)),
1290 unsigned oldMapNumInputs = oldMapOperands.size();
1291 state.operands.append(op->
operand_begin() + memRefOperandPos + 1 +
1297 state.types.push_back(
result.getType());
1300 auto newMapAttr = AffineMapAttr::get(newMap);
1301 for (
auto namedAttr : op->
getAttrs()) {
1302 if (affMapAccInterface &&
1303 namedAttr.getName() ==
1304 affMapAccInterface.getAffineMapAttrForMemRef(oldMemRef).getName())
1305 state.attributes.push_back({namedAttr.getName(), newMapAttr});
1307 state.attributes.push_back(namedAttr);
1311 auto *repOp = builder.
create(state);
1318LogicalResult mlir::affine::replaceAllMemRefUsesWith(
1323 bool allowNonDereferencingOps,
bool replaceInDeallocOp) {
1324 unsigned newMemRefRank = cast<MemRefType>(newMemRef.
getType()).getRank();
1325 (
void)newMemRefRank;
1326 unsigned oldMemRefRank = cast<MemRefType>(oldMemRef.
getType()).getRank();
1327 (
void)oldMemRefRank;
1329 assert(indexRemap.
getNumSymbols() == symbolOperands.size() &&
1330 "symbol operand count mismatch");
1332 extraOperands.size() + oldMemRefRank + symbolOperands.size());
1333 assert(indexRemap.
getNumResults() + extraIndices.size() == newMemRefRank);
1335 assert(oldMemRefRank + extraIndices.size() == newMemRefRank);
1339 assert(cast<MemRefType>(oldMemRef.
getType()).getElementType() ==
1340 cast<MemRefType>(newMemRef.
getType()).getElementType());
1346 for (
auto *user : oldMemRef.
getUsers()) {
1348 if (userFilterFn && !userFilterFn(user))
1354 !replaceInDeallocOp)
1360 if (!isa<AffineMapAccessInterface>(*user)) {
1361 if (!allowNonDereferencingOps) {
1364 <<
"Memref replacement failed: non-deferencing memref user: \n"
1371 LLVM_DEBUG(llvm::dbgs() <<
"Memref replacement failed: use without a "
1372 "memrefs normalizable trait: \n"
1381 opsToReplace.insert(user);
1384 for (
auto *user : opsToReplace) {
1385 if (
failed(replaceAllMemRefUsesWith(
1386 oldMemRef, newMemRef, user, extraIndices, indexRemap, extraOperands,
1387 symbolOperands, allowNonDereferencingOps)))
1422void mlir::affine::createAffineComputationSlice(
1428 if (isa_and_nonnull<AffineApplyOp>(operand.getDefiningOp()))
1429 subOperands.push_back(operand);
1435 if (affineApplyOps.empty())
1440 bool localized =
true;
1441 for (
auto *op : affineApplyOps) {
1443 for (
auto *user :
result.getUsers()) {
1444 if (user != opInst) {
1460 sliceOps->reserve(composedMap.getNumResults());
1461 for (
auto resultExpr : composedMap.getResults()) {
1463 composedMap.getNumSymbols(), resultExpr);
1464 sliceOps->push_back(AffineApplyOp::create(
1465 builder, opInst->
getLoc(), singleResMap, composedOpOperands));
1473 for (
Value &operand : newOperands) {
1476 for (
j = 0, f = subOperands.size();
j < f;
j++) {
1477 if (operand == subOperands[
j])
1480 if (
j < subOperands.size())
1481 operand = (*sliceOps)[
j];
1483 for (
unsigned idx = 0, e = newOperands.size(); idx < e; idx++)
1506 SmallVectorImpl<std::tuple<AffineExpr, unsigned, unsigned>> &tileSizePos) {
1517 if (isa<AffineConstantExpr>(binaryExpr.
getRHS()))
1518 floordivExprs.emplace_back(
1519 std::make_tuple(binaryExpr.
getLHS(), binaryExpr.
getRHS(), pos));
1524 if (floordivExprs.empty()) {
1531 for (std::tuple<AffineExpr, AffineExpr, unsigned> fexpr : floordivExprs) {
1532 AffineExpr floordivExprLHS = std::get<0>(fexpr);
1533 AffineExpr floordivExprRHS = std::get<1>(fexpr);
1534 unsigned floordivPos = std::get<2>(fexpr);
1546 bool notTiled =
false;
1547 if (pos != floordivPos) {
1549 if (e == floordivExprLHS) {
1553 if (floordivExprLHS == binaryExpr.
getLHS() &&
1554 floordivExprRHS == binaryExpr.
getRHS()) {
1558 tileSizePos.emplace_back(
1559 std::make_tuple(binaryExpr.
getRHS(), floordivPos, pos));
1611 if (isa<AffineDimExpr>(e) &&
1612 llvm::any_of(inMemrefTypeDynDims, [&](
unsigned dim) {
1631 binaryExpr = cast<AffineBinaryOpExpr>(oldMapOutput);
1632 newMapOutput = binaryExpr.
getRHS();
1635 binaryExpr = cast<AffineBinaryOpExpr>(oldMapOutput);
1640 newMapOutput = oldMapOutput;
1642 return newMapOutput;
1677template <
typename AllocLikeOp>
1679 MemRefType newMemRefType,
AffineMap map,
1685 unsigned dynIdx = 0;
1686 for (
unsigned d = 0; d < oldMemRefType.getRank(); ++d) {
1687 if (oldMemRefShape[d] < 0) {
1689 inAffineApply.emplace_back(allocOp.getDynamicSizes()[dynIdx]);
1693 auto constantAttr =
b.getIntegerAttr(
b.getIndexType(), oldMemRefShape[d]);
1694 inAffineApply.emplace_back(
1695 arith::ConstantOp::create(
b, allocOp.getLoc(), constantAttr));
1701 unsigned newDimIdx = 0;
1706 if (newMemRefShape[newDimIdx] < 0) {
1709 for (
auto pos : tileSizePos) {
1710 if (newDimIdx == std::get<1>(pos))
1712 else if (newDimIdx == std::get<2>(pos))
1719 AffineApplyOp::create(
b, allocOp.getLoc(), newMap, inAffineApply);
1720 newDynamicSizes.emplace_back(affineApp);
1726template <
typename AllocLikeOp>
1727LogicalResult mlir::affine::normalizeMemRef(AllocLikeOp allocOp) {
1728 MemRefType memrefType = allocOp.getType();
1733 MemRefType newMemRefType = normalizeMemRefType(memrefType);
1734 if (newMemRefType == memrefType)
1739 Value oldMemRef = allocOp.getResult();
1742 AffineMap layoutMap = memrefType.getLayout().getAffineMap();
1743 AllocLikeOp newAlloc;
1748 if (newMemRefType.getNumDynamicDims() > 0 && !tileSizePos.empty()) {
1749 auto oldMemRefType = cast<MemRefType>(oldMemRef.
getType());
1754 newAlloc = AllocLikeOp::create(
b, allocOp.getLoc(), newMemRefType,
1755 newDynamicSizes, allocOp.getAlignmentAttr());
1757 newAlloc = AllocLikeOp::create(
b, allocOp.getLoc(), newMemRefType,
1758 allocOp.getAlignmentAttr());
1761 if (
failed(replaceAllMemRefUsesWith(oldMemRef, newAlloc,
1775 return hasSingleEffect<MemoryEffects::Free>(op, oldMemRef);
1783mlir::affine::normalizeMemRef(memref::ReinterpretCastOp reinterpretCastOp) {
1784 MemRefType memrefType = reinterpretCastOp.getType();
1785 AffineMap oldLayoutMap = memrefType.getLayout().getAffineMap();
1786 Value oldMemRef = reinterpretCastOp.getResult();
1794 MemRefType newMemRefType = normalizeMemRefType(memrefType);
1795 if (newMemRefType == memrefType)
1799 uint64_t newRank = newMemRefType.getRank();
1803 Location loc = reinterpretCastOp.getLoc();
1808 ValueRange oldSizes = reinterpretCastOp.getSizes();
1813 for (
unsigned i = 0, e = memrefType.getRank(); i < e; i++) {
1814 if (memrefType.isDynamicDim(i))
1816 arith::SubIOp::create(
b, loc, oldSizes[0].
getType(), oldSizes[idx++],
1821 for (
unsigned i = 0, e = oldStrides.size(); i < e; i++)
1822 mapOperands[memrefType.getRank() + i] = oldStrides[i];
1826 for (
unsigned i = 0; i < newRank; i++) {
1827 if (!newMemRefType.isDynamicDim(i))
1829 newSizes.push_back(AffineApplyOp::create(
1835 for (
auto &newSize : newSizes) {
1836 newSize = arith::AddIOp::create(
b, loc, newSize.getType(), newSize,
1840 auto newReinterpretCast = memref::ReinterpretCastOp::create(
1841 b, loc, newMemRefType, reinterpretCastOp.getSource(),
1849 if (
failed(replaceAllMemRefUsesWith(oldMemRef,
1858 newReinterpretCast.erase();
1863 reinterpretCastOp.erase();
1867template LogicalResult
1868mlir::affine::normalizeMemRef<memref::AllocaOp>(memref::AllocaOp op);
1869template LogicalResult
1870mlir::affine::normalizeMemRef<memref::AllocOp>(memref::AllocOp op);
1872MemRefType mlir::affine::normalizeMemRefType(MemRefType memrefType) {
1873 unsigned rank = memrefType.getRank();
1877 if (memrefType.getLayout().isIdentity()) {
1882 AffineMap layoutMap = memrefType.getLayout().getAffineMap();
1893 if (memrefType.getNumDynamicDims() > 0 && tileSizePos.empty())
1902 for (
unsigned d = 0; d < rank; ++d) {
1905 fac.addBound(BoundType::LB, d, 0);
1906 fac.addBound(BoundType::UB, d,
shape[d] - 1);
1908 memrefTypeDynDims.emplace_back(d);
1914 if (
failed(fac.composeMatchingMap(layoutMap)))
1918 fac.projectOut(newRank, fac.getNumVars() - newRank - fac.getNumLocalVars());
1921 for (
unsigned d = 0; d < newRank; ++d) {
1924 newShape[d] = ShapedType::kDynamic;
1928 std::optional<int64_t> ubConst = fac.getConstantBound64(BoundType::UB, d);
1933 if (!ubConst.has_value() || *ubConst < 0) {
1934 LLVM_DEBUG(llvm::dbgs()
1935 <<
"can't normalize map due to unknown/invalid upper bound");
1939 newShape[d] = *ubConst + 1;
1943 auto newMemRefType =
1948 return newMemRefType;
1974FailureOr<SmallVector<Value>>
1978 basis = basis.drop_front();
1984 FailureOr<OpFoldResult> nextProd =
1988 basisProd = *nextProd;
1993 results.reserve(divisors.size() + 1);
1994 Value residual = linearIndex;
1995 for (
Value divisor : llvm::reverse(divisors)) {
1996 DivModValue divMod = getDivMod(
b, loc, residual, divisor);
1997 results.push_back(divMod.quotient);
1998 residual = divMod.remainder;
2000 results.push_back(residual);
2004FailureOr<SmallVector<Value>>
2007 bool hasOuterBound) {
2009 basis = basis.drop_front();
2015 FailureOr<OpFoldResult> nextProd =
2019 basisProd = *nextProd;
2024 results.reserve(divisors.size() + 1);
2025 Value residual = linearIndex;
2026 for (
Value divisor : llvm::reverse(divisors)) {
2027 DivModValue divMod = getDivMod(
b, loc, residual, divisor);
2028 results.push_back(divMod.quotient);
2029 residual = divMod.remainder;
2031 results.push_back(residual);
2044 assert(multiIndex.size() == basis.size() ||
2045 multiIndex.size() == basis.size() + 1);
2050 if (multiIndex.size() == basis.size() + 1)
2053 for (
size_t i = 0; i < basis.size(); ++i) {
2059 strides.reserve(stridesAffine.size());
2060 llvm::transform(stridesAffine, std::back_inserter(strides),
2061 [&builder, &basis, loc](
AffineExpr strideExpr) {
2063 builder, loc, strideExpr, basis);
2069 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)
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.