31#include "llvm/ADT/DenseSet.h"
32#include "llvm/Support/DebugLog.h"
33#include "llvm/Support/InterleavedRange.h"
35#define DEBUG_TYPE "vector-utils"
43 if (isa<UnrankedMemRefType, MemRefType>(source.
getType()))
44 return b.createOrFold<memref::DimOp>(loc, source, dim);
45 if (isa<UnrankedTensorType, RankedTensorType>(source.
getType()))
46 return b.createOrFold<tensor::DimOp>(loc, source, dim);
47 llvm_unreachable(
"Expected MemRefType or TensorType");
73 for (
int64_t permDim : transp) {
80 llvm_unreachable(
"Ill-formed transpose pattern");
83FailureOr<std::pair<int, int>>
85 VectorType srcType = op.getSourceVectorType();
87 for (
auto [
index, size] : llvm::enumerate(srcType.getShape()))
89 srcGtOneDims.push_back(
index);
91 if (srcGtOneDims.size() != 2)
101 return std::pair<int, int>(srcGtOneDims[0], srcGtOneDims[1]);
129 if (enclosingLoopToVectorDim.empty())
132 enclosingLoopToVectorDim.begin()->getFirst()->getContext();
136 for (
auto kvp : enclosingLoopToVectorDim) {
137 assert(kvp.second < perm.size());
139 cast<affine::AffineForOp>(kvp.first).getInductionVar(),
indices);
140 unsigned numIndices =
indices.size();
141 unsigned countInvariantIndices = 0;
142 for (
unsigned dim = 0; dim < numIndices; ++dim) {
143 if (!invariants.count(
indices[dim])) {
145 "permutationMap already has an entry along dim");
148 ++countInvariantIndices;
151 assert((countInvariantIndices == numIndices ||
152 countInvariantIndices == numIndices - 1) &&
153 "Vectorization prerequisite violated: at most 1 index may be "
154 "invariant wrt a vectorized loop");
155 (
void)countInvariantIndices;
169 if ([[maybe_unused]]
auto typedParent = dyn_cast<T>(current)) {
170 assert(res.count(current) == 0 &&
"Already inserted");
173 current = current->getParentOp();
188 for (
auto *forInst : enclosingLoops) {
189 auto it = loopToVectorDim.find(forInst);
190 if (it != loopToVectorDim.end()) {
191 enclosingLoopToVectorDim.insert(*it);
194 return ::makePermutationMap(
indices, enclosingLoopToVectorDim);
203bool matcher::operatesOnSuperVectorsOf(
Operation &op,
204 VectorType subVectorType) {
214 VectorType superVectorType;
215 if (
auto transfer = dyn_cast<VectorTransferOpInterface>(op)) {
216 superVectorType = transfer.getVectorType();
218 if (!isa<func::ReturnOp>(op)) {
219 op.
emitError(
"NYI: assuming only return operations can have 0 "
220 " results at this point");
233 op.
emitError(
"NYI: operation has more than 1 result");
242 return ratio.has_value();
246 if (vectorType.isScalable())
251 vectorType.getShape().drop_while([](
auto v) {
return v == 1; });
258 if (!memrefType.areTrailingDimsContiguous(vecRank))
262 auto memrefShape = memrefType.getShape().take_back(vecRank);
266 return llvm::equal(
vectorShape.drop_front(), memrefShape.drop_front());
269std::optional<StaticTileOffsetRange>
271 if (vType.getRank() <= targetRank)
275 auto shapeToUnroll = vType.getShape().drop_back(targetRank);
276 auto inputScalableVecDimsToUnroll =
277 vType.getScalableDims().drop_back(targetRank);
278 const auto *it = llvm::find(inputScalableVecDimsToUnroll,
true);
279 auto firstScalableDim = it - inputScalableVecDimsToUnroll.begin();
280 if (firstScalableDim == 0)
283 inputScalableVecDimsToUnroll =
284 inputScalableVecDimsToUnroll.slice(0, firstScalableDim);
285 assert(!llvm::is_contained(inputScalableVecDimsToUnroll,
true) &&
286 "unexpected leading scalable dimension");
288 shapeToUnroll = shapeToUnroll.slice(0, firstScalableDim);
295 auto loc = xfer->
getLoc();
299 .Case([&](vector::TransferReadOp readOp) {
return readOp.getBase(); })
300 .Case([&](vector::TransferWriteOp writeOp) {
301 return writeOp.getOperand(1);
307 return mixedSourceDims;
311 return (type.getRank() > 1) && (type.getNumScalableDims() <= 1);
364 if (ShapedType::isDynamicShape(baseShape))
369 for (
auto [i, dimSize] : llvm::enumerate(maskSizes)) {
371 cstMaskSizes.push_back(*intSize);
376 if (cstMaskSizes.size() != maskShape.size())
381 for (
auto [i, idx] : llvm::enumerate(
indices)) {
384 cstIndices.push_back(intVal.getSExtValue());
389 if (cstIndices.size() != baseShape.size())
399 int64_t rankDiff = baseShape.size() - cstMaskSizes.size();
400 for (
auto [i, idx] : llvm::enumerate(cstMaskSizes)) {
401 if ( maskShape[i] > baseShape[rankDiff + i] ||
402 baseShape[rankDiff + i] <
403 (std::clamp(cstMaskSizes[i],
int64_t(0), maskShape[i]) +
405 maskShape[i] > cstMaskSizes[i])
415 std::optional<Value> padValue,
416 bool useInBoundsInsteadOfMasking,
418 VectorType vecToReadTy = VectorType::get(
419 inputVectorSizes, cast<ShapedType>(source.
getType()).getElementType(),
420 inputScalableVecDims);
423 useInBoundsInsteadOfMasking);
431 AffineMap permutationMap, VectorType vectorType, ShapedType sourceType) {
433 for (
unsigned i = 0; i < (
unsigned)vectorType.getRank(); ++i) {
435 if (
auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
436 unsigned memDim = dimExpr.getPosition();
437 if (!sourceType.isDynamicDim(memDim) &&
438 sourceType.getDimSize(memDim) % vectorType.getDimSize(i) == 0)
440 }
else if (isa<AffineConstantExpr>(expr)) {
449 const VectorType &vecToReadTy,
450 std::optional<Value> padValue,
451 bool useInBoundsInsteadOfMasking,
454 assert(!llvm::is_contained(vecToReadTy.getScalableDims(),
455 ShapedType::kDynamic) &&
456 "invalid input vector sizes");
457 auto sourceShapedType = cast<ShapedType>(source.
getType());
458 auto sourceShape = sourceShapedType.getShape();
460 int64_t vecToReadRank = vecToReadTy.getRank();
461 auto vecToReadShape = vecToReadTy.getShape();
466 assert(sourceShape.size() == (permutationMap
468 :
static_cast<size_t>(vecToReadRank)) &&
469 "expected source rank to match permutation map dims or vector rank.");
471 static_cast<size_t>(vecToReadRank)) &&
472 "expected permutation map results to match vector rank.");
473 assert((!padValue.has_value() ||
474 padValue.value().getType() == sourceShapedType.getElementType()) &&
475 "expected same pad element type to match source element type");
479 if (useInBoundsInsteadOfMasking) {
480 if (permutationMap) {
484 permutationMap, vecToReadTy, cast<ShapedType>(source.
getType()));
488 for (
unsigned i = 0; i < vecToReadRank; i++)
489 inBoundsVal[i] = (sourceShape[i] == vecToReadShape[i]) &&
490 ShapedType::isStatic(sourceShape[i]);
495 (customIndices.empty() || customIndices.size() == sourceShape.size()) &&
496 "expected as many custom indices as source dims.");
498 customIndices.empty()
499 ?
indices.assign(sourceShape.size(),
501 :
indices.assign(customIndices.begin(), customIndices.end());
504 auto transferReadOp =
505 vector::TransferReadOp::create(builder, loc, vecToReadTy,
512 if (useInBoundsInsteadOfMasking)
513 return transferReadOp;
516 isa<MemRefType>(source.
getType())
522 return transferReadOp;
524 auto maskType = vecToReadTy.cloneWith({}, builder.
getI1Type());
526 vector::CreateMaskOp::create(builder, loc, maskType, mixedSourceDims);
534 bool useInBoundsInsteadOfMasking,
537 ShapedType destType = cast<ShapedType>(dest.
getType());
538 int64_t destRank = destType.getRank();
539 auto destShape = destType.getShape();
541 VectorType vecToStoreType = cast<VectorType>(vecToStore.
getType());
542 int64_t vecToStoreRank = vecToStoreType.getRank();
543 auto vecToStoreShape = vecToStoreType.getShape();
547 if (useInBoundsInsteadOfMasking) {
548 if (permutationMap) {
552 permutationMap, vecToStoreType, cast<ShapedType>(dest.
getType()));
556 for (
unsigned i = 0; i < vecToStoreRank; i++)
558 (destShape[destRank - vecToStoreRank + i] >= vecToStoreShape[i]) &&
559 ShapedType::isStatic(destShape[destRank - vecToStoreRank + i]);
564 bool useDefaultWriteIdxs = writeIndices.empty();
565 assert((useDefaultWriteIdxs ||
566 writeIndices.size() ==
static_cast<size_t>(destRank)) &&
567 "Invalid number of write indices!");
568 if (useDefaultWriteIdxs) {
570 writeIndices.assign(destRank, zero);
576 vector::TransferWriteOp::create(builder, loc,
584 if (useInBoundsInsteadOfMasking)
602 isa<MemRefType>(dest.
getType())
610 writtenDims.push_back(cast<AffineDimExpr>(expr).getPosition());
611 llvm::sort(writtenDims);
613 for (
int64_t dim : writtenDims) {
616 builder.
createOrFold<arith::SubIOp>(loc, size, writeIndices[dim]));
622 destShape, vecToStoreShape))
626 builder.
createOrFold<vector::CreateMaskOp>(loc, writeMaskType, maskSizes);
633 LDBG() <<
"Iteration space static sizes:" << llvm::interleaved(
shape);
635 if (inputVectorSizes.size() !=
shape.size()) {
636 LDBG() <<
"Input vector sizes don't match the number of loops";
639 if (ShapedType::isDynamicShape(inputVectorSizes)) {
640 LDBG() <<
"Input vector sizes can't have dynamic dimensions";
643 if (!llvm::all_of(llvm::zip(
shape, inputVectorSizes),
644 [](std::tuple<int64_t, int64_t> sizePair) {
645 int64_t staticSize = std::get<0>(sizePair);
646 int64_t inputSize = std::get<1>(sizePair);
647 return ShapedType::isDynamic(staticSize) ||
648 staticSize <= inputSize;
650 LDBG() <<
"Input vector sizes must be greater than or equal to iteration "
651 "space static sizes";
671FailureOr<SmallVector<Value>>
675 VectorType ty = cast<VectorType>(
vector.getType());
677 if (ty.getRank() < 2)
682 if (ty.getScalableDims().front())
685 for (
int64_t i = 0, e = ty.getShape().front(); i < e; ++i) {
686 subvectors.push_back(vector::ExtractOp::create(rewriter, loc,
vector, i));
694 assert(op->
getNumResults() == 1 &&
"expected single result");
695 assert(isa<VectorType>(op->
getResult(0).
getType()) &&
"expected vector type");
697 if (resultTy.getRank() < 2)
702 if (resultTy.getScalableDims().front())
706 Value result = ub::PoisonOp::create(rewriter, loc, resultTy);
709 for (
int64_t i = 0, e = resultTy.getShape().front(); i < e; ++i) {
710 Value subVector = unrollFn(rewriter, loc, subTy, i);
711 result = vector::InsertOp::create(rewriter, loc, subVector,
result, i);
static std::optional< VectorShape > vectorShape(Type type)
static SetVector< Operation * > getParentsOfType(Block *block)
Implementation detail that walks up the parents and records the ones with the specified type.
static bool areDimsTransposedIn2DSlice(int64_t dim0, int64_t dim1, ArrayRef< int64_t > transp)
Given the n-D transpose pattern 'transp', return true if 'dim0' and 'dim1' should be transposed with ...
static SetVector< Operation * > getEnclosingforOps(Block *block)
Returns the enclosing AffineForOp, from closest to farthest.
static AffineMap makePermutationMap(ArrayRef< Value > indices, const DenseMap< Operation *, unsigned > &enclosingLoopToVectorDim)
Constructs a permutation map from memref indices to vector dimension.
static bool isMaskTriviallyFoldable(SmallVector< OpFoldResult > &maskSizes, SmallVector< Value > &indices, ArrayRef< int64_t > baseShape, ArrayRef< int64_t > maskShape)
Determines whether a mask for xfer_read/write is trivially "all true".
static SmallVector< bool > computeInBoundsFromPermutationMap(AffineMap permutationMap, VectorType vectorType, ShapedType sourceType)
Compute the in_bounds attribute for a transfer op given its permutation map and the source being acce...
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
Block represents an ordered list of Operations.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
MLIRContext * getContext() const
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.
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 is the basic unit of execution within MLIR.
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
unsigned getNumResults()
Return the number of results held by this operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
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,...
A range-style iterator that allows for iterating over the offsets of all potential tiles of size tile...
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.
This is a builder type that keeps local references to arguments.
Builder & dropDim(unsigned pos)
Erase a dim from shape @pos.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
DenseSet< Value, DenseMapInfo< Value > > getInvariantAccesses(Value iv, ArrayRef< Value > indices)
Given an induction variable iv of type AffineForOp and indices of type IndexType, returns the set of ...
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given memref value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
bool isContiguousSlice(MemRefType memrefType, VectorType vectorType)
Return true if vectorType is a contiguous slice of memrefType, in the sense that it can be read/writt...
Operation * maskOperation(OpBuilder &builder, Operation *maskableOp, Value mask, Value passthru=Value())
Creates a vector.mask operation around a maskable operation.
LogicalResult isValidMaskedInputVector(ArrayRef< int64_t > shape, ArrayRef< int64_t > inputVectorSizes)
Returns success if inputVectorSizes is a valid masking configuraion for given shape,...
VectorType inferTransferOpMaskType(VectorType vecType, AffineMap permMap)
Infers the mask type for a transfer op given its vector type and permutation map.
Operation * createWriteOrMaskedWrite(OpBuilder &builder, Location loc, Value vecToStore, Value dest, SmallVector< Value > writeIndices={}, bool useInBoundsInsteadOfMasking=false, AffineMap permutationMap=AffineMap())
Create a TransferWriteOp of vecToStore into dest.
Value createReadOrMaskedRead(OpBuilder &builder, Location loc, Value source, const VectorType &vecToReadTy, std::optional< Value > padValue=std::nullopt, bool useInBoundsInsteadOfMasking=false, ArrayRef< Value > indices={}, AffineMap permutationMap=AffineMap())
Creates a TransferReadOp from source.
FailureOr< std::pair< int, int > > isTranspose2DSlice(vector::TransposeOp op)
Returns two dims that are greater than one if the transposition is applied on a 2D slice.
FailureOr< SmallVector< Value > > unrollVectorValue(TypedValue< VectorType >, RewriterBase &)
Generic utility for unrolling values of type vector<NxAxBx...> to N values of type vector<AxBx....
std::optional< StaticTileOffsetRange > createUnrollIterator(VectorType vType, int64_t targetRank=1)
Returns an iterator for all positions in the leading dimensions of vType up to the targetRank.
Value createOrFoldDimOp(OpBuilder &b, Location loc, Value source, int64_t dim)
Helper function that creates a memref::DimOp or tensor::DimOp depending on the type of source.
bool isLinearizableVector(VectorType type)
Returns true if the input Vector type can be linearized.
function_ref< Value(PatternRewriter &, Location, VectorType, int64_t)> UnrollVectorOpFn
Generic utility for unrolling n-D vector operations to (n-1)-D operations.
SmallVector< OpFoldResult > getMixedSizesXfer(bool hasTensorSemantics, Operation *xfer, RewriterBase &rewriter)
A wrapper for getMixedSizes for vector.transfer_read and vector.transfer_write Ops (for source and de...
LogicalResult unrollVectorOp(Operation *op, PatternRewriter &rewriter, UnrollVectorOpFn unrollFn)
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
llvm::SetVector< T, Vector, Set, N > SetVector
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
SmallVector< T > applyPermutationMap(AffineMap map, llvm::ArrayRef< T > source)
Apply a permutation from map to source and return the result.
std::optional< SmallVector< int64_t > > computeShapeRatio(ArrayRef< int64_t > shape, ArrayRef< int64_t > subShape)
Return the multi-dimensional integral ratio of subShape to the trailing dimensions of shape.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.