MLIR 24.0.0git
XeGPUPropagateLayout.cpp
Go to the documentation of this file.
1//===- XeGPUPropagateLayout.cpp - XeGPU Layout Propagation ------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
21#include "mlir/IR/Attributes.h"
22#include "mlir/IR/Builders.h"
25#include "mlir/IR/Operation.h"
26#include "mlir/IR/Value.h"
27#include "mlir/IR/Visitors.h"
31#include "mlir/Support/LLVM.h"
32#include "llvm/ADT/ArrayRef.h"
33#include "llvm/ADT/DenseMap.h"
34#include "llvm/ADT/STLExtras.h"
35#include "llvm/ADT/SmallSet.h"
36#include "llvm/ADT/SmallVector.h"
37#include "llvm/ADT/TypeSwitch.h"
38#include "llvm/Support/Casting.h"
39#include "llvm/Support/Debug.h"
40#include "llvm/Support/LogicalResult.h"
41#include "llvm/Support/MathExtras.h"
42#include "llvm/Support/raw_ostream.h"
43#include <limits>
44
45namespace mlir {
46namespace xegpu {
47#define GEN_PASS_DEF_XEGPUPROPAGATELAYOUT
48#include "mlir/Dialect/XeGPU/Transforms/Passes.h.inc"
49} // namespace xegpu
50} // namespace mlir
51
52#define DEBUG_TYPE "xegpu-propagate-layout"
53#define DBGS() (llvm::dbgs() << "[" DEBUG_TYPE "]: ")
54
55using namespace mlir;
56using namespace mlir::dataflow;
57
58namespace {
59
60//===----------------------------------------------------------------------===//
61// LayoutInfo
62//===----------------------------------------------------------------------===//
63
64/// Helper class for tracking the analysis state of an mlir value. For layout
65/// propagation, the analysis state is simply the distribution layout of
66/// each value. The distribution layout information is encapsulated using
67/// xegpu::DistributeLayoutAttr class which can hold information about any type
68/// of distribution layout that XeGPU dialect supports. Purpose of this analysis
69/// to propagate some unique distribution layout for each value in the program
70/// starting from a set of anchor operations (like DPAS, StoreNd, etc.). Note
71/// that analysis will reach a fixed point when all values are reached some
72/// layout and, analysis does not try to modify any already assigned layouts.
73///
74/// Given this, LayoutInfo satisifies the following properties:
75/// 1) A LayoutInfo value can be in one of two states - `assigned` or `not
76/// assigned`.
77/// 2) Two LayoutInfo values are equal if they are both not assigned, or both
78/// assigned with the same layout.
79/// 3) The meet operator works as follows:
80/// - If only one side is assigned, return that side.
81/// - If both sides are assigned, prefer the layout demanded by the user
82/// that is nearer to the producer in program order (smaller
83/// `programOrder`); on a tie keep lhs.
84///
85/// The `programOrder` field records the program-order index of the consumer op
86/// that demanded the layout (stamped via
87/// `LayoutInfoPropagation::makeLayoutInfo` from
88/// `LayoutInfoPropagation::currentProgramOrder`). During this backward analysis
89/// a value can be demanded by several users; keeping the nearest one tends to
90/// preserve a consumer's layout as far up the def chain as possible, minimizing
91/// layout conversions. This is a hint, not an optimum. `programOrder` is never
92/// propagated up the chain - each visited op stamps its own index - so it is
93/// excluded from `operator==`.
94
95struct LayoutInfo {
96private:
97 xegpu::DistributeLayoutAttr storage = nullptr;
98 // Program-order index of the consumer op that demanded this layout. Smaller
99 // means nearer to the producer. Unassigned/unknown demands sort last.
100 int64_t programOrder = std::numeric_limits<int64_t>::max();
101
102public:
103 LayoutInfo() = default;
104 LayoutInfo(const xegpu::DistributeLayoutAttr &layout, int64_t programOrder)
105 : storage(layout), programOrder(programOrder) {}
106
107 // Equality by assignment state and, when both assigned, by the layout:
108 // - one assigned, the other not -> not equal;
109 // - both unassigned -> equal;
110 // - both assigned -> equal iff the layouts match.
111 bool operator==(const LayoutInfo &other) const {
112 if (isAssigned() != other.isAssigned())
113 return false;
114 if (!isAssigned())
115 return true;
116 return storage.isEqualTo(other.storage);
117 }
118
119 static LayoutInfo meet(const LayoutInfo &lhs, const LayoutInfo &rhs);
120
121 static LayoutInfo join(const LayoutInfo &lhs, const LayoutInfo &rhs);
122
123 void print(raw_ostream &os) const;
124
125 bool isAssigned() const { return storage != nullptr; }
126
127 SmallVector<int> getLaneLayout() const;
128
129 SmallVector<int> getLaneData() const;
130
131 SmallVector<int> getInstData() const;
132
133 SmallVector<int> getSgLayout() const;
134
135 SmallVector<int> getSgData() const;
136
137 SmallVector<int> getOrder() const;
138
139 bool isSliceLayout() const {
140 if (!isAssigned())
141 return false;
142 return isa<xegpu::SliceAttr>(storage);
143 }
144
145 int64_t getRank() const {
146 if (!isAssigned())
147 return -1;
148 return storage.getRank();
149 }
150
151 Attribute get() { return storage; }
152 void set(const xegpu::DistributeLayoutAttr &layout) { storage = layout; }
153};
154
155void LayoutInfo::print(raw_ostream &os) const {
156 if (isAssigned()) {
157 os << storage;
158 } else {
159 os << "Not assigned.";
160 }
161}
162
163LayoutInfo LayoutInfo::meet(const LayoutInfo &lhs, const LayoutInfo &rhs) {
164 if (!lhs.isAssigned())
165 return rhs;
166 if (!rhs.isAssigned())
167 return lhs;
168 // Prefer the demand from the user nearer to the producer in program order.
169 // Distinct users always have distinct indices, so this decides every
170 // real conflict; on a tie (same op, or both unknown) keep lhs.
171 if (rhs.programOrder < lhs.programOrder)
172 return rhs;
173 return lhs;
174}
175
176/// Since this is a backward analysis, join method is not used.
177LayoutInfo LayoutInfo::join(const LayoutInfo &lhs, const LayoutInfo &rhs) {
178 llvm_unreachable("Join should not be triggered by layout propagation.");
179}
180
181//===----------------------------------------------------------------------===//
182// LayoutInfoLattice
183//===----------------------------------------------------------------------===//
184
185/// Lattice holding the LayoutInfo for each value.
186struct LayoutInfoLattice : public Lattice<LayoutInfo> {
188 using Lattice::Lattice;
189};
190
191//===----------------------------------------------------------------------===//
192// LayoutInfoPropagation
193//===----------------------------------------------------------------------===//
194
195/// Backward data flow analysis to propagate the lane_layout and lane_data of
196/// each value in the program. Currently, the layouts for operands DPAS,
197/// StoreNd, and StoreScatter are fixed (known before propagation). Purpose of
198/// this analysis is to propagate those known layouts to all their producers and
199/// (other) consumers.
200class LayoutInfoPropagation
201 : public SparseBackwardDataFlowAnalysis<LayoutInfoLattice> {
202public:
204
205private:
206 xegpu::LayoutKind layoutKind;
207 unsigned indexBitWidth;
208
209 // Program-order index of every op, built lazily on first use via a pre-order
210 // walk of the top-level module/function (matching printed-IR order). Used to
211 // tell which consumer of a value is nearer to its producer.
213 // Returns the program-order index of `op`, populating `programOrder` from
214 // `op`'s top-level ancestor on first call.
215 int64_t getProgramOrder(Operation *op);
216
217 int64_t currentProgramOrder = std::numeric_limits<int64_t>::max();
218 LayoutInfo makeLayoutInfo(const xegpu::DistributeLayoutAttr &layout) {
219 return LayoutInfo(layout, currentProgramOrder);
220 }
221
222 void visitDpasOp(xegpu::DpasOp dpas, ArrayRef<LayoutInfoLattice *> operands,
224
225 void visitDpasMxOp(xegpu::DpasMxOp dpasMx,
228
229 void visitStoreNdOp(xegpu::StoreNdOp store,
232
233 void visitStoreScatterOp(xegpu::StoreScatterOp storeScatter,
236
237 void visitLoadNdOp(xegpu::LoadNdOp load,
240
241 void visitLoadGatherOp(xegpu::LoadGatherOp load,
244
245 void visitTransposeOp(vector::TransposeOp transpose,
248
249 void visitVectorBitcastOp(vector::BitCastOp bitcast,
252
253 void visitVectorInterleaveOp(vector::InterleaveOp interleave,
256
257 void visitVectorDeinterleaveOp(vector::DeinterleaveOp deinterleave,
260
261 void visitPrefetchNdOp(xegpu::PrefetchNdOp prefetch,
264
265 void visitVectorMultiReductionOp(vector::MultiDimReductionOp reduction,
268
269 void visitVectorReductionOp(vector::ReductionOp reduction,
272
273 void visitVectorBroadCastOp(vector::BroadcastOp broadcast,
276 void visitShapeCastOp(vector::ShapeCastOp shapeCast,
279 void
280 visitInsertStridedSliceOp(vector::InsertStridedSliceOp insertStridedSlice,
283
284 void visitLoadMatrixOp(xegpu::LoadMatrixOp load,
287
288 void visitStoreMatrixOp(xegpu::StoreMatrixOp store,
291
292 void visitLoadGatherOp(xegpu::LoadMatrixOp load,
295
296 void visitStoreScatterOp(xegpu::StoreMatrixOp store,
299
300 void visitConvertLayoutOp(xegpu::ConvertLayoutOp convertLayout,
303
304 bool hasParamsOfLayoutKind(xegpu::DistributeLayoutAttr anchorLayout);
305
306 // Number of subgroups for `op`. Missing count is fatal in subgroup mode.
307 FailureOr<int64_t> getNumSgOrFail(Operation *op, int sgSize,
308 xegpu::DistributeLayoutAttr consumerLayout);
309
310 // Channel to surface hard failures out of the void visit callbacks.
311 bool propagationFailed = false;
312
313 // Reserved for the anchor ops that are the sources of the propagation
314 // (store/dpas), whose layout must be correct.
315 void markFailure(Operation *op, const llvm::Twine &message) {
316 op->emitError(message);
317 propagationFailed = true;
318 }
319
320public:
321 bool hasFailed() const { return propagationFailed; }
322
323 LayoutInfoPropagation(DataFlowSolver &solver,
324 SymbolTableCollection &symbolTable,
325 xegpu::LayoutKind layoutKind, unsigned indexBitWidth)
326 : SparseBackwardDataFlowAnalysis(solver, symbolTable),
327 layoutKind(layoutKind), indexBitWidth(indexBitWidth) {}
329
330 LogicalResult
331 visitOperation(Operation *op, ArrayRef<LayoutInfoLattice *> operands,
332 ArrayRef<const LayoutInfoLattice *> results) override;
333
334 void visitBranchOperand(OpOperand &operand) override {};
335
336 void visitCallOperand(OpOperand &operand) override {};
337
338 void
339 visitNonControlFlowArguments(RegionSuccessor &successor,
340 ArrayRef<BlockArgument> arguments) override {};
341
342 void visitExternalCall(CallOpInterface call,
344 ArrayRef<const LayoutInfoLattice *> results) override {
345 };
346
347 void setToExitState(LayoutInfoLattice *lattice) override {
348 (void)lattice->meet(LayoutInfo());
349 }
350};
351} // namespace
352
353int64_t LayoutInfoPropagation::getProgramOrder(Operation *op) {
354 auto it = programOrder.find(op);
355 if (it != programOrder.end())
356 return it->second;
357 // First time we see this op's tree: number every op under its top-level
358 // ancestor in pre-order (i.e. printed-IR order). Nested ops (e.g. inside an
359 // scf.for body) get an index between their parent and the parent's next
360 // sibling, so a use inside a loop is "nearer" than a use after it.
361 Operation *root = op;
362 while (root->getParentOp())
363 root = root->getParentOp();
364 int64_t counter = 0;
365 root->walk<WalkOrder::PreOrder>(
366 [&](Operation *o) { programOrder[o] = counter++; });
367 return programOrder.lookup(op);
368}
369
370LogicalResult LayoutInfoPropagation::visitOperation(
371 Operation *op, ArrayRef<LayoutInfoLattice *> operands,
372 ArrayRef<const LayoutInfoLattice *> results) {
373 // Stamp demands pushed by this op with its program-order index so `meet` can
374 // prefer the nearest consumer.
375 currentProgramOrder = getProgramOrder(op);
377 .Case(
378 [&](xegpu::DpasOp dpasOp) { visitDpasOp(dpasOp, operands, results); })
379 .Case([&](xegpu::DpasMxOp dpasMxOp) {
380 visitDpasMxOp(dpasMxOp, operands, results);
381 })
382 .Case([&](xegpu::StoreNdOp storeNdOp) {
383 visitStoreNdOp(storeNdOp, operands, results);
384 })
385 .Case([&](xegpu::StoreScatterOp storeScatterOp) {
386 visitStoreScatterOp(storeScatterOp, operands, results);
387 })
388 .Case([&](xegpu::LoadNdOp loadNdOp) {
389 visitLoadNdOp(loadNdOp, operands, results);
390 })
391 .Case([&](xegpu::LoadGatherOp loadGatherOp) {
392 visitLoadGatherOp(loadGatherOp, operands, results);
393 })
394 .Case([&](xegpu::PrefetchNdOp prefetchNdOp) {
395 visitPrefetchNdOp(prefetchNdOp, operands, results);
396 })
397 .Case([&](vector::TransposeOp transposeOp) {
398 visitTransposeOp(transposeOp, operands, results);
399 })
400 .Case([&](vector::BitCastOp bitcastOp) {
401 visitVectorBitcastOp(bitcastOp, operands, results);
402 })
403 .Case([&](vector::InterleaveOp interleaveOp) {
404 visitVectorInterleaveOp(interleaveOp, operands, results);
405 })
406 .Case([&](vector::DeinterleaveOp deinterleaveOp) {
407 visitVectorDeinterleaveOp(deinterleaveOp, operands, results);
408 })
409 .Case([&](vector::MultiDimReductionOp reductionOp) {
410 visitVectorMultiReductionOp(reductionOp, operands, results);
411 })
412 .Case([&](vector::ReductionOp reductionOp) {
413 visitVectorReductionOp(reductionOp, operands, results);
414 })
415 .Case([&](vector::BroadcastOp broadcastOp) {
416 visitVectorBroadCastOp(broadcastOp, operands, results);
417 })
418 .Case([&](vector::ShapeCastOp shapeCastOp) {
419 visitShapeCastOp(shapeCastOp, operands, results);
420 })
421 .Case([&](vector::InsertStridedSliceOp insertStridedSliceOp) {
422 visitInsertStridedSliceOp(insertStridedSliceOp, operands, results);
423 })
424 .Case([&](xegpu::LoadMatrixOp loadMatrixOp) {
425 visitLoadMatrixOp(loadMatrixOp, operands, results);
426 })
427 .Case([&](xegpu::StoreMatrixOp storeMatrixOp) {
428 visitStoreMatrixOp(storeMatrixOp, operands, results);
429 })
430 .Case([&](xegpu::ConvertLayoutOp convertLayoutOp) {
431 visitConvertLayoutOp(convertLayoutOp, operands, results);
432 })
433 // All other ops.
434 .Default([&](Operation *op) {
435 for (const LayoutInfoLattice *resultInfo : results) {
436 if (!resultInfo->getValue().isAssigned())
437 continue;
438 for (auto [operandInfo, operand] :
439 llvm::zip(operands, op->getOpOperands())) {
440 // If the operand type is not a vector or tensor descriptor, skip
441 // it.
442 if (!isa<xegpu::TensorDescType, VectorType>(
443 operand.get().getType()))
444 continue;
445 // Propagate the result layout to the operand.
446 meet(operandInfo, *resultInfo);
447 }
448 }
449 });
450
451 return success();
452}
453
454bool LayoutInfoPropagation::hasParamsOfLayoutKind(
455 xegpu::DistributeLayoutAttr anchorLayout) {
456 if (anchorLayout == nullptr) {
457 return false;
458 }
459 if (layoutKind == xegpu::LayoutKind::InstData) {
460 return !(anchorLayout.getEffectiveInstDataAsInt().empty());
461 }
462 if (layoutKind == xegpu::LayoutKind::Lane) {
463 return !(anchorLayout.getEffectiveLaneLayoutAsInt().empty() ||
464 anchorLayout.getEffectiveLaneDataAsInt().empty());
465 }
466 if (layoutKind == xegpu::LayoutKind::Subgroup) {
467 return !(anchorLayout.getEffectiveSgLayoutAsInt().empty() ||
468 anchorLayout.getEffectiveSgDataAsInt().empty());
469 }
470 return false;
471}
472
473FailureOr<int64_t> LayoutInfoPropagation::getNumSgOrFail(
474 Operation *op, int sgSize, xegpu::DistributeLayoutAttr consumerLayout) {
475 // The consumer's sg_layout, when present, dictates the count.
476 if (consumerLayout) {
477 auto sgLayout = consumerLayout.getEffectiveSgLayoutAsInt();
478 if (!sgLayout.empty())
479 return llvm::product_of(sgLayout);
480 }
481 // Otherwise fall back to the kernel's known_block_size.
482 if (auto gpuFunc = op->getParentOfType<gpu::GPUFuncOp>()) {
483 std::optional<ArrayRef<int32_t>> knownBlockSize =
484 gpuFunc.getKnownBlockSize();
485 if (knownBlockSize) {
486 bool isPowerOf2Block = llvm::all_of(*knownBlockSize, [](int32_t dim) {
487 return dim > 0 && llvm::isPowerOf2_32(dim);
488 });
489 int64_t numSg = llvm::product_of(*knownBlockSize) / sgSize;
490 if (isPowerOf2Block && numSg > 0)
491 return numSg;
492 }
493 }
494 // Only subgroup mode needs the count; elsewhere a missing one is benign.
495 if (layoutKind == xegpu::LayoutKind::Subgroup) {
496 markFailure(op, "Unable to determine the number of subgroups for the "
497 "operation. Please check @known_block_size is properly "
498 "attached as kernel attributes, with power-of-two "
499 "dimensions covering at least one subgroup.");
500 return failure();
501 }
502 return int64_t{0};
503}
504
505void LayoutInfoPropagation::visitPrefetchNdOp(
506 xegpu::PrefetchNdOp prefetch, ArrayRef<LayoutInfoLattice *> operands,
507 ArrayRef<const LayoutInfoLattice *> results) {
508
509 LayoutInfo prefetchLayout;
510 const auto *uArch = xegpu::uArch::getUArch(getChipStr(prefetch).value_or(""));
511 if (!uArch)
512 return;
513 xegpu::DistributeLayoutAttr anchorLayout = prefetch.getLayoutAttr();
514 if (hasParamsOfLayoutKind(anchorLayout)) {
515 prefetchLayout = makeLayoutInfo(anchorLayout);
516 if (layoutKind == xegpu::LayoutKind::InstData) {
517 const auto *uArchInstruction =
518 dyn_cast<xegpu::uArch::Subgroup2DBlockPrefetchInstruction>(
519 uArch->getInstruction(
520 xegpu::uArch::InstructionKind::Subgroup2DBlockPrefetch));
521 if (!uArchInstruction)
522 return;
524 anchorLayout, prefetch.getTensorDescType().getElementType(),
525 uArchInstruction, uArch->getSubgroupSize());
526 if (!completed) {
527 prefetch.emitWarning(
528 "Failed to identify lane layouts for the specified inst_data.");
529 return;
530 }
531 prefetch.setLayoutAttr(*completed);
532 prefetchLayout = makeLayoutInfo(*completed);
533 }
534 } else {
535 auto tdescTy = prefetch.getTensorDescType();
536 auto numSgOrErr =
537 getNumSgOrFail(prefetch, uArch->getSubgroupSize(), nullptr);
538 if (failed(numSgOrErr))
539 return;
540
541 auto layoutAttr = xegpu::setupPrefetchNdAnchorLayout(
542 layoutKind, tdescTy, numSgOrErr.value_or(0), uArch);
543 if (!layoutAttr) {
544 prefetch.emitWarning(
545 "Failed to determine required layout for prefetch_nd.");
546 return;
547 }
548 prefetchLayout = makeLayoutInfo(layoutAttr);
549 prefetch.setLayoutAttr(layoutAttr);
550 }
551 // Propagate the layout to the source tensor descriptor.
552 propagateIfChanged(operands[0], operands[0]->meet(prefetchLayout));
553}
554
555void LayoutInfoPropagation::visitVectorMultiReductionOp(
556 vector::MultiDimReductionOp reduction,
557 ArrayRef<LayoutInfoLattice *> operands,
558 ArrayRef<const LayoutInfoLattice *> results) {
559 Type resultTy = reduction.getDestType();
560 // The layout of the result must be present.
561 LayoutInfo resLayoutInfo = results[0]->getValue();
562
563 xegpu::DistributeLayoutAttr consumerLayoutAttr;
564 if (!resultTy.isIntOrFloat()) {
565 if (!resLayoutInfo.isAssigned())
566 return;
567 consumerLayoutAttr =
568 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
569 }
570
571 VectorType sourceTy = reduction.getSourceVectorType();
572 SmallVector<int64_t> reductionDims(reduction.getReductionDims());
573
574 const auto *uArch =
575 xegpu::uArch::getUArch(xegpu::getChipStr(reduction).value_or(""));
576 if (!uArch)
577 return;
578
579 auto numSgOrErr =
580 getNumSgOrFail(reduction, uArch->getSubgroupSize(), consumerLayoutAttr);
581 if (failed(numSgOrErr))
582 return;
583
584 // The result layout represents the layout requirements of the operation.
585 // it is recorded to anchor layout or temporary layout.
586 // it must be honored for current op and may conflict with the layout
587 // propagated from consumer op, the conflict is resolved in later phase by
588 // converting the required result layout to the consumer layout
589 auto requiredResLayoutAttr = xegpu::setupMultiReductionResultLayout(
590 layoutKind, sourceTy, consumerLayoutAttr, reductionDims,
591 numSgOrErr.value_or(0), uArch);
592
593 xegpu::setTemporaryLayout(reduction->getResult(0), requiredResLayoutAttr);
594
595 // derive the source layout from the dominant layout and reduction dims
596 auto srcLayoutAttr = xegpu::inferMultiReductionSourceLayout(
597 requiredResLayoutAttr, reductionDims);
598
599 propagateIfChanged(operands[0],
600 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
601 // Accumulator should have the same layout as the result.
602 propagateIfChanged(operands[1],
603 operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
604}
605
606void LayoutInfoPropagation::visitVectorReductionOp(
607 vector::ReductionOp reduction, ArrayRef<LayoutInfoLattice *> operands,
608 ArrayRef<const LayoutInfoLattice *> results) {
609
610 VectorType sourceTy = reduction.getSourceVectorType();
611 const auto *uArch =
612 xegpu::uArch::getUArch(xegpu::getChipStr(reduction).value_or(""));
613 if (!uArch)
614 return;
615
616 auto requiredResLayoutAttr =
617 xegpu::setupReductionResultLayout(layoutKind, sourceTy, uArch);
618 xegpu::setTemporaryLayout(reduction->getResult(0), requiredResLayoutAttr);
619
620 auto srcLayoutAttr = xegpu::inferReductionSourceLayout(requiredResLayoutAttr);
621 propagateIfChanged(operands[0],
622 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
623 if (reduction.getAcc())
624 propagateIfChanged(
625 operands[1], operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
626}
627
628void LayoutInfoPropagation::visitVectorBroadCastOp(
629 vector::BroadcastOp broadcast, ArrayRef<LayoutInfoLattice *> operands,
630 ArrayRef<const LayoutInfoLattice *> results) {
631 // The layout of the result must be present.
632 LayoutInfo resLayoutInfo = results[0]->getValue();
633 if (!resLayoutInfo.isAssigned())
634 return;
635
636 // Only consider vector to vector broadcasts for now.
637 VectorType resultTy = broadcast.getResultVectorType();
638 VectorType sourceTy = dyn_cast<VectorType>(broadcast.getSourceType());
639 // skip layout propagation for non-vector source operand.
640 if (!sourceTy)
641 return;
642
643 auto srcShape = sourceTy.getShape();
644 auto resShape = resultTy.getShape();
645
646 auto resultLayoutAttr =
647 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
648
649 xegpu::DistributeLayoutAttr srcLayoutAttr =
650 xegpu::inferBroadcastSourceLayout(resultLayoutAttr, resShape, srcShape);
651
652 propagateIfChanged(operands[0],
653 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
654}
655
656void LayoutInfoPropagation::visitShapeCastOp(
657 vector::ShapeCastOp shapeCast, ArrayRef<LayoutInfoLattice *> operands,
658 ArrayRef<const LayoutInfoLattice *> results) {
659 // The layout of the result must be present.
660 LayoutInfo resLayoutInfo = results[0]->getValue();
661 if (!resLayoutInfo.isAssigned())
662 return;
663 ArrayRef<int64_t> resShape = shapeCast.getResultVectorType().getShape();
664 ArrayRef<int64_t> srcShape = shapeCast.getSourceVectorType().getShape();
665 auto resultLayoutAttr =
666 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
667
668 xegpu::DistributeLayoutAttr srcLayoutAttr =
669 xegpu::inferShapeCastSourceLayout(resultLayoutAttr, resShape, srcShape);
670 // shape_cast is not an anchor op: another consumer of the source value may
671 // still supply a valid layout, so warn instead of stopping the propagation.
672 if (!srcLayoutAttr) {
673 shapeCast.emitWarning("Failed to infer source layout for shape_cast; "
674 "unsupported shape-cast pattern.");
675 return;
676 }
677
678 propagateIfChanged(operands[0],
679 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
680}
681
682/// Set the layouts for DPAS A, B, and C operands.
683void LayoutInfoPropagation::visitDpasOp(
684 xegpu::DpasOp dpas, ArrayRef<LayoutInfoLattice *> operands,
685 ArrayRef<const LayoutInfoLattice *> results) {
686 LayoutInfo dpasALayout;
687 LayoutInfo dpasBLayout;
688 LayoutInfo dpasCDLayout;
689
690 const auto *uArch = xegpu::uArch::getUArch(getChipStr(dpas).value_or(""));
691 if (!uArch)
692 return;
693 VectorType aTy = dpas.getLhsType();
694 VectorType bTy = dpas.getRhsType();
695 VectorType cdTy = dpas.getResultType();
696
697 xegpu::DistributeLayoutAttr anchorLayoutCD = dpas.getLayoutCdAttr();
698 if (hasParamsOfLayoutKind(anchorLayoutCD)) {
699 xegpu::DistributeLayoutAttr anchorLayoutA = dpas.getLayoutAAttr();
700 xegpu::DistributeLayoutAttr anchorLayoutB = dpas.getLayoutBAttr();
701 assert(hasParamsOfLayoutKind(anchorLayoutA) &&
702 "Expected anchor layout for DPAS A operand.");
703 assert(hasParamsOfLayoutKind(anchorLayoutB) &&
704 "Expected anchor layout for DPAS B operand.");
705 dpasALayout = makeLayoutInfo(anchorLayoutA);
706 dpasBLayout = makeLayoutInfo(anchorLayoutB);
707 dpasCDLayout = makeLayoutInfo(anchorLayoutCD);
708 if (layoutKind == xegpu::LayoutKind::InstData) {
710 anchorLayoutA, anchorLayoutB, anchorLayoutCD, aTy, bTy, cdTy, uArch);
711 if (!completed) {
712 markFailure(
713 dpas,
714 "Failed to identify lane layouts for the specified inst_data.");
715 return;
716 }
717 auto [completedA, completedB, completedCD] = *completed;
718 dpas.setLayoutAAttr(completedA);
719 dpas.setLayoutBAttr(completedB);
720 dpas.setLayoutCdAttr(completedCD);
721 dpasALayout = makeLayoutInfo(completedA);
722 dpasBLayout = makeLayoutInfo(completedB);
723 dpasCDLayout = makeLayoutInfo(completedCD);
724 }
725 } else {
726
727 xegpu::DistributeLayoutAttr consumerLayoutAttr = nullptr;
728 xegpu::DistributeLayoutAttr requiredCDLayoutAttr, requiredALayout,
729 requiredBLayout;
730
731 LayoutInfo consumerLayout = results[0]->getValue();
732 if (!consumerLayout.isAssigned())
733 return;
734 consumerLayoutAttr =
735 dyn_cast<xegpu::DistributeLayoutAttr>(consumerLayout.get());
736
737 auto numSgOrErr =
738 getNumSgOrFail(dpas, uArch->getSubgroupSize(), consumerLayoutAttr);
739 if (failed(numSgOrErr))
740 return;
741
742 auto layouts =
743 xegpu::setupDpasLayout(layoutKind, aTy, bTy, cdTy, consumerLayoutAttr,
744 numSgOrErr.value_or(0), uArch);
745 if (!layouts.has_value()) {
746 markFailure(dpas,
747 "Failed to determine required layouts for DPAS operands.");
748 return;
749 }
750
751 std::tie(requiredALayout, requiredBLayout, requiredCDLayoutAttr) = *layouts;
752
753 dpas.setLayoutAAttr(requiredALayout);
754 dpas.setLayoutBAttr(requiredBLayout);
755 dpas.setLayoutCdAttr(requiredCDLayoutAttr);
756 dpasALayout = makeLayoutInfo(requiredALayout);
757 dpasBLayout = makeLayoutInfo(requiredBLayout);
758 dpasCDLayout = makeLayoutInfo(requiredCDLayoutAttr);
759 }
760 propagateIfChanged(operands[0], operands[0]->meet(dpasALayout));
761 propagateIfChanged(operands[1], operands[1]->meet(dpasBLayout));
762 if (operands.size() > 2)
763 propagateIfChanged(operands[2], operands[2]->meet(dpasCDLayout));
764}
765
766/// Propagate layout for DpasMxOp operands using the layout attributes.
767/// DpasMxOp has operands: a, b, acc (optional), scale_a (optional), scale_b
768/// (optional)
769void LayoutInfoPropagation::visitDpasMxOp(
770 xegpu::DpasMxOp dpasMx, ArrayRef<LayoutInfoLattice *> operands,
771 ArrayRef<const LayoutInfoLattice *> results) {
772
773 // Initialize layout variables
774 LayoutInfo dpasMxALayout, dpasMxBLayout, dpasMxCDLayout;
775 LayoutInfo dpasMxAScaleLayout, dpasMxBScaleLayout;
776
777 // Get existing layout attributes from the operation
778 xegpu::DistributeLayoutAttr anchorLayoutA = dpasMx.getLayoutAAttr();
779 xegpu::DistributeLayoutAttr anchorLayoutB = dpasMx.getLayoutBAttr();
780 xegpu::DistributeLayoutAttr anchorLayoutCD = dpasMx.getLayoutCdAttr();
781
782 const auto *uArch = xegpu::uArch::getUArch(getChipStr(dpasMx).value_or(""));
783 if (!uArch)
784 return;
785
786 VectorType aTy = dpasMx.getAType();
787 VectorType bTy = dpasMx.getBType();
788 VectorType cdTy = dpasMx.getResultType();
789
790 // Get scale types if present
791 VectorType aScaleTy;
792 VectorType bScaleTy;
793 Value scaleA = dpasMx.getScaleA();
794 Value scaleB = dpasMx.getScaleB();
795 if (scaleA)
796 aScaleTy = dyn_cast<VectorType>(scaleA.getType());
797 if (scaleB)
798 bScaleTy = dyn_cast<VectorType>(scaleB.getType());
799
800 // Check if all layouts are already set
801 if (anchorLayoutA && anchorLayoutB && anchorLayoutCD &&
802 hasParamsOfLayoutKind(anchorLayoutA) &&
803 hasParamsOfLayoutKind(anchorLayoutB) &&
804 hasParamsOfLayoutKind(anchorLayoutCD)) {
805 dpasMxALayout = makeLayoutInfo(anchorLayoutA);
806 dpasMxBLayout = makeLayoutInfo(anchorLayoutB);
807 dpasMxCDLayout = makeLayoutInfo(anchorLayoutCD);
808
809 // Get scale layouts if available
810 xegpu::DistributeLayoutAttr anchorLayoutAScale =
811 dpasMx.getLayoutAScaleAttr();
812 xegpu::DistributeLayoutAttr anchorLayoutBScale =
813 dpasMx.getLayoutBScaleAttr();
814 if (anchorLayoutAScale)
815 dpasMxAScaleLayout = makeLayoutInfo(anchorLayoutAScale);
816 if (anchorLayoutBScale)
817 dpasMxBScaleLayout = makeLayoutInfo(anchorLayoutBScale);
818
819 if (layoutKind == xegpu::LayoutKind::InstData) {
821 anchorLayoutA, anchorLayoutB, anchorLayoutCD, aTy, bTy, cdTy,
822 aScaleTy, bScaleTy, uArch);
823 if (!completed) {
824 markFailure(
825 dpasMx,
826 "Failed to identify lane layouts for the specified inst_data.");
827 return;
828 }
829 auto [completedA, completedB, completedCD, completedAScale,
830 completedBScale] = *completed;
831 dpasMx.setLayoutAAttr(completedA);
832 dpasMx.setLayoutBAttr(completedB);
833 dpasMx.setLayoutCdAttr(completedCD);
834 dpasMxALayout = makeLayoutInfo(completedA);
835 dpasMxBLayout = makeLayoutInfo(completedB);
836 dpasMxCDLayout = makeLayoutInfo(completedCD);
837 if (completedAScale) {
838 dpasMx.setLayoutAScaleAttr(completedAScale);
839 dpasMxAScaleLayout = makeLayoutInfo(completedAScale);
840 }
841 if (completedBScale) {
842 dpasMx.setLayoutBScaleAttr(completedBScale);
843 dpasMxBScaleLayout = makeLayoutInfo(completedBScale);
844 }
845 }
846 } else {
847 xegpu::DistributeLayoutAttr consumerLayoutAttr = nullptr;
848 xegpu::DistributeLayoutAttr requiredCDLayoutAttr, requiredALayout,
849 requiredBLayout, requiredAScaleLayout, requiredBScaleLayout;
850
851 LayoutInfo consumerLayout = results[0]->getValue();
852 if (!consumerLayout.isAssigned())
853 return;
854 consumerLayoutAttr =
855 dyn_cast<xegpu::DistributeLayoutAttr>(consumerLayout.get());
856
857 auto numSgOrErr =
858 getNumSgOrFail(dpasMx, uArch->getSubgroupSize(), consumerLayoutAttr);
859 if (failed(numSgOrErr))
860 return;
861
862 auto layouts = xegpu::setupDpasMxLayout(
863 layoutKind, aTy, bTy, cdTy, aScaleTy, bScaleTy, consumerLayoutAttr,
864 numSgOrErr.value_or(0), uArch);
865 if (!layouts.has_value()) {
866 markFailure(dpasMx,
867 "Failed to determine required layouts for DPAS_MX operands.");
868 return;
869 }
870
871 std::tie(requiredALayout, requiredBLayout, requiredCDLayoutAttr,
872 requiredAScaleLayout, requiredBScaleLayout) = *layouts;
873
874 dpasMx.setLayoutAAttr(requiredALayout);
875 dpasMx.setLayoutBAttr(requiredBLayout);
876 dpasMx.setLayoutCdAttr(requiredCDLayoutAttr);
877 if (requiredAScaleLayout)
878 dpasMx.setLayoutAScaleAttr(requiredAScaleLayout);
879 if (requiredBScaleLayout)
880 dpasMx.setLayoutBScaleAttr(requiredBScaleLayout);
881
882 dpasMxALayout = makeLayoutInfo(requiredALayout);
883 dpasMxBLayout = makeLayoutInfo(requiredBLayout);
884 dpasMxCDLayout = makeLayoutInfo(requiredCDLayoutAttr);
885 if (requiredAScaleLayout)
886 dpasMxAScaleLayout = makeLayoutInfo(requiredAScaleLayout);
887 if (requiredBScaleLayout)
888 dpasMxBScaleLayout = makeLayoutInfo(requiredBScaleLayout);
889 }
890
891 // Propagate layouts to operands. Because acc, scale_a, scale_b are all
892 // optional (AttrSizedOperandSegments), the index of each present operand in
893 // `operands` depends on which optionals are actually supplied. Use the
894 // op's accessors to determine the correct positional index.
895 propagateIfChanged(operands[0], operands[0]->meet(dpasMxALayout));
896 propagateIfChanged(operands[1], operands[1]->meet(dpasMxBLayout));
897 unsigned idx = 2;
898 if (dpasMx.getAcc()) {
899 propagateIfChanged(operands[idx], operands[idx]->meet(dpasMxCDLayout));
900 ++idx;
901 }
902 if (dpasMx.getScaleA()) {
903 if (dpasMxAScaleLayout.isAssigned())
904 propagateIfChanged(operands[idx],
905 operands[idx]->meet(dpasMxAScaleLayout));
906 ++idx;
907 }
908 if (dpasMx.getScaleB()) {
909 if (dpasMxBScaleLayout.isAssigned())
910 propagateIfChanged(operands[idx],
911 operands[idx]->meet(dpasMxBScaleLayout));
912 ++idx;
913 }
914}
915
916/// Set the layout for the value and tensor descriptor operands in StoreNdOp.
917void LayoutInfoPropagation::visitStoreNdOp(
918 xegpu::StoreNdOp store, ArrayRef<LayoutInfoLattice *> operands,
919 ArrayRef<const LayoutInfoLattice *> results) {
920 LayoutInfo storeLayout;
921 const auto *uArch = xegpu::uArch::getUArch(getChipStr(store).value_or(""));
922 if (!uArch)
923 return;
924 xegpu::DistributeLayoutAttr anchorLayout = store.getLayoutAttr();
925 if (hasParamsOfLayoutKind(anchorLayout)) {
926 storeLayout = makeLayoutInfo(anchorLayout);
927 if (layoutKind == xegpu::LayoutKind::InstData) {
928
929 const auto *uArchInstruction =
930 dyn_cast<xegpu::uArch::Subgroup2DBlockStoreInstruction>(
931 uArch->getInstruction(
932 xegpu::uArch::InstructionKind::Subgroup2DBlockStore));
933 if (!uArchInstruction)
934 return;
936 anchorLayout, store.getValueType().getElementType(), uArchInstruction,
937 uArch->getSubgroupSize());
938 if (!completed) {
939 markFailure(
940 store,
941 "Failed to identify lane layouts for the specified inst_data.");
942 return;
943 }
944 store.setLayoutAttr(*completed);
945 storeLayout = makeLayoutInfo(*completed);
946 }
947 } else {
948 auto numSgOrErr = getNumSgOrFail(store, uArch->getSubgroupSize(), nullptr);
949 if (failed(numSgOrErr))
950 return;
951
952 auto layoutAttr = xegpu::setupStoreNdAnchorLayout(
953 layoutKind, store.getValueType(), numSgOrErr.value_or(0), uArch);
954 if (!layoutAttr) {
955 markFailure(store, "Failed to determine required layout for store_nd.");
956 return;
957 }
958 storeLayout = makeLayoutInfo(layoutAttr);
959 store.setLayoutAttr(layoutAttr);
960 }
961 // Propagate the layout to the value operand.
962 // Both operands should have the same layout
963 for (LayoutInfoLattice *operand : operands)
964 propagateIfChanged(operand, operand->meet(storeLayout));
965}
966
967/// Propagate the layout of the value to the tensor descriptor operand in
968/// LoadNdOp.
969void LayoutInfoPropagation::visitLoadNdOp(
970 xegpu::LoadNdOp load, ArrayRef<LayoutInfoLattice *> operands,
971 ArrayRef<const LayoutInfoLattice *> results) {
972 LayoutInfo loadLayout;
973
974 const auto *uArch = xegpu::uArch::getUArch(getChipStr(load).value_or(""));
975 if (!uArch)
976 return;
977 LayoutInfo valueLayout = results[0]->getValue();
978 if (!valueLayout.isAssigned())
979 return;
980 auto consumerLayoutAttr =
981 dyn_cast<xegpu::DistributeLayoutAttr>(valueLayout.get());
982 xegpu::DistributeLayoutAttr anchorLayout = load.getLayoutAttr();
983 if (hasParamsOfLayoutKind(anchorLayout)) {
984 loadLayout = makeLayoutInfo(anchorLayout);
985 if (layoutKind == xegpu::LayoutKind::InstData &&
986 !consumerLayoutAttr.getEffectiveLaneLayoutAsInt().empty()) {
987 const auto *uArchInstruction =
988 dyn_cast<xegpu::uArch::Subgroup2DBlockLoadInstruction>(
989 uArch->getInstruction(
990 xegpu::uArch::InstructionKind::Subgroup2DBlockLoad));
991 if (!uArchInstruction)
992 return;
994 anchorLayout, consumerLayoutAttr, load.getType().getElementType(),
995 uArchInstruction, uArch->getSubgroupSize());
996 if (!completed) {
997 load.emitWarning(
998 "Failed to identify lane layouts for the specified inst_data.");
999 return;
1000 }
1001 load.setLayoutAttr(*completed);
1002 loadLayout = makeLayoutInfo(*completed);
1003 }
1004 } else {
1005 auto numSgOrErr =
1006 getNumSgOrFail(load, uArch->getSubgroupSize(), consumerLayoutAttr);
1007 if (failed(numSgOrErr))
1008 return;
1009 auto layoutAttr = xegpu::setupLoadNdAnchorLayout(
1010 layoutKind, load.getType(), consumerLayoutAttr, numSgOrErr.value_or(0),
1011 uArch);
1012 if (!layoutAttr) {
1013 load.emitWarning("Failed to determine required layout for load_nd.");
1014 return;
1015 }
1016 loadLayout = makeLayoutInfo(layoutAttr);
1017 load.setLayoutAttr(layoutAttr);
1018 }
1019 // Propagate the new layout to the tensor descriptor operand.
1020 propagateIfChanged(operands[0], operands[0]->meet(loadLayout));
1021}
1022
1023/// Propagate the layout of the value to the tensor descriptor operand in
1024/// ConvertLayoutOp.
1025void LayoutInfoPropagation::visitConvertLayoutOp(
1026 xegpu::ConvertLayoutOp convert, ArrayRef<LayoutInfoLattice *> operands,
1027 ArrayRef<const LayoutInfoLattice *> results) {
1028
1029 LayoutInfo resultLayout = results[0]->getValue();
1030
1031 // TODO: fix if one of the layouts is a slice layout
1032 auto targetLayoutAttr =
1033 dyn_cast<xegpu::LayoutAttr>(convert.getTargetLayoutAttr());
1034 // input_layout is optional, so it may be null.
1035 auto inputLayoutAttr =
1036 dyn_cast_if_present<xegpu::LayoutAttr>(convert.getInputLayoutAttr());
1037
1038 // The result's propagated layout is authoritative for the converted value.
1039 // Fill the lane_layout / lane_data / order parameters the target_layout is
1040 // missing from it (sg_layout / sg_data / inst_data are left as-is), so the
1041 // target stays consistent with what is actually propagated downstream.
1042 auto resultLayoutAttr = resultLayout.isAssigned()
1043 ? dyn_cast<xegpu::LayoutAttr>(resultLayout.get())
1044 : nullptr;
1045 if (resultLayoutAttr && targetLayoutAttr) {
1046 if (layoutKind == xegpu::LayoutKind::InstData &&
1047 !targetLayoutAttr.getLaneLayout()) {
1048 targetLayoutAttr = xegpu::LayoutAttr::get(
1049 convert.getContext(), targetLayoutAttr.getSgLayout(),
1050 targetLayoutAttr.getSgData(), targetLayoutAttr.getInstData(),
1051 resultLayoutAttr.getLaneLayout(), resultLayoutAttr.getLaneData(),
1052 resultLayoutAttr.getOrder());
1053 convert.setTargetLayoutAttr(targetLayoutAttr);
1054 }
1055 }
1056
1057 // Fill only the lane_layout / lane_data / order parameters the input_layout
1058 // is missing from the target_layout (sg_layout / sg_data / inst_data are left
1059 // as-is), so the producer side receives a fully-populated lane layout.
1060 if (inputLayoutAttr && targetLayoutAttr) {
1061 if (layoutKind == xegpu::LayoutKind::InstData &&
1062 !inputLayoutAttr.getLaneLayout()) {
1063 auto merged = xegpu::LayoutAttr::get(
1064 convert.getContext(), inputLayoutAttr.getSgLayout(),
1065 inputLayoutAttr.getSgData(), inputLayoutAttr.getInstData(),
1066 targetLayoutAttr.getLaneLayout(), targetLayoutAttr.getLaneData(),
1067 targetLayoutAttr.getOrder());
1068 convert.setInputLayoutAttr(merged);
1069 }
1070 }
1071
1072 xegpu::DistributeLayoutAttr anchorLayout = convert.getEffectiveInputLayout();
1073 LayoutInfo convertLayout = makeLayoutInfo(anchorLayout);
1074 // Propagate the new layout to the tensor descriptor operand.
1075 propagateIfChanged(operands[0], operands[0]->meet(convertLayout));
1076}
1077
1078/// For vector::TransposeOp, the layout of the result is transposed and
1079/// propagated to the operand.
1080void LayoutInfoPropagation::visitTransposeOp(
1081 vector::TransposeOp transpose, ArrayRef<LayoutInfoLattice *> operands,
1082 ArrayRef<const LayoutInfoLattice *> results) {
1083 // Need the layout of transpose result to propagate to the operands.
1084 LayoutInfo resultLayout = results[0]->getValue();
1085 if (!resultLayout.isAssigned())
1086 return;
1087
1088 auto consumerLayoutAttr =
1089 dyn_cast<xegpu::DistributeLayoutAttr>(resultLayout.get());
1090 auto srcLayoutAttr = xegpu::inferTransposeSourceLayout(
1091 consumerLayoutAttr, transpose.getPermutation());
1092
1093 // Propagate the new layout to the vector operand.
1094 propagateIfChanged(operands[0],
1095 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1096}
1097
1098/// For vector::BitCastOp, the lane_data of the source layout is changed based
1099/// on the bit width of the source and result types.
1100void LayoutInfoPropagation::visitVectorBitcastOp(
1101 vector::BitCastOp bitcast, ArrayRef<LayoutInfoLattice *> operands,
1102 ArrayRef<const LayoutInfoLattice *> results) {
1103 // Need the layout of bitcast result to propagate to the operands.
1104 LayoutInfo resLayoutInfo = results[0]->getValue();
1105 if (!resLayoutInfo.isAssigned())
1106 return;
1107
1108 auto srcVecType = bitcast.getSourceVectorType();
1109 auto resVecType = bitcast.getResultVectorType();
1110
1111 auto consumerLayoutAttr =
1112 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1113 const auto *uArch =
1114 xegpu::uArch::getUArch(xegpu::getChipStr(bitcast).value_or(""));
1115 if (!uArch)
1116 return;
1117 auto requiredResLayoutAttr = setupBitCastResultLayout(
1118 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1119
1120 xegpu::setTemporaryLayout(bitcast->getResult(0), requiredResLayoutAttr);
1121
1122 int inElemTyBitWidth = srcVecType.getElementType().getIntOrFloatBitWidth();
1123 int outElemTyBitWidth = resVecType.getElementType().getIntOrFloatBitWidth();
1124
1125 // derive the source layout from the dominant layout and reduction dims
1126 auto srcLayoutAttr = xegpu::inferBitCastSourceLayout(
1127 requiredResLayoutAttr, outElemTyBitWidth, inElemTyBitWidth);
1128
1129 propagateIfChanged(operands[0],
1130 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1131}
1132
1133/// For vector::InterleaveOp, the result has double the innermost dimension
1134/// size compared to each source operand. The layout is propagated from result
1135/// to sources, adjusting for the 2x size increase.
1136void LayoutInfoPropagation::visitVectorInterleaveOp(
1137 vector::InterleaveOp interleave, ArrayRef<LayoutInfoLattice *> operands,
1138 ArrayRef<const LayoutInfoLattice *> results) {
1139 // Need the layout of interleave result to propagate to the operands.
1140 LayoutInfo resLayoutInfo = results[0]->getValue();
1141 if (!resLayoutInfo.isAssigned())
1142 return;
1143
1144 auto srcVecType = interleave.getSourceVectorType();
1145 auto resVecType = interleave.getResultVectorType();
1146
1147 auto consumerLayoutAttr =
1148 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1149 const auto *uArch =
1150 xegpu::uArch::getUArch(xegpu::getChipStr(interleave).value_or(""));
1151 if (!uArch)
1152 return;
1153
1154 // Setup the result layout to ensure the source layout can be safely derived
1155 auto requiredResLayoutAttr = setupInterleaveResultLayout(
1156 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1157
1158 xegpu::setTemporaryLayout(interleave->getResult(0), requiredResLayoutAttr);
1159
1160 // Derive the source layout from the result layout (halve the innermost dim)
1161 auto srcLayoutAttr =
1162 xegpu::inferInterleaveSourceLayout(requiredResLayoutAttr);
1163
1164 // Both operands (lhs and rhs) get the same source layout
1165 propagateIfChanged(operands[0],
1166 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1167 propagateIfChanged(operands[1],
1168 operands[1]->meet(makeLayoutInfo(srcLayoutAttr)));
1169}
1170
1171/// For vector::DeinterleaveOp, the source has double the innermost dimension
1172/// size compared to each result. The layout is propagated from results to
1173/// source, adjusting for the 2x size decrease in results.
1174void LayoutInfoPropagation::visitVectorDeinterleaveOp(
1175 vector::DeinterleaveOp deinterleave, ArrayRef<LayoutInfoLattice *> operands,
1176 ArrayRef<const LayoutInfoLattice *> results) {
1177 // Need the layout of deinterleave results to propagate to the operand.
1178 // Use the first result's layout (both results should have the same layout)
1179 LayoutInfo resLayoutInfo = results[0]->getValue();
1180 if (!resLayoutInfo.isAssigned())
1181 return;
1182
1183 auto consumerLayoutAttr =
1184 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1185
1186 // Derive the source layout from the result layout (double the innermost
1187 // dim) No setup function needed - just infer directly
1188 auto srcLayoutAttr = xegpu::inferDeinterleaveSourceLayout(consumerLayoutAttr);
1189
1190 propagateIfChanged(operands[0],
1191 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1192}
1193
1194void LayoutInfoPropagation::visitInsertStridedSliceOp(
1195 vector::InsertStridedSliceOp insertStridedSlice,
1196 ArrayRef<LayoutInfoLattice *> operands,
1197 ArrayRef<const LayoutInfoLattice *> results) {
1198 // The layout of the result must be present.
1199 LayoutInfo resLayoutInfo = results[0]->getValue();
1200 if (!resLayoutInfo.isAssigned())
1201 return;
1202
1203 auto srcVecType = insertStridedSlice.getSourceVectorType();
1204 auto resVecType = insertStridedSlice.getDestVectorType();
1205
1206 auto consumerLayoutAttr =
1207 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1208 const auto *uArch = xegpu::uArch::getUArch(
1209 xegpu::getChipStr(insertStridedSlice).value_or(""));
1210 if (!uArch)
1211 return;
1212
1213 auto requiredResLayoutAttr = xegpu::setupInsertStridedSliceResultLayout(
1214 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1215 xegpu::setTemporaryLayout(insertStridedSlice->getResult(0),
1216 requiredResLayoutAttr);
1217
1218 auto srcLayoutAttr = xegpu::inferInsertStridedSliceSourceLayout(
1219 requiredResLayoutAttr, resVecType.getShape(), srcVecType.getShape());
1220 propagateIfChanged(operands[0],
1221 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1222 propagateIfChanged(operands[1],
1223 operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
1224}
1225
1226/// Propagate the layout of the result to the tensor descriptor, mask and
1227/// offset operands in LoadGatherOp.
1228void LayoutInfoPropagation::visitLoadGatherOp(
1229 xegpu::LoadGatherOp load, ArrayRef<LayoutInfoLattice *> operands,
1230 ArrayRef<const LayoutInfoLattice *> results) {
1231 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1232 xegpu::DistributeLayoutAttr anchorLayoutAttr = load.getLayoutAttr();
1233 const auto *uArch = xegpu::uArch::getUArch(getChipStr(load).value_or(""));
1234 if (!uArch)
1235 return;
1236 VectorType resVecTy = load.getValueType();
1237 int chunkSize = load.getChunkSize().value_or(1);
1238
1239 LayoutInfo resLayoutInfo = results[0]->getValue();
1240 if (!resLayoutInfo.isAssigned())
1241 return;
1242 auto consumerLayoutAttr =
1243 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1244
1245 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1246 requiredAnchorLayoutAttr = anchorLayoutAttr;
1247 if (layoutKind == xegpu::LayoutKind::InstData &&
1248 !consumerLayoutAttr.getEffectiveLaneLayoutAsInt().empty()) {
1249 const auto uArchInstruction =
1250 dyn_cast<xegpu::uArch::LoadGatherInstruction>(
1251 uArch->getInstruction(xegpu::uArch::InstructionKind::LoadGather));
1252 if (!uArchInstruction)
1253 return;
1255 anchorLayoutAttr, consumerLayoutAttr, resVecTy.getElementType(),
1256 uArchInstruction, uArch->getSubgroupSize());
1257 if (!completed) {
1258 load.emitWarning(
1259 "Failed to identify lane layouts for the specified inst_data.");
1260 return;
1261 }
1262 requiredAnchorLayoutAttr = *completed;
1263 load.setLayoutAttr(requiredAnchorLayoutAttr);
1264 }
1265 } else {
1266 if (!resVecTy) {
1267 load.emitWarning("Not propagating, non-vector payload supplied.");
1268 return;
1269 }
1270 requiredAnchorLayoutAttr = xegpu::setupLoadGatherAnchorLayout(
1271 layoutKind, resVecTy, chunkSize, consumerLayoutAttr, uArch);
1272 load.setLayoutAttr(requiredAnchorLayoutAttr);
1273 }
1274
1275 assert((chunkSize <= 1) || (layoutKind != xegpu::LayoutKind::Subgroup));
1276 auto maskLayoutAttr = xegpu::inferMaskOffsetLayoutForScatterIO(
1277 requiredAnchorLayoutAttr, chunkSize);
1278 LayoutInfo maskLayoutInfo = makeLayoutInfo(maskLayoutAttr);
1279 auto loadLayoutInfo = makeLayoutInfo(requiredAnchorLayoutAttr);
1280
1281 // Propagate the new layout to the tensor descriptor operand.
1282 if (isa<xegpu::TensorDescType>(load.getSourceType()))
1283 propagateIfChanged(operands[0], operands[0]->meet(loadLayoutInfo));
1284 // Propagate the new layout to the offset and mask operands.
1285 propagateIfChanged(operands[1], operands[1]->meet(maskLayoutInfo));
1286 propagateIfChanged(operands[2], operands[2]->meet(maskLayoutInfo));
1287}
1288
1289/// Set the layout for the value, tensor descriptor, offset and mask operands
1290/// in the StoreScatterOp.
1291void LayoutInfoPropagation::visitStoreScatterOp(
1292 xegpu::StoreScatterOp storeScatter, ArrayRef<LayoutInfoLattice *> operands,
1293 ArrayRef<const LayoutInfoLattice *> results) {
1294
1295 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1296 xegpu::DistributeLayoutAttr anchorLayoutAttr = storeScatter.getLayoutAttr();
1297 const auto *uArch =
1298 xegpu::uArch::getUArch(getChipStr(storeScatter).value_or(""));
1299 if (!uArch)
1300 return;
1301 VectorType srcVecTy = storeScatter.getValueType();
1302 int chunkSize = storeScatter.getChunkSize().value_or(1);
1303
1304 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1305 requiredAnchorLayoutAttr = anchorLayoutAttr;
1306 if (layoutKind == xegpu::LayoutKind::InstData) {
1307 const auto uArchInstruction =
1308 dyn_cast<xegpu::uArch::StoreScatterInstruction>(uArch->getInstruction(
1309 xegpu::uArch::InstructionKind::StoreScatter));
1310 if (!uArchInstruction)
1311 return;
1313 anchorLayoutAttr, srcVecTy.getElementType(), uArchInstruction,
1314 uArch->getSubgroupSize());
1315 if (!completed) {
1316 markFailure(
1317 storeScatter,
1318 "Failed to identify lane layouts for the specified inst_data.");
1319 return;
1320 }
1321 requiredAnchorLayoutAttr = *completed;
1322 storeScatter.setLayoutAttr(requiredAnchorLayoutAttr);
1323 }
1324 } else {
1325 if (!srcVecTy) {
1326 storeScatter.emitWarning("Not propagating, non-vector payload supplied.");
1327 return;
1328 }
1329 auto numSgOrErr =
1330 getNumSgOrFail(storeScatter, uArch->getSubgroupSize(), nullptr);
1331 if (failed(numSgOrErr))
1332 return;
1333 requiredAnchorLayoutAttr = xegpu::setupStoreScatterAnchorLayout(
1334 layoutKind, srcVecTy, chunkSize, numSgOrErr.value_or(0), uArch);
1335 if (!requiredAnchorLayoutAttr) {
1336 markFailure(storeScatter,
1337 "Failed to determine required layout for store scatter.");
1338 return;
1339 }
1340 storeScatter.setLayoutAttr(requiredAnchorLayoutAttr);
1341 }
1342
1343 LayoutInfo srcLayoutInfo = makeLayoutInfo(requiredAnchorLayoutAttr);
1344 assert((chunkSize <= 1) || (layoutKind != xegpu::LayoutKind::Subgroup));
1345 auto maskLayoutAttr = xegpu::inferMaskOffsetLayoutForScatterIO(
1346 requiredAnchorLayoutAttr, chunkSize);
1347 LayoutInfo maskLayoutInfo = makeLayoutInfo(maskLayoutAttr);
1348
1349 // Propagate the payload operand layout
1350 propagateIfChanged(operands[0], operands[0]->meet(srcLayoutInfo));
1351 // Propagate the destination (if tdesc) operand layout
1352 if (isa<xegpu::TensorDescType>(storeScatter.getDestType()))
1353 propagateIfChanged(operands[1], operands[1]->meet(srcLayoutInfo));
1354 // Propagate the new layout to the offset and mask operands.
1355 propagateIfChanged(operands[2], operands[2]->meet(maskLayoutInfo));
1356 propagateIfChanged(operands[3], operands[3]->meet(maskLayoutInfo));
1357}
1358
1359void LayoutInfoPropagation::visitLoadMatrixOp(
1360 xegpu::LoadMatrixOp loadMatrixOp, ArrayRef<LayoutInfoLattice *> operands,
1361 ArrayRef<const LayoutInfoLattice *> results) {
1362
1363 LayoutInfo resLayoutInfo = results[0]->getValue();
1364 if (!resLayoutInfo.isAssigned())
1365 return;
1366
1367 auto consumerLayoutAttr =
1368 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1369
1370 xegpu::DistributeLayoutAttr anchorLayout = loadMatrixOp.getLayoutAttr();
1371
1372 // only need to set anchor layout, no need to porpagate to memdesc and
1373 // offset
1374 if (!hasParamsOfLayoutKind(anchorLayout)) {
1375 VectorType resVecTy =
1376 llvm::cast<VectorType>(loadMatrixOp.getRes().getType());
1377 const auto *uArch =
1378 xegpu::uArch::getUArch(getChipStr(loadMatrixOp).value_or(""));
1379 if (!uArch)
1380 return;
1381 int chunkSize =
1382 1; // placeHolder for future use when LoadMatrix supports coalescing
1383 auto requiredAnchorLayoutAttr = xegpu::setupLoadMatrixAnchorLayout(
1384 layoutKind, resVecTy, chunkSize, consumerLayoutAttr, uArch);
1385 loadMatrixOp.setLayoutAttr(requiredAnchorLayoutAttr);
1386 }
1387}
1388
1389void LayoutInfoPropagation::visitStoreMatrixOp(
1390 xegpu::StoreMatrixOp storeMatrix, ArrayRef<LayoutInfoLattice *> operands,
1391 ArrayRef<const LayoutInfoLattice *> results) {
1392 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1393 xegpu::DistributeLayoutAttr anchorLayoutAttr = storeMatrix.getLayoutAttr();
1394 LayoutInfo layout;
1395 VectorType srcVecTy = llvm::cast<VectorType>(storeMatrix.getData().getType());
1396 const auto *uArch =
1397 xegpu::uArch::getUArch(getChipStr(storeMatrix).value_or(""));
1398 if (!uArch)
1399 return;
1400 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1401 requiredAnchorLayoutAttr = anchorLayoutAttr;
1402 if (layoutKind == xegpu::LayoutKind::InstData) {
1403 const auto uArchInstruction =
1404 dyn_cast<xegpu::uArch::StoreScatterInstruction>(uArch->getInstruction(
1405 xegpu::uArch::InstructionKind::StoreScatter));
1406 if (!uArchInstruction)
1407 return;
1409 anchorLayoutAttr, srcVecTy.getElementType(), uArchInstruction,
1410 uArch->getSubgroupSize());
1411 if (!completed) {
1412 markFailure(
1413 storeMatrix,
1414 "Failed to identify lane layouts for the specified inst_data.");
1415 return;
1416 }
1417 requiredAnchorLayoutAttr = *completed;
1418 storeMatrix.setLayoutAttr(requiredAnchorLayoutAttr);
1419 }
1420 } else {
1421 int chunkSize =
1422 1; // placeHolder for future use when StoreMatrix supports coalescing
1423 auto numSgOrErr =
1424 getNumSgOrFail(storeMatrix, uArch->getSubgroupSize(), nullptr);
1425 if (failed(numSgOrErr))
1426 return;
1427 requiredAnchorLayoutAttr = xegpu::setupStoreMatrixAnchorLayout(
1428 layoutKind, srcVecTy, chunkSize, numSgOrErr.value_or(0), uArch);
1429 if (!requiredAnchorLayoutAttr) {
1430 markFailure(storeMatrix,
1431 "Failed to determine required layout for store matrix.");
1432 return;
1433 }
1434 storeMatrix.setLayoutAttr(requiredAnchorLayoutAttr);
1435 }
1436 layout = makeLayoutInfo(requiredAnchorLayoutAttr);
1437 propagateIfChanged(operands[0], operands[0]->meet(layout));
1438}
1439
1440namespace {
1441//===----------------------------------------------------------------------===//
1442// RunLayoutInfoPropagation
1443//===----------------------------------------------------------------------===//
1444
1445/// Driver class for running the LayoutInfoPropagation analysis.
1446class RunLayoutInfoPropagation {
1447public:
1448 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(RunLayoutInfoPropagation)
1449
1450 RunLayoutInfoPropagation(Operation *op, xegpu::LayoutKind layoutKind,
1451 unsigned indexBitWidth)
1452 : target(op) {
1453 SymbolTableCollection symbolTable;
1454 loadBaselineAnalyses(solver);
1455 analysis = solver.load<LayoutInfoPropagation>(symbolTable, layoutKind,
1456 indexBitWidth);
1457 (void)solver.initializeAndRun(op);
1458 }
1459
1460 LayoutInfo getLayoutInfo(Value val);
1461
1462 void printAnalysisResult(llvm::raw_ostream &os);
1463
1464 bool hasFailed() const { return analysis && analysis->hasFailed(); }
1465
1466private:
1467 DataFlowSolver solver;
1468 const Operation *target;
1469 LayoutInfoPropagation *analysis = nullptr;
1470};
1471} // namespace
1472
1473LayoutInfo RunLayoutInfoPropagation::getLayoutInfo(Value val) {
1474 auto *state = solver.lookupState<LayoutInfoLattice>(val);
1475 if (!state)
1476 return {};
1477 return state->getValue();
1478}
1479
1480// Print the analysis result for debugging purposes.
1481void RunLayoutInfoPropagation::printAnalysisResult(llvm::raw_ostream &os) {
1482 auto printFunctionResult = [&](FunctionOpInterface funcOp) {
1483 os << "function: " << funcOp.getName() << ":\n";
1484 // Function arguments
1485 for (BlockArgument arg : funcOp.getArguments()) {
1486 LayoutInfo layout = getLayoutInfo(arg);
1487 os << "argument: " << arg << "\n";
1488 os << "layout : ";
1489 layout.print(os);
1490 os << "\n";
1491 }
1492 // Function ops
1493 funcOp.walk([&](Operation *op) {
1494 // Skip ops that do not have results
1495 if (op->getResults().empty())
1496 return;
1497 os << "op : ";
1498 // For control-flow ops, print the op name only.
1499 if (isa<BranchOpInterface>(op) || isa<RegionBranchOpInterface>(op))
1500 os << op->getName();
1501 else
1502 op->print(os);
1503 os << "\n";
1504 // Print the layout for each result.
1505 for (auto [i, r] : llvm::enumerate(op->getResults())) {
1506 LayoutInfo layout = getLayoutInfo(r);
1507 os << "layout for result #" << i << ": ";
1508 layout.print(os);
1509 os << "\n";
1510 }
1511 });
1512 };
1513
1514 SmallVector<FunctionOpInterface> funcOps;
1515 if (auto modOp = dyn_cast<ModuleOp>(target)) {
1516 for (auto funcOp : modOp.getOps<FunctionOpInterface>())
1517 funcOps.push_back(funcOp);
1518
1519 // Collect all GpuFuncOps in the module.
1520 for (auto gpuModOp : modOp.getOps<gpu::GPUModuleOp>()) {
1521 for (auto gpuFuncOp : gpuModOp.getOps<FunctionOpInterface>())
1522 funcOps.push_back(gpuFuncOp);
1523 }
1524 }
1525 // Print the analysis result for each function.
1526 for (FunctionOpInterface funcOp : funcOps)
1527 printFunctionResult(funcOp);
1528}
1529
1530namespace {
1531
1532//===----------------------------------------------------------------------===//
1533// ResolveLayoutConflicts
1534//===----------------------------------------------------------------------===//
1535
1536/// Helper to get the defining CreateNdDescOp of a tensor descriptor value.
1537/// This function tries to find the defining CreateNdDescOp recursively
1538/// accross control-flow boundaries.
1539static xegpu::CreateNdDescOp getDefiningCreateNdDescOp(Value tdescValue) {
1540 // Try to get the defining CreateNdDescOp of the tensor descriptor.
1541 auto definingOp = tdescValue.getDefiningOp<xegpu::CreateNdDescOp>();
1542 if (definingOp)
1543 return definingOp;
1544 // If tdescValue is an argument, try to get the tied init value from the
1545 // parent loop-like op.
1546 if (auto arg = dyn_cast<BlockArgument>(tdescValue)) {
1547 auto *parentOp = arg.getOwner()->getParentOp();
1548 if (auto loop = dyn_cast<LoopLikeOpInterface>(parentOp)) {
1549 OpOperand *tiedInit = loop.getTiedLoopInit(arg);
1550 if (tiedInit)
1551 return getDefiningCreateNdDescOp(tiedInit->get());
1552 }
1553 }
1554 // If not found, return null.
1555 return nullptr;
1556}
1557
1558struct ResolveLayoutConflicts {
1559 ResolveLayoutConflicts(Operation *parentOp)
1560 : parentOp(parentOp), builder(parentOp->getContext()) {}
1561 LogicalResult run();
1562
1563private:
1564 Operation *parentOp;
1565 OpBuilder builder;
1566 LogicalResult resolveTensorDescConsumer(OpOperand &operand);
1567 LogicalResult resolveVectorConsumer(OpOperand &operand);
1568 LogicalResult assignResultLayout(OpResult &result);
1569};
1570
1571} // namespace
1572
1573LogicalResult ResolveLayoutConflicts::run() {
1574 // Scan all operations in the parent op and resolve layout conflicts at
1575 // tensor descriptor and vector use points.
1576 auto r = parentOp->walk([&](Operation *op) -> WalkResult {
1577 for (OpResult result : op->getResults()) {
1578 // if the operation inputs vector and output scalar, like multi-reduction
1579 // we need to check if the result has layout and add a convert_layout to
1580 // serve as anchor op for the reduction op's layout.
1581 if (result.getType().isIntOrFloat() &&
1582 (isa<vector::MultiDimReductionOp>(op) ||
1583 isa<vector::ReductionOp>(op))) {
1584 auto res = assignResultLayout(result);
1585 if (failed(res)) {
1586 DBGS() << "Failed to assign layout for scalar consumer of reduction "
1587 << *op << "\n";
1588 return WalkResult::interrupt();
1589 }
1590 }
1591 // If the op is a region branch op with a vector result that has no uses,
1592 // we need to add a convert_layout to serve as an anchor op for the
1593 // result's layout.
1594 if (isa<VectorType>(result.getType()) && result.use_empty() &&
1595 isa<RegionBranchOpInterface>(op)) {
1596 auto res = assignResultLayout(result);
1597 if (failed(res)) {
1598 DBGS() << "Failed to assign layout for vector consumer of region op "
1599 << *op << "\n";
1600 return WalkResult::interrupt();
1601 }
1602 }
1603 }
1604 for (OpOperand &operand : op->getOpOperands()) {
1605 // Handle conflicts in tensor descriptor operands.
1606 Type operandType = operand.get().getType();
1607 if (isa<xegpu::AnchorLayoutInterface>(op) &&
1608 isa<xegpu::TensorDescType>(operandType)) {
1609 auto res = resolveTensorDescConsumer(operand);
1610 if (failed(res)) {
1611 DBGS() << "Failed to resolve tensor descriptor consumer: " << *op
1612 << "\n";
1613 return WalkResult::interrupt();
1614 }
1615 }
1616 // Handle conflicts in vector operands.
1617 if (isa<VectorType>(operandType)) {
1618 auto res = resolveVectorConsumer(operand);
1619 if (failed(res)) {
1620 DBGS() << "Failed to resolve vector consumer: " << *op << "\n";
1621 return WalkResult::interrupt();
1622 }
1623 }
1624 }
1625 return WalkResult::advance();
1626 });
1627
1628 LLVM_DEBUG({
1629 DBGS() << "IR after resolving layout conflicts:\n";
1630 parentOp->dump();
1631 });
1632
1633 return r.wasInterrupted() ? failure() : success();
1634}
1635
1636LogicalResult ResolveLayoutConflicts::assignResultLayout(OpResult &result) {
1637 Operation *producerOp = result.getDefiningOp();
1638 auto producerLayout = xegpu::getDistributeLayoutAttr(result);
1639 // Insert a convert_layout op to assign the layout.
1641 auto convertOp = xegpu::ConvertLayoutOp::create(
1642 builder, producerOp->getLoc(), result.getType(), result, producerLayout,
1643 producerLayout);
1644 result.replaceAllUsesExcept(convertOp.getResult(), convertOp);
1645 return success();
1646}
1647
1648LogicalResult
1649ResolveLayoutConflicts::resolveVectorConsumer(OpOperand &operand) {
1650 Value vectorValue = operand.get();
1651 Operation *consumerOp = operand.getOwner();
1652 // Get the current layout of the vector value.
1653 auto producerLayout = xegpu::getDistributeLayoutAttr(vectorValue);
1654 if (!producerLayout) {
1655 if (auto vectorTy = dyn_cast<VectorType>(vectorValue.getType());
1656 vectorTy && vectorTy.getRank() > 1)
1657 consumerOp->emitWarning("Expected layout for non-1D vectors.");
1658 return success(); // uniform non-tensor-data vector does not require
1659 // layout
1660 }
1661 // Region branch ops (e.g. scf.for) and their terminators (e.g. scf.yield)
1662 // forward their operands to successor region inputs / parent op results;
1663 // their consumer layout is resolved through that forwarding, not at this
1664 // use point.
1665 if (isa<RegionBranchOpInterface, RegionBranchTerminatorOpInterface>(
1666 consumerOp))
1667 return success();
1668
1669 auto consumerLayout = xegpu::getConsumerLayoutAt(operand);
1670 if (!consumerLayout)
1671 return consumerOp->emitError(
1672 "No consumer layout found for vector operand.");
1673
1674 // If layouts are same, no conflict exists, return success.
1675 if (consumerLayout.isEqualTo(producerLayout))
1676 return success();
1677
1678 // Consumer is a convert_layout: retarget its input_layout to the producer
1679 // instead of chaining a second convert. Always safe (single source
1680 // operand).
1681 if (auto consumerConvert = dyn_cast<xegpu::ConvertLayoutOp>(consumerOp)) {
1682 consumerConvert.setInputLayoutAttr(producerLayout);
1683 return success();
1684 }
1685
1686 // Producer is a convert_layout feeding only this use: retarget its
1687 // target_layout to the consumer instead of appending another convert.
1688 if (auto producerConvert =
1689 vectorValue.getDefiningOp<xegpu::ConvertLayoutOp>();
1690 producerConvert && vectorValue.hasOneUse()) {
1691 // Pin the effective input before retargeting target, else an omitted
1692 // input_layout would follow target and make the conversion a no-op.
1693 producerConvert.setInputLayoutAttr(
1694 producerConvert.getEffectiveInputLayout());
1695 producerConvert.setTargetLayoutAttr(consumerLayout);
1696 return success();
1697 }
1698
1699 // If the producer is trivially rematerializable (e.g. `vector.step`, splat
1700 // `arith.constant`), clone it and stamp the consumer's expected layout on
1701 // the clone instead of inserting a `xegpu.convert_layout`. The convert
1702 // would otherwise lower to a cross-subgroup data movement through SLM at
1703 // WG-to-SG distribution time, which is more expensive than
1704 // recomputing a pure value generator.
1705 if (auto *producerOp = vectorValue.getDefiningOp();
1706 producerOp && producerOp->getNumResults() == 1 &&
1707 isa<OpResult>(vectorValue) &&
1709 builder.setInsertionPointAfter(producerOp);
1710 Operation *clone = builder.clone(*producerOp);
1711 OpResult cloneResult = clone->getResult(0);
1712 // Drop the inherited producer layout so the new layout takes effect
1713 xegpu::removeLayoutAttr(cloneResult);
1714 xegpu::setDistributeLayoutAttr(cloneResult, consumerLayout);
1715 operand.set(cloneResult);
1716 return success();
1717 }
1718
1719 // Insert a convert_layout op to resolve the conflict.
1720 builder.setInsertionPointAfterValue(vectorValue);
1721 auto convertOp = xegpu::ConvertLayoutOp::create(
1722 builder, consumerOp->getLoc(), vectorValue.getType(), vectorValue,
1723 producerLayout, consumerLayout);
1724
1725 // Update the operand to use the converted value.
1726 operand.set(convertOp.getResult());
1727 return success();
1728}
1729
1730LogicalResult
1731ResolveLayoutConflicts::resolveTensorDescConsumer(OpOperand &operand) {
1732 Operation *consumerOp = operand.getOwner();
1733 Value tdescValue = operand.get();
1734 auto anchorOp = dyn_cast<xegpu::AnchorLayoutInterface>(consumerOp);
1735 auto currTDescType = dyn_cast<xegpu::TensorDescType>(tdescValue.getType());
1736 assert(anchorOp && currTDescType &&
1737 "Expected anchor layout op and tensor descriptor consumer.");
1738 Attribute currLayout = currTDescType.getLayout();
1739 Attribute expectedLayout = anchorOp.getAnchorLayout();
1740 // A conflict exists in tensor descriptor operand if tensor descriptor's
1741 // layout is different from the anchor layout expected by the consumer.
1742 if (expectedLayout && currLayout && expectedLayout != currLayout) {
1743 // Try to get the defining CreateNdDescOp of the tensor descriptor.
1744 auto conflictingCreateNdOp = getDefiningCreateNdDescOp(tdescValue);
1745 if (!conflictingCreateNdOp) {
1746 DBGS() << "Unable to find defining CreateNdDescOp for tensor descriptor: "
1747 << tdescValue << "\n";
1748 return failure();
1749 }
1750 // Duplicate the CreateNdDescOp with the expected layout.
1751 builder.setInsertionPointAfter(conflictingCreateNdOp);
1752 auto newTensorDescType = xegpu::TensorDescType::get(
1753 conflictingCreateNdOp.getContext(), currTDescType.getShape(),
1754 currTDescType.getElementType(), currTDescType.getEncoding(),
1755 expectedLayout);
1756 xegpu::CreateNdDescOp newOp = xegpu::CreateNdDescOp::create(
1757 builder, consumerOp->getLoc(), newTensorDescType,
1758 conflictingCreateNdOp->getOperands(),
1759 conflictingCreateNdOp->getAttrs());
1760 // Replace the tensor descriptor operand in the consumer op with the new
1761 // tensor descriptor.
1762 consumerOp->replaceUsesOfWith(tdescValue, newOp.getResult());
1763 }
1764 return success();
1765}
1766
1767using GetLayoutFnTy = function_ref<xegpu::DistributeLayoutAttr(Value)>;
1768
1769/// Update an operation with the layout of its results. If the result type is
1770/// a vector type, a temporary layout attribute is added to the operation. If
1771/// the result type is a tensor descriptor type, the type is updated with the
1772/// layout attribute. The users of the result are also updated with the layout
1773/// attribute.
1774///
1775/// If the global propagation left a result without a layout, forward-fill it
1776/// locally from the operand layouts.
1777static LogicalResult updateOpWithForwardFill(mlir::OpBuilder &builder,
1778 mlir::Operation *op,
1779 GetLayoutFnTy getLayoutOfValue) {
1780 // Region ops (like scf.for) are already handled by the
1781 // updateControlFlowOps.
1782 if (mlir::isa<mlir::RegionBranchOpInterface>(op))
1783 return success();
1784
1785 // Iterate over all the results.
1786 for (OpResult result : op->getResults()) {
1787 Type resultType = result.getType();
1788 // Layouts are needed only for vector and tensor descriptor types.
1789 if (!isa<VectorType, xegpu::TensorDescType>(resultType))
1790 continue;
1791 // If the result has no layout but has users, emit a warning and continue.
1792 xegpu::DistributeLayoutAttr layout = getLayoutOfValue(result);
1793 if (!layout) {
1794 // Gather operand layouts, indexed by operand number.
1796 srcLayouts.reserve(op->getNumOperands());
1797 bool anyAssigned = false;
1798 for (Value operand : op->getOperands()) {
1799 auto srclayout = xegpu::getDistributeLayoutAttr(operand);
1800 srcLayouts.push_back(srclayout);
1801 anyAssigned |= (srclayout != nullptr);
1802 }
1803 if (anyAssigned) {
1804 layout =
1806 }
1807 }
1808 if (!layout && result.getNumUses() > 0) {
1809 op->emitWarning("op has users but no layout assigned for its result");
1810 }
1811 // If the result is a tensor descriptor type, update the tensor desc type
1812 // with layout.
1813 if (auto tensorDescTy = dyn_cast<xegpu::TensorDescType>(resultType)) {
1814 auto typeWithLayout = xegpu::TensorDescType::get(
1815 tensorDescTy.getContext(), tensorDescTy.getShape(),
1816 tensorDescTy.getElementType(), tensorDescTy.getEncoding(), layout);
1817 result.setType(typeWithLayout);
1818 continue;
1819 }
1820 // If the result is a vector type, add a temporary layout attribute to the
1821 // op.
1823 }
1824 return success();
1825}
1826
1827/// Update the function arguments and results with the layouts.
1828static LogicalResult updateFunctionOpInterface(mlir::OpBuilder &builder,
1829 mlir::FunctionOpInterface funcOp,
1830 GetLayoutFnTy getLayoutOfValue) {
1831 // Only process functions whose type is a standard MLIR FunctionType.
1832 // Functions using a different type representation (e.g. llvm.func with
1833 // LLVMFunctionType) are not targets for XeGPU layout propagation, and
1834 // calling setType(FunctionType{}) on them would corrupt their type.
1835 if (!isa<FunctionType>(funcOp.getFunctionType()))
1836 return success();
1837 SmallVector<Type> newArgTypes;
1838 // Update the function arguments.
1839 for (BlockArgument arg : funcOp.getArguments()) {
1840 Type argType = arg.getType();
1841 newArgTypes.push_back(argType);
1842 if (!isa<VectorType, xegpu::TensorDescType>(argType))
1843 continue;
1844 xegpu::DistributeLayoutAttr layout = getLayoutOfValue(arg);
1845 if (!layout) {
1846 LLVM_DEBUG(DBGS() << "Expecting layout for function argument: " << arg
1847 << " but got none.\n");
1848 return failure();
1849 }
1850 if (auto tensorDescTy = dyn_cast<xegpu::TensorDescType>(argType)) {
1851 auto newTdescTy = xegpu::TensorDescType::get(
1852 tensorDescTy.getContext(), tensorDescTy.getShape(),
1853 tensorDescTy.getElementType(), tensorDescTy.getEncoding(), layout);
1854 arg.setType(newTdescTy);
1855 newArgTypes.back() = newTdescTy;
1856 }
1857 }
1858 // Update the function type with the new argument types.
1859 // NOTE: We assume that function results are not expected to have layouts.
1860 funcOp.setType(FunctionType::get(funcOp.getContext(), newArgTypes,
1861 funcOp.getResultTypes()));
1862 return success();
1863}
1864
1865namespace {
1866struct XeGPUPropagateLayoutPass final
1867 : public xegpu::impl::XeGPUPropagateLayoutBase<XeGPUPropagateLayoutPass> {
1868 XeGPUPropagateLayoutPass() = default;
1869 XeGPUPropagateLayoutPass(const XeGPUPropagateLayoutPass &other) = default;
1870 XeGPUPropagateLayoutPass(xegpu::XeGPUPropagateLayoutOptions options)
1871 : XeGPUPropagateLayoutBase(std::move(options)) {}
1872 void runOnOperation() override;
1873};
1874
1875} // namespace
1876
1878 LayoutKind layoutKind,
1879 unsigned indexBitWidth, bool printOnly) {
1880 RunLayoutInfoPropagation analysis(target, layoutKind, indexBitWidth);
1881 // Print the analysis result and exit. (for debugging purposes)
1882 if (printOnly) {
1883 auto &os = llvm::outs();
1884 analysis.printAnalysisResult(os);
1885 return success();
1886 }
1887 // An op with no determinable layout cannot be lowered; stop before the update
1888 // walk fabricates degenerate layouts from the unlabeled values.
1889 if (analysis.hasFailed())
1890 return failure();
1891 // Helper to convert LayoutInfo to xegpu::LayoutAttr.
1892 auto getLayoutFromPropagation =
1893 [&](Value val) -> xegpu::DistributeLayoutAttr {
1894 LayoutInfo layout = analysis.getLayoutInfo(val);
1895 if (auto opResult = dyn_cast<OpResult>(val)) {
1896 Operation *defOp = opResult.getDefiningOp();
1897 if (auto anchorOp = dyn_cast<xegpu::AnchorLayoutInterface>(defOp)) {
1898 auto anchorLayout = anchorOp.getAnchorLayout();
1899 if (anchorLayout != nullptr)
1900 return anchorLayout;
1901 }
1902 xegpu::DistributeLayoutAttr requiredResLayoutAttr =
1903 xegpu::getTemporaryLayout(opResult);
1904 if (requiredResLayoutAttr != nullptr)
1905 return requiredResLayoutAttr;
1906 }
1907 if (!layout.isAssigned())
1908 return {};
1909 xegpu::DistributeLayoutAttr layoutAttr =
1910 cast<xegpu::DistributeLayoutAttr>(layout.get());
1911 if (layout.isSliceLayout())
1912 return cast<xegpu::SliceAttr>(layoutAttr);
1913
1914 return cast<xegpu::LayoutAttr>(layoutAttr);
1915 };
1916
1917 Operation *op = target;
1918 auto walkResult = op->walk([&](mlir::Block *block) -> WalkResult {
1919 for (mlir::Operation &op : block->getOperations()) {
1920 LogicalResult r = success();
1922 .Case([&](mlir::RegionBranchTerminatorOpInterface branchTermOp) {
1924 branchTermOp, getLayoutFromPropagation);
1925 })
1926 .Case([&](mlir::RegionBranchOpInterface branchOp) {
1928 getLayoutFromPropagation);
1929 })
1930 .Case([&](mlir::FunctionOpInterface funcOp) {
1931 r = updateFunctionOpInterface(builder, funcOp,
1932 getLayoutFromPropagation);
1933 })
1934 .Default([&](Operation *op) {
1935 r = updateOpWithForwardFill(builder, op, getLayoutFromPropagation);
1936 });
1937 if (failed(r)) {
1938 op.emitError("Failed to update operation with the layout.");
1939 return WalkResult::interrupt();
1940 }
1941 }
1942 return WalkResult::advance();
1943 });
1944 if (walkResult.wasInterrupted())
1945 return failure();
1946
1947 return success();
1948}
1949
1951 ResolveLayoutConflicts resolver(target);
1952 return resolver.run();
1953}
1954
1955void XeGPUPropagateLayoutPass::runOnOperation() {
1956
1957 xegpu::removeTemporaryLayoutAttrs(getOperation());
1958
1959 xegpu::LayoutKind layoutKind;
1960 if (this->layoutKind == "lane") {
1961 layoutKind = xegpu::LayoutKind::Lane;
1962 } else if (this->layoutKind == "inst") {
1963 layoutKind = xegpu::LayoutKind::InstData;
1964 } else if (this->layoutKind == "subgroup") {
1965 layoutKind = xegpu::LayoutKind::Subgroup;
1966 } else {
1967 getOperation()->emitError("Unsupported layout kind option: " +
1968 this->layoutKind);
1969 signalPassFailure();
1970 return;
1971 }
1972 OpBuilder builder(&getContext());
1973 if (failed(xegpu::propagateLayouts(builder, getOperation(), layoutKind,
1974 this->indexBitWidth, this->printOnly))) {
1975 signalPassFailure();
1976 return;
1977 }
1978 // Resolve layout conflicts if any.
1979 if (failed(xegpu::resolveLayoutConflicts(getOperation()))) {
1980 signalPassFailure();
1981 return;
1982 }
1983}
return success()
#define DBGS()
Definition Hoisting.cpp:32
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
lhs
b getContext())
auto load
static llvm::ManagedStatic< PassManagerOptions > options
static void print(spirv::VerCapExtAttr triple, DialectAsmPrinter &printer)
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
#define MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CLASS_NAME)
Definition TypeID.h:331
function_ref< xegpu::DistributeLayoutAttr(Value)> GetLayoutFnTy
static LogicalResult updateOpWithForwardFill(mlir::OpBuilder &builder, mlir::Operation *op, GetLayoutFnTy getLayoutOfValue)
Update an operation with the layout of its results.
static LogicalResult updateFunctionOpInterface(mlir::OpBuilder &builder, mlir::FunctionOpInterface funcOp, GetLayoutFnTy getLayoutOfValue)
Update the function arguments and results with the layouts.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents an argument of a Block.
Definition Value.h:306
Block represents an ordered list of Operations.
Definition Block.h:33
OpListType & getOperations()
Definition Block.h:161
The general data-flow analysis solver.
LogicalResult initializeAndRun(Operation *top, llvm::function_ref< bool(DataFlowAnalysis &)> analysisFilter=nullptr)
Initialize analyses starting from the provided top-level operation and run the analysis until fixpoin...
const StateT * lookupState(AnchorT anchor) const
Lookup an analysis state for the given lattice anchor.
AnalysisT * load(Args &&...args)
Load an analysis into the solver. Return the analysis instance.
IRValueT get() const
Return the current value being used by this operand.
void set(IRValueT newValue)
Set the current value being used by this operand.
This class helps build Operations.
Definition Builders.h:210
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:571
void setInsertionPointAfterValue(Value val)
Sets the insertion point to the node after the specified value.
Definition Builders.h:424
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:415
This class represents an operand of an operation.
Definition Value.h:254
This is a value defined by a result of an operation.
Definition Value.h:454
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
void replaceUsesOfWith(Value from, Value to)
Replace any uses of 'from' with 'to' within this operation.
InFlightDiagnostic emitWarning(const Twine &message={})
Emit a warning about this operation, reporting up to any diagnostic handlers that may be listening.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:408
unsigned getNumOperands()
Definition Operation.h:371
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
void print(raw_ostream &os, const OpPrintingFlags &flags={})
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:842
result_range getResults()
Definition Operation.h:440
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 represents a collection of SymbolTables.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
bool hasOneUse() const
Returns true if this value has exactly one use.
Definition Value.h:197
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
This class represents a lattice holding a specific value of type ValueT.
A sparse (backward) data-flow analysis for propagating SSA value lattices backwards across the IR by ...
SparseBackwardDataFlowAnalysis(DataFlowSolver &solver, SymbolTableCollection &symbolTable)
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
void loadBaselineAnalyses(DataFlowSolver &solver)
Populates a DataFlowSolver with analyses that are required to ensure user-defined analyses are run pr...
Definition Utils.h:29
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
const uArch * getUArch(llvm::StringRef archName)
Definition uArchCommon.h:24
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,...
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 inferResultLayoutFromSourceForNonAnchorOp(Operation *op, ArrayRef< DistributeLayoutAttr > operandLayouts)
Infers the result layout attribute for a non-anchor operation from the layouts of its source operands...
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...
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...
DistributeLayoutAttr setupStoreMatrixAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a store matrix operation.
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
void setDistributeLayoutAttr(const OpResult &Result, const DistributeLayoutAttr layout)
[to-be-deprecated] Sets the DistributeLayoutAttr for a given OpResult user should use setAnchorLayout...
DistributeLayoutAttr inferInterleaveSourceLayout(DistributeLayoutAttr resLayout)
Infers the source layout attribute for an interleave operation given the result layout attribute.
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).
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.
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...
DistributeLayoutAttr setupStoreScatterAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a store scatter operation.
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.
DistributeLayoutAttr setupPrefetchNdAnchorLayout(LayoutKind layoutKind, TensorDescType tdescTy, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a prefetch_nd operation.
LogicalResult propagateYieldOperandsToRegionResults(RegionBranchTerminatorOpInterface terminator, GetLayoutFnTy getLayoutOfValue)
Propagate layouts from a region branch terminator's forwarded operands to the matching region results...
LogicalResult resolveLayoutConflicts(Operation *target)
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.
std::optional< std::string > getChipStr(Operation *op)
Retrieves the chip string from the XeVM target attribute of the parent GPU module 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)...
DistributeLayoutAttr getTemporaryLayout(const T &operandOrResult)
get and set distribute layout attribute for non-anchor operations (and offsets/masks of load/store op...
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.
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,...
LogicalResult propagateLayouts(OpBuilder &builder, Operation *target, LayoutKind layoutKind, unsigned indexBitWidth, bool printOnly=false)
DistributeLayoutAttr setupStoreNdAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a store_nd 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.
bool operator==(StringAttr lhs, std::nullptr_t)
Define comparisons for StringAttr against nullptr and itself to avoid the StringRef overloads from be...
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:139
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147