18#include "llvm/ADT/STLExtras.h"
20#define DEBUG_TYPE "vector-drop-unit-dim"
28 bool trimOnlyOneDim =
false,
29 bool allowRank0 =
false) {
36 while (!newShape.empty() && newShape.front() == 1 &&
37 !newScalableDims.front()) {
38 newShape = newShape.drop_front(1);
39 newScalableDims = newScalableDims.drop_front(1);
46 if (newShape.empty() && !allowRank0) {
47 newShape = oldShape.take_back();
48 newScalableDims = oldType.getScalableDims().take_back();
50 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
58 return llvm::any_of(llvm::seq<int64_t>(numDims),
59 [&](
int64_t dim) {
return !xferOp.isDimInBounds(dim); });
73 return builder.
create(state);
79struct CastAwayExtractStridedSliceLeadingOneDim
83 LogicalResult matchAndRewrite(vector::ExtractStridedSliceOp extractOp,
84 PatternRewriter &rewriter)
const override {
88 VectorType oldSrcType = extractOp.getSourceVectorType();
91 if (newSrcType.getRank() == oldSrcType.getRank())
94 int64_t dropCount = oldSrcType.getRank() - newSrcType.getRank();
96 VectorType oldDstType = extractOp.getType();
97 VectorType newDstType =
98 VectorType::get(oldDstType.getShape().drop_front(dropCount),
99 oldDstType.getElementType(),
100 oldDstType.getScalableDims().drop_front(dropCount));
102 Location loc = extractOp.getLoc();
104 Value newSrcVector = rewriter.
createOrFold<ShapeCastOp>(
105 loc, newSrcType, extractOp.getSource());
110 extractOp.getOffsets().getValue().drop_front(dropCount));
112 extractOp.getSizes().getValue().drop_front(dropCount));
114 extractOp.getStrides().getValue().drop_front(dropCount));
116 auto newExtractOp = vector::ExtractStridedSliceOp::create(
117 rewriter, loc, newDstType, newSrcVector, newOffsets, newSizes,
129struct CastAwayInsertStridedSliceLeadingOneDim
133 LogicalResult matchAndRewrite(vector::InsertStridedSliceOp insertOp,
134 PatternRewriter &rewriter)
const override {
135 VectorType oldSrcType = insertOp.getSourceVectorType();
137 VectorType oldDstType = insertOp.getDestVectorType();
140 int64_t srcDropCount = oldSrcType.getRank() - newSrcType.getRank();
141 int64_t dstDropCount = oldDstType.getRank() - newDstType.getRank();
142 if (srcDropCount == 0 && dstDropCount == 0)
146 Location loc = insertOp.getLoc();
148 Value newSrcVector = rewriter.
createOrFold<vector::ShapeCastOp>(
149 loc, newSrcType, insertOp.getValueToStore());
150 Value newDstVector = rewriter.
createOrFold<vector::ShapeCastOp>(
151 loc, newDstType, insertOp.getDest());
154 insertOp.getOffsets().getValue().take_back(newDstType.getRank()));
156 insertOp.getStrides().getValue().take_back(newSrcType.getRank()));
158 auto newInsertOp = vector::InsertStridedSliceOp::create(
159 rewriter, loc, newDstType, newSrcVector, newDstVector, newOffsets,
171struct CastAwayInsertLeadingOneDim :
public OpRewritePattern<vector::InsertOp> {
174 LogicalResult matchAndRewrite(vector::InsertOp insertOp,
175 PatternRewriter &rewriter)
const override {
176 Type oldSrcType = insertOp.getValueToStoreType();
177 Type newSrcType = oldSrcType;
178 int64_t oldSrcRank = 0, newSrcRank = 0;
179 if (
auto type = dyn_cast<VectorType>(oldSrcType)) {
181 oldSrcRank = type.getRank();
182 newSrcRank = cast<VectorType>(newSrcType).getRank();
185 VectorType oldDstType = insertOp.getDestVectorType();
188 int64_t srcDropCount = oldSrcRank - newSrcRank;
189 int64_t dstDropCount = oldDstType.getRank() - newDstType.getRank();
190 if (srcDropCount == 0 && dstDropCount == 0)
194 Location loc = insertOp.getLoc();
196 Value newSrcVector = insertOp.getValueToStore();
197 if (oldSrcRank != 0) {
198 newSrcVector = rewriter.
createOrFold<vector::ShapeCastOp>(
199 loc, cast<VectorType>(newSrcType), insertOp.getValueToStore());
201 Value newDstVector = rewriter.
createOrFold<vector::ShapeCastOp>(
202 loc, newDstType, insertOp.getDest());
208 unsigned oldPosRank = insertOp.getNumIndices();
209 unsigned newPosRank = std::max<int64_t>(0, oldPosRank - dstDropCount);
210 SmallVector<OpFoldResult> oldPosition = insertOp.getMixedPosition();
211 SmallVector<OpFoldResult> newPosition =
212 llvm::to_vector(ArrayRef(oldPosition).take_back(newPosRank));
213 newPosition.resize(newDstType.getRank() - newSrcRank,
216 auto newInsertOp = vector::InsertOp::create(rewriter, loc, newSrcVector,
217 newDstVector, newPosition);
230 return vector::ShapeCastOp::create(
b, loc, newMaskType, mask);
236struct CastAwayTransferReadLeadingOneDim
240 LogicalResult matchAndRewrite(vector::TransferReadOp read,
241 PatternRewriter &rewriter)
const override {
243 if (cast<MaskableOpInterface>(read.getOperation()).isMasked())
246 if (read.getTransferRank() == 0)
248 read,
"Nothing to trim - the transfer itself has rank zero");
250 auto shapedType = cast<ShapedType>(read.getBase().getType());
251 if (shapedType.getElementType() != read.getVectorType().getElementType())
254 VectorType oldType = read.getVectorType();
257 if (newType == oldType)
263 AffineMap oldMap = read.getPermutationMap();
264 ArrayRef<AffineExpr> newResults =
265 oldMap.
getResults().take_back(newType.getRank());
271 if (read.getInBounds())
273 read.getInBoundsAttr().getValue().take_back(newType.getRank()));
275 Value mask = Value();
277 mask = dropUnitDimsFromMask(rewriter, read.getLoc(), read.getMask(),
280 auto newRead = vector::TransferReadOp::create(
281 rewriter, read.getLoc(), newType, read.getBase(), read.getIndices(),
282 AffineMapAttr::get(newMap), read.getPadding(), mask, inBoundsAttr);
292struct CastAwayTransferWriteLeadingOneDim
296 LogicalResult matchAndRewrite(vector::TransferWriteOp write,
297 PatternRewriter &rewriter)
const override {
299 if (cast<MaskableOpInterface>(write.getOperation()).isMasked())
302 if (write.getTransferRank() == 0)
304 write,
"Nothing to trim - the transfer itself has rank zero");
306 auto shapedType = dyn_cast<ShapedType>(write.getBase().getType());
307 if (shapedType.getElementType() != write.getVectorType().getElementType())
310 VectorType oldType = write.getVectorType();
312 if (newType == oldType)
319 ArrayRef<AffineExpr> newResults =
320 oldMap.
getResults().take_back(newType.getRank());
326 if (write.getInBounds())
328 write.getInBoundsAttr().getValue().take_back(newType.getRank()));
330 auto newVector = rewriter.
createOrFold<vector::ShapeCastOp>(
331 write.getLoc(), newType, write.getVector());
333 if (write.getMask()) {
334 Value newMask = dropUnitDimsFromMask(rewriter, write.getLoc(),
335 write.getMask(), newType, newMap);
337 write, newVector, write.getBase(), write.getIndices(),
338 AffineMapAttr::get(newMap), newMask, inBoundsAttr);
343 write, newVector, write.getBase(), write.getIndices(),
344 AffineMapAttr::get(newMap), inBoundsAttr);
358 auto oldValTy = cast<VectorType>(oldVal.
getType());
359 if (oldValTy.getRank() == 1) {
360 return rewriter.
createOrFold<ExtractOp>(loc, oldVal, 0);
379 if (!isa<VectorType>(oldVal.
getType())) {
380 return rewriter.
createOrFold<BroadcastOp>(loc, newTy, oldVal);
383 return rewriter.
createOrFold<ShapeCastOp>(loc, newTy, oldVal);
387mlir::vector::castAwayContractionLeadingOneDim(vector::ContractionOp contractOp,
388 MaskingOpInterface maskingOp,
390 VectorType oldAccType = dyn_cast<VectorType>(contractOp.getAccType());
391 if (oldAccType ==
nullptr)
393 if (oldAccType.getRank() < 1)
395 if (oldAccType.getShape()[0] != 1 || oldAccType.getScalableDims()[0])
398 auto oldIndexingMaps = contractOp.getIndexingMapsArray();
401 auto oldIteratorTypes = contractOp.getIteratorTypes();
405 int64_t dimToDrop = oldIndexingMaps[2].getDimPosition(0);
411 for (
const auto &it : llvm::enumerate(oldIteratorTypes)) {
413 if (currDim == dimToDrop)
415 newIteratorTypes.push_back(it.value());
419 contractOp.getAcc()};
421 auto loc = contractOp.getLoc();
423 for (
const auto &it : llvm::enumerate(oldIndexingMaps)) {
426 bool validExtract =
false;
428 auto map = it.value();
429 int64_t orginalZeroDim = it.value().getDimPosition(0);
430 if (orginalZeroDim != dimToDrop) {
436 bool transposeNeeded =
false;
440 for (
int64_t i = 0, e = map.getNumResults(); i < e; ++i) {
441 int64_t currDim = map.getDimPosition(i);
442 if (currDim == dimToDrop) {
443 transposeNeeded =
true;
444 perm.insert(perm.begin(), i);
446 transposeResults.insert(transposeResults.begin(), targetExpr);
450 transposeResults.push_back(targetExpr);
457 bool transposeNonOuterUnitDims =
false;
458 auto operandShape = cast<ShapedType>(operands[it.index()].getType());
459 for (
auto [
index, dim] :
462 operandShape.getDimSize(
index) != 1) {
463 transposeNonOuterUnitDims =
true;
470 if (transposeNeeded) {
472 contractOp.getContext());
473 if (transposeNonOuterUnitDims) {
481 operands[it.index()] = rewriter.
createOrFold<vector::TransposeOp>(
482 loc, operands[it.index()], perm);
490 if (map.getDimPosition(0) == dimToDrop)
493 for (
int64_t i = 0, e = map.getNumResults(); i < e; ++i) {
494 int64_t currDim = map.getDimPosition(i);
495 if (currDim == dimToDrop)
499 currDim < dimToDrop ? currDim : currDim - 1);
500 results.push_back(targetExpr);
502 newIndexingMaps.push_back(
AffineMap::get(map.getNumDims() - 1, 0, results,
503 contractOp.getContext()));
506 auto oldVal = operands[it.index()];
507 newOperands.push_back(
515 Operation *newOp = vector::ContractionOp::create(
516 rewriter, loc, newOperands[0], newOperands[1], newOperands[2],
518 rewriter.
getArrayAttr(newIteratorTypes), contractOp.getKind(),
519 contractOp.getFastmath());
526 maskingOp.getMask());
532 rewriter, loc, newOp->
getResult(0), contractOp->getResultTypes()[0]);
546struct CastAwayContractionLeadingOneDim
548 using MaskableOpRewritePattern::MaskableOpRewritePattern;
551 matchAndRewriteMaskableOp(vector::ContractionOp contractOp,
552 MaskingOpInterface maskingOp,
553 PatternRewriter &rewriter)
const override {
554 return castAwayContractionLeadingOneDim(contractOp, maskingOp, rewriter);
572 CastAwayElementwiseLeadingOneDim(MLIRContext *context,
573 PatternBenefit benefit = 1)
574 : RewritePattern(MatchAnyOpTypeTag(), benefit, context) {}
576 LogicalResult matchAndRewrite(Operation *op,
577 PatternRewriter &rewriter)
const override {
584 if (newVecType == vecType)
586 int64_t dropDim = vecType.getRank() - newVecType.getRank();
587 SmallVector<Value, 4> newOperands;
589 if (
auto opVecType = dyn_cast<VectorType>(operand.getType())) {
590 newOperands.push_back(vector::ExtractOp::create(
593 newOperands.push_back(operand);
610 auto oldType = cast<VectorType>(operand.
getType());
613 oldType.getScalableDims().take_front(nDropped);
615 llvm::all_of(leadingShape, [](
int64_t d) {
return d == 1; }) &&
616 llvm::none_of(leadingScalable, [](
bool s) {
return s; });
618 return vector::ExtractOp::create(
b, loc, operand,
splatZero(nDropped));
619 VectorType newType = VectorType::get(
620 oldType.getShape().drop_front(nDropped), oldType.getElementType(),
621 oldType.getScalableDims().drop_front(nDropped));
622 return vector::ShapeCastOp::create(
b, loc, newType, operand);
631template <
typename OpTy>
633 using OpRewritePattern<OpTy>::OpRewritePattern;
635 LogicalResult matchAndRewrite(OpTy op,
636 PatternRewriter &rewriter)
const override {
637 VectorType oldResultType = op.getVectorType();
639 if (newResultType == oldResultType)
641 int64_t nDropped = oldResultType.getRank() - newResultType.getRank();
643 Location loc = op.getLoc();
644 SmallVector<Value> newOperands;
645 newOperands.reserve(op->getNumOperands());
646 for (Value operand : op->getOperands()) {
647 if (isa<VectorType>(operand.getType())) {
648 newOperands.push_back(
651 newOperands.push_back(operand);
666template <
typename OpTy>
668 using OpRewritePattern<OpTy>::OpRewritePattern;
670 LogicalResult matchAndRewrite(OpTy op,
671 PatternRewriter &rewriter)
const override {
672 VectorType oldVecType = op.getVectorType();
674 if (newVecType == oldVecType)
676 int64_t nDropped = oldVecType.getRank() - newVecType.getRank();
678 Location loc = op.getLoc();
679 SmallVector<Value> newOperands;
680 newOperands.reserve(op->getNumOperands());
681 for (Value operand : op->getOperands()) {
682 if (isa<VectorType>(operand.getType())) {
683 newOperands.push_back(
686 newOperands.push_back(operand);
699struct CastAwayConstantMaskLeadingOneDim
703 LogicalResult matchAndRewrite(vector::ConstantMaskOp mask,
704 PatternRewriter &rewriter)
const override {
705 VectorType oldType = mask.
getType();
708 if (newType == oldType)
711 int64_t dropDim = oldType.getRank() - newType.getRank();
712 ArrayRef<int64_t> dimSizes = mask.getMaskDimSizes();
716 int64_t flatLeadingSize =
717 llvm::product_of(dimSizes.take_front(dropDim + 1));
718 SmallVector<int64_t> newDimSizes = {flatLeadingSize};
719 newDimSizes.append(dimSizes.begin() + dropDim + 1, dimSizes.end());
721 auto newMask = vector::ConstantMaskOp::create(rewriter, mask.
getLoc(),
722 newType, newDimSizes);
730void mlir::vector::populateCastAwayVectorLeadingOneDimPatterns(
733 .
add<CastAwayExtractStridedSliceLeadingOneDim,
734 CastAwayInsertStridedSliceLeadingOneDim, CastAwayInsertLeadingOneDim,
735 CastAwayConstantMaskLeadingOneDim, CastAwayTransferReadLeadingOneDim,
736 CastAwayTransferWriteLeadingOneDim, CastAwayElementwiseLeadingOneDim,
737 CastAwayContractionLeadingOneDim,
738 CastAwayLoadLikeLeadingOneDim<vector::LoadOp>,
739 CastAwayLoadLikeLeadingOneDim<vector::MaskedLoadOp>,
740 CastAwayLoadLikeLeadingOneDim<vector::ExpandLoadOp>,
741 CastAwayLoadLikeLeadingOneDim<vector::GatherOp>,
742 CastAwayStoreLikeLeadingOneDim<vector::StoreOp>,
743 CastAwayStoreLikeLeadingOneDim<vector::MaskedStoreOp>,
744 CastAwayStoreLikeLeadingOneDim<vector::CompressStoreOp>,
745 CastAwayStoreLikeLeadingOneDim<vector::ScatterOp>>(
static VectorType trimLeadingUnitDims(VectorType oldType, bool trimOnlyOneDim=false, bool allowRank0=false)
static SmallVector< int64_t > splatZero(int64_t rank)
Return a smallVector of size rank containing all zeros.
static bool isLeadingDimOutOfBounds(VectorTransferOpInterface xferOp, int64_t numDims)
static Value restoreLeadingUnitDimViaShapeCastOrBcast(RewriterBase &rewriter, Location loc, mlir::Value oldVal, mlir::Type newTy)
static Value dropLeadingUnitDimViaShapeCastOrExtract(RewriterBase &rewriter, Location loc, mlir::Value oldVal)
static Value dropLeadingOneDimsFromOperand(OpBuilder &b, Location loc, Value operand, int64_t nDropped)
static Operation * createWithProperties(OpBuilder &builder, Operation *op, ValueRange operands, TypeRange resultTypes)
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
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
IntegerAttr getI64IntegerAttr(int64_t value)
AffineExpr getAffineDimExpr(unsigned position)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
MLIRContext * getContext() const
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
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...
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
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.
RewritePattern is the common base class for all DAG to DAG replacements.
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 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.
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.
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
Operation * maskOperation(OpBuilder &builder, Operation *maskableOp, Value mask, Value passthru=Value())
Creates a vector.mask operation around a maskable operation.
VectorType inferTransferOpMaskType(VectorType vecType, AffineMap permMap)
Infers the mask type for a transfer op given its vector type and permutation map.
bool isParallelIterator(Attribute attr)
Returns true if attr has "parallel" iterator type semantics.
Include the generated interface declarations.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Attribute propertiesAttr
This Attribute is used to opaquely construct the properties of the operation.
A pattern for ops that implement MaskableOpInterface and that might be masked (i.e.