21#include "llvm/ADT/SmallVectorExtras.h"
27#include "llvm/ADT/STLExtras.h"
28#include "llvm/ADT/SmallVectorExtras.h"
30#define DEBUG_TYPE "vector-transfer-split"
38 VectorTransferOpInterface xferOp) {
39 assert(xferOp.getPermutationMap().isMinorIdentity() &&
40 "Expected minor identity map");
42 xferOp.zipResultAndIndexing([&](
int64_t resultIdx,
int64_t indicesIdx) {
46 if (xferOp.isDimInBounds(resultIdx))
50 int64_t vectorSize = xferOp.getVectorType().getDimSize(resultIdx);
52 b, loc,
b.getAffineDimExpr(0) +
b.getAffineConstantExpr(vectorSize),
53 {xferOp.getIndices()[indicesIdx]});
58 if (maybeCstSum && maybeCstDimSz && *maybeCstSum <= *maybeCstDimSz)
61 arith::CmpIOp::create(
b, loc, arith::CmpIPredicate::sle,
66 inBoundsCond = arith::AndIOp::create(
b, loc, inBoundsCond, cond);
107splitFullAndPartialTransferPrecondition(VectorTransferOpInterface xferOp) {
109 if (xferOp.getTransferRank() == 0)
113 if (!xferOp.getPermutationMap().isMinorIdentity())
116 if (!xferOp.hasOutOfBoundsDim())
121 if (isa<scf::IfOp>(xferOp->getParentOp()))
135static MemRefType getCastCompatibleMemRefType(MemRefType aT, MemRefType bT) {
136 if (memref::CastOp::areCastCompatible(aT, bT))
138 if (aT.getRank() != bT.getRank())
142 if (
failed(aT.getStridesAndOffset(aStrides, aOffset)) ||
143 failed(bT.getStridesAndOffset(bStrides, bOffset)) ||
144 aStrides.size() != bStrides.size())
150 resStrides(bT.getRank(), 0);
151 for (
int64_t idx = 0, e = aT.getRank(); idx < e; ++idx) {
153 (aShape[idx] == bShape[idx]) ? aShape[idx] : ShapedType::kDynamic;
155 (aStrides[idx] == bStrides[idx]) ? aStrides[idx] : ShapedType::kDynamic;
157 resOffset = (aOffset == bOffset) ? aOffset : ShapedType::kDynamic;
158 return MemRefType::get(
159 resShape, aT.getElementType(),
160 StridedLayoutAttr::get(aT.getContext(), resOffset, resStrides));
167 MemRefType compatibleMemRefType) {
168 MemRefType sourceType = cast<MemRefType>(
memref.getType());
170 if (sourceType.getMemorySpace() != compatibleMemRefType.getMemorySpace()) {
171 sourceType = MemRefType::get(
172 sourceType.getShape(), sourceType.getElementType(),
173 sourceType.getLayout(), compatibleMemRefType.getMemorySpace());
175 memref::MemorySpaceCastOp::create(
b,
memref.getLoc(), sourceType, res);
177 if (sourceType == compatibleMemRefType)
179 return memref::CastOp::create(
b,
memref.getLoc(), compatibleMemRefType, res);
185static std::pair<Value, Value>
186createSubViewIntersection(
RewriterBase &
b, VectorTransferOpInterface xferOp,
189 int64_t memrefRank = xferOp.getShapedType().getRank();
191 assert(memrefRank == cast<MemRefType>(alloc.
getType()).getRank() &&
192 "Expected memref rank to match the alloc rank");
194 xferOp.getIndices().take_front(xferOp.getLeadingShapedRank());
196 sizes.append(leadingIndices.begin(), leadingIndices.end());
197 auto isaWrite = isa<vector::TransferWriteOp>(xferOp);
198 xferOp.zipResultAndIndexing([&](
int64_t resultIdx,
int64_t indicesIdx) {
201 memref::DimOp::create(
b, xferOp.getLoc(), xferOp.getBase(), indicesIdx);
202 Value dimAlloc = memref::DimOp::create(
b, loc, alloc, resultIdx);
203 Value index = xferOp.getIndices()[indicesIdx];
210 affine::AffineMinOp::create(
b, loc,
index.getType(), maps[0],
212 sizes.push_back(affineMin);
219 auto copySrc = memref::SubViewOp::create(
220 b, loc, isaWrite ? alloc : xferOp.getBase(), srcIndices, sizes, strides);
221 auto copyDest = memref::SubViewOp::create(
222 b, loc, isaWrite ? xferOp.getBase() : alloc, destIndices, sizes, strides);
223 return std::make_pair(copySrc, copyDest);
248createFullPartialMemRefCopy(
RewriterBase &
b, vector::TransferReadOp xferOp,
250 MemRefType compatibleMemRefType,
Value alloc) {
254 return scf::IfOp::create(
255 b, loc, inBoundsCond,
257 Value res = castToCompatibleMemRefType(
b,
memref, compatibleMemRefType);
259 llvm::append_range(viewAndIndices, xferOp.getIndices());
260 scf::YieldOp::create(
b, loc, viewAndIndices);
265 Value padding = vector::BroadcastOp::create(
266 b, loc, xferOp.getVectorType(), xferOp.getPadding());
267 memref::StoreOp::create(
269 vector::TypeCastOp::create(
270 b, loc, MemRefType::get({}, padding.
getType()), alloc));
274 std::pair<Value, Value> copyArgs = createSubViewIntersection(
275 rewriter, cast<VectorTransferOpInterface>(xferOp.getOperation()),
277 memref::CopyOp::create(
b, loc, copyArgs.first, copyArgs.second);
279 castToCompatibleMemRefType(
b, alloc, compatibleMemRefType);
281 viewAndIndices.insert(viewAndIndices.end(), xferOp.getTransferRank(),
283 scf::YieldOp::create(
b, loc, viewAndIndices);
306static scf::IfOp createFullPartialVectorTransferRead(
308 Value inBoundsCond, MemRefType compatibleMemRefType,
Value alloc) {
310 scf::IfOp fullPartialIfOp;
313 return scf::IfOp::create(
314 b, loc, inBoundsCond,
316 Value res = castToCompatibleMemRefType(
b,
memref, compatibleMemRefType);
318 llvm::append_range(viewAndIndices, xferOp.getIndices());
319 scf::YieldOp::create(
b, loc, viewAndIndices);
322 Operation *newXfer =
b.clone(*xferOp.getOperation());
323 Value vector = cast<VectorTransferOpInterface>(newXfer).getVector();
324 memref::StoreOp::create(
326 vector::TypeCastOp::create(
327 b, loc, MemRefType::get({},
vector.getType()), alloc));
330 castToCompatibleMemRefType(
b, alloc, compatibleMemRefType);
332 viewAndIndices.insert(viewAndIndices.end(), xferOp.getTransferRank(),
334 scf::YieldOp::create(
b, loc, viewAndIndices);
354getLocationToWriteFullVec(
RewriterBase &
b, vector::TransferWriteOp xferOp,
356 MemRefType compatibleMemRefType,
Value alloc) {
360 return scf::IfOp::create(
361 b, loc, inBoundsCond,
364 castToCompatibleMemRefType(
b,
memref, compatibleMemRefType);
366 llvm::append_range(viewAndIndices, xferOp.getIndices());
367 scf::YieldOp::create(
b, loc, viewAndIndices);
371 castToCompatibleMemRefType(
b, alloc, compatibleMemRefType);
373 viewAndIndices.insert(viewAndIndices.end(),
374 xferOp.getTransferRank(), zero);
375 scf::YieldOp::create(
b, loc, viewAndIndices);
394 vector::TransferWriteOp xferOp,
397 auto notInBounds = arith::XOrIOp::create(
401 std::pair<Value, Value> copyArgs = createSubViewIntersection(
402 rewriter, cast<VectorTransferOpInterface>(xferOp.getOperation()),
404 memref::CopyOp::create(
b, loc, copyArgs.first, copyArgs.second);
421static void createFullPartialVectorTransferWrite(
RewriterBase &
b,
422 vector::TransferWriteOp xferOp,
426 auto notInBounds = arith::XOrIOp::create(
432 vector::TypeCastOp::create(
433 b, loc, MemRefType::get({}, xferOp.getVector().
getType()), alloc),
435 mapping.
map(xferOp.getVector(),
load);
436 b.clone(*xferOp.getOperation(), mapping);
451 if (!isa<scf::ForOp, affine::AffineForOp>(parent))
454 assert(scope &&
"Expected op to be inside automatic allocation scope");
518LogicalResult mlir::vector::splitFullAndPartialTransfer(
521 if (
options.vectorTransferSplit == VectorTransferSplit::None)
525 auto inBoundsAttr =
b.getBoolArrayAttr(bools);
526 if (
options.vectorTransferSplit == VectorTransferSplit::ForceInBounds) {
527 b.modifyOpInPlace(xferOp, [&]() {
528 xferOp->setInherentAttr(
b.getStringAttr(xferOp.getInBoundsAttrName()),
537 assert(succeeded(splitFullAndPartialTransferPrecondition(xferOp)) &&
538 "Expected splitFullAndPartialTransferPrecondition to hold");
540 auto xferReadOp = dyn_cast<vector::TransferReadOp>(xferOp.getOperation());
541 auto xferWriteOp = dyn_cast<vector::TransferWriteOp>(xferOp.getOperation());
543 if (!(xferReadOp || xferWriteOp))
545 if (xferWriteOp && xferWriteOp.getMask())
547 if (xferReadOp && xferReadOp.getMask())
552 b.setInsertionPoint(xferOp);
554 b, cast<VectorTransferOpInterface>(xferOp.getOperation()));
562 Operation *scope = getAutomaticAllocationScope(xferOp);
564 "AutomaticAllocationScope with >1 regions");
566 auto shape = xferOp.getVectorType().getShape();
567 Type elementType = xferOp.getVectorType().getElementType();
568 alloc = memref::AllocaOp::create(
b, scope->
getLoc(),
569 MemRefType::get(
shape, elementType),
573 MemRefType compatibleMemRefType =
574 getCastCompatibleMemRefType(cast<MemRefType>(xferOp.getShapedType()),
575 cast<MemRefType>(alloc.
getType()));
576 if (!compatibleMemRefType)
581 returnTypes[0] = compatibleMemRefType;
583 if (
auto xferReadOp =
584 dyn_cast<vector::TransferReadOp>(xferOp.getOperation())) {
586 scf::IfOp fullPartialIfOp =
587 options.vectorTransferSplit == VectorTransferSplit::VectorTransfer
588 ? createFullPartialVectorTransferRead(
b, xferReadOp, returnTypes,
590 compatibleMemRefType, alloc)
591 : createFullPartialMemRefCopy(
b, xferReadOp, returnTypes,
592 inBoundsCond, compatibleMemRefType,
595 *ifOp = fullPartialIfOp;
598 for (
unsigned i = 0, e = returnTypes.size(); i != e; ++i)
599 xferReadOp.setOperand(i, fullPartialIfOp.getResult(i));
601 b.modifyOpInPlace(xferOp, [&]() {
602 xferOp->setInherentAttr(b.getStringAttr(xferOp.getInBoundsAttrName()),
609 auto xferWriteOp = cast<vector::TransferWriteOp>(xferOp.getOperation());
612 auto memrefAndIndices = getLocationToWriteFullVec(
613 b, xferWriteOp, returnTypes, inBoundsCond, compatibleMemRefType, alloc);
619 mapping.
map(xferWriteOp.getBase(), memrefAndIndices.front());
620 mapping.
map(xferWriteOp.getIndices(), memrefAndIndices.drop_front());
621 auto *
clone =
b.clone(*xferWriteOp, mapping);
622 clone->setInherentAttr(xferWriteOp.getInBoundsAttrName(), inBoundsAttr);
626 if (
options.vectorTransferSplit == VectorTransferSplit::VectorTransfer)
627 createFullPartialVectorTransferWrite(
b, xferWriteOp, inBoundsCond, alloc);
629 createFullPartialMemRefCopy(
b, xferWriteOp, inBoundsCond, alloc);
640 using FilterConstraintType =
641 std::function<LogicalResult(VectorTransferOpInterface op)>;
643 explicit VectorTransferFullPartialRewriter(
644 MLIRContext *context,
645 VectorTransformsOptions options = VectorTransformsOptions(),
646 FilterConstraintType filter =
647 [](VectorTransferOpInterface op) {
return success(); },
648 PatternBenefit benefit = 1)
649 : RewritePattern(MatchAnyOpTypeTag(), benefit, context), options(options),
650 filter(std::move(filter)) {}
653 LogicalResult matchAndRewrite(Operation *op,
654 PatternRewriter &rewriter)
const override;
657 VectorTransformsOptions options;
658 FilterConstraintType filter;
663LogicalResult VectorTransferFullPartialRewriter::matchAndRewrite(
665 auto xferOp = dyn_cast<VectorTransferOpInterface>(op);
666 if (!xferOp ||
failed(splitFullAndPartialTransferPrecondition(xferOp)) ||
669 return splitFullAndPartialTransfer(rewriter, xferOp,
options);
674 patterns.
add<VectorTransferFullPartialRewriter>(patterns.
getContext(),
static llvm::ManagedStatic< PassManagerOptions > options
static Value createInBoundsCond(RewriterBase &b, VectorTransferOpInterface xferOp)
Build the condition to ensure that a particular VectorTransferOpInterface is in-bounds.
Base type for affine expression.
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
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...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
This class represents a single result from folding an operation.
A trait of region holding operations that define a new scope for automatic allocations,...
Operation is the basic unit of execution within MLIR.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
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...
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.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given memref value.
SmallVector< Value > ValueVector
An owning vector of values, handy to return from functions.
void populateVectorTransferFullPartialPatterns(RewritePatternSet &patterns, const VectorTransformsOptions &options)
Populate patterns with the following patterns.
Include the generated interface declarations.
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.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.