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