MLIR 23.0.0git
VectorToXeGPU.cpp
Go to the documentation of this file.
1//===- VectorToXeGPU.cpp - Convert vector to XeGPU 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 XeGPU dialect ops.
10//
11//===----------------------------------------------------------------------===//
12
15
24#include "mlir/Pass/Pass.h"
26#include "llvm/ADT/TypeSwitch.h"
27
28#include <algorithm>
29#include <optional>
30
31namespace mlir {
32#define GEN_PASS_DEF_CONVERTVECTORTOXEGPU
33#include "mlir/Conversion/Passes.h.inc"
34} // namespace mlir
35
36using namespace mlir;
37
38namespace {
39
40// Return true if value represents a zero constant.
41static bool isZeroConstant(Value val) {
42 auto constant = val.getDefiningOp<arith::ConstantOp>();
43 if (!constant)
44 return false;
45
46 return TypeSwitch<Attribute, bool>(constant.getValue())
47 .Case([](FloatAttr floatAttr) { return floatAttr.getValue().isZero(); })
48 .Case([](IntegerAttr intAttr) { return intAttr.getValue().isZero(); })
49 .Default(false);
50}
51
52static LogicalResult storeLoadPreconditions(PatternRewriter &rewriter,
53 Operation *op, VectorType vecTy,
54 MemRefType memTy) {
55 // Validate only vector as the basic vector store and load ops guarantee
56 // XeGPU-compatible memref source.
57 unsigned vecRank = vecTy.getRank();
58 if (!(vecRank == 1 || vecRank == 2))
59 return rewriter.notifyMatchFailure(op, "Expects 1D or 2D vector");
60
61 if (!vecTy.getElementType().isIntOrFloat())
62 return rewriter.notifyMatchFailure(
63 op, "Expected scalar type with known bitwidth");
64
65 // XeGPU requires the memref to have a scalar integer or float element type.
66 // Memrefs with vector element types (e.g. memref<?xvector<4xf32>>) are not
67 // supported because createNdDescriptor computes byte offsets using
68 // getElementTypeBitWidth(), which asserts on non-integer/float types.
69 if (!memTy.getElementType().isIntOrFloat())
70 return rewriter.notifyMatchFailure(
71 op, "Unsupported memref element type: expected integer or float");
72
73 return success();
74}
75
76static LogicalResult transferPreconditions(PatternRewriter &rewriter,
77 VectorTransferOpInterface xferOp) {
78 if (xferOp.getMask())
79 return rewriter.notifyMatchFailure(xferOp,
80 "Masked transfer is not supported");
81
82 auto srcTy = dyn_cast<MemRefType>(xferOp.getShapedType());
83 if (!srcTy)
84 return rewriter.notifyMatchFailure(xferOp, "Expects memref source");
85
86 // Validate further transfer op semantics.
88 int64_t offset;
89 if (failed(srcTy.getStridesAndOffset(strides, offset)))
90 return rewriter.notifyMatchFailure(xferOp,
91 "The memref strides cannot be inferred");
92 if (strides.empty())
93 return rewriter.notifyMatchFailure(xferOp, "0D memref is not supported");
94 if (strides.back() != 1)
95 return rewriter.notifyMatchFailure(
96 xferOp, "Buffer must be contiguous in the innermost dimension");
97
98 VectorType vecTy = xferOp.getVectorType();
99 unsigned vecRank = vecTy.getRank();
100 if (vecRank == 0)
101 return rewriter.notifyMatchFailure(xferOp, "0D vectors are not supported");
102 if (xferOp.hasOutOfBoundsDim() && vecRank < 2)
103 return rewriter.notifyMatchFailure(
104 xferOp, "Boundary check is available only for block instructions.");
105
106 AffineMap map = xferOp.getPermutationMap();
107 if (!map.isProjectedPermutation(/*allowZeroInResults=*/false))
108 return rewriter.notifyMatchFailure(xferOp, "Unsupported permutation map");
109 unsigned numInputDims = map.getNumInputs();
110 for (AffineExpr expr : map.getResults().take_back(vecRank)) {
111 auto dim = dyn_cast<AffineDimExpr>(expr);
112 if (dim.getPosition() < (numInputDims - vecRank))
113 return rewriter.notifyMatchFailure(
114 xferOp, "Only the innermost dimensions can be accessed");
115 }
116
117 return success();
118}
119
120static xegpu::CreateNdDescOp createNdDescriptor(PatternRewriter &rewriter,
121 Location loc,
122 xegpu::TensorDescType descType,
124 MemRefType srcTy = src.getType();
125 assert(srcTy.isStrided() && "Expected strided memref type");
126 auto [strides, offset] = srcTy.getStridesAndOffset();
127 bool isStatic = true;
128
129 // Memref is dynamic if any of its shape, offset or strides is dynamic.
130 if (!srcTy.hasStaticShape())
131 isStatic = false;
132
133 if (!ShapedType::isStatic(offset))
134 isStatic = false;
135
136 for (auto stride : strides) {
137 if (!ShapedType::isStatic(stride)) {
138 isStatic = false;
139 break;
140 }
141 }
142
143 xegpu::CreateNdDescOp ndDesc;
144 if (isStatic) {
145 ndDesc = xegpu::CreateNdDescOp::create(rewriter, loc, descType, src);
146 } else {
147 // In case of ranked dynamic memref, instead of passing on the memref,
148 // i64 base address, source's offset, shape and strides have to be
149 // explicitly provided.
150 auto meta = memref::ExtractStridedMetadataOp::create(rewriter, loc, src);
151 auto baseAddrIndex = memref::ExtractAlignedPointerAsIndexOp::create(
152 rewriter, loc, meta.getBaseBuffer());
153 auto offset = meta.getOffset();
154 auto elemByteSize = srcTy.getElementTypeBitWidth() / 8;
155 auto offsetInBytes = arith::MulIOp::create(
156 rewriter, loc, offset,
157 arith::ConstantIndexOp::create(rewriter, loc, elemByteSize));
158 auto adjustedBaseAddr = arith::AddIOp::create(
159 rewriter, loc, baseAddrIndex.getResult(), offsetInBytes);
160 auto adjustedAddrI64 = arith::IndexCastOp::create(
161 rewriter, loc, rewriter.getI64Type(), adjustedBaseAddr);
162 ndDesc = xegpu::CreateNdDescOp::create(
163 rewriter, loc, descType, adjustedAddrI64,
164 meta.getConstifiedMixedSizes(), meta.getConstifiedMixedStrides());
165 }
166
167 return ndDesc;
168}
169
170// Adjusts the strides of a memref according to a given permutation map for
171// vector operations.
172//
173// This function updates the innermost strides in the `strides` array to
174// reflect the permutation specified by `permMap`. The permutation is computed
175// using the inverse and broadcasting-aware version of the permutation map,
176// and is applied to the relevant strides. This ensures that memory accesses
177// are consistent with the logical permutation of vector elements.
178//
179// Example:
180// Suppose we have a memref of rank 4 with strides `[s0, s1, s2, s3]`.
181// If the permutation map swaps the last two dimensions (e.g., [0, 1] -> [1,
182// 0]), then after calling this function, the last two strides will be
183// swapped:
184// Original strides: [s0, s1, s2, s3]
185// After permutation: [s0, s1, s3, s2]
186//
187static void adjustStridesForPermutation(AffineMap permMap,
188 SmallVectorImpl<Value> &strides) {
189
193 SmallVector<int64_t> perms64(perms.begin(), perms.end());
194 strides = applyPermutation(strides, perms64);
195}
196
197// Computes memory strides and a memref offset for vector transfer operations,
198// handling both static and dynamic memrefs while applying permutation
199// transformations for XeGPU lowering.
200template <
201 typename OpType,
202 typename = std::enable_if_t<llvm::is_one_of<
203 std::decay_t<OpType>, vector::TransferReadOp, vector::TransferWriteOp,
204 vector::GatherOp, vector::ScatterOp>::value>>
205static std::pair<SmallVector<Value>, Value>
206computeMemrefMeta(OpType xferOp, PatternRewriter &rewriter) {
207 SmallVector<Value> strides;
208 Value baseMemref = xferOp.getBase();
209 MemRefType memrefType = dyn_cast<MemRefType>(baseMemref.getType());
210
211 Location loc = xferOp.getLoc();
212 Value offsetVal = nullptr;
213 if (memrefType.hasStaticShape()) {
214 int64_t offset;
215 SmallVector<int64_t> intStrides;
216 if (failed(memrefType.getStridesAndOffset(intStrides, offset)))
217 return {{}, offsetVal};
218 bool hasDynamicStrides = llvm::any_of(intStrides, [](int64_t strideVal) {
219 return ShapedType::isDynamic(strideVal);
220 });
221
222 if (!hasDynamicStrides)
223 for (int64_t s : intStrides)
224 strides.push_back(arith::ConstantIndexOp::create(rewriter, loc, s));
225
226 if (!ShapedType::isDynamic(offset))
227 offsetVal = arith::ConstantIndexOp::create(rewriter, loc, offset);
228 }
229
230 if (strides.empty() || !offsetVal) {
231 // For dynamic shape memref, use memref.extract_strided_metadata to get
232 // stride values
233 unsigned rank = memrefType.getRank();
234 Type indexType = rewriter.getIndexType();
235
236 // Result types: [base_memref, offset, stride0, stride1, ..., strideN-1,
237 // size0, size1, ..., sizeN-1]
238 SmallVector<Type> resultTypes;
239 resultTypes.push_back(MemRefType::get(
240 {}, memrefType.getElementType())); // base memref (unranked)
241 resultTypes.push_back(indexType); // offset
242
243 for (unsigned i = 0; i < rank; ++i)
244 resultTypes.push_back(indexType); // strides
245
246 for (unsigned i = 0; i < rank; ++i)
247 resultTypes.push_back(indexType); // sizes
248
249 auto meta = memref::ExtractStridedMetadataOp::create(
250 rewriter, loc, resultTypes, baseMemref);
251
252 if (strides.empty())
253 strides.append(meta.getStrides().begin(), meta.getStrides().end());
254
255 if (!offsetVal)
256 offsetVal = meta.getOffset();
257 }
258
259 // Strides are returned in original memref order; permutation is applied in
260 // computeOffsets only where offsets are indexed in vector order.
261 return {strides, offsetVal};
262}
263
264// This function compute the vectors of localOffsets for scattered load/stores.
265// It is used in the lowering of vector.transfer_read/write to
266// load_gather/store_scatter Example:
267// %0 = vector.transfer_read %expand_shape[%block_id_y, %c0, %c0, %c0, %c0],
268// %cst {in_bounds = [true, true, true, true]}>} :
269// memref<8x4x2x6x32xbf16>, vector<4x2x6x32xbf16>
270//
271// %6 = vector.step: vector<4xindex>
272// %7 = vector.step: vector<2xindex>
273// %8 = vector.step: vector<6xindex>
274// %9 = vector.step: vector<32xindex>
275// %10 = arith.mul %6, 384
276// %11 = arith.mul %7, 192
277// %12 = arith.mul %8, 32
278// %13 = arith.mul %9, 1
279// %14 = vector.shape_cast %10: vector<4xindex> -> vector<4x1x1x1xbf16>
280// %15 = vector.shape_cast %11: vector<2xindex> -> vector<1x2x1x1xbf16>
281// %16 = vector.shape_cast %12: vector<6xindex> -> vector<1x1x6x1xbf16>
282// %17 = vector.shape_cast %13: vector<32xindex> -> vector<1x1x1x32xbf16>
283// %18 = vector.broadcast %14: vector<4x1x1x1xbf16> -> vector<4x2x6x32xindex>
284// %19 = vector.broadcast %15: vector<1x2x1x1xbf16> -> vector<4x2x6x32xindex>
285// %20 = vector.broadcast %16: vector<1x1x6x1xbf16> -> vector<4x2x6x32xindex>
286// %21 = vector.broadcast %17: vector<1x1x1x32xbf16> -> vector<4x2x6x32xindex>
287// %22 = arith.add %18, %19
288// %23 = arith.add %20, %21
289// %local_offsets = arith.add %22, %23
290// %orig_offset = %block_id_y * 4x2x6x32 // consider using affine map
291// %offsets = memref_offset + orig_offset + local_offsets
292static Value computeOffsets(VectorTransferOpInterface xferOp,
293 PatternRewriter &rewriter, ArrayRef<Value> strides,
294 Value baseOffset) {
295 Location loc = xferOp.getLoc();
296 VectorType vectorType = xferOp.getVectorType();
297 SmallVector<Value> indices(xferOp.getIndices().begin(),
298 xferOp.getIndices().end());
299 ArrayRef<int64_t> vectorShape = vectorType.getShape();
300
301 // Create vector.step operations for each dimension
302 SmallVector<Value> stepVectors;
303 llvm::map_to_vector(vectorShape, [&](int64_t dim) {
304 auto stepType = VectorType::get({dim}, rewriter.getIndexType());
305 auto stepOp = vector::StepOp::create(rewriter, loc, stepType);
306 stepVectors.push_back(stepOp);
307 return stepOp;
308 });
309
310 // Local offsets are indexed in vector order, so permute strides; the base
311 // offset below uses the original memref-order strides.
312 SmallVector<Value> permutedStrides(strides.begin(), strides.end());
313 adjustStridesForPermutation(xferOp.getPermutationMap(), permutedStrides);
314
315 // Multiply step vectors by corresponding strides
316 size_t memrefRank = permutedStrides.size();
317 size_t vectorRank = vectorShape.size();
318 SmallVector<Value> strideMultiplied;
319 for (size_t i = 0; i < vectorRank; ++i) {
320 size_t memrefDim = memrefRank - vectorRank + i;
321 Value strideValue = permutedStrides[memrefDim];
322 auto mulType = dyn_cast<VectorType>(stepVectors[i].getType());
323 auto bcastOp =
324 vector::BroadcastOp::create(rewriter, loc, mulType, strideValue);
325 auto mulOp = arith::MulIOp::create(rewriter, loc, stepVectors[i], bcastOp);
326 strideMultiplied.push_back(mulOp);
327 }
328
329 // Shape cast each multiplied vector to add singleton dimensions
330 SmallVector<Value> shapeCasted;
331 for (size_t i = 0; i < vectorRank; ++i) {
332 SmallVector<int64_t> newShape(vectorRank, 1);
333 newShape[i] = vectorShape[i];
334 auto newType = VectorType::get(newShape, rewriter.getIndexType());
335 auto castOp = vector::ShapeCastOp::create(rewriter, loc, newType,
336 strideMultiplied[i]);
337 shapeCasted.push_back(castOp);
338 }
339
340 // Broadcast each shape-casted vector to full vector shape
341 SmallVector<Value> broadcasted;
342 auto fullIndexVectorType =
343 VectorType::get(vectorShape, rewriter.getIndexType());
344 for (Value shapeCastVal : shapeCasted) {
345 auto broadcastOp = vector::BroadcastOp::create(
346 rewriter, loc, fullIndexVectorType, shapeCastVal);
347 broadcasted.push_back(broadcastOp);
348 }
349
350 // Add all broadcasted vectors together to compute local offsets
351 Value localOffsets = broadcasted[0];
352 for (size_t i = 1; i < broadcasted.size(); ++i)
353 localOffsets =
354 arith::AddIOp::create(rewriter, loc, localOffsets, broadcasted[i]);
355
356 // Compute base offset from transfer read indices
357 for (size_t i = 0; i < indices.size(); ++i) {
358 Value strideVal = strides[i];
359 Value offsetContrib =
360 arith::MulIOp::create(rewriter, loc, indices[i], strideVal);
361 baseOffset =
362 arith::AddIOp::create(rewriter, loc, baseOffset, offsetContrib);
363 }
364 // Broadcast base offset to match vector shape
365 Value bcastBase = vector::BroadcastOp::create(
366 rewriter, loc, fullIndexVectorType, baseOffset);
367 localOffsets = arith::AddIOp::create(rewriter, loc, bcastBase, localOffsets);
368 return localOffsets;
369}
370
371// Compute the element-wise offsets for vector.gather or vector.scatter ops.
372//
373// This function linearizes the base offsets of the gather/scatter operation
374// and combines them with the per-element indices to produce a final vector of
375// memory offsets.
376template <
377 typename OpType,
378 typename = std::enable_if_t<llvm::is_one_of<
379 std::decay_t<OpType>, vector::GatherOp, vector::ScatterOp>::value>>
380static Value computeOffsets(PatternRewriter &rewriter, OpType gatScatOp,
381 ArrayRef<Value> strides, Value baseOffset) {
382 Location loc = gatScatOp.getLoc();
383 SmallVector<Value> offsets = gatScatOp.getOffsets();
384 for (size_t i = 0; i < offsets.size(); ++i) {
385 Value offsetContrib =
386 arith::MulIOp::create(rewriter, loc, offsets[i], strides[i]);
387 baseOffset =
388 arith::AddIOp::create(rewriter, loc, baseOffset, offsetContrib);
389 }
390 Value indices = gatScatOp.getIndices();
391 VectorType vecType = cast<VectorType>(indices.getType());
392
393 Value strideVector =
394 vector::BroadcastOp::create(rewriter, loc, vecType, strides.back())
395 .getResult();
396 Value stridedIndices =
397 arith::MulIOp::create(rewriter, loc, strideVector, indices).getResult();
398
399 Value baseVector =
400 vector::BroadcastOp::create(
401 rewriter, loc,
402 VectorType::get(vecType.getShape(), rewriter.getIndexType()),
403 baseOffset)
404 .getResult();
405 return arith::AddIOp::create(rewriter, loc, baseVector, stridedIndices)
406 .getResult();
407}
408
409// Collapses shapes of a nD memref to the target rank while applying offsets for
410// the collapsed dimensions. Returns the new memref value and the remaining
411// offsets for the last targetRank dimensions. For example:
412// input: %memref = memref<2x4x8x32xf32>, offsets=[%i0, %i1, %i2, %i3],
413// output: %memref[%i0, %i1, 0, 0] -> memref<8x32xf32>, offsets: [%i2, %i3]
414static std::pair<Value, SmallVector<OpFoldResult>>
415convertMemrefAndOffsetsToTargetRank(PatternRewriter &rewriter, Location loc,
418 int64_t targetRank) {
419 auto memrefType = cast<MemRefType>(memref.getType());
420 unsigned rank = memrefType.getRank();
421
422 if (rank <= targetRank)
423 return {memref, offsets};
424
425 int64_t numCombinedDims = rank - targetRank;
426 SmallVector<OpFoldResult> subviewOffsets;
427 SmallVector<OpFoldResult> subviewSizes;
428 SmallVector<OpFoldResult> subviewStrides;
429
430 // For the combined dimensions: use the provided offsets, size=1, stride=1
431 for (unsigned i = 0; i < numCombinedDims; ++i) {
432 subviewOffsets.push_back(offsets[i]);
433 subviewSizes.push_back(rewriter.getI64IntegerAttr(1));
434 subviewStrides.push_back(rewriter.getI64IntegerAttr(1));
435 }
436
437 // For the last targetRank dimensions: offset=0, use full size, stride=1
438 SmallVector<int64_t> resultShape;
439 auto originalShape = memrefType.getShape();
440 auto meta = memref::ExtractStridedMetadataOp::create(rewriter, loc, memref);
441 for (unsigned i = numCombinedDims; i < rank; ++i) {
442 subviewOffsets.push_back(rewriter.getI64IntegerAttr(0));
443 if (ShapedType::isDynamic(originalShape[i])) {
444 subviewSizes.push_back(meta.getSizes()[i]);
445 resultShape.push_back(ShapedType::kDynamic);
446 } else {
447 subviewSizes.push_back(rewriter.getI64IntegerAttr(originalShape[i]));
448 resultShape.push_back(originalShape[i]);
449 }
450 subviewStrides.push_back(rewriter.getI64IntegerAttr(1));
451 }
452
453 auto resultType = memref::SubViewOp::inferRankReducedResultType(
454 resultShape, memrefType, subviewOffsets, subviewSizes, subviewStrides);
455 auto subviewOp =
456 memref::SubViewOp::create(rewriter, loc, resultType, memref,
457 subviewOffsets, subviewSizes, subviewStrides);
458
459 // Return the remaining offsets for the last targetRank dimensions
460 SmallVector<OpFoldResult> newOffsets(offsets.begin() + numCombinedDims,
461 offsets.end());
462 return {subviewOp.getResult(), newOffsets};
463}
464
465template <
466 typename OpType,
467 typename = std::enable_if_t<llvm::is_one_of<
468 std::decay_t<OpType>, vector::TransferReadOp, vector::TransferWriteOp,
469 vector::GatherOp, vector::ScatterOp>::value>>
470// Convert memref to i64 base pointer
471static Value memrefToIndexPtr(OpType xferOp, PatternRewriter &rewriter) {
472 Location loc = xferOp.getLoc();
473 auto indexPtr = memref::ExtractAlignedPointerAsIndexOp::create(
474 rewriter, loc, xferOp.getBase())
475 .getResult();
476 return arith::IndexCastOp::create(rewriter, loc, rewriter.getI64Type(),
477 indexPtr)
478 .getResult();
479}
480
481static LogicalResult lowerToScatteredLoadOp(vector::TransferReadOp readOp,
482 PatternRewriter &rewriter) {
483
484 Location loc = readOp.getLoc();
485 VectorType vectorType = readOp.getVectorType();
486 ArrayRef<int64_t> vectorShape = vectorType.getShape();
487 auto memrefType = dyn_cast<MemRefType>(readOp.getShapedType());
488 if (!memrefType)
489 return rewriter.notifyMatchFailure(readOp, "Expected memref source");
490
491 auto meta = computeMemrefMeta(readOp, rewriter);
492 if (meta.first.empty())
493 return rewriter.notifyMatchFailure(readOp, "Failed to compute strides");
494
495 Value localOffsets =
496 computeOffsets(readOp, rewriter, meta.first, meta.second);
497
498 Value flatMemref = memrefToIndexPtr(readOp, rewriter);
499
500 Value mask = vector::ConstantMaskOp::create(
501 rewriter, loc, VectorType::get(vectorShape, rewriter.getI1Type()),
503 auto gatherOp = xegpu::LoadGatherOp::create(
504 rewriter, loc, vectorType, flatMemref, localOffsets, mask,
505 /*chunk_size=*/IntegerAttr{},
506 /*l1_hint=*/xegpu::CachePolicyAttr{},
507 /*l2_hint=*/xegpu::CachePolicyAttr{},
508 /*l3_hint=*/xegpu::CachePolicyAttr{},
509 /*layout=*/nullptr, /*contiguity=*/nullptr);
510
511 rewriter.replaceOp(readOp, gatherOp.getResult());
512 return success();
513}
514
515static LogicalResult lowerToScatteredStoreOp(vector::TransferWriteOp writeOp,
516 PatternRewriter &rewriter) {
517
518 Location loc = writeOp.getLoc();
519 VectorType vectorType = writeOp.getVectorType();
520 ArrayRef<int64_t> vectorShape = vectorType.getShape();
521
522 auto memrefType = dyn_cast<MemRefType>(writeOp.getShapedType());
523 if (!memrefType)
524 return rewriter.notifyMatchFailure(writeOp, "Expected memref source");
525
526 auto meta = computeMemrefMeta(writeOp, rewriter);
527 if (meta.first.empty())
528 return rewriter.notifyMatchFailure(writeOp, "Failed to compute strides");
529
530 Value localOffsets =
531 computeOffsets(writeOp, rewriter, meta.first, meta.second);
532
533 Value flatMemref = memrefToIndexPtr(writeOp, rewriter);
534
535 Value mask = vector::ConstantMaskOp::create(
536 rewriter, loc, VectorType::get(vectorShape, rewriter.getI1Type()),
538 xegpu::StoreScatterOp::create(rewriter, loc, writeOp.getVector(), flatMemref,
539 localOffsets, mask,
540 /*chunk_size=*/IntegerAttr{},
541 /*l1_hint=*/xegpu::CachePolicyAttr{},
542 /*l2_hint=*/xegpu::CachePolicyAttr{},
543 /*l3_hint=*/xegpu::CachePolicyAttr{},
544 /*layout=*/nullptr, /*contiguity=*/nullptr);
545 rewriter.eraseOp(writeOp);
546 return success();
547}
548
549struct TransferReadLowering : public OpRewritePattern<vector::TransferReadOp> {
550 using Base::Base;
551
552 LogicalResult matchAndRewrite(vector::TransferReadOp readOp,
553 PatternRewriter &rewriter) const override {
554 Location loc = readOp.getLoc();
555
556 if (failed(transferPreconditions(rewriter, readOp)))
557 return failure();
558 auto readMemTy = cast<MemRefType>(readOp.getShapedType());
559 VectorType loadedVecTy = readOp.getVectorType();
560 bool isOutOfBounds = readOp.hasOutOfBoundsDim();
561 // Check if the memref has address space 3 (shared local memory)
562 bool isSharedMemory = xegpu::XeGPUDialect::isSharedMemory(readMemTy);
563 // Handle the SLM case.
564 if (isSharedMemory) {
565 // load_matrix supports 1D and 2D loads from SLM.
566 if (loadedVecTy.getRank() != 1 && loadedVecTy.getRank() != 2)
567 return rewriter.notifyMatchFailure(
568 readOp, "Only 1D and 2D vector loads are supported for SLM");
569 AffineMap readMap = readOp.getPermutationMap();
570 if (!readMap.isMinorIdentity())
571 return rewriter.notifyMatchFailure(
572 readOp,
573 "Non identity transposition is not supported for SLM loads.");
574 // Out of bounds case is not supported for SLM loads.
575 if (isOutOfBounds)
576 return rewriter.notifyMatchFailure(
577 readOp, "Out-of-bounds access is not supported for SLM loads");
578
579 // Create mem_desc for SLM
580 auto memDescType =
581 xegpu::MemDescType::get(rewriter.getContext(), readMemTy.getShape(),
582 readMemTy.getElementType(),
583 /*mem_layout=*/nullptr);
584 auto createMemDescOp = xegpu::CreateMemDescOp::create(
585 rewriter, loc, memDescType, readOp.getBase());
586 // Convert indices to OpFoldResult for LoadMatrixOp
587 SmallVector<OpFoldResult> indices =
588 getAsOpFoldResult(readOp.getIndices());
589 auto loadMatrixOp = xegpu::LoadMatrixOp::create(
590 rewriter, loc, loadedVecTy, createMemDescOp.getResult(), indices,
591 /*layout=*/nullptr);
592
593 rewriter.replaceOp(readOp, loadMatrixOp.getResult());
594 return success();
595 }
596
597 // TODO:This check needs to be replaced with proper uArch capability check
598 auto chip = xegpu::getChipStr(readOp);
599 // Lower to scattered load Op if the target HW doesn't have 2d block load
600 // support and the load is not from shared memory.
601 if ((chip != "pvc" && chip != "bmg" && chip != "cri") ||
602 readOp.getVectorType().getRank() > 2) {
603
604 // TODO: add support for OutOfBound access
605 if (isOutOfBounds)
606 return failure();
607 return lowerToScatteredLoadOp(readOp, rewriter);
608 }
609
610 // Handle the 1D non-SLM case using load.gather.
611 if (loadedVecTy.getRank() == 1 && !isOutOfBounds)
612 return lowerToScatteredLoadOp(readOp, rewriter);
613
614 // Perform common data transfer checks.
615 // TODO: Maybe too strict for SLM case.
616 if (failed(
617 storeLoadPreconditions(rewriter, readOp, loadedVecTy, readMemTy)))
618 return failure();
619
620 if (isOutOfBounds && !isZeroConstant(readOp.getPadding()))
621 return rewriter.notifyMatchFailure(
622 readOp, "Unsupported non-zero padded out-of-bounds read");
623
624 AffineMap readMap = readOp.getPermutationMap();
625 // Check if this is a transpose: the map must have exactly 2 results,
626 // and those 2 results must be the last 2 input dimensions interchanged.
627 // Examples:
628 // (d0, d1) -> (d1, d0) // transpose
629 // (d0, d1) -> (d0, d1) // not a transpose
630 // (d0, d1, d2) -> (d2, d1) // transpose (last 2 dims swapped)
631 bool isTransposeLoad = false;
632 if (readMap.getNumResults() == 2) {
633 auto results = readMap.getResults();
634 unsigned numInputs = readMap.getNumInputs();
635 if (numInputs >= 2) {
636 auto lastDim = getAffineDimExpr(numInputs - 1, readMap.getContext());
637 auto secondLastDim =
638 getAffineDimExpr(numInputs - 2, readMap.getContext());
639 isTransposeLoad =
640 (results[0] == lastDim && results[1] == secondLastDim);
641 }
642 }
643 auto elementType = loadedVecTy.getElementType();
644
645 SmallVector<int64_t> descShape(loadedVecTy.getShape());
646 if (isTransposeLoad) {
647 // If load is transposed, simply swap the last two dimensions of the
648 // loaded vector type to get the descriptor shape.
649 size_t rank = descShape.size();
650 assert(rank >= 2 && "Transpose requires at least 2 dimensions");
651 std::swap(descShape[rank - 1], descShape[rank - 2]);
652 loadedVecTy = VectorType::get(descShape, elementType);
653 }
654 auto descType = xegpu::TensorDescType::get(
655 descShape, elementType, /*array_length=*/1,
656 /*boundary_check=*/isOutOfBounds, xegpu::MemorySpace::Global);
657 auto [src, indices] = convertMemrefAndOffsetsToTargetRank(
658 rewriter, loc, readOp.getBase(), getAsOpFoldResult(readOp.getIndices()),
659 loadedVecTy.getRank());
660 // By default, no specific caching policy is assigned.
661 xegpu::CachePolicyAttr hint = nullptr;
662 xegpu::CreateNdDescOp ndDesc = createNdDescriptor(
663 rewriter, loc, descType, dyn_cast<TypedValue<MemRefType>>(src));
664
665 Operation *loadedOp =
666 xegpu::LoadNdOp::create(rewriter, loc, loadedVecTy, ndDesc, indices,
667 /*packed=*/nullptr, /*transpose=*/nullptr,
668 /*l1_hint=*/hint,
669 /*l2_hint=*/hint, /*l3_hint=*/hint,
670 /*layout=*/nullptr);
671 if (isTransposeLoad) {
672 // Transposing the loaded vector with a separate vector.transpose
673 // operation
674 auto range = llvm::seq<int64_t>(0, readMap.getResults().size());
675 SmallVector<int64_t> perm(
676 range.rbegin(), range.rend()); // reverse the range for transpose
677 loadedOp = vector::TransposeOp::create(rewriter, loc,
678 loadedOp->getResult(0), perm);
679 }
680 rewriter.replaceOp(readOp, loadedOp);
681
682 return success();
683 }
684};
685
686struct TransferWriteLowering
687 : public OpRewritePattern<vector::TransferWriteOp> {
688 using Base::Base;
689
690 LogicalResult matchAndRewrite(vector::TransferWriteOp writeOp,
691 PatternRewriter &rewriter) const override {
692 Location loc = writeOp.getLoc();
693
694 if (failed(transferPreconditions(rewriter, writeOp)))
695 return failure();
696 // Perform common data transfer checks.
697 VectorType vecTy = writeOp.getVectorType();
698 auto writeMemTy = cast<MemRefType>(writeOp.getShapedType());
699 // Check if the memref has address space 3 (shared local memory)
700 bool isSharedMemory = xegpu::XeGPUDialect::isSharedMemory(writeMemTy);
701
702 // For shared local memory (address space 3), use create_mem_desc +
703 // store_matrix
704 if (isSharedMemory) {
705 // store_matrix supports 1D and 2D stores to SLM.
706 if (vecTy.getRank() != 1 && vecTy.getRank() != 2)
707 return rewriter.notifyMatchFailure(
708 writeOp, "Only 1D and 2D vector stores are supported for SLM");
709 // Create mem_desc for SLM
710 auto memDescType =
711 xegpu::MemDescType::get(rewriter.getContext(), writeMemTy.getShape(),
712 writeMemTy.getElementType(),
713 /*mem_layout=*/nullptr);
714
715 auto createMemDescOp = xegpu::CreateMemDescOp::create(
716 rewriter, loc, memDescType, writeOp.getBase());
717
718 // Convert indices to OpFoldResult for StoreMatrixOp
719 SmallVector<OpFoldResult> indices =
720 getAsOpFoldResult(writeOp.getIndices());
721
722 xegpu::StoreMatrixOp::create(rewriter, loc, writeOp.getVector(),
723 createMemDescOp.getResult(), indices,
724 /*layout=*/nullptr);
725
726 rewriter.eraseOp(writeOp);
727 return success();
728 }
729
730 // TODO:This check needs to be replaced with proper uArch capability check
731 auto chip = xegpu::getChipStr(writeOp);
732 // Lower to scattered store Op if the target HW doesn't have 2d block
733 // store support and the memref is not SLM.
734 if ((chip != "pvc" && chip != "bmg" && chip != "cri") ||
735 writeOp.getVectorType().getRank() > 2) {
736
737 // TODO: add support for OutOfBound access
738 if (writeOp.hasOutOfBoundsDim())
739 return failure();
740 return lowerToScatteredStoreOp(writeOp, rewriter);
741 }
742
743 if (failed(storeLoadPreconditions(rewriter, writeOp, vecTy, writeMemTy)))
744 return failure();
745
746 AffineMap map = writeOp.getPermutationMap();
747 if (!map.isMinorIdentity())
748 return rewriter.notifyMatchFailure(writeOp, "Expects identity map");
749
750 auto [src, indices] = convertMemrefAndOffsetsToTargetRank(
751 rewriter, loc, writeOp.getBase(),
752 getAsOpFoldResult(writeOp.getIndices()), vecTy.getRank());
753
754 auto descType = xegpu::TensorDescType::get(
755 vecTy.getShape(), vecTy.getElementType(),
756 /*array_length=*/1, /*boundary_check=*/writeOp.hasOutOfBoundsDim(),
757 xegpu::MemorySpace::Global);
758 // By default, no specific caching policy is assigned.
759 xegpu::CachePolicyAttr hint = nullptr;
760 xegpu::CreateNdDescOp ndDesc = createNdDescriptor(
761 rewriter, loc, descType, dyn_cast<TypedValue<MemRefType>>(src));
762
763 auto storeOp = xegpu::StoreNdOp::create(rewriter, loc, writeOp.getVector(),
764 ndDesc, indices,
765 /*l1_hint=*/hint,
766 /*l2_hint=*/hint, /*l3_hint=*/hint,
767 /*layout=*/nullptr);
768 rewriter.replaceOp(writeOp, storeOp);
769
770 return success();
771 }
772};
773
774struct GatherLowering : public OpRewritePattern<vector::GatherOp> {
775 using Base::Base;
776
777 LogicalResult matchAndRewrite(vector::GatherOp gatherOp,
778 PatternRewriter &rewriter) const override {
779 auto srcTy = dyn_cast<MemRefType>(gatherOp.getBase().getType());
780 if (!srcTy)
781 return rewriter.notifyMatchFailure(gatherOp, "Expects memref source");
782
783 Location loc = gatherOp.getLoc();
784 VectorType vectorType = gatherOp.getVectorType();
785
786 auto meta = computeMemrefMeta(gatherOp, rewriter);
787 if (meta.first.empty())
788 return rewriter.notifyMatchFailure(gatherOp, "Failed to compute strides");
789
790 Value localOffsets =
791 computeOffsets(rewriter, gatherOp, meta.first, meta.second);
792 Value flatMemref = memrefToIndexPtr(gatherOp, rewriter);
793
794 auto xeGatherOp = xegpu::LoadGatherOp::create(
795 rewriter, loc, vectorType, flatMemref, localOffsets, gatherOp.getMask(),
796 /*chunk_size=*/IntegerAttr{},
797 /*l1_hint=*/xegpu::CachePolicyAttr{},
798 /*l2_hint=*/xegpu::CachePolicyAttr{},
799 /*l3_hint=*/xegpu::CachePolicyAttr{},
800 /*layout=*/nullptr, /*contiguity=*/nullptr);
801
802 auto selectOp =
803 arith::SelectOp::create(rewriter, loc, gatherOp.getMask(),
804 xeGatherOp.getResult(), gatherOp.getPassThru());
805 rewriter.replaceOp(gatherOp, selectOp.getResult());
806 return success();
807 }
808};
809
810struct ScatterLowering : public OpRewritePattern<vector::ScatterOp> {
811 using Base::Base;
812
813 LogicalResult matchAndRewrite(vector::ScatterOp scatterOp,
814 PatternRewriter &rewriter) const override {
815 auto srcTy = dyn_cast<MemRefType>(scatterOp.getBase().getType());
816 if (!srcTy)
817 return rewriter.notifyMatchFailure(scatterOp, "Expects memref source");
818
819 Location loc = scatterOp.getLoc();
820 auto meta = computeMemrefMeta(scatterOp, rewriter);
821 if (meta.first.empty())
822 return rewriter.notifyMatchFailure(scatterOp,
823 "Failed to compute strides");
824
825 Value localOffsets =
826 computeOffsets(rewriter, scatterOp, meta.first, meta.second);
827 Value flatMemref = memrefToIndexPtr(scatterOp, rewriter);
828
829 xegpu::StoreScatterOp::create(rewriter, loc, scatterOp.getValueToStore(),
830 flatMemref, localOffsets, scatterOp.getMask(),
831 /*chunk_size=*/IntegerAttr{},
832 /*l1_hint=*/xegpu::CachePolicyAttr{},
833 /*l2_hint=*/xegpu::CachePolicyAttr{},
834 /*l3_hint=*/xegpu::CachePolicyAttr{},
835 /*layout=*/nullptr,
836 /*contiguity=*/nullptr);
837 rewriter.eraseOp(scatterOp);
838 return success();
839 }
840};
841
842struct LoadLowering : public OpRewritePattern<vector::LoadOp> {
843 using Base::Base;
844
845 LogicalResult matchAndRewrite(vector::LoadOp loadOp,
846 PatternRewriter &rewriter) const override {
847 Location loc = loadOp.getLoc();
848
849 VectorType vecTy = loadOp.getResult().getType();
850 MemRefType memTy = loadOp.getBase().getType();
851 if (failed(storeLoadPreconditions(rewriter, loadOp, vecTy, memTy)))
852 return failure();
853
854 // Boundary check is available only for block instructions.
855 bool boundaryCheck = vecTy.getRank() > 1;
856 // By default, no specific caching policy is assigned.
857 xegpu::CachePolicyAttr hint = nullptr;
858
859 auto [src, indices] = convertMemrefAndOffsetsToTargetRank(
860 rewriter, loc, loadOp.getBase(), getAsOpFoldResult(loadOp.getIndices()),
861 vecTy.getRank());
862
863 auto descType = xegpu::TensorDescType::get(
864 vecTy.getShape(), vecTy.getElementType(), /*array_length=*/1,
865 boundaryCheck, xegpu::MemorySpace::Global);
866
867 xegpu::CreateNdDescOp ndDesc = createNdDescriptor(
868 rewriter, loc, descType, dyn_cast<TypedValue<MemRefType>>(src));
869 auto loadNdOp =
870 xegpu::LoadNdOp::create(rewriter, loc, vecTy, ndDesc, indices,
871 /*packed=*/nullptr, /*transpose=*/nullptr,
872 /*l1_hint=*/hint,
873 /*l2_hint=*/hint, /*l3_hint=*/hint,
874 /*layout=*/nullptr);
875 rewriter.replaceOp(loadOp, loadNdOp);
876
877 return success();
878 }
879};
880
881struct StoreLowering : public OpRewritePattern<vector::StoreOp> {
882 using Base::Base;
883
884 LogicalResult matchAndRewrite(vector::StoreOp storeOp,
885 PatternRewriter &rewriter) const override {
886 Location loc = storeOp.getLoc();
887
888 TypedValue<VectorType> vector = storeOp.getValueToStore();
889 VectorType vecTy = vector.getType();
890 MemRefType memTy = storeOp.getBase().getType();
891 if (failed(storeLoadPreconditions(rewriter, storeOp, vecTy, memTy)))
892 return failure();
893
894 // Boundary check is available only for block instructions.
895 bool boundaryCheck = vecTy.getRank() > 1;
896
897 auto [src, indices] = convertMemrefAndOffsetsToTargetRank(
898 rewriter, loc, storeOp.getBase(),
899 getAsOpFoldResult(storeOp.getIndices()), vecTy.getRank());
900
901 auto descType = xegpu::TensorDescType::get(
902 vecTy.getShape(), vecTy.getElementType(),
903 /*array_length=*/1, boundaryCheck, xegpu::MemorySpace::Global);
904
905 // By default, no specific caching policy is assigned.
906 xegpu::CachePolicyAttr hint = nullptr;
907 xegpu::CreateNdDescOp ndDesc = createNdDescriptor(
908 rewriter, loc, descType, dyn_cast<TypedValue<MemRefType>>(src));
909
910 auto storeNdOp =
911 xegpu::StoreNdOp::create(rewriter, loc, vector, ndDesc, indices,
912 /*l1_hint=*/hint,
913 /*l2_hint=*/hint, /*l3_hint=*/hint,
914 /*layout=*/nullptr);
915
916 rewriter.replaceOp(storeOp, storeNdOp);
917
918 return success();
919 }
920};
921
922struct ContractionLowering : public OpRewritePattern<vector::ContractionOp> {
923 using Base::Base;
924
925 LogicalResult matchAndRewrite(vector::ContractionOp contractOp,
926 PatternRewriter &rewriter) const override {
927 Location loc = contractOp.getLoc();
928
929 if (contractOp.getKind() != vector::CombiningKind::ADD)
930 return rewriter.notifyMatchFailure(contractOp,
931 "Expects add combining kind");
932
933 TypedValue<Type> acc = contractOp.getAcc();
934 VectorType accType = dyn_cast<VectorType>(acc.getType());
935 if (!accType || accType.getRank() != 2)
936 return rewriter.notifyMatchFailure(contractOp, "Expects acc 2D vector");
937
938 // Accept only plain 2D data layout.
939 // VNNI packing is applied to DPAS as a separate lowering step.
940 TypedValue<VectorType> lhs = contractOp.getLhs();
941 TypedValue<VectorType> rhs = contractOp.getRhs();
942 if (lhs.getType().getRank() != 2 || rhs.getType().getRank() != 2)
943 return rewriter.notifyMatchFailure(contractOp,
944 "Expects lhs and rhs 2D vectors");
945
946 if (!isRowMajorMatmul(contractOp.getIndexingMapsAttr()))
947 return rewriter.notifyMatchFailure(contractOp, "Invalid indexing maps");
948
949 auto dpasOp = xegpu::DpasOp::create(rewriter, loc,
950 TypeRange{contractOp.getResultType()},
951 ValueRange{lhs, rhs, acc});
952 rewriter.replaceOp(contractOp, dpasOp);
953
954 return success();
955 }
956};
957
958// Returns `memrefTy` with its memory space replaced by `newMemSpace`.
959static MemRefType withMemorySpace(MemRefType memrefTy, Attribute newMemSpace) {
960 return MemRefType::get(memrefTy.getShape(), memrefTy.getElementType(),
961 memrefTy.getLayout(), newMemSpace);
962}
963
964// Rewrite every `memref.alloca` not already in shared local memory (SLM) to
965// be in SLM (address space 3), and propagate the new memory space through
966// memref-producing aliasing users (e.g. memref.cast, memref.subview,
967// memref.expand_shape, ...). Consumers that take a memref operand but
968// produce a non-memref result (e.g. vector.transfer_read, vector.load) are
969// left untouched: their operand type simply reflects the new memory space.
970//
971// This makes `xegpu.load_matrix`/`xegpu.store_matrix` lowering work end-to-end
972// for IR coming from bufferization, which by default assigns memory space 0/1
973// to allocations.
974static void promoteAllocasToSLM(Operation *root) {
975 MLIRContext *ctx = root->getContext();
976 Attribute slmAttr = IntegerAttr::get(IntegerType::get(ctx, 64), 3);
977
978 // A user is treated as a memref-producing alias (e.g. memref.cast,
979 // memref.subview, memref.expand_shape, ...) if it is side-effect free and
980 // produces at least one memref result. This excludes ops like memref.copy
981 // that have memory effects.
982 auto isMemrefResultOp = [](Operation *op) {
983 if (!isMemoryEffectFree(op))
984 return false;
985 return llvm::any_of(op->getResultTypes(),
986 [](Type t) { return isa<MemRefType>(t); });
987 };
988
989 // Update `v`'s type to have SLM memory space, then walk forward through
990 // memref-producing users and update their result types accordingly.
991 std::function<void(Value)> propagate = [&](Value v) {
992 auto memrefTy = dyn_cast<MemRefType>(v.getType());
993 if (!memrefTy || xegpu::XeGPUDialect::isSharedMemory(memrefTy))
994 return;
995 v.setType(withMemorySpace(memrefTy, slmAttr));
996 for (Operation *user : v.getUsers()) {
997 if (!isMemrefResultOp(user))
998 continue;
999 for (Value result : user->getResults())
1000 propagate(result);
1001 }
1002 };
1003
1005 root->walk([&](memref::AllocaOp op) {
1006 auto memrefTy = dyn_cast<MemRefType>(op.getResult().getType());
1007 if (!memrefTy || xegpu::XeGPUDialect::isSharedMemory(memrefTy))
1008 return;
1009 allocas.push_back(op);
1010 });
1011
1012 for (memref::AllocaOp alloca : allocas) {
1013 OpBuilder builder(alloca);
1014 auto memrefTy = cast<MemRefType>(alloca.getResult().getType());
1015 auto newTy = withMemorySpace(memrefTy, slmAttr);
1016 auto newOp = memref::AllocaOp::create(
1017 builder, alloca.getLoc(), newTy, alloca.getDynamicSizes(),
1018 alloca.getSymbolOperands(), alloca.getAlignmentAttr());
1019 alloca.getResult().replaceAllUsesWith(newOp.getResult());
1020 alloca.erase();
1021 // Propagate the new memory space through memref-producing consumers.
1022 for (Operation *user : newOp.getResult().getUsers()) {
1023 if (!isMemrefResultOp(user))
1024 continue;
1025 for (Value result : user->getResults())
1026 propagate(result);
1027 }
1028 }
1029}
1030
1031struct ConvertVectorToXeGPUPass
1032 : public impl::ConvertVectorToXeGPUBase<ConvertVectorToXeGPUPass> {
1033 void runOnOperation() override {
1034 // Promote local allocations to SLM (address space 3) so that
1035 // load_matrix/store_matrix lowerings have well-typed memref operands.
1036 promoteAllocasToSLM(getOperation());
1037
1038 RewritePatternSet patterns(&getContext());
1041 if (failed(applyPatternsGreedily(getOperation(), std::move(patterns))))
1042 return signalPassFailure();
1043 }
1044};
1045
1046} // namespace
1047
1049 RewritePatternSet &patterns) {
1050 patterns
1051 .add<TransferReadLowering, TransferWriteLowering, LoadLowering,
1052 ScatterLowering, GatherLowering, StoreLowering, ContractionLowering>(
1053 patterns.getContext());
1054}
return success()
lhs
b getContext())
static std::optional< VectorShape > vectorShape(Type type)
static bool isSharedMemory(MemRefType type)
Return true if this is a shared memory memref type.
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
bool isMinorIdentity() const
Returns true if this affine map is a minor identity, i.e.
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
ArrayRef< AffineExpr > getResults() const
bool isPermutationOfMinorIdentityWithBroadcasting(SmallVectorImpl< unsigned > &permutedDims) const
Return true if this affine map can be converted to a minor identity with broadcast by doing a permute...
unsigned getNumResults() const
unsigned getNumInputs() const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
Attributes are known-constant values of operations.
Definition Attributes.h:25
IntegerType getI64Type()
Definition Builders.cpp:69
IntegerAttr getI64IntegerAttr(int64_t value)
Definition Builders.cpp:116
IntegerType getI1Type()
Definition Builders.cpp:57
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:55
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class helps build Operations.
Definition Builders.h:209
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
result_type_range getResultTypes()
Definition Operation.h:453
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:822
user_range getUsers()
Returns a range of all users.
Definition Operation.h:898
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
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.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
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...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
user_range getUsers() const
Definition Value.h:218
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:384
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
std::optional< std::string > getChipStr(Operation *op)
Retrieves the chip string from the XeVM target attribute of the parent GPU module operation.
Include the generated interface declarations.
void populatePrepareVectorToMMAPatterns(RewritePatternSet &patterns, bool useNvGpu=false)
Patterns to transform vector ops into a canonical form to convert to MMA matrix operations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
AffineMap inverseAndBroadcastProjectedPermutation(AffineMap map)
Return the reverse map of a projected permutation where the projected dimensions are transformed into...
SmallVector< T > applyPermutation(ArrayRef< T > input, ArrayRef< int64_t > permutation)
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...
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
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.
Definition Value.h:494
void populateVectorToXeGPUConversionPatterns(RewritePatternSet &patterns)
Collect a set of patterns to convert from the vector to XeGPU ops.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:139
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
bool isRowMajorMatmul(ArrayAttr indexingMaps)
Tests whether the given maps describe a row major matmul.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...