33#include "llvm/ADT/STLExtras.h"
34#include "llvm/ADT/TypeSwitch.h"
35#include "llvm/Support/DebugLog.h"
37#define DEBUG_TYPE "vector-to-nvgpu"
40#define GEN_PASS_DEF_CONVERTVECTORTONVGPUPASS
41#include "mlir/Conversion/Passes.h.inc"
52template <
typename TransferOpType>
56 indices.append(xferOp.getIndices().begin(), xferOp.getIndices().end());
58 unsigned offsetsIdx = 0;
59 for (
auto expr : xferOp.getPermutationMap().getResults()) {
60 if (
auto dim = dyn_cast<AffineDimExpr>(expr)) {
63 dims.push_back(prevIdx);
66 rewriter, loc, d0 + offsetMap.
getResult(offsetsIdx++), dims);
75 auto infer = [&](MapList m) {
80 auto iteratorTypes =
contract.getIteratorTypes().getValue();
88 if (
contract.getIndexingMapsArray() != infer({{m, k}, {n, k}, {m, n}}))
99 FailureOr<nvgpu::WarpMatrixInfo> warpMatrixInfo =
101 if (failed(warpMatrixInfo))
105 if (failed(contractOp))
112 return (cast<VectorType>(op->getResult(0).getType()) ==
113 cast<VectorType>((*contractOp).getRhs().getType()));
115 return (cast<VectorType>(op->getResult(0).getType()) ==
116 cast<VectorType>((*contractOp).getAcc().getType()));
122 if (isa<scf::ForOp, scf::YieldOp>(op))
127 auto maskableOp = dyn_cast<vector::MaskableOpInterface>(op);
128 if (maskableOp && maskableOp.isMasked())
130 if (
auto transferRead = dyn_cast<vector::TransferReadOp>(op))
132 if (
auto transferWrite = dyn_cast<vector::TransferWriteOp>(op))
134 if (
auto extractStridedSlice = dyn_cast<vector::ExtractStridedSliceOp>(op))
136 if (
auto contract = dyn_cast<vector::ContractionOp>(op))
138 if (
auto constant = dyn_cast<arith::ConstantOp>(op))
140 if (
auto broadcast = dyn_cast<vector::BroadcastOp>(op))
142 if (
auto signedExtend = dyn_cast<arith::ExtSIOp>(op))
144 if (
auto unsignedExtend = dyn_cast<arith::ExtUIOp>(op))
153 return llvm::any_of(op->
getResultTypes(), llvm::IsaPred<VectorType>);
156 backwardSliceOptions.
filter = hasVectorDest;
162 forwardSliceOptions.
filter = hasVectorSrc;
168 auto getCachedBackwardSlice =
170 auto [it,
inserted] = backwardSliceCache.try_emplace(currentOp);
177 assert(
result.succeeded() &&
"expected a backward slice");
179 it->second = backwardSlice.takeVector();
183 auto getCachedForwardSlice =
185 auto [it,
inserted] = forwardSliceCache.try_emplace(currentOp);
194 if (
auto forOp = dyn_cast<scf::ForOp>(currentOp)) {
195 for (
Value forOpResult : forOp.getResults())
202 it->second = forwardSlice.takeVector();
206 auto cachedSupportsMMAMatrixType = [&](
Operation *currentOp) {
208 supportsMMAMatrixTypeCache.try_emplace(currentOp,
false);
216 if (!isa<vector::ContractionOp>(nestedOp) &&
219 if (backwardSliceCache.contains(nestedOp))
223 dependentOps.insert(nestedOp);
224 unsigned currentIndex = 0;
225 while (currentIndex != dependentOps.size()) {
226 Operation *currentOp = dependentOps[currentIndex++];
227 dependentOps.insert_range(getCachedBackwardSlice(currentOp));
228 dependentOps.insert_range(getCachedForwardSlice(currentOp));
233 if (llvm::any_of(dependentOps, [&](
Operation *op) {
234 if (!cachedSupportsMMAMatrixType(op)) {
235 LDBG() <<
"cannot convert op: " << *op;
242 opToConvert.insert_range(dependentOps);
252 regInfo.elementsPerRegister};
253 Type elType = regInfo.registerLLVMType;
254 if (
auto vecType = dyn_cast<VectorType>(elType))
255 elType = vecType.getElementType();
256 return VectorType::get(
shape, elType);
266 FailureOr<nvgpu::WarpMatrixInfo> warpMatrixInfo =
268 if (failed(warpMatrixInfo)) {
269 LDBG() <<
"no warpMatrixInfo";
273 FailureOr<nvgpu::FragmentElementInfo> regInfo =
274 nvgpu::getMmaSyncRegisterType(*warpMatrixInfo);
275 if (failed(regInfo)) {
276 LDBG() <<
"not mma sync reg info";
281 auto dense = dyn_cast<SplatElementsAttr>(op.getValue());
283 LDBG() <<
"not a splat";
288 rewriter, op.getLoc(), vectorType,
290 valueMapping[op.getResult()] =
result;
305 LDBG() <<
"Failed because the result of `vector.transfer_read` "
306 "is not a 2d operand";
315 auto exprM = dyn_cast<AffineDimExpr>(dM);
316 auto exprN = dyn_cast<AffineDimExpr>(dN);
318 if (!exprM || !exprN) {
319 LDBG() <<
"Failed because expressions are not affine dim "
320 "expressions, then transpose cannot be determined.";
324 return exprM.getPosition() > exprN.getPosition();
334 FailureOr<nvgpu::WarpMatrixInfo> warpMatrixInfo =
336 if (failed(warpMatrixInfo)) {
337 LDBG() <<
"no warpMatrixInfo";
341 FailureOr<nvgpu::FragmentElementInfo> regInfo =
342 nvgpu::getMmaSyncRegisterType(*warpMatrixInfo);
343 if (failed(regInfo)) {
344 LDBG() <<
"not mma sync reg info";
349 if (failed(transpose)) {
350 LDBG() <<
"failed to determine the transpose";
352 op,
"Op should likely not be converted to a nvgpu.ldmatrix call.");
355 FailureOr<nvgpu::LdMatrixParams> params =
356 nvgpu::getLdMatrixParams(*warpMatrixInfo, *transpose);
358 if (failed(params)) {
359 LDBG() <<
"failed to convert vector.transfer_read to ldmatrix. "
360 <<
"Op should likely not be converted to a nvgpu.ldmatrix call.";
362 op,
"failed to convert vector.transfer_read to ldmatrix; this op "
363 "likely should not be converted to a nvgpu.ldmatrix call.");
367 auto laneId = gpu::LaneIdOp::create(rewriter, loc,
nullptr);
368 FailureOr<AffineMap> offsets =
369 nvgpu::getLaneIdToLdMatrixMatrixCoord(rewriter, loc, *params);
370 if (failed(offsets)) {
371 LDBG() <<
"no offsets";
381 nvgpu::LdMatrixOp newOp =
382 nvgpu::LdMatrixOp::create(rewriter, loc, vectorType, op.getBase(),
383 indices, *transpose, params->numTiles);
384 valueMapping[op] = newOp->getResult(0);
395 FailureOr<nvgpu::WarpMatrixInfo> warpMatrixInfo =
397 if (failed(warpMatrixInfo))
399 FailureOr<nvgpu::FragmentElementInfo> regInfo =
400 nvgpu::getMmaSyncRegisterType(*warpMatrixInfo);
401 if (failed(regInfo)) {
403 op,
"Failed to deduce register fragment type during "
404 "conversion to distributed non-ldmatrix compatible load");
407 Value laneId = gpu::LaneIdOp::create(rewriter, loc,
nullptr);
410 Type loadedElType = regInfo->registerLLVMType;
413 Value fill = arith::ConstantOp::create(
414 rewriter, op.getLoc(), vectorType.getElementType(),
415 rewriter.
getZeroAttr(vectorType.getElementType()));
417 vector::BroadcastOp::create(rewriter, op.getLoc(), vectorType, fill);
419 bool isTransposeLoad = !op.getPermutationMap().isMinorIdentity();
423 if (!isTransposeLoad) {
424 if (!isa<VectorType>(loadedElType)) {
425 loadedElType = VectorType::get({1}, loadedElType);
428 for (
int i = 0; i < vectorType.getShape()[0]; i++) {
429 FailureOr<AffineMap> coords = nvgpu::getLaneIdAndValueIdToOperandCoord(
430 rewriter, op.getLoc(), *warpMatrixInfo);
434 Value logicalValueId = arith::ConstantOp::create(
436 rewriter.
getIndexAttr(i * regInfo->elementsPerRegister));
439 rewriter, op, *coords, {laneId, logicalValueId}, newIndices);
441 Value el = vector::LoadOp::create(rewriter, loc, loadedElType,
442 op.getBase(), newIndices);
443 result = vector::InsertOp::create(rewriter, loc, el,
result, i);
446 if (
auto vecType = dyn_cast<VectorType>(loadedElType)) {
447 loadedElType = vecType.getElementType();
449 for (
int i = 0; i < vectorType.getShape()[0]; i++) {
450 for (
unsigned innerIdx = 0; innerIdx < vectorType.getShape()[1];
453 Value logicalValueId = arith::ConstantOp::create(
455 rewriter.
getIndexAttr(i * regInfo->elementsPerRegister + innerIdx));
456 FailureOr<AffineMap> coords = nvgpu::getLaneIdAndValueIdToOperandCoord(
457 rewriter, op.getLoc(), *warpMatrixInfo);
463 rewriter, op, *coords, {laneId, logicalValueId}, newIndices);
464 Value el = memref::LoadOp::create(rewriter, op.getLoc(), loadedElType,
465 op.getBase(), newIndices);
466 result = vector::InsertOp::create(rewriter, op.getLoc(), el,
result,
472 valueMapping[op.getResult()] =
result;
479 dyn_cast_or_null<gpu::AddressSpaceAttr>(type.getMemorySpace());
480 return addressSpace &&
481 addressSpace.getValue() == gpu::GPUDialect::getWorkgroupAddressSpace();
493 FailureOr<nvgpu::WarpMatrixInfo> warpMatrixInfo =
495 if (failed(warpMatrixInfo))
498 bool isLdMatrixCompatible =
500 nvgpu::inferTileWidthInBits(*warpMatrixInfo) == 128;
502 VectorType vecTy = op.getVectorType();
503 int64_t bitWidth = vecTy.getElementType().getIntOrFloatBitWidth();
508 if (!op.getPermutationMap().isMinorIdentity() &&
509 (bitWidth != 16 || vecTy.getDimSize(1) < 8 ||
510 vecTy.getDimSize(0) * bitWidth < 128))
511 isLdMatrixCompatible =
false;
513 if (!isLdMatrixCompatible)
526 auto it = valueMapping.find(op.getVector());
527 if (it == valueMapping.end())
529 Value matrix = it->second;
531 FailureOr<nvgpu::WarpMatrixInfo> warpMatrixInfo =
533 if (failed(warpMatrixInfo))
535 FailureOr<nvgpu::FragmentElementInfo> regInfo =
536 nvgpu::getMmaSyncRegisterType(*warpMatrixInfo);
541 Value laneId = gpu::LaneIdOp::create(rewriter, loc,
nullptr);
543 for (
unsigned i = 0; i < vectorType.getShape()[0]; i++) {
544 Value logicalValueId = arith::ConstantOp::create(
546 rewriter.
getIndexAttr(i * regInfo->elementsPerRegister));
547 FailureOr<AffineMap> coords = nvgpu::getLaneIdAndValueIdToOperandCoord(
548 rewriter, op.getLoc(), *warpMatrixInfo);
556 rewriter, op, *coords, {laneId, logicalValueId}, newIndices);
557 vector::StoreOp::create(rewriter, loc, el, op.getBase(), newIndices);
560 LDBG() <<
"erase: " << op;
567 for (
auto attr : arrayAttr)
568 results.push_back(cast<IntegerAttr>(attr).getInt());
573 vector::ExtractStridedSliceOp op,
580 FailureOr<nvgpu::WarpMatrixInfo> warpMatrixInfo =
582 if (failed(warpMatrixInfo))
585 FailureOr<nvgpu::FragmentElementInfo> mmaSyncFragmentInfo =
586 nvgpu::getMmaSyncRegisterType(*warpMatrixInfo);
587 if (failed(mmaSyncFragmentInfo))
591 auto transferReadOp = op.getSource().getDefiningOp<vector::TransferReadOp>();
596 if (failed(warpMatrixInfo))
599 FailureOr<nvgpu::FragmentElementInfo> ldFragmentInfo =
600 nvgpu::getMmaSyncRegisterType(*warpMatrixInfo);
601 if (failed(ldFragmentInfo))
605 (mmaSyncFragmentInfo->elementsPerRegister ==
606 ldFragmentInfo->elementsPerRegister) &&
607 "Number of elements per register should be same for load and mma.sync");
610 std::array<int64_t, 2> strides = {1,
612 std::array<int64_t, 2> sliceShape = {
613 mmaSyncFragmentInfo->numRegistersPerFragment,
614 mmaSyncFragmentInfo->elementsPerRegister};
615 auto it = valueMapping.find(transferReadOp);
616 if (it == valueMapping.end())
618 auto sourceVector = it->second;
631 std::array<int64_t, 2> sliceOffset = {0, 0};
633 if (offsets[0] && offsets[1])
634 return op->emitError() <<
"Slicing fragments in 2D is not supported. ";
636 sliceOffset[0] = (warpVectorShape[0] / offsets[0]);
638 sliceOffset[0] = (warpVectorShape[1] / offsets[1]);
640 Value newOp = vector::ExtractStridedSliceOp::create(
641 rewriter, loc, sourceVector, sliceOffset, sliceShape, strides);
643 valueMapping[op] = newOp;
653 auto itA = valueMapping.find(op.getLhs());
654 auto itB = valueMapping.find(op.getRhs());
655 auto itC = valueMapping.find(op.getAcc());
656 if (itA == valueMapping.end() || itB == valueMapping.end() ||
657 itC == valueMapping.end())
659 Value opA = itA->second, opB = itB->second, opC = itC->second;
660 int64_t m = cast<VectorType>(op.getLhs().getType()).getShape()[0];
661 int64_t n = cast<VectorType>(op.getRhs().getType()).getShape()[0];
662 int64_t k = cast<VectorType>(op.getLhs().getType()).getShape()[1];
663 Value matmul = nvgpu::MmaSyncOp::create(rewriter, op.getLoc(), opA, opB, opC,
665 valueMapping[op.getResult()] = matmul;
680 .Case([&](vector::TransferReadOp transferReadOp) {
684 .Case([&](vector::TransferWriteOp transferWriteOp) {
688 .Case([&](vector::ExtractStridedSliceOp extractStridedSliceOp) {
692 .Case([&](vector::ContractionOp contractionOp) {
696 .Case([&](scf::ForOp forOp) {
699 .Case([&](scf::YieldOp yieldOp) {
702 .Case([&](arith::ConstantOp constOp) {
706 return op->
emitError() <<
"unhandled vector to mma type: " << *op;
710 <<
"failed to convert op during vector-to-nvgpu conversion";
717struct ConvertVectorToNVGPUPass
720 void runOnOperation()
override {
724 return signalPassFailure();
728 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 convertContractOpToMmaSync(RewriterBase &rewriter, vector::ContractionOp op, llvm::DenseMap< Value, Value > &valueMapping)
static bool isSharedMemory(MemRefType type)
Return true if this is a shared memory memref type.
static VectorType getMmaSyncVectorOperandType(const nvgpu::FragmentElementInfo ®Info)
Returns the vector type which represents a matrix fragment.
static void populateFromInt64AttrArray(ArrayAttr arrayAttr, SmallVectorImpl< int64_t > &results)
static FailureOr< bool > isTransposed(vector::TransferReadOp op)
Check if the loaded matrix operand requires transposed.
static LogicalResult convertTransferReadToLoads(RewriterBase &rewriter, vector::TransferReadOp op, llvm::DenseMap< Value, Value > &valueMapping)
Converts a vector.transfer_read operation directly to either a vector.load or a nvgpu....
static LogicalResult creatLdMatrixCompatibleLoads(RewriterBase &rewriter, vector::TransferReadOp op, llvm::DenseMap< Value, Value > &valueMapping)
static bool contractSupportsMMAMatrixType(vector::ContractionOp contract)
static bool extractStridedSliceSupportsMMAMatrixType(vector::ExtractStridedSliceOp op)
Returns true if the extract strided slice op is supported with mma.sync path.
static LogicalResult convertConstantOpMmaSync(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 LogicalResult convertTransferWriteToStores(RewriterBase &rewriter, vector::TransferWriteOp op, llvm::DenseMap< Value, Value > &valueMapping)
static void getXferIndices(RewriterBase &rewriter, TransferOpType xferOp, AffineMap offsetMap, ArrayRef< Value > dimValues, SmallVector< Value, 4 > &indices)
For a vector TransferOpType xferOp, an empty indices vector, and an AffineMap representing offsets to...
static LogicalResult convertExtractStridedSlice(RewriterBase &rewriter, vector::ExtractStridedSliceOp op, llvm::DenseMap< Value, Value > &valueMapping)
static LogicalResult createNonLdMatrixLoads(RewriterBase &rewriter, vector::TransferReadOp op, llvm::DenseMap< Value, Value > &valueMapping)
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
unsigned getNumDims() 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.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
IntegerAttr getIndexAttr(int64_t value)
TypedAttr getZeroAttr(Type type)
AffineExpr getAffineDimExpr(unsigned position)
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
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.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation is the basic unit of execution within MLIR.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
operand_type_range getOperandTypes()
result_type_range getResultTypes()
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),...
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
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,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
FailureOr< vector::ContractionOp > getUserContract(Operation *op)
Returns the first user of the op that is vector.contract.
FailureOr< WarpMatrixInfo > getWarpMatrixInfo(Operation *op)
If op is a vector.transfer_write, return the WarpMatrixInfo for the vector operand.
bool canLowerToWarpMatrixOperation(vector::TransferWriteOp op)
Returns the number of bits in a single tile row.
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.
void populateVectorContractCanonicalizeMatmulToMMT(RewritePatternSet &patterns, std::function< LogicalResult(vector::ContractionOp)> constraint=[](vector::ContractionOp) { return success();}, PatternBenefit=1)
Canonicalization of a vector.contract a, b, c with row-major matmul semantics to a contraction with M...
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.
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...
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
LogicalResult convertVectorToNVVMCompatibleMMASync(RewriterBase &rewriter, Operation *rootOp)
Convert vector ops nested under rootOp to vector and GPU operations compatible with the nvvm....
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
template bool integerExtendSupportsMMAMatrixType< arith::ExtUIOp >(arith::ExtUIOp)
void populatePrepareVectorToNVMMAPatterns(RewritePatternSet &patterns)
Populate patterns that prepare vector operations for lowering to nvgpu.mma.sync operations.
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.