29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/TypeSwitch.h"
31#include "llvm/Support/DebugLog.h"
33#define DEBUG_TYPE "vector-to-gpu"
36#define GEN_PASS_DEF_CONVERTVECTORTOGPUPASS
37#include "mlir/Conversion/Passes.h.inc"
45 auto infer = [&](MapList m) {
50 auto iteratorTypes =
contract.getIteratorTypes().getValue();
58 if (
contract.getIndexingMapsArray() != infer({{m, k}, {k, n}, {m, n}}))
72 const unsigned nDim = permutationMap.
getNumDims();
73 if (0 == nDim || permutationMap.
getResults().empty())
92static std::optional<int64_t>
94 auto memrefType = dyn_cast<MemRefType>(type);
98 if (memrefType.getRank() < 2)
102 if (failed(memrefType.getStridesAndOffset(strides, offset)) ||
109 unsigned strideIndex = strides.size();
112 if (
auto cst = dyn_cast<AffineConstantExpr>(
result)) {
114 if (0 != cst.getValue())
119 auto dim = dyn_cast<AffineDimExpr>(
result);
123 strideIndex = std::min(strideIndex, dim.getPosition());
129 if (strideIndex + 1 >= strides.size())
132 const int64_t stride = strides[strideIndex];
133 if (stride == ShapedType::kDynamic)
140 if (readOp.getMask() || readOp.hasOutOfBoundsDim() ||
141 readOp.getVectorType().getRank() != 2)
149 if (readOp.getVectorType().getElementType().isInteger(8))
150 if (!readOp->hasOneUse() || (!isa<arith::ExtSIOp>(*readOp->user_begin()) &&
151 !isa<arith::ExtUIOp>(*readOp->user_begin())))
156 return llvm::is_contained(permutationMap.
getResults(), innerDim);
163 if (writeOp.getTransferRank() == 0)
166 if (writeOp.getMask() || writeOp.hasOutOfBoundsDim() ||
167 writeOp.getVectorType().getRank() != 2)
171 std::optional<int64_t> stride =
174 if (!stride.has_value() || stride.value() == 0)
179 return llvm::is_contained(permutationMap.
getResults(), innerDim);
183 if (isa<scf::ForOp, scf::YieldOp>(op))
188 auto maskableOp = dyn_cast<vector::MaskableOpInterface>(op);
189 if (maskableOp && maskableOp.isMasked())
191 if (
auto transferRead = dyn_cast<vector::TransferReadOp>(op))
193 if (
auto transferWrite = dyn_cast<vector::TransferWriteOp>(op))
195 if (
auto contract = dyn_cast<vector::ContractionOp>(op))
197 if (
auto constant = dyn_cast<arith::ConstantOp>(op))
199 if (
auto broadcast = dyn_cast<vector::BroadcastOp>(op))
201 if (
auto signedExtend = dyn_cast<arith::ExtSIOp>(op))
203 if (
auto unsignedExtend = dyn_cast<arith::ExtUIOp>(op))
212 return llvm::any_of(op->
getResultTypes(), llvm::IsaPred<VectorType>);
215 backwardSliceOptions.
filter = hasVectorDest;
221 forwardSliceOptions.
filter = hasVectorSrc;
227 auto getCachedBackwardSlice =
229 auto [it,
inserted] = backwardSliceCache.try_emplace(currentOp);
236 assert(
result.succeeded() &&
"expected a backward slice");
238 it->second = backwardSlice.takeVector();
242 auto getCachedForwardSlice =
244 auto [it,
inserted] = forwardSliceCache.try_emplace(currentOp);
253 if (
auto forOp = dyn_cast<scf::ForOp>(currentOp)) {
254 for (
Value forOpResult : forOp.getResults())
261 it->second = forwardSlice.takeVector();
265 auto cachedSupportsMMAMatrixType = [&](
Operation *currentOp) {
267 supportsMMAMatrixTypeCache.try_emplace(currentOp,
false);
275 if (!isa<vector::ContractionOp>(nestedOp) &&
278 if (backwardSliceCache.contains(nestedOp))
282 dependentOps.insert(nestedOp);
283 unsigned currentIndex = 0;
284 while (currentIndex != dependentOps.size()) {
285 Operation *currentOp = dependentOps[currentIndex++];
286 dependentOps.insert_range(getCachedBackwardSlice(currentOp));
287 dependentOps.insert_range(getCachedForwardSlice(currentOp));
292 if (llvm::any_of(dependentOps, [&](
Operation *op) {
293 if (!cachedSupportsMMAMatrixType(op)) {
294 LDBG() <<
"cannot convert op: " << *op;
301 opToConvert.insert_range(dependentOps);
310struct PrepareContractToGPUMMA
314 LogicalResult matchAndRewrite(vector::ContractionOp op,
315 PatternRewriter &rewriter)
const override {
321 Location loc = op.getLoc();
322 Value
lhs = op.getLhs(),
rhs = op.getRhs(), res = op.getAcc();
325 using MapList = ArrayRef<ArrayRef<AffineExpr>>;
326 auto infer = [&](MapList m) {
331 static constexpr std::array<int64_t, 2> perm = {1, 0};
332 auto iteratorTypes = op.getIteratorTypes().getValue();
333 SmallVector<AffineMap, 4> maps = op.getIndexingMapsArray();
342 if (maps == infer({{m, k}, {k, n}, {m, n}}))
344 if (maps == infer({{m, k}, {n, k}, {m, n}})) {
345 rhs = vector::TransposeOp::create(rewriter, loc,
rhs, perm);
346 }
else if (maps == infer({{k, m}, {k, n}, {m, n}})) {
347 lhs = vector::TransposeOp::create(rewriter, loc,
lhs, perm);
348 }
else if (maps == infer({{k, m}, {n, k}, {m, n}})) {
349 rhs = vector::TransposeOp::create(rewriter, loc,
rhs, perm);
350 lhs = vector::TransposeOp::create(rewriter, loc,
lhs, perm);
351 }
else if (maps == infer({{m, k}, {k, n}, {n, m}})) {
353 rhs = vector::TransposeOp::create(rewriter, loc,
rhs, perm);
354 lhs = vector::TransposeOp::create(rewriter, loc,
lhs, perm);
355 }
else if (maps == infer({{m, k}, {n, k}, {n, m}})) {
357 rhs = vector::TransposeOp::create(rewriter, loc,
rhs, perm);
358 }
else if (maps == infer({{k, m}, {k, n}, {n, m}})) {
360 lhs = vector::TransposeOp::create(rewriter, loc,
lhs, perm);
361 }
else if (maps == infer({{k, m}, {n, k}, {n, m}})) {
370 op.getIteratorTypes(), op.getKind(), op.getFastmath());
390 llvm::make_isa_range<vector::ContractionOp>(op->
getUsers())) {
406 assert(op.getTransferRank() > 0 &&
"unexpected 0-d transfer");
408 "expected convertible operation");
411 std::optional<int64_t> stride =
413 if (!stride.has_value()) {
414 LDBG() <<
"no stride";
424 Value mappingResult = op.getResult();
425 auto elType = op.getVectorType().getElementType();
427 if (op->hasOneUse()) {
428 auto *user = *op->user_begin();
430 if (isa<arith::ExtSIOp, arith::ExtUIOp>(user)) {
431 elType = IntegerType::get(
432 op.getContext(), cast<IntegerType>(elType).getWidth(),
433 isa<arith::ExtSIOp>(user) ? IntegerType::Signed
434 : IntegerType::Unsigned);
435 mappingResult = user->getResult(0);
440 Value load = gpu::SubgroupMmaLoadMatrixOp::create(
441 rewriter, op.getLoc(), type, op.getBase(), op.getIndices(),
443 isTranspose ? rewriter.
getUnitAttr() : UnitAttr());
444 valueMapping[mappingResult] =
load;
446 LDBG() <<
"transfer read to: " <<
load;
458 std::optional<int64_t> stride =
460 if (!stride.has_value()) {
461 LDBG() <<
"no stride";
470 auto it = valueMapping.find(op.getVector());
471 if (it == valueMapping.end()) {
472 LDBG() <<
"no mapping";
476 Value matrix = it->second;
477 auto store = gpu::SubgroupMmaStoreMatrixOp::create(
478 rewriter, op.getLoc(), matrix, op.getBase(), op.getIndices(),
480 isTranspose ? rewriter.
getUnitAttr() : UnitAttr());
483 LDBG() <<
"transfer write to: " << store;
485 LDBG() <<
"erase: " << op;
496 auto itA = valueMapping.find(op.getLhs());
497 auto itB = valueMapping.find(op.getRhs());
498 auto itC = valueMapping.find(op.getAcc());
499 if (itA == valueMapping.end() || itB == valueMapping.end() ||
500 itC == valueMapping.end())
502 Value opA = itA->second, opB = itB->second, opC = itC->second;
503 Value matmul = gpu::SubgroupMmaComputeOp::create(rewriter, op.getLoc(),
507 valueMapping[op.getResult()] = matmul;
521 cast<SplatElementsAttr>(op.getValue()).getSplatValue<TypedAttr>();
522 auto scalarConstant =
523 arith::ConstantOp::create(rewriter, op.getLoc(), splat.getType(), splat);
525 auto vecType = cast<VectorType>(op.getType());
527 vecType.getShape(), vecType.getElementType(), llvm::StringRef(fragType));
528 auto matrix = gpu::SubgroupMmaConstantMatrixOp::create(rewriter, op.getLoc(),
529 type, scalarConstant);
530 valueMapping[op.getResult()] = matrix;
544 auto vecType = op.getResultVectorType();
546 vecType.getShape(), vecType.getElementType(), llvm::StringRef(fragType));
547 auto matrix = gpu::SubgroupMmaConstantMatrixOp::create(rewriter, op.getLoc(),
548 type, op.getSource());
549 valueMapping[op.getResult()] = matrix;
556 gpu::MMAElementwiseOp opType,
563 auto it = valueMapping.find(operand);
564 if (it == valueMapping.end())
566 matrixOperands.push_back(it->second);
568 auto resultType = cast<gpu::MMAMatrixType>(matrixOperands[0].
getType());
569 if (opType == gpu::MMAElementwiseOp::EXTF ||
570 opType == gpu::MMAElementwiseOp::TRUNCF) {
574 vectorType.getElementType(),
575 resultType.getOperand());
578 Value newOp = gpu::SubgroupMmaElementwiseOp::create(
579 rewriter, op->
getLoc(), resultType, matrixOperands, opType);
585 patterns.
add<PrepareContractToGPUMMA>(patterns.
getContext());
594 auto globalRes = LogicalResult::success();
596 LDBG() <<
"Process op: " << *op;
598 auto res = LogicalResult::success();
599 if (
auto transferRead = dyn_cast<vector::TransferReadOp>(op)) {
601 }
else if (
auto transferWrite = dyn_cast<vector::TransferWriteOp>(op)) {
603 }
else if (
auto contractOp = dyn_cast<vector::ContractionOp>(op)) {
605 }
else if (
auto constantOp = dyn_cast<arith::ConstantOp>(op)) {
607 }
else if (
auto broadcastOp = dyn_cast<vector::BroadcastOp>(op)) {
609 }
else if (
auto forOp = dyn_cast<scf::ForOp>(op)) {
611 }
else if (
auto yieldOp = dyn_cast<scf::YieldOp>(op)) {
617 globalRes = failure();
623struct ConvertVectorToGPUPass
626 void runOnOperation()
override {
630 return signalPassFailure();
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
static bool supportsMMaMatrixType(Operation *op)
static LogicalResult convertTransferWriteOp(RewriterBase &rewriter, vector::TransferWriteOp op, llvm::DenseMap< Value, Value > &valueMapping)
static LogicalResult convertContractOp(RewriterBase &rewriter, vector::ContractionOp op, llvm::DenseMap< Value, Value > &valueMapping)
static const char * inferFragType(Operation *op)
static LogicalResult convertTransferReadOp(RewriterBase &rewriter, vector::TransferReadOp op, llvm::DenseMap< Value, Value > &valueMapping)
static LogicalResult convertBroadcastOp(RewriterBase &rewriter, vector::BroadcastOp op, llvm::DenseMap< Value, Value > &valueMapping)
Convert a vector.broadcast from scalar to a SubgroupMmaConstantMatrix op.
static bool transferWriteSupportsMMAMatrixType(vector::TransferWriteOp writeOp)
static bool contractSupportsMMAMatrixType(vector::ContractionOp contract)
static LogicalResult convertElementwiseOp(RewriterBase &rewriter, Operation *op, gpu::MMAElementwiseOp opType, llvm::DenseMap< Value, Value > &valueMapping)
Convert an elementwise op to the equivalent elementwise op on MMA matrix.
static bool isFirstResultLastMapDimension(AffineMap permutationMap)
static bool transferReadSupportsMMAMatrixType(vector::TransferReadOp readOp)
static LogicalResult convertConstantOp(RewriterBase &rewriter, arith::ConstantOp op, llvm::DenseMap< Value, Value > &valueMapping)
Convert a 2D splat ConstantOp to a SubgroupMmaConstantMatrix op.
static SetVector< Operation * > getOpToConvert(mlir::Operation *op)
static std::optional< int64_t > getStaticallyKnownRowStride(ShapedType type, AffineMap permutationMap)
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
MLIRContext * getContext() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
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...
AffineExpr getResult(unsigned idx) const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
This class represents an argument of a Block.
IntegerAttr getIndexAttr(int64_t value)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
MLIRContext * getContext() const
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
MLIRContext is the top-level object for a collection of MLIR operations.
RAII guard to reset the insertion point of the builder when destroyed.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
bool hasOneUse()
Returns true if this operation has exactly one use.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
user_range getUsers()
Returns a range of all users.
user_iterator user_begin()
unsigned getNumResults()
Return the number of results held by this operation.
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 eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
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...
MMAMatrix represents a matrix held by a subgroup for matrix-matrix multiply accumulate operations.
static MMAMatrixType get(ArrayRef< int64_t > shape, Type elementType, StringRef operand)
Get MMAMatrixType and verify construction Invariants.
bool isReductionIterator(Attribute attr)
Returns true if attr has "reduction" iterator type semantics.
bool isParallelIterator(Attribute attr)
Returns true if attr has "parallel" iterator type semantics.
Include the generated interface declarations.
LogicalResult convertYieldOpForMMA(RewriterBase &rewriter, scf::YieldOp op, llvm::DenseMap< Value, Value > &valueMapping)
Update a yield operation to yield the converted values.
LogicalResult getBackwardSlice(Operation *op, SetVector< Operation * > *backwardSlice, const BackwardSliceOptions &options={})
Fills backwardSlice with the computed backward slice (i.e.
bool broadcastSupportsMMAMatrixType(vector::BroadcastOp broadcastOp)
Return true if this is a broadcast from scalar to a 2D vector.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
bool elementwiseSupportsMMAMatrixType(Operation *op)
Return true if the operation is supported as elementwise on an MMA matrix.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
LogicalResult convertForOpForMMA(RewriterBase &rewriter, scf::ForOp op, llvm::DenseMap< Value, Value > &valueMapping)
Extend loop-carried values with their converted counterparts.
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...
std::optional< gpu::MMAElementwiseOp > convertElementwiseOpToMMA(Operation *op)
Return the MMA elementwise enum associated with the operation if supported.
bool constantSupportsMMAMatrixType(arith::ConstantOp constantOp)
Return true if the constant is a splat to a 2D vector so that it can be converted to an MMA constant ...
llvm::SetVector< T, Vector, Set, N > SetVector
SliceOptions ForwardSliceOptions
void populateVectorTransferReadTransposePatterns(RewritePatternSet &patterns)
Populate patterns that fold vector.transpose into vector.transfer_read, including through arithmetic ...
template bool integerExtendSupportsMMAMatrixType< arith::ExtSIOp >(arith::ExtSIOp)
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
template bool integerExtendSupportsMMAMatrixType< arith::ExtUIOp >(arith::ExtUIOp)
void populatePrepareVectorToMMAPatterns(RewritePatternSet &patterns)
Populate patterns that prepare vector operations for lowering to GPU dialect MMA matrix operations.
LogicalResult convertVectorToMMAOps(RewriterBase &rewriter, Operation *rootOp)
Convert vector ops to MMA matrix operations nested under rootOp.
SetVector< Operation * > topologicalSort(const SetVector< Operation * > &toSort)
Sorts all operations in toSort topologically while also considering region semantics.
void getForwardSlice(Operation *op, SetVector< Operation * > *forwardSlice, const ForwardSliceOptions &options={})
Fills forwardSlice with the computed forward slice (i.e.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This trait tags element-wise ops on vectors or tensors.