MLIR 23.0.0git
XeGPULayoutImpl.cpp
Go to the documentation of this file.
1//===---- XeGPULayoutImpl.cpp - MLIR Utilities for XeGPUOps
2//------------------===//
3//
4// Part of the MLIR Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements layout utility functions for XeGPU dialect
11// transformation.
12//
13//===----------------------------------------------------------------------===//
14
23#include "mlir/IR/Builders.h"
24#include "mlir/IR/Operation.h"
25#include "mlir/IR/ValueRange.h"
30#include "llvm/ADT/PostOrderIterator.h"
31#include "llvm/Support/FormatVariadic.h"
32#include <cstdint>
33#include <numeric>
34
35using namespace mlir;
36
40 out.reserve(attrs.size());
41
42 for (auto attr : attrs) {
43 if (auto dist = dyn_cast<xegpu::DistributeLayoutAttr>(attr.getValue())) {
44 auto newLayout = dist.dropSgLayoutAndData();
45 if (newLayout)
46 out.emplace_back(attr.getName(), newLayout);
47 } else {
48 out.push_back(attr);
49 }
50 }
51
52 return out;
53}
54
58 out.reserve(attrs.size());
59
60 for (auto attr : attrs) {
61 if (auto dist = dyn_cast<xegpu::DistributeLayoutAttr>(attr.getValue())) {
62 auto newLayout = dist.dropInstData();
63 if (newLayout)
64 out.emplace_back(attr.getName(), newLayout);
65 } else {
66 out.push_back(attr);
67 }
68 }
69
70 return out;
71}
72
73// Sets the layout on a TensorDesc value by updating its type to include
74// the given layout, if the type does not already have a layout attached.
75static void setTensorDescLayout(Value val, xegpu::DistributeLayoutAttr layout) {
76 auto tensorDescTy = dyn_cast<xegpu::TensorDescType>(val.getType());
77 if (!tensorDescTy || tensorDescTy.getLayoutAttr())
78 return;
79 auto typeWithLayout = xegpu::TensorDescType::get(
80 tensorDescTy.getContext(), tensorDescTy.getShape(),
81 tensorDescTy.getElementType(), tensorDescTy.getEncoding(), layout);
82 val.setType(typeWithLayout);
83}
84
85// the walkRegionBackward() is a recursive function
86// the input rootOp is the function operation, which is also a region op.
87// it recursively processes the region op in reverse topological order.
88static void walkRegionBackward(Region &region,
90
91 // Use post-order traversal to process blocks in reverse topological order.
92 // This ensures that use blocks are visited before def blocks, which is
93 // required for backward layout propagation.
94 if (region.empty())
95 return;
96 llvm::ReversePostOrderTraversal<Region *> rpot(&region);
97 SmallVector<Block *> blocks(rpot.begin(), rpot.end());
98 for (Block *block : llvm::reverse(blocks)) {
99 // ops: back -> front
100 for (Operation &op : llvm::reverse(*block)) {
101 // make sure we first visit inside the region op (so yield op first)
102 // and then move to region op itself
103 // Regions are iterated in forward order so that for multi-region ops
104 // like scf.while, earlier regions (e.g., "before/cond") are processed
105 // first. This ensures that when a later region's terminator (e.g., "do"
106 // yield) needs the layout of an earlier region's block args, those
107 // layouts are already available from use points.
108 for (Region &nested : op.getRegions())
109 walkRegionBackward(nested, visit);
110
111 visit(&op);
112 }
113 }
114}
115
116static xegpu::DistributeLayoutAttr getLayoutFromUsePoints(Value result) {
117 xegpu::DistributeLayoutAttr layout = nullptr;
118 for (OpOperand &use : result.getUses()) {
119 if (auto tmpLayout = xegpu::getDistributeLayoutAttr(use)) {
120 if (!layout)
121 layout = tmpLayout;
122 break;
123 }
124 }
125 return layout;
126}
127
128// Returns true if `op` is safe and cheap to clone (no side effects, no
129// regions, and all operands are themselves trivially rematerializable, e.g.
130// block-arg-free pure value generators such as `vector.step`, splat
131// `arith.constant`, or `vector.create_mask` whose operands are constants).
133 if (!op || op->getNumRegions() != 0)
134 return false;
135 if (!isMemoryEffectFree(op))
136 return false;
137 for (Value v : op->getOperands()) {
138 Operation *defOp = v.getDefiningOp();
139 if (!defOp)
140 return false;
141 if (!isTriviallyRematerializable(defOp))
142 return false;
143 }
144 return true;
145}
146
147// For regular operations: First the result layouts are propagated from uses.
148// Then the result layouts are propagated to uses (operands).
150 if (op->getNumResults() == 0)
151 return;
152 if (op->getNumResults() > 1 && !isa<vector::DeinterleaveOp>(op))
153 return;
154 OpResult result = op->getResult(0);
155 xegpu::DistributeLayoutAttr resLayout = getLayoutFromUsePoints(result);
156 Type resultType = result.getType();
157
158 if (!resLayout)
159 return;
160
161 // Recover layout for TensorDesc type results by updating the type to include
162 // the layout. For vector type
163 if (isa<xegpu::TensorDescType>(resultType))
164 setTensorDescLayout(result, resLayout);
165
166 // Recover layout for vector type results, or for multi-reduction ops which
167 // may reduce to a scalar that still needs a layout.
168 if (isa<VectorType>(resultType) || isa<vector::MultiDimReductionOp>(op))
170
171 if (isa<vector::DeinterleaveOp>(op))
172 xegpu::setTemporaryLayout(op->getResult(1), resLayout);
173
174 for (OpOperand &opr : op->getOpOperands()) {
175 xegpu::DistributeLayoutAttr operandLayout =
177 if (isa<VectorType>(opr.get().getType()) && operandLayout)
178 xegpu::setTemporaryLayout(opr, operandLayout);
179 }
180}
181
182// Propagate layout from region op results and sibling region block args
183// to yield/condition operands. For each successor of this terminator:
184// - Parent successor: propagate from parent op's result layouts (use points).
185// - Region successor: propagate from target region's block arg layouts (use
186// points), e.g., scf.yield in "after/do" region propagates to "before/cond"
187// block args.
189 mlir::RegionBranchTerminatorOpInterface yieldOp) {
190 auto regionBranchOp =
191 dyn_cast<RegionBranchOpInterface>(yieldOp->getParentOp());
192 if (!regionBranchOp)
193 return;
194
196 SmallVector<Attribute> operandAttrs(yieldOp->getNumOperands(), nullptr);
197 yieldOp.getSuccessorRegions(operandAttrs, successors);
198
199 for (const RegionSuccessor &successor : successors) {
200 OperandRange succOps = yieldOp.getSuccessorOperands(successor);
201 if (succOps.empty())
202 continue;
203 unsigned beginIdx = succOps.getBeginOperandIndex();
204 ValueRange successorInputs = regionBranchOp.getSuccessorInputs(successor);
205 unsigned count = std::min<unsigned>(succOps.size(), successorInputs.size());
206
207 for (unsigned i = 0; i < count; ++i) {
208 xegpu::DistributeLayoutAttr layout;
209 if (successor.isOperation()) {
210 // For parent successor, get layout from external use points of the
211 // parent op's results.
212 auto regionResult = regionBranchOp->getResult(i);
213 layout = getLayoutFromUsePoints(regionResult);
214 if (layout) {
215 // set layout for the region op, like scf.loop
216 xegpu::setTemporaryLayout(regionResult, layout);
217 if (isa<xegpu::TensorDescType>(regionResult.getType()))
218 setTensorDescLayout(regionResult, layout);
219 }
220 } else {
221 // For region successor, get layout from the target region's block
222 // arg use points (e.g., "before/cond" region args for scf.while
223 // "after/do" yield).
224 layout = getLayoutFromUsePoints(successorInputs[i]);
225 }
226 if (!layout)
227 continue;
228 auto operandType = succOps[i].getType();
229 if (isa<VectorType>(operandType) ||
230 dyn_cast<xegpu::TensorDescType>(operandType))
231 // recover layout for yield op operands
232 xegpu::setTemporaryLayout(yieldOp->getOpOperand(beginIdx + i), layout);
233 }
234 }
235}
236
237// Propagate layout from region arguments to region op's init operands. This
238// sets the temporary layout for region arguments and init operands.
239LogicalResult
240xegpu::propagateRegionArgsToInits(mlir::RegionBranchOpInterface regionOp,
241 xegpu::GetLayoutFnTy getLayoutOfValue) {
242 // Iterate all regions of the region op. For each block argument that has a
243 // layout (obtained via `getLayoutOfValue`), trace back to find the
244 // corresponding init operand of the regionOp and set the layout on it.
245 // This works generically for scf.for, scf.while, and other
246 // RegionBranchOpInterface ops.
247 for (Region &region : regionOp->getRegions()) {
248 RegionSuccessor regionSuccessor(&region);
249 // Use getSuccessorInputs to get the block arguments that correspond to
250 // predecessor operands. This correctly handles ops like scf.for where
251 // the induction variable is a block arg but not a successor input.
252 ValueRange successorInputs = regionOp.getSuccessorInputs(regionSuccessor);
253 for (auto [inputIdx, regionArg] : llvm::enumerate(successorInputs)) {
254 auto layout = getLayoutOfValue(regionArg);
255 if (!layout)
256 continue;
257
258 // Recover layout for tensor_desc block args by updating the type.
259 if (isa<xegpu::TensorDescType>(regionArg.getType()))
260 setTensorDescLayout(regionArg, layout);
261
262 // Recover layout for region op operands, like scf.for's init operands.
263 // Find all predecessor values that flow into this block argument.
264 SmallVector<Value> predValues;
265 regionOp.getPredecessorValues(regionSuccessor, inputIdx, predValues);
266 for (Value predVal : predValues) {
267 // Match predecessor value to an operand of the regionOp.
268 for (OpOperand &operand : regionOp->getOpOperands()) {
269 if (operand.get() == predVal)
270 xegpu::setTemporaryLayout(operand, layout);
271 }
272 }
273 }
274 }
275 return success();
276}
277
278// Prerequisite for Layout Recovery
279// It relies on the following invariant:
280// 1. there is no layout conflict between different uses of the same definition.
281// 2. each definition has a well-defined layout requirement at its use point.
282// - Every definition must have at least one use that appears after it in
283// topological order.
284// - TODO: If a definition has no such use (e.g., a loop result or region
285// output), an explicit convert_layout operation is inserted to create a
286// use.
287// - Only the result of convert_layout is permitted to have no subsequent
288// use.
289//
290// The recovery proceeds by scanning the operation in reverse topological order
291// as follows:
292// For regular operations: First the result layouts are propagated from uses.
293// Then the result layouts are propagated to operands.
294//
295// For region operations (e.g., loops):
296// - When backward propagation reaches a region op, it sets the layout of
297// the region op’s results according to use points like regular ops.
298// - Then, the result layouts (such as a loop output) are propagated to
299// their corresponding operands in the yield.
300// - When backward propagation reaches the first operation inside the
301// region, the pass examines the region op’s initialization list,
302// propagating from region arguments to the corresponding initialization
303// operands.
304// - This ensures that layouts are consistently propagated
305// across region boundaries while preserving a single well-defined use for
306// each definition at the region-op level.
308 auto processFunc = [&](Region &body, StringRef funcName) {
309 walkRegionBackward(body, [&](Operation *op) {
310 if (auto regionOp = dyn_cast<mlir::RegionBranchOpInterface>(op)) {
313 } else if (auto yieldOp =
314 dyn_cast<mlir::RegionBranchTerminatorOpInterface>(op)) {
316 } else if (!dyn_cast<xegpu::AnchorLayoutInterface>(op)) {
318 }
319 });
320 };
322 rootOp->walk([&](func::FuncOp func) {
323 processFunc(func.getBody(), func.getSymName());
324 });
325 rootOp->walk([&](gpu::GPUFuncOp func) {
326 processFunc(func.getBody(), func.getName());
327 });
328
329 return true;
330}
331
332template <typename T, typename>
333void xegpu::removeLayoutAttr(const T &operandOrResult) {
334 Operation *owner = operandOrResult.getOwner();
335 std::string name = xegpu::getTemporaryLayoutName(operandOrResult);
336 if (owner->hasAttrOfType<DistributeLayoutAttr>(name))
337 owner->removeAttr(name);
338}
339
340// Explicit instantiation for OpResult
341template void
343
344// Explicit instantiation for OpOperand
345template void
347
349 op->walk([&](Operation *nestOp) {
350 // Remove all attributes of DistributeLayoutAttr type
351 SmallVector<StringAttr> attrsToRemove;
352 for (auto namedAttr : nestOp->getAttrs()) {
353 if (isa<DistributeLayoutAttr>(namedAttr.getValue()))
354 attrsToRemove.push_back(namedAttr.getName());
355 }
356 for (auto attrName : attrsToRemove)
357 nestOp->removeAttr(attrName);
358 });
359}
360
362 op->walk([&](Operation *nestOp) {
363 SmallVector<StringAttr> attrsToRemove;
364 for (auto namedAttr : nestOp->getDiscardableAttrs()) {
365 if (isa<xegpu::DistributeLayoutAttr>(namedAttr.getValue()))
366 attrsToRemove.push_back(namedAttr.getName());
367 }
368 for (auto attrName : attrsToRemove)
369 nestOp->removeDiscardableAttr(attrName);
370 });
371}
372
373/// Returns true if every dimension of `shape` except the innermost
374/// `numInnerDims` is a unit (size-1) dimension.
375[[maybe_unused]] static bool leadingDimsAreUnit(ArrayRef<int64_t> shape,
376 int numInnerDims) {
377 int numLeading = static_cast<int>(shape.size()) - numInnerDims;
378 if (numLeading <= 0)
379 return true;
380 return llvm::all_of(shape.take_front(numLeading),
381 [](int64_t dim) { return dim == 1; });
382}
383
384static xegpu::LayoutAttr buildInstDataLayoutWithLane(
385 mlir::MLIRContext *context, ArrayRef<int64_t> instData,
386 ArrayRef<int64_t> laneLayout, ArrayRef<int64_t> laneData,
387 DenseI32ArrayAttr orderAttr = nullptr) {
388 auto toI32Attr = [&](auto range) {
389 SmallVector<int32_t> v(range.begin(), range.end());
390 return DenseI32ArrayAttr::get(context, v);
391 };
392 return xegpu::LayoutAttr::get(context, /*sg_layout=*/nullptr,
393 /*sg_data=*/nullptr, toI32Attr(instData),
394 toI32Attr(laneLayout), toI32Attr(laneData),
395 orderAttr);
396}
397
399 ArrayRef<int64_t> laneLayout,
400 ArrayRef<int64_t> laneData) {
401 return !llvm::any_of(llvm::seq<int>(0, dataShape.size()), [&](int dim) {
402 return dataShape[dim] % (laneLayout[dim] * laneData[dim]) != 0;
403 });
404}
405
406static xegpu::LayoutAttr
408 ArrayRef<int64_t> laneData,
409 DenseI32ArrayAttr orderAttr = nullptr) {
410 auto toI32Attr = [&](auto range) {
411 SmallVector<int32_t> v(range.begin(), range.end());
412 return DenseI32ArrayAttr::get(context, v);
413 };
414 return xegpu::LayoutAttr::get(context, /*sg_layout=*/nullptr,
415 /*sg_data=*/nullptr,
416 /*inst_data=*/nullptr, toI32Attr(laneLayout),
417 toI32Attr(laneData), orderAttr);
418}
419
420static xegpu::LayoutAttr
422 ArrayRef<int64_t> sgData, ArrayRef<int64_t> instData,
423 ArrayRef<int64_t> laneLayout, ArrayRef<int64_t> laneData,
424 DenseI32ArrayAttr orderAttr = nullptr) {
425 auto toI32Attr = [&](auto range) {
426 SmallVector<int32_t> v(range.begin(), range.end());
427 return DenseI32ArrayAttr::get(context, v);
428 };
429 return xegpu::LayoutAttr::get(
430 context, sgLayout.empty() ? nullptr : toI32Attr(sgLayout),
431 sgData.empty() ? nullptr : toI32Attr(sgData),
432 instData.empty() ? nullptr : toI32Attr(instData),
433 laneLayout.empty() ? nullptr : toI32Attr(laneLayout),
434 laneData.empty() ? nullptr : toI32Attr(laneData), orderAttr);
435}
436
437static xegpu::LayoutAttr buildSgLayout(mlir::MLIRContext *context,
438 ArrayRef<int64_t> wgTileShape,
439 ArrayRef<int64_t> sgLayout,
440 int dimK = -1,
441 DenseI32ArrayAttr orderAttr = nullptr) {
442 SmallVector<int64_t> sgData(sgLayout.size());
443 for (int dim = 0; dim < (int)sgLayout.size(); ++dim) {
444 if (dim == dimK)
445 sgData[dim] = wgTileShape[dim];
446 else
447 sgData[dim] = wgTileShape[dim] / sgLayout[dim];
448 }
449 return buildLayout(context, sgLayout, sgData,
450 /*inst_data=*/{}, /*lane_layout=*/{},
451 /*lane_data=*/{}, /*order=*/nullptr);
452}
453
454/// Infers the source layout attribute for a broadcast operation given the
455/// result layout attribute, result shape, source shape.
456xegpu::DistributeLayoutAttr
457xegpu::inferBroadcastSourceLayout(xegpu::DistributeLayoutAttr resLayout,
458 ArrayRef<int64_t> resShape,
459 ArrayRef<int64_t> srcShape) {
460
461 SmallVector<int64_t> bcastDims;
462 size_t dimDiff = resShape.size() - srcShape.size();
463 auto bcastSourceLayout = resLayout;
464
465 // Right-aligned source in result, look for stretched unit dims.
466 for (size_t i = dimDiff; i < resShape.size(); i++) {
467 if ((srcShape[i - dimDiff] == 1) && (resShape[i] != 1))
468 bcastDims.push_back(i);
469 }
470
471 // Case UnitDimStretch (e.g., 1x4 -> 4x4): the source layout data field must
472 // be 1.
473 if (!bcastDims.empty())
474 bcastSourceLayout = bcastSourceLayout.setUnitDimData(bcastDims);
475
476 // Case RankDiff:
477 if (dimDiff) {
478 SmallVector<int64_t> sliceDims;
479 bool isOuterDimDiffUnitDims = llvm::all_of(
480 resShape.take_front(dimDiff), [&](int64_t dim) { return dim == 1; });
481 if (dimDiff && bcastDims.size() == dimDiff && isOuterDimDiffUnitDims) {
482 // Case RankDiffInnerDims (e.g., 1x4 -> 1x16x4):
483 // slice the expanded inner dims
484 sliceDims.assign(bcastDims.begin(), bcastDims.end());
485 } else {
486 // Case RankDiffOuterDims (e.g., 1x4 -> 1x1x4):
487 // slice the outer dims
488 llvm::append_range(sliceDims, llvm::seq<int64_t>(0, dimDiff));
489 }
490 bcastSourceLayout = xegpu::SliceAttr::get(
491 resLayout.getContext(), bcastSourceLayout,
492 DenseI64ArrayAttr::get(resLayout.getContext(), sliceDims));
493 }
494 return bcastSourceLayout;
495}
496
497/// Infers the source layout attribute for a reduction operation given the
498/// result layout attribute and reduced dims.
499xegpu::DistributeLayoutAttr
500xegpu::inferMultiReductionSourceLayout(xegpu::DistributeLayoutAttr resLayout,
501 SmallVector<int64_t> reduceDims) {
502
503 assert(isa<xegpu::SliceAttr>(resLayout) &&
504 "reduction result layout must be slice layout");
505
506 xegpu::SliceAttr sliceLayout = dyn_cast<xegpu::SliceAttr>(resLayout);
507
508 assert((reduceDims == sliceLayout.getDims().asArrayRef()) &&
509 "reduction dims must match with slice dims");
510
511 return sliceLayout.getParent();
512}
513
514xegpu::DistributeLayoutAttr
515xegpu::inferReductionSourceLayout(xegpu::DistributeLayoutAttr resLayout) {
516 return xegpu::inferMultiReductionSourceLayout(resLayout, {0});
517}
518
519/// Infers the source layout attribute for a transpose operation given the
520/// result layout attribute and permutation.
521///
522/// vector.transpose semantics is `result[i] = source[permutation[i]]`, so
523/// `result_layout[i] = source_layout[permutation[i]]`. To recover the source
524/// layout from the result layout we must apply the inverse permutation.
525xegpu::DistributeLayoutAttr
526xegpu::inferTransposeSourceLayout(xegpu::DistributeLayoutAttr resLayout,
527 ArrayRef<int64_t> permutation) {
529 invertPermutationVector(permutation);
530 return resLayout.transposeDims(inversePermutation);
531}
532
533/// Infers the source layout attribute for a bitcast operation given the
534/// result layout attribute, result element type bitwidth, and source element
535/// type bitwidth.
536xegpu::DistributeLayoutAttr
537xegpu::inferBitCastSourceLayout(xegpu::DistributeLayoutAttr resLayout,
538 int resElemTyBitWidth, int srcElemTyBitWidth) {
539
540 SmallVector<int64_t> sgData = resLayout.getEffectiveSgDataAsInt();
541 SmallVector<int64_t> instData = resLayout.getEffectiveInstDataAsInt();
542 SmallVector<int64_t> laneData = resLayout.getEffectiveLaneDataAsInt();
543 size_t sgDataSize = sgData.size();
544 size_t instDataSize = instData.size();
545 size_t laneDataSize = laneData.size();
546 int64_t sgDataValue = -1;
547 int64_t instDataValue = -1;
548 int64_t laneDataValue = -1;
549 int64_t dim = resLayout.getRank() - 1;
550
551 if (srcElemTyBitWidth <= resElemTyBitWidth) {
552 int bitWidthRatio = resElemTyBitWidth / srcElemTyBitWidth;
553 if (sgDataSize)
554 sgDataValue = sgData.back() * bitWidthRatio;
555 if (instDataSize)
556 instDataValue = instData.back() * bitWidthRatio;
557 if (laneDataSize)
558 laneDataValue = laneData.back() * bitWidthRatio;
559 } else {
560 int bitWidthRatio = srcElemTyBitWidth / resElemTyBitWidth;
561 if (sgDataSize) {
562 assert((sgData.back() % bitWidthRatio) == 0 &&
563 "sgData not divisible by bitWidthRatio");
564 sgDataValue = sgData.back() / bitWidthRatio;
565 }
566 if (instDataSize) {
567 assert((instData.back() % bitWidthRatio) == 0 &&
568 "instData not divisible by bitWidthRatio");
569 instDataValue = instData.back() / bitWidthRatio;
570 }
571 if (laneDataSize) {
572 assert((laneData.back() % bitWidthRatio) == 0 &&
573 "laneData not divisible by bitWidthRatio");
574 laneDataValue = laneData.back() / bitWidthRatio;
575 }
576 }
577
578 xegpu::DistributeLayoutAttr finalSrcLayout;
579 finalSrcLayout =
580 resLayout.setDimData(dim, sgDataValue, instDataValue, laneDataValue);
581
582 return finalSrcLayout;
583}
584
585/// Infers the source layout attribute for an interleave operation given the
586/// result layout attribute. Interleave doubles the size of the innermost
587/// dimension, so the layout inference is similar to bitcast where the source
588/// element type is larger than the result element type (ratio = 2).
589xegpu::DistributeLayoutAttr
590xegpu::inferInterleaveSourceLayout(xegpu::DistributeLayoutAttr resLayout) {
591
592 SmallVector<int64_t> sgData = resLayout.getEffectiveSgDataAsInt();
593 SmallVector<int64_t> instData = resLayout.getEffectiveInstDataAsInt();
594 SmallVector<int64_t> laneData = resLayout.getEffectiveLaneDataAsInt();
595 size_t sgDataSize = sgData.size();
596 size_t instDataSize = instData.size();
597 size_t laneDataSize = laneData.size();
598 int64_t sgDataValue = -1;
599 int64_t instDataValue = -1;
600 int64_t laneDataValue = -1;
601 int64_t dim = resLayout.getRank() - 1;
602
603 // Interleave doubles the innermost dimension, so we need to halve the
604 // layout values (similar to bitcast with ratio = 2)
605 constexpr int ratio = 2;
606 if (sgDataSize) {
607 assert((sgData.back() % ratio) == 0 &&
608 "sgData not divisible by interleave ratio");
609 sgDataValue = sgData.back() / ratio;
610 }
611 if (instDataSize) {
612 assert((instData.back() % ratio) == 0 &&
613 "instData not divisible by interleave ratio");
614 instDataValue = instData.back() / ratio;
615 }
616 if (laneDataSize) {
617 assert((laneData.back() % ratio) == 0 &&
618 "laneData not divisible by interleave ratio");
619 laneDataValue = laneData.back() / ratio;
620 }
621
622 return resLayout.setDimData(dim, sgDataValue, instDataValue, laneDataValue);
623}
624
625/// Infers the source layout attribute for a deinterleave operation given the
626/// result layout attribute. Deinterleave halves the size of the innermost
627/// dimension, so the layout inference is similar to bitcast where the source
628/// element type is smaller than the result element type (ratio = 2).
629xegpu::DistributeLayoutAttr
630xegpu::inferDeinterleaveSourceLayout(xegpu::DistributeLayoutAttr resLayout) {
631
632 SmallVector<int64_t> sgData = resLayout.getEffectiveSgDataAsInt();
633 SmallVector<int64_t> instData = resLayout.getEffectiveInstDataAsInt();
634 SmallVector<int64_t> laneData = resLayout.getEffectiveLaneDataAsInt();
635 size_t sgDataSize = sgData.size();
636 size_t instDataSize = instData.size();
637 size_t laneDataSize = laneData.size();
638 int64_t sgDataValue = -1;
639 int64_t instDataValue = -1;
640 int64_t laneDataValue = -1;
641 int64_t dim = resLayout.getRank() - 1;
642
643 // Deinterleave halves the innermost dimension, so we need to double the
644 // layout values (similar to bitcast with ratio = 2)
645 constexpr int ratio = 2;
646 if (sgDataSize)
647 sgDataValue = sgData.back() * ratio;
648 if (instDataSize)
649 instDataValue = instData.back() * ratio;
650 if (laneDataSize)
651 laneDataValue = laneData.back() * ratio;
652
653 return resLayout.setDimData(dim, sgDataValue, instDataValue, laneDataValue);
654}
655
656/// Infers the source layout attribute for an insert strided slice operation
657/// given the result layout attribute, result shape, and source shape. Removes
658/// leading dimensions from the result layout to match the source shape size.
659xegpu::DistributeLayoutAttr xegpu::inferInsertStridedSliceSourceLayout(
660 xegpu::DistributeLayoutAttr resLayout, ArrayRef<int64_t> resShape,
661 ArrayRef<int64_t> srcShape) {
662
663 int srcShapeSize = srcShape.size();
664 int resShapeSize = resShape.size();
665 int dimDiff = resShapeSize - srcShapeSize;
666
667 if (dimDiff > 0) {
668 // assert that the leading dimensions being sliced off are not distributed
669 // (i.e. sg_layout and lane_layout for those dimensions are all 1)
670 auto resSgLayout = resLayout.getEffectiveSgLayoutAsInt();
671 auto resLaneLayout = resLayout.getEffectiveLaneLayoutAsInt();
672 for (int i = 0; i < dimDiff; i++) {
673 assert((resSgLayout.size() == 0 || resSgLayout[i] == 1) &&
674 (resLaneLayout.size() == 0 || resLaneLayout[i] == 1) &&
675 "Leading dimensions being sliced off must not be distributed");
676 }
677 return resLayout.dropDims(llvm::to_vector(llvm::seq<int64_t>(0, dimDiff)));
678 }
679 return resLayout;
680}
681
682/// Infers the source layout attribute for an insert operation
683/// given the result layout attribute, result shape, and source shape. Removes
684/// leading dimensions from the result layout to match the source shape size.
685// TODO: add propagation support for insert op
686xegpu::DistributeLayoutAttr
687xegpu::inferInsertSourceLayout(xegpu::DistributeLayoutAttr resLayout,
688 ArrayRef<int64_t> resShape,
689 ArrayRef<int64_t> srcShape) {
690
691 int srcShapeSize = srcShape.size();
692 int resShapeSize = resShape.size();
693 int dimDiff = resShapeSize - srcShapeSize;
694
695 if (dimDiff > 0) {
696 // assert that the leading dimensions being sliced off are not distributed
697 // (i.e. sg_layout and lane_layout for those dimensions are all 1)
698 auto resSgLayout = resLayout.getEffectiveSgLayoutAsInt();
699 auto resLaneLayout = resLayout.getEffectiveLaneLayoutAsInt();
700 for (int i = 0; i < dimDiff; i++) {
701 assert((resSgLayout.size() == 0 || resSgLayout[i] == 1) &&
702 (resLaneLayout.size() == 0 || resLaneLayout[i] == 1) &&
703 "Leading dimensions being sliced off must not be distributed");
704 }
705 return resLayout.dropDims(llvm::to_vector(llvm::seq<int64_t>(0, dimDiff)));
706 }
707 return resLayout;
708}
709
710/// Infers the source layout attribute for extract operation
711/// given the result layout attribute, result shape, and source shape. Adds
712/// leading dimensions to the source layout to match the source shape size.
713// TODO: add layout attribute interface: expandDim() and use it here.
714// TODO: add propagation support for extract op
715xegpu::DistributeLayoutAttr
716xegpu::inferExtractSourceLayout(xegpu::DistributeLayoutAttr resLayout,
717 ArrayRef<int64_t> resShape,
718 ArrayRef<int64_t> srcShape) {
719
720 int srcShapeSize = srcShape.size();
721 int resShapeSize = resShape.size();
722 int dimDiff = srcShapeSize - resShapeSize;
723 auto context = resLayout.getContext();
724 // construct the source layout by adding unit dimensions to the front of
725 // result layout
726 if (dimDiff > 0) {
727 auto sgLayout = resLayout.getEffectiveSgLayoutAsInt();
728 auto sgData = resLayout.getEffectiveSgDataAsInt();
729 auto instData = resLayout.getEffectiveInstDataAsInt();
730 auto laneLayout = resLayout.getEffectiveLaneLayoutAsInt();
731 auto laneData = resLayout.getEffectiveLaneDataAsInt();
732 auto order = resLayout.getEffectiveOrderAsInt();
733
734 // Example: result shape is 3D with order [1, 2, 0], source shape is 5D
735 // (adding 2 leading dimensions). Expected source order: [3, 4, 2, 1, 0]
736 // Step 1: shift existing order by dimDiff: [1, 2, 0] -> [3, 4, 2]
737 // Step 2: append new leading dims in reverse (slowest first): [3, 4, 2, 1,
738 // 0]
739
740 // Shift existing dimension indices in order by dimDiff to account for the
741 // new leading dimensions being added to the source shape
742 for (auto &o : order)
743 o += dimDiff;
744
745 // Add unit dimensions to the front of non-empty layout vectors and append
746 // the new dimension indices to the order array in reverse (slowest
747 // dimension has the lowest index and appears last in the order array)
748 for (int i = 0; i < dimDiff; i++) {
749 if (!sgLayout.empty())
750 sgLayout.insert(sgLayout.begin(), 1);
751 if (!sgData.empty())
752 sgData.insert(sgData.begin(), 1);
753 if (!instData.empty())
754 instData.insert(instData.begin(), 1);
755 if (!laneLayout.empty())
756 laneLayout.insert(laneLayout.begin(), 1);
757 if (!laneData.empty())
758 laneData.insert(laneData.begin(), 1);
759 order.push_back(dimDiff - 1 - i);
760 }
761
763 context, SmallVector<int32_t>(order.begin(), order.end()));
764 if (!resLayout.getOrder())
765 orderAttr = nullptr;
766
767 return buildLayout(context, sgLayout, sgData, instData, laneLayout,
768 laneData, orderAttr);
769 }
770 return resLayout;
771}
772
773/// Infers the source layout attribute for a shape cast operation given the
774/// result layout attribute, result shape, and source shape.
775xegpu::DistributeLayoutAttr
776xegpu::inferShapeCastSourceLayout(xegpu::DistributeLayoutAttr resLayout,
777 ArrayRef<int64_t> resShape,
778 ArrayRef<int64_t> srcShape) {
779
780 // There are three use cases:
781 // 1. expand dims of low-rank dimensions (e.g., 1D to 2D): to set up the
782 // tensor before broadcast
783 // 2. split dim of a high-rank dimension (e.g., 1D to 2D): to setup tensor
784 // for multi-stage reduction
785 // 3. combines all dims to a single dim and put in the innermost dim in 2d as
786 // [1, combinedData] or [combinedData]. Say, [2, 4, 8] -> [1, 64] or [64]
787 // Use cases are only supported after workgroup distribution,
788 // like cross-sg reduction saves multidimension data to
789 // 1D slm buffer, shapecast inserted by cse/canonicalization passes.
790
791 // Use case 1: Shapes only differ by expanding unit dimensions, for broadcast
792 SmallVector<int64_t> expandedUnitDims;
793
794 if (xegpu::matchUnitDimExpansion(srcShape, resShape, expandedUnitDims)) {
795 // create a slice layout for the source by removing the expanded unit dims
796 auto sliceDimsAttr = DenseI64ArrayAttr::get(
797 resLayout.getContext(), ArrayRef<int64_t>(expandedUnitDims));
798 auto srcLayout =
799 xegpu::SliceAttr::get(resLayout.getContext(), resLayout, sliceDimsAttr);
800 return srcLayout;
801 }
802
803 // Use case 2: Dim split from source to result, for multi-stage reduction
804 SmallVector<SmallVector<int64_t>> splitDimGroups;
805 if (xegpu::matchSplitDimExpansion(srcShape, resShape, splitDimGroups)) {
806 auto srcLayout = resLayout;
807 for (const auto &dimGroup : splitDimGroups)
808 srcLayout = srcLayout.collapseDims(dimGroup);
809
810 return srcLayout;
811 }
812
813 // Use case 3: General dim collapse, for cross-sg reduction to SLM and other
814 // shape casts where consecutive src dims fold into a single dst dim.
816 if (xegpu::matchDimCollapse(srcShape, resShape, collapseDims)) {
817 auto srcLayout = resLayout;
818 for (int64_t dstIdx = static_cast<int64_t>(collapseDims.size()) - 1;
819 dstIdx >= 0; --dstIdx) {
820 ArrayRef<int64_t> srcDims = collapseDims[dstIdx];
821 if (srcDims.empty()) {
822 srcLayout = srcLayout.dropDims({dstIdx});
823 continue;
824 }
825 if (srcDims.size() == 1)
826 continue;
827 SmallVector<int64_t> targetShape;
828 targetShape.reserve(srcDims.size());
829 for (int64_t d : srcDims)
830 targetShape.push_back(srcShape[d]);
831 srcLayout = srcLayout.expandDim(dstIdx, targetShape);
832 }
833 return srcLayout;
834 }
835 return nullptr;
836}
837
838/// Infers the layout attribute for mask and offset operand for Chunked load
839/// and store, given the anchor layout attribute for the value being load/store.
840xegpu::DistributeLayoutAttr xegpu::inferMaskOffsetLayoutForScatterIO(
841 xegpu::DistributeLayoutAttr payloadLayout, int chunkSize) {
842 auto rank = payloadLayout.getRank();
843 if (chunkSize > 1)
844 return payloadLayout.dropDims(
845 llvm::to_vector(llvm::seq<int64_t>(rank - 1, rank)));
846 return payloadLayout;
847}
848
849//===----------------------------------------------------------------------===//
850// Layout derivation helpers: factorize sgCount into
851// sg_layout candidates, then
852// compute per-subgroup (sgData) and per-lane
853// (lane_layout/lane_data/inst_data).
854//===----------------------------------------------------------------------===//
855
857
858/// Enumerates all ways to split `total` into `rank` factors whose product
859/// equals `total`. Returns the list of all such factorizations.
861 int64_t rank) {
863 SmallVector<int64_t> current(rank, 0);
864
865 // Returns all divisors of `n` in ascending order.
866 auto getDivisors = [](int64_t n) {
868 for (int64_t i = 1; i * i <= n; ++i) {
869 if (n % i == 0) {
870 divs.push_back(i);
871 if (i != n / i)
872 divs.push_back(n / i);
873 }
874 }
875 llvm::sort(divs);
876 return divs;
877 };
878
879 std::function<void(int64_t, int64_t)> generate = [&](int64_t dim,
880 int64_t remaining) {
881 if (dim == rank - 1) {
882 current[dim] = remaining;
883 results.push_back(LayoutRepresentation(current));
884 return;
885 }
886 for (int64_t factor : getDivisors(remaining)) {
887 current[dim] = factor;
888 generate(dim + 1, remaining / factor);
889 }
890 };
891
892 generate(0, total);
893 return results;
894}
895
896// Computes all valid N-dimensional sg_layout candidates for the given
897// sgCount, whose sgData (= wgShape / sgLayout):
898// 1. Evenly divides wgShape (i.e., wgShape[d] % sgLayout[d] == 0).
899// 2. Is a multiple of instData (i.e., sgData[d] % instData[d] == 0).
900// Results are sorted by balance (smallest max-min spread first), with
901// lexicographic order as a tiebreaker.
902//
903// Example (2D):
904// wgShape = [128, 64], instData = [8, 16], sgCount = 32
905// Returns: [[8,4], [16,2]], corresponding to sgData [16,16] and [8,32].
908 int64_t sgCount) {
909 int64_t rank = wgShape.size();
910 assert(rank > 0 && "wgShape must be non-empty");
911 assert(static_cast<int64_t>(instData.size()) == rank &&
912 "instData rank must match wgShape rank");
913
914 // Step 1: Get all N-D factorizations of sgCount.
915 auto allFactorizations = enumerateFactorizations(sgCount, rank);
916
917 // Step 2: Filter to keep only valid candidates.
919 for (const auto &sgLayout : allFactorizations) {
920 bool valid = true;
921 for (int64_t dim = 0; dim < rank; ++dim) {
922 if (wgShape[dim] % sgLayout[dim] != 0) {
923 valid = false;
924 break;
925 }
926 int64_t sgData = wgShape[dim] / sgLayout[dim];
927 if (sgData % instData[dim] != 0) {
928 valid = false;
929 break;
930 }
931 }
932 if (valid)
933 candidates.push_back(sgLayout);
934 }
935
936 // Step 3: Sort by balance (smallest max-min spread), then lexicographic.
937 llvm::sort(candidates, [](const LayoutRepresentation &lhs,
938 const LayoutRepresentation &rhs) {
939 int64_t spreadLhs = *llvm::max_element(lhs) - *llvm::min_element(lhs);
940 int64_t spreadRhs = *llvm::max_element(rhs) - *llvm::min_element(rhs);
941 if (spreadLhs != spreadRhs)
942 return spreadLhs < spreadRhs;
943 return lhs < rhs;
944 });
945 return candidates;
946}
947
948/// Helper function to compute inst_data vectors for DPAS operands A, B, and
949/// C/D.
950static std::optional<SmallVector<int64_t>> get2DBlockIOInstDataLayout(
951 ArrayRef<int64_t> dataShape, Type elemTy,
952 const xegpu::uArch::BlockIOInstructionInterface *uArchInstruction,
953 bool transform = false, bool transpose = false) {
954 int rank = dataShape.size();
955 auto blockWHC =
956 uArchInstruction->getBlockWidthHeightCount(elemTy, transform, transpose);
957 if (!blockWHC)
958 return std::nullopt;
959 auto [bWidths, bHeights, bCounts] = blockWHC.value();
960 // Compute inst_data from hardware block params. For Nd ops, the lane
961 // factorization above (laneLayout / laneData) is rigid; inst_data must be
962 // a multiple of lane_layout * lane_data on each dim (Category A
963 // invariant).
964 SmallVector<int64_t> instData(rank, 1);
965 assert(rank >= 2 && "dataShape must be at least 2D for 2D-block IO");
966 int instWidth =
967 xegpu::getLargestDivisor(static_cast<int>(dataShape.back()), bWidths);
968 int instHeight =
969 xegpu::getLargestDivisor(static_cast<int>(dataShape[rank - 2]), bHeights);
970 instData.back() = instWidth;
971 instData[rank - 2] = instHeight;
972
973 return instData;
974}
975
976/// Helper function to compute inst_data vectors for DPAS operands A, B, and
977/// C/D. Look up the uArch table and search for the largest supported block size
978/// that divides the data shape
979static std::optional<std::tuple<SmallVector<int64_t>, SmallVector<int64_t>,
982 VectorType aTy, VectorType bTy, VectorType cdTy,
983 const xegpu::uArch::MMAInstructionInterface *uArchInstruction) {
984
985 // M dimension is the second-to-last dim of A (handles batch dims).
986 const unsigned dataALen = aTy.getShape()[aTy.getRank() - 2];
987 auto supportedALen = uArchInstruction->getSupportedM(aTy.getElementType());
988 const int maxALen =
989 xegpu::getLargestDivisor(dataALen, ArrayRef<unsigned>(supportedALen));
990
991 // N dimension is the last dim of B.
992 const unsigned dataBLen = bTy.getShape().back();
993 auto supportedBLen = uArchInstruction->getSupportedN(bTy.getElementType());
994 const int maxBLen =
995 xegpu::getLargestDivisor(dataBLen, ArrayRef<unsigned>(supportedBLen));
996
997 auto supportedCLen = uArchInstruction->getSupportedN(cdTy.getElementType());
998 const int maxCLen =
999 xegpu::getLargestDivisor(dataBLen, ArrayRef<unsigned>(supportedCLen));
1000 if (maxALen == -1 || maxBLen == -1 || maxCLen == -1)
1001 return std::nullopt;
1002
1003 auto supportedKLen = uArchInstruction->getSupportedK(aTy.getElementType());
1004 if (supportedKLen.empty())
1005 return std::nullopt;
1006 auto kDimSize = supportedKLen[0];
1007
1008 SmallVector<int64_t> instDataA(aTy.getRank(), 1);
1009 instDataA[aTy.getRank() - 2] = maxALen;
1010 instDataA[aTy.getRank() - 1] = kDimSize;
1011 SmallVector<int64_t> instDataB(bTy.getRank(), 1);
1012 instDataB[bTy.getRank() - 2] = kDimSize;
1013 instDataB[bTy.getRank() - 1] = maxBLen;
1014 SmallVector<int64_t> instDataCD(cdTy.getRank(), 1);
1015 instDataCD[cdTy.getRank() - 2] = maxALen;
1016 instDataCD[cdTy.getRank() - 1] = maxCLen;
1017 return std::make_tuple(instDataA, instDataB, instDataCD);
1018}
1019
1020/// Computes lane_layout and lane_data for scatter-style store anchor layouts
1021/// (store scatter, store matrix). Lanes and the per-lane vector both live on
1022/// the innermost dim:
1023/// - laneLayout[innermost] = min(subgroupSize, srcShape[innermost])
1024/// - laneData[innermost] = min(srcShape[innermost] / laneLayout[innermost],
1025/// maxChunkSize)
1026/// All other entries are 1.
1027static std::pair<SmallVector<int64_t>, SmallVector<int64_t>>
1029 int64_t subgroupSize, int64_t maxChunkSize) {
1030 int64_t rank = instShape.size();
1031 SmallVector<int64_t> laneLayout(rank, 1), laneData(rank, 1);
1032 int64_t innermost = rank - 1;
1033 laneLayout[innermost] = std::min(subgroupSize, instShape[innermost]);
1034 laneData[innermost] =
1035 std::min(instShape[innermost] / laneLayout[innermost], maxChunkSize);
1036 return {laneLayout, laneData};
1037}
1038
1039// Computes the per-lane layout and data for a 2D block load/store/prefetch:
1040// lanes are spread across the subgroup along the last dim (or rank-2 if
1041// transposed), and laneData packs sub-bitwidth elements along the packing dim.
1042static std::pair<SmallVector<int64_t>, SmallVector<int64_t>>
1044 int64_t subgroupSize, int64_t bitwidth,
1045 int64_t packingSize, bool transform = false) {
1046 int64_t rank = instShape.size();
1047 SmallVector<int64_t> laneLayout(rank, 1), laneData(rank, 1);
1048 int kDim = transform ? rank - 2 : rank - 1;
1049 unsigned vnniFactor = packingSize / bitwidth;
1050 laneData[kDim] = bitwidth < packingSize ? vnniFactor : 1;
1051 laneLayout.back() =
1052 std::min(subgroupSize, instShape.back() / laneData.back());
1053
1054 // assert that the lane layout and data fit in the inst shape
1055 for (int64_t i = 0; i < rank; ++i) {
1056 int64_t laneProduct = laneLayout[i] * laneData[i];
1057 assert(instShape[i] % laneProduct == 0 &&
1058 "lane_layout * lane_data must evenly divide the inst shape");
1059 (void)laneProduct;
1060 }
1061 return {laneLayout, laneData};
1062}
1063
1064/// Computes the (lane_layout, lane_data) for a multi-reduction's source layout.
1065/// Only the innermost two dims are distributed; leading dims are assumed unit.
1066/// `subgroupSize` lanes go on one dim; up to `maxReduceVectorSize` elements are
1067/// packed into lane_data on the other. To minimize cross-lane reduction, lanes
1068/// are spread across a non-reduction dim when possible so the reduction happens
1069/// within a lane. inst_data is the element-wise product lane_layout *
1070/// lane_data.
1071///
1072/// e.g. with srcShape=[32, 128], subgroupSize=16, maxReduceVectorSize=2:
1073/// - Switch: reductionDims=[1] and consumerReductionDims=[] -> lanes move
1074/// to the non-reduction dim 0: lane_layout=[16, 1], lane_data=[1, 2].
1075/// - Default: reductionDims=[0, 1] (both reduced) -> lanes stay on the
1076/// innermost dim: lane_layout=[1, 16], lane_data=[2, 1].
1077static std::pair<SmallVector<int64_t>, SmallVector<int64_t>>
1079 ArrayRef<int64_t> reductionDims,
1080 int subgroupSize, int64_t maxReduceVectorSize,
1081 bool verticalLaneLayout = false) {
1082 int srcRank = srcShape.size();
1083 SmallVector<int64_t> laneLayout(srcRank, 1), laneData(srcRank, 1);
1084
1085 int innermost = srcRank - 1;
1086 int secondInnermost = srcRank - 2;
1087
1088 if (verticalLaneLayout && secondInnermost >= 0) {
1089 std::swap(innermost, secondInnermost);
1090 }
1091 int laneDim = innermost;
1092 int vectorDim = secondInnermost; // negative for rank 1
1093
1094 laneLayout[laneDim] =
1095 std::min(static_cast<int64_t>(subgroupSize), srcShape[laneDim]);
1096 if (vectorDim >= 0)
1097 laneData[vectorDim] = std::min(maxReduceVectorSize, srcShape[vectorDim]);
1098
1099 return {laneLayout, laneData};
1100}
1101
1102//===----------------------------------------------------------------------===//
1103// Result/anchor-layout setup. Each op category derives lane_layout/lane_data
1104// (and inst_data / sgData) differently. Two things vary across ops:
1105//
1106// * Consumer dependence: consumer-driven ops prefer the layout requested by
1107// their downstream uses and fall back to uArch defaults only when it is
1108// absent/invalid; sinks (StoreNd, PrefetchNd) have no consumer and always
1109// pick their own layout from uArch.
1110//
1111// * Derivation direction between inst_data and lane_layout/lane_data. Both
1112// obey the invariant inst_data = k * lane_layout * lane_data, where `k` is
1113// a per-dim integer >= 1 giving how many times each lane repeats its
1114// access to cover one instruction's data tile (k == 1 means one lane
1115// position per element; k > 1 means the instruction loads/stores several
1116// elements per lane along that dim). Ops solve this invariant from
1117// opposite ends:
1118// - Rigid-lane ops (Nd block IO, DPAS): hardware fixes lane_layout /
1119// lane_data first, then inst_data is built as a multiple of their
1120// product (using get2DBlockIOInstDataLayout / getDpasInstDataLayouts).
1121// - inst_data-first ops (scatter load): take inst_data from the consumer
1122// and derive lane_layout/lane_data underneath it.
1123//
1124// - DPAS (+DPAS_MX) : rigid lanes — inst_data from HW block dims; A/B/C/D
1125// lanes/data follow each operand's matmul role; DPAS_MX
1126// additionally lays out the scale operand.
1127// - LoadNd : consumer-driven, rigid lanes — honors the consumer's
1128// inst_data / lane / sg_layout (incl. transpose & VNNI
1129// packing) when it satisfies uArch block constraints,
1130// else falls back to the default 2D-block scheme (lanes
1131// on the last dim, rank-2 if transposed). The fallback
1132// picks the LARGEST uArch block that divides the data
1133// shape, so the resulting inst_data block can be bigger
1134// than what the consumer asked for (fewer, wider
1135// loads).
1136// - StoreNd/PrefetchNd: data sinks, no consumer, rigid lanes — pick the
1137// 2D-block layout directly from uArch (no VNNI
1138// packing).
1139// - Load (scatter) : load_gather / load_matrix, consumer-driven,
1140// inst_data-first — reuse the consumer's inst_data and
1141// derive lane_layout/lane_data, else default to lanes +
1142// per-lane chunk on the innermost dim (chunk capped by
1143// maxChunkSize).
1144// - Store (scatter) : store_scatter / store_matrix — same scatter scheme,
1145// but always self-derived from the scatter default.
1146// - Reduction : (multi_)reduction, consumer-driven — distribute the
1147// inner two dims, with lanes on the innermost dim by
1148// default (reducing across lanes) and switched to a
1149// non-reduction dim only when that keeps the reduction
1150// within a lane. Reuses the consumer's slice layout
1151// when it slices exactly the reduction dims, otherwise
1152// re-derives. See setupMultiReductionResultLayout for
1153// the exact switch condition and worked examples.
1154// - BitCast/Interleave: scale the innermost data field by the bitwidth /
1155// interleave ratio so the source layout divides back
1156// out.
1157// - InsertStridedSlice: clamp lane_data per dim to fit the inserted slice
1158// (Lane kind only; sg/inst layouts unsupported).
1159//===----------------------------------------------------------------------===//
1160
1161/// Helper function to set up subgroup layouts for DPAS operands A, B, and
1162/// C/D. Compute subgroup layout candidates based on wgtile and instData, and
1163/// then pick the best one that satisfies all operands and the consumer (if
1164/// specified).
1165static std::optional<
1166 std::tuple<xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr,
1167 xegpu::DistributeLayoutAttr>>
1169 mlir::MLIRContext *context, VectorType aTy, VectorType bTy, VectorType cdTy,
1170 xegpu::DistributeLayoutAttr consumerLayout, int numSg,
1172 instDataVecs) {
1173 auto [instDataA, instDataB, instDataCD] = instDataVecs;
1174
1175 std::optional<LayoutRepresentation> consumerSgLayout = std::nullopt;
1176 if (consumerLayout && consumerLayout.isForWorkgroup()) {
1177 consumerSgLayout = consumerLayout.getEffectiveSgLayoutAsInt();
1178 }
1179
1180 // Get all valid layouts for A, B and C/D operands
1181 auto layoutsA = getSgLayoutCandidates(aTy.getShape(), instDataA, numSg);
1182 auto layoutsB = getSgLayoutCandidates(bTy.getShape(), instDataB, numSg);
1183 auto layoutsCD = getSgLayoutCandidates(cdTy.getShape(), instDataCD, numSg);
1184 if (layoutsA.empty() || layoutsB.empty() || layoutsCD.empty())
1185 return std::nullopt;
1186
1187 // Pick the best subgroup layout
1188 std::optional<LayoutRepresentation> bestPick;
1189 auto checkAlignedSgDataAB = [&](const LayoutRepresentation &sgLayout) {
1190 return aTy.getShape().back() / sgLayout[1] ==
1191 bTy.getShape().front() / sgLayout[0];
1192 };
1193 for (auto &sgLayout : layoutsB) {
1194 if (llvm::is_contained(layoutsA, sgLayout) &&
1195 llvm::is_contained(layoutsCD, sgLayout)) {
1196 if (!checkAlignedSgDataAB(sgLayout))
1197 continue;
1198 // Is in (A and B and CD) and matches consumer -> best pick
1199 if (consumerSgLayout.has_value() && sgLayout == *consumerSgLayout) {
1200 bestPick = sgLayout;
1201 break;
1202 }
1203 // Is in (A and B and CD) layoutsB is ordered from most
1204 // balanced to least. So the first one we see is the most balanced one,
1205 // remember it and later only update if there is one that matches the
1206 // consumer.
1207 if (!bestPick)
1208 bestPick = sgLayout;
1209 }
1210 }
1211 if (!bestPick)
1212 return std::nullopt;
1213
1214 const auto &picked = *bestPick;
1215
1216 auto dpasALayout = buildSgLayout(context, aTy.getShape(), picked,
1217 /*dimK=*/aTy.getRank() - 1);
1218 auto dpasBLayout = buildSgLayout(context, bTy.getShape(), picked,
1219 /*dimK=*/bTy.getRank() - 2);
1220 auto dpasCDLayout = buildSgLayout(context, cdTy.getShape(), picked);
1221 return std::make_tuple(dpasALayout, dpasBLayout, dpasCDLayout);
1222}
1223
1224/// Sets up the anchor layouts for dpas operands (A, B, and C/D).
1225/// The numSg and consumerLayout (optional) are only used by sg layout
1226/// creation.
1227std::optional<
1228 std::tuple<xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr,
1229 xegpu::DistributeLayoutAttr>>
1230xegpu::setupDpasLayout(xegpu::LayoutKind layoutKind, VectorType aTy,
1231 VectorType bTy, VectorType cdTy,
1232 xegpu::DistributeLayoutAttr consumerLayout, int numSg,
1233 const xegpu::uArch::uArch *uArch) {
1234 auto context = aTy.getContext();
1235 const auto *uArchInstruction =
1236 dyn_cast<xegpu::uArch::SubgroupMatrixMultiplyAcc>(uArch->getInstruction(
1238 if (!uArchInstruction)
1239 return std::nullopt;
1240 auto subgroupSize = uArch->getSubgroupSize();
1241
1242 auto [laneLayoutA, laneDataA] = compute2DBlockIOLaneLayoutAndData(
1243 aTy.getShape(), subgroupSize,
1244 aTy.getElementType().getIntOrFloatBitWidth(),
1245 uArchInstruction->getPackedFormatBitSizeA());
1246 auto [laneLayoutB, laneDataB] = compute2DBlockIOLaneLayoutAndData(
1247 bTy.getShape(), subgroupSize,
1248 bTy.getElementType().getIntOrFloatBitWidth(),
1249 uArchInstruction->getPackedFormatBitSizeB(), /*vnni=*/true);
1250 auto [laneLayoutCD, laneDataCD] = compute2DBlockIOLaneLayoutAndData(
1251 cdTy.getShape(), subgroupSize,
1252 cdTy.getElementType().getIntOrFloatBitWidth(),
1253 cdTy.getElementType().getIntOrFloatBitWidth());
1254
1255 auto instDataVecs = getDpasInstDataLayouts(aTy, bTy, cdTy, uArchInstruction);
1256 if (!instDataVecs)
1257 return std::nullopt;
1258
1259 if (layoutKind == xegpu::LayoutKind::Subgroup) {
1260 assert(numSg > 0 &&
1261 "Number of subgroups must be provided for sg layout creation.");
1262 return getDpasSubgroupLayouts(context, aTy, bTy, cdTy, consumerLayout,
1263 numSg, *instDataVecs);
1264 } else if (layoutKind == xegpu::LayoutKind::InstData) {
1265 auto [instDataA, instDataB, instDataCD] = *instDataVecs;
1266 return std::make_tuple(
1267 buildInstDataLayoutWithLane(context, instDataA, laneLayoutA, laneDataA),
1268 buildInstDataLayoutWithLane(context, instDataB, laneLayoutB, laneDataB),
1269 buildInstDataLayoutWithLane(context, instDataCD, laneLayoutCD,
1270 laneDataCD));
1271 } else if (layoutKind == xegpu::LayoutKind::Lane) {
1272 auto aLayout = buildLaneLayout(context, laneLayoutA, laneDataA);
1273 auto bLayout = buildLaneLayout(context, laneLayoutB, laneDataB);
1274 auto cdLayout = buildLaneLayout(context, laneLayoutCD, laneDataCD);
1275 return std::make_tuple(aLayout, bLayout, cdLayout);
1276 }
1277 return std::nullopt;
1278}
1279
1280/// Helper to create a scale layout derived from a matrix operand layout.
1281/// The scale layout is computed by mapping each dimension of the matrix
1282/// layout to the corresponding scale tensor dimension using the ratio
1283/// between the matrix and scale shapes.
1284static xegpu::DistributeLayoutAttr
1285createScaleLayout(mlir::MLIRContext *context, VectorType matrixTy,
1286 VectorType scaleTy, xegpu::DistributeLayoutAttr matrixLayout,
1287 bool isBScale, const xegpu::uArch::uArch *uArch) {
1288 if (!scaleTy || !matrixLayout)
1289 return nullptr;
1290
1291 // Calculate scaling factor by dividing matrix shape by scale shape
1292 ArrayRef<int64_t> matrixShape = matrixTy.getShape();
1293 ArrayRef<int64_t> scaleShape = scaleTy.getShape();
1294
1295 // Scale shapes can be 1D or 2D, handle both cases
1296 if (scaleShape.empty())
1297 return nullptr;
1298
1299 auto uArchInstruction =
1300 dyn_cast<xegpu::uArch::SubgroupScaledMatrixMultiplyAcc>(
1301 uArch->getInstruction(
1303
1304 int64_t rank = matrixLayout.getRank();
1305 assert(rank >= 2 && "dpas layouts must be at least two dimensions");
1306
1307 SmallVector<int64_t> sgLayout = matrixLayout.getEffectiveSgLayoutAsInt();
1308 SmallVector<int64_t> sgData = matrixLayout.getEffectiveSgDataAsInt();
1309 SmallVector<int64_t> instData = matrixLayout.getEffectiveInstDataAsInt();
1310 SmallVector<int64_t> laneLayout = matrixLayout.getEffectiveLaneLayoutAsInt();
1311 SmallVector<int64_t> laneData = matrixLayout.getEffectiveLaneDataAsInt();
1312 auto order = matrixLayout.getOrder();
1313
1314 SmallVector<int64_t> scaleSgLayout;
1315 SmallVector<int64_t> scaleSgData;
1316 if (!sgLayout.empty() && !sgData.empty()) {
1317 scaleSgLayout.assign(sgLayout.begin(), sgLayout.end());
1318 scaleSgData.assign(sgData.begin(), sgData.end());
1319 scaleSgData[rank - 2] = std::max<int64_t>(
1320 scaleShape[rank - 2] / (matrixShape[rank - 2] / sgData[rank - 2]), 1);
1321 scaleSgData[rank - 1] = std::max<int64_t>(
1322 scaleShape[rank - 1] / (matrixShape[rank - 1] / sgData[rank - 1]), 1);
1323 }
1324
1325 // For DPAS_MX scales: if matrix has inst_data, scale needs adjusted
1326 // inst_data. Scale inst_data is derived from matrix inst_data divided by
1327 // scale factor.
1328 SmallVector<int64_t> scaleInstData;
1329 if (!instData.empty()) {
1330 scaleInstData.assign(instData.begin(), instData.end());
1331 if (isBScale)
1332 scaleInstData[rank - 2] = std::max<int64_t>(
1333 scaleShape[rank - 2] / (matrixShape[rank - 2] / instData[rank - 2]),
1334 1);
1335 else
1336 scaleInstData[rank - 1] = std::max<int64_t>(
1337 scaleShape[rank - 1] / (matrixShape[rank - 1] / instData[rank - 1]),
1338 1);
1339 }
1340
1341 SmallVector<int64_t> scaleLaneLayout;
1342 SmallVector<int64_t> scaleLaneData;
1343 if (!laneLayout.empty() && !laneData.empty()) {
1344 scaleLaneLayout.assign(laneLayout.begin(), laneLayout.end());
1345 scaleLaneData.assign(laneData.size(), 1);
1346
1347 bool isRowMajor = uArchInstruction->isLaneLayoutRowMajorOrder();
1348 if (isBScale ^ isRowMajor)
1349 std::swap(scaleLaneLayout[rank - 2], scaleLaneLayout[rank - 1]);
1350 // Cap lane_layout by the per-instruction tile (inst_data) on each dim.
1351 // Then derive lane_data = inst_data / lane_layout so the Category A
1352 // invariant inst_data = lane_layout * lane_data * k (with k = 1) holds
1353 // for the scale operand's load_nd consumer.
1354 auto layoutCap = scaleInstData.empty() ? scaleShape : scaleInstData;
1355 for (int64_t d = rank - 2; d < rank; ++d)
1356 scaleLaneLayout[d] = std::min<int64_t>(layoutCap[d], scaleLaneLayout[d]);
1357 }
1358 return buildLayout(context, scaleSgLayout, scaleSgData, scaleInstData,
1359 scaleLaneLayout, scaleLaneData, order);
1360}
1361
1362/// Sets up the anchor layouts for dpas_mx operands (A, B, C/D, A_scale, and
1363/// B_scale). The numSg and consumerLayout (optional) are only used by sg
1364/// layout creation.
1365std::optional<
1366 std::tuple<xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr,
1367 xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr,
1368 xegpu::DistributeLayoutAttr>>
1369xegpu::setupDpasMxLayout(xegpu::LayoutKind layoutKind, VectorType aTy,
1370 VectorType bTy, VectorType cdTy, VectorType aScaleTy,
1371 VectorType bScaleTy,
1372 xegpu::DistributeLayoutAttr consumerLayout, int numSg,
1373 const xegpu::uArch::uArch *uArch) {
1374 auto context = aTy.getContext();
1375 const auto *uArchInstruction =
1376 dyn_cast<xegpu::uArch::SubgroupMatrixMultiplyAcc>(uArch->getInstruction(
1378 if (!uArchInstruction)
1379 return std::nullopt;
1380 auto subgroupSize = uArch->getSubgroupSize();
1381
1382 auto [laneLayoutA, laneDataA] = compute2DBlockIOLaneLayoutAndData(
1383 aTy.getShape(), subgroupSize,
1384 aTy.getElementType().getIntOrFloatBitWidth(),
1385 uArchInstruction->getPackedFormatBitSizeA());
1386 auto [laneLayoutB, laneDataB] = compute2DBlockIOLaneLayoutAndData(
1387 bTy.getShape(), subgroupSize,
1388 bTy.getElementType().getIntOrFloatBitWidth(),
1389 uArchInstruction->getPackedFormatBitSizeB(), /*vnni=*/true);
1390 auto [laneLayoutCD, laneDataCD] = compute2DBlockIOLaneLayoutAndData(
1391 cdTy.getShape(), subgroupSize,
1392 cdTy.getElementType().getIntOrFloatBitWidth(),
1393 cdTy.getElementType().getIntOrFloatBitWidth());
1394 auto instDataVecs = getDpasInstDataLayouts(aTy, bTy, cdTy, uArchInstruction);
1395 if (!instDataVecs)
1396 return std::nullopt;
1397
1398 if (layoutKind == xegpu::LayoutKind::Subgroup) {
1399 assert(numSg > 0 &&
1400 "Number of subgroups must be provided for sg layout creation.");
1401 auto dpasLayouts = getDpasSubgroupLayouts(
1402 context, aTy, bTy, cdTy, consumerLayout, numSg, *instDataVecs);
1403 if (!dpasLayouts)
1404 return std::nullopt;
1405
1406 auto [dpasALayout, dpasBLayout, dpasCDLayout] = *dpasLayouts;
1407
1408 // Create scale layouts
1409 auto aScaleLayout =
1410 createScaleLayout(context, aTy, aScaleTy, dpasALayout, false, uArch);
1411
1412 auto bScaleLayout =
1413 createScaleLayout(context, bTy, bScaleTy, dpasBLayout, true, uArch);
1414
1415 return std::make_tuple(dpasALayout, dpasBLayout, dpasCDLayout, aScaleLayout,
1416 bScaleLayout);
1417 } else if (layoutKind == xegpu::LayoutKind::InstData) {
1418
1419 auto [instDataA, instDataB, instDataCD] = *instDataVecs;
1420
1421 auto dpasALayout =
1422 buildInstDataLayoutWithLane(context, instDataA, laneLayoutA, laneDataA);
1423 auto dpasBLayout =
1424 buildInstDataLayoutWithLane(context, instDataB, laneLayoutB, laneDataB);
1425 auto dpasCDLayout = buildInstDataLayoutWithLane(context, instDataCD,
1426 laneLayoutCD, laneDataCD);
1427
1428 auto aScaleLayout =
1429 createScaleLayout(context, aTy, aScaleTy, dpasALayout, false, uArch);
1430 auto bScaleLayout =
1431 createScaleLayout(context, bTy, bScaleTy, dpasBLayout, true, uArch);
1432
1433 return std::make_tuple(dpasALayout, dpasBLayout, dpasCDLayout, aScaleLayout,
1434 bScaleLayout);
1435 } else if (layoutKind == xegpu::LayoutKind::Lane) {
1436 auto dpasALayout = buildLaneLayout(context, laneLayoutA, laneDataA);
1437 auto dpasBLayout = buildLaneLayout(context, laneLayoutB, laneDataB);
1438 auto dpasCDLayout = buildLaneLayout(context, laneLayoutCD, laneDataCD);
1439
1440 auto aScaleLayout =
1441 createScaleLayout(context, aTy, aScaleTy, dpasALayout, false, uArch);
1442 auto bScaleLayout =
1443 createScaleLayout(context, bTy, bScaleTy, dpasBLayout, true, uArch);
1444
1445 return std::make_tuple(dpasALayout, dpasBLayout, dpasCDLayout, aScaleLayout,
1446 bScaleLayout);
1447 }
1448 return std::nullopt;
1449}
1450
1451/// Sets up the anchor layout for a store_nd operation. StoreNd picks its
1452/// own layout based on uArch block parameters (it does not take a consumer
1453/// layout, since it is a data sink).
1454xegpu::DistributeLayoutAttr
1456 VectorType srcVecTy, int numSg,
1457 const xegpu::uArch::uArch *uArch) {
1458 const auto *uArchInstruction =
1459 dyn_cast<xegpu::uArch::Subgroup2DBlockStoreInstruction>(
1460 uArch->getInstruction(
1462 if (!uArchInstruction)
1463 return nullptr;
1464
1465 auto context = srcVecTy.getContext();
1466 Type elemTy = srcVecTy.getElementType();
1467 auto subgroupSize = uArch->getSubgroupSize();
1468 auto dataShape = srcVecTy.getShape();
1469 [[maybe_unused]] int rank = srcVecTy.getRank();
1470 assert(rank >= 2 && "Expected at least 2D shape for ND op");
1471
1472 // Compute the default 2D block IO lane layout / lane data.
1473 unsigned bitwidth = elemTy.getIntOrFloatBitWidth();
1474 auto [laneLayout, laneData] = compute2DBlockIOLaneLayoutAndData(
1475 dataShape, subgroupSize, bitwidth,
1476 uArchInstruction->getPackedFormatBitSize());
1477
1478 if (layoutKind == xegpu::LayoutKind::Lane)
1479 return buildLaneLayout(context, laneLayout, laneData);
1480
1481 auto instData =
1482 get2DBlockIOInstDataLayout(dataShape, elemTy, uArchInstruction);
1483
1484 if (layoutKind == xegpu::LayoutKind::InstData) {
1485 assert(instData && isValidLaneLayout(*instData, laneLayout, laneData) &&
1486 "Expected the store layout to satisfy uArch block constraints");
1487 return buildInstDataLayoutWithLane(context, *instData, laneLayout,
1488 laneData);
1489 }
1490
1491 if (layoutKind == xegpu::LayoutKind::Subgroup) {
1492 assert(numSg > 0 &&
1493 "Number of subgroups must be provided for sg layout creation.");
1494 auto sgLayouts = getSgLayoutCandidates(dataShape, *instData, numSg);
1495 if (sgLayouts.empty())
1496 return nullptr;
1497 return buildSgLayout(context, dataShape, sgLayouts.front(), /*dimK=*/-1);
1498 }
1499
1500 return nullptr;
1501}
1502
1503/// Sets up the anchor layout for a prefetch_nd operation. PrefetchNd has no
1504/// consumer (it produces no value), so it picks its own layout from uArch
1505/// block parameters.
1506xegpu::DistributeLayoutAttr
1508 xegpu::TensorDescType tdescTy, int numSg,
1509 const xegpu::uArch::uArch *uArch) {
1510
1511 const auto *uArchInstruction =
1512 dyn_cast<xegpu::uArch::Subgroup2DBlockPrefetchInstruction>(
1513 uArch->getInstruction(
1515 if (!uArchInstruction)
1516 return nullptr;
1517
1518 auto context = tdescTy.getContext();
1519 Type elemTy = tdescTy.getElementType();
1520 auto subgroupSize = uArch->getSubgroupSize();
1521 auto dataShape = tdescTy.getShape();
1522 [[maybe_unused]] int rank = tdescTy.getRank();
1523 assert(rank >= 2 && "Expected at least 2D shape for ND op");
1524
1525 // Compute the default 2D block IO lane layout / lane data.
1526 unsigned bitwidth = elemTy.getIntOrFloatBitWidth();
1527 auto [laneLayout, laneData] = compute2DBlockIOLaneLayoutAndData(
1528 dataShape, subgroupSize, bitwidth,
1529 uArchInstruction->getPackedFormatBitSize());
1530
1531 if (layoutKind == xegpu::LayoutKind::Lane)
1532 return buildLaneLayout(context, laneLayout, laneData);
1533
1534 auto instData =
1535 get2DBlockIOInstDataLayout(dataShape, elemTy, uArchInstruction);
1536
1537 if (layoutKind == xegpu::LayoutKind::InstData) {
1538 assert(instData && isValidLaneLayout(*instData, laneLayout, laneData) &&
1539 "Expected the prefetch layout to satisfy uArch block constraints");
1540 return buildInstDataLayoutWithLane(context, *instData, laneLayout,
1541 laneData);
1542 }
1543
1544 if (layoutKind == xegpu::LayoutKind::Subgroup) {
1545 assert(numSg > 0 &&
1546 "Number of subgroups must be provided for sg layout creation.");
1547 auto sgLayouts = getSgLayoutCandidates(dataShape, *instData, numSg);
1548 if (sgLayouts.empty())
1549 return nullptr;
1550 return buildSgLayout(context, dataShape, sgLayouts.front(), /*dimK=*/-1);
1551 }
1552
1553 return nullptr;
1554}
1555
1556/// Sets up the anchor layout for a load_nd operation. LoadNd takes a
1557/// consumer layout (from its result's downstream uses) and validates it
1558/// against uArch constraints; if valid, the consumer's `inst_data` /
1559/// `sg_layout` are honored. Otherwise the helper falls back to defaults
1560/// derived from uArch block parameters.
1561xegpu::DistributeLayoutAttr
1563 VectorType resVecTy,
1564 xegpu::DistributeLayoutAttr consumerLayout,
1565 int numSg, const xegpu::uArch::uArch *uArch) {
1566
1567 assert(consumerLayout && "Expected a valid consumer layout");
1568 if (layoutKind == xegpu::LayoutKind::Subgroup) {
1569 assert(consumerLayout.isForWorkgroup() &&
1570 "Expected consumer layout to be a complete workgroup-level layout");
1571 return consumerLayout;
1572 }
1573
1574 auto context = resVecTy.getContext();
1575 Type elemTy = resVecTy.getElementType();
1576 auto subgroupSize = uArch->getSubgroupSize();
1577 auto dataShape = resVecTy.getShape();
1578 const auto *uArchInstruction =
1579 dyn_cast<xegpu::uArch::Subgroup2DBlockLoadInstruction>(
1580 uArch->getInstruction(
1582 if (!uArchInstruction)
1583 return nullptr;
1584
1585 int rank = resVecTy.getRank();
1586 SmallVector<int64_t> consumerInstData =
1587 consumerLayout.getEffectiveInstDataAsInt();
1588 SmallVector<int64_t> consumerLaneLayout =
1589 consumerLayout.getEffectiveLaneLayoutAsInt();
1590 SmallVector<int64_t> consumerLaneData =
1591 consumerLayout.getEffectiveLaneDataAsInt();
1592 auto consumerOrderAttr = consumerLayout.getOrder();
1593
1594 assert(!consumerLaneLayout.empty() && !consumerLaneData.empty() &&
1595 "Expected consumer layout to have lane_layout and lane_data");
1596
1597 // vertical lane layout means that the blockload must be transposed
1598 // note scaleA on PVC has vertical lane layout even without transposed order
1599 // attr
1600 bool hasTranspose =
1601 consumerLaneLayout[rank - 2] > 1 && consumerLaneLayout[rank - 1] == 1;
1602 bool hasTransform = !hasTranspose && consumerLaneData[rank - 2] > 1 &&
1603 consumerLaneData[rank - 1] == 1;
1604 assert((consumerLaneData[rank - 2] == 1 || consumerLaneData[rank - 1] == 1) &&
1605 "Expected consumer lane data to have at most one non-unit dim");
1606
1607 if (layoutKind == xegpu::LayoutKind::InstData) {
1608 auto blockWHC = uArchInstruction->getBlockWidthHeightCount(
1609 elemTy, hasTransform, hasTranspose,
1610 /*upConv=*/false);
1611 if (!blockWHC)
1612 return nullptr;
1613 auto [bWidths, bHeights, bCounts] = blockWHC.value();
1614
1615 SmallVector<int64_t> laneLayout;
1616 // set the laneLayout to use consumer's LaneLayout as base, but adjust its
1617 // size to match the subgroupsize in case its original value is larger than
1618 // 1
1619 for (int i = 0; i < rank; i++) {
1620 if (consumerLaneLayout[i] > 1)
1621 laneLayout.push_back(std::max(static_cast<int64_t>(subgroupSize),
1622 consumerLaneLayout[i]));
1623 else
1624 laneLayout.push_back(1);
1625 }
1626
1627 // See whether the consumer's inst_data satisfies the block constraints.
1628 int64_t height = consumerInstData[rank - 2];
1629 int64_t width = consumerInstData[rank - 1];
1630 auto maxBlockCount = *llvm::max_element(bCounts);
1631 auto maxWidth = *llvm::max_element(bWidths);
1632 if (llvm::is_contained(bWidths, static_cast<int>(width)) ||
1633 (width % maxWidth == 0 && width / maxWidth < maxBlockCount)) {
1634 if (llvm::is_contained(bHeights, static_cast<int>(height))) {
1635 return buildInstDataLayoutWithLane(context, consumerInstData,
1636 laneLayout, consumerLaneData,
1637 consumerOrderAttr);
1638 }
1639 }
1640
1641 // if consumer instData size too small, try the larger one. like DPAS_MX's
1642 // scale is smaller than block load
1643 auto instData = get2DBlockIOInstDataLayout(
1644 dataShape, elemTy, uArchInstruction, hasTransform, hasTranspose);
1645 // assert instData is valid against consumer layout since
1646 // transform/transpose attribute are derived from consumer layout
1647 assert(instData &&
1648 isValidLaneLayout(*instData, laneLayout, consumerLaneData) &&
1649 "Expected the load layout to satisfy uArch block constraints");
1650 return buildInstDataLayoutWithLane(context, *instData, laneLayout,
1651 consumerLaneData, consumerOrderAttr);
1652 }
1653 if (layoutKind == xegpu::LayoutKind::Lane) {
1654 assert(isValidLaneLayout(dataShape, consumerLaneLayout, consumerLaneData) &&
1655 "Expected the lane layout to satisfy uArch block constraints");
1656 return consumerLayout;
1657 }
1658 return nullptr;
1659}
1660
1661/// Sets up the anchor layout for load gather and load matrix operation.
1662/// load matrix lowers to load gather and 1d block load. All of them share the
1663/// same layout setup logic.
1664///
1665/// For Subgroup layout, uses the consumer layout directly.
1666///
1667/// For InstData layout, takes consumer's inst_data as-is. lane_layout and
1668/// lane_data are taken from the consumer when present; otherwise the helper
1669/// derives the standard scatter-style default (subgroupSize lanes on the
1670/// innermost dim, per-lane vector capped by maxChunkSize).
1671///
1672/// For Lane layout, lane_layout/lane_data are taken from the consumer when
1673/// present; otherwise derived from the same default.
1674static xegpu::DistributeLayoutAttr setupGenericLoadAnchorLayout(
1675 xegpu::LayoutKind layoutKind, mlir::MLIRContext *context,
1676 xegpu::DistributeLayoutAttr consumerLayout, int maxChunkSize,
1677 ArrayRef<int64_t> resShape, int subgroupSize) {
1678
1679 if (layoutKind == xegpu::LayoutKind::Subgroup)
1680 return consumerLayout;
1681
1682 SmallVector<int64_t> consumerInstData =
1683 consumerLayout.getEffectiveInstDataAsInt();
1684 SmallVector<int64_t> consumerLaneLayout =
1685 consumerLayout.getEffectiveLaneLayoutAsInt();
1686 SmallVector<int64_t> consumerLaneData =
1687 consumerLayout.getEffectiveLaneDataAsInt();
1688
1689 // Pick lane_layout / lane_data: prefer consumer's, fall back to the
1690 // scatter-store default (subgroupSize lanes on innermost dim, per-lane
1691 // vector capped by maxChunkSize).
1692 SmallVector<int64_t> laneLayout;
1693 SmallVector<int64_t> laneData;
1694 assert(!consumerLaneLayout.empty() && !consumerLaneData.empty() &&
1695 "Expected consumer layout to have lane_layout and lane_data");
1696 laneLayout.assign(consumerLaneLayout.begin(), consumerLaneLayout.end());
1697 laneData.assign(consumerLaneData.begin(), consumerLaneData.end());
1698
1699 if (layoutKind == xegpu::LayoutKind::InstData) {
1700 // Take consumer's inst_data as-is. If the consumer doesn't have one,
1701 // fall back to lane_layout * lane_data per dim.
1702 SmallVector<int64_t> instData;
1703 if (!consumerInstData.empty()) {
1704 instData.assign(consumerInstData.begin(), consumerInstData.end());
1705 } else {
1706 instData.resize(resShape.size());
1707 for (size_t i = 0; i < resShape.size(); ++i)
1708 instData[i] = laneLayout[i] * laneData[i];
1709 }
1710 return buildInstDataLayoutWithLane(context, instData, laneLayout, laneData);
1711 }
1712 if (layoutKind == xegpu::LayoutKind::Lane)
1713 return buildLaneLayout(context, laneLayout, laneData);
1714 return nullptr;
1715}
1716
1717/// Sets up the anchor layout for a load gather operation.
1718xegpu::DistributeLayoutAttr xegpu::setupLoadGatherAnchorLayout(
1719 xegpu::LayoutKind layoutKind, VectorType resVecTy, int contigChunkSize,
1720 xegpu::DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch) {
1721
1722 const int subgroupSize = uArch->getSubgroupSize();
1723 ArrayRef<int64_t> resShape = resVecTy.getShape();
1724 auto context = resVecTy.getContext();
1725
1726 const auto *uArchInstruction = dyn_cast<xegpu::uArch::LoadGatherInstruction>(
1727 uArch->getInstruction(xegpu::uArch::InstructionKind::LoadGather));
1728 int maxChunkSize =
1729 std::min(uArchInstruction->getMaxLaneAccessSizeBytes(), contigChunkSize);
1730
1731 return setupGenericLoadAnchorLayout(layoutKind, context, consumerLayout,
1732 maxChunkSize, resShape, subgroupSize);
1733}
1734
1735/// Sets up the anchor layout for load matrix operation.
1736/// TODO: enhance load matrix to indicate lowering to chunked load or not.
1737xegpu::DistributeLayoutAttr
1739 VectorType resVecTy, int contigChunkSize,
1740 xegpu::DistributeLayoutAttr consumerLayout,
1741 const xegpu::uArch::uArch *uArch) {
1742
1743 const int subgroupSize = uArch->getSubgroupSize();
1744 ArrayRef<int64_t> resShape = resVecTy.getShape();
1745 auto context = resVecTy.getContext();
1746
1747 const auto *uArchInstruction = dyn_cast<xegpu::uArch::LoadGatherInstruction>(
1749 int maxChunkSize =
1750 std::min(uArchInstruction->getMaxLaneAccessSizeBytes(), contigChunkSize);
1751 return setupGenericLoadAnchorLayout(layoutKind, context, consumerLayout,
1752 maxChunkSize, resShape, subgroupSize);
1753}
1754
1755/// Sets up the anchor layout for store scatter and store matrix operation.
1756/// store matrix lowers to store scatter and 1d block store. All of them
1757/// share the same layout setup logic. For Subgroup layout, not supported
1758/// yet.
1759///
1760/// Lane layout is derived first via `computeScatterIOLaneLayoutAndData`;
1761/// inst_data is then the element-wise product lane_layout * lane_data.
1762static xegpu::DistributeLayoutAttr
1764 mlir::MLIRContext *context, int maxChunkSize,
1765 ArrayRef<int64_t> srcShape, int subgroupSize) {
1766
1767 if (layoutKind == xegpu::LayoutKind::Subgroup) {
1768 assert(false &&
1769 "subgroup layout assignment not supported for storeScatter.");
1770 return nullptr;
1771 }
1772
1773 auto [laneLayout, laneData] =
1774 computeScatterIOLaneLayoutAndData(srcShape, subgroupSize, maxChunkSize);
1775
1776 if (layoutKind == xegpu::LayoutKind::InstData) {
1777 SmallVector<int64_t> instData(srcShape.size());
1778 for (size_t i = 0; i < srcShape.size(); ++i)
1779 instData[i] = laneLayout[i] * laneData[i];
1780 return buildInstDataLayoutWithLane(context, instData, laneLayout, laneData);
1781 }
1782 if (layoutKind == xegpu::LayoutKind::Lane) {
1783 return buildLaneLayout(context, laneLayout, laneData);
1784 }
1785 return nullptr;
1786}
1787
1788/// Sets up the anchor layout for a store scatter operation.
1789xegpu::DistributeLayoutAttr
1791 VectorType srcVecTy, int contigChunkSize,
1792 const uArch::uArch *uArch) {
1793
1794 const int subgroupSize = uArch->getSubgroupSize();
1795 ArrayRef<int64_t> srcShape = srcVecTy.getShape();
1796 auto context = srcVecTy.getContext();
1797
1798 const auto *uArchInstruction =
1799 dyn_cast<xegpu::uArch::StoreScatterInstruction>(
1801 int maxChunkSize =
1802 std::min(uArchInstruction->getMaxLaneAccessSizeBytes(), contigChunkSize);
1803 return setupGenericStoreAnchorLayout(layoutKind, context, maxChunkSize,
1804 srcShape, subgroupSize);
1805}
1806
1807/// Sets up the anchor layout for a store matrix operation.
1808xegpu::DistributeLayoutAttr
1810 VectorType srcVecTy, int contigChunkSize,
1811 const xegpu::uArch::uArch *uArch) {
1812
1813 const int subgroupSize = uArch->getSubgroupSize();
1814 ArrayRef<int64_t> srcShape = srcVecTy.getShape();
1815 auto context = srcVecTy.getContext();
1816
1817 const auto *uArchInstruction =
1818 dyn_cast<xegpu::uArch::StoreScatterInstruction>(
1820 int maxChunkSize =
1821 std::min(uArchInstruction->getMaxLaneAccessSizeBytes(), contigChunkSize);
1822
1823 return setupGenericStoreAnchorLayout(layoutKind, context, maxChunkSize,
1824 srcShape, subgroupSize);
1825}
1826
1827/// Completes a scatter IO layout by deriving lane_layout and lane_data from
1828/// `specifiedLayout`'s inst_data when they are missing. The layout is returned
1829/// unchanged if `specifiedLayout` is null, carries no inst_data, or already has
1830/// both lane_layout and lane_data.
1831///
1832/// When lane info is absent, inst_data is treated as the effective shape and
1833/// the lane factorization is filled in as follows:
1834/// - If `consumerLayout` is present and its lane_layout / lane_data are a
1835/// valid factorization of inst_data, that consumer lane info is reused so
1836/// the completed layout matches the consumer (avoiding a relayout).
1837/// - Otherwise a standard scatter-style factorization is computed via
1838/// `computeScatterIOLaneLayoutAndData`, bounded by `maxChunkSize` — the
1839/// per-lane load width reported by the uArch's LoadGather instruction
1840/// (`getMaxLaneAccessSizeBytes`).
1841///
1842std::optional<xegpu::DistributeLayoutAttr>
1844 xegpu::DistributeLayoutAttr specifiedLayout,
1845 xegpu::DistributeLayoutAttr consumerLayout, Type elemTy,
1846 const xegpu::uArch::LoadGatherInstruction *uArchInstruction,
1847 const int subgroupSize) {
1848 if (!specifiedLayout)
1849 return specifiedLayout;
1850 SmallVector<int64_t> specifiedInstData =
1851 specifiedLayout.getEffectiveInstDataAsInt();
1852 if (specifiedInstData.empty())
1853 return specifiedLayout;
1854 if (!specifiedLayout.getEffectiveLaneLayoutAsInt().empty() &&
1855 !specifiedLayout.getEffectiveLaneDataAsInt().empty())
1856 return specifiedLayout;
1857
1858 // Reuse the load-side setup with inst_data as the destination shape.
1859 auto *context = specifiedLayout.getContext();
1860 int maxChunkSize = uArchInstruction->getMaxLaneAccessSizeBytes();
1861 if (consumerLayout) {
1862 auto consumerLaneLayout = consumerLayout.getEffectiveLaneLayoutAsInt();
1863 auto consumerLaneData = consumerLayout.getEffectiveLaneDataAsInt();
1864 if (!consumerLaneLayout.empty() && !consumerLaneData.empty() &&
1865 isValidLaneLayout(specifiedInstData, consumerLaneLayout,
1866 consumerLaneData))
1867 return buildInstDataLayoutWithLane(context, specifiedInstData,
1868 consumerLaneLayout, consumerLaneData);
1869 }
1870 auto [defLaneLayout, defLaneData] = computeScatterIOLaneLayoutAndData(
1871 specifiedInstData, subgroupSize, maxChunkSize);
1872 if (!isValidLaneLayout(specifiedInstData, defLaneLayout, defLaneData))
1873 return std::nullopt;
1874 return buildInstDataLayoutWithLane(context, specifiedInstData, defLaneLayout,
1875 defLaneData);
1876}
1877
1878/// Like completeScatterLoadLaneLayoutFromInstData, but for scatter stores. A
1879/// store is a data sink, so lane info is derived purely from inst_data (bounded
1880/// by the uArch's per-lane store width); there is no consumer layout to reuse.
1881std::optional<xegpu::DistributeLayoutAttr>
1883 xegpu::DistributeLayoutAttr specifiedLayout, Type elemTy,
1884 const xegpu::uArch::StoreScatterInstruction *uArchInstruction,
1885 const int subgroupSize) {
1886 if (!specifiedLayout)
1887 return specifiedLayout;
1888 SmallVector<int64_t> specifiedInstData =
1889 specifiedLayout.getEffectiveInstDataAsInt();
1890 if (specifiedInstData.empty())
1891 return specifiedLayout;
1892 if (!specifiedLayout.getEffectiveLaneLayoutAsInt().empty() &&
1893 !specifiedLayout.getEffectiveLaneDataAsInt().empty())
1894 return specifiedLayout;
1895
1896 // Reuse the store-side setup with inst_data as the source shape.
1897 auto *context = specifiedLayout.getContext();
1898 int maxChunkSize = uArchInstruction->getMaxLaneAccessSizeBytes();
1899 auto [defLaneLayout, defLaneData] = computeScatterIOLaneLayoutAndData(
1900 specifiedInstData, subgroupSize, maxChunkSize);
1901 if (!isValidLaneLayout(specifiedInstData, defLaneLayout, defLaneData))
1902 return std::nullopt;
1903 return buildInstDataLayoutWithLane(context, specifiedInstData, defLaneLayout,
1904 defLaneData);
1905}
1906
1907/// Completes a 2D-block store/prefetch layout from its inst_data. store_nd and
1908/// prefetch_nd are data sinks, so lane info is derived purely from inst_data
1909/// (no consumer to reuse). One helper serves both via
1910/// BlockIOInstructionInterface.
1911std::optional<xegpu::DistributeLayoutAttr>
1913 xegpu::DistributeLayoutAttr specifiedLayout, Type elemTy,
1914 const xegpu::uArch::BlockIOInstructionInterface *uArchInstruction,
1915 const int subgroupSize) {
1916 if (!specifiedLayout)
1917 return specifiedLayout;
1918 SmallVector<int64_t> specifiedInstData =
1919 specifiedLayout.getEffectiveInstDataAsInt();
1920 if (specifiedInstData.empty())
1921 return specifiedLayout;
1922 if (!specifiedLayout.getEffectiveLaneLayoutAsInt().empty() &&
1923 !specifiedLayout.getEffectiveLaneDataAsInt().empty())
1924 return specifiedLayout;
1925
1926 auto *context = specifiedLayout.getContext();
1927 auto [laneLayout, laneData] = compute2DBlockIOLaneLayoutAndData(
1928 specifiedInstData, subgroupSize, elemTy.getIntOrFloatBitWidth(),
1929 uArchInstruction->getPackedFormatBitSize());
1930 if (!isValidLaneLayout(specifiedInstData, laneLayout, laneData))
1931 return std::nullopt;
1932 return buildInstDataLayoutWithLane(context, specifiedInstData, laneLayout,
1933 laneData);
1934}
1935
1936/// Like completeBlockStoreLaneLayoutFromInstData, but for load_nd. The
1937/// consumer's lane_data and order are reused as-is; lane_layout is rebuilt from
1938/// the consumer's lane_layout, bumping every non-unit dim up to the subgroup
1939/// size. The user-provided inst_data is preserved.
1940std::optional<xegpu::DistributeLayoutAttr>
1942 xegpu::DistributeLayoutAttr specifiedLayout,
1943 xegpu::DistributeLayoutAttr consumerLayout, Type elemTy,
1944 const xegpu::uArch::BlockIOInstructionInterface *uArchInstruction,
1945 const int subgroupSize) {
1946 if (!specifiedLayout)
1947 return specifiedLayout;
1948 SmallVector<int64_t> specifiedInstData =
1949 specifiedLayout.getEffectiveInstDataAsInt();
1950 if (specifiedInstData.empty())
1951 return specifiedLayout;
1952 if (!specifiedLayout.getEffectiveLaneLayoutAsInt().empty() &&
1953 !specifiedLayout.getEffectiveLaneDataAsInt().empty())
1954 return specifiedLayout;
1955 if (!consumerLayout)
1956 return specifiedLayout;
1957 SmallVector<int64_t> consumerLaneLayout =
1958 consumerLayout.getEffectiveLaneLayoutAsInt();
1959 SmallVector<int64_t> consumerLaneData =
1960 consumerLayout.getEffectiveLaneDataAsInt();
1961 if (consumerLaneLayout.empty() || consumerLaneData.empty())
1962 return specifiedLayout;
1963
1964 auto *context = specifiedLayout.getContext();
1965 int rank = specifiedInstData.size();
1966
1967 SmallVector<int64_t> laneLayout;
1968 // set the laneLayout to use consumer's LaneLayout as base, but adjust its
1969 // size to match the subgroupsize in case its original value is larger than 1
1970 for (int i = 0; i < rank; i++) {
1971 if (consumerLaneLayout[i] > 1) {
1972 laneLayout.push_back(
1973 std::max(static_cast<int64_t>(subgroupSize), consumerLaneLayout[i]));
1974 } else {
1975 laneLayout.push_back(1);
1976 }
1977 }
1978
1979 if (!isValidLaneLayout(specifiedInstData, laneLayout, consumerLaneData))
1980 return std::nullopt;
1981 return buildInstDataLayoutWithLane(context, specifiedInstData, laneLayout,
1982 consumerLaneData,
1983 consumerLayout.getOrder());
1984}
1985
1986/// Completes user-provided DPAS A/B/C-D anchors that carry only inst_data by
1987/// filling in lane_layout / lane_data. The lane factorization mirrors the
1988/// InstData branch of `setupDpasLayout` (derived from each operand's shape and
1989/// matmul role, B using VNNI packing); the user's inst_data is preserved.
1990std::optional<
1991 std::tuple<xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr,
1992 xegpu::DistributeLayoutAttr>>
1993xegpu::completeDpasLaneLayoutFromInstData(xegpu::DistributeLayoutAttr aLayout,
1994 xegpu::DistributeLayoutAttr bLayout,
1995 xegpu::DistributeLayoutAttr cdLayout,
1996 VectorType aTy, VectorType bTy,
1997 VectorType cdTy,
1998 const xegpu::uArch::uArch *uArch) {
1999 auto context = aTy.getContext();
2000 const auto *uArchInstruction =
2001 dyn_cast<xegpu::uArch::SubgroupMatrixMultiplyAcc>(uArch->getInstruction(
2003 if (!uArchInstruction)
2004 return std::nullopt;
2005 auto subgroupSize = uArch->getSubgroupSize();
2006 llvm::SmallVector<int64_t> laneLayoutA, laneDataA, laneLayoutB, laneDataB,
2007 laneLayoutCD, laneDataCD;
2008 SmallVector<int64_t> instDataA = aLayout.getEffectiveInstDataAsInt();
2009 SmallVector<int64_t> instDataB = bLayout.getEffectiveInstDataAsInt();
2010 SmallVector<int64_t> instDataCD = cdLayout.getEffectiveInstDataAsInt();
2011
2012 if (isa<xegpu::uArch::Xe2, xegpu::uArch::Xe3>(uArch)) {
2013 std::tie(laneLayoutA, laneDataA) = compute2DBlockIOLaneLayoutAndData(
2014 aTy.getShape(), subgroupSize,
2015 aTy.getElementType().getIntOrFloatBitWidth(),
2016 uArchInstruction->getPackedFormatBitSizeA());
2017 std::tie(laneLayoutB, laneDataB) = compute2DBlockIOLaneLayoutAndData(
2018 bTy.getShape(), subgroupSize,
2019 bTy.getElementType().getIntOrFloatBitWidth(),
2020 uArchInstruction->getPackedFormatBitSizeB(), /*vnni=*/true);
2021 std::tie(laneLayoutCD, laneDataCD) = compute2DBlockIOLaneLayoutAndData(
2022 cdTy.getShape(), subgroupSize,
2023 cdTy.getElementType().getIntOrFloatBitWidth(),
2024 cdTy.getElementType().getIntOrFloatBitWidth());
2025 } else {
2026 assert(false && "Unsupported uArch for DPAS lane layout completion");
2027 }
2028
2029 if (!isValidLaneLayout(instDataA, laneLayoutA, laneDataA) ||
2030 !isValidLaneLayout(instDataB, laneLayoutB, laneDataB) ||
2031 !isValidLaneLayout(instDataCD, laneLayoutCD, laneDataCD))
2032 return std::nullopt;
2033 return std::make_tuple(
2034 buildInstDataLayoutWithLane(context, instDataA, laneLayoutA, laneDataA,
2035 aLayout.getOrder()),
2036 buildInstDataLayoutWithLane(context, instDataB, laneLayoutB, laneDataB,
2037 bLayout.getOrder()),
2038 buildInstDataLayoutWithLane(context, instDataCD, laneLayoutCD, laneDataCD,
2039 cdLayout.getOrder()));
2040}
2041
2042/// Like completeDpasLaneLayoutFromInstData, but for dpas_mx: also re-derives
2043/// the A_scale / B_scale layouts from the completed A / B layouts via
2044/// `createScaleLayout`, matching the default path of `setupDpasMxLayout`.
2045std::optional<
2046 std::tuple<xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr,
2047 xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr,
2048 xegpu::DistributeLayoutAttr>>
2050 xegpu::DistributeLayoutAttr aLayout, xegpu::DistributeLayoutAttr bLayout,
2051 xegpu::DistributeLayoutAttr cdLayout, VectorType aTy, VectorType bTy,
2052 VectorType cdTy, VectorType aScaleTy, VectorType bScaleTy,
2053 const xegpu::uArch::uArch *uArch) {
2054 auto completed = completeDpasLaneLayoutFromInstData(
2055 aLayout, bLayout, cdLayout, aTy, bTy, cdTy, uArch);
2056 if (!completed)
2057 return std::nullopt;
2058 auto context = aTy.getContext();
2059 auto [completedA, completedB, completedCD] = *completed;
2060
2061 auto aScaleLayout =
2062 createScaleLayout(context, aTy, aScaleTy, completedA, false, uArch);
2063 auto bScaleLayout =
2064 createScaleLayout(context, bTy, bScaleTy, completedB, true, uArch);
2065
2066 return std::make_tuple(completedA, completedB, completedCD, aScaleLayout,
2067 bScaleLayout);
2068}
2069
2070/// Sets up layout for reduction operations by creating a SliceAttr for the
2071/// result.
2072///
2073/// Algorithm Overview:
2074/// This function attempts to construct a source layout that, when sliced along
2075/// reduction dimensions, produces a result layout compatible with the
2076/// consumer layout.
2077///
2078/// For subgroup layouts, it first tries to align the source layout's subgroup
2079/// layout and data with the consumer's layout on non-reduction dimensions.
2080/// Then, it distributes remaining subgroups across reduction dimensions. This
2081/// avoids subgroup data redistribution overhead between the reduced result and
2082/// its consumer. When the consumer layout is a slice layout, it attempts to
2083/// reuse the slice layout's parent layout for the source to further minimize
2084/// potential data redistribution.
2085///
2086/// This is a best-effort alignment, not a hard constraint: the goal is only to
2087/// pick a *legal* source layout that minimizes redistribution against the
2088/// (single, first-arriving) consumer layout. There is no failure path - when
2089/// the consumer's slice layout cannot be reused as-is (example 2 below), the
2090/// function falls back to distributing all subgroups on the non-reduction
2091/// dimensions first and the remainder on the reduction dimensions, which always
2092/// yields a valid source layout. If the resulting source layout still differs
2093/// from what some consumer expects (e.g. a second, inconsistent consumer), that
2094/// mismatch is reconciled later by the layout conflict resolution process
2095/// (`ResolveLayoutConflicts`), which inserts a `convert_layout` op - this
2096/// function never has to give up.
2097///
2098/// For the InstData and Lane layout kinds only the innermost two dimensions
2099/// are distributed; all leading dimensions are assumed to be unit dimensions.
2100/// This assumption is checked via `leadingDimsAreUnit`. The lane_layout and
2101/// lane_data are computed by `computeReductionLaneLayoutAndData`, which picks
2102/// a layout that minimizes cross-lane reduction (reducing within a lane when
2103/// only one of the innermost two dims is a reduction dim). The inst_data is
2104/// simply the element-wise product lane_layout * lane_data.
2105///
2106/// The function returns the *result* layout (the SliceAttr). The *source*
2107/// layout it decides on is the parent of that slice; both are listed below so
2108/// the relationship is explicit.
2109///
2110/// Examples:
2111/// 1. Subgroup layout - Row reduction on 2D tensor:
2112/// srcShape=[32, 128], reductionDims=[1], resShape=[32], subgroupSize=16,
2113/// NumSg=32
2114/// * Consumer Layout:
2115/// #xegpu.slice<#xegpu.layout<sg_layout=[4, 8], sg_data=[8, 8]>, dims =
2116/// [1]>}
2117/// * Source Layout (decided by this function):
2118/// #xegpu.layout<sg_layout=[4, 8], sg_data=[8, 16]>
2119/// * Result Layout (returned):
2120/// #xegpu.slice<#xegpu.layout<sg_layout=[4, 8], sg_data=[8, 16]>, dims =
2121/// [1]>}
2122/// The consumer slices exactly the reduction dim, so its parent layout is
2123/// reused for the source: sg_layout is kept, but the source's sg_data on
2124/// the reduction dim is grown from 8 to 16 (= srcShape[1] / sg_layout[1] =
2125/// 128 / 8) so the source tile is evenly distributed over the reduction
2126/// dim. Slicing that source over dim 1 reproduces the consumer.
2127///
2128/// 2. Subgroup layout - Same shapes as above but consumer doesn't have a
2129/// reusable slice layout, so the algorithm distributes all subgroups on the
2130/// non-reduction dims first and the remainder on the reduction dims.
2131/// 2a. * Consumer Layout:
2132/// #xegpu.layout<sg_layout=[32], sg_data=[1]>
2133/// * Source Layout (decided by this function):
2134/// #xegpu.layout<sg_layout=[32, 1], sg_data=[1, 128]>
2135/// * Result Layout (returned):
2136/// #xegpu.slice<#xegpu.layout<sg_layout=[32, 1], sg_data=[1, 128]>,
2137/// dims = [1]>}
2138/// All 32 subgroups land on the non-reduction dim 0; the reduction dim
2139/// 1 gets the leftover (sg_layout=1, so the whole length 128 lives in
2140/// one subgroup's sg_data).
2141/// 2b. * Consumer Layout:
2142/// #xegpu.slice<#xegpu.layout<sg_layout=[8, 2, 4], sg_data=[4, 64,
2143/// 32]>, dims = [1, 2]>}
2144/// * Source Layout (decided by this function):
2145/// #xegpu.layout<sg_layout=[8, 4], sg_data=[4, 32]>
2146/// * Result Layout (returned):
2147/// #xegpu.slice<#xegpu.layout<sg_layout=[8, 4], sg_data=[4, 32]>,
2148/// dims = [1]>}
2149/// The consumer slices dims [1, 2] which do not match this op's
2150/// reductionDims, so it can't be reused as-is; subgroups are
2151/// re-distributed (non-reduction dim first, then reduction dim).
2152///
2153/// 3. Lane layout - Default (lanes on innermost dim):
2154/// srcShape=[32, 64], reductionDims=[0], subgroupSize=16
2155/// * Source Layout (decided by this function):
2156/// laneLayout=[1, 16], laneData=[1, 1] (returned sliced over dim 0).
2157/// The innermost dim is not reduced, so lanes stay on it.
2158///
2159/// 4. Lane layout - Switch (lanes moved off the reduction dim):
2160/// srcShape=[32, 64], reductionDims=[1], subgroupSize=16
2161/// * Source Layout (decided by this function):
2162/// laneLayout=[16, 1], laneData=[1, 1] (returned sliced over dim 1).
2163/// The innermost dim is the sole reduction dim, so lanes move to the
2164/// non-reduction dim to reduce within a lane. This switch only happens
2165/// when the consumer has no reduction dims to broadcast the result back
2166/// along (i.e. the consumer layout is not a slice over this reduction);
2167/// otherwise the default (example 3) is used.
2168///
2169/// 5. Lane layout - No switch when both inner dims are reduced (reduction to
2170/// scalar):
2171/// srcShape=[32, 64], reductionDims=[0, 1], subgroupSize=16
2172/// * Source Layout (decided by this function):
2173/// laneLayout=[1, 16], laneData=[1, 1] (returned sliced over dims
2174/// [0,1]).
2175/// Both dims are reduced, so this is not a *sole* innermost reduction; the
2176/// switch condition (example 4) does not apply and lanes stay on the
2177/// innermost dim. The cross-lane reduction here is unavoidable.
2178///
2179/// 6. Lane layout - No switch when the consumer slices the reduction dim:
2180/// srcShape=[32, 64], reductionDims=[1], subgroupSize=16
2181/// * Consumer Layout:
2182/// #xegpu.slice<#xegpu.layout<laneLayout=[1, 16], laneData=[1, 1]>,
2183/// dims = [1]>}
2184/// * Source Layout (decided by this function):
2185/// #xegpu.layout<laneLayout=[1, 16], laneData=[1, 1]> (the consumer
2186/// slice's parent, reused directly; returned sliced over dim 1).
2187/// Same shape/reductionDims as example 4, but here the consumer is a slice
2188/// over the reduction dim, so it can broadcast the result back along that
2189/// dim. The slice's parent layout is reused as the source (no switch, no
2190/// re-derivation); the inst_data propagation step has already inserted a
2191/// convert_layout if needed, so the lane-level layout can be reused as-is.
2192
2194 xegpu::LayoutKind layoutKind, VectorType srcVecTy,
2195 DistributeLayoutAttr consumerLayout, SmallVector<int64_t> reductionDims,
2196 int numSg, const xegpu::uArch::uArch *uArch) {
2197
2198 auto srcShape = srcVecTy.getShape();
2199 int srcRank = srcShape.size();
2200 auto context = srcVecTy.getContext();
2201
2202 const int subgroupSize = uArch->getSubgroupSize();
2203 int64_t maxReduceVectorSize = 1; // could extend to spirv vector Size
2204 xegpu::DistributeLayoutAttr srcLayout;
2205 if (layoutKind == xegpu::LayoutKind::Subgroup) {
2206 xegpu::SliceAttr consumerSliceLayout =
2207 dyn_cast_if_present<xegpu::SliceAttr>(consumerLayout);
2208 if (consumerSliceLayout &&
2209 consumerSliceLayout.getDims().asArrayRef().equals(reductionDims)) {
2210 srcLayout = consumerSliceLayout.getParent();
2211 SmallVector<int64_t> sgLayoutFromConsumer =
2212 srcLayout.getEffectiveSgLayoutAsInt();
2213 auto srcSgData = computeShapeRatio(srcShape, sgLayoutFromConsumer);
2214 if (srcSgData)
2215 for (int dim = 0; dim < srcRank; dim++) {
2216 if (llvm::is_contained(reductionDims, dim))
2217 srcLayout =
2218 srcLayout.setDimData(dim, srcSgData.value()[dim], -1, -1);
2219 }
2220 } else {
2221 SmallVector<int64_t> consumerSgLayout =
2222 consumerLayout ? consumerLayout.getEffectiveSgLayoutAsInt()
2224 SmallVector<int64_t> consumerSgData =
2225 consumerLayout ? consumerLayout.getEffectiveSgDataAsInt()
2227 SmallVector<int64_t> consumerOrder =
2228 consumerLayout ? consumerLayout.getEffectiveOrderAsInt()
2230 DenseI32ArrayAttr orderAttr =
2231 consumerLayout ? consumerLayout.getOrder() : nullptr;
2232 SmallVector<int64_t> sgLayout(srcRank), sgData(srcRank), order(srcRank);
2233 int remainingSgCount =
2234 consumerLayout ? consumerLayout.getNumSubgroups() : numSg;
2235 int consumerIdx = 0;
2236
2237 // First pass: Match consumer's layout on non-reduction dimensions
2238 for (int i = 0; i < srcRank; i++) {
2239 if (!llvm::is_contained(reductionDims, i) &&
2240 consumerIdx < static_cast<int>(consumerSgLayout.size())) {
2241 sgLayout[i] = consumerSgLayout[consumerIdx];
2242 sgData[i] = consumerSgData[consumerIdx];
2243 remainingSgCount /= sgLayout[i];
2244 order[i] = consumerOrder[consumerIdx];
2245 consumerIdx++;
2246 }
2247 }
2248
2249 // Second pass: Distribute remaining subgroups across reduction dimensions
2250 // the reduction to scalar case is handled only by this loop
2251 int64_t remainOrder = consumerSgLayout.size();
2252 for (int i = 0; i < srcRank; i++) {
2253 if (llvm::is_contained(reductionDims, i)) {
2254 sgLayout[i] =
2255 std::min(srcShape[i], static_cast<int64_t>(remainingSgCount));
2256 assert((srcShape[i] % sgLayout[i] == 0) &&
2257 "source shape not divisible by sg_layout");
2258 sgData[i] = srcShape[i] / sgLayout[i];
2259 remainingSgCount /= sgLayout[i];
2260 order[i] = remainOrder++;
2261 }
2262 }
2264 context, SmallVector<int32_t>(order.begin(), order.end()));
2265 if (!orderAttr || orderAttr.empty())
2266 resOrderAttr = nullptr;
2267 assert(remainingSgCount == 1 && "not all subgroups distributed");
2268 srcLayout = buildLayout(context, sgLayout, sgData,
2269 /*instData=*/{}, /*laneLayout=*/{},
2270 /*laneData=*/{}, resOrderAttr);
2271 }
2272 } else if (layoutKind == xegpu::LayoutKind::InstData) {
2273 xegpu::SliceAttr consumerSliceLayout =
2274 dyn_cast_if_present<xegpu::SliceAttr>(consumerLayout);
2275 auto consumerReductionDims =
2276 consumerSliceLayout
2277 ? SmallVector<int64_t>(consumerSliceLayout.getDims().asArrayRef())
2279 // A[i] reduced from A[i, j] is stored out directly, use vertical Lane
2280 // layout like [16, 1]
2281 bool verticalLaneLayout = consumerReductionDims.empty() &&
2282 reductionDims.size() == 1 &&
2283 reductionDims[0] == (srcRank - 1);
2284 auto [laneLayout, laneData] = computeReductionLaneLayoutAndData(
2285 srcShape, reductionDims, subgroupSize, maxReduceVectorSize,
2286 verticalLaneLayout);
2287 // inst_data is the per-instruction data, i.e. the element-wise product of
2288 // lane_layout and lane_data.
2289 SmallVector<int64_t> instData(srcRank);
2290 for (int i = 0; i < srcRank; i++)
2291 instData[i] = laneLayout[i] * laneData[i];
2292 srcLayout =
2293 buildInstDataLayoutWithLane(context, instData, laneLayout, laneData);
2294 } else if (layoutKind == xegpu::LayoutKind::Lane) {
2295 // Only the innermost two dimensions are distributed; all leading dimensions
2296 // are assumed to be unit dimensions.
2297 assert(leadingDimsAreUnit(srcShape, /*numInnerDims=*/2) &&
2298 "Lane reduction layout assumes all leading (non-innermost-two) "
2299 "dimensions are unit dimensions");
2300 xegpu::SliceAttr consumerSliceLayout =
2301 dyn_cast_if_present<xegpu::SliceAttr>(consumerLayout);
2302 auto consumerReductionDims =
2303 consumerSliceLayout
2304 ? SmallVector<int64_t>(consumerSliceLayout.getDims().asArrayRef())
2306 if (consumerSliceLayout &&
2307 consumerSliceLayout.getDims().asArrayRef().equals(reductionDims)) {
2308 // at the lane level, the consumerSliceLayout can be directly reused
2309 // since the inst_data propagation already insert convert_layout if
2310 // the layout is not consistent
2311 srcLayout = consumerSliceLayout.getParent();
2312 } else {
2313 bool verticalLaneLayout = consumerReductionDims.empty() &&
2314 reductionDims.size() == 1 &&
2315 reductionDims[0] == (srcRank - 1);
2316 auto [laneLayout, laneData] = computeReductionLaneLayoutAndData(
2317 srcShape, reductionDims, subgroupSize, maxReduceVectorSize,
2318 verticalLaneLayout);
2319 srcLayout = buildLaneLayout(context, laneLayout, laneData);
2320 }
2321 }
2322
2323 return xegpu::SliceAttr::get(context, srcLayout,
2324 DenseI64ArrayAttr::get(context, reductionDims));
2325}
2326
2327/// Sets up layout for Reduction operations by creating a SliceAttr for the
2328/// result.
2329xegpu::SliceAttr
2331 VectorType srcVecTy,
2332 const xegpu::uArch::uArch *uArch) {
2333
2334 auto srcShape = srcVecTy.getShape();
2335 auto context = srcVecTy.getContext();
2336 auto subgroupSize = uArch->getSubgroupSize();
2337 xegpu::LayoutAttr srcLayout;
2338
2339 if (layoutKind == xegpu::LayoutKind::Subgroup) {
2340 assert(false &&
2341 "subgroup layout assignment not supported for reduction (op "
2342 "is not expected at this level).");
2343 } else if (layoutKind == xegpu::LayoutKind::InstData) {
2344 assert(false &&
2345 "instData layout assignment not supported for reduction (op "
2346 "is not expected at this level).");
2347 } else if (layoutKind == xegpu::LayoutKind::Lane) {
2348 SmallVector<int64_t> laneLayout(1), laneData(1);
2349 laneLayout[0] = std::min(static_cast<int64_t>(subgroupSize), srcShape[0]);
2350 laneData[0] = 1;
2351 srcLayout = buildLaneLayout(context, laneLayout, laneData);
2352 }
2353
2354 auto result = xegpu::SliceAttr::get(context, srcLayout,
2355 DenseI64ArrayAttr::get(context, 0));
2356 return result;
2357}
2358
2359/// Adjusts `consumerLayout`'s innermost-dim data field selected by
2360/// `layoutKind` so that the source layout can be safely inferred by dividing
2361/// that value by `ratio`. Doubles the value until the divisibility constraint
2362/// is met, bounded above by `bound` like result-shape.
2363///
2364/// Used by ops whose source relates to the result by a fixed factor along the
2365/// innermost dim (e.g., bitcast: bitwidth ratio; interleave: 2x).
2366///
2367/// Divisibility constraints per LayoutKind:
2368/// - Subgroup: sgData[innermost] % ratio == 0
2369/// - InstData: instData[innermost] % (laneLayout[innermost] * ratio) == 0
2370/// (laneLayout falls back to subgroupSize if absent)
2371/// - Lane: laneData[innermost] % ratio == 0
2372static xegpu::DistributeLayoutAttr
2373adjustInnermostDimForDivisibility(xegpu::DistributeLayoutAttr consumerLayout,
2374 xegpu::LayoutKind layoutKind,
2375 size_t innerMostDim, int ratio, int64_t bound,
2376 const xegpu::uArch::uArch *uArch) {
2377 SmallVector<int64_t> sgData = consumerLayout.getEffectiveSgDataAsInt();
2378 SmallVector<int64_t> instData = consumerLayout.getEffectiveInstDataAsInt();
2379 SmallVector<int64_t> laneData = consumerLayout.getEffectiveLaneDataAsInt();
2380 SmallVector<int64_t> laneLayout =
2381 consumerLayout.getEffectiveLaneLayoutAsInt();
2382
2383 int64_t sgDataValue = -1;
2384 int64_t instDataValue = -1;
2385 int64_t laneDataValue = -1;
2386
2387 if (layoutKind == xegpu::LayoutKind::Subgroup) {
2388 sgDataValue = sgData[innerMostDim];
2389 while ((sgDataValue <= bound) && (sgDataValue % ratio) != 0)
2390 sgDataValue *= 2;
2391 } else if (layoutKind == xegpu::LayoutKind::InstData) {
2392 instDataValue = instData[innerMostDim];
2393 const int innermostDimLaneLayout = laneLayout.empty()
2394 ? uArch->getSubgroupSize()
2395 : laneLayout[innerMostDim];
2396 while ((instDataValue <= bound) &&
2397 (instDataValue % (innermostDimLaneLayout * ratio) != 0))
2398 instDataValue *= 2;
2399 assert((bound % instDataValue) == 0 &&
2400 "bound, instData, and laneLayout for innermost must be 2^n!");
2401 } else if (layoutKind == xegpu::LayoutKind::Lane) {
2402 laneDataValue = laneData[innerMostDim];
2403 while ((laneDataValue <= bound) && (laneDataValue % ratio) != 0)
2404 laneDataValue *= 2;
2405 }
2406
2407 return consumerLayout.setDimData(innerMostDim, sgDataValue, instDataValue,
2408 laneDataValue);
2409}
2410
2411/// Sets up the result layout for a bitcast operation.
2412/// When casting to a smaller bitwidth, adjusts the layout dimensions (sgData,
2413/// instData, or laneData) by multiplying by the bitwidth ratio to ensure the
2414/// result layout can be correctly divided back to the source layout during
2415/// inference.
2416///
2417/// Examples:
2418/// 1. Casting f32 -> f16 (32-bit to 16-bit, bitWidthRatio = 2):
2419/// Consumer layout: instData=[1, 16], subgroupSize=16
2420/// Source shape: [8, 32]
2421/// Result layout: instData=[1, 32] (16 * 2)
2422/// The innermost dimension is multiplied by 2 to maintain consistency.
2423///
2424/// 2. Casting f32 -> i8 (32-bit to 8-bit, bitWidthRatio = 4):
2425/// Consumer instData=[1, 16], subgroupSize=16
2426/// Source shape: [4, 128]
2427/// adjust the instData from [1, 16] to [1, 16 * 4 = 64]
2428///
2429/// 3. Casting i8 -> i32 (8-bit to 32-bit, bitWidthRatio = 1/4):
2430/// Consumer layout: laneLayout=[1, 16], laneData=[1, 4]
2431/// No adjustment needed - returns consumer layout directly.
2432///
2433xegpu::DistributeLayoutAttr xegpu::setupBitCastResultLayout(
2434 xegpu::LayoutKind layoutKind, VectorType srcVecTy, VectorType resVecTy,
2435 DistributeLayoutAttr consumerLayout, const xegpu::uArch::uArch *uArch) {
2436
2437 int srcElemTyBitWidth = srcVecTy.getElementType().getIntOrFloatBitWidth();
2438 int resElemTyBitWidth = resVecTy.getElementType().getIntOrFloatBitWidth();
2439
2440 ArrayRef<int64_t> srcShape = srcVecTy.getShape();
2441 ArrayRef<int64_t> resShape = resVecTy.getShape();
2442
2443 assert(consumerLayout.getRank() == static_cast<int64_t>(srcShape.size()) &&
2444 "laneData must be available for all dimensions");
2445
2446 // Casting to same/larger element type: result has fewer (or equal) elements
2447 // along the innermost dim, no adjustment needed.
2448 if (srcElemTyBitWidth <= resElemTyBitWidth)
2449 return consumerLayout;
2450
2451 // Casting to smaller element type: result has more elements along innermost
2452 // dim. Adjust the innermost data field upward so the source layout can be
2453 // recovered by dividing by bitWidthRatio.
2454 size_t innerMostDim = srcShape.size() - 1;
2455 int bitWidthRatio = srcElemTyBitWidth / resElemTyBitWidth;
2456 return adjustInnermostDimForDivisibility(consumerLayout, layoutKind,
2457 innerMostDim, bitWidthRatio,
2458 resShape[innerMostDim], uArch);
2459}
2460
2461/// Sets up the result layout for an interleave operation to ensure the source
2462/// layout can be safely derived. Interleave doubles the innermost dimension,
2463/// so the result layout must ensure that laneData is a multiple
2464/// of 2, and instData must be divisible by innermostDimLaneLayout * 2.
2465///
2466/// Example:
2467/// Interleave: vector<128x256xf4> -> vector<128x512xf4>
2468/// Consumer layout: laneLayout=[1, 16], laneData=[1, 4], instData=[1, 64]
2469/// Result layout adjustment to ensure source can be safely inferred:
2470/// - laneData must be >= 2 and multiple of 2 (so source = laneData/2 is
2471/// valid)
2472/// - instData must be divisible by (16 * 2 = 32) (so source = instData/2 is
2473/// valid)
2474/// - Adjusted instData: ensure (instData % 32 == 0)
2475///
2476xegpu::DistributeLayoutAttr xegpu::setupInterleaveResultLayout(
2477 xegpu::LayoutKind layoutKind, VectorType srcVecTy, VectorType resVecTy,
2478 DistributeLayoutAttr consumerLayout, const xegpu::uArch::uArch *uArch) {
2479
2480 ArrayRef<int64_t> resShape = resVecTy.getShape();
2481 assert(consumerLayout.getRank() == static_cast<int64_t>(resShape.size()) &&
2482 "consumer layout rank must match source shape rank");
2483
2484 // Interleave doubles the innermost dimension (ratio = 2). Adjust the
2485 // innermost data field so the source layout can be recovered by dividing
2486 // by 2.
2487 const size_t innerMostDim = resShape.size() - 1;
2488 constexpr int ratio = 2;
2489 return adjustInnermostDimForDivisibility(consumerLayout, layoutKind,
2490 innerMostDim, ratio,
2491 resShape[innerMostDim], uArch);
2492}
2493
2494/// Sets up the result layout for an insert strided slice operation.
2495/// Creates a result layout based on the specified layout kind (InstData or
2496/// Lane).
2497xegpu::DistributeLayoutAttr xegpu::setupInsertStridedSliceResultLayout(
2498 xegpu::LayoutKind layoutKind, VectorType srcVectorTy,
2499 VectorType resVectorTy, xegpu::DistributeLayoutAttr consumerLayout,
2500 const xegpu::uArch::uArch *uArch) {
2501
2502 xegpu::DistributeLayoutAttr requiredResLayout;
2503 SmallVector<int64_t> consumerInstData =
2504 consumerLayout.getEffectiveInstDataAsInt();
2505 SmallVector<int64_t> consumerLaneData =
2506 consumerLayout.getEffectiveLaneDataAsInt();
2507 SmallVector<int64_t> consumerLaneLayout =
2508 consumerLayout.getEffectiveLaneLayoutAsInt();
2509 ArrayRef<int64_t> srcShape = srcVectorTy.getShape();
2510 int64_t laneDataValue = -1;
2511
2512 requiredResLayout = consumerLayout;
2513 int srcRank = srcShape.size();
2514
2515 if (layoutKind == xegpu::LayoutKind::Subgroup ||
2516 layoutKind == xegpu::LayoutKind::InstData) {
2517 assert(false && "subgroup/instData layout assignment not supported for "
2518 "insertStridedSlice.");
2519 } else if (layoutKind == xegpu::LayoutKind::Lane) {
2520 for (int dim = 0; dim < srcRank; dim++) {
2521 assert(srcShape[dim] % consumerLaneLayout[dim] == 0 &&
2522 "srcShape must be divisible by laneLayout for all dimensions");
2523 laneDataValue = std::min(srcShape[dim] / consumerLaneLayout[dim],
2524 consumerLaneData[dim]);
2525 requiredResLayout =
2526 requiredResLayout.setDimData(dim, -1, -1, laneDataValue);
2527 }
2528 }
2529 return requiredResLayout;
2530}
2531
2532/// Back-propagates a known result layout to the layout required on `operand`
2533/// for a non-anchor (layout-propagating) vector op. Dispatches on the op kind —
2534/// broadcast, (multi)reduction, bitcast, shape/transpose, insert/extract,
2535/// interleave, etc. — applying the shape/permutation/bitwidth transform to
2536/// derive the source layout; elementwise and pass-through ops reuse resLayout
2537/// as-is. Returns nullptr for unknown ops or an absent result layout.
2538xegpu::DistributeLayoutAttr xegpu::inferSourceLayoutFromResultForNonAnchorOp(
2539 OpOperand &operand, xegpu::DistributeLayoutAttr resLayout) {
2540 if (!resLayout)
2541 return nullptr;
2542 Operation *op = operand.getOwner();
2543 unsigned idx = operand.getOperandNumber();
2544
2545 // For vector::BroadcastOp, infer the source layout from the result layout.
2546 if (auto broadcast = dyn_cast<vector::BroadcastOp>(op)) {
2547 auto srcTy = dyn_cast<VectorType>(broadcast.getSourceType());
2548 if (!srcTy)
2549 return nullptr;
2551 resLayout, broadcast.getResultVectorType().getShape(),
2552 srcTy.getShape());
2553 }
2554
2555 // For vector::MultiDimReductionOp, infer source layout from result layout
2556 // using reduction dims. Acc operand is expected to have the same layout as
2557 // the result.
2558 if (auto reduction = dyn_cast<vector::MultiDimReductionOp>(op)) {
2559 if (idx == 0) {
2560 SmallVector<int64_t> reductionDims(reduction.getReductionDims());
2561 return xegpu::inferMultiReductionSourceLayout(resLayout, reductionDims);
2562 }
2563 if (idx == 1)
2564 return resLayout;
2565 }
2566
2567 if (auto reduction = dyn_cast<vector::ReductionOp>(op))
2568 return xegpu::inferReductionSourceLayout(resLayout);
2569
2570 // For vector::BitCastOp, infer source layout from result layout using
2571 // element type bitwidths.
2572 if (auto bitcast = dyn_cast<vector::BitCastOp>(op)) {
2573 int resElemBitWidth =
2574 bitcast.getResultVectorType().getElementType().getIntOrFloatBitWidth();
2575 int srcElemBitWidth =
2576 bitcast.getSourceVectorType().getElementType().getIntOrFloatBitWidth();
2577 return xegpu::inferBitCastSourceLayout(resLayout, resElemBitWidth,
2578 srcElemBitWidth);
2579 }
2580
2581 // For vector::ShapeCastOp, infer source layout from result layout using
2582 // shapes.
2583 if (auto shapeCast = dyn_cast<vector::ShapeCastOp>(op)) {
2585 resLayout, shapeCast.getResultVectorType().getShape(),
2586 shapeCast.getSourceVectorType().getShape());
2587 }
2588
2589 // For vector::InsertStridedSliceOp, infer source layout from result
2590 // layout. Dest vector must have the same layout as the result.
2591 if (auto insertSlice = dyn_cast<vector::InsertStridedSliceOp>(op)) {
2592 if (idx == 0) {
2594 resLayout, insertSlice.getDestVectorType().getShape(),
2595 insertSlice.getSourceVectorType().getShape());
2596 }
2597 if (idx == 1)
2598 return resLayout;
2599 }
2600
2601 // For vector::Insert Op, infer source layout from result layout using
2602 // shapes.
2603 if (auto insert = dyn_cast<vector::InsertOp>(op)) {
2604 VectorType resVecTy = dyn_cast<VectorType>(insert.getResult().getType());
2605 VectorType valueToStoreTy =
2606 dyn_cast<VectorType>(insert.getValueToStore().getType());
2607
2608 if ((idx == 0) && valueToStoreTy) {
2609 return xegpu::inferInsertSourceLayout(resLayout, resVecTy.getShape(),
2610 valueToStoreTy.getShape());
2611 }
2612 if (idx == 1)
2613 return resLayout;
2614 }
2615
2616 // For vector::Extract Op, infer source layout from result layout using
2617 // shapes.
2618 if (auto extract = dyn_cast<vector::ExtractOp>(op)) {
2619 VectorType srcVecTy = dyn_cast<VectorType>(extract.getSource().getType());
2620 VectorType resVecTy = dyn_cast<VectorType>(extract.getResult().getType());
2621 if (!srcVecTy || !resVecTy)
2622 return nullptr;
2623 return xegpu::inferExtractSourceLayout(resLayout, resVecTy.getShape(),
2624 srcVecTy.getShape());
2625 }
2626
2627 // For vector::TransposeOp, infer source layout from result layout using
2628 // permutation.
2629 if (auto transpose = dyn_cast<vector::TransposeOp>(op)) {
2630 return xegpu::inferTransposeSourceLayout(resLayout,
2631 transpose.getPermutation());
2632 }
2633
2634 // For vector::BitCastOp, infer source layout from result layout using
2635 // element type bitwidths.
2636 if (auto bitcast = dyn_cast<vector::BitCastOp>(op)) {
2637 int resElemBitWidth =
2638 bitcast.getResultVectorType().getElementType().getIntOrFloatBitWidth();
2639 int srcElemBitWidth =
2640 bitcast.getSourceVectorType().getElementType().getIntOrFloatBitWidth();
2641 return xegpu::inferBitCastSourceLayout(resLayout, resElemBitWidth,
2642 srcElemBitWidth);
2643 }
2644
2645 // for vector::interleave
2646 if (auto interleave = dyn_cast<vector::InterleaveOp>(op)) {
2647 return xegpu::inferInterleaveSourceLayout(resLayout);
2648 }
2649
2650 // for vector::deinterleave
2651 if (auto deinterleave = dyn_cast<vector::DeinterleaveOp>(op)) {
2652 return xegpu::inferDeinterleaveSourceLayout(resLayout);
2653 }
2654
2655 // For vector::ExtractStridedSliceOp, simply return result layout
2656 if (dyn_cast<vector::ExtractStridedSliceOp>(op))
2657 return resLayout;
2658
2659 // For elementwise operations, all operands must have the same layout as
2660 // the result.
2662 return resLayout;
2663
2664 return nullptr;
2665}
2666
2667/// Returns the layout required on `operand`: anchor ops report their declared
2668/// per-operand layout directly; non-anchor ops back-derive it from their result
2669/// layout via inferSourceLayoutFromResultForNonAnchorOp.
2670xegpu::DistributeLayoutAttr xegpu::getConsumerLayoutAt(OpOperand &operand) {
2671 Operation *op = operand.getOwner();
2672 // Anchor ops declare the layout they
2673 // require on each operand. Trust that declaration directly so that
2674 // ResolveLayoutConflicts compares producer-vs-declared
2675 if (isa<xegpu::AnchorLayoutInterface>(op))
2676 return xegpu::getDistributeLayoutAttr(operand);
2677 // For non-anchor ops, derive the operand layout from the op's result
2678 // layout via op-specific semantics.
2679 xegpu::DistributeLayoutAttr resLayout;
2680 if (op->getNumResults() == 1 || isa<vector::DeinterleaveOp>(op))
2681 resLayout = xegpu::getDistributeLayoutAttr(op->getResult(0));
2682 return inferSourceLayoutFromResultForNonAnchorOp(operand, resLayout);
2683}
return success()
static void visit(Operation *op, DenseSet< Operation * > &visited)
Visits all the pdl.operand(s), pdl.result(s), and pdl.operation(s) connected to the given operation.
Definition PDL.cpp:62
lhs
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
static xegpu::LayoutAttr buildLayout(mlir::MLIRContext *context, ArrayRef< int64_t > sgLayout, ArrayRef< int64_t > sgData, ArrayRef< int64_t > instData, ArrayRef< int64_t > laneLayout, ArrayRef< int64_t > laneData, DenseI32ArrayAttr orderAttr=nullptr)
static xegpu::DistributeLayoutAttr createScaleLayout(mlir::MLIRContext *context, VectorType matrixTy, VectorType scaleTy, xegpu::DistributeLayoutAttr matrixLayout, bool isBScale, const xegpu::uArch::uArch *uArch)
Helper to create a scale layout derived from a matrix operand layout.
static bool leadingDimsAreUnit(ArrayRef< int64_t > shape, int numInnerDims)
Returns true if every dimension of shape except the innermost numInnerDims is a unit (size-1) dimensi...
static xegpu::DistributeLayoutAttr adjustInnermostDimForDivisibility(xegpu::DistributeLayoutAttr consumerLayout, xegpu::LayoutKind layoutKind, size_t innerMostDim, int ratio, int64_t bound, const xegpu::uArch::uArch *uArch)
Adjusts consumerLayout's innermost-dim data field selected by layoutKind so that the source layout ca...
static std::pair< SmallVector< int64_t >, SmallVector< int64_t > > compute2DBlockIOLaneLayoutAndData(ArrayRef< int64_t > instShape, int64_t subgroupSize, int64_t bitwidth, int64_t packingSize, bool transform=false)
static std::pair< SmallVector< int64_t >, SmallVector< int64_t > > computeScatterIOLaneLayoutAndData(ArrayRef< int64_t > instShape, int64_t subgroupSize, int64_t maxChunkSize)
Computes lane_layout and lane_data for scatter-style store anchor layouts (store scatter,...
static xegpu::LayoutAttr buildInstDataLayoutWithLane(mlir::MLIRContext *context, ArrayRef< int64_t > instData, ArrayRef< int64_t > laneLayout, ArrayRef< int64_t > laneData, DenseI32ArrayAttr orderAttr=nullptr)
static xegpu::LayoutAttr buildSgLayout(mlir::MLIRContext *context, ArrayRef< int64_t > wgTileShape, ArrayRef< int64_t > sgLayout, int dimK=-1, DenseI32ArrayAttr orderAttr=nullptr)
static std::pair< SmallVector< int64_t >, SmallVector< int64_t > > computeReductionLaneLayoutAndData(ArrayRef< int64_t > srcShape, ArrayRef< int64_t > reductionDims, int subgroupSize, int64_t maxReduceVectorSize, bool verticalLaneLayout=false)
Computes the (lane_layout, lane_data) for a multi-reduction's source layout.
static std::optional< SmallVector< int64_t > > get2DBlockIOInstDataLayout(ArrayRef< int64_t > dataShape, Type elemTy, const xegpu::uArch::BlockIOInstructionInterface *uArchInstruction, bool transform=false, bool transpose=false)
Helper function to compute inst_data vectors for DPAS operands A, B, and C/D.
static std::optional< std::tuple< xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr, xegpu::DistributeLayoutAttr > > getDpasSubgroupLayouts(mlir::MLIRContext *context, VectorType aTy, VectorType bTy, VectorType cdTy, xegpu::DistributeLayoutAttr consumerLayout, int numSg, std::tuple< SmallVector< int64_t >, SmallVector< int64_t >, SmallVector< int64_t > > instDataVecs)
Helper function to set up subgroup layouts for DPAS operands A, B, and C/D.
static SmallVector< LayoutRepresentation > enumerateFactorizations(int64_t total, int64_t rank)
Enumerates all ways to split total into rank factors whose product equals total.
static xegpu::DistributeLayoutAttr setupGenericLoadAnchorLayout(xegpu::LayoutKind layoutKind, mlir::MLIRContext *context, xegpu::DistributeLayoutAttr consumerLayout, int maxChunkSize, ArrayRef< int64_t > resShape, int subgroupSize)
Sets up the anchor layout for load gather and load matrix operation.
static xegpu::DistributeLayoutAttr setupGenericStoreAnchorLayout(xegpu::LayoutKind layoutKind, mlir::MLIRContext *context, int maxChunkSize, ArrayRef< int64_t > srcShape, int subgroupSize)
Sets up the anchor layout for store scatter and store matrix operation.
SmallVector< int64_t > LayoutRepresentation
static xegpu::DistributeLayoutAttr getLayoutFromUsePoints(Value result)
static SmallVector< LayoutRepresentation > getSgLayoutCandidates(ArrayRef< int64_t > wgShape, ArrayRef< int64_t > instData, int64_t sgCount)
static void propagateResultsToRegularOperands(Operation *op)
static void propagateRegionResultsToYieldOperands(mlir::RegionBranchTerminatorOpInterface yieldOp)
static bool isValidLaneLayout(ArrayRef< int64_t > dataShape, ArrayRef< int64_t > laneLayout, ArrayRef< int64_t > laneData)
static void setTensorDescLayout(Value val, xegpu::DistributeLayoutAttr layout)
static void walkRegionBackward(Region &region, llvm::function_ref< void(Operation *)> visit)
static xegpu::LayoutAttr buildLaneLayout(mlir::MLIRContext *context, ArrayRef< int64_t > laneLayout, ArrayRef< int64_t > laneData, DenseI32ArrayAttr orderAttr=nullptr)
static std::optional< std::tuple< SmallVector< int64_t >, SmallVector< int64_t >, SmallVector< int64_t > > > getDpasInstDataLayouts(VectorType aTy, VectorType bTy, VectorType cdTy, const xegpu::uArch::MMAInstructionInterface *uArchInstruction)
Helper function to compute inst_data vectors for DPAS operands A, B, and C/D.
Block represents an ordered list of Operations.
Definition Block.h:33
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class represents an operand of an operation.
Definition Value.h:254
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Definition Value.cpp:226
This is a value defined by a result of an operation.
Definition Value.h:454
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
unsigned getBeginOperandIndex() const
Return the operand index of the first element of this range.
type_range getType() const
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool hasAttrOfType(NameT &&name)
Definition Operation.h:600
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Definition Operation.h:537
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:699
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:408
auto getDiscardableAttrs()
Return a range of all of discardable attributes on this operation.
Definition Operation.h:511
Attribute removeDiscardableAttr(StringAttr name)
Remove the discardable attribute with the specified name if it exists.
Definition Operation.h:497
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
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
Attribute removeAttr(StringAttr name)
Remove the attribute with the specified name if it exists.
Definition Operation.h:625
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
This class represents a successor of a region.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
bool empty()
Definition Region.h:60
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
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
void setType(Type newType)
Mutate the type of this Value to be of the specified type.
Definition Value.h:116
Type getType() const
Return the type of this value.
Definition Value.h:105
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int32_t > content)
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
DistributeLayoutAttr inferShapeCastSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > resShape, ArrayRef< int64_t > srcShape)
Infers the source layout attribute for a shape cast operation given the result layout attribute,...
bool matchDimCollapse(ArrayRef< int64_t > src, ArrayRef< int64_t > dst, SmallVector< SmallVector< int64_t > > &collapseDims)
DistributeLayoutAttr setupLoadNdAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, DistributeLayoutAttr consumerLayout, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a load_nd operation.
DistributeLayoutAttr setupLoadMatrixAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Sets up the anchor layout for load matrix operation.
DistributeLayoutAttr setupInterleaveResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, VectorType resVectorTy, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Sets up the result layout for an interleave operation to ensure the source layout can be safely deriv...
DistributeLayoutAttr inferTransposeSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > permutation)
Infers the source layout attribute for a transpose operation given the result layout attribute and pe...
DistributeLayoutAttr inferInsertSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > resShape, ArrayRef< int64_t > srcShape)
Infers the source layout attribute for an insert operation.
std::optional< std::tuple< DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr > > completeDpasMxLaneLayoutFromInstData(DistributeLayoutAttr aLayout, DistributeLayoutAttr bLayout, DistributeLayoutAttr cdLayout, VectorType aTy, VectorType bTy, VectorType cdTy, VectorType aScaleTy, VectorType bScaleTy, const uArch::uArch *uArch)
Like completeDpasLaneLayoutFromInstData, but for dpas_mx: additionally re-derives the A_scale / B_sca...
DistributeLayoutAttr inferInsertStridedSliceSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > resShape, ArrayRef< int64_t > srcShape)
Infers the source layout attribute for an insert strided slice operation given the result layout attr...
void removeTemporaryLayoutAttrs(Operation *op)
Removes the temporary layout attributes for each OpOperand and OpResult of the given operation.
std::optional< std::tuple< DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr > > completeDpasLaneLayoutFromInstData(DistributeLayoutAttr aLayout, DistributeLayoutAttr bLayout, DistributeLayoutAttr cdLayout, VectorType aTy, VectorType bTy, VectorType cdTy, const uArch::uArch *uArch)
Completes user-provided DPAS A/B/C-D anchors that carry only inst_data by filling in lane_layout / la...
void setTemporaryLayout(const T &operandOrResult, const DistributeLayoutAttr layout)
LayoutKind
Specifies the level of a layout hierarchy for comparison or propagation.
Definition XeGPU.h:32
SmallVector< NamedAttribute > dropInstDataOnAttrs(ArrayRef< NamedAttribute > attrs)
Updates the NamedAttribute sequence by dropping inst-data information from any DistributeLayoutAttr f...
DistributeLayoutAttr inferSourceLayoutFromResultForNonAnchorOp(OpOperand &operand, DistributeLayoutAttr resLayout)
Infers the source layout attribute for an operand using result layout attribute.
DistributeLayoutAttr inferInterleaveSourceLayout(DistributeLayoutAttr resLayout)
Infers the source layout attribute for an interleave operation given the result layout attribute.
bool matchUnitDimExpansion(ArrayRef< int64_t > src, ArrayRef< int64_t > dst, SmallVector< int64_t > &expandedUnitDims)
int getLargestDivisor(T dim, ArrayRef< T > candidates, ArrayRef< T > candidateMultiples={})
Helper Function to find a proper instruction multiple for the user-supplied sg-level data shape (dive...
bool recoverTemporaryLayouts(Operation *rootOp)
Attach layout attributes to all vector-type operands of operations within the given operation's neste...
DistributeLayoutAttr inferBroadcastSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > resShape, ArrayRef< int64_t > srcShape)
Infers the source layout attribute for a broadcast operation given the result layout attribute,...
std::optional< std::tuple< DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr > > setupDpasMxLayout(LayoutKind layoutKind, VectorType aTy, VectorType bTy, VectorType cdTy, VectorType aScaleTy, VectorType bScaleTy, DistributeLayoutAttr consumerLayout, int numSg, const uArch::uArch *uArch)
Sets up the anchor layouts for dpas_mx operands (A, B, C/D, A_scale, and B_scale).
DistributeLayoutAttr setupStoreMatrixAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, const uArch::uArch *uArch)
Sets up the anchor layout for a store matrix operation.
SliceAttr setupMultiReductionResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, DistributeLayoutAttr consumerLayout, SmallVector< int64_t > reductionDims, int numSg, const uArch::uArch *uArch)
Note on the consumerLayout argument used by the consumer-driven setup* / complete* helpers below:
DistributeLayoutAttr setupLoadGatherAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Sets up the anchor layout for a load gather operation.
llvm::function_ref< DistributeLayoutAttr(Value)> GetLayoutFnTy
Callable returning the propagated layout for a given Value, used by the layout-propagation helpers be...
std::optional< DistributeLayoutAttr > completeScatterLoadLaneLayoutFromInstData(DistributeLayoutAttr userSpecifiedLayout, DistributeLayoutAttr consumerLayout, Type elemTy, const xegpu::uArch::LoadGatherInstruction *uArchInstruction, const int subgroupSize)
If the consumer layout has only inst_data (no lane_layout/lane_data), completes it by running the cor...
bool matchSplitDimExpansion(ArrayRef< int64_t > src, ArrayRef< int64_t > dst, SmallVector< SmallVector< int64_t > > &splitDimGroups)
DistributeLayoutAttr setupBitCastResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, VectorType resVectorTy, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Setup the result layout attribute for a bitcast operation based on element type bitwidths.
void removeLayoutAttr(const T &operandOrResult)
Removes the LayoutAttr for a given OpOperand or OpResult if it exists.
DistributeLayoutAttr inferMaskOffsetLayoutForScatterIO(DistributeLayoutAttr payloadLayout, int chunkSize)
Infers the layout attribute for mask and offset operand for Chunked load and store,...
DistributeLayoutAttr getDistributeLayoutAttr(const Value value)
Retrieves the DistributeLayoutAttr associated with a given Value.
SmallVector< NamedAttribute > dropSgLayoutAndDataOnAttrs(ArrayRef< NamedAttribute > attrs)
Updates the NamedAttribute sequence by dropping sg-layout and sg-data information from any Distribute...
DistributeLayoutAttr setupPrefetchNdAnchorLayout(LayoutKind layoutKind, TensorDescType tdescTy, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a prefetch_nd operation.
DistributeLayoutAttr inferExtractSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > resShape, ArrayRef< int64_t > srcShape)
Infers the source layout attribute for an extract operation.
std::string getTemporaryLayoutName(const OpOperand &operand)
Return the attribute name for the OpOperand to attach DistributeLayoutAttr.
DistributeLayoutAttr inferBitCastSourceLayout(DistributeLayoutAttr resLayout, int resElemTyBitWidth, int srcElemTyBitWidth)
Infers the source layout attribute for a bitcast operation given the result layout attribute,...
DistributeLayoutAttr setupInsertStridedSliceResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, VectorType resVectorTy, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Sets up the result layout for an insert strided slice operation.
DistributeLayoutAttr inferReductionSourceLayout(DistributeLayoutAttr resLayout)
Infers the source layout attribute for a reduction operation given the result layout attribute and re...
std::optional< DistributeLayoutAttr > completeScatterStoreLaneLayoutFromInstData(DistributeLayoutAttr specifiedLayout, Type elemTy, const xegpu::uArch::StoreScatterInstruction *uArchInstruction, const int subgroupSize)
Like completeScatterLoadLaneLayoutFromInstData, but for scatter stores (store_scatter / store_matrix)...
std::optional< DistributeLayoutAttr > completeBlockStoreLaneLayoutFromInstData(DistributeLayoutAttr specifiedLayout, Type elemTy, const xegpu::uArch::BlockIOInstructionInterface *uArchInstruction, const int subgroupSize)
Completes a user-provided 2D-block store_nd / prefetch_nd anchor that has only inst_data.
DistributeLayoutAttr inferDeinterleaveSourceLayout(DistributeLayoutAttr resLayout)
Infers the source layout attribute for a deinterleave operation given the result layout attribute.
DistributeLayoutAttr getConsumerLayoutAt(OpOperand &operand)
Gets the expected layout for a given consumer operand.
void removeLayoutAttrs(Operation *op)
Removes the DistributeLayoutAttr for each OpOperand and OpResult of the given operation if they exist...
DistributeLayoutAttr inferMultiReductionSourceLayout(DistributeLayoutAttr resLayout, SmallVector< int64_t > reduceDims)
Infers the source layout attribute for a reduction operation given the result layout attribute and re...
bool isTriviallyRematerializable(Operation *op)
Returns true if op is safe and cheap to clone: it has no side effects, no regions,...
DistributeLayoutAttr setupStoreNdAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a store_nd operation.
DistributeLayoutAttr setupStoreScatterAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, const uArch::uArch *uArch)
Sets up the anchor layout for a store scatter operation.
std::optional< DistributeLayoutAttr > completeBlockLoadLaneLayoutFromInstData(DistributeLayoutAttr specifiedLayout, DistributeLayoutAttr consumerLayout, Type elemTy, const xegpu::uArch::BlockIOInstructionInterface *uArchInstruction, const int subgroupSize)
Like completeBlockStoreLaneLayoutFromInstData, but for load_nd.
LogicalResult propagateRegionArgsToInits(RegionBranchOpInterface regionOp, GetLayoutFnTy getLayoutOfValue)
Propagate layouts from a region branch op's region entry block arguments back to its init operands.
std::optional< std::tuple< DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr > > setupDpasLayout(LayoutKind layoutKind, VectorType aTy, VectorType bTy, VectorType cdTy, DistributeLayoutAttr consumerLayout, int numSg, const uArch::uArch *uArch)
Sets up the anchor layouts for a dpas operands (A, B, and C/D).
SliceAttr setupReductionResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, const uArch::uArch *uArch)
Sets up layout for Reduction operations by creating a SliceAttr for the result.
Include the generated interface declarations.
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
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.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
virtual int32_t getPackedFormatBitSize() const =0
std::optional< BlockShapes > getBlockWidthHeightCount(Type elemTy, bool hasTransform=false, bool hasTranspose=false, bool upConv=false) const
Definition uArchBase.h:175
int32_t getMaxLaneAccessSizeBytes() const override
Definition uArchBase.h:216
virtual llvm::SmallVector< uint32_t, 8 > getSupportedN(Type type) const =0
virtual llvm::SmallVector< uint32_t, 8 > getSupportedK(Type type) const =0
virtual llvm::SmallVector< uint32_t, 8 > getSupportedM(Type type) const =0
int32_t getMaxLaneAccessSizeBytes() const override
Definition uArchBase.h:221
virtual int getSubgroupSize() const =0
const Instruction * getInstruction(InstructionKind instKind) const
Definition uArchBase.h:115