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()));
268 auto *parentOp = res->getParentOp();
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->setDiscardableAttr(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->getDiscardableAttr(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 storeOp : llvm::make_isa_range<AffineWriteOpInterface>(
846 loadOp.getMemRef().getUsers())) {
858 if (srcAccess != destAccess)
878 assert(lastWriteStoreOp ==
nullptr &&
879 "multiple simultaneous replacement stores");
880 lastWriteStoreOp = storeOp;
883 if (!lastWriteStoreOp)
888 cast<AffineWriteOpInterface>(lastWriteStoreOp).getValueToStore();
891 if (storeVal.
getType() != loadOp.getValue().getType())
895 memrefsToErase.insert(loadOp.getMemRef());
897 loadOpsToErase.push_back(loadOp);
902 affine::AffineReadOpInterface>(
918 for (
auto writeB : llvm::make_isa_range<AffineWriteOpInterface>(
919 writeA.getMemRef().getUsers())) {
921 if (writeB == writeA)
925 if (writeB->getParentRegion() != writeA->getParentRegion())
932 if (srcAccess != destAccess)
941 if (writeA.getValueToStore().getType() !=
942 writeB.getValueToStore().getType())
955 opsToErase.push_back(writeA);
966static void loadCSE(AffineReadOpInterface loadA,
971 for (
auto *user : loadA.getMemRef().getUsers()) {
972 auto loadB = dyn_cast<AffineReadOpInterface>(user);
973 if (!loadB || loadB == loadA)
980 if (srcAccess != destAccess) {
990 loadB.getOperation(), loadA,
mayAlias))
995 if (loadB.getValue().getType() != loadA.getValue().getType())
998 loadCandidates.push_back(loadB);
1004 for (AffineReadOpInterface option : loadCandidates) {
1005 if (llvm::all_of(loadCandidates, [&](AffineReadOpInterface depStore) {
1006 return depStore == option ||
1007 domInfo.
dominates(option.getOperation(),
1008 depStore.getOperation());
1010 loadB = option.getValue();
1016 loadA.getValue().replaceAllUsesWith(loadB);
1018 loadOpsToErase.push_back(loadA);
1047void mlir::affine::affineScalarReplace(func::FuncOp f,
DominanceInfo &domInfo,
1057 return !aliasAnalysis.
alias(val1, val2).
isNo();
1061 f.walk([&](AffineReadOpInterface loadOp) {
1064 for (
auto *op : opsToErase)
1069 f.walk([&](AffineWriteOpInterface storeOp) {
1072 for (
auto *op : opsToErase)
1080 for (
auto memref : memrefsToErase) {
1088 return !isa<AffineWriteOpInterface>(ownerOp) &&
1089 !hasSingleEffect<MemoryEffects::Free>(ownerOp, memref);
1094 for (
auto *user : llvm::make_early_inc_range(
memref.getUsers()))
1102 f.walk([&](AffineReadOpInterface loadOp) {
1105 for (
auto *op : opsToErase)
1112 return isa<AffineMapAccessInterface, memref::LoadOp, memref::StoreOp>(op);
1116LogicalResult mlir::affine::replaceAllMemRefUsesWith(
1120 bool allowNonDereferencingOps) {
1121 unsigned newMemRefRank = cast<MemRefType>(newMemRef.
getType()).getRank();
1122 (
void)newMemRefRank;
1123 unsigned oldMemRefRank = cast<MemRefType>(oldMemRef.
getType()).getRank();
1124 (
void)oldMemRefRank;
1126 assert(indexRemap.
getNumSymbols() == symbolOperands.size() &&
1127 "symbolic operand count mismatch");
1129 extraOperands.size() + oldMemRefRank + symbolOperands.size());
1130 assert(indexRemap.
getNumResults() + extraIndices.size() == newMemRefRank);
1132 assert(oldMemRefRank + extraIndices.size() == newMemRefRank);
1136 assert(cast<MemRefType>(oldMemRef.
getType()).getElementType() ==
1137 cast<MemRefType>(newMemRef.
getType()).getElementType());
1140 for (
const auto &opEntry : llvm::enumerate(op->
getOperands())) {
1141 if (opEntry.value() == oldMemRef)
1142 usePositions.push_back(opEntry.index());
1146 if (usePositions.empty())
1149 unsigned memRefOperandPos = usePositions.front();
1155 if (!allowNonDereferencingOps) {
1161 for (
unsigned pos : usePositions)
1166 if (usePositions.size() > 1) {
1168 LLVM_DEBUG(llvm::dbgs()
1169 <<
"multiple dereferencing uses in a single op not supported");
1176 unsigned oldMemRefNumIndices = oldMemRefRank;
1178 auto affMapAccInterface = dyn_cast<AffineMapAccessInterface>(op);
1179 if (affMapAccInterface) {
1184 affMapAccInterface.getAffineMapAttrForMemRef(oldMemRef);
1185 oldMap = cast<AffineMapAttr>(oldMapAttrPair.
getValue()).getValue();
1188 oldMapOperands.assign(startIdx, startIdx + oldMemRefNumIndices);
1193 oldMemRefOperands.reserve(oldMemRefRank);
1194 if (affMapAccInterface &&
1196 for (
auto resultExpr : oldMap.
getResults()) {
1199 auto afOp = AffineApplyOp::create(builder, op->
getLoc(), singleResMap,
1201 oldMemRefOperands.push_back(afOp);
1202 affineApplyOps.push_back(afOp);
1205 oldMemRefOperands.assign(oldMapOperands.begin(), oldMapOperands.end());
1212 remapOperands.reserve(extraOperands.size() + oldMemRefRank +
1213 symbolOperands.size());
1214 remapOperands.append(extraOperands.begin(), extraOperands.end());
1215 remapOperands.append(oldMemRefOperands.begin(), oldMemRefOperands.end());
1216 remapOperands.append(symbolOperands.begin(), symbolOperands.end());
1219 remapOutputs.reserve(oldMemRefRank);
1223 for (
auto resultExpr : indexRemap.
getResults()) {
1226 auto afOp = AffineApplyOp::create(builder, op->
getLoc(), singleResMap,
1228 remapOutputs.push_back(afOp);
1229 affineApplyOps.push_back(afOp);
1233 remapOutputs.assign(remapOperands.begin(), remapOperands.end());
1236 newMapOperands.reserve(newMemRefRank);
1239 for (
Value extraIndex : extraIndices) {
1241 "invalid memory op index");
1242 newMapOperands.push_back(extraIndex);
1246 newMapOperands.append(remapOutputs.begin(), remapOutputs.end());
1249 assert(newMapOperands.size() == newMemRefRank);
1255 for (
Value value : affineApplyOps)
1256 if (value.use_empty())
1257 value.getDefiningOp()->erase();
1261 state.operands.reserve(op->
getNumOperands() + extraIndices.size());
1266 state.operands.push_back(newMemRef);
1269 if (affMapAccInterface) {
1270 state.operands.append(newMapOperands.begin(), newMapOperands.end());
1275 for (
unsigned i = 0; i < newMemRefRank; i++) {
1276 state.operands.push_back(AffineApplyOp::create(
1279 newMap.getResult(i)),
1285 unsigned oldMapNumInputs = oldMapOperands.size();
1286 state.operands.append(op->
operand_begin() + memRefOperandPos + 1 +
1292 state.types.push_back(
result.getType());
1295 auto newMapAttr = AffineMapAttr::get(newMap);
1300 auto *repOp = builder.
create(state);
1301 if (affMapAccInterface) {
1302 StringAttr mapAttrName =
1303 affMapAccInterface.getAffineMapAttrForMemRef(oldMemRef).getName();
1304 repOp->setInherentAttr(mapAttrName, newMapAttr);
1312LogicalResult mlir::affine::replaceAllMemRefUsesWith(
1317 bool allowNonDereferencingOps,
bool replaceInDeallocOp) {
1318 unsigned newMemRefRank = cast<MemRefType>(newMemRef.
getType()).getRank();
1319 (
void)newMemRefRank;
1320 unsigned oldMemRefRank = cast<MemRefType>(oldMemRef.
getType()).getRank();
1321 (
void)oldMemRefRank;
1323 assert(indexRemap.
getNumSymbols() == symbolOperands.size() &&
1324 "symbol operand count mismatch");
1326 extraOperands.size() + oldMemRefRank + symbolOperands.size());
1327 assert(indexRemap.
getNumResults() + extraIndices.size() == newMemRefRank);
1329 assert(oldMemRefRank + extraIndices.size() == newMemRefRank);
1333 assert(cast<MemRefType>(oldMemRef.
getType()).getElementType() ==
1334 cast<MemRefType>(newMemRef.
getType()).getElementType());
1340 for (
auto *user : oldMemRef.
getUsers()) {
1342 if (userFilterFn && !userFilterFn(user))
1348 !replaceInDeallocOp)
1354 if (!isa<AffineMapAccessInterface>(*user)) {
1355 if (!allowNonDereferencingOps) {
1358 <<
"Memref replacement failed: non-deferencing memref user: \n"
1365 LLVM_DEBUG(llvm::dbgs() <<
"Memref replacement failed: use without a "
1366 "memrefs normalizable trait: \n"
1375 opsToReplace.insert(user);
1378 for (
auto *user : opsToReplace) {
1379 if (
failed(replaceAllMemRefUsesWith(
1380 oldMemRef, newMemRef, user, extraIndices, indexRemap, extraOperands,
1381 symbolOperands, allowNonDereferencingOps)))
1416void mlir::affine::createAffineComputationSlice(
1422 if (isa_and_nonnull<AffineApplyOp>(operand.getDefiningOp()))
1423 subOperands.push_back(operand);
1429 if (affineApplyOps.empty())
1434 bool localized =
true;
1435 for (
auto *op : affineApplyOps) {
1437 for (
auto *user :
result.getUsers()) {
1438 if (user != opInst) {
1454 sliceOps->reserve(composedMap.getNumResults());
1455 for (
auto resultExpr : composedMap.getResults()) {
1457 composedMap.getNumSymbols(), resultExpr);
1458 sliceOps->push_back(AffineApplyOp::create(
1459 builder, opInst->
getLoc(), singleResMap, composedOpOperands));
1467 for (
Value &operand : newOperands) {
1470 for (
j = 0, f = subOperands.size();
j < f;
j++) {
1471 if (operand == subOperands[
j])
1474 if (
j < subOperands.size())
1475 operand = (*sliceOps)[
j];
1477 for (
unsigned idx = 0, e = newOperands.size(); idx < e; idx++)
1500 SmallVectorImpl<std::tuple<AffineExpr, unsigned, unsigned>> &tileSizePos) {
1511 if (isa<AffineConstantExpr>(binaryExpr.
getRHS()))
1512 floordivExprs.emplace_back(
1513 std::make_tuple(binaryExpr.
getLHS(), binaryExpr.
getRHS(), pos));
1518 if (floordivExprs.empty()) {
1525 for (std::tuple<AffineExpr, AffineExpr, unsigned> fexpr : floordivExprs) {
1526 AffineExpr floordivExprLHS = std::get<0>(fexpr);
1527 AffineExpr floordivExprRHS = std::get<1>(fexpr);
1528 unsigned floordivPos = std::get<2>(fexpr);
1540 bool notTiled =
false;
1541 if (pos != floordivPos) {
1543 if (e == floordivExprLHS) {
1547 if (floordivExprLHS == binaryExpr.
getLHS() &&
1548 floordivExprRHS == binaryExpr.
getRHS()) {
1552 tileSizePos.emplace_back(
1553 std::make_tuple(binaryExpr.
getRHS(), floordivPos, pos));
1605 if (isa<AffineDimExpr>(e) &&
1606 llvm::any_of(inMemrefTypeDynDims, [&](
unsigned dim) {
1625 binaryExpr = cast<AffineBinaryOpExpr>(oldMapOutput);
1626 newMapOutput = binaryExpr.
getRHS();
1629 binaryExpr = cast<AffineBinaryOpExpr>(oldMapOutput);
1634 newMapOutput = oldMapOutput;
1636 return newMapOutput;
1671template <
typename AllocLikeOp>
1673 MemRefType newMemRefType,
AffineMap map,
1679 unsigned dynIdx = 0;
1680 for (
unsigned d = 0; d < oldMemRefType.getRank(); ++d) {
1681 if (oldMemRefShape[d] < 0) {
1683 inAffineApply.emplace_back(allocOp.getDynamicSizes()[dynIdx]);
1687 auto constantAttr =
b.getIntegerAttr(
b.getIndexType(), oldMemRefShape[d]);
1688 inAffineApply.emplace_back(
1689 arith::ConstantOp::create(
b, allocOp.getLoc(), constantAttr));
1695 unsigned newDimIdx = 0;
1700 if (newMemRefShape[newDimIdx] < 0) {
1703 for (
auto pos : tileSizePos) {
1704 if (newDimIdx == std::get<1>(pos))
1706 else if (newDimIdx == std::get<2>(pos))
1713 AffineApplyOp::create(
b, allocOp.getLoc(), newMap, inAffineApply);
1714 newDynamicSizes.emplace_back(affineApp);
1720template <
typename AllocLikeOp>
1721LogicalResult mlir::affine::normalizeMemRef(AllocLikeOp allocOp) {
1722 MemRefType memrefType = allocOp.getType();
1727 MemRefType newMemRefType = normalizeMemRefType(memrefType);
1728 if (newMemRefType == memrefType)
1733 Value oldMemRef = allocOp.getResult();
1736 AffineMap layoutMap = memrefType.getLayout().getAffineMap();
1737 AllocLikeOp newAlloc;
1742 if (newMemRefType.getNumDynamicDims() > 0 && !tileSizePos.empty()) {
1743 auto oldMemRefType = cast<MemRefType>(oldMemRef.
getType());
1748 newAlloc = AllocLikeOp::create(
b, allocOp.getLoc(), newMemRefType,
1749 newDynamicSizes, allocOp.getAlignmentAttr());
1751 newAlloc = AllocLikeOp::create(
b, allocOp.getLoc(), newMemRefType,
1752 allocOp.getAlignmentAttr());
1755 if (
failed(replaceAllMemRefUsesWith(oldMemRef, newAlloc,
1769 return hasSingleEffect<MemoryEffects::Free>(op, oldMemRef);
1777mlir::affine::normalizeMemRef(memref::ReinterpretCastOp reinterpretCastOp) {
1778 MemRefType memrefType = reinterpretCastOp.getType();
1779 AffineMap oldLayoutMap = memrefType.getLayout().getAffineMap();
1780 Value oldMemRef = reinterpretCastOp.getResult();
1788 MemRefType newMemRefType = normalizeMemRefType(memrefType);
1789 if (newMemRefType == memrefType)
1793 uint64_t newRank = newMemRefType.getRank();
1797 Location loc = reinterpretCastOp.getLoc();
1802 ValueRange oldSizes = reinterpretCastOp.getSizes();
1807 for (
unsigned i = 0, e = memrefType.getRank(); i < e; i++) {
1808 if (memrefType.isDynamicDim(i))
1810 arith::SubIOp::create(
b, loc, oldSizes[0].
getType(), oldSizes[idx++],
1815 for (
unsigned i = 0, e = oldStrides.size(); i < e; i++)
1816 mapOperands[memrefType.getRank() + i] = oldStrides[i];
1820 for (
unsigned i = 0; i < newRank; i++) {
1821 if (!newMemRefType.isDynamicDim(i))
1823 newSizes.push_back(AffineApplyOp::create(
1829 for (
auto &newSize : newSizes) {
1830 newSize = arith::AddIOp::create(
b, loc, newSize.getType(), newSize,
1834 auto newReinterpretCast = memref::ReinterpretCastOp::create(
1835 b, loc, newMemRefType, reinterpretCastOp.getSource(),
1843 if (
failed(replaceAllMemRefUsesWith(oldMemRef,
1852 newReinterpretCast.erase();
1857 reinterpretCastOp.erase();
1861template LogicalResult
1862mlir::affine::normalizeMemRef<memref::AllocaOp>(memref::AllocaOp op);
1863template LogicalResult
1864mlir::affine::normalizeMemRef<memref::AllocOp>(memref::AllocOp op);
1866MemRefType mlir::affine::normalizeMemRefType(MemRefType memrefType) {
1867 unsigned rank = memrefType.getRank();
1871 if (memrefType.getLayout().isIdentity()) {
1876 AffineMap layoutMap = memrefType.getLayout().getAffineMap();
1887 if (memrefType.getNumDynamicDims() > 0 && tileSizePos.empty())
1896 for (
unsigned d = 0; d < rank; ++d) {
1899 fac.addBound(BoundType::LB, d, 0);
1900 fac.addBound(BoundType::UB, d,
shape[d] - 1);
1902 memrefTypeDynDims.emplace_back(d);
1908 if (
failed(fac.composeMatchingMap(layoutMap)))
1912 fac.projectOut(newRank, fac.getNumVars() - newRank - fac.getNumLocalVars());
1915 for (
unsigned d = 0; d < newRank; ++d) {
1918 newShape[d] = ShapedType::kDynamic;
1922 std::optional<int64_t> ubConst = fac.getConstantBound64(BoundType::UB, d);
1927 if (!ubConst.has_value() || *ubConst < 0) {
1928 LLVM_DEBUG(llvm::dbgs()
1929 <<
"can't normalize map due to unknown/invalid upper bound");
1933 newShape[d] = *ubConst + 1;
1937 auto newMemRefType =
1942 return newMemRefType;
1968FailureOr<SmallVector<Value>>
1972 basis = basis.drop_front();
1978 FailureOr<OpFoldResult> nextProd =
1982 basisProd = *nextProd;
1987 results.reserve(divisors.size() + 1);
1988 Value residual = linearIndex;
1989 for (
Value divisor : llvm::reverse(divisors)) {
1990 DivModValue divMod = getDivMod(
b, loc, residual, divisor);
1991 results.push_back(divMod.quotient);
1992 residual = divMod.remainder;
1994 results.push_back(residual);
1998FailureOr<SmallVector<Value>>
2001 bool hasOuterBound) {
2003 basis = basis.drop_front();
2009 FailureOr<OpFoldResult> nextProd =
2013 basisProd = *nextProd;
2018 results.reserve(divisors.size() + 1);
2019 Value residual = linearIndex;
2020 for (
Value divisor : llvm::reverse(divisors)) {
2021 DivModValue divMod = getDivMod(
b, loc, residual, divisor);
2022 results.push_back(divMod.quotient);
2023 residual = divMod.remainder;
2025 results.push_back(residual);
2038 assert(multiIndex.size() == basis.size() ||
2039 multiIndex.size() == basis.size() + 1);
2044 if (multiIndex.size() == basis.size() + 1)
2047 for (
size_t i = 0; i < basis.size(); ++i) {
2053 strides.reserve(stridesAffine.size());
2054 llvm::transform(stridesAffine, std::back_inserter(strides),
2055 [&builder, &basis, loc](
AffineExpr strideExpr) {
2057 builder, loc, strideExpr, basis);
2063 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)
void setOperand(unsigned idx, Value value)
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()
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
operand_iterator operand_end()
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
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),...
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, LinearizedIndexNoWrapFlags noWrapFlags={})
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.