MLIR 24.0.0git
VectorDropLeadUnitDim.cpp
Go to the documentation of this file.
1//===- VectorDropLeadUnitDim.cpp - Conversion within the Vector dialect ---===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include <numeric>
10
16#include "mlir/IR/Builders.h"
18#include "llvm/ADT/STLExtras.h"
19
20#define DEBUG_TYPE "vector-drop-unit-dim"
21
22using namespace mlir;
23using namespace mlir::vector;
24
25// Trims leading one dimensions from `oldType` and returns the result type.
26// Returns `vector<1xT>` if `oldType` only has one element.
27static VectorType trimLeadingUnitDims(VectorType oldType) {
28 ArrayRef<int64_t> oldShape = oldType.getShape();
29 ArrayRef<int64_t> newShape = oldShape;
30
31 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
32 ArrayRef<bool> newScalableDims = oldScalableDims;
33
34 while (!newShape.empty() && newShape.front() == 1 &&
35 !newScalableDims.front()) {
36 newShape = newShape.drop_front(1);
37 newScalableDims = newScalableDims.drop_front(1);
38 }
39
40 // Make sure we have at least 1 dimension per vector type requirements.
41 if (newShape.empty()) {
42 newShape = oldShape.take_back();
43 newScalableDims = oldType.getScalableDims().take_back();
44 }
45 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
46}
47
48/// Return a smallVector of size `rank` containing all zeros.
50 return SmallVector<int64_t>(rank, 0);
51}
52
54 ValueRange operands,
55 TypeRange resultTypes) {
56 OperationState state(op->getLoc(), op->getName(), operands, resultTypes,
57 op->getDiscardableAttrDictionary().getValue());
59 return builder.create(state);
60}
61namespace {
62
63// Casts away leading one dimensions in vector.extract_strided_slice's vector
64// input by inserting vector.broadcast.
65struct CastAwayExtractStridedSliceLeadingOneDim
66 : public OpRewritePattern<vector::ExtractStridedSliceOp> {
67 using Base::Base;
68
69 LogicalResult matchAndRewrite(vector::ExtractStridedSliceOp extractOp,
70 PatternRewriter &rewriter) const override {
71 // vector.extract_strided_slice requires the input and output vector to have
72 // the same rank. Here we drop leading one dimensions from the input vector
73 // type to make sure we don't cause mismatch.
74 VectorType oldSrcType = extractOp.getSourceVectorType();
75 VectorType newSrcType = trimLeadingUnitDims(oldSrcType);
76
77 if (newSrcType.getRank() == oldSrcType.getRank())
78 return failure();
79
80 int64_t dropCount = oldSrcType.getRank() - newSrcType.getRank();
81
82 VectorType oldDstType = extractOp.getType();
83 VectorType newDstType =
84 VectorType::get(oldDstType.getShape().drop_front(dropCount),
85 oldDstType.getElementType(),
86 oldDstType.getScalableDims().drop_front(dropCount));
87
88 Location loc = extractOp.getLoc();
89
90 Value newSrcVector = rewriter.createOrFold<ShapeCastOp>(
91 loc, newSrcType, extractOp.getSource());
92
93 // The offsets/sizes/strides attribute can have a less number of elements
94 // than the input vector's rank: it is meant for the leading dimensions.
95 auto newOffsets = rewriter.getArrayAttr(
96 extractOp.getOffsets().getValue().drop_front(dropCount));
97 auto newSizes = rewriter.getArrayAttr(
98 extractOp.getSizes().getValue().drop_front(dropCount));
99 auto newStrides = rewriter.getArrayAttr(
100 extractOp.getStrides().getValue().drop_front(dropCount));
101
102 auto newExtractOp = vector::ExtractStridedSliceOp::create(
103 rewriter, loc, newDstType, newSrcVector, newOffsets, newSizes,
104 newStrides);
105
106 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(extractOp, oldDstType,
107 newExtractOp);
108
109 return success();
110 }
111};
112
113// Casts away leading one dimensions in vector.insert_strided_slice's vector
114// inputs by inserting vector.broadcast.
115struct CastAwayInsertStridedSliceLeadingOneDim
116 : public OpRewritePattern<vector::InsertStridedSliceOp> {
117 using Base::Base;
118
119 LogicalResult matchAndRewrite(vector::InsertStridedSliceOp insertOp,
120 PatternRewriter &rewriter) const override {
121 VectorType oldSrcType = insertOp.getSourceVectorType();
122 VectorType newSrcType = trimLeadingUnitDims(oldSrcType);
123 VectorType oldDstType = insertOp.getDestVectorType();
124 VectorType newDstType = trimLeadingUnitDims(oldDstType);
125
126 int64_t srcDropCount = oldSrcType.getRank() - newSrcType.getRank();
127 int64_t dstDropCount = oldDstType.getRank() - newDstType.getRank();
128 if (srcDropCount == 0 && dstDropCount == 0)
129 return failure();
130
131 // Trim leading one dimensions from both operands.
132 Location loc = insertOp.getLoc();
133
134 Value newSrcVector = rewriter.createOrFold<vector::ShapeCastOp>(
135 loc, newSrcType, insertOp.getValueToStore());
136 Value newDstVector = rewriter.createOrFold<vector::ShapeCastOp>(
137 loc, newDstType, insertOp.getDest());
138
139 auto newOffsets = rewriter.getArrayAttr(
140 insertOp.getOffsets().getValue().take_back(newDstType.getRank()));
141 auto newStrides = rewriter.getArrayAttr(
142 insertOp.getStrides().getValue().take_back(newSrcType.getRank()));
143
144 auto newInsertOp = vector::InsertStridedSliceOp::create(
145 rewriter, loc, newDstType, newSrcVector, newDstVector, newOffsets,
146 newStrides);
147
148 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(insertOp, oldDstType,
149 newInsertOp);
150
151 return success();
152 }
153};
154
155// Casts away leading one dimensions in vector.insert's vector inputs by
156// inserting vector.shape_cast.
157struct CastAwayInsertLeadingOneDim : public OpRewritePattern<vector::InsertOp> {
158 using Base::Base;
159
160 LogicalResult matchAndRewrite(vector::InsertOp insertOp,
161 PatternRewriter &rewriter) const override {
162 Type oldSrcType = insertOp.getValueToStoreType();
163 Type newSrcType = oldSrcType;
164 int64_t oldSrcRank = 0, newSrcRank = 0;
165 if (auto type = dyn_cast<VectorType>(oldSrcType)) {
166 newSrcType = trimLeadingUnitDims(type);
167 oldSrcRank = type.getRank();
168 newSrcRank = cast<VectorType>(newSrcType).getRank();
169 }
170
171 VectorType oldDstType = insertOp.getDestVectorType();
172 VectorType newDstType = trimLeadingUnitDims(oldDstType);
173
174 int64_t srcDropCount = oldSrcRank - newSrcRank;
175 int64_t dstDropCount = oldDstType.getRank() - newDstType.getRank();
176 if (srcDropCount == 0 && dstDropCount == 0)
177 return failure();
178
179 // Trim leading one dimensions from both operands.
180 Location loc = insertOp.getLoc();
181
182 Value newSrcVector = insertOp.getValueToStore();
183 if (oldSrcRank != 0) {
184 newSrcVector = rewriter.createOrFold<vector::ShapeCastOp>(
185 loc, cast<VectorType>(newSrcType), insertOp.getValueToStore());
186 }
187 Value newDstVector = rewriter.createOrFold<vector::ShapeCastOp>(
188 loc, newDstType, insertOp.getDest());
189
190 // New position rank needs to be computed in two steps: (1) if destination
191 // type has leading unit dims, we also trim the position array accordingly,
192 // then (2) if source type also has leading unit dims, we need to append
193 // zeroes to the position array accordingly.
194 unsigned oldPosRank = insertOp.getNumIndices();
195 unsigned newPosRank = std::max<int64_t>(0, oldPosRank - dstDropCount);
196 SmallVector<OpFoldResult> oldPosition = insertOp.getMixedPosition();
197 SmallVector<OpFoldResult> newPosition =
198 llvm::to_vector(ArrayRef(oldPosition).take_back(newPosRank));
199 newPosition.resize(newDstType.getRank() - newSrcRank,
200 rewriter.getI64IntegerAttr(0));
201
202 auto newInsertOp = vector::InsertOp::create(rewriter, loc, newSrcVector,
203 newDstVector, newPosition);
204
205 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(insertOp, oldDstType,
206 newInsertOp);
207
208 return success();
209 }
210};
211
212static Value dropUnitDimsFromMask(OpBuilder &b, Location loc, Value mask,
213 VectorType newType, AffineMap newMap) {
214 VectorType newMaskType = inferTransferOpMaskType(newType, newMap);
215
216 return vector::ShapeCastOp::create(b, loc, newMaskType, mask);
217}
218
219// Turns vector.transfer_read on vector with leading 1 dimensions into
220// vector.shape_cast followed by vector.transfer_read on vector without leading
221// 1 dimensions.
222struct CastAwayTransferReadLeadingOneDim
223 : public OpRewritePattern<vector::TransferReadOp> {
224 using Base::Base;
225
226 LogicalResult matchAndRewrite(vector::TransferReadOp read,
227 PatternRewriter &rewriter) const override {
228 // TODO(#78787): Not supported masked op yet.
229 if (cast<MaskableOpInterface>(read.getOperation()).isMasked())
230 return failure();
231
232 if (read.getTransferRank() == 0)
233 return rewriter.notifyMatchFailure(
234 read, "Nothing to trim - the transfer itself has rank zero");
235
236 auto shapedType = cast<ShapedType>(read.getBase().getType());
237 if (shapedType.getElementType() != read.getVectorType().getElementType())
238 return failure();
239
240 VectorType oldType = read.getVectorType();
241 VectorType newType = trimLeadingUnitDims(oldType);
242
243 if (newType == oldType)
244 return failure();
245
246 AffineMap oldMap = read.getPermutationMap();
247 ArrayRef<AffineExpr> newResults =
248 oldMap.getResults().take_back(newType.getRank());
249 AffineMap newMap =
250 AffineMap::get(oldMap.getNumDims(), oldMap.getNumSymbols(), newResults,
251 rewriter.getContext());
252
253 ArrayAttr inBoundsAttr;
254 if (read.getInBounds())
255 inBoundsAttr = rewriter.getArrayAttr(
256 read.getInBoundsAttr().getValue().take_back(newType.getRank()));
257
258 Value mask = Value();
259 if (read.getMask())
260 mask = dropUnitDimsFromMask(rewriter, read.getLoc(), read.getMask(),
261 newType, newMap);
262
263 auto newRead = vector::TransferReadOp::create(
264 rewriter, read.getLoc(), newType, read.getBase(), read.getIndices(),
265 AffineMapAttr::get(newMap), read.getPadding(), mask, inBoundsAttr);
266 rewriter.replaceOpWithNewOp<vector::BroadcastOp>(read, oldType, newRead);
267
268 return success();
269 }
270};
271
272// Turns vector.transfer_write on vector with leading 1 dimensions into
273// vector.shape_cast followed by vector.transfer_write on vector without leading
274// 1 dimensions.
275struct CastAwayTransferWriteLeadingOneDim
276 : public OpRewritePattern<vector::TransferWriteOp> {
277 using Base::Base;
278
279 LogicalResult matchAndRewrite(vector::TransferWriteOp write,
280 PatternRewriter &rewriter) const override {
281 // TODO(#78787): Not supported masked op yet.
282 if (cast<MaskableOpInterface>(write.getOperation()).isMasked())
283 return failure();
284
285 if (write.getTransferRank() == 0)
286 return rewriter.notifyMatchFailure(
287 write, "Nothing to trim - the transfer itself has rank zero");
288
289 auto shapedType = dyn_cast<ShapedType>(write.getBase().getType());
290 if (shapedType.getElementType() != write.getVectorType().getElementType())
291 return failure();
292
293 VectorType oldType = write.getVectorType();
294 VectorType newType = trimLeadingUnitDims(oldType);
295 if (newType == oldType)
296 return failure();
297
298 AffineMap oldMap = write.getPermutationMap();
299 ArrayRef<AffineExpr> newResults =
300 oldMap.getResults().take_back(newType.getRank());
301 AffineMap newMap =
302 AffineMap::get(oldMap.getNumDims(), oldMap.getNumSymbols(), newResults,
303 rewriter.getContext());
304
305 ArrayAttr inBoundsAttr;
306 if (write.getInBounds())
307 inBoundsAttr = rewriter.getArrayAttr(
308 write.getInBoundsAttr().getValue().take_back(newType.getRank()));
309
310 auto newVector = rewriter.createOrFold<vector::ShapeCastOp>(
311 write.getLoc(), newType, write.getVector());
312
313 if (write.getMask()) {
314 Value newMask = dropUnitDimsFromMask(rewriter, write.getLoc(),
315 write.getMask(), newType, newMap);
316 rewriter.replaceOpWithNewOp<vector::TransferWriteOp>(
317 write, newVector, write.getBase(), write.getIndices(),
318 AffineMapAttr::get(newMap), newMask, inBoundsAttr);
319 return success();
320 }
321
322 rewriter.replaceOpWithNewOp<vector::TransferWriteOp>(
323 write, newVector, write.getBase(), write.getIndices(),
324 AffineMapAttr::get(newMap), inBoundsAttr);
325 return success();
326 }
327};
328
329} // namespace
330
331FailureOr<Value>
332mlir::vector::castAwayContractionLeadingOneDim(vector::ContractionOp contractOp,
333 MaskingOpInterface maskingOp,
334 RewriterBase &rewriter) {
335 VectorType oldAccType = dyn_cast<VectorType>(contractOp.getAccType());
336 if (oldAccType == nullptr)
337 return failure();
338 if (oldAccType.getRank() < 1)
339 return failure();
340 if (oldAccType.getShape()[0] != 1)
341 return failure();
342 // currently we support only dropping one dim but the pattern can be applied
343 // greedily to drop more.
344 int64_t dropDim = 1;
345
346 auto oldIndexingMaps = contractOp.getIndexingMapsArray();
347 SmallVector<AffineMap> newIndexingMaps;
348
349 auto oldIteratorTypes = contractOp.getIteratorTypes();
350 SmallVector<Attribute> newIteratorTypes;
351
352 int64_t dimToDrop = oldIndexingMaps[2].getDimPosition(0);
353
354 if (!isParallelIterator(oldIteratorTypes[dimToDrop]))
355 // only parallel type iterators can be dropped.
356 return failure();
357
358 for (const auto &it : llvm::enumerate(oldIteratorTypes)) {
359 int64_t currDim = it.index();
360 if (currDim == dimToDrop)
361 continue;
362 newIteratorTypes.push_back(it.value());
363 }
364
365 SmallVector<Value> operands = {contractOp.getLhs(), contractOp.getRhs(),
366 contractOp.getAcc()};
367 SmallVector<Value> newOperands;
368 auto loc = contractOp.getLoc();
369
370 for (const auto &it : llvm::enumerate(oldIndexingMaps)) {
371 // Check if the dim to be dropped exists as a leading dim in the operand
372 // if it does then we use vector.extract to drop it.
373 bool validExtract = false;
375 auto map = it.value();
376 int64_t orginalZeroDim = it.value().getDimPosition(0);
377 if (orginalZeroDim != dimToDrop) {
378 // There are two reasons to be in this path, 1. We need to
379 // transpose the operand to make the dim to be dropped
380 // leading. 2. The dim to be dropped does not exist and in
381 // that case we dont want to add a unit transpose but we must
382 // check all the indices to make sure this is the case.
383 bool transposeNeeded = false;
385 SmallVector<AffineExpr> transposeResults;
386
387 for (int64_t i = 0, e = map.getNumResults(); i < e; ++i) {
388 int64_t currDim = map.getDimPosition(i);
389 if (currDim == dimToDrop) {
390 transposeNeeded = true;
391 perm.insert(perm.begin(), i);
392 auto targetExpr = rewriter.getAffineDimExpr(currDim);
393 transposeResults.insert(transposeResults.begin(), targetExpr);
394 } else {
395 perm.push_back(i);
396 auto targetExpr = rewriter.getAffineDimExpr(currDim);
397 transposeResults.push_back(targetExpr);
398 }
399 }
400
401 // Checks if only the outer, unit dimensions (of size 1) are permuted.
402 // Such transposes do not materially effect the underlying vector and can
403 // be omitted. EG: perm [1, 0, 2] applied to vector<1x1x8xi32>
404 bool transposeNonOuterUnitDims = false;
405 auto operandShape = cast<ShapedType>(operands[it.index()].getType());
406 for (auto [index, dim] :
407 llvm::enumerate(ArrayRef<int64_t>(perm).drop_back(1))) {
408 if (dim != static_cast<int64_t>(index) &&
409 operandShape.getDimSize(index) != 1) {
410 transposeNonOuterUnitDims = true;
411 break;
412 }
413 }
414
415 // Do the transpose now if needed so that we can drop the
416 // correct dim using extract later.
417 if (transposeNeeded) {
418 map = AffineMap::get(map.getNumDims(), 0, transposeResults,
419 contractOp.getContext());
420 if (transposeNonOuterUnitDims) {
421 operands[it.index()] = rewriter.createOrFold<vector::TransposeOp>(
422 loc, operands[it.index()], perm);
423 }
424 }
425 }
426 // We have taken care to have the dim to be dropped be
427 // the leading dim. If its still not leading that means it
428 // does not exist in this operand and hence we do not need
429 // an extract.
430 if (map.getDimPosition(0) == dimToDrop)
431 validExtract = true;
432
433 for (int64_t i = 0, e = map.getNumResults(); i < e; ++i) {
434 int64_t currDim = map.getDimPosition(i);
435 if (currDim == dimToDrop)
436 // This is the dim we are dropping.
437 continue;
438 auto targetExpr = rewriter.getAffineDimExpr(
439 currDim < dimToDrop ? currDim : currDim - 1);
440 results.push_back(targetExpr);
441 }
442 newIndexingMaps.push_back(AffineMap::get(map.getNumDims() - 1, 0, results,
443 contractOp.getContext()));
444 // Extract if its a valid extraction, otherwise use the operand
445 // without extraction.
446 newOperands.push_back(validExtract
447 ? vector::ExtractOp::create(rewriter, loc,
448 operands[it.index()],
449 splatZero(dropDim))
450 : operands[it.index()]);
451 }
452
453 // Depending on whether this vector.contract is masked, the replacing Op
454 // should either be a new vector.contract Op or vector.mask Op.
455 Operation *newOp = vector::ContractionOp::create(
456 rewriter, loc, newOperands[0], newOperands[1], newOperands[2],
457 rewriter.getAffineMapArrayAttr(newIndexingMaps),
458 rewriter.getArrayAttr(newIteratorTypes), contractOp.getKind());
459
460 if (maskingOp) {
461 auto newMask = vector::ExtractOp::create(rewriter, loc, maskingOp.getMask(),
462 splatZero(dropDim));
463
464 newOp = mlir::vector::maskOperation(rewriter, newOp, newMask);
465 }
466
467 return vector::BroadcastOp::create(rewriter, loc,
468 contractOp->getResultTypes()[0],
469 newOp->getResults()[0])
470 .getResult();
471}
472
473namespace {
474
475/// Turns vector.contract on vector with leading 1 dimensions into
476/// vector.extract followed by vector.contract on vector without leading
477/// 1 dimensions. Also performs transpose of lhs and rhs operands if required
478/// prior to extract.
479struct CastAwayContractionLeadingOneDim
480 : public MaskableOpRewritePattern<vector::ContractionOp> {
481 using MaskableOpRewritePattern::MaskableOpRewritePattern;
482
483 FailureOr<Value>
484 matchAndRewriteMaskableOp(vector::ContractionOp contractOp,
485 MaskingOpInterface maskingOp,
486 PatternRewriter &rewriter) const override {
487 return castAwayContractionLeadingOneDim(contractOp, maskingOp, rewriter);
488 }
489};
490
491/// Looks at elementwise operations on vectors with at least one leading
492/// dimension equal 1, e.g. vector<1x[4]x1xf32> (but not vector<2x[4]x1xf32>),
493/// and cast aways the leading one dimensions (_plural_) and then broadcasts
494/// the results.
495///
496/// Example before:
497/// %1 = arith.mulf %arg0, %arg1 : vector<1x4x1xf32>
498/// Example after:
499/// %2 = arith.mulf %0, %1 : vector<4x1xf32>
500/// %3 = vector.broadcast %2 : vector<4x1xf32> to vector<1x4x1xf32>
501///
502/// Does support scalable vectors.
503class CastAwayElementwiseLeadingOneDim : public RewritePattern {
504public:
505 CastAwayElementwiseLeadingOneDim(MLIRContext *context,
506 PatternBenefit benefit = 1)
507 : RewritePattern(MatchAnyOpTypeTag(), benefit, context) {}
508
509 LogicalResult matchAndRewrite(Operation *op,
510 PatternRewriter &rewriter) const override {
512 return failure();
513 auto vecType = dyn_cast<VectorType>(op->getResultTypes()[0]);
514 if (!vecType)
515 return failure();
516 VectorType newVecType = trimLeadingUnitDims(vecType);
517 if (newVecType == vecType)
518 return failure();
519 int64_t dropDim = vecType.getRank() - newVecType.getRank();
520 SmallVector<Value, 4> newOperands;
521 for (Value operand : op->getOperands()) {
522 if (auto opVecType = dyn_cast<VectorType>(operand.getType())) {
523 newOperands.push_back(vector::ExtractOp::create(
524 rewriter, op->getLoc(), operand, splatZero(dropDim)));
525 } else {
526 newOperands.push_back(operand);
527 }
528 }
529 Operation *newOp =
530 createWithProperties(rewriter, op, newOperands, TypeRange{newVecType});
531 rewriter.replaceOpWithNewOp<vector::BroadcastOp>(op, vecType,
532 newOp->getResult(0));
533 return success();
534 }
535};
536} // namespace
537
538// Drops `dropDim` leading dimensions from `operand` using vector.extract when
539// those dims are all non-scalable units (the cheap, structural rewrite); falls
540// back to vector.shape_cast otherwise.
542 Value operand, int64_t nDropped) {
543 auto oldType = cast<VectorType>(operand.getType());
544 ArrayRef<int64_t> leadingShape = oldType.getShape().take_front(nDropped);
545 ArrayRef<bool> leadingScalable =
546 oldType.getScalableDims().take_front(nDropped);
547 bool extractable =
548 llvm::all_of(leadingShape, [](int64_t d) { return d == 1; }) &&
549 llvm::none_of(leadingScalable, [](bool s) { return s; });
550 if (extractable)
551 return vector::ExtractOp::create(b, loc, operand, splatZero(nDropped));
552 VectorType newType = VectorType::get(
553 oldType.getShape().drop_front(nDropped), oldType.getElementType(),
554 oldType.getScalableDims().drop_front(nDropped));
555 return vector::ShapeCastOp::create(b, loc, newType, operand);
556}
557
558namespace {
559
560// Drops leading 1 dimensions from load-like memory operaitons. REmoves leading
561// unit dimensions from the result types and then broadcasts back in those 1s,
562// while also extracting (or shape_cast-ing) any leading unit dimensions on
563// the input operands.
564template <typename OpTy>
565struct CastAwayLoadLikeLeadingOneDim : public OpRewritePattern<OpTy> {
566 using OpRewritePattern<OpTy>::OpRewritePattern;
567
568 LogicalResult matchAndRewrite(OpTy op,
569 PatternRewriter &rewriter) const override {
570 VectorType oldResultType = op.getVectorType();
571 VectorType newResultType = trimLeadingUnitDims(oldResultType);
572 if (newResultType == oldResultType)
573 return failure();
574 int64_t nDropped = oldResultType.getRank() - newResultType.getRank();
575
576 Location loc = op.getLoc();
577 SmallVector<Value> newOperands;
578 newOperands.reserve(op->getNumOperands());
579 for (Value operand : op->getOperands()) {
580 if (isa<VectorType>(operand.getType())) {
581 newOperands.push_back(
582 dropLeadingOneDimsFromOperand(rewriter, loc, operand, nDropped));
583 } else {
584 newOperands.push_back(operand);
585 }
586 }
587
588 Operation *newOp = createWithProperties(rewriter, op, newOperands,
589 TypeRange{newResultType});
590 rewriter.replaceOpWithNewOp<vector::BroadcastOp>(op, oldResultType,
591 newOp->getResult(0));
592 return success();
593 }
594};
595
596// Drops leading 1 dimensions from store-like memory ops. Extracts or
597// `shape_cast`s away those leading unit dimensions and leaves any scalar
598// operands alone.
599template <typename OpTy>
600struct CastAwayStoreLikeLeadingOneDim : public OpRewritePattern<OpTy> {
601 using OpRewritePattern<OpTy>::OpRewritePattern;
602
603 LogicalResult matchAndRewrite(OpTy op,
604 PatternRewriter &rewriter) const override {
605 VectorType oldVecType = op.getVectorType();
606 VectorType newVecType = trimLeadingUnitDims(oldVecType);
607 if (newVecType == oldVecType)
608 return failure();
609 int64_t nDropped = oldVecType.getRank() - newVecType.getRank();
610
611 Location loc = op.getLoc();
612 SmallVector<Value> newOperands;
613 newOperands.reserve(op->getNumOperands());
614 for (Value operand : op->getOperands()) {
615 if (isa<VectorType>(operand.getType())) {
616 newOperands.push_back(
617 dropLeadingOneDimsFromOperand(rewriter, loc, operand, nDropped));
618 } else {
619 newOperands.push_back(operand);
620 }
621 }
622
623 Operation *newOp =
624 createWithProperties(rewriter, op, newOperands, op->getResultTypes());
625 rewriter.replaceOp(op, newOp->getResults());
626 return success();
627 }
628};
629
630// Drops leading 1 dimensions from vector.constant_mask and inserts a
631// vector.broadcast back to the original shape.
632struct CastAwayConstantMaskLeadingOneDim
633 : public OpRewritePattern<vector::ConstantMaskOp> {
634 using Base::Base;
635
636 LogicalResult matchAndRewrite(vector::ConstantMaskOp mask,
637 PatternRewriter &rewriter) const override {
638 VectorType oldType = mask.getType();
639 VectorType newType = trimLeadingUnitDims(oldType);
640
641 if (newType == oldType)
642 return failure();
643
644 int64_t dropDim = oldType.getRank() - newType.getRank();
645 ArrayRef<int64_t> dimSizes = mask.getMaskDimSizes();
646
647 // If any of the dropped unit dims has a size of `0`, the entire mask is a
648 // zero mask, else the unit dim has no effect on the mask.
649 int64_t flatLeadingSize =
650 llvm::product_of(dimSizes.take_front(dropDim + 1));
651 SmallVector<int64_t> newDimSizes = {flatLeadingSize};
652 newDimSizes.append(dimSizes.begin() + dropDim + 1, dimSizes.end());
653
654 auto newMask = vector::ConstantMaskOp::create(rewriter, mask.getLoc(),
655 newType, newDimSizes);
656 rewriter.replaceOpWithNewOp<vector::BroadcastOp>(mask, oldType, newMask);
657 return success();
658 }
659};
660
661} // namespace
662
663void mlir::vector::populateCastAwayVectorLeadingOneDimPatterns(
664 RewritePatternSet &patterns, PatternBenefit benefit) {
665 patterns
666 .add<CastAwayExtractStridedSliceLeadingOneDim,
667 CastAwayInsertStridedSliceLeadingOneDim, CastAwayInsertLeadingOneDim,
668 CastAwayConstantMaskLeadingOneDim, CastAwayTransferReadLeadingOneDim,
669 CastAwayTransferWriteLeadingOneDim, CastAwayElementwiseLeadingOneDim,
670 CastAwayContractionLeadingOneDim,
671 CastAwayLoadLikeLeadingOneDim<vector::LoadOp>,
672 CastAwayLoadLikeLeadingOneDim<vector::MaskedLoadOp>,
673 CastAwayLoadLikeLeadingOneDim<vector::ExpandLoadOp>,
674 CastAwayLoadLikeLeadingOneDim<vector::GatherOp>,
675 CastAwayStoreLikeLeadingOneDim<vector::StoreOp>,
676 CastAwayStoreLikeLeadingOneDim<vector::MaskedStoreOp>,
677 CastAwayStoreLikeLeadingOneDim<vector::CompressStoreOp>,
678 CastAwayStoreLikeLeadingOneDim<vector::ScatterOp>>(
679 patterns.getContext(), benefit);
680}
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
ArrayAttr()
static SmallVector< int64_t > splatZero(int64_t rank)
Return a smallVector of size rank containing all zeros.
static Value dropLeadingOneDimsFromOperand(OpBuilder &b, Location loc, Value operand, int64_t nDropped)
static VectorType trimLeadingUnitDims(VectorType oldType)
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.
Definition AffineMap.h:46
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)
Definition Builders.cpp:120
AffineExpr getAffineDimExpr(unsigned position)
Definition Builders.cpp:373
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition Builders.cpp:275
MLIRContext * getContext() const
Definition Builders.h:56
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
Definition Builders.cpp:327
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
This class helps build Operations.
Definition Builders.h:210
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...
Definition Builders.h:528
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition Builders.cpp:466
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
Definition Operation.h:553
result_type_range getResultTypes()
Definition Operation.h:453
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
result_range getResults()
Definition Operation.h:440
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
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.
Definition TypeRange.h:40
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
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.
Definition VectorOps.h:151
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.