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();
584 AffineValueMap ubValueMap(op.getUpperBoundMap(), op.getUpperBoundOperands());
593 for (
unsigned i = 0; i < numResult; ++i)
594 scaleDownExprs[i] = opBuilder.getAffineDimExpr(i).ceilDiv(origLoopStep);
602 op.setUpperBound(newUbValueMap.
getOperands(), newUbMap);
603 op.setLowerBound({}, opBuilder.getConstantAffineMap(0));
608 opBuilder.setInsertionPointToStart(op.getBody());
611 AffineMap::get(1, 0, -opBuilder.getAffineDimExpr(0) * origLoopStep);
615 (
void)newIvToOldIvMap.canonicalize();
617 AffineApplyOp::create(opBuilder, loc, newIvToOldIvMap.getAffineMap(),
618 newIvToOldIvMap.getOperands());
619 op.getInductionVar().replaceAllUsesExcept(newIV->getResult(0), newIV);
645 unsigned minSurroundingLoops) {
659 for (
unsigned d = nsLoops + 1; d > minSurroundingLoops; d--) {
661 srcAccess, destAccess, d, &dependenceConstraints,
676template <
typename EffectType,
typename T>
682 bool hasSideEffect =
false;
691 if (
auto memEffect = dyn_cast<MemoryEffectOpInterface>(op)) {
693 memEffect.getEffects(effects);
695 bool opMayHaveEffect =
false;
696 for (
auto effect : effects) {
699 if (isa<EffectType>(effect.getEffect())) {
700 if (effect.getValue() && effect.getValue() !=
memref &&
703 opMayHaveEffect =
true;
708 if (!opMayHaveEffect)
713 if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) {
720 unsigned minSurroundingLoops =
723 hasSideEffect =
true;
729 hasSideEffect =
true;
736 for (
Region ®ion : op->getRegions())
737 for (
Block &block : region)
745 hasSideEffect =
true;
756 checkOperation(parent);
765 "Checking for side effect between two operations without a common "
773 until(untilOp->getParentOp(), untilOp);
785 for (
auto iter = ++from->getIterator(), end = from->
getBlock()->
end();
786 iter != end && &*iter != untilOp; ++iter) {
787 checkOperation(&*iter);
792 if (untilOp->getBlock() != from->
getBlock())
794 todoBlocks.push_back(succ);
799 while (!todoBlocks.empty()) {
800 Block *blk = todoBlocks.pop_back_val();
804 for (
auto &op : *blk) {
810 todoBlocks.push_back(succ);
815 return !hasSideEffect;
834 for (
auto storeOp : llvm::make_isa_range<AffineWriteOpInterface>(
835 loadOp.getMemRef().getUsers())) {
847 if (srcAccess != destAccess)
867 assert(lastWriteStoreOp ==
nullptr &&
868 "multiple simultaneous replacement stores");
869 lastWriteStoreOp = storeOp;
872 if (!lastWriteStoreOp)
877 cast<AffineWriteOpInterface>(lastWriteStoreOp).getValueToStore();
880 if (storeVal.
getType() != loadOp.getValue().getType())
884 memrefsToErase.insert(loadOp.getMemRef());
886 loadOpsToErase.push_back(loadOp);
891 affine::AffineReadOpInterface>(
907 for (
auto writeB : llvm::make_isa_range<AffineWriteOpInterface>(
908 writeA.getMemRef().getUsers())) {
910 if (writeB == writeA)
914 if (writeB->getParentRegion() != writeA->getParentRegion())
921 if (srcAccess != destAccess)
930 if (writeA.getValueToStore().getType() !=
931 writeB.getValueToStore().getType())
944 opsToErase.push_back(writeA);
955static void loadCSE(AffineReadOpInterface loadA,
960 for (
auto *user : loadA.getMemRef().getUsers()) {
961 auto loadB = dyn_cast<AffineReadOpInterface>(user);
962 if (!loadB || loadB == loadA)
969 if (srcAccess != destAccess) {
979 loadB.getOperation(), loadA,
mayAlias))
984 if (loadB.getValue().getType() != loadA.getValue().getType())
987 loadCandidates.push_back(loadB);
993 for (AffineReadOpInterface option : loadCandidates) {
994 if (llvm::all_of(loadCandidates, [&](AffineReadOpInterface depStore) {
995 return depStore == option ||
997 depStore.getOperation());
999 loadB = option.getValue();
1005 loadA.getValue().replaceAllUsesWith(loadB);
1007 loadOpsToErase.push_back(loadA);
1036void mlir::affine::affineScalarReplace(func::FuncOp f,
DominanceInfo &domInfo,
1046 return !aliasAnalysis.
alias(val1, val2).
isNo();
1050 f.walk([&](AffineReadOpInterface loadOp) {
1053 for (
auto *op : opsToErase)
1058 f.walk([&](AffineWriteOpInterface storeOp) {
1061 for (
auto *op : opsToErase)
1069 for (
auto memref : memrefsToErase) {
1077 return !isa<AffineWriteOpInterface>(ownerOp) &&
1078 !hasSingleEffect<MemoryEffects::Free>(ownerOp, memref);
1083 for (
auto *user : llvm::make_early_inc_range(
memref.getUsers()))
1091 f.walk([&](AffineReadOpInterface loadOp) {
1094 for (
auto *op : opsToErase)
1101 return isa<AffineMapAccessInterface, memref::LoadOp, memref::StoreOp>(op);
1105LogicalResult mlir::affine::replaceAllMemRefUsesWith(
1109 bool allowNonDereferencingOps) {
1110 unsigned newMemRefRank = cast<MemRefType>(newMemRef.
getType()).getRank();
1111 (
void)newMemRefRank;
1112 unsigned oldMemRefRank = cast<MemRefType>(oldMemRef.
getType()).getRank();
1113 (
void)oldMemRefRank;
1115 assert(indexRemap.
getNumSymbols() == symbolOperands.size() &&
1116 "symbolic operand count mismatch");
1118 extraOperands.size() + oldMemRefRank + symbolOperands.size());
1119 assert(indexRemap.
getNumResults() + extraIndices.size() == newMemRefRank);
1121 assert(oldMemRefRank + extraIndices.size() == newMemRefRank);
1125 assert(cast<MemRefType>(oldMemRef.
getType()).getElementType() ==
1126 cast<MemRefType>(newMemRef.
getType()).getElementType());
1129 for (
const auto &opEntry : llvm::enumerate(op->
getOperands())) {
1130 if (opEntry.value() == oldMemRef)
1131 usePositions.push_back(opEntry.index());
1135 if (usePositions.empty())
1138 unsigned memRefOperandPos = usePositions.front();
1144 if (!allowNonDereferencingOps) {
1150 for (
unsigned pos : usePositions)
1155 if (usePositions.size() > 1) {
1157 LLVM_DEBUG(llvm::dbgs()
1158 <<
"multiple dereferencing uses in a single op not supported");
1165 unsigned oldMemRefNumIndices = oldMemRefRank;
1167 auto affMapAccInterface = dyn_cast<AffineMapAccessInterface>(op);
1168 if (affMapAccInterface) {
1173 affMapAccInterface.getAffineMapAttrForMemRef(oldMemRef);
1174 oldMap = cast<AffineMapAttr>(oldMapAttrPair.
getValue()).getValue();
1177 oldMapOperands.assign(startIdx, startIdx + oldMemRefNumIndices);
1182 oldMemRefOperands.reserve(oldMemRefRank);
1183 if (affMapAccInterface &&
1185 for (
auto resultExpr : oldMap.
getResults()) {
1188 auto afOp = AffineApplyOp::create(builder, op->
getLoc(), singleResMap,
1190 oldMemRefOperands.push_back(afOp);
1191 affineApplyOps.push_back(afOp);
1194 oldMemRefOperands.assign(oldMapOperands.begin(), oldMapOperands.end());
1201 remapOperands.reserve(extraOperands.size() + oldMemRefRank +
1202 symbolOperands.size());
1203 remapOperands.append(extraOperands.begin(), extraOperands.end());
1204 remapOperands.append(oldMemRefOperands.begin(), oldMemRefOperands.end());
1205 remapOperands.append(symbolOperands.begin(), symbolOperands.end());
1208 remapOutputs.reserve(oldMemRefRank);
1212 for (
auto resultExpr : indexRemap.
getResults()) {
1215 auto afOp = AffineApplyOp::create(builder, op->
getLoc(), singleResMap,
1217 remapOutputs.push_back(afOp);
1218 affineApplyOps.push_back(afOp);
1222 remapOutputs.assign(remapOperands.begin(), remapOperands.end());
1225 newMapOperands.reserve(newMemRefRank);
1228 for (
Value extraIndex : extraIndices) {
1230 "invalid memory op index");
1231 newMapOperands.push_back(extraIndex);
1235 newMapOperands.append(remapOutputs.begin(), remapOutputs.end());
1238 assert(newMapOperands.size() == newMemRefRank);
1244 for (
Value value : affineApplyOps)
1245 if (value.use_empty())
1246 value.getDefiningOp()->erase();
1250 state.operands.reserve(op->
getNumOperands() + extraIndices.size());
1255 state.operands.push_back(newMemRef);
1258 if (affMapAccInterface) {
1259 state.operands.append(newMapOperands.begin(), newMapOperands.end());
1264 for (
unsigned i = 0; i < newMemRefRank; i++) {
1265 state.operands.push_back(AffineApplyOp::create(
1268 newMap.getResult(i)),
1274 unsigned oldMapNumInputs = oldMapOperands.size();
1275 state.operands.append(op->
operand_begin() + memRefOperandPos + 1 +
1281 state.types.push_back(
result.getType());
1284 auto newMapAttr = AffineMapAttr::get(newMap);
1289 auto *repOp = builder.
create(state);
1290 if (affMapAccInterface) {
1291 StringAttr mapAttrName =
1292 affMapAccInterface.getAffineMapAttrForMemRef(oldMemRef).getName();
1293 repOp->setInherentAttr(mapAttrName, newMapAttr);
1301LogicalResult mlir::affine::replaceAllMemRefUsesWith(
1306 bool allowNonDereferencingOps,
bool replaceInDeallocOp) {
1307 unsigned newMemRefRank = cast<MemRefType>(newMemRef.
getType()).getRank();
1308 (
void)newMemRefRank;
1309 unsigned oldMemRefRank = cast<MemRefType>(oldMemRef.
getType()).getRank();
1310 (
void)oldMemRefRank;
1312 assert(indexRemap.
getNumSymbols() == symbolOperands.size() &&
1313 "symbol operand count mismatch");
1315 extraOperands.size() + oldMemRefRank + symbolOperands.size());
1316 assert(indexRemap.
getNumResults() + extraIndices.size() == newMemRefRank);
1318 assert(oldMemRefRank + extraIndices.size() == newMemRefRank);
1322 assert(cast<MemRefType>(oldMemRef.
getType()).getElementType() ==
1323 cast<MemRefType>(newMemRef.
getType()).getElementType());
1329 for (
auto *user : oldMemRef.
getUsers()) {
1331 if (userFilterFn && !userFilterFn(user))
1337 !replaceInDeallocOp)
1343 if (!isa<AffineMapAccessInterface>(*user)) {
1344 if (!allowNonDereferencingOps) {
1347 <<
"Memref replacement failed: non-deferencing memref user: \n"
1354 LLVM_DEBUG(llvm::dbgs() <<
"Memref replacement failed: use without a "
1355 "memrefs normalizable trait: \n"
1364 opsToReplace.insert(user);
1367 for (
auto *user : opsToReplace) {
1368 if (
failed(replaceAllMemRefUsesWith(
1369 oldMemRef, newMemRef, user, extraIndices, indexRemap, extraOperands,
1370 symbolOperands, allowNonDereferencingOps)))
1405void mlir::affine::createAffineComputationSlice(
1411 if (isa_and_nonnull<AffineApplyOp>(operand.getDefiningOp()))
1412 subOperands.push_back(operand);
1418 if (affineApplyOps.empty())
1423 bool localized =
true;
1424 for (
auto *op : affineApplyOps) {
1426 for (
auto *user :
result.getUsers()) {
1427 if (user != opInst) {
1443 sliceOps->reserve(composedMap.getNumResults());
1444 for (
auto resultExpr : composedMap.getResults()) {
1446 composedMap.getNumSymbols(), resultExpr);
1447 sliceOps->push_back(AffineApplyOp::create(
1448 builder, opInst->
getLoc(), singleResMap, composedOpOperands));
1456 for (
Value &operand : newOperands) {
1459 for (
j = 0, f = subOperands.size();
j < f;
j++) {
1460 if (operand == subOperands[
j])
1463 if (
j < subOperands.size())
1464 operand = (*sliceOps)[
j];
1466 for (
unsigned idx = 0, e = newOperands.size(); idx < e; idx++)
1489 SmallVectorImpl<std::tuple<AffineExpr, unsigned, unsigned>> &tileSizePos) {
1500 if (isa<AffineConstantExpr>(binaryExpr.
getRHS()))
1501 floordivExprs.emplace_back(
1502 std::make_tuple(binaryExpr.
getLHS(), binaryExpr.
getRHS(), pos));
1507 if (floordivExprs.empty()) {
1514 for (std::tuple<AffineExpr, AffineExpr, unsigned> fexpr : floordivExprs) {
1515 AffineExpr floordivExprLHS = std::get<0>(fexpr);
1516 AffineExpr floordivExprRHS = std::get<1>(fexpr);
1517 unsigned floordivPos = std::get<2>(fexpr);
1529 bool notTiled =
false;
1530 if (pos != floordivPos) {
1532 if (e == floordivExprLHS) {
1536 if (floordivExprLHS == binaryExpr.
getLHS() &&
1537 floordivExprRHS == binaryExpr.
getRHS()) {
1541 tileSizePos.emplace_back(
1542 std::make_tuple(binaryExpr.
getRHS(), floordivPos, pos));
1594 if (isa<AffineDimExpr>(e) &&
1595 llvm::any_of(inMemrefTypeDynDims, [&](
unsigned dim) {
1614 binaryExpr = cast<AffineBinaryOpExpr>(oldMapOutput);
1615 newMapOutput = binaryExpr.
getRHS();
1618 binaryExpr = cast<AffineBinaryOpExpr>(oldMapOutput);
1623 newMapOutput = oldMapOutput;
1625 return newMapOutput;
1660template <
typename AllocLikeOp>
1662 MemRefType newMemRefType,
AffineMap map,
1668 unsigned dynIdx = 0;
1669 for (
unsigned d = 0; d < oldMemRefType.getRank(); ++d) {
1670 if (oldMemRefShape[d] < 0) {
1672 inAffineApply.emplace_back(allocOp.getDynamicSizes()[dynIdx]);
1676 auto constantAttr =
b.getIntegerAttr(
b.getIndexType(), oldMemRefShape[d]);
1677 inAffineApply.emplace_back(
1678 arith::ConstantOp::create(
b, allocOp.getLoc(), constantAttr));
1684 unsigned newDimIdx = 0;
1689 if (newMemRefShape[newDimIdx] < 0) {
1692 for (
auto pos : tileSizePos) {
1693 if (newDimIdx == std::get<1>(pos))
1695 else if (newDimIdx == std::get<2>(pos))
1702 AffineApplyOp::create(
b, allocOp.getLoc(), newMap, inAffineApply);
1703 newDynamicSizes.emplace_back(affineApp);
1709template <
typename AllocLikeOp>
1710LogicalResult mlir::affine::normalizeMemRef(AllocLikeOp allocOp) {
1711 MemRefType memrefType = allocOp.getType();
1716 MemRefType newMemRefType = normalizeMemRefType(memrefType);
1717 if (newMemRefType == memrefType)
1722 Value oldMemRef = allocOp.getResult();
1725 AffineMap layoutMap = memrefType.getLayout().getAffineMap();
1726 AllocLikeOp newAlloc;
1731 if (newMemRefType.getNumDynamicDims() > 0 && !tileSizePos.empty()) {
1732 auto oldMemRefType = cast<MemRefType>(oldMemRef.
getType());
1737 newAlloc = AllocLikeOp::create(
b, allocOp.getLoc(), newMemRefType,
1738 newDynamicSizes, allocOp.getAlignmentAttr());
1740 newAlloc = AllocLikeOp::create(
b, allocOp.getLoc(), newMemRefType,
1741 allocOp.getAlignmentAttr());
1744 if (
failed(replaceAllMemRefUsesWith(oldMemRef, newAlloc,
1758 return hasSingleEffect<MemoryEffects::Free>(op, oldMemRef);
1766mlir::affine::normalizeMemRef(memref::ReinterpretCastOp reinterpretCastOp) {
1767 MemRefType memrefType = reinterpretCastOp.getType();
1768 AffineMap oldLayoutMap = memrefType.getLayout().getAffineMap();
1769 Value oldMemRef = reinterpretCastOp.getResult();
1777 MemRefType newMemRefType = normalizeMemRefType(memrefType);
1778 if (newMemRefType == memrefType)
1782 uint64_t newRank = newMemRefType.getRank();
1786 Location loc = reinterpretCastOp.getLoc();
1791 ValueRange oldSizes = reinterpretCastOp.getSizes();
1796 for (
unsigned i = 0, e = memrefType.getRank(); i < e; i++) {
1797 if (memrefType.isDynamicDim(i))
1799 arith::SubIOp::create(
b, loc, oldSizes[0].
getType(), oldSizes[idx++],
1804 for (
unsigned i = 0, e = oldStrides.size(); i < e; i++)
1805 mapOperands[memrefType.getRank() + i] = oldStrides[i];
1809 for (
unsigned i = 0; i < newRank; i++) {
1810 if (!newMemRefType.isDynamicDim(i))
1812 newSizes.push_back(AffineApplyOp::create(
1818 for (
auto &newSize : newSizes) {
1819 newSize = arith::AddIOp::create(
b, loc, newSize.getType(), newSize,
1823 auto newReinterpretCast = memref::ReinterpretCastOp::create(
1824 b, loc, newMemRefType, reinterpretCastOp.getSource(),
1832 if (
failed(replaceAllMemRefUsesWith(oldMemRef,
1841 newReinterpretCast.erase();
1846 reinterpretCastOp.erase();
1850template LogicalResult
1851mlir::affine::normalizeMemRef<memref::AllocaOp>(memref::AllocaOp op);
1852template LogicalResult
1853mlir::affine::normalizeMemRef<memref::AllocOp>(memref::AllocOp op);
1855MemRefType mlir::affine::normalizeMemRefType(MemRefType memrefType) {
1856 unsigned rank = memrefType.getRank();
1860 if (memrefType.getLayout().isIdentity()) {
1865 AffineMap layoutMap = memrefType.getLayout().getAffineMap();
1876 if (memrefType.getNumDynamicDims() > 0 && tileSizePos.empty())
1885 for (
unsigned d = 0; d < rank; ++d) {
1888 fac.addBound(BoundType::LB, d, 0);
1889 fac.addBound(BoundType::UB, d,
shape[d] - 1);
1891 memrefTypeDynDims.emplace_back(d);
1897 if (
failed(fac.composeMatchingMap(layoutMap)))
1901 fac.projectOut(newRank, fac.getNumVars() - newRank - fac.getNumLocalVars());
1904 for (
unsigned d = 0; d < newRank; ++d) {
1907 newShape[d] = ShapedType::kDynamic;
1911 std::optional<int64_t> ubConst = fac.getConstantBound64(BoundType::UB, d);
1916 if (!ubConst.has_value() || *ubConst < 0) {
1917 LLVM_DEBUG(llvm::dbgs()
1918 <<
"can't normalize map due to unknown/invalid upper bound");
1922 newShape[d] = *ubConst + 1;
1926 auto newMemRefType =
1931 return newMemRefType;
1957FailureOr<SmallVector<Value>>
1961 basis = basis.drop_front();
1967 FailureOr<OpFoldResult> nextProd =
1971 basisProd = *nextProd;
1976 results.reserve(divisors.size() + 1);
1977 Value residual = linearIndex;
1978 for (
Value divisor : llvm::reverse(divisors)) {
1979 DivModValue divMod = getDivMod(
b, loc, residual, divisor);
1980 results.push_back(divMod.quotient);
1981 residual = divMod.remainder;
1983 results.push_back(residual);
1987FailureOr<SmallVector<Value>>
1990 bool hasOuterBound) {
1992 basis = basis.drop_front();
1998 FailureOr<OpFoldResult> nextProd =
2002 basisProd = *nextProd;
2007 results.reserve(divisors.size() + 1);
2008 Value residual = linearIndex;
2009 for (
Value divisor : llvm::reverse(divisors)) {
2010 DivModValue divMod = getDivMod(
b, loc, residual, divisor);
2011 results.push_back(divMod.quotient);
2012 residual = divMod.remainder;
2014 results.push_back(residual);
2027 assert(multiIndex.size() == basis.size() ||
2028 multiIndex.size() == basis.size() + 1);
2033 if (multiIndex.size() == basis.size() + 1)
2036 for (
size_t i = 0; i < basis.size(); ++i) {
2042 strides.reserve(stridesAffine.size());
2043 llvm::transform(stridesAffine, std::back_inserter(strides),
2044 [&builder, &basis, loc](
AffineExpr strideExpr) {
2046 builder, loc, strideExpr, basis);
2052 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.
static void cartesianProductDifference(const AffineValueMap &a, const AffineValueMap &b, AffineValueMap *res)
Subtract results of 'b' from results of 'a' over their Cartesian product.
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)
Subtract each result of 'b' from the result of 'a' at the same index; the result counts must be equal...
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.