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