32#include "llvm/ADT/SmallVectorExtras.h"
33#include "llvm/ADT/TypeSwitch.h"
34#include "llvm/Support/Debug.h"
35#include "llvm/Support/DebugLog.h"
36#include "llvm/Support/InterleavedRange.h"
37#include "llvm/Support/raw_ostream.h"
41#define DEBUG_TYPE "linalg-transforms"
60 .Case([&](scf::ForOp forOp) {
61 scf::ForOp partialIteration;
64 return partialIteration->getResults();
65 assert(!partialIteration &&
"expected that loop was not peeled");
66 return forOp->getResults();
75 for (
auto loopOp : loops)
88 if (!e.isFunctionOfDim(dim))
99 return llvm::interleaved(ri,
", ",
"|",
"");
150static FailureOr<SmallVector<std::optional<int64_t>>>
154 int64_t newDim = iteratorTypes.size();
155 iteratorTypes.push_back(iteratorTypes[dim]);
158 indexingMaps.size(), std::nullopt);
160 for (
int64_t operandIdx = 0, e = indexingMaps.size(); operandIdx < e;
162 AffineMap map = indexingMaps[operandIdx];
165 assert(map.
getNumDims() == newDim &&
"num dims invariant violation");
173 "num results invariant violation");
175 if (!maybeOperandDimensionToPack.has_value()) {
176 newMaps.push_back(map);
181 if (!isa<AffineDimExpr>(map.
getResult(maybeOperandDimensionToPack.value())))
187 newMaps.push_back(map);
190 packedDimPerIndexingMap[operandIdx] = maybeOperandDimensionToPack;
192 indexingMaps = newMaps;
194 return packedDimPerIndexingMap;
200struct PackedOperandsDim {
201 OpFoldResult packedSize;
202 SmallVector<std::optional<int64_t>> packedDimForEachOperand;
206struct PackedOperandsDimList {
207 void pushBack(PackedOperandsDim &&packedOperandsDims) {
208 spec.emplace_back(packedOperandsDims);
211 SmallVector<int64_t> extractPackedDimsForOperand(int64_t operandPos);
213 SmallVector<OpFoldResult> extractPackSizesForOperand(int64_t operandPos);
216 SmallVector<PackedOperandsDim> spec;
228 ArrayRef<std::optional<int64_t>> packedLoopToInnerLoop) {
229 if (!packedLinalgOp.hasIndexSemantics())
233 llvm::to_vector(packedLinalgOp.getBlock()->getOps<IndexOp>());
234 for (IndexOp indexOp : indexOps) {
235 unsigned dim = indexOp.getDim();
236 assert(dim < packedLoopToInnerLoop.size() &&
"invalid linalg.index dim");
237 std::optional<int64_t> innerDim = packedLoopToInnerLoop[dim];
244 IndexOp::create(rewriter, indexOp.getLoc(), dim).getResult();
246 IndexOp::create(rewriter, indexOp.getLoc(), *innerDim).getResult();
252 rewriter, indexOp.getLoc(), outer * tileSize + inner,
253 {outerIndex, innerIndex, packedSizes[dim]});
255 rewriter, indexOp.getLoc(), originalIndex));
260 linalg::PackOp packOp,
261 bool lowerPadLikeWithInsertSlice) {
265 if (!packOp.hasPureTensorSemantics())
268 auto packedTensorType =
269 cast<RankedTensorType>(packOp->getResultTypes().front());
277 PackingMetadata packingMetadata;
300 for (
auto [pos, innerSize] :
301 llvm::zip_equal(packOp.getInnerDimsPos(), packOp.getMixedTiles())) {
303 packedToStripMinedShapePerm[packingMetadata.outerPositions[pos]];
313 rewriter, loc, map, {outerSize, origSize, innerSize});
315 RankedTensorType collapsed = tensor::CollapseShapeOp::inferCollapsedType(
317 packingMetadata.reassociations);
318 Value paddingValue = packOp.getPaddingValue();
320 paddingValue = arith::ConstantOp::create(
324 tensor::PadOp::create(rewriter, loc, collapsed, packOp.getSource(), lows,
325 highs, paddingValue,
false);
327 LDBG() <<
"insertPositions: "
328 << llvm::interleaved(packingMetadata.insertPositions);
329 LDBG() <<
"outerPositions: "
330 << llvm::interleaved(packingMetadata.outerPositions);
331 LDBG() <<
"packedShape: " << llvm::interleaved(packedTensorType.getShape());
332 LDBG() <<
"packedToStripMinedShapePerm: "
333 << llvm::interleaved(packedToStripMinedShapePerm);
334 LDBG() <<
"reassociations: "
335 << llvm::interleaved(llvm::map_range(packingMetadata.reassociations,
337 LDBG() <<
"stripMinedShape: " << llvm::interleaved(stripMinedShape);
338 LDBG() <<
"collapsed type: " << collapsed;
340 if (lowerPadLikeWithInsertSlice && packOp.isLikePad()) {
359 auto insertSliceOp = tensor::InsertSliceOp::create(
360 rewriter, loc, padOp, packOp.getDest(),
363 LDBG() <<
"insert_slice op: " << insertSliceOp;
365 rewriter.
replaceOp(packOp, insertSliceOp->getResults());
373 auto expandShapeResultType =
375 auto reshapeOp = tensor::ExpandShapeOp::create(
376 rewriter, loc, expandShapeResultType, padOp.getResult(),
377 packingMetadata.reassociations, stripMinedMixedSizes);
382 auto transposeOp = linalg::TransposeOp::create(
383 rewriter, loc, reshapeOp.getResult(), packOp.getDest(), transpPerm);
385 LDBG() <<
"reshape op: " << reshapeOp;
386 LDBG() <<
"transpPerm: " << llvm::interleaved(transpPerm);
387 LDBG() <<
"transpose op: " << transposeOp;
390 rewriter.
replaceOp(packOp, transposeOp->getResults());
395FailureOr<LowerUnPackOpResult>
397 bool lowerUnpadLikeWithExtractSlice) {
401 if (!unPackOp.hasPureTensorSemantics())
408 auto packedTensorType = cast<RankedTensorType>(unPackOp.getSourceType());
409 int64_t packedRank = packedTensorType.getRank();
412 auto destTensorType = cast<RankedTensorType>(unPackOp.getDest().getType());
413 if (lowerUnpadLikeWithExtractSlice && unPackOp.isLikeUnPad()) {
422 auto extractSliceOp = tensor::ExtractSliceOp::create(
423 rewriter, loc, destTensorType, unPackOp.getSource(),
427 rewriter.
replaceOp(unPackOp, extractSliceOp->getResults());
430 nullptr, extractSliceOp,
436 PackingMetadata packingMetadata;
446 RankedTensorType stripMinedTensorType =
448 RankedTensorType collapsedType = tensor::CollapseShapeOp::inferCollapsedType(
449 stripMinedTensorType, packingMetadata.reassociations);
456 auto emptyOp = tensor::EmptyOp::create(rewriter, loc, dims,
457 stripMinedTensorType.getElementType());
459 linalg::TransposeOp::create(rewriter, loc, unPackOp.getSource(), emptyOp,
460 packedToStripMinedShapePerm);
462 LDBG() <<
"insertPositions: "
463 << llvm::interleaved(packingMetadata.insertPositions);
464 LDBG() <<
"packedShape: " << llvm::interleaved(packedTensorType.getShape());
465 LDBG() <<
"packedToStripMinedShapePerm: "
466 << llvm::interleaved(packedToStripMinedShapePerm);
467 LDBG() <<
"reassociations: "
468 << llvm::interleaved(llvm::map_range(packingMetadata.reassociations,
470 LDBG() <<
"stripMinedShape: " << llvm::interleaved(stripMinedShape);
471 LDBG() <<
"collapsed type: " << collapsedType;
474 auto reshapeOp = tensor::CollapseShapeOp::create(
475 rewriter, loc, collapsedType, transposeOp->getResult(0),
476 packingMetadata.reassociations);
479 int64_t destRank = destTensorType.getRank();
480 auto extractSliceOp = tensor::ExtractSliceOp::create(
481 rewriter, loc, destTensorType, reshapeOp->getResult(0),
487 auto copyOp = linalg::CopyOp::create(
488 rewriter, loc, extractSliceOp->getResult(0), unPackOp.getDest());
491 rewriter.
replaceOp(unPackOp, copyOp->getResults());
498PackedOperandsDimList::extractPackedDimsForOperand(
int64_t operandPos) {
500 for (
auto &i : spec) {
501 if (!i.packedDimForEachOperand[operandPos].has_value())
503 res.push_back(i.packedDimForEachOperand[operandPos].value());
508SmallVector<OpFoldResult>
509PackedOperandsDimList::extractPackSizesForOperand(int64_t operandPos) {
510 SmallVector<OpFoldResult> res;
511 for (
auto &i : spec) {
512 if (!i.packedDimForEachOperand[operandPos].has_value())
514 res.push_back(i.packedSize);
523 linalg::LinalgOp linalgOp,
525 if (packedSizes.size() != linalgOp.getNumLoops()) {
527 "incorrect number of pack sizes");
529 if (!linalgOp.hasPureTensorSemantics()) {
531 linalgOp,
"expects LinalgOp with pure tensor semantics");
537 linalgOp.getIteratorTypesArray();
538 LDBG() <<
"Start packing: " << linalgOp;
539 LDBG() <<
"maps: " << llvm::interleaved(indexingMaps);
540 LDBG() <<
"iterators: " << llvm::interleaved(iteratorTypes);
545 PackedOperandsDimList listOfPackedOperandsDim;
547 for (
int64_t i = 0, e = packedSizes.size(); i < e; ++i) {
550 if (maybeConstant.has_value() && maybeConstant.value() == 0)
553 PackedOperandsDim packedOperandsDims;
554 packedOperandsDims.packedSize = packedSizes[i];
555 FailureOr<SmallVector<std::optional<int64_t>>>
556 maybePackedDimForEachOperand =
558 if (failed(maybePackedDimForEachOperand))
560 packedLoopToInnerLoop[i] = iteratorTypes.size() - 1;
561 packedOperandsDims.packedDimForEachOperand = *maybePackedDimForEachOperand;
563 LDBG() <<
"++++ After pack size #" << i <<
": " << packedSizes[i];
564 LDBG() <<
"maps: " << llvm::interleaved(indexingMaps);
565 LDBG() <<
"iterators: " << llvm::interleaved(iteratorTypes);
566 LDBG() <<
"packedDimForEachOperand: "
567 << llvm::interleaved(packedOperandsDims.packedDimForEachOperand);
569 listOfPackedOperandsDim.pushBack(std::move(packedOperandsDims));
575 llvm::to_vector(llvm::make_pointer_range(linalgOp.getDpsInitsMutable()));
577 for (
const auto &operandsList : {inputOperands, initOperands}) {
578 for (
OpOperand *opOperand : operandsList) {
579 int64_t pos = opOperand->getOperandNumber();
580 Value operand = opOperand->get();
582 listOfPackedOperandsDim.extractPackedDimsForOperand(pos);
584 listOfPackedOperandsDim.extractPackSizesForOperand(pos);
585 LDBG() <<
"operand: " << operand;
586 LDBG() <<
"innerPos: " << llvm::interleaved(innerPos);
587 LDBG() <<
"innerPackSizes: " << llvm::interleaved(innerPackSizes);
588 if (innerPackSizes.empty()) {
589 inputsAndInits.push_back(operand);
592 Value dest = linalg::PackOp::createDestinationTensor(
593 rewriter, loc, operand, innerPackSizes, innerPos,
595 ShapedType operandType = cast<ShapedType>(operand.
getType());
596 bool areConstantTiles =
600 if (areConstantTiles && operandType.hasStaticShape() &&
601 !linalg::PackOp::requirePaddingValue(
602 operandType.getShape(), innerPos,
603 cast<ShapedType>(dest.
getType()).getShape(), {},
605 packOps.push_back(linalg::PackOp::create(rewriter, loc, operand, dest,
606 innerPos, innerPackSizes));
612 Value zero = arith::ConstantOp::create(rewriter, loc, zeroAttr);
613 packOps.push_back(linalg::PackOp::create(
614 rewriter, loc, operand, dest, innerPos, innerPackSizes, zero));
616 inputsAndInits.push_back(packOps.back().getResult());
622 ValueRange{inputsAndInits}.take_front(linalgOp.getNumDpsInputs());
624 ValueRange{inputsAndInits}.take_back(linalgOp.getNumDpsInits());
625 auto packedLinalgOp =
626 linalg::GenericOp::create(rewriter, linalgOp.getLoc(), inits.
getTypes(),
627 inputs, inits, indexingMaps, iteratorTypes);
628 packedLinalgOp.getRegion().takeBody(linalgOp->getRegion(0));
630 packedLoopToInnerLoop);
635 linalg::PackOp maybePackedInit =
636 inits[resultNum].getDefiningOp<linalg::PackOp>();
637 if (!maybePackedInit) {
638 results.push_back(
result);
642 unPackOps.push_back(linalg::UnPackOp::create(
643 rewriter, packedLinalgOp->getLoc(),
result, maybePackedInit.getSource(),
644 maybePackedInit.getInnerDimsPos(), maybePackedInit.getMixedTiles()));
645 results.push_back(unPackOps.back().getResult());
653 cast<linalg::LinalgOp>(packedLinalgOp.getOperation()),
682 assert(linalgOp == opOperand.
getOwner() &&
"linalg op must own the operand");
686 cast<RankedTensorType>(opOperand.
get().
getType()), permutation);
688 assert(tensorType == transposedValue.
getType() &&
689 "expected tensor type mismatch");
694 llvm::map_to_vector(permutation, [](
int64_t i) ->
unsigned {
return i; });
698 permutationMap.
compose(linalgOp.getMatchingIndexingMap(&opOperand));
702 indexingMaps[linalgOp.getIndexingMapIndex(&opOperand)] = transposedMap;
708 auto transposedGenericOp = linalg::GenericOp::create(
712 operandsRef.drop_front(linalgOp.getNumDpsInputs()).
getTypes(),
713 operandsRef.take_front(linalgOp.getNumDpsInputs()),
714 operandsRef.drop_front(linalgOp.getNumDpsInputs()),
716 linalgOp.getIteratorTypesArray());
717 transposedGenericOp.getRegion().takeBody(linalgOp->getRegion(0));
718 rewriter.
replaceOp(linalgOp, transposedGenericOp->getResults());
720 return cast<linalg::LinalgOp>(transposedGenericOp.getOperation());
723FailureOr<PackTransposeResult>
725 linalg::LinalgOp linalgOp, linalg::UnPackOp maybeUnPackOp,
732 linalg::PackOp transposedPackOp =
733 packOp.createTransposedClone(rewriter, loc, innerPerm, outerPerm);
735 if (packOp.hasPureBufferSemantics() || !packOp.getResult().hasOneUse())
738 OpOperand &packUse = *packOp->getUses().begin();
739 if (packUse.
getOwner() != linalgOp) {
741 linalgOp,
"not a single use by the LinalgOp target");
744 (!linalgOp.isDpsInit(&packUse) ||
745 maybeUnPackOp.getSource() != linalgOp.getTiedOpResult(&packUse))) {
747 "not produced by the LinalgOp target");
753 int64_t numLeadingDims = packOp.getSourceRank();
754 int64_t numTrailingDims = packOp.getInnerDimsPos().size();
758 if (permutation.empty())
759 llvm::append_range(permutation, llvm::seq<int64_t>(0, numLeadingDims));
761 if (innerPerm.empty()) {
764 llvm::seq<int64_t>(numLeadingDims, numLeadingDims + numTrailingDims));
766 llvm::append_range(permutation,
767 llvm::map_range(innerPerm, [&](
int64_t pos) {
768 return numLeadingDims + pos;
780 rewriter, linalgOp, packUse, permutation, transposedPackOp.getResult());
783 linalg::UnPackOp transposedUnPackOp;
786 transposedLinalgOp->getOpOperand(packUseOperandNumber);
787 OpResult transposedResult = transposedLinalgOp.getTiedOpResult(&opOperand);
789 transposedUnPackOp = maybeUnPackOp.createTransposedClone(
790 rewriter, loc, transposedResult, innerPerm, outerPerm);
792 rewriter.
replaceOp(maybeUnPackOp, transposedUnPackOp->getResults());
796 if (packOp.hasPureTensorSemantics())
797 rewriter.
replaceOp(packOp, transposedPackOp->getResults());
822 assert(mnkPackedSizes.size() == 3 &&
"unexpected num of packing sizes");
823 assert((mnkPaddedSizesNextMultipleOf.empty() ||
824 mnkPaddedSizesNextMultipleOf.size() == 3) &&
825 "num of packing sizes next multiple should be empty or of size 3");
826 assert(mnkOrder.size() == 3 &&
"unexpected mnkOrder size");
829 int64_t numLoops = linalgOp.getNumLoops();
831 LDBG() <<
"need 3+ loops to find a matmul to pack, got " << numLoops
832 <<
" in: " << linalgOp;
834 linalgOp,
"need 3+ loops to find a matmul to pack");
838 int64_t numPackedDims = mnkPackedSizes.size();
840 for (
int64_t i = 0, e = numPackedDims; i < e; ++i)
841 mmnnkkPos[i] = numLoops - numPackedDims + mnkOrder[i];
843 for (
int64_t i = 0, e = numPackedDims; i < e; ++i)
844 packedSizes[mnkOrder[i]] = mnkPackedSizes[i];
846 for (
int64_t i = 0, e = numPackedDims; i < e; ++i) {
847 paddedSizesNextMultipleOf[mnkOrder[i]] =
848 mnkPaddedSizesNextMultipleOf.empty() ? 0
849 : mnkPaddedSizesNextMultipleOf[i];
853 FailureOr<ContractionDimensions> maybeDimensions =
855 if (failed(maybeDimensions)) {
856 LDBG() <<
"couldn't infer matmul iterators in: " << linalgOp;
858 "couldn't infer matmul iterators");
866 int64_t mPos = maybeDimensions->m.back(), nPos = maybeDimensions->n.back(),
867 kPos = maybeDimensions->k.back();
868 LDBG() <<
"Start packing generic op greedily with (m@" << mPos <<
", n@"
869 << nPos <<
", k@" << kPos <<
"): " << linalgOp;
872 auto genericOp = dyn_cast<GenericOp>(linalgOp.getOperation());
874 FailureOr<LinalgOp> generalizeResult =
876 assert(succeeded(generalizeResult) &&
877 isa<GenericOp>(generalizeResult->getOperation()) &&
878 "unexpected failure generalizing op");
879 genericOp = cast<GenericOp>(generalizeResult->getOperation());
887 LDBG() <<
"perm: " << llvm::interleaved(permutation);
890 FailureOr<GenericOp> interchangeResult =
892 assert(succeeded(interchangeResult) &&
"unexpected failure interchanging op");
893 genericOp = *interchangeResult;
894 LDBG() <<
"Generalized Op to pack: " << genericOp;
911 cast<LinalgOp>(genericOp.getOperation())
912 .createLoopRanges(rewriter, genericOp.getLoc());
916 LDBG() <<
"paddedSizesNextMultipleOf: "
917 << llvm::interleaved(paddedSizesNextMultipleOf);
918 LDBG() <<
"loopRanges: "
919 << llvm::interleaved(
920 llvm::map_range(loopRanges, [](
Range r) {
return r.
size; }));
923 for (
int64_t i = 0, e = numPackedDims; i < e; ++i) {
924 if (paddedSizesNextMultipleOf[i] == 0) {
925 adjustedPackedSizes.push_back(packedSizes[i]);
932 rewriter, genericOp->getLoc(), d0.
ceilDiv(s0) * s0,
933 {loopRanges[adjustedPackedSizes.size()].size,
934 rewriter.getIndexAttr(paddedSizesNextMultipleOf[i])}));
936 LDBG() <<
"adjustedPackedSizes: " << llvm::interleaved(adjustedPackedSizes);
942 return pack(rewriter, genericOp, adjustedPackedSizes);
955 b.setInsertionPointToStart(
956 &op->getParentOfType<func::FuncOp>().getBody().front());
957 return llvm::map_to_vector<4>(tileSizes, [&](
int64_t s) {
975 auto padValue = padOp.getConstantPaddingValue();
978 if (padValue.getParentBlock() == &padOp.getRegion().front())
980 return FillOp::create(rewriter, padOp.getLoc(), padValue, dest).result();
984 auto generateOp = tensor::GenerateOp::create(rewriter, padOp.getLoc(),
985 padOp.getResultType(), dynSizes);
988 padOp.getRegion().cloneInto(&generateOp.getRegion(), bvm);
997 if (
auto val = llvm::dyn_cast_if_present<Value>(ofr))
1000 rewriter, padOp.getLoc(),
1001 cast<IntegerAttr>(cast<Attribute>(ofr)).getInt())
1005 auto resultType = padOp.getResultType();
1009 for (
unsigned dim = 0; dim < resultType.getRank(); ++dim) {
1010 if (resultType.isDynamicDim(dim)) {
1012 padOp.getSource(), dim));
1015 padOp.getLoc(), srcSize, getIdxValue(padOp.getMixedLowPad()[dim]));
1017 padOp.getLoc(), plusLow, getIdxValue(padOp.getMixedHighPad()[dim]));
1018 dynSizes.push_back(plusHigh);
1020 staticSizes.push_back(resultType.getDimSize(dim));
1025 tensor::EmptyOp::create(rewriter, padOp.getLoc(), staticSizes,
1026 resultType.getElementType(), dynSizes);
1030 auto sourceType = padOp.getSourceType();
1038 padOp, padOp.getSource(), fill, padOp.getMixedLowPad(), srcSizes,
1046 if (!sliceOp.hasUnitStride())
1049 auto padOp = sliceOp.getSource().getDefiningOp<tensor::PadOp>();
1053 bool zeroSliceGuard =
true;
1055 if (std::optional<bool> control = controlFn(sliceOp))
1056 zeroSliceGuard = *control;
1061 FailureOr<TilingResult> tilingResult =
1063 sliceOp.getMixedSizes(), zeroSliceGuard);
1064 if (failed(tilingResult))
1067 RankedTensorType sourceType = sliceOp.getSourceType();
1068 RankedTensorType resultType = sliceOp.getResultType();
1072 if (sourceType.getRank() == resultType.getRank()) {
1073 rewriter.
replaceOp(sliceOp, tilingResult->tiledValues);
1079 rewriter, sliceOp.getLoc(), tilingResult->tiledValues[0], resultType);
1081 rewriter.
replaceOp(sliceOp, rankReduced);
1091 linalg::PackOp packOp) {
1092 Value input = packOp.getSource();
1096 if (!packOp.hasPureTensorSemantics())
1099 if (!packOp.getPaddingValue()) {
1103 assert(llvm::all_of(packOp.getAllOuterDims(),
1104 [](
int64_t val) { return val == 1; }) &&
1105 "some outer dims are != 1");
1108 ShapedType inputType = packOp.getSourceType();
1109 int64_t inputRank = inputType.getRank();
1112 packOp.getDimAndTileMapping();
1119 for (
int64_t dimIdx = 0; dimIdx < inputRank; ++dimIdx) {
1122 if (!tileAndPosMapping.count(dimIdx)) {
1123 int64_t inputDimSize = inputType.getDimSize(dimIdx);
1124 assert(inputDimSize == 1 &&
1125 "with all outer dims == 1, this non-tiled input dim should be 1!");
1126 paddedShape.push_back(inputDimSize);
1133 OpFoldResult tileSizeForDim = tileAndPosMapping.lookup(dimIdx);
1137 if (cstTileSize.has_value()) {
1138 paddedShape.push_back(cstTileSize.value());
1143 paddedShape.push_back(ShapedType::kDynamic);
1146 dynamicTileSizes.push_back(llvm::dyn_cast<Value>(tileSizeForDim));
1149 RankedTensorType::get(paddedShape, inputType.getElementType());
1151 false, loc, builder,
1159static SmallVector<int64_t>
1161 constexpr int64_t kNonTiledMarker = -1;
1163 for (
auto [
index, value] : llvm::enumerate(perm))
1166 vec, [&](
int64_t v) {
return v != kNonTiledMarker; });
1173static SmallVector<int64_t>
1182 for (
auto i : llvm::seq<unsigned>(0, unpackedRank)) {
1183 if (llvm::is_contained(innerDimsPos, i)) {
1184 innerDims.push_back(dim++);
1189 outerDims.push_back(dim++);
1190 if (!outerDimsPerm.empty())
1191 rankReducedOuterDimsPerm.push_back(outerDimsPerm[i]);
1202 rankReducedOuterDimsPerm =
1204 if (!rankReducedOuterDimsPerm.empty())
1208 perm.append(innerDims);
1218 if (!packOp.hasPureTensorSemantics())
1221 if (llvm::any_of(packOp.getTiledOuterDims(),
1222 [](
int64_t dim) { return dim != 1; })) {
1224 packOp,
"not all outer dimensions of the result are 1s");
1232 if (packOp.getPaddingValue() &&
1233 llvm::any_of(packOp.getAllOuterDims(),
1234 [](
int64_t dim) { return dim != 1; })) {
1236 packOp,
"cannot decompose padded pack with a non-unit un-tiled outer "
1241 auto outerDimsPerm = packOp.getOuterDimsPerm();
1247 if (!llvm::all_of(outerDimsPerm, [&innerDimsPos, &packOp](
int64_t dim) {
1248 static int prev = 0;
1250 if (llvm::is_contained(innerDimsPos, dim))
1255 if (dim < prev && (packOp.getResult().getType().getShape()[prev] != 1 ||
1256 packOp.getResult().getType().getShape()[dim] != 1))
1263 packOp,
"At least one non-unit and un-tiled outer dim is permuted, "
1264 "this is not supported ATM!");
1269 int64_t srcRank = packOp.getSourceRank();
1288 for (
int64_t i = 0; i < srcRank; i++) {
1296 if (llvm::is_contained(innerDimsPos, i))
1298 srcPermForTranspose.push_back(i);
1300 srcPermForTranspose.append(innerDimsPos.begin(), innerDimsPos.end());
1304 ShapedType inputTy = cast<ShapedType>(input.
getType());
1306 for (
int64_t i = 0; i < srcRank; i++) {
1307 if (llvm::is_contained(innerDimsPos, i)) {
1311 if (inputTy.isStaticDim(i))
1312 shapeForEmptyOp.push_back(rewriter.
getIndexAttr(inputTy.getShape()[i]));
1314 shapeForEmptyOp.emplace_back(
1315 tensor::DimOp::create(rewriter, loc, input, i).getResult());
1317 shapeForEmptyOp.append(packOp.getMixedTiles());
1324 llvm::transform(shapeForEmptyOp, shapeForEmptyOp.begin(),
1326 if (auto val = llvm::dyn_cast<Value>(ofr))
1327 return getAsOpFoldResult(val);
1331 LDBG() <<
"Pack permutation: " << packOp;
1332 LDBG() <<
"perm: " << llvm::interleaved(srcPermForTranspose);
1333 LDBG() <<
"Shape of empty tensor: " << llvm::interleaved(shapeForEmptyOp);
1335 Value empty = tensor::EmptyOp::create(
1336 rewriter, loc, shapeForEmptyOp, packOp.getSourceType().getElementType());
1339 auto transposedOp = linalg::TransposeOp::create(rewriter, loc, input, empty,
1340 srcPermForTranspose);
1352 for (
auto size : packOp.getAllOuterDims()) {
1356 for (
auto tileSize : packOp.getMixedTiles()) {
1357 auto [_, tileSizeOfr] =
1359 writeSizes.push_back(tileSizeOfr);
1362 auto insert = tensor::InsertSliceOp::create(
1363 rewriter, loc, transposedOp.getResult()[0], packOp.getDest(), writeSizes);
1366 rewriter.
replaceOp(packOp, insert.getResult());
1373 if (!unpackOp.hasPureTensorSemantics())
1376 int64_t destRank = unpackOp.getDestRank();
1379 if (llvm::any_of(unpackOp.getTiledOuterDims(),
1380 [](
int64_t dim) { return dim != 1; })) {
1383 "require the tiled outer dimensions of the result are all 1s");
1389 Value source = unpackOp.getSource();
1391 unpackOp.getDimAndTileMapping();
1410 for (
auto i : llvm::seq<unsigned>(0, destRank)) {
1419 if (dimAndTileMapping.count(i)) {
1420 extractSliceSizes.push_back(oneIdxAttr);
1426 if (ShapedType::isDynamic(srcShape[i])) {
1428 tensor::DimOp::create(rewriter, loc, source, i).getResult();
1429 extractSliceSizes.push_back(dynamicDim);
1430 shapeForEmptyOp.push_back(dynamicDim);
1432 extractSliceSizes.push_back(rewriter.
getIndexAttr(srcShape[i]));
1433 if (srcShape[i] != 1)
1434 shapeForEmptyOp.push_back(rewriter.
getIndexAttr(srcShape[i]));
1438 if (srcShape[i] != 1) {
1439 readShapeForExtractSlice.push_back(srcShape[i]);
1444 auto mixedTiles = unpackOp.getMixedTiles();
1445 extractSliceSizes.append(mixedTiles.begin(), mixedTiles.end());
1446 shapeForEmptyOp.append(mixedTiles.begin(), mixedTiles.end());
1450 auto tileShape = srcShape.drop_front(destRank);
1452 readShapeForExtractSlice.append(tileShape.begin(), tileShape.end());
1453 Type elemType = unpackOp.getSourceType().getElementType();
1454 auto readType = RankedTensorType::get(readShapeForExtractSlice, elemType);
1455 Value innerTile = tensor::ExtractSliceOp::create(
1456 rewriter, loc, readType, unpackOp.getSource(), extractSliceSizes);
1460 srcShape.take_front(destRank), innerDimsPos, unpackOp.getOuterDimsPerm());
1466 tensor::EmptyOp::create(rewriter, loc, shapeForEmptyOp, elemType);
1468 linalg::TransposeOp::create(rewriter, loc, innerTile, empty, perm);
1474 for (
auto i : llvm::seq<unsigned>(0, destRank)) {
1475 if (dimAndTileMapping.count(i) || destShape[i] != 1)
1476 tileSizes.push_back(
1481 tensor::ExtractSliceOp::create(rewriter, loc, RankedTensorType(),
1482 transposedOp.getResult()[0], tileSizes);
1486 for (
int i = 0, idx = 0; i < destRank; ++i) {
1487 if (dimAndTileMapping.count(i) || destShape[i] != 1)
1488 writeSizes.push_back(tileSizes[idx++]);
1490 writeSizes.push_back(oneIdxAttr);
1492 auto insert = tensor::InsertSliceOp::create(rewriter, loc, partialTile,
1493 unpackOp.getDest(), writeSizes);
1494 rewriter.
replaceOp(unpackOp, insert.getResult());
1508 for (
unsigned dim : dims) {
1512 resultIndices.push_back(i);
1517 return resultIndices;
1525 auto tensorType = cast<RankedTensorType>(
tensor.getType());
1526 int64_t rank = tensorType.getRank();
1530 for (
int64_t i = 0; i < rank; ++i) {
1531 if (!llvm::is_contained(dimsToRemove, i))
1532 newShape.push_back(tensorType.getDimSize(i));
1535 auto newType = RankedTensorType::get(newShape, tensorType.getElementType());
1543static std::optional<AffineExpr>
1547 bool onlyReferencesDroppedDims =
true;
1548 for (
unsigned d = 0; d < newNumDims + dimsToDrop.size(); ++d) {
1550 onlyReferencesDroppedDims =
false;
1554 if (onlyReferencesDroppedDims && llvm::any_of(dimsToDrop, [&](
unsigned d) {
1557 return std::nullopt;
1562 unsigned newDimIdx = 0;
1563 for (
unsigned d = 0; d < newNumDims + dimsToDrop.size(); ++d) {
1564 if (llvm::is_contained(dimsToDrop, d)) {
1578 if (failed(maybeDims))
1582 if (maybeDims->outputImage.size() != 2 || maybeDims->filterLoop.size() != 2)
1585 if (op.hasPureBufferSemantics())
1589 unsigned outSpatial0 = maybeDims->outputImage[0];
1590 unsigned outSpatial1 = maybeDims->outputImage[1];
1591 unsigned filterSpatial0 = maybeDims->filterLoop[0];
1592 unsigned filterSpatial1 = maybeDims->filterLoop[1];
1596 int64_t outSize0 = loopRanges[outSpatial0];
1597 int64_t outSize1 = loopRanges[outSpatial1];
1598 int64_t filterSize0 = loopRanges[filterSpatial0];
1599 int64_t filterSize1 = loopRanges[filterSpatial1];
1602 bool canRemoveSpatial0 = (filterSize0 == 1 && outSize0 == 1);
1603 bool canRemoveSpatial1 = (filterSize1 == 1 && outSize1 == 1);
1604 if (!canRemoveSpatial0 && !canRemoveSpatial1)
1611 if (canRemoveSpatial0) {
1612 loopDimsToRemove.push_back(outSpatial0);
1613 loopDimsToRemove.push_back(filterSpatial0);
1615 loopDimsToRemove.push_back(outSpatial1);
1616 loopDimsToRemove.push_back(filterSpatial1);
1618 llvm::sort(loopDimsToRemove);
1623 unsigned numDims = op.getNumLoops();
1624 unsigned newNumDims = numDims - loopDimsToRemove.size();
1625 for (
AffineMap map : op.getIndexingMapsArray()) {
1631 newResults.push_back(*newExpr);
1633 newMaps.push_back(
AffineMap::get(newNumDims, 0, newResults, ctx));
1638 auto iterTypes = op.getIteratorTypesArray();
1639 for (
unsigned idx = 0; idx < iterTypes.size(); ++idx) {
1640 if (!llvm::is_contained(loopDimsToRemove, idx))
1641 newIterTypes.push_back(iterTypes[idx]);
1647 for (
OpOperand *input : op.getDpsInputOperands()) {
1648 AffineMap map = op.getMatchingIndexingMap(input);
1652 tensorDimsToRemove);
1653 newInputs.push_back(reduced);
1656 OpOperand &output = *op.getDpsInitsMutable().begin();
1657 AffineMap outputMap = op.getMatchingIndexingMap(&output);
1661 outputDimsToRemove);
1666 newInputs, newOutput, newMaps, newIterTypes);
1668 newOp.getRegion().begin());
1672 LinalgOp resultOp = newOp;
1673 if (!isa<GenericOp>(op)) {
1675 if (succeeded(specializedOp))
1676 resultOp = *specializedOp;
1681 rewriter, loc, resultOp->getResult(0), output.
get());
1689struct DownscaleSizeOneWindowedConvolution final
1691 DownscaleSizeOneWindowedConvolution(
MLIRContext *context,
1695 LogicalResult matchAndRewrite(LinalgOp op,
1704 patterns.
add<DownscaleSizeOneWindowedConvolution>(patterns.
getContext(),
Base type for affine expression.
bool isFunctionOfDim(unsigned position) const
Return true if the affine expression involves AffineDimExpr position.
AffineExpr replaceDims(ArrayRef< AffineExpr > dimReplacements) const
Dim-only version of replaceDimsAndSymbols.
AffineExpr ceilDiv(uint64_t v) const
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
MLIRContext * getContext() const
static AffineMap 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 ... numDims) by dims[offset + shift ... shift + numDims).
AffineMap insertResult(AffineExpr expr, unsigned pos) const
Returns a new AffineMap with the same number of dims and symbols and an extra result inserted at pos.
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
Attributes are known-constant values of operations.
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
TypedAttr getZeroAttr(Type type)
AffineExpr getAffineDimExpr(unsigned position)
MLIRContext * getContext() const
This is a utility class for mapping one set of IR entities to another.
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 createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
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.
This is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
result_range getResults()
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This is a builder type that keeps local references to arguments.
Builder & setShape(ArrayRef< int64_t > newShape)
MLIRContext * getContext() const
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...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void moveOpBefore(Operation *op, Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
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,...
void inlineRegionBefore(Region ®ion, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
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 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.
type_range getTypes() const
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.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Operation * getOwner() const
Return the owner of this operand.
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...
SmallVector< int64_t > getUnPackInverseSrcPerm(linalg::UnPackOp, PackingMetadata &metadata)
Compute inverse permutation for the source tensor (i.e.
FailureOr< PackTransposeResult > packTranspose(RewriterBase &rewriter, linalg::PackOp packOp, linalg::LinalgOp linalgOp, linalg::UnPackOp maybeUnPackOp, ArrayRef< int64_t > outerPerm, ArrayRef< int64_t > innerPerm)
Transpose a single PackOp -> LinalgOp -> UnPackOp chain and return the transposed PackOp -> LinalgOp ...
FailureOr< LowerUnPackOpResult > lowerUnPack(RewriterBase &rewriter, linalg::UnPackOp unPackOp, bool lowerUnpadLikeWithExtractSlice=true)
Rewrite pack as empty + transpose + reshape + extract_slice + copy.
void peelLoops(RewriterBase &rewriter, ArrayRef< scf::ForOp > loops)
Peel 'loops' and applies affine_min/max bounds simplification on the fly where relevant.
FailureOr< ConvolutionDimensions > inferConvolutionDims(LinalgOp linalgOp)
Find at least 1 parallel (output_image) and reduction (filter_loop) dimension candidates that form a ...
FailureOr< LinalgOp > generalizeNamedOp(RewriterBase &rewriter, LinalgOp linalgOp, bool emitCategoryOps=false)
Create a GenericOp or CategoryOp from the given named operation linalgOp and replace the given linalg...
FailureOr< LinalgOp > specializeGenericOp(RewriterBase &rewriter, GenericOp genericOp, bool emitCategoryOps=false)
Replace the given GenericOp with a namedOp or categoryOp.
void populateDecomposeConvolutionPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Linalg decompose convolutions patterns.
LogicalResult vectorizeCopy(RewriterBase &builder, memref::CopyOp copyOp)
Emit a suitable vector form for a Copy op with fully static shape.
FailureOr< GenericOp > interchangeGenericOp(RewriterBase &rewriter, GenericOp genericOp, ArrayRef< unsigned > interchangeVector)
Interchange the iterator_types and iterator_maps dimensions and adapts the index accesses of op.
SmallVector< int64_t > getPackInverseDestPerm(linalg::PackOp packOp, PackingMetadata &metadata)
Compute inverse permutation for the destination tensor (i.e.
void populateDecomposePackUnpackPatterns(RewritePatternSet &patterns)
Populates patterns to decompose linalg.pack and linalg.unpack Ops into e.g.
FailureOr< ContractionDimensions > inferContractionDims(LinalgOp linalgOp)
Find at least 2 parallel (m and n) and 1 reduction (k) dimension candidates that form a matmul subcom...
FailureOr< PackResult > packMatmulGreedily(RewriterBase &rewriter, LinalgOp linalgOp, ArrayRef< OpFoldResult > mnkPackedSizes, ArrayRef< int64_t > mnkPaddedSizesNextMultipleOf, ArrayRef< int64_t > mnkOrder)
Pack a LinalgOp by greedily inferring matmul dimensions (m, n, k) where m and n are proper parallel d...
FailureOr< PackResult > pack(RewriterBase &rewriter, linalg::LinalgOp linalgOp, ArrayRef< OpFoldResult > packedSizes)
Implement packing of a single LinalgOp by packedSizes.
SmallVector< Value > peelLoop(RewriterBase &rewriter, Operation *op)
Try to peel and canonicalize loop op and return the new result.
void populateDecomposePadPatterns(RewritePatternSet &patterns)
Populates patterns to decompose tensor.pad into e.g.
FailureOr< LinalgOp > downscaleSizeOneWindowedConvolution(RewriterBase &rewriter, LinalgOp op)
Rewrite convolution/pooling/depthwise ops with size-1 window dimensions into lower-dimensional ops.
FailureOr< LowerPackResult > lowerPack(RewriterBase &rewriter, linalg::PackOp packOp, bool lowerPadLikeWithInsertSlice=true)
Rewrite pack as pad + reshape + transpose.
LogicalResult peelForLoopAndSimplifyBounds(RewriterBase &rewriter, ForOp forOp, scf::ForOp &partialIteration)
Rewrite a for loop with bounds/step that potentially do not divide evenly into a for loop where the s...
FailureOr< TilingResult > bubbleUpPadSlice(OpBuilder &b, tensor::PadOp padOp, ArrayRef< OpFoldResult > offsets, ArrayRef< OpFoldResult > sizes, bool generateZeroSliceGuard=true)
Bubbles up a slice of this pad by taking the slice first and then performing the padding.
PadOp createPadHighOp(RankedTensorType resType, Value source, Value pad, bool nofold, Location loc, OpBuilder &builder, ValueRange dynOutDims={})
Value createCanonicalRankReducingInsertSliceOp(OpBuilder &b, Location loc, Value tensor, Value dest)
Create a rank-reducing InsertSliceOp @[0 .
Value createCanonicalRankReducingExtractSliceOp(OpBuilder &b, Location loc, Value tensor, RankedTensorType targetType)
Create a rank-reducing ExtractSliceOp @[0 .
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Include the generated interface declarations.
SliceVerificationResult
Enum that captures information related to verifier error conditions on slice insert/extract type of o...
ArrayRef< int64_t > ReassociationIndicesRef
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
SmallVector< int64_t > computePermutationVector(int64_t permSize, ArrayRef< int64_t > positions, ArrayRef< int64_t > desiredPositions)
Return a permutation vector of size permSize that would result in moving positions into desiredPositi...
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
SmallVector< Loops, 8 > tile(ArrayRef< scf::ForOp > forOps, ArrayRef< Value > sizes, ArrayRef< scf::ForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
std::pair< int64_t, OpFoldResult > getSimplifiedOfrAndStaticSizePair(OpFoldResult ofr, Builder &b)
Given OpFoldResult representing dim size value (*), generates a pair of sizes:
void applyPermutationToVector(SmallVector< T, N > &inVec, ArrayRef< int64_t > permutation)
Apply the permutation defined by permutation to inVec.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
SliceVerificationResult isRankReducedType(ShapedType originalType, ShapedType candidateReducedType)
Check if originalType can be rank reduced to candidateReducedType type by dropping some dimensions wi...
bool isPermutationVector(ArrayRef< int64_t > interchange)
Method to check if an interchange vector is a permutation.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
OpInterfaceRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting a...
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...
LogicalResult matchAndRewrite(memref::CopyOp copyOp, PatternRewriter &rewriter) const override
Rewrites a linalg::PackOp into a sequence of:
LogicalResult matchAndRewrite(linalg::PackOp packOp, PatternRewriter &rewriter) const override
Rewrites a linalg::UnPackOp into a sequence of:
LogicalResult matchAndRewrite(linalg::UnPackOp unpackOp, PatternRewriter &rewriter) const override
Rewrite a tensor::PadOp into a sequence of EmptyOp, FillOp and InsertSliceOp.
LogicalResult matchAndRewrite(tensor::PadOp padOp, PatternRewriter &rewriter) const override
Value createFillOrGenerateOp(RewriterBase &rewriter, tensor::PadOp padOp, Value dest, const SmallVector< Value > &dynSizes) const
Filling dest using FillOp constant padding value if possible.
LinalgTilingOptions & setTileSizes(const SmallVector< Value, 4 > &ts)
Set the tileSizeComputationFunction to return the values ts.
TileSizeComputationFunction tileSizeComputationFunction
Computation function that returns the tile sizes for each operation.
Struct to hold the result of a pack call.
Struct to hold the result of a packTranspose call.