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");
1176 auto outerDimsPerm = packOp.getOuterDimsPerm();
1182 if (!llvm::all_of(outerDimsPerm, [&innerDimsPos, &packOp](
int64_t dim) {
1183 static int prev = 0;
1185 if (llvm::is_contained(innerDimsPos, dim))
1190 if (dim < prev && (packOp.getResult().getType().getShape()[prev] != 1 ||
1191 packOp.getResult().getType().getShape()[dim] != 1))
1198 packOp,
"At least one non-unit and un-tiled outer dim is permuted, "
1199 "this is not supported ATM!");
1204 int64_t srcRank = packOp.getSourceRank();
1223 for (
int64_t i = 0; i < srcRank; i++) {
1231 if (llvm::is_contained(innerDimsPos, i))
1233 srcPermForTranspose.push_back(i);
1235 srcPermForTranspose.append(innerDimsPos.begin(), innerDimsPos.end());
1239 ShapedType inputTy = cast<ShapedType>(input.
getType());
1241 for (
int64_t i = 0; i < srcRank; i++) {
1242 if (llvm::is_contained(innerDimsPos, i)) {
1246 if (inputTy.isStaticDim(i))
1247 shapeForEmptyOp.push_back(rewriter.
getIndexAttr(inputTy.getShape()[i]));
1249 shapeForEmptyOp.emplace_back(
1250 tensor::DimOp::create(rewriter, loc, input, i).getResult());
1252 shapeForEmptyOp.append(packOp.getMixedTiles());
1259 llvm::transform(shapeForEmptyOp, shapeForEmptyOp.begin(),
1261 if (auto val = llvm::dyn_cast<Value>(ofr))
1262 return getAsOpFoldResult(val);
1266 LDBG() <<
"Pack permutation: " << packOp;
1267 LDBG() <<
"perm: " << llvm::interleaved(srcPermForTranspose);
1268 LDBG() <<
"Shape of empty tensor: " << llvm::interleaved(shapeForEmptyOp);
1270 Value empty = tensor::EmptyOp::create(
1271 rewriter, loc, shapeForEmptyOp, packOp.getSourceType().getElementType());
1274 auto transposedOp = linalg::TransposeOp::create(rewriter, loc, input, empty,
1275 srcPermForTranspose);
1287 for (
auto size : packOp.getAllOuterDims()) {
1291 for (
auto tileSize : packOp.getMixedTiles()) {
1292 auto [_, tileSizeOfr] =
1294 writeSizes.push_back(tileSizeOfr);
1297 auto insert = tensor::InsertSliceOp::create(
1298 rewriter, loc, transposedOp.getResult()[0], packOp.getDest(), writeSizes);
1301 rewriter.
replaceOp(packOp, insert.getResult());
1308 if (!unpackOp.hasPureTensorSemantics())
1311 int64_t destRank = unpackOp.getDestRank();
1314 if (llvm::any_of(unpackOp.getTiledOuterDims(),
1315 [](
int64_t dim) { return dim != 1; })) {
1318 "require the tiled outer dimensions of the result are all 1s");
1324 Value source = unpackOp.getSource();
1326 unpackOp.getDimAndTileMapping();
1345 for (
auto i : llvm::seq<unsigned>(0, destRank)) {
1354 if (dimAndTileMapping.count(i)) {
1355 extractSliceSizes.push_back(oneIdxAttr);
1361 if (ShapedType::isDynamic(srcShape[i])) {
1363 tensor::DimOp::create(rewriter, loc, source, i).getResult();
1364 extractSliceSizes.push_back(dynamicDim);
1365 shapeForEmptyOp.push_back(dynamicDim);
1367 extractSliceSizes.push_back(rewriter.
getIndexAttr(srcShape[i]));
1368 if (srcShape[i] != 1)
1369 shapeForEmptyOp.push_back(rewriter.
getIndexAttr(srcShape[i]));
1373 if (srcShape[i] != 1) {
1374 readShapeForExtractSlice.push_back(srcShape[i]);
1379 auto mixedTiles = unpackOp.getMixedTiles();
1380 extractSliceSizes.append(mixedTiles.begin(), mixedTiles.end());
1381 shapeForEmptyOp.append(mixedTiles.begin(), mixedTiles.end());
1385 auto tileShape = srcShape.drop_front(destRank);
1387 readShapeForExtractSlice.append(tileShape.begin(), tileShape.end());
1388 Type elemType = unpackOp.getSourceType().getElementType();
1389 auto readType = RankedTensorType::get(readShapeForExtractSlice, elemType);
1390 Value innerTile = tensor::ExtractSliceOp::create(
1391 rewriter, loc, readType, unpackOp.getSource(), extractSliceSizes);
1395 srcShape.take_front(destRank), innerDimsPos, unpackOp.getOuterDimsPerm());
1401 tensor::EmptyOp::create(rewriter, loc, shapeForEmptyOp, elemType);
1403 linalg::TransposeOp::create(rewriter, loc, innerTile, empty, perm);
1409 for (
auto i : llvm::seq<unsigned>(0, destRank)) {
1410 if (dimAndTileMapping.count(i) || destShape[i] != 1)
1411 tileSizes.push_back(
1416 tensor::ExtractSliceOp::create(rewriter, loc, RankedTensorType(),
1417 transposedOp.getResult()[0], tileSizes);
1421 for (
int i = 0, idx = 0; i < destRank; ++i) {
1422 if (dimAndTileMapping.count(i) || destShape[i] != 1)
1423 writeSizes.push_back(tileSizes[idx++]);
1425 writeSizes.push_back(oneIdxAttr);
1427 auto insert = tensor::InsertSliceOp::create(rewriter, loc, partialTile,
1428 unpackOp.getDest(), writeSizes);
1429 rewriter.
replaceOp(unpackOp, insert.getResult());
1443 for (
unsigned dim : dims) {
1447 resultIndices.push_back(i);
1452 return resultIndices;
1460 auto tensorType = cast<RankedTensorType>(
tensor.getType());
1461 int64_t rank = tensorType.getRank();
1465 for (
int64_t i = 0; i < rank; ++i) {
1466 if (!llvm::is_contained(dimsToRemove, i))
1467 newShape.push_back(tensorType.getDimSize(i));
1470 auto newType = RankedTensorType::get(newShape, tensorType.getElementType());
1478static std::optional<AffineExpr>
1482 bool onlyReferencesDroppedDims =
true;
1483 for (
unsigned d = 0; d < newNumDims + dimsToDrop.size(); ++d) {
1485 onlyReferencesDroppedDims =
false;
1489 if (onlyReferencesDroppedDims && llvm::any_of(dimsToDrop, [&](
unsigned d) {
1492 return std::nullopt;
1497 unsigned newDimIdx = 0;
1498 for (
unsigned d = 0; d < newNumDims + dimsToDrop.size(); ++d) {
1499 if (llvm::is_contained(dimsToDrop, d)) {
1513 if (failed(maybeDims))
1517 if (maybeDims->outputImage.size() != 2 || maybeDims->filterLoop.size() != 2)
1520 if (op.hasPureBufferSemantics())
1524 unsigned outSpatial0 = maybeDims->outputImage[0];
1525 unsigned outSpatial1 = maybeDims->outputImage[1];
1526 unsigned filterSpatial0 = maybeDims->filterLoop[0];
1527 unsigned filterSpatial1 = maybeDims->filterLoop[1];
1531 int64_t outSize0 = loopRanges[outSpatial0];
1532 int64_t outSize1 = loopRanges[outSpatial1];
1533 int64_t filterSize0 = loopRanges[filterSpatial0];
1534 int64_t filterSize1 = loopRanges[filterSpatial1];
1537 bool canRemoveSpatial0 = (filterSize0 == 1 && outSize0 == 1);
1538 bool canRemoveSpatial1 = (filterSize1 == 1 && outSize1 == 1);
1539 if (!canRemoveSpatial0 && !canRemoveSpatial1)
1546 if (canRemoveSpatial0) {
1547 loopDimsToRemove.push_back(outSpatial0);
1548 loopDimsToRemove.push_back(filterSpatial0);
1550 loopDimsToRemove.push_back(outSpatial1);
1551 loopDimsToRemove.push_back(filterSpatial1);
1553 llvm::sort(loopDimsToRemove);
1558 unsigned numDims = op.getNumLoops();
1559 unsigned newNumDims = numDims - loopDimsToRemove.size();
1560 for (
AffineMap map : op.getIndexingMapsArray()) {
1566 newResults.push_back(*newExpr);
1568 newMaps.push_back(
AffineMap::get(newNumDims, 0, newResults, ctx));
1573 auto iterTypes = op.getIteratorTypesArray();
1574 for (
unsigned idx = 0; idx < iterTypes.size(); ++idx) {
1575 if (!llvm::is_contained(loopDimsToRemove, idx))
1576 newIterTypes.push_back(iterTypes[idx]);
1582 for (
OpOperand *input : op.getDpsInputOperands()) {
1583 AffineMap map = op.getMatchingIndexingMap(input);
1587 tensorDimsToRemove);
1588 newInputs.push_back(reduced);
1591 OpOperand &output = *op.getDpsInitsMutable().begin();
1592 AffineMap outputMap = op.getMatchingIndexingMap(&output);
1596 outputDimsToRemove);
1601 newInputs, newOutput, newMaps, newIterTypes);
1603 newOp.getRegion().begin());
1607 LinalgOp resultOp = newOp;
1608 if (!isa<GenericOp>(op)) {
1610 if (succeeded(specializedOp))
1611 resultOp = *specializedOp;
1616 rewriter, loc, resultOp->getResult(0), output.
get());
1624struct DownscaleSizeOneWindowedConvolution final
1626 DownscaleSizeOneWindowedConvolution(
MLIRContext *context,
1630 LogicalResult matchAndRewrite(LinalgOp op,
1639 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.