33#include "llvm/ADT/SmallBitVector.h"
34#include "llvm/Support/Debug.h"
37#define GEN_PASS_DEF_LINALGFOLDUNITEXTENTDIMSPASS
38#include "mlir/Dialect/Linalg/Passes.h.inc"
41#define DEBUG_TYPE "linalg-drop-unit-dims"
85 LogicalResult matchAndRewrite(GenericOp genericOp,
87 if (!genericOp.hasPureTensorSemantics())
89 if (genericOp.getNumParallelLoops() != genericOp.getNumLoops())
92 auto outputOperands = genericOp.getDpsInitsMutable();
95 if (genericOp.getMatchingBlockArgument(&op).use_empty())
97 candidates.insert(&op);
100 if (candidates.empty())
104 int64_t origNumInput = genericOp.getNumDpsInputs();
108 newIndexingMaps.append(indexingMaps.begin(),
109 std::next(indexingMaps.begin(), origNumInput));
111 newInputOperands.push_back(op->get());
112 newIndexingMaps.push_back(genericOp.getMatchingIndexingMap(op));
114 newIndexingMaps.append(std::next(indexingMaps.begin(), origNumInput),
119 llvm::to_vector(genericOp.getDpsInits());
123 auto elemType = cast<ShapedType>(op->get().getType()).getElementType();
124 auto empty = tensor::EmptyOp::create(
128 unsigned start = genericOp.getDpsInits().getBeginOperandIndex();
129 newOutputOperands[op->getOperandNumber() - start] = empty.getResult();
132 auto newOp = GenericOp::create(
133 rewriter, loc, genericOp.getResultTypes(), newInputOperands,
134 newOutputOperands, newIndexingMaps, genericOp.getIteratorTypesArray(),
138 Region ®ion = newOp.getRegion();
141 for (
auto bbarg : genericOp.getRegionInputArgs())
145 BlockArgument bbarg = genericOp.getMatchingBlockArgument(op);
150 BlockArgument bbarg = genericOp.getMatchingBlockArgument(&op);
151 if (candidates.count(&op))
157 for (
auto &op : genericOp.getBody()->getOperations()) {
158 rewriter.
clone(op, mapper);
160 rewriter.
replaceOp(genericOp, newOp.getResults());
231 const llvm::SmallDenseSet<unsigned> &unitDims,
233 for (IndexOp indexOp :
234 llvm::make_early_inc_range(genericOp.getBody()->getOps<IndexOp>())) {
237 if (unitDims.count(indexOp.getDim()) != 0) {
241 unsigned droppedDims = llvm::count_if(
242 unitDims, [&](
unsigned dim) {
return dim < indexOp.getDim(); });
243 if (droppedDims != 0)
245 indexOp.getDim() - droppedDims);
256 auto origResultType = cast<RankedTensorType>(origDest.
getType());
257 if (origResultType.getEncoding() !=
nullptr) {
263 unsigned rank = origResultType.getRank();
264 SmallVector<OpFoldResult> offsets(rank, rewriter.
getIndexAttr(0));
265 SmallVector<OpFoldResult> sizes =
267 SmallVector<OpFoldResult> strides(rank, rewriter.
getIndexAttr(1));
269 loc,
result, origDest, offsets, sizes, strides);
274 "unknown rank reduction strategy");
275 return tensor::ExpandShapeOp::create(rewriter, loc, origResultType,
result,
281ControlDropUnitDims::collapseValue(RewriterBase &rewriter, Location loc,
282 Value operand, ArrayRef<int64_t> targetShape,
283 ArrayRef<ReassociationIndices> reassociation,
285 if (
auto memrefType = dyn_cast<MemRefType>(operand.
getType())) {
286 if (!memrefType.getLayout().isIdentity()) {
292 FailureOr<Value> rankReducingExtract =
293 memref::SubViewOp::rankReduceIfNeeded(rewriter, loc, operand,
295 assert(succeeded(rankReducingExtract) &&
"not a unit-extent collapse");
296 return *rankReducingExtract;
302 "unknown rank reduction strategy");
303 MemRefLayoutAttrInterface layout;
304 auto targetType = MemRefType::get(targetShape, memrefType.getElementType(),
305 layout, memrefType.getMemorySpace());
306 return memref::CollapseShapeOp::create(rewriter, loc, targetType, operand,
310 if (
auto tensorType = dyn_cast<RankedTensorType>(operand.
getType())) {
311 if (tensorType.getEncoding() !=
nullptr) {
317 FailureOr<Value> rankReducingExtract =
318 tensor::ExtractSliceOp::rankReduceIfNeeded(rewriter, loc, operand,
320 assert(succeeded(rankReducingExtract) &&
"not a unit-extent collapse");
321 return *rankReducingExtract;
327 "unknown rank reduction strategy");
329 RankedTensorType::get(targetShape, tensorType.getElementType());
330 return tensor::CollapseShapeOp::create(rewriter, loc, targetType, operand,
334 llvm_unreachable(
"unsupported operand type");
349 llvm::SmallDenseMap<unsigned, unsigned> &oldDimsToNewDimsMap,
354 AffineMap indexingMap = op.getMatchingIndexingMap(opOperand);
358 auto isUnitDim = [&](
unsigned dim) {
359 if (
auto dimExpr = dyn_cast<AffineDimExpr>(exprs[dim])) {
360 unsigned oldPosition = dimExpr.getPosition();
361 return !oldDimsToNewDimsMap.count(oldPosition) &&
362 (operandShape[dim] == 1);
366 if (operandShape[dim] == 1) {
371 AffineExpr newExpr = exprs[dim].replaceDims(dimReplacements);
372 auto constAffineExpr = dyn_cast<AffineConstantExpr>(newExpr);
373 return constAffineExpr && constAffineExpr.getValue() == 0;
379 while (dim < operandShape.size() && isUnitDim(dim))
380 reassociationGroup.push_back(dim++);
381 while (dim < operandShape.size()) {
382 assert(!isUnitDim(dim) &&
"expected non unit-extent");
383 reassociationGroup.push_back(dim);
384 AffineExpr newExpr = exprs[dim].replaceDims(dimReplacements);
385 newIndexExprs.push_back(newExpr);
389 while (dim < operandShape.size() && isUnitDim(dim)) {
390 reassociationGroup.push_back(dim++);
393 reassociationGroup.clear();
397 newIndexExprs, context);
401FailureOr<DropUnitDimsResult>
405 auto dpsOp = dyn_cast<DestinationStyleOpInterface>(op.getOperation());
408 op,
"op should implement DestinationStyleOpInterface");
412 if (indexingMaps.empty())
422 "invalid indexing maps for operation");
426 for (
OpOperand &opOperand : op->getOpOperands())
427 llvm::append_range(allShapesSizes, op.getStaticOperandShape(&opOperand));
431 if (allowedUnitDims.empty()) {
433 op,
"control function returns no allowed unit dims to prune");
435 llvm::SmallDenseSet<unsigned> unitDimsFilter(allowedUnitDims.begin(),
436 allowedUnitDims.end());
437 llvm::SmallDenseSet<unsigned> unitDims;
438 for (
const auto &expr : enumerate(invertedMap.
getResults())) {
439 if (
AffineDimExpr dimExpr = dyn_cast<AffineDimExpr>(expr.value())) {
440 if (allShapesSizes[dimExpr.getPosition()] == 1 &&
441 unitDimsFilter.count(expr.index()))
442 unitDims.insert(expr.index());
448 llvm::SmallDenseMap<unsigned, unsigned> oldDimToNewDimMap;
450 unsigned newDims = 0;
451 for (
auto index : llvm::seq<int64_t>(op.getStaticLoopRanges().size())) {
452 if (unitDims.count(
index)) {
453 dimReplacements.push_back(
456 oldDimToNewDimMap[
index] = newDims;
457 dimReplacements.push_back(
478 for (
OpOperand &opOperand : op->getOpOperands()) {
479 auto indexingMap = op.getMatchingIndexingMap(&opOperand);
480 auto replacementInfo =
482 oldDimToNewDimMap, dimReplacements);
483 reassociations.push_back(replacementInfo.reassociation);
484 newIndexingMaps.push_back(replacementInfo.indexMap);
485 targetShapes.push_back(replacementInfo.targetShape);
486 collapsed.push_back(!(replacementInfo.indexMap.getNumResults() ==
487 indexingMap.getNumResults()));
492 if (newIndexingMaps == indexingMaps ||
504 for (
OpOperand &opOperand : op->getOpOperands()) {
505 int64_t idx = opOperand.getOperandNumber();
506 if (!collapsed[idx]) {
507 newOperands.push_back(opOperand.get());
510 FailureOr<Value> collapsed =
511 options.collapseFn(rewriter, loc, opOperand.get(), targetShapes[idx],
513 if (failed(collapsed)) {
517 newOperands.push_back(collapsed.value());
520 IndexingMapOpInterface replacementOp = droppedUnitDimsBuilder(
521 loc, rewriter, op, newOperands, newIndexingMaps, unitDims);
528 for (
auto [
index,
result] : llvm::enumerate(replacementOp->getResults())) {
529 unsigned opOperandIndex =
index + dpsOp.getNumDpsInputs();
530 Value origDest = dpsOp.getDpsInitOperand(
index)->get();
531 if (!collapsed[opOperandIndex]) {
532 resultReplacements.push_back(
result);
535 FailureOr<Value> expanded =
537 reassociations[opOperandIndex],
options);
538 if (failed(expanded)) {
542 resultReplacements.push_back(expanded.value());
548FailureOr<DropUnitDimsResult>
555 const llvm::SmallDenseSet<unsigned> &droppedDims)
556 -> IndexingMapOpInterface {
557 auto genericOp = cast<GenericOp>(op);
561 for (
auto [
index, attr] :
562 llvm::enumerate(genericOp.getIteratorTypesArray())) {
563 if (!droppedDims.count(
index))
564 newIteratorTypes.push_back(attr);
574 resultTypes.reserve(genericOp.getNumResults());
575 for (
unsigned i : llvm::seq<unsigned>(0, genericOp.getNumResults()))
576 resultTypes.push_back(newOutputs[i].
getType());
577 GenericOp replacementOp =
578 GenericOp::create(
b, loc, resultTypes, newInputs, newOutputs,
579 newIndexingMaps, newIteratorTypes);
580 b.cloneRegionBefore(genericOp.getRegion(), replacementOp.getRegion(),
581 replacementOp.getRegion().begin());
587 return replacementOp;
599 LogicalResult matchAndRewrite(GenericOp genericOp,
600 PatternRewriter &rewriter)
const override {
601 FailureOr<DropUnitDimsResult>
result =
620struct DropPadUnitDims :
public OpRewritePattern<tensor::PadOp> {
621 DropPadUnitDims(MLIRContext *context, ControlDropUnitDims
options = {},
622 PatternBenefit benefit = 1)
623 : OpRewritePattern(context, benefit),
options(std::move(
options)) {}
625 LogicalResult matchAndRewrite(tensor::PadOp padOp,
626 PatternRewriter &rewriter)
const override {
628 SmallVector<unsigned> allowedUnitDims =
options.controlFn(padOp);
629 if (allowedUnitDims.empty()) {
631 padOp,
"control function returns no allowed unit dims to prune");
634 if (padOp.getSourceType().getEncoding()) {
636 padOp,
"cannot collapse dims of tensor with encoding");
643 Value paddingVal = padOp.getConstantPaddingValue();
646 padOp,
"unimplemented: non-constant padding value");
649 ArrayRef<int64_t> sourceShape = padOp.getSourceType().getShape();
650 ArrayRef<int64_t> resultShape = padOp.getResultType().getShape();
651 int64_t padRank = sourceShape.size();
653 auto isStaticZero = [](OpFoldResult f) {
657 llvm::SmallDenseSet<unsigned> unitDimsFilter(allowedUnitDims.begin(),
658 allowedUnitDims.end());
659 llvm::SmallDenseSet<unsigned> unitDims;
660 SmallVector<int64_t> newShape;
661 SmallVector<int64_t> newResultShape;
662 SmallVector<OpFoldResult> newLowPad;
663 SmallVector<OpFoldResult> newHighPad;
664 for (
const auto [dim, size, outSize, low, high] : zip_equal(
665 llvm::seq(
static_cast<int64_t
>(0), padRank), sourceShape,
666 resultShape, padOp.getMixedLowPad(), padOp.getMixedHighPad())) {
667 if (unitDimsFilter.contains(dim) && size == 1 && isStaticZero(low) &&
668 isStaticZero(high)) {
669 unitDims.insert(dim);
671 newShape.push_back(size);
672 newResultShape.push_back(outSize);
673 newLowPad.push_back(low);
674 newHighPad.push_back(high);
678 if (unitDims.empty()) {
683 SmallVector<ReassociationIndices> reassociationMap;
685 while (dim < padRank && unitDims.contains(dim))
686 reassociationGroup.push_back(dim++);
687 while (dim < padRank) {
688 assert(!unitDims.contains(dim) &&
"expected non unit-extent");
689 reassociationGroup.push_back(dim);
692 while (dim < padRank && unitDims.contains(dim))
693 reassociationGroup.push_back(dim++);
694 reassociationMap.push_back(reassociationGroup);
695 reassociationGroup.clear();
698 FailureOr<Value> collapsedSource =
699 options.collapseFn(rewriter, padOp.getLoc(), padOp.getSource(),
700 newShape, reassociationMap,
options);
701 if (
failed(collapsedSource)) {
705 auto newResultType = RankedTensorType::get(
706 newResultShape, padOp.getResultType().getElementType());
707 auto newPadOp = tensor::PadOp::create(
708 rewriter, padOp.getLoc(), newResultType,
709 collapsedSource.value(), newLowPad, newHighPad, paddingVal,
712 Value dest = padOp.getResult();
713 if (
options.rankReductionStrategy ==
714 ControlDropUnitDims::RankReductionStrategy::ExtractInsertSlice) {
715 SmallVector<OpFoldResult> expandedSizes;
716 int64_t numUnitDims = 0;
717 for (
auto dim : llvm::seq(
static_cast<int64_t
>(0), padRank)) {
718 if (unitDims.contains(dim)) {
724 rewriter, padOp.getLoc(), newPadOp, dim - numUnitDims));
726 dest = tensor::EmptyOp::create(rewriter, padOp.getLoc(), expandedSizes,
727 padOp.getResultType().getElementType());
730 FailureOr<Value> expandedValue =
731 options.expandFn(rewriter, padOp.getLoc(), newPadOp.getResult(), dest,
733 if (
failed(expandedValue)) {
736 rewriter.
replaceOp(padOp, expandedValue.value());
747struct RankReducedExtractSliceOp
748 :
public OpRewritePattern<tensor::ExtractSliceOp> {
749 using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern;
751 LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
752 PatternRewriter &rewriter)
const override {
753 RankedTensorType resultType = sliceOp.getType();
754 SmallVector<OpFoldResult> targetShape;
755 for (
auto size : resultType.getShape())
758 if (!reassociation ||
759 reassociation->size() ==
static_cast<size_t>(resultType.getRank()))
762 SmallVector<OpFoldResult> offsets = sliceOp.getMixedOffsets();
763 SmallVector<OpFoldResult> strides = sliceOp.getMixedStrides();
764 SmallVector<OpFoldResult> sizes = sliceOp.getMixedSizes();
765 SmallVector<int64_t> staticSizes;
768 sizes.size() - reassociation->size(), staticSizes);
769 RankedTensorType rankReducedType =
772 Location loc = sliceOp.getLoc();
773 Value newSlice = tensor::ExtractSliceOp::create(
774 rewriter, loc, rankReducedType, sliceOp.getSource(), offsets, sizes,
777 sliceOp, resultType, newSlice, *reassociation);
784template <
typename InsertOpTy>
785struct RankReducedInsertSliceOp :
public OpRewritePattern<InsertOpTy> {
786 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
788 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
789 PatternRewriter &rewriter)
const override {
790 RankedTensorType sourceType = insertSliceOp.getSourceType();
791 SmallVector<OpFoldResult> targetShape;
792 for (
auto size : sourceType.getShape())
795 if (!reassociation ||
796 reassociation->size() ==
static_cast<size_t>(sourceType.getRank()))
799 Location loc = insertSliceOp.getLoc();
800 tensor::CollapseShapeOp reshapedSource;
802 OpBuilder::InsertionGuard g(rewriter);
806 if (std::is_same<InsertOpTy, tensor::ParallelInsertSliceOp>::value)
808 reshapedSource = tensor::CollapseShapeOp::create(
809 rewriter, loc, insertSliceOp.getSource(), *reassociation);
812 insertSliceOp, reshapedSource, insertSliceOp.getDest(),
813 insertSliceOp.getMixedOffsets(), insertSliceOp.getMixedSizes(),
814 insertSliceOp.getMixedStrides());
826 patterns.
add<DropPadUnitDims>(context,
options);
832 bool reassociativeReshape =
833 options.rankReductionStrategy ==
835 if (reassociativeReshape) {
836 patterns.
add<RankReducedExtractSliceOp,
837 RankReducedInsertSliceOp<tensor::InsertSliceOp>,
838 RankReducedInsertSliceOp<tensor::ParallelInsertSliceOp>>(
840 tensor::CollapseShapeOp::getCanonicalizationPatterns(patterns, context);
841 tensor::ExpandShapeOp::getCanonicalizationPatterns(patterns, context);
843 linalg::FillOp::getCanonicalizationPatterns(patterns, context);
844 tensor::EmptyOp::getCanonicalizationPatterns(patterns, context);
852 patterns.
add<MoveInitOperandsToInput>(patterns.
getContext());
857struct LinalgFoldUnitExtentDimsPass
858 :
public impl::LinalgFoldUnitExtentDimsPassBase<
859 LinalgFoldUnitExtentDimsPass> {
860 using impl::LinalgFoldUnitExtentDimsPassBase<
861 LinalgFoldUnitExtentDimsPass>::LinalgFoldUnitExtentDimsPassBase;
862 void runOnOperation()
override {
866 if (useRankReducingSlices) {
867 options.rankReductionStrategy = linalg::ControlDropUnitDims::
868 RankReductionStrategy::ExtractInsertSlice;
880 RewritePatternSet patterns(context);
895static SmallVector<ReassociationIndices>
896getReassociationForReshapeAtDim(int64_t rank, int64_t pos) {
897 SmallVector<ReassociationIndices> reassociation(rank - 1, {0, 1});
898 bool lastDim = pos == rank - 1;
900 for (int64_t i = 0; i < rank - 1; i++) {
901 if (i == pos || (lastDim && i == pos - 1))
909 return reassociation;
914static Value collapseSingletonDimAt(PatternRewriter &rewriter, Value val,
918 auto valType = cast<ShapedType>(val.
getType());
919 SmallVector<int64_t> collapsedShape(valType.getShape());
920 collapsedShape.erase(collapsedShape.begin() + pos);
922 FailureOr<Value> collapsed = control.
collapseFn(
923 rewriter, val.
getLoc(), val, collapsedShape,
924 getReassociationForReshapeAtDim(valType.getRank(), pos), control);
925 assert(llvm::succeeded(collapsed) &&
"Collapsing the value failed");
926 return collapsed.value();
935template <
typename FromOpTy,
typename ToOpTy>
936struct RankReduceContractionOps : OpRewritePattern<FromOpTy> {
937 using OpRewritePattern<FromOpTy>::OpRewritePattern;
941 collapseOperands(PatternRewriter &rewriter, ArrayRef<Value> operands,
942 ArrayRef<int64_t> operandCollapseDims)
const {
943 assert(operandCollapseDims.size() == 3 && operands.size() == 3 &&
944 "expected 3 operands and dims");
945 return llvm::map_to_vector(
946 llvm::zip(operands, operandCollapseDims), [&](
auto pair) {
947 return collapseSingletonDimAt(rewriter, std::get<0>(pair),
953 Value expandResult(PatternRewriter &rewriter, Value
result,
954 RankedTensorType expandedType, int64_t dim)
const {
955 return tensor::ExpandShapeOp::create(
957 getReassociationForReshapeAtDim(expandedType.getRank(), dim));
960 LogicalResult matchAndRewrite(FromOpTy contractionOp,
961 PatternRewriter &rewriter)
const override {
962 if (contractionOp.hasUserDefinedMaps()) {
964 contractionOp,
"ops with user-defined maps are not supported");
967 auto loc = contractionOp.getLoc();
968 auto inputs = contractionOp.getDpsInputs();
969 auto inits = contractionOp.getDpsInits();
970 if (inputs.size() != 2 || inits.size() != 1)
972 "expected 2 inputs and 1 init");
973 auto lhs = inputs[0];
974 auto rhs = inputs[1];
975 auto init = inits[0];
976 SmallVector<Value> operands{
lhs,
rhs, init};
978 SmallVector<int64_t> operandUnitDims;
979 if (
failed(getOperandUnitDims(contractionOp, operandUnitDims)))
981 "no reducable dims found");
983 SmallVector<Value> collapsedOperands =
984 collapseOperands(rewriter, operands, operandUnitDims);
985 Value collapsedLhs = collapsedOperands[0];
986 Value collapsedRhs = collapsedOperands[1];
987 Value collapsedInit = collapsedOperands[2];
988 SmallVector<Type, 1> collapsedResultTy;
989 if (isa<RankedTensorType>(collapsedInit.
getType()))
990 collapsedResultTy.push_back(collapsedInit.
getType());
992 if constexpr (std::is_same_v<FromOpTy, BatchMatmulOp> &&
993 std::is_same_v<ToOpTy, MatmulOp>) {
994 if (TypeFnAttr castAttr = contractionOp.getCastAttr()) {
995 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
999 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
1004 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
1008 for (
auto attr : contractionOp->getDiscardableAttrDictionary()) {
1009 if (attr.getName() == LinalgDialect::kMemoizedIndexingMapsAttrName ||
1010 attr.getName() ==
"indexing_maps")
1012 collapsedOp->setDiscardableAttr(attr.getName(), attr.getValue());
1015 auto results = contractionOp.getResults();
1016 assert(results.size() < 2 &&
"expected at most one result");
1017 if (results.empty()) {
1018 rewriter.
replaceOp(contractionOp, collapsedOp);
1022 expandResult(rewriter, collapsedOp.getResultTensors()[0],
1023 cast<RankedTensorType>(results[0].getType()),
1024 operandUnitDims[2]));
1033 virtual LogicalResult
1034 getOperandUnitDims(LinalgOp op,
1035 SmallVectorImpl<int64_t> &operandUnitDims)
const = 0;
1039template <
typename FromOpTy,
typename ToOpTy>
1040struct RankReduceToUnBatched : RankReduceContractionOps<FromOpTy, ToOpTy> {
1041 using RankReduceContractionOps<FromOpTy, ToOpTy>::RankReduceContractionOps;
1045 getOperandUnitDims(LinalgOp op,
1046 SmallVectorImpl<int64_t> &operandUnitDims)
const override {
1047 FailureOr<ContractionDimensions> maybeContractionDims =
1049 if (
failed(maybeContractionDims)) {
1050 LLVM_DEBUG(llvm::dbgs() <<
"could not infer contraction dims");
1053 const ContractionDimensions &contractionDims = maybeContractionDims.value();
1055 if (contractionDims.
batch.size() != 1)
1057 auto batchDim = contractionDims.
batch[0];
1058 SmallVector<std::pair<Value, unsigned>, 3> bOperands;
1059 op.mapIterationSpaceDimToAllOperandDims(batchDim, bOperands);
1060 if (bOperands.size() != 3 || llvm::any_of(bOperands, [](
auto pair) {
1061 return cast<ShapedType>(std::get<0>(pair).getType())
1062 .getShape()[std::get<1>(pair)] != 1;
1064 LLVM_DEBUG(llvm::dbgs() <<
"specified unit dims not found");
1068 operandUnitDims = SmallVector<int64_t>{std::get<1>(bOperands[0]),
1069 std::get<1>(bOperands[1]),
1070 std::get<1>(bOperands[2])};
1076template <
typename FromOpTy,
typename ToOpTy>
1077struct RankReduceMatmul : RankReduceContractionOps<FromOpTy, ToOpTy> {
1078 using RankReduceContractionOps<FromOpTy, ToOpTy>::RankReduceContractionOps;
1081 static bool constexpr reduceLeft =
1082 (std::is_same_v<FromOpTy, BatchMatmulOp> &&
1083 std::is_same_v<ToOpTy, BatchVecmatOp>) ||
1084 (std::is_same_v<FromOpTy, MatmulOp> &&
1085 std::is_same_v<ToOpTy, VecmatOp>) ||
1086 (std::is_same_v<FromOpTy, MatvecOp> && std::is_same_v<ToOpTy, DotOp>);
1090 getOperandUnitDims(LinalgOp op,
1091 SmallVectorImpl<int64_t> &operandUnitDims)
const override {
1092 FailureOr<ContractionDimensions> maybeContractionDims =
1094 if (
failed(maybeContractionDims)) {
1095 LLVM_DEBUG(llvm::dbgs() <<
"could not infer contraction dims");
1098 const ContractionDimensions &contractionDims = maybeContractionDims.value();
1100 if constexpr (reduceLeft) {
1101 auto m = contractionDims.
m[0];
1102 SmallVector<std::pair<Value, unsigned>, 2> mOperands;
1103 op.mapIterationSpaceDimToAllOperandDims(m, mOperands);
1104 if (mOperands.size() != 2)
1106 if (llvm::all_of(mOperands, [](
auto pair) {
1107 return cast<ShapedType>(std::get<0>(pair).
getType())
1108 .getShape()[std::get<1>(pair)] == 1;
1110 operandUnitDims = SmallVector<int64_t>{std::get<1>(mOperands[0]), -1,
1111 std::get<1>(mOperands[1])};
1115 auto n = contractionDims.
n[0];
1116 SmallVector<std::pair<Value, unsigned>, 2> nOperands;
1117 op.mapIterationSpaceDimToAllOperandDims(n, nOperands);
1118 if (nOperands.size() != 2)
1120 if (llvm::all_of(nOperands, [](
auto pair) {
1121 return cast<ShapedType>(std::get<0>(pair).
getType())
1122 .getShape()[std::get<1>(pair)] == 1;
1124 operandUnitDims = SmallVector<int64_t>{-1, std::get<1>(nOperands[0]),
1125 std::get<1>(nOperands[1])};
1129 LLVM_DEBUG(llvm::dbgs() <<
"specified unit dims not found");
1140 patterns.
add<RankReduceToUnBatched<BatchMatmulOp, MatmulOp>>(context);
1141 patterns.
add<RankReduceToUnBatched<BatchMatvecOp, MatvecOp>>(context);
1142 patterns.
add<RankReduceToUnBatched<BatchVecmatOp, VecmatOp>>(context);
1145 patterns.
add<RankReduceMatmul<MatmulOp, VecmatOp>>(context);
1146 patterns.
add<RankReduceMatmul<MatmulOp, MatvecOp>>(context);
1148 patterns.
add<RankReduceMatmul<BatchMatmulOp, BatchVecmatOp>>(context);
1149 patterns.
add<RankReduceMatmul<BatchMatmulOp, BatchMatvecOp>>(context);
1152 patterns.
add<RankReduceMatmul<MatvecOp, DotOp>>(context);
1153 patterns.
add<RankReduceMatmul<VecmatOp, DotOp>>(context);
static void replaceUnitDimIndexOps(GenericOp genericOp, const llvm::SmallDenseSet< unsigned > &unitDims, RewriterBase &rewriter)
Implements a pass that canonicalizes the uses of unit-extent dimensions for broadcasting.
static UnitExtentReplacementInfo dropUnitExtentFromOperandMetadata(MLIRContext *context, IndexingMapOpInterface op, OpOperand *opOperand, llvm::SmallDenseMap< unsigned, unsigned > &oldDimsToNewDimsMap, ArrayRef< AffineExpr > dimReplacements)
static llvm::ManagedStatic< PassManagerOptions > options
A dimensional identifier appearing in an affine expression.
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumSymbols() const
ArrayRef< AffineExpr > getResults() const
This class represents an argument of a Block.
Block represents an ordered list of Operations.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
IntegerAttr getIndexAttr(int64_t value)
MLIRContext * getContext() const
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
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.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
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...
void setInsertionPointAfterValue(Value val)
Sets the insertion point to the node after the specified value.
This class represents an operand of an operation.
Operation is the basic unit of execution within MLIR.
MLIRContext * getContext()
Return the context this operation is associated with.
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 class contains a list of basic blocks and a link to the parent operation it is attached to.
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...
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,...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Location getLoc() const
Return the location of this value.
Specialization of arith.constant op that returns an integer of index type.
void populateMoveInitOperandsToInputPattern(RewritePatternSet &patterns)
A pattern that converts init operands to input operands.
std::function< IndexingMapOpInterface( Location loc, OpBuilder &, IndexingMapOpInterface, ArrayRef< Value > newOperands, ArrayRef< AffineMap > newIndexingMaps, const llvm::SmallDenseSet< unsigned > &droppedDims)> DroppedUnitDimsBuilder
void populateContractionOpRankReducingPatterns(RewritePatternSet &patterns)
Adds patterns that reduce the rank of named contraction ops that have unit dimensions in the operand(...
std::optional< SmallVector< ReassociationIndices > > getReassociationMapForFoldingUnitDims(ArrayRef< OpFoldResult > mixedSizes)
Get the reassociation maps to fold the result of a extract_slice (or source of a insert_slice) operat...
void populateFoldUnitExtentDimsPatterns(RewritePatternSet &patterns, ControlDropUnitDims &options)
Patterns to fold unit-extent dimensions in operands/results of linalg ops on tensors and memref.
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< DropUnitDimsResult > dropUnitDims(RewriterBase &rewriter, IndexingMapOpInterface op, const DroppedUnitDimsBuilder &droppedUnitDimsBuilder, const ControlDropUnitDims &options)
Drop unit extent dimensions from the op and its operands.
SmallVector< NamedAttribute > getPrunedAttributeList(OpTy op)
Returns an attribute list that excludes pre-defined attributes.
void populateFoldUnitExtentDimsCanonicalizationPatterns(RewritePatternSet &patterns, ControlDropUnitDims &options)
Populates canonicalization patterns that simplify IR after folding unit-extent dimensions.
void populateResolveRankedShapedTypeResultDimsPatterns(RewritePatternSet &patterns)
Appends patterns that resolve memref.dim operations with values that are defined by operations that i...
void populateResolveShapedTypeResultDimsPatterns(RewritePatternSet &patterns)
Appends patterns that resolve memref.dim operations with values that are defined by operations that i...
void populateFoldTensorEmptyPatterns(RewritePatternSet &patterns, bool foldSingleUseOnly=false)
Populates patterns with patterns that fold tensor.empty with its consumers.
RankedTensorType inferSliceType(RankedTensorType sourceTensorType, ArrayRef< int64_t > staticSizes, const llvm::SmallBitVector &droppedDims)
Infer a slice type for the given sizes and exact dropped-dimension mask.
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.
AffineMap concatAffineMaps(ArrayRef< AffineMap > maps, MLIRContext *context)
Concatenates a list of maps into a single AffineMap, stepping over potentially empty maps.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
llvm::SetVector< T, Vector, Set, N > SetVector
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
void walkAndApplyPatterns(Operation *op, const FrozenRewritePatternSet &patterns, RewriterBase::Listener *listener=nullptr)
A fast walk-based pattern rewrite driver.
llvm::SmallBitVector getPositionsOfShapeOne(unsigned rank, ArrayRef< int64_t > shape)
SmallVector< int64_t, 2 > ReassociationIndices
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
std::pair< SmallVector< int64_t >, SmallVector< Value > > decomposeMixedValues(ArrayRef< OpFoldResult > mixedValues)
Decompose a vector of mixed static or dynamic values into the corresponding pair of arrays.
Compute the modified metadata for an operands of operation whose unit dims are being dropped.
SmallVector< ReassociationIndices > reassociation
SmallVector< int64_t > targetShape
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
SmallVector< unsigned, 2 > batch
SmallVector< unsigned, 2 > m
SmallVector< unsigned, 2 > n
Transformation to drop unit-extent dimensions from linalg.generic operations.
RankReductionStrategy rankReductionStrategy
CollapseFnTy collapseFn
Function to control how operands are collapsed into their new target shape after dropping unit extent...