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(),
701 if (failed(collapsedSource)) {
709 auto newResultType = RankedTensorType::get(
710 newResultShape, padOp.getResultType().getElementType());
711 auto newPadOp = tensor::PadOp::create(
712 rewriter, padOp.getLoc(), newResultType,
713 collapsedSource.value(), newLowPad, newHighPad, paddingVal,
717 if (
options.rankReductionStrategy ==
721 for (
auto dim : llvm::seq(
static_cast<int64_t>(0), padRank)) {
722 if (unitDims.contains(dim)) {
728 rewriter, padOp.getLoc(), newPadOp, dim - numUnitDims));
730 dest = tensor::EmptyOp::create(rewriter, padOp.getLoc(), expandedSizes,
731 padOp.getResultType().getElementType());
734 FailureOr<Value> expandedValue =
735 options.expandFn(rewriter, padOp.getLoc(), newPadOp.getResult(), dest,
737 if (failed(expandedValue)) {
740 rewriter.
replaceOp(padOp, expandedValue.value());
751struct RankReducedExtractSliceOp
755 LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
757 RankedTensorType resultType = sliceOp.getType();
759 for (
auto size : resultType.getShape())
762 if (!reassociation ||
763 reassociation->size() ==
static_cast<size_t>(resultType.getRank()))
772 sizes.size() - reassociation->size(), staticSizes);
773 RankedTensorType rankReducedType =
777 Value newSlice = tensor::ExtractSliceOp::create(
778 rewriter, loc, rankReducedType, sliceOp.getSource(), offsets, sizes,
781 sliceOp, resultType, newSlice, *reassociation);
788template <
typename InsertOpTy>
789struct RankReducedInsertSliceOp :
public OpRewritePattern<InsertOpTy> {
790 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
792 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
793 PatternRewriter &rewriter)
const override {
794 RankedTensorType sourceType = insertSliceOp.getSourceType();
795 SmallVector<OpFoldResult> targetShape;
796 for (
auto size : sourceType.getShape())
799 if (!reassociation ||
800 reassociation->size() ==
static_cast<size_t>(sourceType.getRank()))
803 Location loc = insertSliceOp.getLoc();
804 tensor::CollapseShapeOp reshapedSource;
806 OpBuilder::InsertionGuard g(rewriter);
810 if (std::is_same<InsertOpTy, tensor::ParallelInsertSliceOp>::value)
812 reshapedSource = tensor::CollapseShapeOp::create(
813 rewriter, loc, insertSliceOp.getSource(), *reassociation);
816 insertSliceOp, reshapedSource, insertSliceOp.getDest(),
817 insertSliceOp.getMixedOffsets(), insertSliceOp.getMixedSizes(),
818 insertSliceOp.getMixedStrides());
830 patterns.
add<DropPadUnitDims>(context,
options);
836 bool reassociativeReshape =
837 options.rankReductionStrategy ==
839 if (reassociativeReshape) {
840 patterns.
add<RankReducedExtractSliceOp,
841 RankReducedInsertSliceOp<tensor::InsertSliceOp>,
842 RankReducedInsertSliceOp<tensor::ParallelInsertSliceOp>>(
844 tensor::CollapseShapeOp::getCanonicalizationPatterns(patterns, context);
845 tensor::ExpandShapeOp::getCanonicalizationPatterns(patterns, context);
847 linalg::FillOp::getCanonicalizationPatterns(patterns, context);
848 tensor::EmptyOp::getCanonicalizationPatterns(patterns, context);
856 patterns.
add<MoveInitOperandsToInput>(patterns.
getContext());
861struct LinalgFoldUnitExtentDimsPass
863 LinalgFoldUnitExtentDimsPass> {
865 LinalgFoldUnitExtentDimsPass>::LinalgFoldUnitExtentDimsPassBase;
866 void runOnOperation()
override {
870 if (useRankReducingSlices) {
871 options.rankReductionStrategy = linalg::ControlDropUnitDims::
872 RankReductionStrategy::ExtractInsertSlice;
884 RewritePatternSet patterns(context);
899static SmallVector<ReassociationIndices>
900getReassociationForReshapeAtDim(int64_t rank, int64_t pos) {
901 SmallVector<ReassociationIndices> reassociation(rank - 1, {0, 1});
902 bool lastDim = pos == rank - 1;
904 for (int64_t i = 0; i < rank - 1; i++) {
905 if (i == pos || (lastDim && i == pos - 1))
913 return reassociation;
918static Value collapseSingletonDimAt(PatternRewriter &rewriter, Value val,
922 auto valType = cast<ShapedType>(val.
getType());
923 SmallVector<int64_t> collapsedShape(valType.getShape());
924 collapsedShape.erase(collapsedShape.begin() + pos);
926 FailureOr<Value> collapsed = control.
collapseFn(
927 rewriter, val.
getLoc(), val, collapsedShape,
928 getReassociationForReshapeAtDim(valType.getRank(), pos), control);
929 assert(llvm::succeeded(collapsed) &&
"Collapsing the value failed");
930 return collapsed.value();
939template <
typename FromOpTy,
typename ToOpTy>
940struct RankReduceContractionOps : OpRewritePattern<FromOpTy> {
941 using OpRewritePattern<FromOpTy>::OpRewritePattern;
945 collapseOperands(PatternRewriter &rewriter, ArrayRef<Value> operands,
946 ArrayRef<int64_t> operandCollapseDims)
const {
947 assert(operandCollapseDims.size() == 3 && operands.size() == 3 &&
948 "expected 3 operands and dims");
949 return llvm::map_to_vector(
950 llvm::zip(operands, operandCollapseDims), [&](
auto pair) {
951 return collapseSingletonDimAt(rewriter, std::get<0>(pair),
957 Value expandResult(PatternRewriter &rewriter, Value
result,
958 RankedTensorType expandedType, int64_t dim)
const {
959 return tensor::ExpandShapeOp::create(
961 getReassociationForReshapeAtDim(expandedType.getRank(), dim));
964 LogicalResult matchAndRewrite(FromOpTy contractionOp,
965 PatternRewriter &rewriter)
const override {
966 if (contractionOp.hasUserDefinedMaps()) {
968 contractionOp,
"ops with user-defined maps are not supported");
971 if constexpr ((std::is_same_v<FromOpTy, MatmulOp> ||
972 std::is_same_v<FromOpTy, BatchMatmulOp>) &&
973 !std::is_same_v<ToOpTy, MatmulOp>) {
974 if (contractionOp.getCast() != TypeFn::cast_signed)
976 contractionOp,
"target op cannot represent a non-signed cast");
979 auto loc = contractionOp.getLoc();
980 auto inputs = contractionOp.getDpsInputs();
981 auto inits = contractionOp.getDpsInits();
982 if (inputs.size() != 2 || inits.size() != 1)
984 "expected 2 inputs and 1 init");
985 auto lhs = inputs[0];
986 auto rhs = inputs[1];
987 auto init = inits[0];
988 SmallVector<Value> operands{
lhs,
rhs, init};
990 SmallVector<int64_t> operandUnitDims;
991 if (
failed(getOperandUnitDims(contractionOp, operandUnitDims)))
993 "no reducable dims found");
995 SmallVector<Value> collapsedOperands =
996 collapseOperands(rewriter, operands, operandUnitDims);
997 Value collapsedLhs = collapsedOperands[0];
998 Value collapsedRhs = collapsedOperands[1];
999 Value collapsedInit = collapsedOperands[2];
1000 SmallVector<Type, 1> collapsedResultTy;
1001 if (isa<RankedTensorType>(collapsedInit.
getType()))
1002 collapsedResultTy.push_back(collapsedInit.
getType());
1004 if constexpr (std::is_same_v<FromOpTy, BatchMatmulOp> &&
1005 std::is_same_v<ToOpTy, MatmulOp>) {
1006 if (TypeFnAttr castAttr = contractionOp.getCastAttr()) {
1007 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
1011 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
1016 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
1020 for (
auto attr : contractionOp->getDiscardableAttrDictionary()) {
1021 if (attr.getName() == LinalgDialect::kMemoizedIndexingMapsAttrName ||
1022 attr.getName() ==
"indexing_maps")
1024 collapsedOp->setDiscardableAttr(attr.getName(), attr.getValue());
1027 auto results = contractionOp.getResults();
1028 assert(results.size() < 2 &&
"expected at most one result");
1029 if (results.empty()) {
1030 rewriter.
replaceOp(contractionOp, collapsedOp);
1034 expandResult(rewriter, collapsedOp.getResultTensors()[0],
1035 cast<RankedTensorType>(results[0].getType()),
1036 operandUnitDims[2]));
1045 virtual LogicalResult
1046 getOperandUnitDims(LinalgOp op,
1047 SmallVectorImpl<int64_t> &operandUnitDims)
const = 0;
1051template <
typename FromOpTy,
typename ToOpTy>
1052struct RankReduceToUnBatched : RankReduceContractionOps<FromOpTy, ToOpTy> {
1053 using RankReduceContractionOps<FromOpTy, ToOpTy>::RankReduceContractionOps;
1057 getOperandUnitDims(LinalgOp op,
1058 SmallVectorImpl<int64_t> &operandUnitDims)
const override {
1059 FailureOr<ContractionDimensions> maybeContractionDims =
1061 if (
failed(maybeContractionDims)) {
1062 LLVM_DEBUG(llvm::dbgs() <<
"could not infer contraction dims");
1065 const ContractionDimensions &contractionDims = maybeContractionDims.value();
1067 if (contractionDims.
batch.size() != 1)
1069 auto batchDim = contractionDims.
batch[0];
1070 SmallVector<std::pair<Value, unsigned>, 3> bOperands;
1071 op.mapIterationSpaceDimToAllOperandDims(batchDim, bOperands);
1072 if (bOperands.size() != 3 || llvm::any_of(bOperands, [](
auto pair) {
1073 return cast<ShapedType>(std::get<0>(pair).getType())
1074 .getShape()[std::get<1>(pair)] != 1;
1076 LLVM_DEBUG(llvm::dbgs() <<
"specified unit dims not found");
1080 operandUnitDims = SmallVector<int64_t>{std::get<1>(bOperands[0]),
1081 std::get<1>(bOperands[1]),
1082 std::get<1>(bOperands[2])};
1088template <
typename FromOpTy,
typename ToOpTy>
1089struct RankReduceMatmul : RankReduceContractionOps<FromOpTy, ToOpTy> {
1090 using RankReduceContractionOps<FromOpTy, ToOpTy>::RankReduceContractionOps;
1093 static bool constexpr reduceLeft =
1094 (std::is_same_v<FromOpTy, BatchMatmulOp> &&
1095 std::is_same_v<ToOpTy, BatchVecmatOp>) ||
1096 (std::is_same_v<FromOpTy, MatmulOp> &&
1097 std::is_same_v<ToOpTy, VecmatOp>) ||
1098 (std::is_same_v<FromOpTy, MatvecOp> && std::is_same_v<ToOpTy, DotOp>);
1102 getOperandUnitDims(LinalgOp op,
1103 SmallVectorImpl<int64_t> &operandUnitDims)
const override {
1104 FailureOr<ContractionDimensions> maybeContractionDims =
1106 if (
failed(maybeContractionDims)) {
1107 LLVM_DEBUG(llvm::dbgs() <<
"could not infer contraction dims");
1110 const ContractionDimensions &contractionDims = maybeContractionDims.value();
1112 if constexpr (reduceLeft) {
1113 auto m = contractionDims.
m[0];
1114 SmallVector<std::pair<Value, unsigned>, 2> mOperands;
1115 op.mapIterationSpaceDimToAllOperandDims(m, mOperands);
1116 if (mOperands.size() != 2)
1118 if (llvm::all_of(mOperands, [](
auto pair) {
1119 return cast<ShapedType>(std::get<0>(pair).
getType())
1120 .getShape()[std::get<1>(pair)] == 1;
1122 operandUnitDims = SmallVector<int64_t>{std::get<1>(mOperands[0]), -1,
1123 std::get<1>(mOperands[1])};
1127 auto n = contractionDims.
n[0];
1128 SmallVector<std::pair<Value, unsigned>, 2> nOperands;
1129 op.mapIterationSpaceDimToAllOperandDims(n, nOperands);
1130 if (nOperands.size() != 2)
1132 if (llvm::all_of(nOperands, [](
auto pair) {
1133 return cast<ShapedType>(std::get<0>(pair).
getType())
1134 .getShape()[std::get<1>(pair)] == 1;
1136 operandUnitDims = SmallVector<int64_t>{-1, std::get<1>(nOperands[0]),
1137 std::get<1>(nOperands[1])};
1141 LLVM_DEBUG(llvm::dbgs() <<
"specified unit dims not found");
1152 patterns.
add<RankReduceToUnBatched<BatchMatmulOp, MatmulOp>>(context);
1153 patterns.
add<RankReduceToUnBatched<BatchMatvecOp, MatvecOp>>(context);
1154 patterns.
add<RankReduceToUnBatched<BatchVecmatOp, VecmatOp>>(context);
1157 patterns.
add<RankReduceMatmul<MatmulOp, VecmatOp>>(context);
1158 patterns.
add<RankReduceMatmul<MatmulOp, MatvecOp>>(context);
1160 patterns.
add<RankReduceMatmul<BatchMatmulOp, BatchVecmatOp>>(context);
1161 patterns.
add<RankReduceMatmul<BatchMatmulOp, BatchMatvecOp>>(context);
1164 patterns.
add<RankReduceMatmul<MatvecOp, DotOp>>(context);
1165 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.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
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.
Block * getParentBlock()
Return the Block in which this Value is defined.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
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...