MLIR 24.0.0git
VectorToGPU.cpp
Go to the documentation of this file.
1//===- VectorToGPU.cpp - Convert vector to GPU dialect ----------*- C++ -*-===//
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// This file implements lowering of vector operations to GPU dialect ops.
10//
11//===----------------------------------------------------------------------===//
12
14
25#include "mlir/IR/Builders.h"
26#include "mlir/IR/Region.h"
27#include "mlir/Pass/Pass.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/TypeSwitch.h"
31#include "llvm/Support/DebugLog.h"
32
33#define DEBUG_TYPE "vector-to-gpu"
34
35namespace mlir {
36#define GEN_PASS_DEF_CONVERTVECTORTOGPUPASS
37#include "mlir/Conversion/Passes.h.inc"
38} // namespace mlir
39
40using namespace mlir;
41
42// Return true if the contract op can be convert to MMA matmul.
43static bool contractSupportsMMAMatrixType(vector::ContractionOp contract) {
44 using MapList = ArrayRef<ArrayRef<AffineExpr>>;
45 auto infer = [&](MapList m) {
46 return AffineMap::inferFromExprList(m, contract.getContext());
47 };
48 AffineExpr m, n, k;
49 bindDims(contract.getContext(), m, n, k);
50 auto iteratorTypes = contract.getIteratorTypes().getValue();
51 if (!(vector::isParallelIterator(iteratorTypes[0]) &&
52 vector::isParallelIterator(iteratorTypes[1]) &&
53 vector::isReductionIterator(iteratorTypes[2])))
54 return false;
55
56 // The contract needs to represent a matmul to be able to convert to
57 // MMAMatrix matmul.
58 if (contract.getIndexingMapsArray() != infer({{m, k}, {k, n}, {m, n}}))
59 return false;
60
61 return true;
62}
63
64// Test whether the permutation map's first result corresponds to its last
65// dimension.
66//
67// In contexts where we only accept maps that have the last (most minor)
68// dimension as exactly one of the two results, this is sufficient to classify
69// whether it represents a transpose.
70static bool isFirstResultLastMapDimension(AffineMap permutationMap) {
71 MLIRContext *ctx = permutationMap.getContext();
72 const unsigned nDim = permutationMap.getNumDims();
73 if (0 == nDim || permutationMap.getResults().empty())
74 return false;
75 return permutationMap.getResult(0) == getAffineDimExpr(nDim - 1, ctx);
76}
77
78// Return the `leadDimension` (row stride) implied by |permutationMap| for
79// |type|, if |type| is a memref with a statically-known layout.
80//
81// The `leadDimension` is the stride (in elements) between consecutive rows in
82// the 2D view described by |permutationMap|. This helper supports the subset
83// of maps permitted by vector.transfer_read:
84// - Exactly 2 results.
85// - Each result is either an affine dimension or the constant 0 (broadcast).
86//
87// Constraints:
88// - Requires the most minor memref stride to be 1.
89//
90// Broadcast:
91// - If either result is constant 0, the implied `leadDimension` is 0.
92static std::optional<int64_t>
93getStaticallyKnownRowStride(ShapedType type, AffineMap permutationMap) {
94 auto memrefType = dyn_cast<MemRefType>(type);
95 if (!memrefType)
96 return std::nullopt;
97 // If the memref is 0 or 1D the horizontal stride is 0.
98 if (memrefType.getRank() < 2)
99 return 0;
100 int64_t offset = 0;
101 SmallVector<int64_t> strides;
102 if (failed(memrefType.getStridesAndOffset(strides, offset)) ||
103 strides.back() != 1)
104 return std::nullopt;
105
106 if (permutationMap.getNumResults() != 2)
107 return std::nullopt;
108
109 unsigned strideIndex = strides.size();
110
111 for (AffineExpr result : permutationMap.getResults()) {
112 if (auto cst = dyn_cast<AffineConstantExpr>(result)) {
113 // Constant value must be zero.
114 if (0 != cst.getValue())
115 return std::nullopt;
116 // A broadcast result forces row stride to 0.
117 return 0;
118 }
119 auto dim = dyn_cast<AffineDimExpr>(result);
120 // Only Dim & Const results are supported.
121 if (!dim)
122 return std::nullopt;
123 strideIndex = std::min(strideIndex, dim.getPosition());
124 }
125
126 // Structural validity check: ensure that the map selects at least one
127 // dimension more major than the most minor dimension. This also excludes
128 // degenerate cases where both results map to the most minor dimension.
129 if (strideIndex + 1 >= strides.size())
130 return std::nullopt;
131
132 const int64_t stride = strides[strideIndex];
133 if (stride == ShapedType::kDynamic)
134 return std::nullopt;
135 return stride;
136}
137
138// Return true if the transfer op can be converted to a MMA matrix load.
139static bool transferReadSupportsMMAMatrixType(vector::TransferReadOp readOp) {
140 if (readOp.getMask() || readOp.hasOutOfBoundsDim() ||
141 readOp.getVectorType().getRank() != 2)
142 return false;
143
144 AffineMap permutationMap = readOp.getPermutationMap();
145 if (!getStaticallyKnownRowStride(readOp.getShapedType(), permutationMap))
146 return false;
147
148 // Only allow integer types if the signedness can be inferred.
149 if (readOp.getVectorType().getElementType().isInteger(8))
150 if (!readOp->hasOneUse() || (!isa<arith::ExtSIOp>(*readOp->user_begin()) &&
151 !isa<arith::ExtUIOp>(*readOp->user_begin())))
152 return false;
153
154 MLIRContext *ctx = readOp.getContext();
155 AffineExpr innerDim = getAffineDimExpr(permutationMap.getNumDims() - 1, ctx);
156 return llvm::is_contained(permutationMap.getResults(), innerDim);
157}
158
159// Return true if the transfer op can be converted to a MMA matrix store.
160static bool
161transferWriteSupportsMMAMatrixType(vector::TransferWriteOp writeOp) {
162 // TODO: support 0-d corner case.
163 if (writeOp.getTransferRank() == 0)
164 return false;
165
166 if (writeOp.getMask() || writeOp.hasOutOfBoundsDim() ||
167 writeOp.getVectorType().getRank() != 2)
168 return false;
169
170 AffineMap permutationMap = writeOp.getPermutationMap();
171 std::optional<int64_t> stride =
172 getStaticallyKnownRowStride(writeOp.getShapedType(), permutationMap);
173 // Stride of zero means broadcast which is not permitted for writes.
174 if (!stride.has_value() || stride.value() == 0)
175 return false;
176
177 MLIRContext *ctx = writeOp.getContext();
178 AffineExpr innerDim = getAffineDimExpr(permutationMap.getNumDims() - 1, ctx);
179 return llvm::is_contained(permutationMap.getResults(), innerDim);
180}
181
183 if (isa<scf::ForOp, scf::YieldOp>(op))
184 return true;
185 // MMA ops cannot honor a mask. An op masked by an enclosing vector.mask has
186 // no mask operand, so the checks below would miss it; rewriting it in place
187 // would leave non-maskable ops inside the vector.mask region.
188 auto maskableOp = dyn_cast<vector::MaskableOpInterface>(op);
189 if (maskableOp && maskableOp.isMasked())
190 return false;
191 if (auto transferRead = dyn_cast<vector::TransferReadOp>(op))
192 return transferReadSupportsMMAMatrixType(transferRead);
193 if (auto transferWrite = dyn_cast<vector::TransferWriteOp>(op))
194 return transferWriteSupportsMMAMatrixType(transferWrite);
195 if (auto contract = dyn_cast<vector::ContractionOp>(op))
197 if (auto constant = dyn_cast<arith::ConstantOp>(op))
198 return constantSupportsMMAMatrixType(constant);
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))
206}
207
208// Analyze slice of operations based on convert op to figure out if the whole
209// slice can be converted to MMA operations.
211 auto hasVectorDest = [](Operation *op) {
212 return llvm::any_of(op->getResultTypes(), llvm::IsaPred<VectorType>);
213 };
214 BackwardSliceOptions backwardSliceOptions;
215 backwardSliceOptions.filter = hasVectorDest;
216
217 auto hasVectorSrc = [](Operation *op) {
218 return llvm::any_of(op->getOperandTypes(), llvm::IsaPred<VectorType>);
219 };
220 ForwardSliceOptions forwardSliceOptions;
221 forwardSliceOptions.filter = hasVectorSrc;
222
225 DenseMap<Operation *, bool> supportsMMAMatrixTypeCache;
226
227 auto getCachedBackwardSlice =
228 [&](Operation *currentOp) -> ArrayRef<Operation *> {
229 auto [it, inserted] = backwardSliceCache.try_emplace(currentOp);
230 if (!inserted)
231 return it->second;
232
233 SetVector<Operation *> backwardSlice;
234 LogicalResult result =
235 getBackwardSlice(currentOp, &backwardSlice, backwardSliceOptions);
236 assert(result.succeeded() && "expected a backward slice");
237 (void)result;
238 it->second = backwardSlice.takeVector();
239 return it->second;
240 };
241
242 auto getCachedForwardSlice =
243 [&](Operation *currentOp) -> ArrayRef<Operation *> {
244 auto [it, inserted] = forwardSliceCache.try_emplace(currentOp);
245 if (!inserted)
246 return it->second;
247
248 SetVector<Operation *> forwardSlice;
249 // Special case for ForOp, we don't want to include the whole region but
250 // only the value using the region arguments.
251 // TODO: We should refine this to only care about the region arguments being
252 // converted to matrix type.
253 if (auto forOp = dyn_cast<scf::ForOp>(currentOp)) {
254 for (Value forOpResult : forOp.getResults())
255 getForwardSlice(forOpResult, &forwardSlice, forwardSliceOptions);
256 for (BlockArgument &arg : forOp.getRegionIterArgs())
257 getForwardSlice(arg, &forwardSlice, forwardSliceOptions);
258 } else {
259 getForwardSlice(currentOp, &forwardSlice, forwardSliceOptions);
260 }
261 it->second = forwardSlice.takeVector();
262 return it->second;
263 };
264
265 auto cachedSupportsMMAMatrixType = [&](Operation *currentOp) {
266 auto [it, inserted] =
267 supportsMMAMatrixTypeCache.try_emplace(currentOp, false);
268 if (inserted)
269 it->second = supportsMMaMatrixType(currentOp);
270 return it->second;
271 };
272
273 SetVector<Operation *> opToConvert;
274 op->walk([&](Operation *nestedOp) {
275 if (!isa<vector::ContractionOp>(nestedOp) &&
277 return;
278 if (backwardSliceCache.contains(nestedOp))
279 return;
280
281 SetVector<Operation *> dependentOps;
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));
288 }
289 // If any instruction cannot use MMA matrix type drop the whole
290 // chain. MMA matrix are stored in an opaque type so they cannot be used
291 // by all operations.
292 if (llvm::any_of(dependentOps, [&](Operation *op) {
293 if (!cachedSupportsMMAMatrixType(op)) {
294 LDBG() << "cannot convert op: " << *op;
295 return true;
296 }
297 return false;
298 }))
299 return;
300
301 opToConvert.insert_range(dependentOps);
302 });
303 // Sort the operations so that we can convert them in topological order.
304 return topologicalSort(opToConvert);
305}
306
307namespace {
308// Transform contract into (m, k)x(k, n)x(m, n) form so that it can be converted
309// to MMA matmul.
310struct PrepareContractToGPUMMA
311 : public OpRewritePattern<vector::ContractionOp> {
312 using Base::Base;
313
314 LogicalResult matchAndRewrite(vector::ContractionOp op,
315 PatternRewriter &rewriter) const override {
316 // The mask is not updated: the rewrite may create ops inside the
317 // vector.mask region or swap the m and n iteration dimensions.
318 if (op.isMasked())
319 return rewriter.notifyMatchFailure(op, "masked contraction");
320
321 Location loc = op.getLoc();
322 Value lhs = op.getLhs(), rhs = op.getRhs(), res = op.getAcc();
323
324 // Set up the parallel/reduction structure in right form.
325 using MapList = ArrayRef<ArrayRef<AffineExpr>>;
326 auto infer = [&](MapList m) {
327 return AffineMap::inferFromExprList(m, op.getContext());
328 };
329 AffineExpr m, n, k;
330 bindDims(rewriter.getContext(), m, n, k);
331 static constexpr std::array<int64_t, 2> perm = {1, 0};
332 auto iteratorTypes = op.getIteratorTypes().getValue();
333 SmallVector<AffineMap, 4> maps = op.getIndexingMapsArray();
334 if (!(vector::isParallelIterator(iteratorTypes[0]) &&
335 vector::isParallelIterator(iteratorTypes[1]) &&
336 vector::isReductionIterator(iteratorTypes[2])))
337 return rewriter.notifyMatchFailure(op, "not a gemm contraction");
338 //
339 // Two outer parallel, one inner reduction (matmat flavor).
340 //
341 // This is the classical row-major matmul, nothing to do.
342 if (maps == infer({{m, k}, {k, n}, {m, n}}))
343 return rewriter.notifyMatchFailure(op, "contraction already prepared");
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}})) {
352 std::swap(rhs, lhs);
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}})) {
356 std::swap(rhs, lhs);
357 rhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
358 } else if (maps == infer({{k, m}, {k, n}, {n, m}})) {
359 std::swap(lhs, rhs);
360 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
361 } else if (maps == infer({{k, m}, {n, k}, {n, m}})) {
362 std::swap(lhs, rhs);
363 } else {
364 // TODO: llvm_unreachable ?
365 return rewriter.notifyMatchFailure(op, "unexpected contraction case");
366 }
367 rewriter.replaceOpWithNewOp<vector::ContractionOp>(
368 op, lhs, rhs, res,
369 rewriter.getAffineMapArrayAttr(infer({{m, k}, {k, n}, {m, n}})),
370 op.getIteratorTypes(), op.getKind(), op.getFastmath());
371 return success();
372 }
373};
374} // namespace
375
376// MMA types have different layout based on how they are used in matmul ops.
377// Figure the right layout to use by looking at op uses.
378// TODO: Change the GPU dialect to abstract the layout at the this level and
379// only care about it during lowering to NVVM.
380static const char *inferFragType(Operation *op) {
381 // We can have arith.ext ops before reaching contract ops. See through them
382 // and other kinds of elementwise ops.
383 if (op->hasOneUse()) {
384 Operation *userOp = *op->user_begin();
385 if (userOp->hasTrait<OpTrait::Elementwise>())
386 return inferFragType(userOp);
387 }
388
389 for (auto contract :
390 llvm::make_isa_range<vector::ContractionOp>(op->getUsers())) {
391 assert(op->getNumResults() == 1);
392 if (contract.getLhs() == op->getResult(0))
393 return "AOp";
394 if (contract.getRhs() == op->getResult(0))
395 return "BOp";
396 }
397 return "COp";
398}
399
400static LogicalResult
401convertTransferReadOp(RewriterBase &rewriter, vector::TransferReadOp op,
402 llvm::DenseMap<Value, Value> &valueMapping) {
403 OpBuilder::InsertionGuard g(rewriter);
404 rewriter.setInsertionPoint(op);
405
406 assert(op.getTransferRank() > 0 && "unexpected 0-d transfer");
408 "expected convertible operation");
409
410 AffineMap permutationMap = op.getPermutationMap();
411 std::optional<int64_t> stride =
412 getStaticallyKnownRowStride(op.getShapedType(), permutationMap);
413 if (!stride.has_value()) {
414 LDBG() << "no stride";
415 return rewriter.notifyMatchFailure(op, "no stride");
416 }
417
418 // transferReadSupportsMMAMatrixType ensures that either of the map results is
419 // the most minor dimension. Under this constraint, whether the map represents
420 // a transposed view can be inferred from whether the first result is the most
421 // minor memref dimension.
422 const bool isTranspose = isFirstResultLastMapDimension(permutationMap);
423
424 Value mappingResult = op.getResult();
425 auto elType = op.getVectorType().getElementType();
426 const char *fragType = inferFragType(op);
427 if (op->hasOneUse()) {
428 auto *user = *op->user_begin();
429 // Infer the signedness of the mma type from the integer extend.
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);
436 }
437 }
438 gpu::MMAMatrixType type =
439 gpu::MMAMatrixType::get(op.getVectorType().getShape(), elType, fragType);
440 Value load = gpu::SubgroupMmaLoadMatrixOp::create(
441 rewriter, op.getLoc(), type, op.getBase(), op.getIndices(),
442 rewriter.getIndexAttr(*stride),
443 isTranspose ? rewriter.getUnitAttr() : UnitAttr());
444 valueMapping[mappingResult] = load;
445
446 LDBG() << "transfer read to: " << load;
447 return success();
448}
449
450static LogicalResult
451convertTransferWriteOp(RewriterBase &rewriter, vector::TransferWriteOp op,
452 llvm::DenseMap<Value, Value> &valueMapping) {
453 OpBuilder::InsertionGuard g(rewriter);
454 rewriter.setInsertionPoint(op);
455
457 AffineMap permutationMap = op.getPermutationMap();
458 std::optional<int64_t> stride =
459 getStaticallyKnownRowStride(op.getShapedType(), permutationMap);
460 if (!stride.has_value()) {
461 LDBG() << "no stride";
462 return rewriter.notifyMatchFailure(op, "no stride");
463 }
464
465 // As for transfer_read, transferWriteSupportsMMAMatrixType ensures that
466 // either of the map results is the most minor dimension, so the first result
467 // being that dimension means a transposed store.
468 const bool isTranspose = isFirstResultLastMapDimension(permutationMap);
469
470 auto it = valueMapping.find(op.getVector());
471 if (it == valueMapping.end()) {
472 LDBG() << "no mapping";
473 return rewriter.notifyMatchFailure(op, "no mapping");
474 }
475
476 Value matrix = it->second;
477 auto store = gpu::SubgroupMmaStoreMatrixOp::create(
478 rewriter, op.getLoc(), matrix, op.getBase(), op.getIndices(),
479 rewriter.getIndexAttr(*stride),
480 isTranspose ? rewriter.getUnitAttr() : UnitAttr());
481 (void)store;
482
483 LDBG() << "transfer write to: " << store;
484
485 LDBG() << "erase: " << op;
486 rewriter.eraseOp(op);
487 return success();
488}
489
490static LogicalResult
491convertContractOp(RewriterBase &rewriter, vector::ContractionOp op,
492 llvm::DenseMap<Value, Value> &valueMapping) {
493 OpBuilder::InsertionGuard g(rewriter);
494 rewriter.setInsertionPoint(op);
495
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())
501 return rewriter.notifyMatchFailure(op, "no mapping");
502 Value opA = itA->second, opB = itB->second, opC = itC->second;
503 Value matmul = gpu::SubgroupMmaComputeOp::create(rewriter, op.getLoc(),
504 opC.getType(), opA, opB, opC,
505 /*a_transpose=*/UnitAttr(),
506 /*b_transpose=*/UnitAttr());
507 valueMapping[op.getResult()] = matmul;
508 return success();
509}
510
511/// Convert a 2D splat ConstantOp to a SubgroupMmaConstantMatrix op.
512static LogicalResult
513convertConstantOp(RewriterBase &rewriter, arith::ConstantOp op,
514 llvm::DenseMap<Value, Value> &valueMapping) {
515 OpBuilder::InsertionGuard g(rewriter);
516 rewriter.setInsertionPoint(op);
517
519
520 auto splat =
521 cast<SplatElementsAttr>(op.getValue()).getSplatValue<TypedAttr>();
522 auto scalarConstant =
523 arith::ConstantOp::create(rewriter, op.getLoc(), splat.getType(), splat);
524 const char *fragType = inferFragType(op);
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;
531 return success();
532}
533
534/// Convert a vector.broadcast from scalar to a SubgroupMmaConstantMatrix op.
535static LogicalResult
536convertBroadcastOp(RewriterBase &rewriter, vector::BroadcastOp op,
537 llvm::DenseMap<Value, Value> &valueMapping) {
538 OpBuilder::InsertionGuard g(rewriter);
539 rewriter.setInsertionPoint(op);
540
542
543 const char *fragType = inferFragType(op);
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;
550 return success();
551}
552
553/// Convert an elementwise op to the equivalent elementwise op on MMA matrix.
554static LogicalResult
556 gpu::MMAElementwiseOp opType,
557 llvm::DenseMap<Value, Value> &valueMapping) {
558 OpBuilder::InsertionGuard g(rewriter);
559 rewriter.setInsertionPoint(op);
560
561 SmallVector<Value> matrixOperands;
562 for (Value operand : op->getOperands()) {
563 auto it = valueMapping.find(operand);
564 if (it == valueMapping.end())
565 return rewriter.notifyMatchFailure(op, "no mapping");
566 matrixOperands.push_back(it->second);
567 }
568 auto resultType = cast<gpu::MMAMatrixType>(matrixOperands[0].getType());
569 if (opType == gpu::MMAElementwiseOp::EXTF ||
570 opType == gpu::MMAElementwiseOp::TRUNCF) {
571 // The floating point extension and truncation has a different result type.
572 auto vectorType = cast<VectorType>(op->getResultTypes()[0]);
573 resultType = gpu::MMAMatrixType::get(resultType.getShape(),
574 vectorType.getElementType(),
575 resultType.getOperand());
576 }
577
578 Value newOp = gpu::SubgroupMmaElementwiseOp::create(
579 rewriter, op->getLoc(), resultType, matrixOperands, opType);
580 valueMapping[op->getResult(0)] = newOp;
581 return success();
582}
583
585 patterns.add<PrepareContractToGPUMMA>(patterns.getContext());
587}
588
590 Operation *rootOp) {
592 llvm::DenseMap<Value, Value> valueMapping;
593
594 auto globalRes = LogicalResult::success();
595 for (Operation *op : ops) {
596 LDBG() << "Process op: " << *op;
597 // Apparently callers do not want to early exit on failure here.
598 auto res = LogicalResult::success();
599 if (auto transferRead = dyn_cast<vector::TransferReadOp>(op)) {
600 res = convertTransferReadOp(rewriter, transferRead, valueMapping);
601 } else if (auto transferWrite = dyn_cast<vector::TransferWriteOp>(op)) {
602 res = convertTransferWriteOp(rewriter, transferWrite, valueMapping);
603 } else if (auto contractOp = dyn_cast<vector::ContractionOp>(op)) {
604 res = convertContractOp(rewriter, contractOp, valueMapping);
605 } else if (auto constantOp = dyn_cast<arith::ConstantOp>(op)) {
606 res = convertConstantOp(rewriter, constantOp, valueMapping);
607 } else if (auto broadcastOp = dyn_cast<vector::BroadcastOp>(op)) {
608 res = convertBroadcastOp(rewriter, broadcastOp, valueMapping);
609 } else if (auto forOp = dyn_cast<scf::ForOp>(op)) {
610 res = convertForOpForMMA(rewriter, forOp, valueMapping);
611 } else if (auto yieldOp = dyn_cast<scf::YieldOp>(op)) {
612 res = convertYieldOpForMMA(rewriter, yieldOp, valueMapping);
613 } else if (auto elementwiseType = convertElementwiseOpToMMA(op)) {
614 res = convertElementwiseOp(rewriter, op, *elementwiseType, valueMapping);
615 }
616 if (failed(res))
617 globalRes = failure();
618 }
619 return globalRes;
620}
621
622namespace {
623struct ConvertVectorToGPUPass
624 : public impl::ConvertVectorToGPUPassBase<ConvertVectorToGPUPass> {
625
626 void runOnOperation() override {
627 RewritePatternSet patterns(&getContext());
629 if (failed(applyPatternsGreedily(getOperation(), std::move(patterns))))
630 return signalPassFailure();
631
632 IRRewriter rewriter(&getContext());
633 (void)convertVectorToMMAOps(rewriter, getOperation());
634 }
635};
636} // namespace
return success()
lhs
b getContext())
auto load
*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.
Definition AffineExpr.h:68
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
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.
Definition Value.h:308
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:116
UnitAttr getUnitAttr()
Definition Builders.cpp:106
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
Definition Builders.h:94
MLIRContext * getContext() const
Definition Builders.h:56
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
Definition Builders.cpp:327
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.
Definition MLIRContext.h:71
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:803
bool hasOneUse()
Returns true if this operation has exactly one use.
Definition Operation.h:903
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:434
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
operand_type_range getOperandTypes()
Definition Operation.h:424
result_type_range getResultTypes()
Definition Operation.h:455
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:405
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),...
Definition Operation.h:851
user_range getUsers()
Returns a range of all users.
Definition Operation.h:927
user_iterator user_begin()
Definition Operation.h:923
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:431
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...
Definition Value.h:96
MMAMatrix represents a matrix held by a subgroup for matrix-matrix multiply accumulate operations.
Definition GPUBase.h:132
static MMAMatrixType get(ArrayRef< int64_t > shape, Type elementType, StringRef operand)
Get MMAMatrixType and verify construction Invariants.
Definition GPUBase.cpp:114
bool isReductionIterator(Attribute attr)
Returns true if attr has "reduction" iterator type semantics.
Definition VectorOps.h:146
bool isParallelIterator(Attribute attr)
Returns true if attr has "parallel" iterator type semantics.
Definition VectorOps.h:141
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.
Definition Utils.cpp:311
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 .
Definition AffineExpr.h:313
LogicalResult convertForOpForMMA(RewriterBase &rewriter, scf::ForOp op, llvm::DenseMap< Value, Value > &valueMapping)
Extend loop-carried values with their converted counterparts.
LogicalResult applyPatternsGreedily(Region &region, 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
Definition LLVM.h:128
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
Definition LLVM.h:123
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.
TransitiveFilter filter