MLIR 24.0.0git
XeGPUUnroll.cpp
Go to the documentation of this file.
1//===- XeGPUUnroll.cpp - patterns to do unrolling ---------------*- 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 contains patterns for unrolling XeGPU operations. It follows a
10// similar concept and design as vector unroll patterns, serving as a complement
11// to them.
12//
13//===----------------------------------------------------------------------===//
14
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/Support/DebugLog.h"
25
26namespace mlir {
27namespace xegpu {
28#define GEN_PASS_DEF_XEGPUUNROLL
29#include "mlir/Dialect/XeGPU/Transforms/Passes.h.inc"
30} // namespace xegpu
31} // namespace mlir
32
33#define DEBUG_TYPE "xegpu-unroll"
34
35using namespace mlir;
36
37namespace {
38
39// Forward declaration for use inside UnrollPattern below.
41unrollByTile(SmallVector<OpFoldResult> mixedOffsets,
42 xegpu::TensorDescType tdescTy, ArrayRef<int64_t> targetShape,
43 const std::function<Value(SmallVector<OpFoldResult>)> &createOp,
44 Location loc, PatternRewriter &rewriter);
45
46template <typename SourceOp>
47struct UnrollPattern : public OpRewritePattern<SourceOp> {
48 UnrollPattern(MLIRContext *context, const xegpu::UnrollOptions &options,
49 PatternBenefit benefit = 1)
50 : OpRewritePattern<SourceOp>(context, benefit), options(options) {}
51
52protected:
53 /// Return the target shape for the given `op`. Return std::nullopt if the
54 /// op shouldn't be or cannot be unrolled.
55 std::optional<SmallVector<int64_t>> getTargetShape(Operation *op) const {
56 LDBG() << "Get unroll shape for: " << *op;
57
58 if (options.filterConstraint && failed(options.filterConstraint(op))) {
59 LDBG() << "--no filter constraint -> BAIL";
60 return std::nullopt;
61 }
62
63 assert(options.nativeShape &&
64 "expects the native shape for native shape call back function.");
65 auto nativeShape = options.nativeShape(op);
66 return nativeShape;
67 }
68
69 SmallVector<Type> getUnrolledTypes(ShapedType type,
70 ArrayRef<int64_t> tileShape) const {
71 return options.getUnrolledTypes(type, tileShape);
72 }
73
74 /// Emulate the the unpack behavior using insert_strided_slice for VectorType
75 /// values and unrealized_conversion_cast for TensorDescType values.
76 Value unpack(ValueRange srcs, Type destTy, ArrayRef<int64_t> blockSize,
77 Location loc, PatternRewriter &rewriter) const {
78 if (auto vecTy = dyn_cast<VectorType>(destTy)) {
79 auto shape = vecTy.getShape();
80 return xegpu::createVectorWithShapeFromValues(rewriter, loc, srcs, shape);
81 }
82
83 if (isa<xegpu::TensorDescType>(destTy)) {
84 auto attr = NamedAttribute(rewriter.getStringAttr(unpackAttrName),
85 rewriter.getUnitAttr());
86 auto blkAttr = NamedAttribute(rewriter.getStringAttr(blockAttrName),
87 rewriter.getDenseI64ArrayAttr(blockSize));
88 auto castOp = UnrealizedConversionCastOp::create(
89 rewriter, loc, destTy, srcs,
90 ArrayRef<NamedAttribute>({attr, blkAttr}));
91 return castOp.getResult(0);
92 }
93
94 llvm_unreachable("Unexpected destTy.");
95 return Value();
96 }
97
98 /// Emulate the the pack behavior using extract_strided_slice for VectorType
99 /// values and unrealized_conversion_cast for TensorDescType values.
100 SmallVector<Value> pack(Value src, TypeRange destTypes,
101 ArrayRef<int64_t> blockSize, Location loc,
102 PatternRewriter &rewriter) const {
103 if (auto vecTy = dyn_cast<VectorType>(src.getType())) {
104 return xegpu::extractVectorsWithShapeFromValue(rewriter, loc, src,
105 blockSize);
106 }
107
108 if (isa<xegpu::TensorDescType>(src.getType())) {
109 auto attr = NamedAttribute(rewriter.getStringAttr(packAttrName),
110 rewriter.getUnitAttr());
111 auto blkAttr = NamedAttribute(rewriter.getStringAttr(blockAttrName),
112 rewriter.getDenseI64ArrayAttr(blockSize));
113 auto castOp = UnrealizedConversionCastOp::create(
114 rewriter, loc, destTypes, src,
115 ArrayRef<NamedAttribute>({attr, blkAttr}));
116 return castOp.getResults();
117 }
118
119 llvm_unreachable("Unexpected src type.");
120 return SmallVector<Value>();
121 }
122
123 /// Helper to pack operands for DPAS-like operations with early return if
124 /// no unrolling is needed.
125 SmallVector<Value> packOperandForDpas(Value operand,
126 ArrayRef<int64_t> blockSize,
127 Location loc,
128 PatternRewriter &rewriter) const {
129 auto vecType = cast<VectorType>(operand.getType());
130 std::optional<SmallVector<int64_t>> grids =
131 computeShapeRatio(vecType.getShape(), blockSize);
132 assert(grids && "Expecting grids to be computed.");
133 auto numNewOps = computeProduct(*grids);
134 if (numNewOps == 1)
135 return SmallVector<Value>({operand});
136 VectorType newVecTy =
137 vecType.cloneWith(blockSize, vecType.getElementType());
138 SmallVector<Type> convertedTypes(numNewOps, newVecTy);
139 return pack(operand, convertedTypes, blockSize, loc, rewriter);
140 }
141
142private:
143 const char *const packAttrName = "__xegpu_blocking_pack__";
144 const char *const unpackAttrName = "__xegpu_blocking_unpack__";
145 const char *const blockAttrName = "__xegpu_blocking_tile_shape__";
146
148};
149
150// Walks tile offsets within the tensor descriptor shape and emits one op per
151// tile by calling `createOp` with the per-tile offsets. Used by LoadNd,
152// StoreNd, CreateNdDesc, and PrefetchNd unrollers, which all need to adjust
153// their explicit offsets for each unrolled tile.
155unrollByTile(SmallVector<OpFoldResult> mixedOffsets,
156 xegpu::TensorDescType tdescTy, ArrayRef<int64_t> targetShape,
157 const std::function<Value(SmallVector<OpFoldResult>)> &createOp,
158 Location loc, PatternRewriter &rewriter) {
159 int64_t rank = tdescTy.getRank();
160 ArrayRef<int64_t> shape = tdescTy.getShape();
161
162 auto addi = [&](OpFoldResult a, int64_t b) -> Value {
163 std::optional<int64_t> maybeInt = getConstantIntValue(a);
164 if (maybeInt) {
165 return arith::ConstantIndexOp::create(rewriter, loc, *maybeInt + b);
166 } else {
167 auto aV = llvm::cast<Value>(a);
168 auto bV = arith::ConstantIndexOp::create(rewriter, loc, b);
169 return rewriter.createOrFold<arith::AddIOp>(loc, aV, bV);
170 }
171 };
172
173 SmallVector<OpFoldResult> oldOffsets = llvm::to_vector(
174 llvm::drop_begin(mixedOffsets, mixedOffsets.size() - rank));
175 auto validIdxes =
176 llvm::seq<int64_t>(mixedOffsets.size() - rank, mixedOffsets.size());
177
178 SmallVector<Value> newOps;
179 for (SmallVector<int64_t> offsets :
180 StaticTileOffsetRange(shape, targetShape)) {
181
182 for (auto [idx, oldOff, offset] :
183 llvm::zip(validIdxes, oldOffsets, offsets))
184 mixedOffsets[idx] = addi(oldOff, offset);
185
186 auto newOp = createOp(mixedOffsets);
187 newOps.push_back(newOp);
188 }
189 return newOps;
190}
191
192struct UnrollCreateNdOp : public UnrollPattern<xegpu::CreateNdDescOp> {
193 using UnrollPattern<xegpu::CreateNdDescOp>::UnrollPattern;
194 LogicalResult matchAndRewrite(xegpu::CreateNdDescOp op,
195 PatternRewriter &rewriter) const override {
196 Location loc = op.getLoc();
197 xegpu::TensorDescType tdescTy = op.getType();
198
199 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
200 if (!targetShape)
201 return failure();
202
203 // Keep the high-D source; only the tile shape shrinks.
204 Value src = op.getSource();
205 auto makeCreateNd = [&](Type tdesc) -> Value {
206 auto ndTy = cast<xegpu::TensorDescType>(tdesc);
207 if (isa<MemRefType>(src.getType()))
208 return xegpu::CreateNdDescOp::create(rewriter, loc, ndTy,
209 cast<TypedValue<MemRefType>>(src));
210 return xegpu::CreateNdDescOp::create(
211 rewriter, loc, ndTy, src, op.getMixedSizes(), op.getMixedStrides());
212 };
213
214 SmallVector<Type> newTdescTys = getUnrolledTypes(tdescTy, *targetShape);
215 SmallVector<Value> newOps;
216 if (tdescTy.getRank() <= 2) {
217 // 2D: one tdesc, broadcast across tiles by pack/unpack.
218 newOps.push_back(makeCreateNd(newTdescTys[0]));
219 } else {
220 // >2D: one tdesc per tile, so the source count matches the pack count.
221 for (Type t : newTdescTys)
222 newOps.push_back(makeCreateNd(t));
223 }
224 Value castOp = unpack(newOps, tdescTy, *targetShape, loc, rewriter);
225 rewriter.replaceOp(op, castOp);
226 return success();
227 }
228};
229
230struct UnrollPrefetchNdOp : public UnrollPattern<xegpu::PrefetchNdOp> {
231 using UnrollPattern<xegpu::PrefetchNdOp>::UnrollPattern;
232 LogicalResult matchAndRewrite(xegpu::PrefetchNdOp op,
233 PatternRewriter &rewriter) const override {
234 Location loc = op.getLoc();
235 xegpu::TensorDescType tdescTy = op.getTensorDescType();
236
237 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
238 if (!targetShape)
239 return failure();
240
241 xegpu::DistributeLayoutAttr layout = op.getLayoutAttr();
242 if (layout)
243 layout = layout.dropInstData();
244
245 // Batch (leading) dims unroll to unit tiles; one tdesc serves all.
246 SmallVector<Type> convertedTdescTypes =
247 getUnrolledTypes(tdescTy, *targetShape);
248 SmallVector<Value> convertedTdesc = pack(
249 op.getTensorDesc(), convertedTdescTypes, *targetShape, loc, rewriter);
250
251 auto createPrefetch = [&](SmallVector<OpFoldResult> offsets) -> Value {
252 xegpu::PrefetchNdOp::create(rewriter, loc, convertedTdesc[0], offsets,
253 op.getL1HintAttr(), op.getL2HintAttr(),
254 op.getL3HintAttr(), layout);
255 return nullptr;
256 };
257 unrollByTile(op.getMixedOffsets(), tdescTy, *targetShape, createPrefetch,
258 loc, rewriter);
259
260 rewriter.eraseOp(op);
261 return success();
262 }
263};
264
265struct UnrollLoadNdOp : public UnrollPattern<xegpu::LoadNdOp> {
266 using UnrollPattern<xegpu::LoadNdOp>::UnrollPattern;
267 LogicalResult matchAndRewrite(xegpu::LoadNdOp op,
268 PatternRewriter &rewriter) const override {
269
270 Location loc = op.getLoc();
271 VectorType valueTy = op.getType();
272 xegpu::TensorDescType tdescTy = op.getTensorDescType();
273
274 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
275 if (!targetShape)
276 return failure();
277
278 xegpu::DistributeLayoutAttr layout = op.getLayoutAttr();
279 if (layout)
280 layout = layout.dropInstData();
281
282 Type elemTy = tdescTy.getElementType();
283 VectorType newValueTy = valueTy.cloneWith(*targetShape, elemTy);
284
285 SmallVector<Value> newOps;
286
287 // Batch (leading) dims unroll to unit tiles; one tdesc serves all.
288 SmallVector<Type> convertedTdescTypes =
289 getUnrolledTypes(tdescTy, *targetShape);
290 SmallVector<Value> convertedTdescs = pack(
291 op.getTensorDesc(), convertedTdescTypes, *targetShape, loc, rewriter);
292
293 auto createLoad = [&](SmallVector<OpFoldResult> offsets) -> Value {
294 return xegpu::LoadNdOp::create(
295 rewriter, loc, newValueTy, convertedTdescs[0], offsets,
296 op.getPackedAttr(), op.getTransposeAttr(), op.getL1HintAttr(),
297 op.getL2HintAttr(), op.getL3HintAttr(), layout);
298 };
299 newOps = unrollByTile(op.getMixedOffsets(), tdescTy, *targetShape,
300 createLoad, loc, rewriter);
301
302 Value castOp = unpack(newOps, op.getType(), *targetShape, loc, rewriter);
303 rewriter.replaceOp(op, castOp);
304 return success();
305 }
306};
307
308struct UnrollStoreNdOp : public UnrollPattern<xegpu::StoreNdOp> {
309 using UnrollPattern<xegpu::StoreNdOp>::UnrollPattern;
310 LogicalResult matchAndRewrite(xegpu::StoreNdOp op,
311 PatternRewriter &rewriter) const override {
312 Location loc = op.getLoc();
313 VectorType valueTy = op.getValueType();
314 xegpu::TensorDescType tdescTy = op.getTensorDescType();
315
316 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
317 if (!targetShape)
318 return failure();
319
320 xegpu::DistributeLayoutAttr layout = op.getLayoutAttr();
321 if (layout)
322 layout = layout.dropInstData();
323
324 SmallVector<Type> convertedValTypes =
325 getUnrolledTypes(valueTy, *targetShape);
326
327 SmallVector<Value> convertedValues =
328 pack(op.getValue(), convertedValTypes, *targetShape, loc, rewriter);
329
330 size_t valueIndex = 0;
331
332 // Batch (leading) dims unroll to unit tiles like any other dim. valueIndex
333 // advances in unrollByTile's tile order, staying in sync with the packed
334 // values.
335 SmallVector<Type> convertedTdescTypes =
336 getUnrolledTypes(tdescTy, *targetShape);
337 SmallVector<Value> convertedTdescs = pack(
338 op.getTensorDesc(), convertedTdescTypes, *targetShape, loc, rewriter);
339
340 auto createStore = [&](SmallVector<OpFoldResult> offsets) {
341 xegpu::StoreNdOp::create(rewriter, loc, convertedValues[valueIndex++],
342 convertedTdescs[0], offsets, op.getL1HintAttr(),
343 op.getL2HintAttr(), op.getL3HintAttr(), layout);
344 return (Value) nullptr;
345 };
346 unrollByTile(op.getMixedOffsets(), tdescTy, *targetShape, createStore, loc,
347 rewriter);
348
349 rewriter.eraseOp(op);
350 return success();
351 }
352};
353
354struct UnrollDpasOp : public UnrollPattern<xegpu::DpasOp> {
355 using UnrollPattern<xegpu::DpasOp>::UnrollPattern;
356 LogicalResult matchAndRewrite(xegpu::DpasOp op,
357 PatternRewriter &rewriter) const override {
358 Location loc = op.getLoc();
359
360 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
361 if (!targetShape || targetShape->size() < 3)
362 return failure();
363
364 // targetShape is [batch..., M, K, N]
365 int64_t tsRank = targetShape->size();
366 auto M = (*targetShape)[tsRank - 3];
367 auto K = (*targetShape)[tsRank - 2];
368 auto N = (*targetShape)[tsRank - 1];
369 ArrayRef<int64_t> batchDims(targetShape->data(), tsRank - 3);
370
371 // Build block sizes including batch dimensions.
372 SmallVector<int64_t> aBlockSize(batchDims);
373 aBlockSize.push_back(M);
374 aBlockSize.push_back(K);
375 SmallVector<int64_t> bBlockSize(batchDims);
376 bBlockSize.push_back(K);
377 bBlockSize.push_back(N);
378 SmallVector<int64_t> cBlockSize(batchDims);
379 cBlockSize.push_back(M);
380 cBlockSize.push_back(N);
381
382 auto a = op.getLhs();
383 auto b = op.getRhs();
384 auto c = op.getAcc();
385
386 SmallVector<Value> aVals = packOperandForDpas(a, aBlockSize, loc, rewriter);
387 SmallVector<Value> bVals = packOperandForDpas(b, bBlockSize, loc, rewriter);
388 SmallVector<Value> cVals;
389 if (c)
390 cVals = packOperandForDpas(c, cBlockSize, loc, rewriter);
391
392 auto ranges = c ? SmallVector<ValueRange>({aVals, bVals, cVals})
393 : SmallVector<ValueRange>({aVals, bVals});
394 if (llvm::any_of(ranges, [](auto &v) { return v.size() == 0; }) ||
395 llvm::all_of(ranges, [](auto &v) { return v.size() == 1; }))
396 return failure();
397
398 VectorType resultTy = op.getResult().getType();
399 auto vecTy = VectorType::get(cBlockSize, resultTy.getElementType());
400
401 auto aShape = a.getType().getShape();
402 auto bShape = b.getType().getShape();
403
404 // Compute iteration counts. Batch dims only iterate over M and N (not
405 // K-reduction), so compute batch iterations from the C block size.
406 int64_t batchRank = batchDims.size();
407 int64_t mIters = aShape[batchRank] / M;
408 int64_t kIters = aShape[batchRank + 1] / K;
409 int64_t nIters = bShape[batchRank + 1] / N;
410
411 // Compute batch iterations (product of batch dim ratios).
412 int64_t batchIters = 1;
413 for (int64_t d = 0; d < batchRank; ++d)
414 batchIters *= aShape[d] / batchDims[d];
415
416 SmallVector<Value> newOps;
417 for (int64_t batch = 0; batch < batchIters; ++batch) {
418 for (int64_t i = 0; i < mIters; ++i) {
419 for (int64_t j = 0; j < nIters; ++j) {
420 Value tmpC;
421 if (c)
422 tmpC = cVals[batch * (mIters * nIters) + i * nIters + j];
423
424 for (int64_t k = 0; k < kIters; ++k) {
425 Value aVec = aVals[batch * (mIters * kIters) + i * kIters + k];
426 Value bVec = bVals[batch * (kIters * nIters) + k * nIters + j];
427 SmallVector<Value> operands({aVec, bVec});
428 if (tmpC)
429 operands.push_back(tmpC);
430
431 auto newDpasOp = xegpu::DpasOp::create(
432 rewriter, loc, TypeRange{vecTy}, operands, op.getProperties(),
434 op->getDiscardableAttrDictionary().getValue()));
436 tmpC = newDpasOp.getResult();
437 }
438 newOps.push_back(tmpC);
439 }
440 }
441 }
442 Value castOp = unpack(newOps, resultTy, cBlockSize, loc, rewriter);
443 rewriter.replaceOp(op, castOp);
444 return success();
445 }
446};
447
448struct UnrollDpasMxOp : public UnrollPattern<xegpu::DpasMxOp> {
449 using UnrollPattern<xegpu::DpasMxOp>::UnrollPattern;
450 LogicalResult matchAndRewrite(xegpu::DpasMxOp op,
451 PatternRewriter &rewriter) const override {
452 Location loc = op.getLoc();
453
454 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
455 if (!targetShape || targetShape->size() < 4)
456 return failure();
457
458 // targetShape is [batch..., M, K, N, S]
459 int64_t tsRank = targetShape->size();
460 auto M = (*targetShape)[tsRank - 4];
461 auto K = (*targetShape)[tsRank - 3];
462 auto N = (*targetShape)[tsRank - 2];
463 auto S = (*targetShape)[tsRank - 1];
464 ArrayRef<int64_t> batchDims(targetShape->data(), tsRank - 4);
465
466 SmallVector<int64_t> aBlockSize(batchDims);
467 aBlockSize.push_back(M);
468 aBlockSize.push_back(K);
469 SmallVector<int64_t> bBlockSize(batchDims);
470 bBlockSize.push_back(K);
471 bBlockSize.push_back(N);
472 SmallVector<int64_t> cBlockSize(batchDims);
473 cBlockSize.push_back(M);
474 cBlockSize.push_back(N);
475 SmallVector<int64_t> aScaleBlockSize(batchDims);
476 aScaleBlockSize.push_back(M);
477 aScaleBlockSize.push_back(S);
478 SmallVector<int64_t> bScaleBlockSize(batchDims);
479 bScaleBlockSize.push_back(S);
480 bScaleBlockSize.push_back(N);
481
482 auto a = op.getA();
483 auto b = op.getB();
484 auto c = op.getAcc();
485 auto ascale = dyn_cast<TypedValue<VectorType>>(op.getScaleA());
486 auto bscale = dyn_cast<TypedValue<VectorType>>(op.getScaleB());
487
488 SmallVector<Value> aVals = packOperandForDpas(a, aBlockSize, loc, rewriter);
489 SmallVector<Value> bVals = packOperandForDpas(b, bBlockSize, loc, rewriter);
490 SmallVector<Value> cVals;
491 if (c)
492 cVals = packOperandForDpas(c, cBlockSize, loc, rewriter);
493 SmallVector<Value> aScaleVals;
494 if (ascale)
495 aScaleVals = packOperandForDpas(ascale, aScaleBlockSize, loc, rewriter);
496 SmallVector<Value> bScaleVals;
497 if (bscale)
498 bScaleVals = packOperandForDpas(bscale, bScaleBlockSize, loc, rewriter);
499
500 VectorType resultTy = op.getResult().getType();
501 auto vecTy = VectorType::get(cBlockSize, resultTy.getElementType());
502
503 auto aShape = a.getType().getShape();
504 auto bShape = b.getType().getShape();
505 int64_t batchRank = batchDims.size();
506 int64_t mIters = aShape[batchRank] / M;
507 int64_t kIters = aShape[batchRank + 1] / K;
508 int64_t nIters = bShape[batchRank + 1] / N;
509
510 int64_t batchIters = 1;
511 for (int64_t d = 0; d < batchRank; ++d)
512 batchIters *= aShape[d] / batchDims[d];
513
514 SmallVector<Value> newOps;
515 xegpu::DpasMxOp newDpasMxOp;
516 for (int64_t batch = 0; batch < batchIters; ++batch) {
517 for (int64_t i = 0; i < mIters; ++i) {
518 for (int64_t j = 0; j < nIters; ++j) {
519 Value tmpC;
520 if (c)
521 tmpC = cVals[batch * (mIters * nIters) + i * nIters + j];
522
523 for (int64_t k = 0; k < kIters; ++k) {
524 Value aVec = aVals[batch * (mIters * kIters) + i * kIters + k];
525 Value bVec = bVals[batch * (kIters * nIters) + k * nIters + j];
526 SmallVector<Value> operands({aVec, bVec});
527 if (tmpC)
528 operands.push_back(tmpC);
529 if (ascale)
530 operands.push_back(
531 aScaleVals[batch * (mIters * kIters) + i * kIters + k]);
532 if (bscale)
533 operands.push_back(
534 bScaleVals[batch * (kIters * nIters) + k * nIters + j]);
535
536 newDpasMxOp = xegpu::DpasMxOp::create(
537 rewriter, loc, TypeRange{vecTy}, operands, op.getProperties(),
539 op->getDiscardableAttrDictionary().getValue()));
541 tmpC = newDpasMxOp.getResult();
542 }
543 newOps.push_back(newDpasMxOp);
544 }
545 }
546 }
547 Value castOp = unpack(newOps, resultTy, cBlockSize, loc, rewriter);
548 rewriter.replaceOp(op, castOp);
549 return success();
550 }
551};
552
553/// This pattern handles the unrolling of LoadGatherOp with offsets (gathered
554/// load).
555/// It unrolls the offsets and mask operands accordingly, and creates multiple
556/// LoadGatherOp with the unrolled operands.
557struct UnrollLoadGatherOp : public UnrollPattern<xegpu::LoadGatherOp> {
558 using UnrollPattern<xegpu::LoadGatherOp>::UnrollPattern;
559 LogicalResult matchAndRewrite(xegpu::LoadGatherOp op,
560 PatternRewriter &rewriter) const override {
561 Location loc = op.getLoc();
562 VectorType valueTy = llvm::dyn_cast<VectorType>(op.getType());
563 Value offsets = op.getOffsets();
564 Value mask = op.getMask();
565
566 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
567 if (!targetShape)
568 return failure();
569
570 SmallVector<int64_t> targetMaskShape(*targetShape);
571 int64_t chunkSize = op.getChunkSize().value_or(1);
572
573 // Unroll mask and offsets with correct shape
574 VectorType maskTy = llvm::dyn_cast<VectorType>(mask.getType());
575 VectorType offsetsTy = llvm::dyn_cast<VectorType>(offsets.getType());
576 Type elemTy = valueTy.getElementType();
577 VectorType newValueTy = VectorType::get(*targetShape, elemTy);
578
579 SmallVector<Type> convertedMaskTypes;
580 SmallVector<Value> convertedMasks;
581 SmallVector<Type> convertedOffsetTypes;
582 SmallVector<Value> convertedOffsets;
583
584 if (chunkSize > 1) {
585 // For chunked loads, mask and offsets have one less dimension
586 targetMaskShape.pop_back();
587 int64_t blockedChunkSize = targetShape->back();
588 int64_t numNewChunks = chunkSize / blockedChunkSize;
589 chunkSize = blockedChunkSize;
590
591 convertedMaskTypes = getUnrolledTypes(maskTy, targetMaskShape);
592 convertedOffsetTypes = getUnrolledTypes(offsetsTy, targetMaskShape);
593
594 SmallVector<Value> convertedMasksBase =
595 pack(mask, convertedMaskTypes, targetMaskShape, loc, rewriter);
596 SmallVector<Value> convertedOffsetsBase =
597 pack(offsets, convertedOffsetTypes, targetMaskShape, loc, rewriter);
598
599 for (auto maskVal : convertedMasksBase)
600 convertedMasks.append(numNewChunks, maskVal);
601
602 for (auto [baseOffset, offsetType] :
603 llvm::zip(convertedOffsetsBase, convertedOffsetTypes)) {
604 for (int64_t i = 0; i < numNewChunks; ++i) {
605 Value inc = arith::ConstantIndexOp::create(rewriter, loc,
606 i * blockedChunkSize);
607 Value incVec =
608 vector::BroadcastOp::create(rewriter, loc, offsetType, inc);
609 Value offsetVal =
610 arith::AddIOp::create(rewriter, loc, baseOffset, incVec);
611 convertedOffsets.push_back(offsetVal);
612 }
613 }
614 } else {
615 convertedMaskTypes = getUnrolledTypes(maskTy, targetMaskShape);
616 convertedMasks =
617 pack(mask, convertedMaskTypes, targetMaskShape, loc, rewriter);
618
619 convertedOffsetTypes = getUnrolledTypes(offsetsTy, *targetShape);
620 convertedOffsets =
621 pack(offsets, convertedOffsetTypes, *targetShape, loc, rewriter);
622 }
623
624 auto layout = op.getLayoutAttr();
625 if (layout)
626 layout = layout.dropInstData();
627
628 SmallVector<Value> newOps;
629 for (auto [o, m] : llvm::zip(convertedOffsets, convertedMasks)) {
630 auto newOp = xegpu::LoadGatherOp::create(
631 rewriter, loc, newValueTy, op.getSource(), o, m,
632 rewriter.getI64IntegerAttr(chunkSize), op.getL1HintAttr(),
633 op.getL2HintAttr(), op.getL3HintAttr(), layout,
634 /*contiguity=*/nullptr);
635 newOps.push_back(newOp);
636 }
637
638 Value castOp = unpack(newOps, op.getType(), *targetShape, loc, rewriter);
639 rewriter.replaceOp(op, castOp);
640 return success();
641 }
642};
643
644/// This pattern handles the unrolling of StoreScatterOp with offsets (scattered
645/// store).
646/// It unrolls the offsets and mask operands accordingly, and creates multiple
647/// StoreScatterOp with the unrolled operands.
648struct UnrollStoreScatterOp : public UnrollPattern<xegpu::StoreScatterOp> {
649 using UnrollPattern<xegpu::StoreScatterOp>::UnrollPattern;
650 LogicalResult matchAndRewrite(xegpu::StoreScatterOp op,
651 PatternRewriter &rewriter) const override {
652 Location loc = op.getLoc();
653 VectorType valueTy = llvm::dyn_cast<VectorType>(op.getValue().getType());
654 Value offsets = op.getOffsets();
655 Value mask = op.getMask();
656
657 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
658 if (!targetShape)
659 return failure();
660
661 int64_t chunkSize = op.getChunkSize().value_or(1);
662
663 SmallVector<int64_t> targetMaskShape(*targetShape);
664 VectorType maskTy = llvm::dyn_cast<VectorType>(mask.getType());
665 VectorType offsetsTy = llvm::dyn_cast<VectorType>(offsets.getType());
666
667 SmallVector<Type> convertedMaskTypes;
668 SmallVector<Value> convertedMasks;
669 SmallVector<Type> convertedOffsetTypes;
670 SmallVector<Value> convertedOffsets;
671
672 if (chunkSize > 1) {
673 targetMaskShape.pop_back();
674 int64_t blockedChunkSize = targetShape->back();
675 int64_t numNewChunks = chunkSize / blockedChunkSize;
676 chunkSize = blockedChunkSize;
677
678 convertedMaskTypes = getUnrolledTypes(maskTy, targetMaskShape);
679 convertedOffsetTypes = getUnrolledTypes(offsetsTy, targetMaskShape);
680
681 SmallVector<Value> convertedMasksBase =
682 pack(mask, convertedMaskTypes, targetMaskShape, loc, rewriter);
683 SmallVector<Value> convertedOffsetsBase =
684 pack(offsets, convertedOffsetTypes, targetMaskShape, loc, rewriter);
685
686 for (auto maskVal : convertedMasksBase)
687 convertedMasks.append(numNewChunks, maskVal);
688
689 for (auto [baseOffset, offsetType] :
690 llvm::zip(convertedOffsetsBase, convertedOffsetTypes)) {
691 for (int64_t i = 0; i < numNewChunks; ++i) {
692 Value inc = arith::ConstantIndexOp::create(rewriter, loc,
693 i * blockedChunkSize);
694 Value incVec =
695 vector::BroadcastOp::create(rewriter, loc, offsetType, inc);
696 Value offsetVal =
697 arith::AddIOp::create(rewriter, loc, baseOffset, incVec);
698 convertedOffsets.push_back(offsetVal);
699 }
700 }
701 } else {
702 convertedMaskTypes = getUnrolledTypes(maskTy, targetMaskShape);
703 convertedMasks =
704 pack(mask, convertedMaskTypes, targetMaskShape, loc, rewriter);
705
706 convertedOffsetTypes = getUnrolledTypes(offsetsTy, *targetShape);
707 convertedOffsets =
708 pack(offsets, convertedOffsetTypes, *targetShape, loc, rewriter);
709 }
710
711 SmallVector<Type> convertedValTypes =
712 getUnrolledTypes(valueTy, *targetShape);
713 SmallVector<Value> convertedValues =
714 pack(op.getValue(), convertedValTypes, *targetShape, loc, rewriter);
715
716 auto layout = op.getLayoutAttr();
717 if (layout)
718 layout = layout.dropInstData();
719
720 for (auto [v, o, m] :
721 llvm::zip(convertedValues, convertedOffsets, convertedMasks)) {
722 xegpu::StoreScatterOp::create(rewriter, loc, v, op.getDest(), o, m,
723 rewriter.getI64IntegerAttr(chunkSize),
724 op.getL1HintAttr(), op.getL2HintAttr(),
725 op.getL3HintAttr(), layout,
726 /*contiguity=*/nullptr);
727 }
728
729 rewriter.eraseOp(op);
730 return success();
731 }
732};
733
734struct UnrollLoadMatrixOp : public UnrollPattern<xegpu::LoadMatrixOp> {
735 using UnrollPattern<xegpu::LoadMatrixOp>::UnrollPattern;
736 LogicalResult matchAndRewrite(xegpu::LoadMatrixOp op,
737 PatternRewriter &rewriter) const override {
738 Location loc = op.getLoc();
739 VectorType valueTy = llvm::dyn_cast<VectorType>(op.getType());
740 assert(valueTy && "the value type must be vector type!");
741
742 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
743 if (!targetShape || targetShape->size() != (size_t)valueTy.getRank())
744 return failure();
745
746 Type elemTy = valueTy.getElementType();
747 ArrayRef<int64_t> shape = valueTy.getShape();
748 xegpu::DistributeLayoutAttr layout = op.getLayoutAttr();
749
750 VectorType newValueTy = valueTy.cloneWith(*targetShape, elemTy);
751
752 SmallVector<OpFoldResult> mixedOffsets = op.getMixedOffsets();
754 for (SmallVector<int64_t> offsets :
755 StaticTileOffsetRange(shape, *targetShape)) {
756 auto adds = xegpu::addElementwise(
757 rewriter, loc, mixedOffsets,
758 getAsIndexOpFoldResult(op.getContext(), offsets));
759 offsetsList.push_back(adds);
760 }
761
762 SmallVector<Value> newOps;
763 if (layout)
764 layout = layout.dropInstData();
765 for (SmallVector<OpFoldResult> offsets : offsetsList) {
766 auto newOp = xegpu::LoadMatrixOp::create(
767 rewriter, op.getLoc(), newValueTy, op.getMemDesc(), offsets, layout);
768 newOps.push_back(newOp);
769 }
770 Value castOp = unpack(newOps, op.getType(), *targetShape, loc, rewriter);
771 rewriter.replaceOp(op, castOp);
772 return success();
773 }
774};
775
776struct UnrollStoreMatrixOp : public UnrollPattern<xegpu::StoreMatrixOp> {
777 using UnrollPattern<xegpu::StoreMatrixOp>::UnrollPattern;
778 LogicalResult matchAndRewrite(xegpu::StoreMatrixOp op,
779 PatternRewriter &rewriter) const override {
780 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
781 if (!targetShape)
782 return failure();
783
784 Location loc = op.getLoc();
785 VectorType valueTy = llvm::dyn_cast<VectorType>(op.getData().getType());
786 assert(valueTy && "the value type must be vector type!");
787 ArrayRef<int64_t> shape = valueTy.getShape();
788 xegpu::DistributeLayoutAttr layout = op.getLayoutAttr();
789 if (layout)
790 layout = layout.dropInstData();
791
792 SmallVector<Type> convertedValTypes =
793 getUnrolledTypes(valueTy, *targetShape);
794 SmallVector<Value> convertedValues =
795 pack(op.getData(), convertedValTypes, *targetShape, loc, rewriter);
796
797 SmallVector<OpFoldResult> mixedOffsets = op.getMixedOffsets();
799 for (SmallVector<int64_t> offsets :
800 StaticTileOffsetRange(shape, *targetShape)) {
801 auto adds = xegpu::addElementwise(
802 rewriter, loc, mixedOffsets,
803 getAsIndexOpFoldResult(op.getContext(), offsets));
804 offsetsList.push_back(adds);
805 }
806
807 for (auto [v, offsets] : llvm::zip_equal(convertedValues, offsetsList))
808 xegpu::StoreMatrixOp::create(rewriter, loc, v, op.getMemDesc(), offsets,
809 layout);
810
811 rewriter.eraseOp(op);
812 return success();
813 }
814};
815
816/// UnrollConvertLayoutOp pattern for unrolling xegpu::ConvertLayoutOp
817/// operations. It first check whether the convert layout op has valid layouts
818/// after inst_data stripped. If it does, it will unroll the vector into
819/// multiple smaller vectors according to the target shape, and create multiple
820/// ConvertLayoutOp with the unrolled vectors and the stripped layouts.
821///
822/// When the input and target layouts have different inst_data, the source is
823/// extracted at the input inst_data granularity and the result is inserted at
824/// the target inst_data granularity, enabling slice cancellation during
825/// canonicalization.
826struct UnrollConvertLayoutOp : public UnrollPattern<xegpu::ConvertLayoutOp> {
827 using UnrollPattern<xegpu::ConvertLayoutOp>::UnrollPattern;
828
829 /// Extracts source in `inTile` slices, regroups into `convTile`-sized
830 /// ConvertLayoutOps, and inserts the result in `outTile` slices.
831 /// Returns failure if the tiles do not evenly divide.
832 LogicalResult
833 rewriteWithRegrouping(xegpu::ConvertLayoutOp op, VectorType valueTy,
834 ArrayRef<int64_t> convTile, ArrayRef<int64_t> inTile,
835 ArrayRef<int64_t> outTile,
836 xegpu::DistributeLayoutAttr inputLayout,
837 xegpu::DistributeLayoutAttr targetLayout, Location loc,
838 PatternRewriter &rewriter) const {
839 ArrayRef<int64_t> vecShape = valueTy.getShape();
840 if (!computeShapeRatio(vecShape, convTile) ||
841 !computeShapeRatio(convTile, inTile) ||
842 !computeShapeRatio(convTile, outTile))
843 return failure();
844
845 Type elemTy = valueTy.getElementType();
846 int64_t rank = valueTy.getRank();
847 VectorType convTy = VectorType::get(convTile, elemTy);
848 SmallVector<int64_t> strides(rank, 1);
849
850 Value source = op.getSource();
851 auto zeroOf = [&](VectorType ty) -> Value {
852 return arith::ConstantOp::create(
853 rewriter, loc, ty,
854 DenseElementsAttr::get(ty, rewriter.getZeroAttr(elemTy)));
855 };
856 auto addOffsets = [](ArrayRef<int64_t> a,
859 for (auto [r, v] : llvm::zip_equal(res, b))
860 r += v;
861 return res;
862 };
863
864 Value result = zeroOf(valueTy);
865 for (SmallVector<int64_t> convOff :
866 StaticTileOffsetRange(vecShape, convTile)) {
867 // Build the convert tile from inTile-sized slices of the source.
868 Value conv;
869 if (convTile == inTile) {
870 conv = vector::ExtractStridedSliceOp::create(
871 rewriter, loc, source, convOff, convTile, strides);
872 } else {
873 conv = zeroOf(convTy);
874 for (SmallVector<int64_t> inLocal :
875 StaticTileOffsetRange(convTile, inTile)) {
876 Value piece = vector::ExtractStridedSliceOp::create(
877 rewriter, loc, source, addOffsets(convOff, inLocal), inTile,
878 strides);
879 conv = vector::InsertStridedSliceOp::create(rewriter, loc, piece,
880 conv, inLocal, strides);
881 }
882 }
883
884 conv = xegpu::ConvertLayoutOp::create(rewriter, loc, convTy, conv,
885 inputLayout, targetLayout);
886
887 // Write the converted tile into the result as outTile-sized slices.
888 if (convTile == outTile) {
889 result = vector::InsertStridedSliceOp::create(rewriter, loc, conv,
890 result, convOff, strides);
891 } else {
892 for (SmallVector<int64_t> outLocal :
893 StaticTileOffsetRange(convTile, outTile)) {
894 Value piece = vector::ExtractStridedSliceOp::create(
895 rewriter, loc, conv, outLocal, outTile, strides);
896 result = vector::InsertStridedSliceOp::create(
897 rewriter, loc, piece, result, addOffsets(convOff, outLocal),
898 strides);
899 }
900 }
901 }
902
903 rewriter.replaceOp(op, result);
904 return success();
905 }
906
907 LogicalResult matchAndRewrite(xegpu::ConvertLayoutOp op,
908 PatternRewriter &rewriter) const override {
909 Location loc = op.getLoc();
910 Type valType = op.getType();
911
912 xegpu::DistributeLayoutAttr inputLayout = op.getEffectiveInputLayout();
913 xegpu::DistributeLayoutAttr targetLayout = op.getTargetLayoutAttr();
914 if (!inputLayout || !targetLayout)
915 return rewriter.notifyMatchFailure(op, "missing layout attributes.");
916
917 if (valType.isIntOrFloat()) {
918 rewriter.replaceOp(op, op.getSource());
919 return success();
920 }
921
922 // Capture inst_data granularities before stripping them.
923 SmallVector<int64_t> inTile = inputLayout.getEffectiveInstDataAsInt();
924 SmallVector<int64_t> outTile = targetLayout.getEffectiveInstDataAsInt();
925 if (inTile.empty() || outTile.empty())
926 return rewriter.notifyMatchFailure(op, "Not a target ConvertLayoutOp.");
927
928 inputLayout = inputLayout.dropInstData();
929 targetLayout = targetLayout.dropInstData();
930
931 VectorType valueTy = llvm::dyn_cast<VectorType>(op.getType());
932 assert(valueTy && "the value type must be vector type!");
933
934 std::optional<SmallVector<int64_t>> targetShape = getTargetShape(op);
935 if (!targetShape || targetShape->size() != (size_t)valueTy.getRank())
936 return failure();
937
938 // Nothing to convert if layouts match after stripping inst_data.
939 if (!inputLayout || !targetLayout || inputLayout.isEqualTo(targetLayout)) {
940 rewriter.replaceOp(op, op.getSource());
941 return success();
942 }
943
944 // Try regrouping: extract at inTile, convert, insert at outTile.
945 if (succeeded(rewriteWithRegrouping(op, valueTy, *targetShape, inTile,
946 outTile, inputLayout, targetLayout, loc,
947 rewriter)))
948 return success();
949
950 // Fallback: pack/unpack at the convert tile granularity.
951 SmallVector<Type> convertedValTypes =
952 getUnrolledTypes(valueTy, *targetShape);
953 SmallVector<Value> convertedValues =
954 pack(op.getOperand(), convertedValTypes, *targetShape, loc, rewriter);
955 SmallVector<Value> newOps;
956 for (auto [v, t] : llvm::zip(convertedValues, convertedValTypes)) {
957 auto newOp = xegpu::ConvertLayoutOp::create(rewriter, loc, t, v,
958 inputLayout, targetLayout);
959 newOps.push_back(newOp);
960 }
961 Value newSource = unpack(newOps, op.getType(), *targetShape, loc, rewriter);
962
963 rewriter.replaceOp(op, newSource);
964 return success();
965 }
966};
967
968/// Unrolls vector.multi_reduction by sequentially reducing tiles with
969/// elementwise arith operations first, then a single multi_reduction
970/// per non-reduced tile position. This avoids generating long chains of
971/// multi_reduction ops (as the upstream pattern does) and is more efficient.
972///
973/// Example:
974/// vector.multi_reduction <32x64xf16> to <32xf16> (tile_shape=32, 32)
975/// -- Upstream pattern generates:
976/// %tmp1 = vector.multi_reduction %tile0, %zero_acc <32x32xf16> to <32xf16>
977/// %res = vector.multi_reduction %tmp1, %tile1 <32x32xf16> to <32xf16>
978/// -- This pattern generates:
979/// %tmp1 = arith.reduction %tile0, %tile1 <32x32xf16> -> <32x32xf16> //
980/// elementwise %res = vector.multi_reduction %tmp1, %zero_acc <32x32xf16> to
981/// <32xf16>
982struct UnrollMultiReductionOp
983 : public UnrollPattern<vector::MultiDimReductionOp> {
984 UnrollMultiReductionOp(MLIRContext *context,
986 PatternBenefit benefit = 2)
987 : UnrollPattern<vector::MultiDimReductionOp>(context, options, benefit) {}
988
989 LogicalResult matchAndRewrite(vector::MultiDimReductionOp reductionOp,
990 PatternRewriter &rewriter) const override {
991 VectorType srcTy = reductionOp.getSourceVectorType();
992 ArrayRef<int64_t> srcShape = srcTy.getShape();
993 int64_t srcRank = srcTy.getRank();
994
995 Location loc = reductionOp.getLoc();
996 Value source = reductionOp.getSource();
997 Value acc = reductionOp.getAcc();
998 vector::CombiningKind kind = reductionOp.getKind();
999
1000 // Result must be a vector (not scalar).
1001 auto resultType = dyn_cast<VectorType>(reductionOp.getDestType());
1002 if (!resultType)
1003 return failure();
1004
1005 std::optional<SmallVector<int64_t>> targetShapeOpt =
1006 getTargetShape(reductionOp);
1007 if (!targetShapeOpt ||
1008 static_cast<int64_t>(targetShapeOpt->size()) != srcRank)
1009 return failure();
1010
1011 SmallVector<int64_t> targetShape = *targetShapeOpt;
1012
1013 // Check divisibility for all dimensions.
1014 for (int64_t i = 0; i < srcRank; ++i) {
1015 if (srcShape[i] % targetShape[i] != 0)
1016 return failure();
1017 }
1018
1019 SmallVector<bool> reductionMask = reductionOp.getReductionMask();
1020 // Identify reduced and kept dimensions from the reduction mask.
1021 SmallVector<int64_t> reducedDims, keptDims;
1022 for (int64_t i = 0; i < srcRank; ++i) {
1023 if (reductionMask[i])
1024 reducedDims.push_back(i);
1025 else
1026 keptDims.push_back(i);
1027 }
1028
1029 // Compute the number of tiles along each reduced dimension and their
1030 // product
1031 SmallVector<int64_t> numReducedTilesPerDim;
1032 for (int64_t d : reducedDims)
1033 numReducedTilesPerDim.push_back(srcShape[d] / targetShape[d]);
1034
1035 // Build kept shapes for iterating over non-reduced dimensions.
1036 SmallVector<int64_t> keptShape, keptTileShape;
1037 for (int64_t d : keptDims) {
1038 keptShape.push_back(srcShape[d]);
1039 keptTileShape.push_back(targetShape[d]);
1040 }
1041
1042 // Initialize the result vector for assembly.
1043 Value result = arith::ConstantOp::create(rewriter, loc, resultType,
1044 rewriter.getZeroAttr(resultType));
1045
1046 // Iterate over all tile positions in the kept dimensions.
1047 // Ex: [off0, off1, _ _ off4]
1048 // blanks are offsets for the reduced dims, they will be
1049 // generated in the inner loop below
1050 for (SmallVector<int64_t> keptOffsets :
1051 StaticTileOffsetRange(keptShape, keptTileShape)) {
1052
1053 // Reconstruct full-rank base offsets with 0 for reduced dims.
1054 // Ex: [off0, off1, 0, 0, off4]
1055 SmallVector<int64_t> baseOffsets(srcRank, 0);
1056 for (auto [idx, dim] : llvm::enumerate(keptDims))
1057 baseOffsets[dim] = keptOffsets[idx];
1058
1059 // Generate the full tile indices for the reduced dimensions.
1060 // Ex: if reduceDimShapes = [32, 64] and
1061 // reducedDimTargetShapes = [16, 16], then reducedTileCoords:
1062 // [(0, 0), (0, 1), (0, 2), (0, 3),
1063 // (1, 0), (1, 1), (1, 2), (1, 3)]
1064 auto reducedTileCoords = StaticTileOffsetRange(
1065 numReducedTilesPerDim, SmallVector<int64_t>(reducedDims.size(), 1));
1066
1067 // Step 1: Fill "blanks" in the offsets for the reduced dimensions
1068 // using 'reducedTileCoords' and extract according tiles.
1069 // Ex: tiles = [source[off0, off1, off2_red, off3_red, off4], ...]
1070 SmallVector<Value> tiles;
1071 for (SmallVector<int64_t> reducedTileIdx : reducedTileCoords) {
1072 SmallVector<int64_t> offsets(baseOffsets);
1073 for (auto [idx, dim] : llvm::enumerate(reducedDims))
1074 offsets[dim] = reducedTileIdx[idx] * targetShape[dim];
1075 SmallVector<int64_t> strides(srcRank, 1);
1076 Value tile = vector::ExtractStridedSliceOp::create(
1077 rewriter, loc, source, offsets, targetShape, strides);
1078 tiles.push_back(tile);
1079 }
1080
1081 // Step 2: Sequentially reduce tiles using elementwise arith operations.
1082 Value reduced = tiles[0];
1083 for (size_t i = 1; i < tiles.size(); ++i)
1084 reduced =
1085 vector::makeArithReduction(rewriter, loc, kind, reduced, tiles[i]);
1086
1087 // Step 3: Perform a single multi_reduction with the accumulator slice.
1088 SmallVector<int64_t> accStrides(keptTileShape.size(), 1);
1089 Value accSlice = vector::ExtractStridedSliceOp::create(
1090 rewriter, loc, acc, keptOffsets, keptTileShape, accStrides);
1091
1092 auto newReduction = vector::MultiDimReductionOp::create(
1093 rewriter, loc, reduced, accSlice, reductionMask, kind);
1094
1095 // Step 4: Insert the reduced result into the output vector.
1096 SmallVector<int64_t> dstStrides(keptTileShape.size(), 1);
1097 result = vector::InsertStridedSliceOp::create(
1098 rewriter, loc, newReduction, result, keptOffsets, dstStrides);
1099 }
1100
1101 rewriter.replaceOp(reductionOp, result);
1102 return success();
1103 }
1104};
1105
1106} // namespace
1107
1110 patterns
1111 .add<UnrollCreateNdOp, UnrollPrefetchNdOp, UnrollLoadNdOp,
1112 UnrollStoreNdOp, UnrollDpasOp, UnrollDpasMxOp, UnrollLoadMatrixOp,
1113 UnrollStoreMatrixOp, UnrollLoadGatherOp, UnrollStoreScatterOp,
1114 UnrollConvertLayoutOp, UnrollMultiReductionOp>(patterns.getContext(),
1115 options);
1116}
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static llvm::ManagedStatic< PassManagerOptions > options
static std::optional< SmallVector< int64_t > > getTargetShape(const vector::UnrollVectorOptions &options, Operation *op)
Return the target shape for unrolling for the given op.
UnitAttr getUnitAttr()
Definition Builders.cpp:106
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
Definition Builders.cpp:175
IntegerAttr getI64IntegerAttr(int64_t value)
Definition Builders.cpp:120
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
TypedAttr getZeroAttr(Type type)
Definition Builders.cpp:333
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
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
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Definition Builders.h:528
This class represents a single result from folding an operation.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
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,...
A range-style iterator that allows for iterating over the offsets of all potential tiles of size tile...
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
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
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
Value makeArithReduction(OpBuilder &b, Location loc, CombiningKind kind, Value v1, Value acc, arith::FastMathFlagsAttr fastmath=nullptr, Value mask=nullptr)
Returns the result value of reducing two scalar/vector values with the corresponding arith operation.
Value createVectorWithShapeFromValues(OpBuilder &builder, Location loc, ValueRange values, ArrayRef< int64_t > shape)
Create a vector of shape from a set of values using vector.insert_stride_slice.
void populateXeGPUUnrollPatterns(RewritePatternSet &patterns, const UnrollOptions &options)
Collect a set of patterns to unroll xegpu operations to a smaller shapes.
SmallVector< NamedAttribute > dropInstDataOnAttrs(ArrayRef< NamedAttribute > attrs)
Updates the NamedAttribute sequence by dropping inst-data information from any DistributeLayoutAttr f...
void dropInstDataOnInherentAttrs(Operation *op)
Drops inst-data information from DistributeLayoutAttrs stored as inherent attributes on the operation...
SmallVector< Value > extractVectorsWithShapeFromValue(OpBuilder &builder, Location loc, Value value, ArrayRef< int64_t > shape)
Extract a set of small vectors from a value with a given shape using vector.extract_stride_slice.
SmallVector< OpFoldResult > addElementwise(OpBuilder &builder, Location loc, ArrayRef< OpFoldResult > lhs, ArrayRef< OpFoldResult > rhs)
Generates element-wise addition ops of two arrays with same length.
Include the generated interface declarations.
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
int64_t computeProduct(ArrayRef< int64_t > basis)
Self-explicit.
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
SmallVector< Loops, 8 > tile(ArrayRef< scf::ForOp > forOps, ArrayRef< Value > sizes, ArrayRef< scf::ForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
Definition Utils.cpp:1380
std::optional< SmallVector< int64_t > > computeShapeRatio(ArrayRef< int64_t > shape, ArrayRef< int64_t > subShape)
Return the multi-dimensional integral ratio of subShape to the trailing dimensions of shape.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Options to control the XeGPU unrolling.
Definition Transforms.h:29
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.