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