18#include "llvm/ADT/SetOperations.h"
19#include "llvm/ADT/SetVector.h"
20#include "llvm/ADT/TypeSwitch.h"
21#include "llvm/Support/Debug.h"
25#define GEN_PASS_DEF_LINALGDATALAYOUTPROPAGATION
26#include "mlir/Dialect/Linalg/Passes.h.inc"
32#define DEBUG_TYPE "linalg-data-layout-propagation"
36static bool hasGatherSemantics(linalg::GenericOp genericOp) {
37 for (
Operation &op : genericOp.getBody()->getOperations())
38 if (isa<tensor::ExtractOp, linalg::IndexOp>(op))
46 int64_t getNumTiledLoops()
const {
return tileToPointMapping.size(); };
58template <
typename OpTy>
59static FailureOr<PackInfo>
60getPackingInfoFromOperand(
OpOperand *opOperand, linalg::GenericOp genericOp,
61 OpTy packOrUnPackOp) {
62 static_assert(llvm::is_one_of<OpTy, linalg::PackOp, linalg::UnPackOp>::value,
63 "applies to only pack or unpack operations");
65 { llvm::dbgs() <<
"--- Construct PackInfo From an operand ---\n"; });
67 AffineMap indexingMap = genericOp.getMatchingIndexingMap(opOperand);
70 genericOp.getIteratorTypesArray();
73 int64_t origNumDims = indexingMap.getNumDims();
76 for (
auto [
index, innerDimPos, tileSize] :
77 llvm::zip_equal(llvm::seq<unsigned>(0, innerDimsPos.size()),
78 innerDimsPos, packOrUnPackOp.getMixedTiles())) {
79 auto expr = exprs[innerDimPos];
80 if (!isa<AffineDimExpr>(expr))
83 cast<AffineDimExpr>(exprs[innerDimPos]).getPosition();
86 packInfo.tiledDimsPos.push_back(domainDimPos);
87 packInfo.domainDimAndTileMapping[domainDimPos] = tileSize;
88 packInfo.tileToPointMapping[domainDimPos] = origNumDims +
index;
90 llvm::dbgs() <<
"map innerDimPos=" << innerDimPos
91 <<
" to iteration dimension (d" << domainDimPos <<
", d"
92 << packInfo.tileToPointMapping[domainDimPos]
93 <<
"), which has size=("
94 << packInfo.domainDimAndTileMapping[domainDimPos] <<
")\n";
100 auto areAllAffineDimExpr = [&](
int dim) {
102 if (llvm::any_of(map.getResults(), [dim](
AffineExpr expr) {
103 return expr.isFunctionOfDim(dim) && !isa<AffineDimExpr>(expr);
110 for (
int64_t i : packInfo.tiledDimsPos)
111 if (!areAllAffineDimExpr(i))
128 for (
auto [
index, dim] : llvm::enumerate(packOrUnPackOp.getOuterDimsPerm())) {
129 auto permutedExpr = indexingMap.getResult(dim);
130 if (
auto dimExpr = dyn_cast<AffineDimExpr>(permutedExpr)) {
131 permutedOuterDims.push_back(dimExpr.getPosition());
141 if (!permutedOuterDims.empty()) {
144 permutedOuterDims.end());
145 for (
int i = 0, e = indexingMap.getNumDims(); i < e; i++)
146 packInfo.outerDimsOnDomainPerm.push_back(
147 permutedDomainDims.contains(i) ? permutedOuterDims[outerDimIndex++]
150 llvm::dbgs() <<
"map outer dimsDimsPerm to ";
151 for (
auto dim : packInfo.outerDimsOnDomainPerm)
152 llvm::dbgs() << dim <<
" ";
153 llvm::dbgs() <<
"\n";
171 assert(!perm.empty() &&
"expect perm not to be empty");
172 assert(!exprs.empty() &&
"expect exprs not to be empty");
173 if (exprs.size() == 1)
177 for (
auto [pos, expr] : llvm::enumerate(exprs)) {
181 if (
auto dimExpr = dyn_cast<AffineDimExpr>(expr))
182 currentPositionTileLoops[dimExpr.getPosition()] = pos;
184 currentPositionTileLoops[pos] = pos;
187 if (currentPositionTileLoops.count(loopIdx))
188 outerDimsPerm.push_back(currentPositionTileLoops.lookup(loopIdx));
190 return outerDimsPerm;
193struct PackedOperandDetails {
203static bool getPackedOperandDetails(
206 PackedOperandDetails currOperandDetails;
207 int64_t numOrigLoops = genericOp.getNumLoops();
208 int64_t numInnerLoops = packInfo.getNumTiledLoops();
209 int64_t numLoops = numOrigLoops + numInnerLoops;
210 AffineMap origIndexingMap = genericOp.getMatchingIndexingMap(opOperand);
215 if (genericOp.isScalar(opOperand) || exprs.empty()) {
216 currOperandDetails.indexingMap =
218 packedOperandMap[opOperand] = currOperandDetails;
224 for (
auto [
index, expr] : llvm::enumerate(exprs)) {
225 if (
auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
226 int64_t dimPos = dimExpr.getPosition();
227 domainDimToOperandDim[dimPos] =
index;
233 for (
auto dimPos : packInfo.tiledDimsPos) {
234 if (!domainDimToOperandDim.count(dimPos))
237 innerTileSizes.push_back(packInfo.domainDimAndTileMapping[dimPos]);
238 innerDimsPos.push_back(
index);
239 exprs.push_back(
b.getAffineDimExpr(packInfo.tileToPointMapping[dimPos]));
244 if (!packInfo.outerDimsOnDomainPerm.empty()) {
245 outerDimsPerm = computeOuterDims(packInfo.outerDimsOnDomainPerm, exprs);
250 for (
auto i : llvm::seq<unsigned>(0, origIndexingMap.
getNumResults())) {
251 if (
auto dimExpr = dyn_cast<AffineDimExpr>(exprs[i])) {
252 int64_t dimPos = dimExpr.getPosition();
253 exprs[i] =
b.getAffineDimExpr(inversedOuterPerm[dimPos]);
256 assert(isa<AffineConstantExpr>(exprs[i]) &&
257 "Attempted to permute non-constant and non-affine dim expression");
261 if (!outerDimsPerm.empty()) {
263 for (
const auto &en : enumerate(outerDimsPerm))
264 auxVec[en.index()] = exprs[en.value()];
268 currOperandDetails.indexingMap =
272 if (innerDimsPos.empty() && outerDimsPerm.empty()) {
273 packedOperandMap[opOperand] = currOperandDetails;
276 auto inputType = cast<RankedTensorType>(opOperand->
get().
getType());
278 auto maybeIntInnerTileSizes =
281 return maybeCst.value_or(ShapedType::kDynamic);
283 bool requirePadding = linalg::PackOp::requirePaddingValueStrict(
284 inputType.getShape(), innerDimsPos,
285 linalg::PackOp::inferPackedTensorType(inputType, maybeIntInnerTileSizes,
286 innerDimsPos, outerDimsPerm)
288 outerDimsPerm, innerTileSizes);
289 currOperandDetails.innerDimsPos = innerDimsPos;
290 currOperandDetails.innerTileSizes = innerTileSizes;
291 currOperandDetails.outerDimsPerm = outerDimsPerm;
292 packedOperandMap[opOperand] = currOperandDetails;
294 return requirePadding;
328static std::tuple<Value, AffineMap> getOrCreatePackedViewOfOperand(
331 assert(packedOperandMap.contains(opOperand) &&
332 "packed operand details expected to be populated");
333 auto currOperandDetails = packedOperandMap.at(opOperand);
334 auto innerDimsPos = currOperandDetails.innerDimsPos;
335 auto outerDimsPerm = currOperandDetails.outerDimsPerm;
336 auto innerTileSizes = currOperandDetails.innerTileSizes;
337 if (innerDimsPos.empty() && outerDimsPerm.empty())
338 return std::make_tuple(opOperand->
get(), currOperandDetails.indexingMap);
340 auto empty = linalg::PackOp::createDestinationTensor(
341 b, loc, opOperand->
get(), innerTileSizes, innerDimsPos, outerDimsPerm);
342 auto poison = ub::PoisonOp::create(
344 PackOp packedOperand =
345 linalg::PackOp::create(
b, loc, opOperand->
get(), empty, innerDimsPos,
346 innerTileSizes, poison, outerDimsPerm);
347 return std::make_tuple(packedOperand.getResult(),
348 currOperandDetails.indexingMap);
357static FailureOr<GenericOp>
359 AffineMap packedOutIndexingMap,
const PackInfo &packInfo,
360 bool isFoldableUnpackPack,
bool poisonPaddingOk) {
365 auto hasEquivalentTiles = [](PackOp packOp, UnPackOp unPackOp) {
366 return packOp.getOuterDimsPerm() == unPackOp.getOuterDimsPerm() &&
367 packOp.getInnerDimsPos() == unPackOp.getInnerDimsPos() &&
368 llvm::equal(packOp.getMixedTiles(), unPackOp.getMixedTiles());
371 bool requiresPadding =
false;
372 for (
OpOperand *inputOperand : genericOp.getDpsInputOperands()) {
373 requiresPadding |= getPackedOperandDetails(rewriter, packInfo, genericOp,
374 inputOperand, packedOperandMap);
376 if (requiresPadding && !poisonPaddingOk)
379 for (
OpOperand *inputOperand : genericOp.getDpsInputOperands()) {
380 auto [packedOperand, packedIndexingMap] = getOrCreatePackedViewOfOperand(
381 rewriter, loc, inputOperand, packedOperandMap);
382 auto unpackOp = inputOperand->get().getDefiningOp<linalg::UnPackOp>();
383 auto packOp = packedOperand.getDefiningOp<linalg::PackOp>();
384 if (packOp && unpackOp && hasEquivalentTiles(packOp, unpackOp)) {
385 inputOperandsFromUnpackedSource.push_back(unpackOp.getSource());
387 inputOperandsFromUnpackedSource.push_back(packedOperand);
389 inputOperands.push_back(packedOperand);
390 indexingMaps.push_back(packedIndexingMap);
395 if (isFoldableUnpackPack) {
396 inputOperands = inputOperandsFromUnpackedSource;
398 auto destUnPack = destPack.getSource().getDefiningOp<linalg::UnPackOp>();
399 if (destUnPack && hasEquivalentTiles(destPack, destUnPack)) {
400 dest = destUnPack.getSource();
405 int64_t numInnerLoops = packInfo.getNumTiledLoops();
407 genericOp.getIteratorTypesArray();
408 iterTypes.append(numInnerLoops, utils::IteratorType::parallel);
410 indexingMaps.push_back(packedOutIndexingMap);
412 auto newGenericOp = linalg::GenericOp::create(
413 rewriter, loc, dest.
getType(), inputOperands, dest, indexingMaps,
417 newGenericOp.getRegion().begin());
421static bool isGenericOutsNotUsed(linalg::GenericOp genericOp) {
422 return llvm::all_of(genericOp.getDpsInitsMutable(), [&](
OpOperand &operand) {
423 return genericOp.getMatchingBlockArgument(&operand).use_empty();
470static FailureOr<GenericOp>
471bubbleUpPackOpThroughGenericOp(
RewriterBase &rewriter, linalg::PackOp packOp,
473 bool poisonPaddingOk) {
474 auto genericOp = packOp.getSource().getDefiningOp<GenericOp>();
479 if (!controlFn(&packOp.getSourceMutable()))
485 if (hasGatherSemantics(genericOp))
490 if (genericOp.getNumResults() != 1)
497 if (!genericOp->getResult(0).hasOneUse())
504 if (packOp.getPaddingValue())
507 OpOperand *opOperand = genericOp.getDpsInitOperand(0);
508 auto packInfo = getPackingInfoFromOperand(opOperand, genericOp, packOp);
509 if (failed(packInfo))
522 Value packOpDest = packOp.getDest();
525 if (
auto emptyOp = packOpDest.
getDefiningOp<tensor::EmptyOp>()) {
526 packOpDest = tensor::EmptyOp::create(rewriter, genericOp->getLoc(),
527 emptyOp.getMixedSizes(),
528 emptyOp.
getType().getElementType());
537 bool requiresPadding = getPackedOperandDetails(rewriter, *packInfo, genericOp,
538 opOperand, packedOperandMap);
539 if (requiresPadding && !poisonPaddingOk)
542 auto [packedOutOperand, packedOutIndexingMap] =
543 getOrCreatePackedViewOfOperand(rewriter, genericOp.getLoc(), opOperand,
550 Value dest = packedOutOperand;
552 genericOp.getDpsInitOperand(0)->get().
getDefiningOp<tensor::EmptyOp>();
553 if (initTensor || isGenericOutsNotUsed(genericOp)) {
558 return packGenericOp(rewriter, genericOp, dest, packedOutIndexingMap,
564struct BubbleUpPackOpThroughGenericOpPattern
567 BubbleUpPackOpThroughGenericOpPattern(
MLIRContext *context,
569 bool poisonPaddingOk)
571 poisonPaddingOk(std::move(poisonPaddingOk)) {}
573 LogicalResult matchAndRewrite(linalg::PackOp packOp,
578 if (!packOp.hasPureTensorSemantics())
581 auto genericOp = bubbleUpPackOpThroughGenericOp(rewriter, packOp, controlFn,
583 if (failed(genericOp))
585 rewriter.
replaceOp(packOp, genericOp->getResults());
591 bool poisonPaddingOk;
597class BubbleUpPackThroughPadOp final :
public OpRewritePattern<linalg::PackOp> {
602 LogicalResult matchAndRewrite(linalg::PackOp packOp,
607 if (!packOp.hasPureTensorSemantics())
610 auto padOp = packOp.getSource().getDefiningOp<tensor::PadOp>();
615 if (!controlFn(&packOp.getSourceMutable()))
619 if (packOp.getPaddingValue())
626 Value paddingVal = padOp.getConstantPaddingValue();
630 if (!packOp.getDest().getDefiningOp<tensor::EmptyOp>())
636 llvm::SmallBitVector paddedDims = padOp.getPaddedDims();
637 llvm::SmallBitVector innerDims(paddedDims.size());
638 for (
int64_t dim : innerDimsPos)
640 if (paddedDims.anyCommon(innerDims))
649 auto empty = linalg::PackOp::createDestinationTensor(
650 rewriter, loc, padOp.getSource(), mixedTiles, innerDimsPos,
652 auto sourcePack = linalg::PackOp::create(
653 rewriter, loc, padOp.getSource(), empty, innerDimsPos, mixedTiles,
654 std::nullopt, outerDimsPerm);
659 if (!outerDimsPerm.empty()) {
665 size_t pointLoopsSize = innerDimsPos.size();
666 lowPad.append(pointLoopsSize, rewriter.
getIndexAttr(0));
667 highPad.append(pointLoopsSize, rewriter.
getIndexAttr(0));
669 auto newPadOp = tensor::PadOp::create(
670 rewriter, loc,
Type(), sourcePack.getResult(), lowPad,
671 highPad, paddingVal, padOp.getNofold());
675 if (!padOp->hasOneUse()) {
676 auto unpackEmpty = linalg::UnPackOp::createDestinationTensor(
677 rewriter, loc, newPadOp, mixedTiles, innerDimsPos, outerDimsPerm);
678 UnPackOp unpackedPad =
679 linalg::UnPackOp::create(rewriter, loc, newPadOp, unpackEmpty,
680 innerDimsPos, mixedTiles, outerDimsPerm);
685 rewriter.
replaceOp(packOp, newPadOp.getResult());
708 for (
auto pos : dimsPos) {
710 int64_t projectedPos = reassocIndices[pos].back();
711 for (
auto i : llvm::reverse(reassocIndices[pos])) {
713 if (dim > 1 || ShapedType::isDynamic(dim)) {
718 projectedDimsPos.push_back(projectedPos);
720 return projectedDimsPos;
727 for (
auto [pos, tileSize] : llvm::zip_equal(dimsPos, tileSizes)) {
729 if (ShapedType::isDynamic(dim) || (dim % tileSize) != 0)
741static int64_t applyPermutationAndReindexReassoc(
744 if (!permutation.empty())
776bubbleUpPackOpThroughCollapseShape(tensor::CollapseShapeOp collapseOp,
777 linalg::PackOp packOp,
782 if (!packOp.hasPureTensorSemantics())
791 collapseOp.getReassociationIndices();
800 projectToInnerMostNonUnitDimsPos(innerDimsPos, reassocIndices, srcShape);
802 if (!isDimsDivisibleByTileSizes(projectedInnerDimsPos, srcShape,
810 for (
auto outerPos : outerDimsPerm)
811 llvm::append_range(newOuterDimsPerm, reassocIndices[outerPos]);
813 auto emptyOp = linalg::PackOp::createDestinationTensor(
814 rewriter, packOp.getLoc(), collapseOp.getSrc(), packOp.getMixedTiles(),
815 projectedInnerDimsPos, newOuterDimsPerm);
816 auto newPackOp = linalg::PackOp::create(
817 rewriter, packOp.getLoc(), collapseOp.getSrc(), emptyOp,
818 projectedInnerDimsPos, packOp.getMixedTiles(), packOp.getPaddingValue(),
826 applyPermutationAndReindexReassoc(newReassocIndices, outerDimsPerm);
828 for (
size_t i = 0; i < innerDimsPos.size(); ++i) {
829 newReassocIndices.push_back({nextPos});
833 auto newCollapseOp = tensor::CollapseShapeOp::create(
834 rewriter, collapseOp.getLoc(), packOp.getResult().
getType(),
835 newPackOp.getResult(), newReassocIndices);
836 rewriter.
replaceOp(packOp, newCollapseOp);
853 for (
auto pos : dimsPos) {
854 for (
auto [idx,
indices] : llvm::enumerate(reassocIndices)) {
858 if (llvm::is_contained(
indices, pos)) {
859 projectedPos.push_back(idx);
864 assert(projectedPos.size() == dimsPos.size() &&
"Invalid dim pos projection");
887bubbleUpPackOpThroughExpandShape(tensor::ExpandShapeOp expandOp,
888 linalg::PackOp packOp,
893 if (!packOp.hasPureTensorSemantics())
901 "non-identity outer dims perm NYI");
906 expandOp.getReassociationIndices();
910 for (
auto [idx,
indices] : llvm::enumerate(reassoc)) {
915 llvm::set_intersection(packDimsPos, expandDimPos);
919 if (packedDims.empty())
924 if (packedDims.size() != 1)
926 packOp,
"only one of the expanded dimensions can be packed");
929 if (packedDims.front() !=
indices.back())
931 packOp,
"can only pack the inner-most expanded dimension");
936 projectDimsPosIntoReassocPos(packInnerDims, reassoc);
946 RankedTensorType newPackType = linalg::PackOp::inferPackedTensorType(
947 expandOp.getSrcType(), packOp.getStaticInnerTiles(),
953 packOp,
"could not reassociate dims after bubbling up");
955 Value destTensor = linalg::PackOp::createDestinationTensor(
956 rewriter, packOp.getLoc(), expandOp.getSrc(), packOp.getMixedTiles(),
958 PackOp packedVal = linalg::PackOp::create(
959 rewriter, packOp.getLoc(), expandOp.getSrc(), destTensor,
960 projectedInnerDimsPos, packOp.getMixedTiles(), packOp.getPaddingValue(),
963 Value newExpandOp = tensor::ExpandShapeOp::create(
964 rewriter, packOp.getLoc(), packOp.getDestType(), packedVal.getResult(),
971class BubbleUpPackOpThroughReshapeOp final
977 LogicalResult matchAndRewrite(linalg::PackOp packOp,
982 if (!packOp.hasPureTensorSemantics())
985 Operation *srcOp = packOp.getSource().getDefiningOp();
992 if (llvm::any_of(packOp.getStaticTiles(), ShapedType::isDynamic))
996 if (!controlFn(&packOp.getSourceMutable()))
1000 .Case([&](tensor::CollapseShapeOp op) {
1001 return bubbleUpPackOpThroughCollapseShape(op, packOp, rewriter);
1003 .Case([&](tensor::ExpandShapeOp op) {
1004 return bubbleUpPackOpThroughExpandShape(op, packOp, rewriter);
1006 .Default(failure());
1032static LogicalResult pushDownUnPackOpThroughExpandShape(
1033 linalg::UnPackOp unPackOp, tensor::ExpandShapeOp expandOp,
1038 if (!unPackOp.hasPureTensorSemantics())
1042 if (!controlFn(&expandOp.getSrcMutable()))
1049 auto expandTy = dyn_cast<RankedTensorType>(expandOp.getType());
1054 expandOp.getReassociationIndices();
1063 projectToInnerMostNonUnitDimsPos(innerDimsPos, reassocIndices, dstShape);
1065 if (!isDimsDivisibleByTileSizes(projectedInnerDimsPos, dstShape,
1073 for (
auto outerPos : outerDimsPerm)
1074 llvm::append_range(newOuterDimsPerm, reassocIndices[outerPos]);
1081 applyPermutationAndReindexReassoc(newReassocIndices, outerDimsPerm);
1083 for (
size_t i = 0; i < innerDimsPos.size(); ++i) {
1084 newReassocIndices.push_back({nextPos});
1088 RankedTensorType newExpandType = linalg::PackOp::inferPackedTensorType(
1089 expandTy, innerTileSizes, projectedInnerDimsPos, newOuterDimsPerm);
1091 tensor::ExpandShapeOp::create(rewriter, expandOp.getLoc(), newExpandType,
1092 unPackOp.getSource(), newReassocIndices);
1094 auto emptyOp = linalg::UnPackOp::createDestinationTensor(
1095 rewriter, unPackOp.getLoc(), newExpandOp, unPackOp.getMixedTiles(),
1096 projectedInnerDimsPos, newOuterDimsPerm);
1097 auto newUnPackOp = linalg::UnPackOp::create(
1098 rewriter, unPackOp.getLoc(), newExpandOp.getResult(), emptyOp,
1099 projectedInnerDimsPos, unPackOp.getMixedTiles(), newOuterDimsPerm);
1100 rewriter.
replaceOp(expandOp, newUnPackOp);
1105class PushDownUnPackOpThroughReshapeOp final
1108 PushDownUnPackOpThroughReshapeOp(
MLIRContext *context,
1113 LogicalResult matchAndRewrite(linalg::UnPackOp unPackOp,
1118 if (!unPackOp.hasPureTensorSemantics())
1123 if (!
result.hasOneUse()) {
1127 if (llvm::any_of(unPackOp.getStaticTiles(), ShapedType::isDynamic))
1132 .Case([&](tensor::ExpandShapeOp op) {
1133 return pushDownUnPackOpThroughExpandShape(unPackOp, op, rewriter,
1136 .Default(failure());
1146static FailureOr<OpOperand *> getUnPackedOperand(GenericOp genericOp) {
1148 for (
OpOperand &operand : genericOp->getOpOperands()) {
1149 auto unPackOp = operand.get().getDefiningOp<linalg::UnPackOp>();
1152 if (unPackedOperand)
1154 unPackedOperand = &operand;
1156 if (!unPackedOperand)
1158 return unPackedOperand;
1195static FailureOr<std::tuple<GenericOp, Value>>
1196pushDownUnPackOpThroughGenericOp(
RewriterBase &rewriter, GenericOp genericOp,
1198 bool poisonPaddingOk) {
1199 if (genericOp.getNumResults() != 1)
1202 if (hasGatherSemantics(genericOp))
1206 auto maybeUnPackedOperand = getUnPackedOperand(genericOp);
1207 if (failed(maybeUnPackedOperand))
1209 OpOperand *unPackedOperand = *(maybeUnPackedOperand);
1212 linalg::UnPackOp producerUnPackOp =
1214 assert(producerUnPackOp &&
"expect a valid UnPackOp");
1216 if (!controlFn(unPackedOperand))
1220 getPackingInfoFromOperand(unPackedOperand, genericOp, producerUnPackOp);
1221 if (failed(packInfo))
1226 bool requiresPadding =
1227 getPackedOperandDetails(rewriter, *packInfo, genericOp,
1228 genericOp.getDpsInitOperand(0), packedOperandMap);
1229 if (requiresPadding && !poisonPaddingOk)
1232 auto [packedOutOperand, packedOutIndexingMap] =
1233 getOrCreatePackedViewOfOperand(rewriter, genericOp.getLoc(),
1234 genericOp.getDpsInitOperand(0),
1236 auto destPack = packedOutOperand.getDefiningOp<linalg::PackOp>();
1243 Value dest = packedOutOperand;
1245 genericOp.getDpsInitOperand(0)->get().
getDefiningOp<tensor::EmptyOp>();
1246 if (initTensor || isGenericOutsNotUsed(genericOp)) {
1248 dest = destPack.getDest();
1255 auto maybeGenericOp =
1256 packGenericOp(rewriter, genericOp, dest, packedOutIndexingMap, *packInfo,
1257 true, poisonPaddingOk);
1258 if (failed(maybeGenericOp))
1260 GenericOp newGenericOp = *maybeGenericOp;
1262 newGenericOp.getTiedOpResult(newGenericOp.getDpsInitOperand(0));
1266 return std::make_tuple(newGenericOp, newResult);
1268 auto mixedTiles = destPack.getMixedTiles();
1269 auto innerDimsPos = destPack.getInnerDimsPos();
1270 auto outerDimsPerm = destPack.getOuterDimsPerm();
1274 linalg::UnPackOp::create(rewriter, genericOp.getLoc(), newResult,
1275 destPack.getSource(), innerDimsPos, mixedTiles,
1279 return std::make_tuple(newGenericOp, unPackOpRes);
1283struct PushDownUnPackOpThroughGenericOp :
public OpRewritePattern<GenericOp> {
1285 PushDownUnPackOpThroughGenericOp(
MLIRContext *context,
1287 bool poisonPaddingOk)
1289 poisonPaddingOk(std::move(poisonPaddingOk)) {}
1291 LogicalResult matchAndRewrite(GenericOp genericOp,
1293 auto genericAndRepl = pushDownUnPackOpThroughGenericOp(
1294 rewriter, genericOp, controlFn, poisonPaddingOk);
1295 if (failed(genericAndRepl))
1297 rewriter.
replaceOp(genericOp, std::get<1>(*genericAndRepl));
1303 bool poisonPaddingOk;
1309struct PushDownUnPackThroughPadOp :
public OpRewritePattern<tensor::PadOp> {
1313 LogicalResult matchAndRewrite(tensor::PadOp padOp,
1315 linalg::UnPackOp unpackOp =
1316 padOp.getSource().getDefiningOp<linalg::UnPackOp>();
1323 if (!unpackOp.hasPureTensorSemantics())
1326 if (!controlFn(&padOp.getSourceMutable()))
1331 llvm::SmallBitVector paddedDims = padOp.getPaddedDims();
1333 llvm::SmallBitVector innerDims(paddedDims.size());
1334 for (
int64_t dim : innerDimsPos)
1335 innerDims.flip(dim);
1336 if (paddedDims.anyCommon(innerDims))
1339 Value paddingVal = padOp.getConstantPaddingValue();
1347 if (!outerDimsPerm.empty()) {
1352 size_t pointLoopsSize = innerDimsPos.size();
1353 lowPad.append(pointLoopsSize, rewriter.
getIndexAttr(0));
1354 highPad.append(pointLoopsSize, rewriter.
getIndexAttr(0));
1356 auto newPadOp = tensor::PadOp::create(rewriter, loc,
Type(),
1357 unpackOp.getSource(), lowPad, highPad,
1358 paddingVal, padOp.getNofold());
1361 Value outputUnPack =
1362 tensor::EmptyOp::create(rewriter, loc, padOp.getResultType().getShape(),
1363 padOp.getResultType().getElementType());
1366 rewriter, loc, newPadOp.getResult(), outputUnPack, innerDimsPos,
1367 unpackOp.getMixedTiles(), outerDimsPerm);
1377struct SliceDimInfo {
1385static FailureOr<SmallVector<OpOperand *>>
1386getSliceOperands(GenericOp genericOp) {
1388 for (
auto operand : genericOp.getDpsInputOperands()) {
1389 auto extractOp = operand->get().getDefiningOp<tensor::ExtractSliceOp>();
1392 sliceOperands.push_back(operand);
1394 if (sliceOperands.empty()) {
1397 return sliceOperands;
1403static FailureOr<llvm::DenseMap<int64_t, SliceDimInfo>>
1404getPartialSliceDimInfo(GenericOp genericOp,
OpOperand *sliceOperand) {
1405 tensor::ExtractSliceOp producerSliceOp =
1407 assert(producerSliceOp &&
"expect a valid ExtractSliceOp");
1413 genericOp.getContext(), producerSliceOp.getSourceType().getShape());
1415 for (
auto [idx, expr] : llvm::enumerate(
1416 genericOp.getMatchingIndexingMap(sliceOperand).getResults())) {
1425 if (!isa<AffineDimExpr>(expr)) {
1428 SliceDimInfo sliceDimInfo{offsets[idx], sizes[idx],
shape[idx]};
1429 int64_t dimPos = cast<AffineDimExpr>(expr).getPosition();
1430 partialSliceDimMap[dimPos] = sliceDimInfo;
1434 for (
OpOperand &operand : genericOp->getOpOperands()) {
1435 if (operand == *sliceOperand) {
1438 AffineMap IndexingMap = genericOp.getMatchingIndexingMap(&operand);
1440 if (isa<AffineDimExpr>(expr)) {
1444 if (auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
1445 if (partialSliceDimMap.contains(dimExpr.getPosition())) {
1446 return WalkResult::interrupt();
1451 if (status.wasInterrupted()) {
1459 return partialSliceDimMap;
1462static FailureOr<std::tuple<GenericOp, Value>>
1463pushDownExtractSliceOpThroughGenericOp(
RewriterBase &rewriter,
1464 GenericOp genericOp,
1466 if (genericOp.getNumResults() != 1)
1468 genericOp,
"propagation through multi-result generic is unsupported.");
1469 if (hasGatherSemantics(genericOp))
1472 "propagation through generic with gather semantics is unsupported.");
1474 auto maybeSliceOperands = getSliceOperands(genericOp);
1475 if (
failed(maybeSliceOperands))
1480 bool foundValidOperand =
false;
1481 for (
auto currSliceOperand : sliceOperands) {
1482 if (controlFn(currSliceOperand)) {
1483 sliceOperand = currSliceOperand;
1484 foundValidOperand =
true;
1488 if (!foundValidOperand) {
1493 tensor::ExtractSliceOp producerSliceOp =
1495 assert(producerSliceOp &&
"expect a valid ExtractSliceOp");
1497 if (producerSliceOp.getSource().getType().getRank() !=
1498 producerSliceOp.getResult().getType().getRank()) {
1501 "propagation of rank-reducing extract slice is unsupported.");
1507 genericOp,
"propagation of strided extract slice is unsupported.");
1512 auto maybePartialSliceDimMap =
1513 getPartialSliceDimInfo(genericOp, sliceOperand);
1515 if (
failed(maybePartialSliceDimMap)) {
1519 auto partialSliceDimMap = *maybePartialSliceDimMap;
1522 genericOp.getIteratorTypesArray();
1523 bool hasPartialReductionDimSlice =
1524 llvm::any_of(partialSliceDimMap, [&](
const auto &
slice) {
1526 return iterators[sliceDim] == utils::IteratorType::reduction;
1530 Location loc = genericOp->getLoc();
1541 for (
auto [idx, operand] : llvm::enumerate(genericOp.getDpsInputOperands())) {
1542 if (idx == OperandIndex && !hasPartialReductionDimSlice) {
1543 paddedInputs.push_back(producerSliceOp.getSource());
1546 AffineMap IndexingMap = genericOp.getMatchingIndexingMap(operand);
1548 paddedInputs.push_back(operand->get());
1555 for (
auto [idx, expr] : llvm::enumerate(IndexingMap.
getResults())) {
1556 if (!isa<AffineDimExpr>(expr)) {
1560 if (!partialSliceDimMap.contains(dimExpr.
getPosition())) {
1563 SliceDimInfo sliceDimInfo = partialSliceDimMap[dimExpr.
getPosition()];
1564 operandLowPads[idx] = sliceDimInfo.offset;
1565 operandHighPads[idx] =
1566 sub(sub(sliceDimInfo.outputSize, sliceDimInfo.offset),
1567 sliceDimInfo.sliceSize);
1569 auto paddingValue = ub::PoisonOp::create(
1571 auto paddedOperand = tensor::PadOp::create(
1572 rewriter, loc,
Type(), operand->get(), operandLowPads, operandHighPads,
1573 paddingValue,
false);
1574 paddedInputs.push_back(paddedOperand);
1577 genericOp.getMatchingIndexingMap(genericOp.getDpsInitOperand(0));
1579 auto outputShapeType =
1580 llvm::cast<ShapedType>(genericOp.getDpsInitOperand(0)->get().getType());
1582 outputShapeType.getShape(),
1591 for (
auto [idx, expr] : llvm::enumerate(outputIndexingMap.
getResults())) {
1592 if (!isa<AffineDimExpr>(expr)) {
1596 if (!partialSliceDimMap.contains(dimExpr.
getPosition())) {
1599 SliceDimInfo sliceDimInfo = partialSliceDimMap[dimExpr.
getPosition()];
1600 outputLowPads[idx] = sliceDimInfo.offset;
1601 outputHighPads[idx] = sub(sub(sliceDimInfo.outputSize, sliceDimInfo.offset),
1602 sliceDimInfo.sliceSize);
1603 OutputShape[idx] = sliceDimInfo.outputSize;
1604 newSizes[idx] = sliceDimInfo.sliceSize;
1609 if (isGenericOutsNotUsed(genericOp)) {
1611 tensor::EmptyOp::create(rewriter, loc, OutputShape, outputElType);
1613 auto paddingValue = ub::PoisonOp::create(rewriter, loc, outputElType);
1614 newPadOutput = tensor::PadOp::create(
1615 rewriter, loc,
Type(), genericOp.getDpsInits()[0], outputLowPads,
1616 outputHighPads, paddingValue,
false);
1619 auto newGenericOp = linalg::GenericOp::create(
1620 rewriter, loc, newPadOutput.
getType(), paddedInputs, {newPadOutput},
1621 genericOp.getIndexingMapsArray(), genericOp.getIteratorTypesArray(),
1624 newGenericOp.getRegion().begin());
1626 auto extractOp = tensor::ExtractSliceOp::create(
1628 newGenericOp.getTiedOpResult(newGenericOp.getDpsInitOperand(0)),
1629 outputLowPads, newSizes, newStrides);
1630 Value extractRes = extractOp.getResult();
1632 return std::make_tuple(newGenericOp, extractRes);
1635class PushDownExtractSliceOpThroughGenericOp final
1638 PushDownExtractSliceOpThroughGenericOp(MLIRContext *context,
1640 : OpRewritePattern<GenericOp>(context), controlFn(std::move(fun)) {}
1642 LogicalResult matchAndRewrite(GenericOp genericOp,
1643 PatternRewriter &rewriter)
const override {
1644 auto genericAndRepl =
1645 pushDownExtractSliceOpThroughGenericOp(rewriter, genericOp, controlFn);
1646 if (
failed(genericAndRepl))
1648 rewriter.
replaceOp(genericOp, std::get<1>(*genericAndRepl));
1661 bool PoisonPaddingOk) {
1662 patterns.
insert<BubbleUpPackThroughPadOp, BubbleUpPackOpThroughReshapeOp,
1663 PushDownUnPackThroughPadOp, PushDownUnPackOpThroughReshapeOp>(
1664 patterns.
getContext(), controlPackUnPackPropagation);
1665 patterns.
insert<BubbleUpPackOpThroughGenericOpPattern,
1666 PushDownUnPackOpThroughGenericOp>(
1667 patterns.
getContext(), controlPackUnPackPropagation, PoisonPaddingOk);
1673 patterns.
insert<PushDownExtractSliceOpThroughGenericOp>(
1674 patterns.
getContext(), controlPackUnPackPropagation);
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
A dimensional identifier appearing in an affine expression.
unsigned getPosition() const
Base type for affine expression.
RetT walk(FnT &&callback) const
Walk all of the AffineExpr's in this expression in postorder.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
IntegerAttr getIndexAttr(int64_t value)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
MLIRContext * getContext() const
A class for computing basic dominance information.
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
IRValueT get() const
Return the current value being used by this operand.
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.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void cloneRegionBefore(Region ®ion, Region &parent, Region::iterator before, IRMapping &mapping)
Clone the blocks that belong to "region" before the given position in another region "parent".
This class represents a single result from folding an operation.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
unsigned getNumResults()
Return the number of results held by this operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
RewritePatternSet & insert(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
MLIRContext * getContext() const
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
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,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
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.
bool hasOneUse() const
Returns true if this value has exactly one use.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
void populateDataLayoutPropagationPatterns(RewritePatternSet &patterns, const ControlPropagationFn &controlPackUnPackPropagation, bool PoisonPaddingOk=false)
Patterns to bubble up or down data layout ops across other operations.
void populateExtractSliceSinkingPatterns(RewritePatternSet &patterns, const ControlPropagationFn &controlPackUnPackPropagation)
Patterns to sink extract slice across other operations.
bool isParallelIterator(utils::IteratorType iteratorType)
Check if iterator type has "parallel" semantics.
std::function< bool(OpOperand *opOperand)> ControlPropagationFn
Function type which is used to control propagation of linalg.pack/unpack ops.
SmallVector< NamedAttribute > getPrunedAttributeList(OpTy op)
Returns an attribute list that excludes pre-defined attributes.
Include the generated interface declarations.
bool isConstantIntValue(OpFoldResult ofr, int64_t value)
Return true if ofr is constant integer equal to value.
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
bool areAllConstantIntValue(ArrayRef< OpFoldResult > ofrs, int64_t value)
Return true if all of ofrs are constant integers equal to value.
bool isEqualConstantIntOrValue(OpFoldResult ofr1, OpFoldResult ofr2)
Return true if ofr1 and ofr2 are the same integer constant attribute values or the same SSA value.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
std::optional< SmallVector< ReassociationIndices > > getReassociationIndicesForReshape(ShapedType sourceType, ShapedType targetType)
Return the reassociations maps to use to reshape given the source type and the target type when possi...
bool isIdentityPermutation(ArrayRef< int64_t > permutation)
Returns true if permutation is an identity permutation.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
llvm::TypeSwitch< T, ResultT > TypeSwitch
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
SmallVector< int64_t, 2 > ReassociationIndices
void applyPermutationToVector(SmallVector< T, N > &inVec, ArrayRef< int64_t > permutation)
Apply the permutation defined by permutation to inVec.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...