23 #include "llvm/ADT/STLExtras.h"
24 #include "llvm/ADT/ScopeExit.h"
25 #include "llvm/ADT/SmallBitVector.h"
26 #include "llvm/ADT/SmallVectorExtras.h"
27 #include "llvm/ADT/TypeSwitch.h"
28 #include "llvm/Support/Debug.h"
29 #include "llvm/Support/MathExtras.h"
36 using llvm::divideCeilSigned;
37 using llvm::divideFloorSigned;
40 #define DEBUG_TYPE "affine-ops"
42 #include "mlir/Dialect/Affine/IR/AffineOpsDialect.cpp.inc"
49 if (
auto arg = llvm::dyn_cast<BlockArgument>(value))
50 return arg.getParentRegion() == region;
73 if (llvm::isa<BlockArgument>(value))
74 return legalityCheck(mapping.
lookup(value), dest);
81 bool isDimLikeOp = isa<ShapedDimOpInterface>(value.
getDefiningOp());
92 return llvm::all_of(values, [&](
Value v) {
99 template <
typename OpTy>
102 static_assert(llvm::is_one_of<OpTy, AffineReadOpInterface,
103 AffineWriteOpInterface>::value,
104 "only ops with affine read/write interface are supported");
111 dimOperands, src, dest, mapping,
115 symbolOperands, src, dest, mapping,
132 op.getMapOperands(), src, dest, mapping,
137 op.getMapOperands(), src, dest, mapping,
164 if (!isa<AffineParallelOp, AffineForOp, AffineIfOp>(destOp))
169 if (!llvm::hasSingleElement(*src))
177 if (
auto iface = dyn_cast<MemoryEffectOpInterface>(op)) {
178 if (iface.hasNoEffect())
186 .Case<AffineApplyOp, AffineReadOpInterface,
187 AffineWriteOpInterface>([&](
auto op) {
212 isa<AffineForOp, AffineParallelOp, AffineIfOp>(parentOp);
216 bool shouldAnalyzeRecursively(
Operation *op)
const final {
return true; }
224 void AffineDialect::initialize() {
227 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"
229 addInterfaces<AffineInlinerInterface>();
230 declarePromisedInterfaces<ValueBoundsOpInterface, AffineApplyOp, AffineMaxOp,
239 if (
auto poison = dyn_cast<ub::PoisonAttr>(value))
240 return builder.
create<ub::PoisonOp>(loc, type, poison);
241 return arith::ConstantOp::materialize(builder, value, type, loc);
249 if (
auto arg = llvm::dyn_cast<BlockArgument>(value)) {
265 while (
auto *parentOp = curOp->getParentOp()) {
276 if (!isa<AffineForOp, AffineIfOp, AffineParallelOp>(parentOp))
298 auto *parentOp = llvm::cast<BlockArgument>(value).getOwner()->getParentOp();
300 isa<AffineForOp, AffineParallelOp>(parentOp));
321 auto *parentOp = llvm::cast<BlockArgument>(value).getOwner()->
getParentOp();
322 return isa<AffineForOp, AffineParallelOp>(parentOp);
326 if (
auto applyOp = dyn_cast<AffineApplyOp>(op))
327 return applyOp.isValidDim(region);
330 if (
auto dimOp = dyn_cast<ShapedDimOpInterface>(op))
338 template <
typename AnyMemRefDefOp>
341 MemRefType memRefType = memrefDefOp.getType();
344 if (index >= memRefType.getRank()) {
349 if (!memRefType.isDynamicDim(index))
352 unsigned dynamicDimPos = memRefType.getDynamicDimIndex(index);
353 return isValidSymbol(*(memrefDefOp.getDynamicSizes().begin() + dynamicDimPos),
365 if (llvm::isa<BlockArgument>(dimOp.getShapedValue()))
373 if (!index.has_value())
377 Operation *op = dimOp.getShapedValue().getDefiningOp();
378 while (
auto castOp = dyn_cast<memref::CastOp>(op)) {
380 if (isa<UnrankedMemRefType>(castOp.getSource().getType()))
382 op = castOp.getSource().getDefiningOp();
387 int64_t i = index.value();
389 .Case<memref::ViewOp, memref::SubViewOp, memref::AllocOp>(
391 .Default([](
Operation *) {
return false; });
458 if (
isPure(defOp) && llvm::all_of(defOp->getOperands(), [&](
Value operand) {
459 return affine::isValidSymbol(operand, region);
465 if (
auto dimOp = dyn_cast<ShapedDimOpInterface>(defOp))
489 printer <<
'(' << operands.take_front(numDims) <<
')';
490 if (operands.size() > numDims)
491 printer <<
'[' << operands.drop_front(numDims) <<
']';
501 numDims = opInfos.size();
515 template <
typename OpTy>
520 for (
auto operand : operands) {
521 if (opIt++ < numDims) {
523 return op.emitOpError(
"operand cannot be used as a dimension id");
525 return op.emitOpError(
"operand cannot be used as a symbol");
536 return AffineValueMap(getAffineMap(), getOperands(), getResult());
543 AffineMapAttr mapAttr;
549 auto map = mapAttr.getValue();
551 if (map.getNumDims() != numDims ||
552 numDims + map.getNumSymbols() != result.
operands.size()) {
554 "dimension or symbol index mismatch");
557 result.
types.append(map.getNumResults(), indexTy);
562 p <<
" " << getMapAttr();
564 getAffineMap().getNumDims(), p);
575 "operand count and affine map dimension and symbol count must match");
579 return emitOpError(
"mapping must produce one value");
587 return llvm::all_of(getOperands(),
595 return llvm::all_of(getOperands(),
602 return llvm::all_of(getOperands(),
609 return llvm::all_of(getOperands(), [&](
Value operand) {
615 auto map = getAffineMap();
618 auto expr = map.getResult(0);
619 if (
auto dim = dyn_cast<AffineDimExpr>(expr))
620 return getOperand(dim.getPosition());
621 if (
auto sym = dyn_cast<AffineSymbolExpr>(expr))
622 return getOperand(map.getNumDims() + sym.getPosition());
626 bool hasPoison =
false;
628 map.constantFold(adaptor.getMapOperands(), result, &hasPoison);
631 if (failed(foldResult))
648 auto dimExpr = dyn_cast<AffineDimExpr>(e);
658 Value operand = operands[dimExpr.getPosition()];
659 int64_t operandDivisor = 1;
663 if (forOp.hasConstantLowerBound() && forOp.getConstantLowerBound() == 0) {
664 operandDivisor = forOp.getStepAsInt();
666 uint64_t lbLargestKnownDivisor =
667 forOp.getLowerBoundMap().getLargestKnownDivisorOfMapExprs();
668 operandDivisor = std::gcd(lbLargestKnownDivisor, forOp.getStepAsInt());
671 return operandDivisor;
678 if (
auto constExpr = dyn_cast<AffineConstantExpr>(e)) {
679 int64_t constVal = constExpr.getValue();
680 return constVal >= 0 && constVal < k;
682 auto dimExpr = dyn_cast<AffineDimExpr>(e);
685 Value operand = operands[dimExpr.getPosition()];
689 if (forOp.hasConstantLowerBound() && forOp.getConstantLowerBound() >= 0 &&
690 forOp.hasConstantUpperBound() && forOp.getConstantUpperBound() <= k) {
706 auto bin = dyn_cast<AffineBinaryOpExpr>(e);
714 quotientTimesDiv = llhs;
720 quotientTimesDiv = rlhs;
730 if (forOp && forOp.hasConstantLowerBound())
731 return forOp.getConstantLowerBound();
738 if (!forOp || !forOp.hasConstantUpperBound())
743 if (forOp.hasConstantLowerBound()) {
744 return forOp.getConstantUpperBound() - 1 -
745 (forOp.getConstantUpperBound() - forOp.getConstantLowerBound() - 1) %
746 forOp.getStepAsInt();
748 return forOp.getConstantUpperBound() - 1;
759 constLowerBounds.reserve(operands.size());
760 constUpperBounds.reserve(operands.size());
761 for (
Value operand : operands) {
766 if (
auto constExpr = dyn_cast<AffineConstantExpr>(expr))
767 return constExpr.getValue();
782 constLowerBounds.reserve(operands.size());
783 constUpperBounds.reserve(operands.size());
784 for (
Value operand : operands) {
789 std::optional<int64_t> lowerBound;
790 if (
auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
791 lowerBound = constExpr.getValue();
794 constLowerBounds, constUpperBounds,
805 auto binExpr = dyn_cast<AffineBinaryOpExpr>(expr);
816 binExpr = dyn_cast<AffineBinaryOpExpr>(expr);
824 lhs = binExpr.getLHS();
825 rhs = binExpr.getRHS();
826 auto rhsConst = dyn_cast<AffineConstantExpr>(rhs);
830 int64_t rhsConstVal = rhsConst.getValue();
832 if (rhsConstVal <= 0)
837 std::optional<int64_t> lhsLbConst =
839 std::optional<int64_t> lhsUbConst =
841 if (lhsLbConst && lhsUbConst) {
842 int64_t lhsLbConstVal = *lhsLbConst;
843 int64_t lhsUbConstVal = *lhsUbConst;
847 divideFloorSigned(lhsLbConstVal, rhsConstVal) ==
848 divideFloorSigned(lhsUbConstVal, rhsConstVal)) {
850 divideFloorSigned(lhsLbConstVal, rhsConstVal), context);
856 divideCeilSigned(lhsLbConstVal, rhsConstVal) ==
857 divideCeilSigned(lhsUbConstVal, rhsConstVal)) {
864 lhsLbConstVal < rhsConstVal && lhsUbConstVal < rhsConstVal) {
876 if (
isQTimesDPlusR(lhs, operands, divisor, quotientTimesDiv, rem)) {
877 if (rhsConstVal % divisor == 0 &&
879 expr = quotientTimesDiv.
floorDiv(rhsConst);
880 }
else if (divisor % rhsConstVal == 0 &&
882 expr = rem % rhsConst;
908 if (operands.empty())
914 constLowerBounds.reserve(operands.size());
915 constUpperBounds.reserve(operands.size());
916 for (
Value operand : operands) {
930 if (
auto constExpr = dyn_cast<AffineConstantExpr>(e)) {
931 lowerBounds.push_back(constExpr.getValue());
932 upperBounds.push_back(constExpr.getValue());
934 lowerBounds.push_back(
936 constLowerBounds, constUpperBounds,
938 upperBounds.push_back(
940 constLowerBounds, constUpperBounds,
949 unsigned i = exprEn.index();
951 if (lowerBounds[i] && upperBounds[i] && *lowerBounds[i] == *upperBounds[i])
956 if (!upperBounds[i]) {
957 irredundantExprs.push_back(e);
963 auto otherLowerBound = en.value();
964 unsigned pos = en.index();
965 if (pos == i || !otherLowerBound)
967 if (*otherLowerBound > *upperBounds[i])
969 if (*otherLowerBound < *upperBounds[i])
974 if (upperBounds[pos] && lowerBounds[i] &&
975 lowerBounds[i] == upperBounds[i] &&
976 otherLowerBound == *upperBounds[pos] && i < pos)
980 irredundantExprs.push_back(e);
982 if (!lowerBounds[i]) {
983 irredundantExprs.push_back(e);
988 auto otherUpperBound = en.value();
989 unsigned pos = en.index();
990 if (pos == i || !otherUpperBound)
992 if (*otherUpperBound < *lowerBounds[i])
994 if (*otherUpperBound > *lowerBounds[i])
996 if (lowerBounds[pos] && upperBounds[i] &&
997 lowerBounds[i] == upperBounds[i] &&
998 otherUpperBound == lowerBounds[pos] && i < pos)
1002 irredundantExprs.push_back(e);
1014 static void LLVM_ATTRIBUTE_UNUSED
1016 assert(map.
getNumInputs() == operands.size() &&
"invalid operands for map");
1022 newResults.push_back(expr);
1039 unsigned dimOrSymbolPosition,
1043 bool isDimReplacement = (dimOrSymbolPosition < dims.size());
1044 unsigned pos = isDimReplacement ? dimOrSymbolPosition
1045 : dimOrSymbolPosition - dims.size();
1046 Value &v = isDimReplacement ? dims[pos] : syms[pos];
1059 AffineMap composeMap = affineApply.getAffineMap();
1060 assert(composeMap.
getNumResults() == 1 &&
"affine.apply with >1 results");
1062 affineApply.getMapOperands().end());
1076 dims.append(composeDims.begin(), composeDims.end());
1077 syms.append(composeSyms.begin(), composeSyms.end());
1078 *map = map->
replace(toReplace, replacementExpr, dims.size(), syms.size());
1107 for (
unsigned pos = 0; pos != dims.size() + syms.size(); ++pos)
1119 unsigned nDims = 0, nSyms = 0;
1121 dimReplacements.reserve(dims.size());
1122 symReplacements.reserve(syms.size());
1123 for (
auto *container : {&dims, &syms}) {
1124 bool isDim = (container == &dims);
1125 auto &repls = isDim ? dimReplacements : symReplacements;
1127 Value v = en.value();
1131 "map is function of unexpected expr@pos");
1137 operands->push_back(v);
1150 while (llvm::any_of(*operands, [](
Value v) {
1164 return b.
create<AffineApplyOp>(loc, map, valueOperands);
1186 for (
unsigned i : llvm::seq<unsigned>(0, map.
getNumResults())) {
1193 llvm::append_range(dims,
1195 llvm::append_range(symbols,
1202 operands = llvm::to_vector(llvm::concat<Value>(dims, symbols));
1211 assert(map.
getNumResults() == 1 &&
"building affine.apply with !=1 result");
1221 AffineApplyOp applyOp =
1226 for (
unsigned i = 0, e = constOperands.size(); i != e; ++i)
1231 if (failed(applyOp->fold(constOperands, foldResults)) ||
1232 foldResults.empty()) {
1234 listener->notifyOperationInserted(applyOp, {});
1235 return applyOp.getResult();
1239 return llvm::getSingleElement(foldResults);
1257 return llvm::map_to_vector(llvm::seq<unsigned>(0, map.
getNumResults()),
1259 return makeComposedFoldedAffineApply(
1260 b, loc, map.getSubMap({i}), operands);
1264 template <
typename OpTy>
1276 return makeComposedMinMax<AffineMinOp>(b, loc, map, operands);
1279 template <
typename OpTy>
1291 auto minMaxOp = makeComposedMinMax<OpTy>(newBuilder, loc, map, operands);
1295 for (
unsigned i = 0, e = constOperands.size(); i != e; ++i)
1300 if (failed(minMaxOp->fold(constOperands, foldResults)) ||
1301 foldResults.empty()) {
1303 listener->notifyOperationInserted(minMaxOp, {});
1304 return minMaxOp.getResult();
1308 return llvm::getSingleElement(foldResults);
1315 return makeComposedFoldedMinMax<AffineMinOp>(b, loc, map, operands);
1322 return makeComposedFoldedMinMax<AffineMaxOp>(b, loc, map, operands);
1327 template <
class MapOrSet>
1330 if (!mapOrSet || operands->empty())
1333 assert(mapOrSet->getNumInputs() == operands->size() &&
1334 "map/set inputs must match number of operands");
1336 auto *context = mapOrSet->getContext();
1338 resultOperands.reserve(operands->size());
1340 remappedSymbols.reserve(operands->size());
1341 unsigned nextDim = 0;
1342 unsigned nextSym = 0;
1343 unsigned oldNumSyms = mapOrSet->getNumSymbols();
1345 for (
unsigned i = 0, e = mapOrSet->getNumInputs(); i != e; ++i) {
1346 if (i < mapOrSet->getNumDims()) {
1350 remappedSymbols.push_back((*operands)[i]);
1353 resultOperands.push_back((*operands)[i]);
1356 resultOperands.push_back((*operands)[i]);
1360 resultOperands.append(remappedSymbols.begin(), remappedSymbols.end());
1361 *operands = resultOperands;
1362 *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, {}, nextDim,
1363 oldNumSyms + nextSym);
1365 assert(mapOrSet->getNumInputs() == operands->size() &&
1366 "map/set inputs must match number of operands");
1370 template <
class MapOrSet>
1373 static_assert(llvm::is_one_of<MapOrSet, AffineMap, IntegerSet>::value,
1374 "Argument must be either of AffineMap or IntegerSet type");
1376 if (!mapOrSet || operands->empty())
1379 assert(mapOrSet->getNumInputs() == operands->size() &&
1380 "map/set inputs must match number of operands");
1382 canonicalizePromotedSymbols<MapOrSet>(mapOrSet, operands);
1385 llvm::SmallBitVector usedDims(mapOrSet->getNumDims());
1386 llvm::SmallBitVector usedSyms(mapOrSet->getNumSymbols());
1388 if (
auto dimExpr = dyn_cast<AffineDimExpr>(expr))
1389 usedDims[dimExpr.getPosition()] =
true;
1390 else if (
auto symExpr = dyn_cast<AffineSymbolExpr>(expr))
1391 usedSyms[symExpr.getPosition()] =
true;
1394 auto *context = mapOrSet->getContext();
1397 resultOperands.reserve(operands->size());
1399 llvm::SmallDenseMap<Value, AffineExpr, 8> seenDims;
1401 unsigned nextDim = 0;
1402 for (
unsigned i = 0, e = mapOrSet->getNumDims(); i != e; ++i) {
1405 auto it = seenDims.find((*operands)[i]);
1406 if (it == seenDims.end()) {
1408 resultOperands.push_back((*operands)[i]);
1409 seenDims.insert(std::make_pair((*operands)[i], dimRemapping[i]));
1411 dimRemapping[i] = it->second;
1415 llvm::SmallDenseMap<Value, AffineExpr, 8> seenSymbols;
1417 unsigned nextSym = 0;
1418 for (
unsigned i = 0, e = mapOrSet->getNumSymbols(); i != e; ++i) {
1424 IntegerAttr operandCst;
1425 if (
matchPattern((*operands)[i + mapOrSet->getNumDims()],
1432 auto it = seenSymbols.find((*operands)[i + mapOrSet->getNumDims()]);
1433 if (it == seenSymbols.end()) {
1435 resultOperands.push_back((*operands)[i + mapOrSet->getNumDims()]);
1436 seenSymbols.insert(std::make_pair((*operands)[i + mapOrSet->getNumDims()],
1439 symRemapping[i] = it->second;
1442 *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, symRemapping,
1444 *operands = resultOperands;
1449 canonicalizeMapOrSetAndOperands<AffineMap>(map, operands);
1454 canonicalizeMapOrSetAndOperands<IntegerSet>(set, operands);
1461 template <
typename AffineOpTy>
1470 LogicalResult matchAndRewrite(AffineOpTy affineOp,
1473 llvm::is_one_of<AffineOpTy, AffineLoadOp, AffinePrefetchOp,
1474 AffineStoreOp, AffineApplyOp, AffineMinOp, AffineMaxOp,
1475 AffineVectorStoreOp, AffineVectorLoadOp>::value,
1476 "affine load/store/vectorstore/vectorload/apply/prefetch/min/max op "
1478 auto map = affineOp.getAffineMap();
1480 auto oldOperands = affineOp.getMapOperands();
1485 if (map == oldMap && std::equal(oldOperands.begin(), oldOperands.end(),
1486 resultOperands.begin()))
1489 replaceAffineOp(rewriter, affineOp, map, resultOperands);
1497 void SimplifyAffineOp<AffineLoadOp>::replaceAffineOp(
1504 void SimplifyAffineOp<AffinePrefetchOp>::replaceAffineOp(
1508 prefetch, prefetch.getMemref(), map, mapOperands, prefetch.getIsWrite(),
1509 prefetch.getLocalityHint(), prefetch.getIsDataCache());
1512 void SimplifyAffineOp<AffineStoreOp>::replaceAffineOp(
1516 store, store.getValueToStore(), store.getMemRef(), map, mapOperands);
1519 void SimplifyAffineOp<AffineVectorLoadOp>::replaceAffineOp(
1523 vectorload, vectorload.getVectorType(), vectorload.getMemRef(), map,
1527 void SimplifyAffineOp<AffineVectorStoreOp>::replaceAffineOp(
1531 vectorstore, vectorstore.getValueToStore(), vectorstore.getMemRef(), map,
1536 template <
typename AffineOpTy>
1537 void SimplifyAffineOp<AffineOpTy>::replaceAffineOp(
1546 results.
add<SimplifyAffineOp<AffineApplyOp>>(context);
1577 p <<
" " << getSrcMemRef() <<
'[';
1579 p <<
"], " << getDstMemRef() <<
'[';
1581 p <<
"], " << getTagMemRef() <<
'[';
1585 p <<
", " << getStride();
1586 p <<
", " << getNumElementsPerStride();
1588 p <<
" : " << getSrcMemRefType() <<
", " << getDstMemRefType() <<
", "
1589 << getTagMemRefType();
1601 AffineMapAttr srcMapAttr;
1604 AffineMapAttr dstMapAttr;
1607 AffineMapAttr tagMapAttr;
1622 getSrcMapAttrStrName(),
1626 getDstMapAttrStrName(),
1630 getTagMapAttrStrName(),
1639 if (!strideInfo.empty() && strideInfo.size() != 2) {
1641 "expected two stride related operands");
1643 bool isStrided = strideInfo.size() == 2;
1648 if (types.size() != 3)
1666 if (srcMapOperands.size() != srcMapAttr.getValue().getNumInputs() ||
1667 dstMapOperands.size() != dstMapAttr.getValue().getNumInputs() ||
1668 tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())
1670 "memref operand count not equal to map.numInputs");
1674 LogicalResult AffineDmaStartOp::verifyInvariantsImpl() {
1675 if (!llvm::isa<MemRefType>(getOperand(getSrcMemRefOperandIndex()).
getType()))
1676 return emitOpError(
"expected DMA source to be of memref type");
1677 if (!llvm::isa<MemRefType>(getOperand(getDstMemRefOperandIndex()).
getType()))
1678 return emitOpError(
"expected DMA destination to be of memref type");
1679 if (!llvm::isa<MemRefType>(getOperand(getTagMemRefOperandIndex()).
getType()))
1680 return emitOpError(
"expected DMA tag to be of memref type");
1682 unsigned numInputsAllMaps = getSrcMap().getNumInputs() +
1683 getDstMap().getNumInputs() +
1684 getTagMap().getNumInputs();
1685 if (getNumOperands() != numInputsAllMaps + 3 + 1 &&
1686 getNumOperands() != numInputsAllMaps + 3 + 1 + 2) {
1687 return emitOpError(
"incorrect number of operands");
1691 for (
auto idx : getSrcIndices()) {
1692 if (!idx.getType().isIndex())
1693 return emitOpError(
"src index to dma_start must have 'index' type");
1696 "src index must be a valid dimension or symbol identifier");
1698 for (
auto idx : getDstIndices()) {
1699 if (!idx.getType().isIndex())
1700 return emitOpError(
"dst index to dma_start must have 'index' type");
1703 "dst index must be a valid dimension or symbol identifier");
1705 for (
auto idx : getTagIndices()) {
1706 if (!idx.getType().isIndex())
1707 return emitOpError(
"tag index to dma_start must have 'index' type");
1710 "tag index must be a valid dimension or symbol identifier");
1721 void AffineDmaStartOp::getEffects(
1747 p <<
" " << getTagMemRef() <<
'[';
1752 p <<
" : " << getTagMemRef().getType();
1763 AffineMapAttr tagMapAttr;
1772 getTagMapAttrStrName(),
1781 if (!llvm::isa<MemRefType>(type))
1783 "expected tag to be of memref type");
1785 if (tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())
1787 "tag memref operand count != to map.numInputs");
1791 LogicalResult AffineDmaWaitOp::verifyInvariantsImpl() {
1792 if (!llvm::isa<MemRefType>(getOperand(0).
getType()))
1793 return emitOpError(
"expected DMA tag to be of memref type");
1795 for (
auto idx : getTagIndices()) {
1796 if (!idx.getType().isIndex())
1797 return emitOpError(
"index to dma_wait must have 'index' type");
1800 "index must be a valid dimension or symbol identifier");
1811 void AffineDmaWaitOp::getEffects(
1827 ValueRange iterArgs, BodyBuilderFn bodyBuilder) {
1828 assert(((!lbMap && lbOperands.empty()) ||
1830 "lower bound operand count does not match the affine map");
1831 assert(((!ubMap && ubOperands.empty()) ||
1833 "upper bound operand count does not match the affine map");
1834 assert(step > 0 &&
"step has to be a positive integer constant");
1840 getOperandSegmentSizeAttr(),
1842 static_cast<int32_t>(ubOperands.size()),
1843 static_cast<int32_t>(iterArgs.size())}));
1845 for (
Value val : iterArgs)
1867 Value inductionVar =
1869 for (
Value val : iterArgs)
1870 bodyBlock->
addArgument(val.getType(), val.getLoc());
1875 if (iterArgs.empty() && !bodyBuilder) {
1876 ensureTerminator(*bodyRegion, builder, result.
location);
1877 }
else if (bodyBuilder) {
1880 bodyBuilder(builder, result.
location, inductionVar,
1886 int64_t ub, int64_t step,
ValueRange iterArgs,
1887 BodyBuilderFn bodyBuilder) {
1890 return build(builder, result, {}, lbMap, {}, ubMap, step, iterArgs,
1894 LogicalResult AffineForOp::verifyRegions() {
1897 auto *body = getBody();
1898 if (body->getNumArguments() == 0 || !body->getArgument(0).getType().isIndex())
1899 return emitOpError(
"expected body to have a single index argument for the "
1900 "induction variable");
1904 if (getLowerBoundMap().getNumInputs() > 0)
1906 getLowerBoundMap().getNumDims())))
1909 if (getUpperBoundMap().getNumInputs() > 0)
1911 getUpperBoundMap().getNumDims())))
1913 if (getLowerBoundMap().getNumResults() < 1)
1914 return emitOpError(
"expected lower bound map to have at least one result");
1915 if (getUpperBoundMap().getNumResults() < 1)
1916 return emitOpError(
"expected upper bound map to have at least one result");
1918 unsigned opNumResults = getNumResults();
1919 if (opNumResults == 0)
1925 if (getNumIterOperands() != opNumResults)
1927 "mismatch between the number of loop-carried values and results");
1928 if (getNumRegionIterArgs() != opNumResults)
1930 "mismatch between the number of basic block args and results");
1940 bool failedToParsedMinMax =
1944 auto boundAttrStrName =
1945 isLower ? AffineForOp::getLowerBoundMapAttrName(result.
name)
1946 : AffineForOp::getUpperBoundMapAttrName(result.
name);
1953 if (!boundOpInfos.empty()) {
1955 if (boundOpInfos.size() > 1)
1957 "expected only one loop bound operand");
1982 if (
auto affineMapAttr = llvm::dyn_cast<AffineMapAttr>(boundAttr)) {
1983 unsigned currentNumOperands = result.
operands.size();
1988 auto map = affineMapAttr.getValue();
1992 "dim operand count and affine map dim count must match");
1994 unsigned numDimAndSymbolOperands =
1995 result.
operands.size() - currentNumOperands;
1996 if (numDims + map.
getNumSymbols() != numDimAndSymbolOperands)
1999 "symbol operand count and affine map symbol count must match");
2005 return p.
emitError(attrLoc,
"lower loop bound affine map with "
2006 "multiple results requires 'max' prefix");
2008 return p.
emitError(attrLoc,
"upper loop bound affine map with multiple "
2009 "results requires 'min' prefix");
2015 if (
auto integerAttr = llvm::dyn_cast<IntegerAttr>(boundAttr)) {
2025 "expected valid affine map representation for loop bounds");
2037 int64_t numOperands = result.
operands.size();
2040 int64_t numLbOperands = result.
operands.size() - numOperands;
2043 numOperands = result.
operands.size();
2046 int64_t numUbOperands = result.
operands.size() - numOperands;
2051 getStepAttrName(result.
name),
2055 IntegerAttr stepAttr;
2057 getStepAttrName(result.
name).data(),
2061 if (stepAttr.getValue().isNegative())
2064 "expected step to be representable as a positive signed integer");
2072 regionArgs.push_back(inductionVariable);
2080 for (
auto argOperandType :
2081 llvm::zip(llvm::drop_begin(regionArgs), operands, result.
types)) {
2082 Type type = std::get<2>(argOperandType);
2083 std::get<0>(argOperandType).type = type;
2091 getOperandSegmentSizeAttr(),
2093 static_cast<int32_t>(numUbOperands),
2094 static_cast<int32_t>(operands.size())}));
2098 if (regionArgs.size() != result.
types.size() + 1)
2101 "mismatch between the number of loop-carried values and results");
2105 AffineForOp::ensureTerminator(*body, builder, result.
location);
2127 if (
auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
2128 p << constExpr.getValue();
2136 if (dyn_cast<AffineSymbolExpr>(expr)) {
2152 unsigned AffineForOp::getNumIterOperands() {
2153 AffineMap lbMap = getLowerBoundMapAttr().getValue();
2154 AffineMap ubMap = getUpperBoundMapAttr().getValue();
2159 std::optional<MutableArrayRef<OpOperand>>
2160 AffineForOp::getYieldedValuesMutable() {
2161 return cast<AffineYieldOp>(getBody()->getTerminator()).getOperandsMutable();
2173 if (getStepAsInt() != 1)
2174 p <<
" step " << getStepAsInt();
2176 bool printBlockTerminators =
false;
2177 if (getNumIterOperands() > 0) {
2179 auto regionArgs = getRegionIterArgs();
2180 auto operands = getInits();
2182 llvm::interleaveComma(llvm::zip(regionArgs, operands), p, [&](
auto it) {
2183 p << std::get<0>(it) <<
" = " << std::get<1>(it);
2185 p <<
") -> (" << getResultTypes() <<
")";
2186 printBlockTerminators =
true;
2191 printBlockTerminators);
2193 (*this)->getAttrs(),
2194 {getLowerBoundMapAttrName(getOperation()->getName()),
2195 getUpperBoundMapAttrName(getOperation()->getName()),
2196 getStepAttrName(getOperation()->getName()),
2197 getOperandSegmentSizeAttr()});
2202 auto foldLowerOrUpperBound = [&forOp](
bool lower) {
2206 auto boundOperands =
2207 lower ? forOp.getLowerBoundOperands() : forOp.getUpperBoundOperands();
2208 for (
auto operand : boundOperands) {
2211 operandConstants.push_back(operandCst);
2215 lower ? forOp.getLowerBoundMap() : forOp.getUpperBoundMap();
2217 "bound maps should have at least one result");
2219 if (failed(boundMap.
constantFold(operandConstants, foldedResults)))
2223 assert(!foldedResults.empty() &&
"bounds should have at least one result");
2224 auto maxOrMin = llvm::cast<IntegerAttr>(foldedResults[0]).getValue();
2225 for (
unsigned i = 1, e = foldedResults.size(); i < e; i++) {
2226 auto foldedResult = llvm::cast<IntegerAttr>(foldedResults[i]).getValue();
2227 maxOrMin = lower ? llvm::APIntOps::smax(maxOrMin, foldedResult)
2228 : llvm::APIntOps::smin(maxOrMin, foldedResult);
2230 lower ? forOp.setConstantLowerBound(maxOrMin.getSExtValue())
2231 : forOp.setConstantUpperBound(maxOrMin.getSExtValue());
2236 bool folded =
false;
2237 if (!forOp.hasConstantLowerBound())
2238 folded |= succeeded(foldLowerOrUpperBound(
true));
2241 if (!forOp.hasConstantUpperBound())
2242 folded |= succeeded(foldLowerOrUpperBound(
false));
2243 return success(folded);
2251 auto lbMap = forOp.getLowerBoundMap();
2252 auto ubMap = forOp.getUpperBoundMap();
2253 auto prevLbMap = lbMap;
2254 auto prevUbMap = ubMap;
2267 if (lbMap == prevLbMap && ubMap == prevUbMap)
2270 if (lbMap != prevLbMap)
2271 forOp.setLowerBound(lbOperands, lbMap);
2272 if (ubMap != prevUbMap)
2273 forOp.setUpperBound(ubOperands, ubMap);
2279 static std::optional<uint64_t> getTrivialConstantTripCount(AffineForOp forOp) {
2280 int64_t step = forOp.getStepAsInt();
2281 if (!forOp.hasConstantBounds() || step <= 0)
2282 return std::nullopt;
2283 int64_t lb = forOp.getConstantLowerBound();
2284 int64_t ub = forOp.getConstantUpperBound();
2285 return ub - lb <= 0 ? 0 : (ub - lb + step - 1) / step;
2293 LogicalResult matchAndRewrite(AffineForOp forOp,
2296 if (!llvm::hasSingleElement(*forOp.getBody()))
2298 if (forOp.getNumResults() == 0)
2300 std::optional<uint64_t> tripCount = getTrivialConstantTripCount(forOp);
2301 if (tripCount && *tripCount == 0) {
2304 rewriter.
replaceOp(forOp, forOp.getInits());
2308 auto yieldOp = cast<AffineYieldOp>(forOp.getBody()->getTerminator());
2309 auto iterArgs = forOp.getRegionIterArgs();
2310 bool hasValDefinedOutsideLoop =
false;
2311 bool iterArgsNotInOrder =
false;
2312 for (
unsigned i = 0, e = yieldOp->getNumOperands(); i < e; ++i) {
2313 Value val = yieldOp.getOperand(i);
2314 auto *iterArgIt = llvm::find(iterArgs, val);
2317 if (val == forOp.getInductionVar())
2319 if (iterArgIt == iterArgs.end()) {
2321 assert(forOp.isDefinedOutsideOfLoop(val) &&
2322 "must be defined outside of the loop");
2323 hasValDefinedOutsideLoop =
true;
2324 replacements.push_back(val);
2326 unsigned pos = std::distance(iterArgs.begin(), iterArgIt);
2328 iterArgsNotInOrder =
true;
2329 replacements.push_back(forOp.getInits()[pos]);
2334 if (!tripCount.has_value() &&
2335 (hasValDefinedOutsideLoop || iterArgsNotInOrder))
2339 if (tripCount.has_value() && tripCount.value() >= 2 && iterArgsNotInOrder)
2341 rewriter.
replaceOp(forOp, replacements);
2349 results.
add<AffineForEmptyLoopFolder>(context);
2353 assert((point.
isParent() || point == getRegion()) &&
"invalid region point");
2360 void AffineForOp::getSuccessorRegions(
2362 assert((point.
isParent() || point == getRegion()) &&
"expected loop region");
2367 std::optional<uint64_t> tripCount = getTrivialConstantTripCount(*
this);
2368 if (point.
isParent() && tripCount.has_value()) {
2369 if (tripCount.value() > 0) {
2370 regions.push_back(
RegionSuccessor(&getRegion(), getRegionIterArgs()));
2373 if (tripCount.value() == 0) {
2381 if (!point.
isParent() && tripCount && *tripCount == 1) {
2388 regions.push_back(
RegionSuccessor(&getRegion(), getRegionIterArgs()));
2394 std::optional<uint64_t> tripCount = getTrivialConstantTripCount(op);
2395 return tripCount && *tripCount == 0;
2398 LogicalResult AffineForOp::fold(FoldAdaptor adaptor,
2408 results.assign(getInits().begin(), getInits().end());
2411 return success(folded);
2424 assert(map.
getNumResults() >= 1 &&
"bound map has at least one result");
2425 getLowerBoundOperandsMutable().assign(lbOperands);
2426 setLowerBoundMap(map);
2431 assert(map.
getNumResults() >= 1 &&
"bound map has at least one result");
2432 getUpperBoundOperandsMutable().assign(ubOperands);
2433 setUpperBoundMap(map);
2436 bool AffineForOp::hasConstantLowerBound() {
2437 return getLowerBoundMap().isSingleConstant();
2440 bool AffineForOp::hasConstantUpperBound() {
2441 return getUpperBoundMap().isSingleConstant();
2444 int64_t AffineForOp::getConstantLowerBound() {
2445 return getLowerBoundMap().getSingleConstantResult();
2448 int64_t AffineForOp::getConstantUpperBound() {
2449 return getUpperBoundMap().getSingleConstantResult();
2452 void AffineForOp::setConstantLowerBound(int64_t value) {
2456 void AffineForOp::setConstantUpperBound(int64_t value) {
2460 AffineForOp::operand_range AffineForOp::getControlOperands() {
2465 bool AffineForOp::matchingBoundOperandList() {
2466 auto lbMap = getLowerBoundMap();
2467 auto ubMap = getUpperBoundMap();
2473 for (
unsigned i = 0, e = lbMap.
getNumInputs(); i < e; i++) {
2475 if (getOperand(i) != getOperand(numOperands + i))
2483 std::optional<SmallVector<Value>> AffineForOp::getLoopInductionVars() {
2487 std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopLowerBounds() {
2488 if (!hasConstantLowerBound())
2489 return std::nullopt;
2492 OpFoldResult(b.getI64IntegerAttr(getConstantLowerBound()))};
2495 std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopSteps() {
2501 std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopUpperBounds() {
2502 if (!hasConstantUpperBound())
2506 OpFoldResult(b.getI64IntegerAttr(getConstantUpperBound()))};
2509 FailureOr<LoopLikeOpInterface> AffineForOp::replaceWithAdditionalYields(
2511 bool replaceInitOperandUsesInLoop,
2516 auto inits = llvm::to_vector(getInits());
2517 inits.append(newInitOperands.begin(), newInitOperands.end());
2518 AffineForOp newLoop = rewriter.
create<AffineForOp>(
2523 auto yieldOp = cast<AffineYieldOp>(getBody()->getTerminator());
2525 newLoop.getBody()->getArguments().take_back(newInitOperands.size());
2530 newYieldValuesFn(rewriter, getLoc(), newIterArgs);
2531 assert(newInitOperands.size() == newYieldedValues.size() &&
2532 "expected as many new yield values as new iter operands");
2534 yieldOp.getOperandsMutable().append(newYieldedValues);
2539 rewriter.
mergeBlocks(getBody(), newLoop.getBody(),
2540 newLoop.getBody()->getArguments().take_front(
2541 getBody()->getNumArguments()));
2543 if (replaceInitOperandUsesInLoop) {
2546 for (
auto it : llvm::zip(newInitOperands, newIterArgs)) {
2557 newLoop->getResults().take_front(getNumResults()));
2558 return cast<LoopLikeOpInterface>(newLoop.getOperation());
2586 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
2587 if (!ivArg || !ivArg.getOwner() || !ivArg.getOwner()->getParent())
2588 return AffineForOp();
2590 ivArg.getOwner()->getParent()->getParentOfType<AffineForOp>())
2592 return forOp.getInductionVar() == val ? forOp : AffineForOp();
2593 return AffineForOp();
2597 auto ivArg = llvm::dyn_cast<BlockArgument>(val);
2598 if (!ivArg || !ivArg.getOwner())
2601 auto parallelOp = dyn_cast<AffineParallelOp>(containingOp);
2602 if (parallelOp && llvm::is_contained(parallelOp.getIVs(), val))
2611 ivs->reserve(forInsts.size());
2612 for (
auto forInst : forInsts)
2613 ivs->push_back(forInst.getInductionVar());
2618 ivs.reserve(affineOps.size());
2621 if (
auto forOp = dyn_cast<AffineForOp>(op))
2622 ivs.push_back(forOp.getInductionVar());
2623 else if (
auto parallelOp = dyn_cast<AffineParallelOp>(op))
2624 for (
size_t i = 0; i < parallelOp.getBody()->getNumArguments(); i++)
2625 ivs.push_back(parallelOp.getBody()->getArgument(i));
2631 template <
typename BoundListTy,
typename LoopCreatorTy>
2636 LoopCreatorTy &&loopCreatorFn) {
2637 assert(lbs.size() == ubs.size() &&
"Mismatch in number of arguments");
2638 assert(lbs.size() == steps.size() &&
"Mismatch in number of arguments");
2650 ivs.reserve(lbs.size());
2651 for (
unsigned i = 0, e = lbs.size(); i < e; ++i) {
2657 if (i == e - 1 && bodyBuilderFn) {
2659 bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
2661 nestedBuilder.
create<AffineYieldOp>(nestedLoc);
2666 auto loop = loopCreatorFn(builder, loc, lbs[i], ubs[i], steps[i], loopBody);
2674 int64_t ub, int64_t step,
2675 AffineForOp::BodyBuilderFn bodyBuilderFn) {
2676 return builder.
create<AffineForOp>(loc, lb, ub, step,
2677 std::nullopt, bodyBuilderFn);
2684 AffineForOp::BodyBuilderFn bodyBuilderFn) {
2687 if (lbConst && ubConst)
2689 ubConst.value(), step, bodyBuilderFn);
2692 std::nullopt, bodyBuilderFn);
2720 LogicalResult matchAndRewrite(AffineIfOp ifOp,
2722 if (ifOp.getElseRegion().empty() ||
2723 !llvm::hasSingleElement(*ifOp.getElseBlock()) || ifOp.getNumResults())
2738 LogicalResult matchAndRewrite(AffineIfOp op,
2741 auto isTriviallyFalse = [](
IntegerSet iSet) {
2742 return iSet.isEmptyIntegerSet();
2746 return (iSet.getNumEqualities() == 1 && iSet.getNumInequalities() == 0 &&
2747 iSet.getConstraint(0) == 0);
2750 IntegerSet affineIfConditions = op.getIntegerSet();
2752 if (isTriviallyFalse(affineIfConditions)) {
2756 if (op.getNumResults() == 0 && !op.hasElse()) {
2762 blockToMove = op.getElseBlock();
2763 }
else if (isTriviallyTrue(affineIfConditions)) {
2764 blockToMove = op.getThenBlock();
2782 rewriter.
eraseOp(blockToMoveTerminator);
2790 void AffineIfOp::getSuccessorRegions(
2799 if (getElseRegion().empty()) {
2800 regions.push_back(getResults());
2816 auto conditionAttr =
2817 (*this)->getAttrOfType<IntegerSetAttr>(getConditionAttrStrName());
2819 return emitOpError(
"requires an integer set attribute named 'condition'");
2822 IntegerSet condition = conditionAttr.getValue();
2824 return emitOpError(
"operand count and condition integer set dimension and "
2825 "symbol count must match");
2837 IntegerSetAttr conditionAttr;
2840 AffineIfOp::getConditionAttrStrName(),
2846 auto set = conditionAttr.getValue();
2847 if (set.getNumDims() != numDims)
2850 "dim operand count and integer set dim count must match");
2851 if (numDims + set.getNumSymbols() != result.
operands.size())
2854 "symbol operand count and integer set symbol count must match");
2868 AffineIfOp::ensureTerminator(*thenRegion, parser.
getBuilder(),
2875 AffineIfOp::ensureTerminator(*elseRegion, parser.
getBuilder(),
2887 auto conditionAttr =
2888 (*this)->getAttrOfType<IntegerSetAttr>(getConditionAttrStrName());
2889 p <<
" " << conditionAttr;
2891 conditionAttr.getValue().getNumDims(), p);
2898 auto &elseRegion = this->getElseRegion();
2899 if (!elseRegion.
empty()) {
2908 getConditionAttrStrName());
2913 ->getAttrOfType<IntegerSetAttr>(getConditionAttrStrName())
2917 void AffineIfOp::setIntegerSet(
IntegerSet newSet) {
2923 (*this)->setOperands(operands);
2928 bool withElseRegion) {
2929 assert(resultTypes.empty() || withElseRegion);
2938 if (resultTypes.empty())
2939 AffineIfOp::ensureTerminator(*thenRegion, builder, result.
location);
2942 if (withElseRegion) {
2944 if (resultTypes.empty())
2945 AffineIfOp::ensureTerminator(*elseRegion, builder, result.
location);
2951 AffineIfOp::build(builder, result, {}, set, args,
2966 if (llvm::none_of(operands,
2977 auto set = getIntegerSet();
2983 if (getIntegerSet() == set && llvm::equal(operands, getOperands()))
2986 setConditional(set, operands);
2992 results.
add<SimplifyDeadElse, AlwaysTrueOrFalseIf>(context);
3001 assert(operands.size() == 1 + map.
getNumInputs() &&
"inconsistent operands");
3005 auto memrefType = llvm::cast<MemRefType>(operands[0].
getType());
3006 result.
types.push_back(memrefType.getElementType());
3011 assert(map.
getNumInputs() == mapOperands.size() &&
"inconsistent index info");
3014 auto memrefType = llvm::cast<MemRefType>(memref.
getType());
3016 result.
types.push_back(memrefType.getElementType());
3021 auto memrefType = llvm::cast<MemRefType>(memref.
getType());
3022 int64_t rank = memrefType.getRank();
3027 build(builder, result, memref, map, indices);
3036 AffineMapAttr mapAttr;
3041 AffineLoadOp::getMapAttrStrName(),
3051 p <<
" " << getMemRef() <<
'[';
3052 if (AffineMapAttr mapAttr =
3053 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()))
3057 {getMapAttrStrName()});
3063 template <
typename AffineMemOpTy>
3064 static LogicalResult
3067 MemRefType memrefType,
unsigned numIndexOperands) {
3070 return op->emitOpError(
"affine map num results must equal memref rank");
3072 return op->emitOpError(
"expects as many subscripts as affine map inputs");
3074 for (
auto idx : mapOperands) {
3075 if (!idx.getType().isIndex())
3076 return op->emitOpError(
"index to load must have 'index' type");
3086 if (
getType() != memrefType.getElementType())
3087 return emitOpError(
"result type must match element type of memref");
3090 *
this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()),
3091 getMapOperands(), memrefType,
3092 getNumOperands() - 1)))
3100 results.
add<SimplifyAffineOp<AffineLoadOp>>(context);
3109 auto getGlobalOp = getMemref().getDefiningOp<memref::GetGlobalOp>();
3116 auto global = dyn_cast_or_null<memref::GlobalOp>(
3123 llvm::dyn_cast_or_null<DenseElementsAttr>(global.getConstantInitValue());
3127 if (
auto splatAttr = llvm::dyn_cast<SplatElementsAttr>(cstAttr))
3128 return splatAttr.getSplatValue<
Attribute>();
3130 if (!getAffineMap().isConstant())
3132 auto indices = llvm::to_vector<4>(
3133 llvm::map_range(getAffineMap().getConstantResults(),
3134 [](int64_t v) -> uint64_t {
return v; }));
3135 return cstAttr.getValues<
Attribute>()[indices];
3145 assert(map.
getNumInputs() == mapOperands.size() &&
"inconsistent index info");
3156 auto memrefType = llvm::cast<MemRefType>(memref.
getType());
3157 int64_t rank = memrefType.getRank();
3162 build(builder, result, valueToStore, memref, map, indices);
3171 AffineMapAttr mapAttr;
3176 mapOperands, mapAttr, AffineStoreOp::getMapAttrStrName(),
3187 p <<
" " << getValueToStore();
3188 p <<
", " << getMemRef() <<
'[';
3189 if (AffineMapAttr mapAttr =
3190 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()))
3194 {getMapAttrStrName()});
3201 if (getValueToStore().
getType() != memrefType.getElementType())
3203 "value to store must have the same type as memref element type");
3206 *
this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()),
3207 getMapOperands(), memrefType,
3208 getNumOperands() - 2)))
3216 results.
add<SimplifyAffineOp<AffineStoreOp>>(context);
3219 LogicalResult AffineStoreOp::fold(FoldAdaptor adaptor,
3229 template <
typename T>
3232 if (op.getNumOperands() !=
3233 op.getMap().getNumDims() + op.getMap().getNumSymbols())
3234 return op.emitOpError(
3235 "operand count and affine map dimension and symbol count must match");
3237 if (op.getMap().getNumResults() == 0)
3238 return op.emitOpError(
"affine map expect at least one result");
3242 template <
typename T>
3244 p <<
' ' << op->getAttr(T::getMapAttrStrName());
3245 auto operands = op.getOperands();
3246 unsigned numDims = op.getMap().getNumDims();
3247 p <<
'(' << operands.take_front(numDims) <<
')';
3249 if (operands.size() != numDims)
3250 p <<
'[' << operands.drop_front(numDims) <<
']';
3252 {T::getMapAttrStrName()});
3255 template <
typename T>
3262 AffineMapAttr mapAttr;
3278 template <
typename T>
3280 static_assert(llvm::is_one_of<T, AffineMinOp, AffineMaxOp>::value,
3281 "expected affine min or max op");
3287 auto foldedMap = op.getMap().partialConstantFold(operands, &results);
3289 if (foldedMap.getNumSymbols() == 1 && foldedMap.isSymbolIdentity())
3290 return op.getOperand(0);
3293 if (results.empty()) {
3295 if (foldedMap == op.getMap())
3298 return op.getResult();
3302 auto resultIt = std::is_same<T, AffineMinOp>::value
3303 ? llvm::min_element(results)
3304 : llvm::max_element(results);
3305 if (resultIt == results.end())
3311 template <
typename T>
3317 AffineMap oldMap = affineOp.getAffineMap();
3323 if (!llvm::is_contained(newExprs, expr))
3324 newExprs.push_back(expr);
3354 template <
typename T>
3360 AffineMap oldMap = affineOp.getAffineMap();
3362 affineOp.getMapOperands().take_front(oldMap.
getNumDims());
3364 affineOp.getMapOperands().take_back(oldMap.
getNumSymbols());
3366 auto newDimOperands = llvm::to_vector<8>(dimOperands);
3367 auto newSymOperands = llvm::to_vector<8>(symOperands);
3375 if (
auto symExpr = dyn_cast<AffineSymbolExpr>(expr)) {
3376 Value symValue = symOperands[symExpr.getPosition()];
3378 producerOps.push_back(producerOp);
3381 }
else if (
auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
3382 Value dimValue = dimOperands[dimExpr.getPosition()];
3384 producerOps.push_back(producerOp);
3391 newExprs.push_back(expr);
3394 if (producerOps.empty())
3401 for (T producerOp : producerOps) {
3402 AffineMap producerMap = producerOp.getAffineMap();
3403 unsigned numProducerDims = producerMap.
getNumDims();
3408 producerOp.getMapOperands().take_front(numProducerDims);
3410 producerOp.getMapOperands().take_back(numProducerSyms);
3411 newDimOperands.append(dimValues.begin(), dimValues.end());
3412 newSymOperands.append(symValues.begin(), symValues.end());
3416 newExprs.push_back(expr.shiftDims(numProducerDims, numUsedDims)
3417 .shiftSymbols(numProducerSyms, numUsedSyms));
3420 numUsedDims += numProducerDims;
3421 numUsedSyms += numProducerSyms;
3427 llvm::to_vector<8>(llvm::concat<Value>(newDimOperands, newSymOperands));
3446 if (!resultExpr.isPureAffine())
3451 if (failed(flattenResult))
3464 if (llvm::is_sorted(flattenedExprs))
3469 llvm::to_vector(llvm::seq<unsigned>(0, map.
getNumResults()));
3470 llvm::sort(resultPermutation, [&](
unsigned lhs,
unsigned rhs) {
3471 return flattenedExprs[lhs] < flattenedExprs[rhs];
3474 for (
unsigned idx : resultPermutation)
3495 template <
typename T>
3501 AffineMap map = affineOp.getAffineMap();
3509 template <
typename T>
3515 if (affineOp.getMap().getNumResults() != 1)
3518 affineOp.getOperands());
3546 return parseAffineMinMaxOp<AffineMinOp>(parser, result);
3574 return parseAffineMinMaxOp<AffineMaxOp>(parser, result);
3593 IntegerAttr hintInfo;
3595 StringRef readOrWrite, cacheType;
3597 AffineMapAttr mapAttr;
3601 AffinePrefetchOp::getMapAttrStrName(),
3607 AffinePrefetchOp::getLocalityHintAttrStrName(),
3617 if (readOrWrite !=
"read" && readOrWrite !=
"write")
3619 "rw specifier has to be 'read' or 'write'");
3620 result.
addAttribute(AffinePrefetchOp::getIsWriteAttrStrName(),
3623 if (cacheType !=
"data" && cacheType !=
"instr")
3625 "cache type has to be 'data' or 'instr'");
3627 result.
addAttribute(AffinePrefetchOp::getIsDataCacheAttrStrName(),
3634 p <<
" " << getMemref() <<
'[';
3635 AffineMapAttr mapAttr =
3636 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName());
3639 p <<
']' <<
", " << (getIsWrite() ?
"write" :
"read") <<
", "
3640 <<
"locality<" << getLocalityHint() <<
">, "
3641 << (getIsDataCache() ?
"data" :
"instr");
3643 (*this)->getAttrs(),
3644 {getMapAttrStrName(), getLocalityHintAttrStrName(),
3645 getIsDataCacheAttrStrName(), getIsWriteAttrStrName()});
3650 auto mapAttr = (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName());
3654 return emitOpError(
"affine.prefetch affine map num results must equal"
3657 return emitOpError(
"too few operands");
3659 if (getNumOperands() != 1)
3660 return emitOpError(
"too few operands");
3664 for (
auto idx : getMapOperands()) {
3667 "index must be a valid dimension or symbol identifier");
3675 results.
add<SimplifyAffineOp<AffinePrefetchOp>>(context);
3678 LogicalResult AffinePrefetchOp::fold(FoldAdaptor adaptor,
3693 auto ubs = llvm::to_vector<4>(llvm::map_range(ranges, [&](int64_t value) {
3697 build(builder, result, resultTypes, reductions, lbs, {}, ubs,
3707 assert(llvm::all_of(lbMaps,
3709 return m.getNumDims() == lbMaps[0].getNumDims() &&
3710 m.getNumSymbols() == lbMaps[0].getNumSymbols();
3712 "expected all lower bounds maps to have the same number of dimensions "
3714 assert(llvm::all_of(ubMaps,
3716 return m.getNumDims() == ubMaps[0].getNumDims() &&
3717 m.getNumSymbols() == ubMaps[0].getNumSymbols();
3719 "expected all upper bounds maps to have the same number of dimensions "
3721 assert((lbMaps.empty() || lbMaps[0].getNumInputs() == lbArgs.size()) &&
3722 "expected lower bound maps to have as many inputs as lower bound "
3724 assert((ubMaps.empty() || ubMaps[0].getNumInputs() == ubArgs.size()) &&
3725 "expected upper bound maps to have as many inputs as upper bound "
3733 for (arith::AtomicRMWKind reduction : reductions)
3734 reductionAttrs.push_back(
3746 groups.reserve(groups.size() + maps.size());
3747 exprs.reserve(maps.size());
3749 llvm::append_range(exprs, m.getResults());
3750 groups.push_back(m.getNumResults());
3752 return AffineMap::get(maps[0].getNumDims(), maps[0].getNumSymbols(), exprs,
3758 AffineMap lbMap = concatMapsSameInput(lbMaps, lbGroups);
3759 AffineMap ubMap = concatMapsSameInput(ubMaps, ubGroups);
3777 for (
unsigned i = 0, e = steps.size(); i < e; ++i)
3779 if (resultTypes.empty())
3780 ensureTerminator(*bodyRegion, builder, result.
location);
3784 return {&getRegion()};
3787 unsigned AffineParallelOp::getNumDims() {
return getSteps().size(); }
3789 AffineParallelOp::operand_range AffineParallelOp::getLowerBoundsOperands() {
3790 return getOperands().take_front(getLowerBoundsMap().getNumInputs());
3793 AffineParallelOp::operand_range AffineParallelOp::getUpperBoundsOperands() {
3794 return getOperands().drop_front(getLowerBoundsMap().getNumInputs());
3797 AffineMap AffineParallelOp::getLowerBoundMap(
unsigned pos) {
3798 auto values = getLowerBoundsGroups().getValues<int32_t>();
3800 for (
unsigned i = 0; i < pos; ++i)
3802 return getLowerBoundsMap().getSliceMap(start, values[pos]);
3805 AffineMap AffineParallelOp::getUpperBoundMap(
unsigned pos) {
3806 auto values = getUpperBoundsGroups().getValues<int32_t>();
3808 for (
unsigned i = 0; i < pos; ++i)
3810 return getUpperBoundsMap().getSliceMap(start, values[pos]);
3814 return AffineValueMap(getLowerBoundsMap(), getLowerBoundsOperands());
3818 return AffineValueMap(getUpperBoundsMap(), getUpperBoundsOperands());
3821 std::optional<SmallVector<int64_t, 8>> AffineParallelOp::getConstantRanges() {
3822 if (hasMinMaxBounds())
3823 return std::nullopt;
3828 AffineValueMap::difference(getUpperBoundsValueMap(), getLowerBoundsValueMap(),
3831 for (
unsigned i = 0, e = rangesValueMap.
getNumResults(); i < e; ++i) {
3832 auto expr = rangesValueMap.
getResult(i);
3833 auto cst = dyn_cast<AffineConstantExpr>(expr);
3835 return std::nullopt;
3836 out.push_back(cst.getValue());
3841 Block *AffineParallelOp::getBody() {
return &getRegion().
front(); }
3843 OpBuilder AffineParallelOp::getBodyBuilder() {
3844 return OpBuilder(getBody(), std::prev(getBody()->end()));
3849 "operands to map must match number of inputs");
3851 auto ubOperands = getUpperBoundsOperands();
3854 newOperands.append(ubOperands.begin(), ubOperands.end());
3855 (*this)->setOperands(newOperands);
3862 "operands to map must match number of inputs");
3865 newOperands.append(ubOperands.begin(), ubOperands.end());
3866 (*this)->setOperands(newOperands);
3872 setStepsAttr(getBodyBuilder().getI64ArrayAttr(newSteps));
3877 arith::AtomicRMWKind op) {
3879 case arith::AtomicRMWKind::addf:
3880 return isa<FloatType>(resultType);
3881 case arith::AtomicRMWKind::addi:
3882 return isa<IntegerType>(resultType);
3883 case arith::AtomicRMWKind::assign:
3885 case arith::AtomicRMWKind::mulf:
3886 return isa<FloatType>(resultType);
3887 case arith::AtomicRMWKind::muli:
3888 return isa<IntegerType>(resultType);
3889 case arith::AtomicRMWKind::maximumf:
3890 return isa<FloatType>(resultType);
3891 case arith::AtomicRMWKind::minimumf:
3892 return isa<FloatType>(resultType);
3893 case arith::AtomicRMWKind::maxs: {
3894 auto intType = llvm::dyn_cast<IntegerType>(resultType);
3895 return intType && intType.isSigned();
3897 case arith::AtomicRMWKind::mins: {
3898 auto intType = llvm::dyn_cast<IntegerType>(resultType);
3899 return intType && intType.isSigned();
3901 case arith::AtomicRMWKind::maxu: {
3902 auto intType = llvm::dyn_cast<IntegerType>(resultType);
3903 return intType && intType.isUnsigned();
3905 case arith::AtomicRMWKind::minu: {
3906 auto intType = llvm::dyn_cast<IntegerType>(resultType);
3907 return intType && intType.isUnsigned();
3909 case arith::AtomicRMWKind::ori:
3910 return isa<IntegerType>(resultType);
3911 case arith::AtomicRMWKind::andi:
3912 return isa<IntegerType>(resultType);
3919 auto numDims = getNumDims();
3922 getSteps().size() != numDims || getBody()->getNumArguments() != numDims) {
3923 return emitOpError() <<
"the number of region arguments ("
3924 << getBody()->getNumArguments()
3925 <<
") and the number of map groups for lower ("
3926 << getLowerBoundsGroups().getNumElements()
3927 <<
") and upper bound ("
3928 << getUpperBoundsGroups().getNumElements()
3929 <<
"), and the number of steps (" << getSteps().size()
3930 <<
") must all match";
3933 unsigned expectedNumLBResults = 0;
3934 for (APInt v : getLowerBoundsGroups()) {
3935 unsigned results = v.getZExtValue();
3937 return emitOpError()
3938 <<
"expected lower bound map to have at least one result";
3939 expectedNumLBResults += results;
3941 if (expectedNumLBResults != getLowerBoundsMap().getNumResults())
3942 return emitOpError() <<
"expected lower bounds map to have "
3943 << expectedNumLBResults <<
" results";
3944 unsigned expectedNumUBResults = 0;
3945 for (APInt v : getUpperBoundsGroups()) {
3946 unsigned results = v.getZExtValue();
3948 return emitOpError()
3949 <<
"expected upper bound map to have at least one result";
3950 expectedNumUBResults += results;
3952 if (expectedNumUBResults != getUpperBoundsMap().getNumResults())
3953 return emitOpError() <<
"expected upper bounds map to have "
3954 << expectedNumUBResults <<
" results";
3956 if (getReductions().size() != getNumResults())
3957 return emitOpError(
"a reduction must be specified for each output");
3963 auto intAttr = llvm::dyn_cast<IntegerAttr>(attr);
3964 if (!intAttr || !arith::symbolizeAtomicRMWKind(intAttr.getInt()))
3965 return emitOpError(
"invalid reduction attribute");
3966 auto kind = arith::symbolizeAtomicRMWKind(intAttr.getInt()).value();
3968 return emitOpError(
"result type cannot match reduction attribute");
3974 getLowerBoundsMap().getNumDims())))
3978 getUpperBoundsMap().getNumDims())))
3983 LogicalResult AffineValueMap::canonicalize() {
3985 auto newMap = getAffineMap();
3987 if (newMap == getAffineMap() && newOperands == operands)
3989 reset(newMap, newOperands);
4002 if (!lbCanonicalized && !ubCanonicalized)
4005 if (lbCanonicalized)
4007 if (ubCanonicalized)
4013 LogicalResult AffineParallelOp::fold(FoldAdaptor adaptor,
4025 StringRef keyword) {
4028 ValueRange dimOperands = operands.take_front(numDims);
4029 ValueRange symOperands = operands.drop_front(numDims);
4031 for (llvm::APInt groupSize : group) {
4035 unsigned size = groupSize.getZExtValue();
4040 p << keyword <<
'(';
4050 p <<
" (" << getBody()->getArguments() <<
") = (";
4052 getLowerBoundsOperands(),
"max");
4055 getUpperBoundsOperands(),
"min");
4058 bool elideSteps = llvm::all_of(steps, [](int64_t step) {
return step == 1; });
4061 llvm::interleaveComma(steps, p);
4064 if (getNumResults()) {
4066 llvm::interleaveComma(getReductions(), p, [&](
auto &attr) {
4067 arith::AtomicRMWKind sym = *arith::symbolizeAtomicRMWKind(
4068 llvm::cast<IntegerAttr>(attr).getInt());
4069 p <<
"\"" << arith::stringifyAtomicRMWKind(sym) <<
"\"";
4071 p <<
") -> (" << getResultTypes() <<
")";
4078 (*this)->getAttrs(),
4079 {AffineParallelOp::getReductionsAttrStrName(),
4080 AffineParallelOp::getLowerBoundsMapAttrStrName(),
4081 AffineParallelOp::getLowerBoundsGroupsAttrStrName(),
4082 AffineParallelOp::getUpperBoundsMapAttrStrName(),
4083 AffineParallelOp::getUpperBoundsGroupsAttrStrName(),
4084 AffineParallelOp::getStepsAttrStrName()});
4097 "expected operands to be dim or symbol expression");
4100 for (
const auto &list : operands) {
4104 for (
Value operand : valueOperands) {
4105 unsigned pos = std::distance(uniqueOperands.begin(),
4106 llvm::find(uniqueOperands, operand));
4107 if (pos == uniqueOperands.size())
4108 uniqueOperands.push_back(operand);
4109 replacements.push_back(
4119 enum class MinMaxKind { Min, Max };
4143 const llvm::StringLiteral tmpAttrStrName =
"__pseudo_bound_map";
4145 StringRef mapName =
kind == MinMaxKind::Min
4146 ? AffineParallelOp::getUpperBoundsMapAttrStrName()
4147 : AffineParallelOp::getLowerBoundsMapAttrStrName();
4148 StringRef groupsName =
4149 kind == MinMaxKind::Min
4150 ? AffineParallelOp::getUpperBoundsGroupsAttrStrName()
4151 : AffineParallelOp::getLowerBoundsGroupsAttrStrName();
4168 auto parseOperands = [&]() {
4170 kind == MinMaxKind::Min ?
"min" :
"max"))) {
4171 mapOperands.clear();
4178 llvm::append_range(flatExprs, map.getValue().getResults());
4180 auto dimsRef = operandsRef.take_front(map.getValue().getNumDims());
4182 auto symsRef = operandsRef.drop_front(map.getValue().getNumDims());
4184 flatDimOperands.append(map.getValue().getNumResults(), dims);
4185 flatSymOperands.append(map.getValue().getNumResults(), syms);
4186 numMapsPerGroup.push_back(map.getValue().getNumResults());
4189 flatSymOperands.emplace_back(),
4190 flatExprs.emplace_back())))
4192 numMapsPerGroup.push_back(1);
4199 unsigned totalNumDims = 0;
4200 unsigned totalNumSyms = 0;
4201 for (
unsigned i = 0, e = flatExprs.size(); i < e; ++i) {
4202 unsigned numDims = flatDimOperands[i].size();
4203 unsigned numSyms = flatSymOperands[i].size();
4204 flatExprs[i] = flatExprs[i]
4205 .shiftDims(numDims, totalNumDims)
4206 .shiftSymbols(numSyms, totalNumSyms);
4207 totalNumDims += numDims;
4208 totalNumSyms += numSyms;
4220 result.
operands.append(dimOperands.begin(), dimOperands.end());
4221 result.
operands.append(symOperands.begin(), symOperands.end());
4224 auto flatMap =
AffineMap::get(totalNumDims, totalNumSyms, flatExprs,
4226 flatMap = flatMap.replaceDimsAndSymbols(
4227 dimRplacements, symRepacements, dimOperands.size(), symOperands.size());
4251 AffineMapAttr stepsMapAttr;
4256 result.
addAttribute(AffineParallelOp::getStepsAttrStrName(),
4260 AffineParallelOp::getStepsAttrStrName(),
4267 auto stepsMap = stepsMapAttr.getValue();
4268 for (
const auto &result : stepsMap.getResults()) {
4269 auto constExpr = dyn_cast<AffineConstantExpr>(result);
4272 "steps must be constant integers");
4273 steps.push_back(constExpr.getValue());
4275 result.
addAttribute(AffineParallelOp::getStepsAttrStrName(),
4285 auto parseAttributes = [&]() -> ParseResult {
4295 std::optional<arith::AtomicRMWKind> reduction =
4296 arith::symbolizeAtomicRMWKind(attrVal.getValue());
4298 return parser.
emitError(loc,
"invalid reduction value: ") << attrVal;
4299 reductions.push_back(
4307 result.
addAttribute(AffineParallelOp::getReductionsAttrStrName(),
4316 for (
auto &iv : ivs)
4317 iv.type = indexType;
4323 AffineParallelOp::ensureTerminator(*body, builder, result.
location);
4332 auto *parentOp = (*this)->getParentOp();
4333 auto results = parentOp->getResults();
4334 auto operands = getOperands();
4336 if (!isa<AffineParallelOp, AffineIfOp, AffineForOp>(parentOp))
4337 return emitOpError() <<
"only terminates affine.if/for/parallel regions";
4338 if (parentOp->getNumResults() != getNumOperands())
4339 return emitOpError() <<
"parent of yield must have same number of "
4340 "results as the yield operands";
4341 for (
auto it : llvm::zip(results, operands)) {
4343 return emitOpError() <<
"types mismatch between yield op and its parent";
4356 assert(operands.size() == 1 + map.
getNumInputs() &&
"inconsistent operands");
4360 result.
types.push_back(resultType);
4364 VectorType resultType,
Value memref,
4366 assert(map.
getNumInputs() == mapOperands.size() &&
"inconsistent index info");
4370 result.
types.push_back(resultType);
4374 VectorType resultType,
Value memref,
4376 auto memrefType = llvm::cast<MemRefType>(memref.
getType());
4377 int64_t rank = memrefType.getRank();
4382 build(builder, result, resultType, memref, map, indices);
4385 void AffineVectorLoadOp::getCanonicalizationPatterns(
RewritePatternSet &results,
4387 results.
add<SimplifyAffineOp<AffineVectorLoadOp>>(context);
4395 MemRefType memrefType;
4396 VectorType resultType;
4398 AffineMapAttr mapAttr;
4403 AffineVectorLoadOp::getMapAttrStrName(),
4414 p <<
" " << getMemRef() <<
'[';
4415 if (AffineMapAttr mapAttr =
4416 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()))
4420 {getMapAttrStrName()});
4426 VectorType vectorType) {
4428 if (memrefType.getElementType() != vectorType.getElementType())
4430 "requires memref and vector types of the same elemental type");
4437 *
this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()),
4438 getMapOperands(), memrefType,
4439 getNumOperands() - 1)))
4455 assert(map.
getNumInputs() == mapOperands.size() &&
"inconsistent index info");
4466 auto memrefType = llvm::cast<MemRefType>(memref.
getType());
4467 int64_t rank = memrefType.getRank();
4472 build(builder, result, valueToStore, memref, map, indices);
4474 void AffineVectorStoreOp::getCanonicalizationPatterns(
4476 results.
add<SimplifyAffineOp<AffineVectorStoreOp>>(context);
4483 MemRefType memrefType;
4484 VectorType resultType;
4487 AffineMapAttr mapAttr;
4493 AffineVectorStoreOp::getMapAttrStrName(),
4504 p <<
" " << getValueToStore();
4505 p <<
", " << getMemRef() <<
'[';
4506 if (AffineMapAttr mapAttr =
4507 (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()))
4511 {getMapAttrStrName()});
4512 p <<
" : " <<
getMemRefType() <<
", " << getValueToStore().getType();
4518 *
this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()),
4519 getMapOperands(), memrefType,
4520 getNumOperands() - 2)))
4533 void AffineDelinearizeIndexOp::build(
OpBuilder &odsBuilder,
4537 bool hasOuterBound) {
4539 : staticBasis.size() + 1,
4541 build(odsBuilder, odsState, returnTypes, linearIndex, dynamicBasis,
4545 void AffineDelinearizeIndexOp::build(
OpBuilder &odsBuilder,
4548 bool hasOuterBound) {
4549 if (hasOuterBound && !basis.empty() && basis.front() ==
nullptr) {
4550 hasOuterBound =
false;
4551 basis = basis.drop_front();
4557 build(odsBuilder, odsState, linearIndex, dynamicBasis, staticBasis,
4561 void AffineDelinearizeIndexOp::build(
OpBuilder &odsBuilder,
4565 bool hasOuterBound) {
4566 if (hasOuterBound && !basis.empty() && basis.front() ==
OpFoldResult()) {
4567 hasOuterBound =
false;
4568 basis = basis.drop_front();
4573 build(odsBuilder, odsState, linearIndex, dynamicBasis, staticBasis,
4577 void AffineDelinearizeIndexOp::build(
OpBuilder &odsBuilder,
4580 bool hasOuterBound) {
4581 build(odsBuilder, odsState, linearIndex,
ValueRange{}, basis, hasOuterBound);
4586 if (getNumResults() != staticBasis.size() &&
4587 getNumResults() != staticBasis.size() + 1)
4588 return emitOpError(
"should return an index for each basis element and up "
4589 "to one extra index");
4591 auto dynamicMarkersCount = llvm::count_if(staticBasis, ShapedType::isDynamic);
4592 if (
static_cast<size_t>(dynamicMarkersCount) != getDynamicBasis().size())
4594 "mismatch between dynamic and static basis (kDynamic marker but no "
4595 "corresponding dynamic basis entry) -- this can only happen due to an "
4596 "incorrect fold/rewrite");
4598 if (!llvm::all_of(staticBasis, [](int64_t v) {
4599 return v > 0 || ShapedType::isDynamic(v);
4601 return emitOpError(
"no basis element may be statically non-positive");
4610 static std::optional<SmallVector<int64_t>>
4614 uint64_t dynamicBasisIndex = 0;
4617 mutableDynamicBasis.
erase(dynamicBasisIndex);
4619 ++dynamicBasisIndex;
4624 if (dynamicBasisIndex == dynamicBasis.size())
4625 return std::nullopt;
4631 staticBasis.push_back(ShapedType::kDynamic);
4633 staticBasis.push_back(*basisVal);
4640 AffineDelinearizeIndexOp::fold(FoldAdaptor adaptor,
4642 std::optional<SmallVector<int64_t>> maybeStaticBasis =
4644 adaptor.getDynamicBasis());
4645 if (maybeStaticBasis) {
4646 setStaticBasis(*maybeStaticBasis);
4651 if (getNumResults() == 1) {
4652 result.push_back(getLinearIndex());
4656 if (adaptor.getLinearIndex() ==
nullptr)
4659 if (!adaptor.getDynamicBasis().empty())
4662 int64_t highPart = cast<IntegerAttr>(adaptor.getLinearIndex()).getInt();
4663 Type attrType = getLinearIndex().getType();
4666 if (hasOuterBound())
4667 staticBasis = staticBasis.drop_front();
4668 for (int64_t modulus : llvm::reverse(staticBasis)) {
4669 result.push_back(
IntegerAttr::get(attrType, llvm::mod(highPart, modulus)));
4670 highPart = llvm::divideFloorSigned(highPart, modulus);
4673 std::reverse(result.begin(), result.end());
4679 if (hasOuterBound()) {
4680 if (getStaticBasis().front() == ::mlir::ShapedType::kDynamic)
4682 getDynamicBasis().drop_front(), builder);
4684 return getMixedValues(getStaticBasis().drop_front(), getDynamicBasis(),
4688 return getMixedValues(getStaticBasis(), getDynamicBasis(), builder);
4693 if (!hasOuterBound())
4701 struct DropUnitExtentBasis
4705 LogicalResult matchAndRewrite(affine::AffineDelinearizeIndexOp delinearizeOp,
4708 std::optional<Value> zero = std::nullopt;
4709 Location loc = delinearizeOp->getLoc();
4712 zero = rewriter.
create<arith::ConstantIndexOp>(loc, 0);
4713 return zero.value();
4719 for (
auto [index, basis] :
4721 std::optional<int64_t> basisVal =
4723 if (basisVal && *basisVal == 1)
4724 replacements[index] =
getZero();
4726 newBasis.push_back(basis);
4729 if (newBasis.size() == delinearizeOp.getNumResults())
4731 "no unit basis elements");
4733 if (!newBasis.empty()) {
4735 auto newDelinearizeOp = rewriter.
create<affine::AffineDelinearizeIndexOp>(
4736 loc, delinearizeOp.getLinearIndex(), newBasis);
4739 for (
auto &replacement : replacements) {
4742 replacement = newDelinearizeOp->
getResult(newIndex++);
4746 rewriter.
replaceOp(delinearizeOp, replacements);
4761 struct CancelDelinearizeOfLinearizeDisjointExactTail
4765 LogicalResult matchAndRewrite(affine::AffineDelinearizeIndexOp delinearizeOp,
4767 auto linearizeOp = delinearizeOp.getLinearIndex()
4768 .getDefiningOp<affine::AffineLinearizeIndexOp>();
4771 "index doesn't come from linearize");
4773 if (!linearizeOp.getDisjoint())
4776 ValueRange linearizeIns = linearizeOp.getMultiIndex();
4780 size_t numMatches = 0;
4781 for (
auto [linSize, delinSize] : llvm::zip(
4782 llvm::reverse(linearizeBasis), llvm::reverse(delinearizeBasis))) {
4783 if (linSize != delinSize)
4788 if (numMatches == 0)
4790 delinearizeOp,
"final basis element doesn't match linearize");
4793 if (numMatches == linearizeBasis.size() &&
4794 numMatches == delinearizeBasis.size() &&
4795 linearizeIns.size() == delinearizeOp.getNumResults()) {
4796 rewriter.
replaceOp(delinearizeOp, linearizeOp.getMultiIndex());
4800 Value newLinearize = rewriter.
create<affine::AffineLinearizeIndexOp>(
4801 linearizeOp.getLoc(), linearizeIns.drop_back(numMatches),
4803 linearizeOp.getDisjoint());
4804 auto newDelinearize = rewriter.
create<affine::AffineDelinearizeIndexOp>(
4805 delinearizeOp.getLoc(), newLinearize,
4807 delinearizeOp.hasOuterBound());
4809 mergedResults.append(linearizeIns.take_back(numMatches).begin(),
4810 linearizeIns.take_back(numMatches).end());
4811 rewriter.
replaceOp(delinearizeOp, mergedResults);
4829 struct SplitDelinearizeSpanningLastLinearizeArg final
4833 LogicalResult matchAndRewrite(affine::AffineDelinearizeIndexOp delinearizeOp,
4835 auto linearizeOp = delinearizeOp.getLinearIndex()
4836 .getDefiningOp<affine::AffineLinearizeIndexOp>();
4839 "index doesn't come from linearize");
4841 if (!linearizeOp.getDisjoint())
4843 "linearize isn't disjoint");
4845 int64_t target = linearizeOp.getStaticBasis().back();
4846 if (ShapedType::isDynamic(target))
4848 linearizeOp,
"linearize ends with dynamic basis value");
4850 int64_t sizeToSplit = 1;
4851 size_t elemsToSplit = 0;
4853 for (int64_t basisElem : llvm::reverse(basis)) {
4854 if (ShapedType::isDynamic(basisElem))
4856 delinearizeOp,
"dynamic basis element while scanning for split");
4857 sizeToSplit *= basisElem;
4860 if (sizeToSplit > target)
4862 "overshot last argument size");
4863 if (sizeToSplit == target)
4867 if (sizeToSplit < target)
4869 delinearizeOp,
"product of known basis elements doesn't exceed last "
4870 "linearize argument");
4872 if (elemsToSplit < 2)
4875 "need at least two elements to form the basis product");
4877 Value linearizeWithoutBack =
4878 rewriter.
create<affine::AffineLinearizeIndexOp>(
4879 linearizeOp.getLoc(), linearizeOp.getMultiIndex().drop_back(),
4880 linearizeOp.getDynamicBasis(),
4881 linearizeOp.getStaticBasis().drop_back(),
4882 linearizeOp.getDisjoint());
4883 auto delinearizeWithoutSplitPart =
4884 rewriter.
create<affine::AffineDelinearizeIndexOp>(
4885 delinearizeOp.getLoc(), linearizeWithoutBack,
4886 delinearizeOp.getDynamicBasis(), basis.drop_back(elemsToSplit),
4887 delinearizeOp.hasOuterBound());
4888 auto delinearizeBack = rewriter.
create<affine::AffineDelinearizeIndexOp>(
4889 delinearizeOp.getLoc(), linearizeOp.getMultiIndex().back(),
4890 basis.take_back(elemsToSplit),
true);
4892 llvm::concat<Value>(delinearizeWithoutSplitPart.getResults(),
4893 delinearizeBack.getResults()));
4894 rewriter.
replaceOp(delinearizeOp, results);
4901 void affine::AffineDelinearizeIndexOp::getCanonicalizationPatterns(
4904 .insert<CancelDelinearizeOfLinearizeDisjointExactTail,
4905 DropUnitExtentBasis, SplitDelinearizeSpanningLastLinearizeArg>(
4913 void AffineLinearizeIndexOp::build(
OpBuilder &odsBuilder,
4917 if (!basis.empty() && basis.front() ==
Value())
4918 basis = basis.drop_front();
4923 build(odsBuilder, odsState, multiIndex, dynamicBasis, staticBasis, disjoint);
4926 void AffineLinearizeIndexOp::build(
OpBuilder &odsBuilder,
4932 basis = basis.drop_front();
4936 build(odsBuilder, odsState, multiIndex, dynamicBasis, staticBasis, disjoint);
4939 void AffineLinearizeIndexOp::build(
OpBuilder &odsBuilder,
4943 build(odsBuilder, odsState, multiIndex,
ValueRange{}, basis, disjoint);
4947 size_t numIndexes = getMultiIndex().size();
4948 size_t numBasisElems = getStaticBasis().size();
4949 if (numIndexes != numBasisElems && numIndexes != numBasisElems + 1)
4950 return emitOpError(
"should be passed a basis element for each index except "
4951 "possibly the first");
4953 auto dynamicMarkersCount =
4954 llvm::count_if(getStaticBasis(), ShapedType::isDynamic);
4955 if (
static_cast<size_t>(dynamicMarkersCount) != getDynamicBasis().size())
4957 "mismatch between dynamic and static basis (kDynamic marker but no "
4958 "corresponding dynamic basis entry) -- this can only happen due to an "
4959 "incorrect fold/rewrite");
4964 OpFoldResult AffineLinearizeIndexOp::fold(FoldAdaptor adaptor) {
4965 std::optional<SmallVector<int64_t>> maybeStaticBasis =
4967 adaptor.getDynamicBasis());
4968 if (maybeStaticBasis) {
4969 setStaticBasis(*maybeStaticBasis);
4973 if (getMultiIndex().empty())
4977 if (getMultiIndex().size() == 1)
4978 return getMultiIndex().front();
4980 if (llvm::any_of(adaptor.getMultiIndex(),
4981 [](
Attribute a) { return a == nullptr; }))
4984 if (!adaptor.getDynamicBasis().empty())
4989 for (
auto [length, indexAttr] :
4990 llvm::zip_first(llvm::reverse(getStaticBasis()),
4991 llvm::reverse(adaptor.getMultiIndex()))) {
4992 result = result + cast<IntegerAttr>(indexAttr).getInt() * stride;
4993 stride = stride * length;
4996 if (!hasOuterBound())
4999 cast<IntegerAttr>(adaptor.getMultiIndex().front()).getInt() * stride;
5006 if (hasOuterBound()) {
5007 if (getStaticBasis().front() == ::mlir::ShapedType::kDynamic)
5009 getDynamicBasis().drop_front(), builder);
5011 return getMixedValues(getStaticBasis().drop_front(), getDynamicBasis(),
5015 return getMixedValues(getStaticBasis(), getDynamicBasis(), builder);
5020 if (!hasOuterBound())
5036 struct DropLinearizeUnitComponentsIfDisjointOrZero final
5040 LogicalResult matchAndRewrite(affine::AffineLinearizeIndexOp op,
5043 size_t numIndices = multiIndex.size();
5045 newIndices.reserve(numIndices);
5047 newBasis.reserve(numIndices);
5049 if (!op.hasOuterBound()) {
5050 newIndices.push_back(multiIndex.front());
5051 multiIndex = multiIndex.drop_front();
5055 for (
auto [index, basisElem] : llvm::zip_equal(multiIndex, basis)) {
5057 if (!basisEntry || *basisEntry != 1) {
5058 newIndices.push_back(index);
5059 newBasis.push_back(basisElem);
5064 if (!op.getDisjoint() && (!indexValue || *indexValue != 0)) {
5065 newIndices.push_back(index);
5066 newBasis.push_back(basisElem);
5070 if (newIndices.size() == numIndices)
5072 "no unit basis entries to replace");
5074 if (newIndices.size() == 0) {
5079 op, newIndices, newBasis, op.getDisjoint());
5088 int64_t nDynamic = 0;
5098 dynamicPart.push_back(cast<Value>(term));
5102 if (
auto constant = dyn_cast<AffineConstantExpr>(result))
5104 return builder.
create<AffineApplyOp>(loc, result, dynamicPart).getResult();
5134 struct CancelLinearizeOfDelinearizePortion final
5145 unsigned linStart = 0;
5146 unsigned delinStart = 0;
5147 unsigned length = 0;
5151 LogicalResult matchAndRewrite(affine::AffineLinearizeIndexOp linearizeOp,
5158 ValueRange multiIndex = linearizeOp.getMultiIndex();
5159 unsigned numLinArgs = multiIndex.size();
5160 unsigned linArgIdx = 0;
5164 while (linArgIdx < numLinArgs) {
5165 auto asResult = dyn_cast<OpResult>(multiIndex[linArgIdx]);
5171 auto delinearizeOp =
5172 dyn_cast<AffineDelinearizeIndexOp>(asResult.getOwner());
5173 if (!delinearizeOp) {
5190 unsigned delinArgIdx = asResult.getResultNumber();
5192 OpFoldResult firstDelinBound = delinBasis[delinArgIdx];
5194 bool boundsMatch = firstDelinBound == firstLinBound;
5195 bool bothAtFront = linArgIdx == 0 && delinArgIdx == 0;
5196 bool knownByDisjoint =
5197 linearizeOp.getDisjoint() && delinArgIdx == 0 && !firstDelinBound;
5198 if (!boundsMatch && !bothAtFront && !knownByDisjoint) {
5204 unsigned numDelinOuts = delinearizeOp.getNumResults();
5205 for (;
j + linArgIdx < numLinArgs &&
j + delinArgIdx < numDelinOuts;
5207 if (multiIndex[linArgIdx +
j] !=
5208 delinearizeOp.getResult(delinArgIdx +
j))
5210 if (linBasis[linArgIdx +
j] != delinBasis[delinArgIdx +
j])
5216 if (
j <= 1 || !alreadyMatchedDelinearize.insert(delinearizeOp).second) {
5220 matches.push_back(Match{delinearizeOp, linArgIdx, delinArgIdx,
j});
5224 if (matches.empty())
5226 linearizeOp,
"no run of delinearize outputs to deal with");
5234 newIndex.reserve(numLinArgs);
5236 newBasis.reserve(numLinArgs);
5237 unsigned prevMatchEnd = 0;
5238 for (Match m : matches) {
5239 unsigned gap = m.linStart - prevMatchEnd;
5240 llvm::append_range(newIndex, multiIndex.slice(prevMatchEnd, gap));
5241 llvm::append_range(newBasis, linBasisRef.slice(prevMatchEnd, gap));
5243 prevMatchEnd = m.linStart + m.length;
5245 PatternRewriter::InsertionGuard g(rewriter);
5249 linBasisRef.slice(m.linStart, m.length);
5256 if (m.length == m.delinearize.getNumResults()) {
5257 newIndex.push_back(m.delinearize.getLinearIndex());
5258 newBasis.push_back(newSize);
5266 newDelinBasis.erase(newDelinBasis.begin() + m.delinStart,
5267 newDelinBasis.begin() + m.delinStart + m.length);
5268 newDelinBasis.insert(newDelinBasis.begin() + m.delinStart, newSize);
5269 auto newDelinearize = rewriter.
create<AffineDelinearizeIndexOp>(
5270 m.delinearize.getLoc(), m.delinearize.getLinearIndex(),
5276 Value combinedElem = newDelinearize.getResult(m.delinStart);
5277 auto residualDelinearize = rewriter.
create<AffineDelinearizeIndexOp>(
5278 m.delinearize.getLoc(), combinedElem, basisToMerge);
5283 llvm::append_range(newDelinResults,
5284 newDelinearize.getResults().take_front(m.delinStart));
5285 llvm::append_range(newDelinResults, residualDelinearize.getResults());
5288 newDelinearize.getResults().drop_front(m.delinStart + 1));
5290 delinearizeReplacements.push_back(newDelinResults);
5291 newIndex.push_back(combinedElem);
5292 newBasis.push_back(newSize);
5294 llvm::append_range(newIndex, multiIndex.drop_front(prevMatchEnd));
5295 llvm::append_range(newBasis, linBasisRef.drop_front(prevMatchEnd));
5297 linearizeOp, newIndex, newBasis, linearizeOp.getDisjoint());
5299 for (
auto [m, newResults] :
5300 llvm::zip_equal(matches, delinearizeReplacements)) {
5301 if (newResults.empty())
5303 rewriter.
replaceOp(m.delinearize, newResults);
5314 struct DropLinearizeLeadingZero final
5318 LogicalResult matchAndRewrite(affine::AffineLinearizeIndexOp op,
5320 Value leadingIdx = op.getMultiIndex().front();
5324 if (op.getMultiIndex().size() == 1) {
5331 if (op.hasOuterBound())
5332 newMixedBasis = newMixedBasis.drop_front();
5335 op, op.getMultiIndex().drop_front(), newMixedBasis, op.getDisjoint());
5341 void affine::AffineLinearizeIndexOp::getCanonicalizationPatterns(
5343 patterns.add<CancelLinearizeOfDelinearizePortion, DropLinearizeLeadingZero,
5344 DropLinearizeUnitComponentsIfDisjointOrZero>(context);
5351 #define GET_OP_CLASSES
5352 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"
static AffineForOp buildAffineLoopFromConstants(OpBuilder &builder, Location loc, int64_t lb, int64_t ub, int64_t step, AffineForOp::BodyBuilderFn bodyBuilderFn)
Creates an affine loop from the bounds known to be constants.
static bool hasTrivialZeroTripCount(AffineForOp op)
Returns true if the affine.for has zero iterations in trivial cases.
static void composeMultiResultAffineMap(AffineMap &map, SmallVectorImpl< Value > &operands)
Composes the given affine map with the given list of operands, pulling in the maps from any affine....
static LogicalResult verifyMemoryOpIndexing(AffineMemOpTy op, AffineMapAttr mapAttr, Operation::operand_range mapOperands, MemRefType memrefType, unsigned numIndexOperands)
Verify common indexing invariants of affine.load, affine.store, affine.vector_load and affine....
static void printAffineMinMaxOp(OpAsmPrinter &p, T op)
static bool isResultTypeMatchAtomicRMWKind(Type resultType, arith::AtomicRMWKind op)
static bool remainsLegalAfterInline(Value value, Region *src, Region *dest, const IRMapping &mapping, function_ref< bool(Value, Region *)> legalityCheck)
Checks if value known to be a legal affine dimension or symbol in src region remains legal if the ope...
static void printMinMaxBound(OpAsmPrinter &p, AffineMapAttr mapAttr, DenseIntElementsAttr group, ValueRange operands, StringRef keyword)
Prints a lower(upper) bound of an affine parallel loop with max(min) conditions in it.
static void LLVM_ATTRIBUTE_UNUSED simplifyMapWithOperands(AffineMap &map, ArrayRef< Value > operands)
Simplify the map while exploiting information on the values in operands.
static OpFoldResult foldMinMaxOp(T op, ArrayRef< Attribute > operands)
Fold an affine min or max operation with the given operands.
static LogicalResult canonicalizeLoopBounds(AffineForOp forOp)
Canonicalize the bounds of the given loop.
static void simplifyExprAndOperands(AffineExpr &expr, unsigned numDims, unsigned numSymbols, ArrayRef< Value > operands)
Simplify expr while exploiting information from the values in operands.
static bool isValidAffineIndexOperand(Value value, Region *region)
static void canonicalizeMapOrSetAndOperands(MapOrSet *mapOrSet, SmallVectorImpl< Value > *operands)
static void composeAffineMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Iterate over operands and fold away all those produced by an AffineApplyOp iteratively.
static std::optional< int64_t > getUpperBound(Value iv)
Gets the constant upper bound on an affine.for iv.
static ParseResult parseBound(bool isLower, OperationState &result, OpAsmParser &p)
Parse a for operation loop bounds.
static std::optional< int64_t > getLowerBound(Value iv)
Gets the constant lower bound on an iv.
static void composeSetAndOperands(IntegerSet &set, SmallVectorImpl< Value > &operands)
Compose any affine.apply ops feeding into operands of the integer set set by composing the maps of su...
static LogicalResult replaceDimOrSym(AffineMap *map, unsigned dimOrSymbolPosition, SmallVectorImpl< Value > &dims, SmallVectorImpl< Value > &syms)
Replace all occurrences of AffineExpr at position pos in map by the defining AffineApplyOp expression...
static void canonicalizePromotedSymbols(MapOrSet *mapOrSet, SmallVectorImpl< Value > *operands)
static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType)
Verify common invariants of affine.vector_load and affine.vector_store.
static void simplifyMinOrMaxExprWithOperands(AffineMap &map, ArrayRef< Value > operands, bool isMax)
Simplify the expressions in map while making use of lower or upper bounds of its operands.
static ParseResult parseAffineMinMaxOp(OpAsmParser &parser, OperationState &result)
static bool isMemRefSizeValidSymbol(AnyMemRefDefOp memrefDefOp, unsigned index, Region *region)
Returns true if the 'index' dimension of the memref defined by memrefDefOp is a statically shaped one...
static bool isNonNegativeBoundedBy(AffineExpr e, ArrayRef< Value > operands, int64_t k)
Check if e is known to be: 0 <= e < k.
static ParseResult parseAffineMapWithMinMax(OpAsmParser &parser, OperationState &result, MinMaxKind kind)
Parses an affine map that can contain a min/max for groups of its results, e.g., max(expr-1,...
static AffineForOp buildAffineLoopFromValues(OpBuilder &builder, Location loc, Value lb, Value ub, int64_t step, AffineForOp::BodyBuilderFn bodyBuilderFn)
Creates an affine loop from the bounds that may or may not be constants.
static void printDimAndSymbolList(Operation::operand_iterator begin, Operation::operand_iterator end, unsigned numDims, OpAsmPrinter &printer)
Prints dimension and symbol list.
static int64_t getLargestKnownDivisor(AffineExpr e, ArrayRef< Value > operands)
Returns the largest known divisor of e.
static OpTy makeComposedMinMax(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
static void buildAffineLoopNestImpl(OpBuilder &builder, Location loc, BoundListTy lbs, BoundListTy ubs, ArrayRef< int64_t > steps, function_ref< void(OpBuilder &, Location, ValueRange)> bodyBuilderFn, LoopCreatorTy &&loopCreatorFn)
Builds an affine loop nest, using "loopCreatorFn" to create individual loop operations.
static LogicalResult foldLoopBounds(AffineForOp forOp)
Fold the constant bounds of a loop.
static LogicalResult verifyDimAndSymbolIdentifiers(OpTy &op, Operation::operand_range operands, unsigned numDims)
Utility function to verify that a set of operands are valid dimension and symbol identifiers.
static OpFoldResult makeComposedFoldedMinMax(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
static bool isDimOpValidSymbol(ShapedDimOpInterface dimOp, Region *region)
Returns true if the result of the dim op is a valid symbol for region.
static bool isQTimesDPlusR(AffineExpr e, ArrayRef< Value > operands, int64_t &div, AffineExpr "ientTimesDiv, AffineExpr &rem)
Check if expression e is of the form d*e_1 + e_2 where 0 <= e_2 < d.
static ParseResult deduplicateAndResolveOperands(OpAsmParser &parser, ArrayRef< SmallVector< OpAsmParser::UnresolvedOperand >> operands, SmallVectorImpl< Value > &uniqueOperands, SmallVectorImpl< AffineExpr > &replacements, AffineExprKind kind)
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static LogicalResult verifyAffineMinMaxOp(T op)
static void printBound(AffineMapAttr boundMap, Operation::operand_range boundOperands, const char *prefix, OpAsmPrinter &p)
static std::optional< SmallVector< int64_t > > foldCstValueToCstAttrBasis(ArrayRef< OpFoldResult > mixedBasis, MutableOperandRange mutableDynamicBasis, ArrayRef< Attribute > dynamicBasis)
Given mixed basis of affine.delinearize_index/linearize_index replace constant SSA values with the co...
static LogicalResult canonicalizeMapExprAndTermOrder(AffineMap &map)
Canonicalize the result expression order of an affine map and return success if the order changed.
static Value getZero(OpBuilder &b, Location loc, Type elementType)
Get zero value for an element type.
static Operation * materializeConstant(Dialect *dialect, OpBuilder &builder, Attribute value, Type type, Location loc)
A utility function used to materialize a constant for a given attribute and type.
static MLIRContext * getContext(OpFoldResult val)
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
static int64_t getNumElements(Type t)
Compute the total number of elements in the given type, also taking into account nested types.
union mlir::linalg::@1183::ArityGroupAndKind::Kind kind
static Operation::operand_range getLowerBoundOperands(AffineForOp forOp)
static Operation::operand_range getUpperBoundOperands(AffineForOp forOp)
static void print(spirv::VerCapExtAttr triple, DialectAsmPrinter &printer)
static VectorType getVectorType(Type scalarTy, const VectorizationStrategy *strategy)
Returns the vector type resulting from applying the provided vectorization strategy on the scalar typ...
RetTy walkPostOrder(AffineExpr expr)
Base type for affine expression.
AffineExpr floorDiv(uint64_t v) const
AffineExprKind getKind() const
Return the classification for this type.
int64_t getLargestKnownDivisor() const
Returns the greatest known integral divisor of this affine expression.
MLIRContext * getContext() const
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
AffineMap getSliceMap(unsigned start, unsigned length) const
Returns the map consisting of length expressions starting from start.
MLIRContext * getContext() const
bool isFunctionOfDim(unsigned position) const
Return true if any affine expression involves AffineDimExpr position.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
AffineMap shiftDims(unsigned shift, unsigned offset=0) const
Replace dims[offset ...
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
bool isFunctionOfSymbol(unsigned position) const
Return true if any affine expression involves AffineSymbolExpr position.
unsigned getNumResults() const
AffineMap replaceDimsAndSymbols(ArrayRef< AffineExpr > dimReplacements, ArrayRef< AffineExpr > symReplacements, unsigned numResultDims, unsigned numResultSyms) const
This method substitutes any uses of dimensions and symbols (e.g.
unsigned getNumInputs() const
AffineMap shiftSymbols(unsigned shift, unsigned offset=0) const
Replace symbols[offset ...
AffineExpr getResult(unsigned idx) const
AffineMap replace(AffineExpr expr, AffineExpr replacement, unsigned numResultDims, unsigned numResultSyms) const
Sparse replace method.
static AffineMap getConstantMap(int64_t val, MLIRContext *context)
Returns a single constant result affine map.
AffineMap getSubMap(ArrayRef< unsigned > resultPos) const
Returns the map consisting of the resultPos subset.
LogicalResult constantFold(ArrayRef< Attribute > operandConstants, SmallVectorImpl< Attribute > &results, bool *hasPoison=nullptr) const
Folds the results of the application of an affine map on the provided operands to a constant if possi...
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr >> exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
@ Paren
Parens surrounding zero or more operands.
@ OptionalSquare
Square brackets supporting zero or more ops, or nothing.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult addTypeToList(Type type, SmallVectorImpl< Type > &result)
Add the specified type to the end of the specified type list and return success.
virtual ParseResult parseOptionalRParen()=0
Parse a ) token if present.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
virtual ParseResult parseArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an arrow followed by a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
void printOptionalArrowTypeList(TypeRange &&types)
Print an optional arrow followed by a type list.
Attributes are known-constant values of operations.
Block represents an ordered list of Operations.
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
BlockArgListType getArguments()
This class is a general helper class for creating context-global objects like types,...
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
IntegerAttr getIntegerAttr(Type type, int64_t value)
AffineMap getDimIdentityMap()
AffineMap getMultiDimIdentityMap(unsigned rank)
AffineExpr getAffineSymbolExpr(unsigned position)
AffineExpr getAffineConstantExpr(int64_t constant)
DenseIntElementsAttr getI32TensorAttr(ArrayRef< int32_t > values)
Tensor-typed DenseIntElementsAttr getters.
IntegerAttr getI64IntegerAttr(int64_t value)
IntegerType getIntegerType(unsigned width)
BoolAttr getBoolAttr(bool value)
AffineMap getEmptyAffineMap()
Returns a zero result affine map with no dimensions or symbols: () -> ().
AffineMap getConstantAffineMap(int64_t val)
Returns a single constant result affine map with 0 dimensions and 0 symbols.
MLIRContext * getContext() const
AffineMap getSymbolIdentityMap()
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
An attribute that represents a reference to a dense integer vector or tensor object.
This is the interface that must be implemented by the dialects of operations to be inlined.
DialectInlinerInterface(Dialect *dialect)
This is a utility class for mapping one set of IR entities to another.
auto lookup(T from) const
Lookup a mapped value within the map.
An integer set representing a conjunction of one or more affine equalities and inequalities.
unsigned getNumDims() const
static IntegerSet get(unsigned dimCount, unsigned symbolCount, ArrayRef< AffineExpr > constraints, ArrayRef< bool > eqFlags)
MLIRContext * getContext() const
unsigned getNumInputs() const
ArrayRef< AffineExpr > getConstraints() const
ArrayRef< bool > getEqFlags() const
Returns the equality bits, which specify whether each of the constraints is an equality or inequality...
unsigned getNumSymbols() const
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 class provides a mutable adaptor for a range of operands.
void erase(unsigned subStart, unsigned subLen=1)
Erase the operands within the given sub-range.
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
void pop_back()
Pop last element from list.
Attribute erase(StringAttr name)
Erase the attribute with the given name from the list.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region ®ion, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult parseArgument(Argument &result, bool allowType=false, bool allowAttrs=false)=0
Parse a single argument with the following syntax:
ParseResult parseTrailingOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None)
Parse zero or more trailing SSA comma-separated trailing operand references with a specified surround...
virtual ParseResult parseArgumentList(SmallVectorImpl< Argument > &result, Delimiter delimiter=Delimiter::None, bool allowType=false, bool allowAttrs=false)=0
Parse zero or more arguments with a specified surrounding delimiter.
virtual ParseResult parseAffineMapOfSSAIds(SmallVectorImpl< UnresolvedOperand > &operands, Attribute &map, StringRef attrName, NamedAttrList &attrs, Delimiter delimiter=Delimiter::Square)=0
Parses an affine map attribute where dims and symbols are SSA operands.
ParseResult parseAssignmentList(SmallVectorImpl< Argument > &lhs, SmallVectorImpl< UnresolvedOperand > &rhs)
Parse a list of assignments of the form (x1 = y1, x2 = y2, ...)
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseAffineExprOfSSAIds(SmallVectorImpl< UnresolvedOperand > &dimOperands, SmallVectorImpl< UnresolvedOperand > &symbOperands, AffineExpr &expr)=0
Parses an affine expression where dims and symbols are SSA operands.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
virtual void printAffineExprOfSSAIds(AffineExpr expr, ValueRange dimOperands, ValueRange symOperands)=0
Prints an affine expression of SSA ids with SSA id names used instead of dims and symbols.
virtual void printAffineMapOfSSAIds(AffineMapAttr mapAttr, ValueRange operands)=0
Prints an affine map of SSA ids, where SSA id names are used in place of dims/symbols.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
virtual void printRegionArgument(BlockArgument arg, ArrayRef< NamedAttribute > argAttrs={}, bool omitType=false)=0
Print a block argument in the usual format of: ssaName : type {attr1=42} loc("here") where location p...
virtual void printOperand(Value value)=0
Print implementations for various things an operation contains.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Block::iterator getInsertionPoint() const
Returns the current insertion point of the builder.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Listener * getListener() const
Returns the current listener of this builder, or nullptr if this builder doesn't have a listener.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes=std::nullopt, ArrayRef< Location > locs=std::nullopt)
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
This class represents a single result from folding an operation.
This class represents an operand of an operation.
A trait of region holding operations that defines a new scope for polyhedral optimization purposes.
This class provides the API for ops that are known to be isolated from above.
A trait used to provide symbol table functionalities to a region operation.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
operand_range getOperands()
Returns an iterator on the underlying Value's.
Region * getParentRegion()
Returns the region to which the instruction belongs.
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
operand_range::iterator operand_iterator
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
bool isParent() const
Returns true if branching from the parent op.
This class represents a successor of a region.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Operation * getParentOp()
Return the parent operation this region is attached to.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
void mergeBlocks(Block *source, Block *dest, ValueRange argValues=std::nullopt)
Inline the operations of block 'source' into the end of block 'dest'.
virtual void finalizeOpModification(Operation *op)
This method is used to signal the end of an in-place modification of the given operation.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues=std::nullopt)
Inline the operations of block 'source' into block 'dest' before the given position.
virtual void startOpModification(Operation *op)
This method is used to notify the rewriter that an in-place operation modification is about to happen...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents a specific instance of an effect.
static DerivedEffect * get()
Returns a unique instance for the derived effect class.
static DefaultResource * get()
Returns a unique instance for the given effect class.
std::vector< SmallVector< int64_t, 8 > > operandExprStack
static Operation * lookupSymbolIn(Operation *op, StringAttr symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
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.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
AffineBound represents a lower or upper bound in the for operation.
AffineDmaStartOp starts a non-blocking DMA operation that transfers data from a source memref to a de...
AffineDmaWaitOp blocks until the completion of a DMA operation associated with the tag element 'tag[i...
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.
ArrayRef< Value > getOperands() const
AffineExpr getResult(unsigned i)
AffineMap getAffineMap() const
unsigned getNumResults() const
Operation * getOwner() const
Return the owner of this operand.
constexpr auto RecursivelySpeculatable
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto NotSpeculatable
void buildAffineLoopNest(OpBuilder &builder, Location loc, ArrayRef< int64_t > lbs, ArrayRef< int64_t > ubs, ArrayRef< int64_t > steps, function_ref< void(OpBuilder &, Location, ValueRange)> bodyBuilderFn=nullptr)
Builds a perfect nest of affine.for loops, i.e., each loop except the innermost one contains only ano...
void fullyComposeAffineMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Given an affine map map and its input operands, this method composes into map, maps of AffineApplyOps...
void extractForInductionVars(ArrayRef< AffineForOp > forInsts, SmallVectorImpl< Value > *ivs)
Extracts the induction variables from a list of AffineForOps and places them in the output argument i...
bool isValidDim(Value value)
Returns true if the given Value can be used as a dimension id in the region of the closest surroundin...
SmallVector< OpFoldResult > makeComposedFoldedMultiResultAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
Variant of makeComposedFoldedAffineApply suitable for multi-result maps.
bool isAffineInductionVar(Value val)
Returns true if the provided value is the induction variable of an AffineForOp or AffineParallelOp.
AffineForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
void canonicalizeMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Modifies both map and operands in-place so as to:
OpFoldResult makeComposedFoldedAffineMax(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
Constructs an AffineMinOp that computes a maximum across the results of applying map to operands,...
bool isAffineForInductionVar(Value val)
Returns true if the provided value is the induction variable of an AffineForOp.
OpFoldResult makeComposedFoldedAffineMin(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
Constructs an AffineMinOp that computes a minimum across the results of applying map to operands,...
bool isTopLevelValue(Value value)
A utility function to check if a value is defined at the top level of an op with trait AffineScope or...
Region * getAffineAnalysisScope(Operation *op)
Returns the closest region enclosing op that is held by a non-affine operation; nullptr if there is n...
void canonicalizeSetAndOperands(IntegerSet *set, SmallVectorImpl< Value > *operands)
Canonicalizes an integer set the same way canonicalizeMapAndOperands does for affine maps.
void extractInductionVars(ArrayRef< Operation * > affineOps, SmallVectorImpl< Value > &ivs)
Extracts the induction variables from a list of either AffineForOp or AffineParallelOp and places the...
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...
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
AffineParallelOp getAffineParallelInductionVarOwner(Value val)
Returns true if the provided value is among the induction variables of an AffineParallelOp.
Region * getAffineScope(Operation *op)
Returns the closest region enclosing op that is held by an operation with trait AffineScope; nullptr ...
ParseResult parseDimAndSymbolList(OpAsmParser &parser, SmallVectorImpl< Value > &operands, unsigned &numDims)
Parses dimension and symbol list.
bool isAffineParallelInductionVar(Value val)
Returns true if val is the induction variable of an AffineParallelOp.
AffineMinOp makeComposedAffineMin(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
Returns an AffineMinOp obtained by composing map and operands with AffineApplyOps supplying those ope...
BaseMemRefType getMemRefType(Value value, const BufferizationOptions &options, MemRefLayoutAttrInterface layout={}, Attribute memorySpace=nullptr)
Return a MemRefType to which the type of the given value can be bufferized.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
LogicalResult foldMemRefCast(Operation *op, Value inner=nullptr)
This is a common utility used for patterns of the form "someop(memref.cast) -> someop".
QueryRef parse(llvm::StringRef line, const QuerySession &qs)
Include the generated interface declarations.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
const FrozenRewritePatternSet GreedyRewriteConfig bool * changed
AffineMap removeDuplicateExprs(AffineMap map)
Returns a map with the same dimension and symbol count as map, but whose results are the unique affin...
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
std::optional< int64_t > getBoundForAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols, ArrayRef< std::optional< int64_t >> constLowerBounds, ArrayRef< std::optional< int64_t >> constUpperBounds, bool isUpper)
Get a lower or upper (depending on isUpper) bound for expr while using the constant lower and upper b...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
bool isPure(Operation *op)
Returns true if the given operation is pure, i.e., is speculatable that does not touch memory.
int64_t computeProduct(ArrayRef< int64_t > basis)
Self-explicit.
@ 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.
@ DimId
Dimensional identifier.
@ FloorDiv
RHS of floordiv is always a constant or a symbolic expression.
@ SymbolId
Symbolic identifier.
AffineExpr getAffineBinaryOpExpr(AffineExprKind kind, AffineExpr lhs, AffineExpr rhs)
std::function< SmallVector< Value >(OpBuilder &b, Location loc, ArrayRef< BlockArgument > newBbArgs)> NewYieldValuesFn
A function that returns the additional yielded values during replaceWithAdditionalYields.
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
const FrozenRewritePatternSet & patterns
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...
AffineMap foldAttributesIntoMap(Builder &b, AffineMap map, ArrayRef< OpFoldResult > operands, SmallVector< Value > &remainingValues)
Fold all attributes among the given operands into the affine map.
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
Canonicalize the affine map result expression order of an affine min/max operation.
LogicalResult matchAndRewrite(T affineOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(T affineOp, PatternRewriter &rewriter) const override
Remove duplicated expressions in affine min/max ops.
LogicalResult matchAndRewrite(T affineOp, PatternRewriter &rewriter) const override
Merge an affine min/max op to its consumers if its consumer is also an affine min/max op.
LogicalResult matchAndRewrite(T affineOp, PatternRewriter &rewriter) const override
This is the representation of an operand reference.
This class represents a listener that may be used to hook into various actions within an OpBuilder.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
T & getOrAddProperties()
Get (or create) a properties of the provided type to be set on the operation on creation.
SmallVector< Value, 4 > operands
void addOperands(ValueRange newOperands)
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
void addTypes(ArrayRef< Type > newTypes)
SmallVector< std::unique_ptr< Region >, 1 > regions
Regions that the op will hold.
SmallVector< Type, 4 > types
Types of the results of this operation.
Region * addRegion()
Create a region that should be attached to the operation.
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.