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