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;
222 linalg::PackOp packOp,
223 bool lowerPadLikeWithInsertSlice) {
225 if (!packOp.hasPureTensorSemantics())
228 auto packedTensorType =
229 cast<RankedTensorType>(packOp->getResultTypes().front());
237 PackingMetadata packingMetadata;
260 for (
auto [pos, innerSize] :
261 llvm::zip_equal(packOp.getInnerDimsPos(), packOp.getMixedTiles())) {
263 packedToStripMinedShapePerm[packingMetadata.outerPositions[pos]];
273 rewriter, loc, map, {outerSize, origSize, innerSize});
275 RankedTensorType collapsed = tensor::CollapseShapeOp::inferCollapsedType(
277 packingMetadata.reassociations);
278 Value paddingValue = packOp.getPaddingValue();
280 paddingValue = arith::ConstantOp::create(
284 tensor::PadOp::create(rewriter, loc, collapsed, packOp.getSource(), lows,
285 highs, paddingValue,
false);
287 LDBG() <<
"insertPositions: "
288 << llvm::interleaved(packingMetadata.insertPositions);
289 LDBG() <<
"outerPositions: "
290 << llvm::interleaved(packingMetadata.outerPositions);
291 LDBG() <<
"packedShape: " << llvm::interleaved(packedTensorType.getShape());
292 LDBG() <<
"packedToStripMinedShapePerm: "
293 << llvm::interleaved(packedToStripMinedShapePerm);
294 LDBG() <<
"reassociations: "
295 << llvm::interleaved(llvm::map_range(packingMetadata.reassociations,
297 LDBG() <<
"stripMinedShape: " << llvm::interleaved(stripMinedShape);
298 LDBG() <<
"collapsed type: " << collapsed;
300 if (lowerPadLikeWithInsertSlice && packOp.isLikePad()) {
319 auto insertSliceOp = tensor::InsertSliceOp::create(
320 rewriter, loc, padOp, packOp.getDest(),
323 LDBG() <<
"insert_slice op: " << insertSliceOp;
325 rewriter.
replaceOp(packOp, insertSliceOp->getResults());
333 auto expandShapeResultType =
335 auto reshapeOp = tensor::ExpandShapeOp::create(
336 rewriter, loc, expandShapeResultType, padOp.getResult(),
337 packingMetadata.reassociations, stripMinedMixedSizes);
342 auto transposeOp = linalg::TransposeOp::create(
343 rewriter, loc, reshapeOp.getResult(), packOp.getDest(), transpPerm);
345 LDBG() <<
"reshape op: " << reshapeOp;
346 LDBG() <<
"transpPerm: " << llvm::interleaved(transpPerm);
347 LDBG() <<
"transpose op: " << transposeOp;
350 rewriter.
replaceOp(packOp, transposeOp->getResults());
355FailureOr<LowerUnPackOpResult>
357 bool lowerUnpadLikeWithExtractSlice) {
359 if (!unPackOp.hasPureTensorSemantics())
366 auto packedTensorType = cast<RankedTensorType>(unPackOp.getSourceType());
367 int64_t packedRank = packedTensorType.getRank();
370 auto destTensorType = cast<RankedTensorType>(unPackOp.getDest().getType());
371 if (lowerUnpadLikeWithExtractSlice && unPackOp.isLikeUnPad()) {
380 auto extractSliceOp = tensor::ExtractSliceOp::create(
381 rewriter, loc, destTensorType, unPackOp.getSource(),
385 rewriter.
replaceOp(unPackOp, extractSliceOp->getResults());
388 nullptr, extractSliceOp,
394 PackingMetadata packingMetadata;
404 RankedTensorType stripMinedTensorType =
406 RankedTensorType collapsedType = tensor::CollapseShapeOp::inferCollapsedType(
407 stripMinedTensorType, packingMetadata.reassociations);
414 auto emptyOp = tensor::EmptyOp::create(rewriter, loc, dims,
415 stripMinedTensorType.getElementType());
417 linalg::TransposeOp::create(rewriter, loc, unPackOp.getSource(), emptyOp,
418 packedToStripMinedShapePerm);
420 LDBG() <<
"insertPositions: "
421 << llvm::interleaved(packingMetadata.insertPositions);
422 LDBG() <<
"packedShape: " << llvm::interleaved(packedTensorType.getShape());
423 LDBG() <<
"packedToStripMinedShapePerm: "
424 << llvm::interleaved(packedToStripMinedShapePerm);
425 LDBG() <<
"reassociations: "
426 << llvm::interleaved(llvm::map_range(packingMetadata.reassociations,
428 LDBG() <<
"stripMinedShape: " << llvm::interleaved(stripMinedShape);
429 LDBG() <<
"collapsed type: " << collapsedType;
432 auto reshapeOp = tensor::CollapseShapeOp::create(
433 rewriter, loc, collapsedType, transposeOp->getResult(0),
434 packingMetadata.reassociations);
437 int64_t destRank = destTensorType.getRank();
438 auto extractSliceOp = tensor::ExtractSliceOp::create(
439 rewriter, loc, destTensorType, reshapeOp->getResult(0),
445 auto copyOp = linalg::CopyOp::create(
446 rewriter, loc, extractSliceOp->getResult(0), unPackOp.getDest());
449 rewriter.
replaceOp(unPackOp, copyOp->getResults());
456PackedOperandsDimList::extractPackedDimsForOperand(
int64_t operandPos) {
458 for (
auto &i : spec) {
459 if (!i.packedDimForEachOperand[operandPos].has_value())
461 res.push_back(i.packedDimForEachOperand[operandPos].value());
466SmallVector<OpFoldResult>
467PackedOperandsDimList::extractPackSizesForOperand(int64_t operandPos) {
468 SmallVector<OpFoldResult> res;
469 for (
auto &i : spec) {
470 if (!i.packedDimForEachOperand[operandPos].has_value())
472 res.push_back(i.packedSize);
481 linalg::LinalgOp linalgOp,
483 if (packedSizes.size() != linalgOp.getNumLoops()) {
485 "incorrect number of pack sizes");
487 if (!linalgOp.hasPureTensorSemantics()) {
489 linalgOp,
"expects LinalgOp with pure tensor semantics");
495 linalgOp.getIteratorTypesArray();
496 LDBG() <<
"Start packing: " << linalgOp;
497 LDBG() <<
"maps: " << llvm::interleaved(indexingMaps);
498 LDBG() <<
"iterators: " << llvm::interleaved(iteratorTypes);
503 PackedOperandsDimList listOfPackedOperandsDim;
504 for (
int64_t i = 0, e = packedSizes.size(); i < e; ++i) {
507 if (maybeConstant.has_value() && maybeConstant.value() == 0)
510 PackedOperandsDim packedOperandsDims;
511 packedOperandsDims.packedSize = packedSizes[i];
512 FailureOr<SmallVector<std::optional<int64_t>>>
513 maybePackedDimForEachOperand =
515 if (failed(maybePackedDimForEachOperand))
517 packedOperandsDims.packedDimForEachOperand = *maybePackedDimForEachOperand;
519 LDBG() <<
"++++ After pack size #" << i <<
": " << packedSizes[i];
520 LDBG() <<
"maps: " << llvm::interleaved(indexingMaps);
521 LDBG() <<
"iterators: " << llvm::interleaved(iteratorTypes);
522 LDBG() <<
"packedDimForEachOperand: "
523 << llvm::interleaved(packedOperandsDims.packedDimForEachOperand);
525 listOfPackedOperandsDim.pushBack(std::move(packedOperandsDims));
531 llvm::to_vector(llvm::make_pointer_range(linalgOp.getDpsInitsMutable()));
533 for (
const auto &operandsList : {inputOperands, initOperands}) {
534 for (
OpOperand *opOperand : operandsList) {
535 int64_t pos = opOperand->getOperandNumber();
536 Value operand = opOperand->get();
538 listOfPackedOperandsDim.extractPackedDimsForOperand(pos);
540 listOfPackedOperandsDim.extractPackSizesForOperand(pos);
541 LDBG() <<
"operand: " << operand;
542 LDBG() <<
"innerPos: " << llvm::interleaved(innerPos);
543 LDBG() <<
"innerPackSizes: " << llvm::interleaved(innerPackSizes);
544 if (innerPackSizes.empty()) {
545 inputsAndInits.push_back(operand);
548 Value dest = linalg::PackOp::createDestinationTensor(
549 rewriter, loc, operand, innerPackSizes, innerPos,
551 ShapedType operandType = cast<ShapedType>(operand.
getType());
552 bool areConstantTiles =
556 if (areConstantTiles && operandType.hasStaticShape() &&
557 !linalg::PackOp::requirePaddingValue(
558 operandType.getShape(), innerPos,
559 cast<ShapedType>(dest.
getType()).getShape(), {},
561 packOps.push_back(linalg::PackOp::create(rewriter, loc, operand, dest,
562 innerPos, innerPackSizes));
568 Value zero = arith::ConstantOp::create(rewriter, loc, zeroAttr);
569 packOps.push_back(linalg::PackOp::create(
570 rewriter, loc, operand, dest, innerPos, innerPackSizes, zero));
572 inputsAndInits.push_back(packOps.back().getResult());
578 ValueRange{inputsAndInits}.take_front(linalgOp.getNumDpsInputs());
580 ValueRange{inputsAndInits}.take_back(linalgOp.getNumDpsInits());
581 auto packedLinalgOp =
582 linalg::GenericOp::create(rewriter, linalgOp.getLoc(), inits.
getTypes(),
583 inputs, inits, indexingMaps, iteratorTypes);
584 packedLinalgOp.getRegion().takeBody(linalgOp->getRegion(0));
589 linalg::PackOp maybePackedInit =
590 inits[resultNum].getDefiningOp<linalg::PackOp>();
591 if (!maybePackedInit) {
592 results.push_back(
result);
596 unPackOps.push_back(linalg::UnPackOp::create(
597 rewriter, packedLinalgOp->getLoc(),
result, maybePackedInit.getSource(),
598 maybePackedInit.getInnerDimsPos(), maybePackedInit.getMixedTiles()));
599 results.push_back(unPackOps.back().getResult());
607 cast<linalg::LinalgOp>(packedLinalgOp.getOperation()),
636 assert(linalgOp == opOperand.
getOwner() &&
"linalg op must own the operand");
640 cast<RankedTensorType>(opOperand.
get().
getType()), permutation);
642 assert(tensorType == transposedValue.
getType() &&
643 "expected tensor type mismatch");
648 llvm::map_to_vector(permutation, [](
int64_t i) ->
unsigned {
return i; });
652 permutationMap.
compose(linalgOp.getMatchingIndexingMap(&opOperand));
656 indexingMaps[linalgOp.getIndexingMapIndex(&opOperand)] = transposedMap;
662 auto transposedGenericOp = linalg::GenericOp::create(
666 operandsRef.drop_front(linalgOp.getNumDpsInputs()).
getTypes(),
667 operandsRef.take_front(linalgOp.getNumDpsInputs()),
668 operandsRef.drop_front(linalgOp.getNumDpsInputs()),
670 linalgOp.getIteratorTypesArray());
671 transposedGenericOp.getRegion().takeBody(linalgOp->getRegion(0));
672 rewriter.
replaceOp(linalgOp, transposedGenericOp->getResults());
674 return cast<linalg::LinalgOp>(transposedGenericOp.getOperation());
677FailureOr<PackTransposeResult>
679 linalg::LinalgOp linalgOp, linalg::UnPackOp maybeUnPackOp,
686 linalg::PackOp transposedPackOp =
687 packOp.createTransposedClone(rewriter, loc, innerPerm, outerPerm);
689 if (packOp.hasPureBufferSemantics() || !packOp.getResult().hasOneUse())
692 OpOperand &packUse = *packOp->getUses().begin();
693 if (packUse.
getOwner() != linalgOp) {
695 linalgOp,
"not a single use by the LinalgOp target");
698 (!linalgOp.isDpsInit(&packUse) ||
699 maybeUnPackOp.getSource() != linalgOp.getTiedOpResult(&packUse))) {
701 "not produced by the LinalgOp target");
707 int64_t numLeadingDims = packOp.getSourceRank();
708 int64_t numTrailingDims = packOp.getInnerDimsPos().size();
712 if (permutation.empty())
713 llvm::append_range(permutation, llvm::seq<int64_t>(0, numLeadingDims));
715 if (innerPerm.empty()) {
718 llvm::seq<int64_t>(numLeadingDims, numLeadingDims + numTrailingDims));
720 llvm::append_range(permutation,
721 llvm::map_range(innerPerm, [&](
int64_t pos) {
722 return numLeadingDims + pos;
734 rewriter, linalgOp, packUse, permutation, transposedPackOp.getResult());
737 linalg::UnPackOp transposedUnPackOp;
740 transposedLinalgOp->getOpOperand(packUseOperandNumber);
741 OpResult transposedResult = transposedLinalgOp.getTiedOpResult(&opOperand);
743 transposedUnPackOp = maybeUnPackOp.createTransposedClone(
744 rewriter, loc, transposedResult, innerPerm, outerPerm);
746 rewriter.
replaceOp(maybeUnPackOp, transposedUnPackOp->getResults());
750 if (packOp.hasPureTensorSemantics())
751 rewriter.
replaceOp(packOp, transposedPackOp->getResults());
776 assert(mnkPackedSizes.size() == 3 &&
"unexpected num of packing sizes");
777 assert((mnkPaddedSizesNextMultipleOf.empty() ||
778 mnkPaddedSizesNextMultipleOf.size() == 3) &&
779 "num of packing sizes next multiple should be empty or of size 3");
780 assert(mnkOrder.size() == 3 &&
"unexpected mnkOrder size");
783 int64_t numLoops = linalgOp.getNumLoops();
785 LDBG() <<
"need 3+ loops to find a matmul to pack, got " << numLoops
786 <<
" in: " << linalgOp;
788 linalgOp,
"need 3+ loops to find a matmul to pack");
792 int64_t numPackedDims = mnkPackedSizes.size();
794 for (
int64_t i = 0, e = numPackedDims; i < e; ++i)
795 mmnnkkPos[i] = numLoops - numPackedDims + mnkOrder[i];
797 for (
int64_t i = 0, e = numPackedDims; i < e; ++i)
798 packedSizes[mnkOrder[i]] = mnkPackedSizes[i];
800 for (
int64_t i = 0, e = numPackedDims; i < e; ++i) {
801 paddedSizesNextMultipleOf[mnkOrder[i]] =
802 mnkPaddedSizesNextMultipleOf.empty() ? 0
803 : mnkPaddedSizesNextMultipleOf[i];
807 FailureOr<ContractionDimensions> maybeDimensions =
809 if (failed(maybeDimensions)) {
810 LDBG() <<
"couldn't infer matmul iterators in: " << linalgOp;
812 "couldn't infer matmul iterators");
820 int64_t mPos = maybeDimensions->m.back(), nPos = maybeDimensions->n.back(),
821 kPos = maybeDimensions->k.back();
822 LDBG() <<
"Start packing generic op greedily with (m@" << mPos <<
", n@"
823 << nPos <<
", k@" << kPos <<
"): " << linalgOp;
826 auto genericOp = dyn_cast<GenericOp>(linalgOp.getOperation());
828 FailureOr<GenericOp> generalizeResult =
830 assert(succeeded(generalizeResult) &&
"unexpected failure generalizing op");
831 genericOp = *generalizeResult;
839 LDBG() <<
"perm: " << llvm::interleaved(permutation);
842 FailureOr<GenericOp> interchangeResult =
844 assert(succeeded(interchangeResult) &&
"unexpected failure interchanging op");
845 genericOp = *interchangeResult;
846 LDBG() <<
"Generalized Op to pack: " << genericOp;
863 cast<LinalgOp>(genericOp.getOperation())
864 .createLoopRanges(rewriter, genericOp.getLoc());
868 LDBG() <<
"paddedSizesNextMultipleOf: "
869 << llvm::interleaved(paddedSizesNextMultipleOf);
870 LDBG() <<
"loopRanges: "
871 << llvm::interleaved(
872 llvm::map_range(loopRanges, [](
Range r) {
return r.
size; }));
875 for (
int64_t i = 0, e = numPackedDims; i < e; ++i) {
876 if (paddedSizesNextMultipleOf[i] == 0) {
877 adjustedPackedSizes.push_back(packedSizes[i]);
884 rewriter, genericOp->getLoc(), d0.
ceilDiv(s0) * s0,
885 {loopRanges[adjustedPackedSizes.size()].size,
886 rewriter.getIndexAttr(paddedSizesNextMultipleOf[i])}));
888 LDBG() <<
"adjustedPackedSizes: " << llvm::interleaved(adjustedPackedSizes);
894 return pack(rewriter, genericOp, adjustedPackedSizes);
907 b.setInsertionPointToStart(
908 &op->getParentOfType<func::FuncOp>().getBody().front());
909 return llvm::map_to_vector<4>(tileSizes, [&](
int64_t s) {
927 auto padValue = padOp.getConstantPaddingValue();
930 if (padValue.getParentBlock() == &padOp.getRegion().front())
932 return FillOp::create(rewriter, padOp.getLoc(), padValue, dest).result();
936 auto generateOp = tensor::GenerateOp::create(rewriter, padOp.getLoc(),
937 padOp.getResultType(), dynSizes);
940 padOp.getRegion().cloneInto(&generateOp.getRegion(), bvm);
949 if (
auto val = llvm::dyn_cast_if_present<Value>(ofr))
952 rewriter, padOp.getLoc(),
953 cast<IntegerAttr>(cast<Attribute>(ofr)).getInt())
957 auto resultType = padOp.getResultType();
961 for (
unsigned dim = 0; dim < resultType.getRank(); ++dim) {
962 if (resultType.isDynamicDim(dim)) {
964 padOp.getSource(), dim));
967 padOp.getLoc(), srcSize, getIdxValue(padOp.getMixedLowPad()[dim]));
969 padOp.getLoc(), plusLow, getIdxValue(padOp.getMixedHighPad()[dim]));
970 dynSizes.push_back(plusHigh);
972 staticSizes.push_back(resultType.getDimSize(dim));
977 tensor::EmptyOp::create(rewriter, padOp.getLoc(), staticSizes,
978 resultType.getElementType(), dynSizes);
982 auto sourceType = padOp.getSourceType();
990 padOp, padOp.getSource(), fill, padOp.getMixedLowPad(), srcSizes,
998 if (!sliceOp.hasUnitStride())
1001 auto padOp = sliceOp.getSource().getDefiningOp<tensor::PadOp>();
1005 bool zeroSliceGuard =
true;
1007 if (std::optional<bool> control = controlFn(sliceOp))
1008 zeroSliceGuard = *control;
1013 FailureOr<TilingResult> tilingResult =
1015 sliceOp.getMixedSizes(), zeroSliceGuard);
1016 if (failed(tilingResult))
1019 RankedTensorType sourceType = sliceOp.getSourceType();
1020 RankedTensorType resultType = sliceOp.getResultType();
1024 if (sourceType.getRank() == resultType.getRank()) {
1025 rewriter.
replaceOp(sliceOp, tilingResult->tiledValues);
1031 rewriter, sliceOp.getLoc(), tilingResult->tiledValues[0], resultType);
1033 rewriter.
replaceOp(sliceOp, rankReduced);
1043 linalg::PackOp packOp) {
1044 Value input = packOp.getSource();
1046 if (!packOp.hasPureTensorSemantics())
1049 if (!packOp.getPaddingValue()) {
1053 assert(llvm::all_of(packOp.getAllOuterDims(),
1054 [](
int64_t val) { return val == 1; }) &&
1055 "some outer dims are != 1");
1058 ShapedType inputType = packOp.getSourceType();
1059 int64_t inputRank = inputType.getRank();
1062 packOp.getDimAndTileMapping();
1069 for (
int64_t dimIdx = 0; dimIdx < inputRank; ++dimIdx) {
1072 if (!tileAndPosMapping.count(dimIdx)) {
1073 int64_t inputDimSize = inputType.getDimSize(dimIdx);
1074 assert(inputDimSize == 1 &&
1075 "with all outer dims == 1, this non-tiled input dim should be 1!");
1076 paddedShape.push_back(inputDimSize);
1083 OpFoldResult tileSizeForDim = tileAndPosMapping.lookup(dimIdx);
1087 if (cstTileSize.has_value()) {
1088 paddedShape.push_back(cstTileSize.value());
1093 paddedShape.push_back(ShapedType::kDynamic);
1096 dynamicTileSizes.push_back(llvm::dyn_cast<Value>(tileSizeForDim));
1099 RankedTensorType::get(paddedShape, inputType.getElementType());
1101 false, loc, builder,
1109static SmallVector<int64_t>
1111 constexpr int64_t kNonTiledMarker = -1;
1113 for (
auto [
index, value] : llvm::enumerate(perm))
1116 vec, [&](
int64_t v) {
return v != kNonTiledMarker; });
1123static SmallVector<int64_t>
1132 for (
auto i : llvm::seq<unsigned>(0, unpackedRank)) {
1133 if (llvm::is_contained(innerDimsPos, i)) {
1134 innerDims.push_back(dim++);
1139 outerDims.push_back(dim++);
1140 if (!outerDimsPerm.empty())
1141 rankReducedOuterDimsPerm.push_back(outerDimsPerm[i]);
1152 rankReducedOuterDimsPerm =
1154 if (!rankReducedOuterDimsPerm.empty())
1158 perm.append(innerDims);
1166 if (!packOp.hasPureTensorSemantics())
1169 if (llvm::any_of(packOp.getTiledOuterDims(),
1170 [](
int64_t dim) { return dim != 1; })) {
1172 packOp,
"not all outer dimensions of the result are 1s");
1180 if (packOp.getPaddingValue() &&
1181 llvm::any_of(packOp.getAllOuterDims(),
1182 [](
int64_t dim) { return dim != 1; })) {
1184 packOp,
"cannot decompose padded pack with a non-unit un-tiled outer "
1189 auto outerDimsPerm = packOp.getOuterDimsPerm();
1195 if (!llvm::all_of(outerDimsPerm, [&innerDimsPos, &packOp](
int64_t dim) {
1196 static int prev = 0;
1198 if (llvm::is_contained(innerDimsPos, dim))
1203 if (dim < prev && (packOp.getResult().getType().getShape()[prev] != 1 ||
1204 packOp.getResult().getType().getShape()[dim] != 1))
1211 packOp,
"At least one non-unit and un-tiled outer dim is permuted, "
1212 "this is not supported ATM!");
1217 int64_t srcRank = packOp.getSourceRank();
1236 for (
int64_t i = 0; i < srcRank; i++) {
1244 if (llvm::is_contained(innerDimsPos, i))
1246 srcPermForTranspose.push_back(i);
1248 srcPermForTranspose.append(innerDimsPos.begin(), innerDimsPos.end());
1252 ShapedType inputTy = cast<ShapedType>(input.
getType());
1254 for (
int64_t i = 0; i < srcRank; i++) {
1255 if (llvm::is_contained(innerDimsPos, i)) {
1259 if (inputTy.isStaticDim(i))
1260 shapeForEmptyOp.push_back(rewriter.
getIndexAttr(inputTy.getShape()[i]));
1262 shapeForEmptyOp.emplace_back(
1263 tensor::DimOp::create(rewriter, loc, input, i).getResult());
1265 shapeForEmptyOp.append(packOp.getMixedTiles());
1272 llvm::transform(shapeForEmptyOp, shapeForEmptyOp.begin(),
1274 if (auto val = llvm::dyn_cast<Value>(ofr))
1275 return getAsOpFoldResult(val);
1279 LDBG() <<
"Pack permutation: " << packOp;
1280 LDBG() <<
"perm: " << llvm::interleaved(srcPermForTranspose);
1281 LDBG() <<
"Shape of empty tensor: " << llvm::interleaved(shapeForEmptyOp);
1283 Value empty = tensor::EmptyOp::create(
1284 rewriter, loc, shapeForEmptyOp, packOp.getSourceType().getElementType());
1287 auto transposedOp = linalg::TransposeOp::create(rewriter, loc, input, empty,
1288 srcPermForTranspose);
1300 for (
auto size : packOp.getAllOuterDims()) {
1304 for (
auto tileSize : packOp.getMixedTiles()) {
1305 auto [_, tileSizeOfr] =
1307 writeSizes.push_back(tileSizeOfr);
1310 auto insert = tensor::InsertSliceOp::create(
1311 rewriter, loc, transposedOp.getResult()[0], packOp.getDest(), writeSizes);
1314 rewriter.
replaceOp(packOp, insert.getResult());
1321 if (!unpackOp.hasPureTensorSemantics())
1324 int64_t destRank = unpackOp.getDestRank();
1327 if (llvm::any_of(unpackOp.getTiledOuterDims(),
1328 [](
int64_t dim) { return dim != 1; })) {
1331 "require the tiled outer dimensions of the result are all 1s");
1337 Value source = unpackOp.getSource();
1339 unpackOp.getDimAndTileMapping();
1358 for (
auto i : llvm::seq<unsigned>(0, destRank)) {
1367 if (dimAndTileMapping.count(i)) {
1368 extractSliceSizes.push_back(oneIdxAttr);
1374 if (ShapedType::isDynamic(srcShape[i])) {
1376 tensor::DimOp::create(rewriter, loc, source, i).getResult();
1377 extractSliceSizes.push_back(dynamicDim);
1378 shapeForEmptyOp.push_back(dynamicDim);
1380 extractSliceSizes.push_back(rewriter.
getIndexAttr(srcShape[i]));
1381 if (srcShape[i] != 1)
1382 shapeForEmptyOp.push_back(rewriter.
getIndexAttr(srcShape[i]));
1386 if (srcShape[i] != 1) {
1387 readShapeForExtractSlice.push_back(srcShape[i]);
1392 auto mixedTiles = unpackOp.getMixedTiles();
1393 extractSliceSizes.append(mixedTiles.begin(), mixedTiles.end());
1394 shapeForEmptyOp.append(mixedTiles.begin(), mixedTiles.end());
1398 auto tileShape = srcShape.drop_front(destRank);
1400 readShapeForExtractSlice.append(tileShape.begin(), tileShape.end());
1401 Type elemType = unpackOp.getSourceType().getElementType();
1402 auto readType = RankedTensorType::get(readShapeForExtractSlice, elemType);
1403 Value innerTile = tensor::ExtractSliceOp::create(
1404 rewriter, loc, readType, unpackOp.getSource(), extractSliceSizes);
1408 srcShape.take_front(destRank), innerDimsPos, unpackOp.getOuterDimsPerm());
1414 tensor::EmptyOp::create(rewriter, loc, shapeForEmptyOp, elemType);
1416 linalg::TransposeOp::create(rewriter, loc, innerTile, empty, perm);
1422 for (
auto i : llvm::seq<unsigned>(0, destRank)) {
1423 if (dimAndTileMapping.count(i) || destShape[i] != 1)
1424 tileSizes.push_back(
1429 tensor::ExtractSliceOp::create(rewriter, loc, RankedTensorType(),
1430 transposedOp.getResult()[0], tileSizes);
1434 for (
int i = 0, idx = 0; i < destRank; ++i) {
1435 if (dimAndTileMapping.count(i) || destShape[i] != 1)
1436 writeSizes.push_back(tileSizes[idx++]);
1438 writeSizes.push_back(oneIdxAttr);
1440 auto insert = tensor::InsertSliceOp::create(rewriter, loc, partialTile,
1441 unpackOp.getDest(), writeSizes);
1442 rewriter.
replaceOp(unpackOp, insert.getResult());
1456 for (
unsigned dim : dims) {
1460 resultIndices.push_back(i);
1465 return resultIndices;
1473 auto tensorType = cast<RankedTensorType>(
tensor.getType());
1474 int64_t rank = tensorType.getRank();
1478 for (
int64_t i = 0; i < rank; ++i) {
1479 if (!llvm::is_contained(dimsToRemove, i))
1480 newShape.push_back(tensorType.getDimSize(i));
1483 auto newType = RankedTensorType::get(newShape, tensorType.getElementType());
1491static std::optional<AffineExpr>
1495 bool onlyReferencesDroppedDims =
true;
1496 for (
unsigned d = 0; d < newNumDims + dimsToDrop.size(); ++d) {
1498 onlyReferencesDroppedDims =
false;
1502 if (onlyReferencesDroppedDims && llvm::any_of(dimsToDrop, [&](
unsigned d) {
1505 return std::nullopt;
1510 unsigned newDimIdx = 0;
1511 for (
unsigned d = 0; d < newNumDims + dimsToDrop.size(); ++d) {
1512 if (llvm::is_contained(dimsToDrop, d)) {
1526 if (failed(maybeDims))
1530 if (maybeDims->outputImage.size() != 2 || maybeDims->filterLoop.size() != 2)
1533 if (op.hasPureBufferSemantics())
1537 unsigned outSpatial0 = maybeDims->outputImage[0];
1538 unsigned outSpatial1 = maybeDims->outputImage[1];
1539 unsigned filterSpatial0 = maybeDims->filterLoop[0];
1540 unsigned filterSpatial1 = maybeDims->filterLoop[1];
1544 int64_t outSize0 = loopRanges[outSpatial0];
1545 int64_t outSize1 = loopRanges[outSpatial1];
1546 int64_t filterSize0 = loopRanges[filterSpatial0];
1547 int64_t filterSize1 = loopRanges[filterSpatial1];
1550 bool canRemoveSpatial0 = (filterSize0 == 1 && outSize0 == 1);
1551 bool canRemoveSpatial1 = (filterSize1 == 1 && outSize1 == 1);
1552 if (!canRemoveSpatial0 && !canRemoveSpatial1)
1559 if (canRemoveSpatial0) {
1560 loopDimsToRemove.push_back(outSpatial0);
1561 loopDimsToRemove.push_back(filterSpatial0);
1563 loopDimsToRemove.push_back(outSpatial1);
1564 loopDimsToRemove.push_back(filterSpatial1);
1566 llvm::sort(loopDimsToRemove);
1571 unsigned numDims = op.getNumLoops();
1572 unsigned newNumDims = numDims - loopDimsToRemove.size();
1573 for (
AffineMap map : op.getIndexingMapsArray()) {
1579 newResults.push_back(*newExpr);
1581 newMaps.push_back(
AffineMap::get(newNumDims, 0, newResults, ctx));
1586 auto iterTypes = op.getIteratorTypesArray();
1587 for (
unsigned idx = 0; idx < iterTypes.size(); ++idx) {
1588 if (!llvm::is_contained(loopDimsToRemove, idx))
1589 newIterTypes.push_back(iterTypes[idx]);
1595 for (
OpOperand *input : op.getDpsInputOperands()) {
1596 AffineMap map = op.getMatchingIndexingMap(input);
1600 tensorDimsToRemove);
1601 newInputs.push_back(reduced);
1604 OpOperand &output = *op.getDpsInitsMutable().begin();
1605 AffineMap outputMap = op.getMatchingIndexingMap(&output);
1609 outputDimsToRemove);
1614 newInputs, newOutput, newMaps, newIterTypes);
1616 newOp.getRegion().begin());
1620 LinalgOp resultOp = newOp;
1621 if (!isa<GenericOp>(op)) {
1623 if (succeeded(specializedOp))
1624 resultOp = *specializedOp;
1629 rewriter, loc, resultOp->getResult(0), output.
get());
1637struct DownscaleSizeOneWindowedConvolution final
1639 DownscaleSizeOneWindowedConvolution(
MLIRContext *context,
1643 LogicalResult matchAndRewrite(LinalgOp op,
1652 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< LinalgOp > specializeGenericOp(RewriterBase &rewriter, GenericOp genericOp, const GenericOpSpecializationOptions &options={})
Replace the given GenericOp with a namedOp or categoryOp.
FailureOr< ConvolutionDimensions > inferConvolutionDims(LinalgOp linalgOp)
Find at least 1 parallel (output_image) and reduction (filter_loop) dimension candidates that form a ...
FailureOr< GenericOp > generalizeNamedOp(RewriterBase &rewriter, LinalgOp linalgOp)
Create a GenericOp from the given named operation linalgOp and replace the given linalgOp.
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 .
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.