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"
44#define GEN_PASS_DEF_XEGPUPROPAGATELAYOUT
45#include "mlir/Dialect/XeGPU/Transforms/Passes.h.inc"
49#define DEBUG_TYPE "xegpu-propagate-layout"
50#define DBGS() (llvm::dbgs() << "[" DEBUG_TYPE "]: ")
83 xegpu::DistributeLayoutAttr storage =
nullptr;
86 LayoutInfo() =
default;
87 LayoutInfo(
const xegpu::DistributeLayoutAttr &layout) : storage(layout) {}
91 bool operator==(
const LayoutInfo &other)
const {
92 return this->isAssigned() == other.isAssigned();
95 static LayoutInfo meet(
const LayoutInfo &
lhs,
const LayoutInfo &
rhs);
97 static LayoutInfo
join(
const LayoutInfo &
lhs,
const LayoutInfo &
rhs);
101 bool isAssigned()
const {
return storage !=
nullptr; }
117 bool isSliceLayout()
const {
120 return isa<xegpu::SliceAttr>(storage);
126 return storage.getRank();
130 void set(
const xegpu::DistributeLayoutAttr &layout) { storage = layout; }
137 os <<
"Not assigned.";
141LayoutInfo LayoutInfo::meet(
const LayoutInfo &
lhs,
const LayoutInfo &
rhs) {
142 if (!
lhs.isAssigned())
148LayoutInfo LayoutInfo::join(
const LayoutInfo &
lhs,
const LayoutInfo &
rhs) {
149 llvm_unreachable(
"Join should not be triggered by layout propagation.");
157struct LayoutInfoLattice :
public Lattice<LayoutInfo> {
159 using Lattice::Lattice;
171class LayoutInfoPropagation
178 unsigned indexBitWidth;
182 void visitDpasMxOp(xegpu::DpasMxOp dpasMx,
186 void visitStoreNdOp(xegpu::StoreNdOp store,
190 void visitStoreScatterOp(xegpu::StoreScatterOp storeScatter,
194 void visitLoadNdOp(xegpu::LoadNdOp
load,
198 void visitLoadGatherOp(xegpu::LoadGatherOp
load,
202 void visitTransposeOp(vector::TransposeOp transpose,
206 void visitVectorBitcastOp(vector::BitCastOp bitcast,
210 void visitVectorInterleaveOp(vector::InterleaveOp interleave,
214 void visitVectorDeinterleaveOp(vector::DeinterleaveOp deinterleave,
218 void visitPrefetchNdOp(xegpu::PrefetchNdOp prefetch,
222 void visitVectorMultiReductionOp(vector::MultiDimReductionOp reduction,
226 void visitVectorReductionOp(vector::ReductionOp reduction,
230 void visitVectorBroadCastOp(vector::BroadcastOp
broadcast,
233 void visitShapeCastOp(vector::ShapeCastOp shapeCast,
237 visitInsertStridedSliceOp(vector::InsertStridedSliceOp insertStridedSlice,
241 void visitLoadMatrixOp(xegpu::LoadMatrixOp
load,
245 void visitStoreMatrixOp(xegpu::StoreMatrixOp store,
249 void visitLoadGatherOp(xegpu::LoadMatrixOp
load,
253 void visitStoreScatterOp(xegpu::StoreMatrixOp store,
257 void visitConvertLayoutOp(xegpu::ConvertLayoutOp convertLayout,
261 bool hasParamsOfLayoutKind(xegpu::DistributeLayoutAttr anchorLayout);
268 layoutKind(layoutKind), indexBitWidth(indexBitWidth) {}
275 void visitBranchOperand(
OpOperand &operand)
override {};
277 void visitCallOperand(
OpOperand &operand)
override {};
280 visitNonControlFlowArguments(RegionSuccessor &successor,
281 ArrayRef<BlockArgument> arguments)
override {};
283 void visitExternalCall(CallOpInterface call,
284 ArrayRef<LayoutInfoLattice *> operands,
285 ArrayRef<const LayoutInfoLattice *> results)
override {
288 void setToExitState(LayoutInfoLattice *lattice)
override {
289 (void)lattice->meet(LayoutInfo());
294LogicalResult LayoutInfoPropagation::visitOperation(
295 Operation *op, ArrayRef<LayoutInfoLattice *> operands,
296 ArrayRef<const LayoutInfoLattice *> results) {
299 [&](xegpu::DpasOp dpasOp) { visitDpasOp(dpasOp, operands, results); })
300 .Case([&](xegpu::DpasMxOp dpasMxOp) {
301 visitDpasMxOp(dpasMxOp, operands, results);
303 .Case([&](xegpu::StoreNdOp storeNdOp) {
304 visitStoreNdOp(storeNdOp, operands, results);
306 .Case([&](xegpu::StoreScatterOp storeScatterOp) {
307 visitStoreScatterOp(storeScatterOp, operands, results);
309 .Case([&](xegpu::LoadNdOp loadNdOp) {
310 visitLoadNdOp(loadNdOp, operands, results);
312 .Case([&](xegpu::LoadGatherOp loadGatherOp) {
313 visitLoadGatherOp(loadGatherOp, operands, results);
315 .Case([&](xegpu::PrefetchNdOp prefetchNdOp) {
316 visitPrefetchNdOp(prefetchNdOp, operands, results);
318 .Case([&](vector::TransposeOp transposeOp) {
319 visitTransposeOp(transposeOp, operands, results);
321 .Case([&](vector::BitCastOp bitcastOp) {
322 visitVectorBitcastOp(bitcastOp, operands, results);
324 .Case([&](vector::InterleaveOp interleaveOp) {
325 visitVectorInterleaveOp(interleaveOp, operands, results);
327 .Case([&](vector::DeinterleaveOp deinterleaveOp) {
328 visitVectorDeinterleaveOp(deinterleaveOp, operands, results);
330 .Case([&](vector::MultiDimReductionOp reductionOp) {
331 visitVectorMultiReductionOp(reductionOp, operands, results);
333 .Case([&](vector::ReductionOp reductionOp) {
334 visitVectorReductionOp(reductionOp, operands, results);
336 .Case([&](vector::BroadcastOp broadcastOp) {
337 visitVectorBroadCastOp(broadcastOp, operands, results);
339 .Case([&](vector::ShapeCastOp shapeCastOp) {
340 visitShapeCastOp(shapeCastOp, operands, results);
342 .Case([&](vector::InsertStridedSliceOp insertStridedSliceOp) {
343 visitInsertStridedSliceOp(insertStridedSliceOp, operands, results);
345 .Case([&](xegpu::LoadMatrixOp loadMatrixOp) {
346 visitLoadMatrixOp(loadMatrixOp, operands, results);
348 .Case([&](xegpu::StoreMatrixOp storeMatrixOp) {
349 visitStoreMatrixOp(storeMatrixOp, operands, results);
351 .Case([&](xegpu::ConvertLayoutOp convertLayoutOp) {
352 visitConvertLayoutOp(convertLayoutOp, operands, results);
355 .Default([&](Operation *op) {
356 for (
const LayoutInfoLattice *resultInfo : results) {
357 if (!resultInfo->getValue().isAssigned())
359 for (
auto [operandInfo, operand] :
363 if (!isa<xegpu::TensorDescType, VectorType>(
364 operand.get().getType()))
367 meet(operandInfo, *resultInfo);
375bool LayoutInfoPropagation::hasParamsOfLayoutKind(
376 xegpu::DistributeLayoutAttr anchorLayout) {
377 if (anchorLayout ==
nullptr) {
380 if (layoutKind == xegpu::LayoutKind::InstData) {
381 return !(anchorLayout.getEffectiveInstDataAsInt().empty());
383 if (layoutKind == xegpu::LayoutKind::Lane) {
384 return !(anchorLayout.getEffectiveLaneLayoutAsInt().empty() ||
385 anchorLayout.getEffectiveLaneDataAsInt().empty());
387 if (layoutKind == xegpu::LayoutKind::Subgroup) {
388 return !(anchorLayout.getEffectiveSgLayoutAsInt().empty() ||
389 anchorLayout.getEffectiveSgDataAsInt().empty());
401SmallVector<std::pair<int, int>>
405 for (
int sgLayout0 = 1; sgLayout0 <= sgCount; ++sgLayout0) {
406 if (sgCount % sgLayout0)
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]))
414 candidates.emplace_back(sgLayout0, sgLayout1);
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;
432 xegpu::DistributeLayoutAttr consumerLayout =
nullptr) {
434 if (consumerLayout) {
435 auto sgLayout = consumerLayout.getEffectiveSgLayoutAsInt();
436 if (!sgLayout.empty())
437 return llvm::product_of(sgLayout);
443 auto knownBlockSize = gpuFunc.getKnownBlockSize();
444 if (!knownBlockSize.has_value())
446 const int flatBlockSize = llvm::product_of(knownBlockSize.value());
447 return flatBlockSize / sgSize;
450void LayoutInfoPropagation::visitPrefetchNdOp(
451 xegpu::PrefetchNdOp prefetch, ArrayRef<LayoutInfoLattice *> operands,
452 ArrayRef<const LayoutInfoLattice *> results) {
454 LayoutInfo prefetchLayout;
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)
469 anchorLayout, prefetch.getTensorDescType().getElementType(),
470 uArchInstruction, uArch->getSubgroupSize());
472 prefetch.emitWarning(
473 "Failed to identify lane layouts for the specified inst_data.");
476 prefetch.setLayoutAttr(*completed);
477 prefetchLayout = LayoutInfo(*completed);
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.");
489 layoutKind, tdescTy, numSgOrErr.value_or(0), uArch);
491 prefetch.emitWarning(
492 "Failed to determine required layout for prefetch_nd.");
495 prefetchLayout = LayoutInfo(layoutAttr);
496 prefetch.setLayoutAttr(layoutAttr);
499 propagateIfChanged(operands[0], operands[0]->meet(prefetchLayout));
502void LayoutInfoPropagation::visitVectorMultiReductionOp(
503 vector::MultiDimReductionOp reduction,
504 ArrayRef<LayoutInfoLattice *> operands,
505 ArrayRef<const LayoutInfoLattice *> results) {
506 Type resultTy = reduction.getDestType();
508 LayoutInfo resLayoutInfo = results[0]->getValue();
510 xegpu::DistributeLayoutAttr consumerLayoutAttr;
512 if (!resLayoutInfo.isAssigned())
515 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
518 VectorType sourceTy = reduction.getSourceVectorType();
519 SmallVector<int64_t> reductionDims(reduction.getReductionDims());
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.");
540 layoutKind, sourceTy, consumerLayoutAttr, reductionDims,
541 numSgOrErr.value_or(0), uArch);
547 requiredResLayoutAttr, reductionDims);
549 propagateIfChanged(operands[0], operands[0]->meet(LayoutInfo(srcLayoutAttr)));
551 propagateIfChanged(operands[1],
552 operands[1]->meet(LayoutInfo(requiredResLayoutAttr)));
555void LayoutInfoPropagation::visitVectorReductionOp(
556 vector::ReductionOp reduction, ArrayRef<LayoutInfoLattice *> operands,
557 ArrayRef<const LayoutInfoLattice *> results) {
559 VectorType sourceTy = reduction.getSourceVectorType();
565 auto requiredResLayoutAttr =
570 propagateIfChanged(operands[0], operands[0]->meet(LayoutInfo(srcLayoutAttr)));
571 if (reduction.getAcc())
572 propagateIfChanged(operands[1],
573 operands[1]->meet(LayoutInfo(requiredResLayoutAttr)));
576void LayoutInfoPropagation::visitVectorBroadCastOp(
577 vector::BroadcastOp
broadcast, ArrayRef<LayoutInfoLattice *> operands,
578 ArrayRef<const LayoutInfoLattice *> results) {
580 LayoutInfo resLayoutInfo = results[0]->getValue();
581 if (!resLayoutInfo.isAssigned())
585 VectorType resultTy =
broadcast.getResultVectorType();
586 VectorType sourceTy = dyn_cast<VectorType>(
broadcast.getSourceType());
591 auto srcShape = sourceTy.getShape();
592 auto resShape = resultTy.getShape();
594 auto resultLayoutAttr =
595 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
597 xegpu::DistributeLayoutAttr srcLayoutAttr =
600 propagateIfChanged(operands[0], operands[0]->meet(LayoutInfo(srcLayoutAttr)));
603void LayoutInfoPropagation::visitShapeCastOp(
604 vector::ShapeCastOp shapeCast, ArrayRef<LayoutInfoLattice *> operands,
605 ArrayRef<const LayoutInfoLattice *> results) {
607 LayoutInfo resLayoutInfo = results[0]->getValue();
608 if (!resLayoutInfo.isAssigned())
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());
615 xegpu::DistributeLayoutAttr srcLayoutAttr =
619 if (!srcLayoutAttr) {
620 shapeCast.emitWarning(
"Failed to infer source layout for shape_cast; "
621 "unsupported shape-cast pattern.");
625 propagateIfChanged(operands[0], operands[0]->meet(LayoutInfo(srcLayoutAttr)));
629void LayoutInfoPropagation::visitDpasOp(
630 xegpu::DpasOp dpas, ArrayRef<LayoutInfoLattice *> operands,
631 ArrayRef<const LayoutInfoLattice *> results) {
632 LayoutInfo dpasALayout;
633 LayoutInfo dpasBLayout;
634 LayoutInfo dpasCDLayout;
639 VectorType aTy = dpas.getLhsType();
640 VectorType bTy = dpas.getRhsType();
641 VectorType cdTy = dpas.getResultType();
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);
659 "Failed to identify lane layouts for the specified inst_data.");
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);
672 xegpu::DistributeLayoutAttr consumerLayoutAttr =
nullptr;
673 xegpu::DistributeLayoutAttr requiredCDLayoutAttr, requiredALayout,
676 LayoutInfo consumerLayout = results[0]->getValue();
677 if (!consumerLayout.isAssigned())
680 dyn_cast<xegpu::DistributeLayoutAttr>(consumerLayout.get());
683 getNumSg(dpas, uArch->getSubgroupSize(), consumerLayoutAttr);
684 if (layoutKind == xegpu::LayoutKind::Subgroup &&
failed(numSgOrErr)) {
686 "Unable to determine the number of subgroups for the operation.");
692 numSgOrErr.value_or(0), uArch);
693 if (!layouts.has_value()) {
695 "Failed to determine required layouts for DPAS operands.");
699 std::tie(requiredALayout, requiredBLayout, requiredCDLayoutAttr) = *layouts;
701 dpas.setLayoutAAttr(requiredALayout);
702 dpas.setLayoutBAttr(requiredBLayout);
703 dpas.setLayoutCdAttr(requiredCDLayoutAttr);
704 dpasALayout = LayoutInfo(requiredALayout);
705 dpasBLayout = LayoutInfo(requiredBLayout);
706 dpasCDLayout = LayoutInfo(requiredCDLayoutAttr);
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));
717void LayoutInfoPropagation::visitDpasMxOp(
718 xegpu::DpasMxOp dpasMx, ArrayRef<LayoutInfoLattice *> operands,
719 ArrayRef<const LayoutInfoLattice *> results) {
722 LayoutInfo dpasMxALayout, dpasMxBLayout, dpasMxCDLayout;
723 LayoutInfo dpasMxAScaleLayout, dpasMxBScaleLayout;
726 xegpu::DistributeLayoutAttr anchorLayoutA = dpasMx.getLayoutAAttr();
727 xegpu::DistributeLayoutAttr anchorLayoutB = dpasMx.getLayoutBAttr();
728 xegpu::DistributeLayoutAttr anchorLayoutCD = dpasMx.getLayoutCdAttr();
734 VectorType aTy = dpasMx.getAType();
735 VectorType bTy = dpasMx.getBType();
736 VectorType cdTy = dpasMx.getResultType();
741 Value scaleA = dpasMx.getScaleA();
742 Value scaleB = dpasMx.getScaleB();
744 aScaleTy = dyn_cast<VectorType>(scaleA.
getType());
746 bScaleTy = dyn_cast<VectorType>(scaleB.
getType());
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);
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);
767 if (layoutKind == xegpu::LayoutKind::InstData) {
769 anchorLayoutA, anchorLayoutB, anchorLayoutCD, aTy, bTy, cdTy,
770 aScaleTy, bScaleTy, uArch);
773 "Failed to identify lane layouts for the specified inst_data.");
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);
788 if (completedBScale) {
789 dpasMx.setLayoutBScaleAttr(completedBScale);
790 dpasMxBScaleLayout = LayoutInfo(completedBScale);
794 xegpu::DistributeLayoutAttr consumerLayoutAttr =
nullptr;
795 xegpu::DistributeLayoutAttr requiredCDLayoutAttr, requiredALayout,
796 requiredBLayout, requiredAScaleLayout, requiredBScaleLayout;
798 LayoutInfo consumerLayout = results[0]->getValue();
799 if (!consumerLayout.isAssigned())
802 dyn_cast<xegpu::DistributeLayoutAttr>(consumerLayout.get());
805 getNumSg(dpasMx, uArch->getSubgroupSize(), consumerLayoutAttr);
806 if (layoutKind == xegpu::LayoutKind::Subgroup &&
failed(numSgOrErr)) {
808 "Unable to determine the number of subgroups for the operation.");
813 layoutKind, aTy, bTy, cdTy, aScaleTy, bScaleTy, consumerLayoutAttr,
814 numSgOrErr.value_or(0), uArch);
815 if (!layouts.has_value()) {
817 "Failed to determine required layouts for DPAS_MX operands.");
821 std::tie(requiredALayout, requiredBLayout, requiredCDLayoutAttr,
822 requiredAScaleLayout, requiredBScaleLayout) = *layouts;
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);
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);
845 propagateIfChanged(operands[0], operands[0]->meet(dpasMxALayout));
846 propagateIfChanged(operands[1], operands[1]->meet(dpasMxBLayout));
848 if (dpasMx.getAcc()) {
849 propagateIfChanged(operands[idx], operands[idx]->meet(dpasMxCDLayout));
852 if (dpasMx.getScaleA()) {
853 if (dpasMxAScaleLayout.isAssigned())
854 propagateIfChanged(operands[idx],
855 operands[idx]->meet(dpasMxAScaleLayout));
858 if (dpasMx.getScaleB()) {
859 if (dpasMxBScaleLayout.isAssigned())
860 propagateIfChanged(operands[idx],
861 operands[idx]->meet(dpasMxBScaleLayout));
867void LayoutInfoPropagation::visitStoreNdOp(
868 xegpu::StoreNdOp store, ArrayRef<LayoutInfoLattice *> operands,
869 ArrayRef<const LayoutInfoLattice *> results) {
870 LayoutInfo storeLayout;
874 xegpu::DistributeLayoutAttr anchorLayout = store.getLayoutAttr();
875 if (hasParamsOfLayoutKind(anchorLayout)) {
876 storeLayout = LayoutInfo(anchorLayout);
877 if (layoutKind == xegpu::LayoutKind::InstData) {
879 const auto *uArchInstruction =
880 dyn_cast<xegpu::uArch::Subgroup2DBlockStoreInstruction>(
881 uArch->getInstruction(
882 xegpu::uArch::InstructionKind::Subgroup2DBlockStore));
883 if (!uArchInstruction)
886 anchorLayout, store.getValueType().getElementType(), uArchInstruction,
887 uArch->getSubgroupSize());
890 "Failed to identify lane layouts for the specified inst_data.");
893 store.setLayoutAttr(*completed);
894 storeLayout = LayoutInfo(*completed);
897 auto numSgOrErr =
getNumSg(store, uArch->getSubgroupSize());
898 if (layoutKind == xegpu::LayoutKind::Subgroup &&
failed(numSgOrErr)) {
900 "Unable to determine the number of subgroups for the operation.");
905 layoutKind, store.getValueType(), numSgOrErr.value_or(0), uArch);
907 store.emitWarning(
"Failed to determine required layout for store_nd.");
910 storeLayout = LayoutInfo(layoutAttr);
911 store.setLayoutAttr(layoutAttr);
915 for (LayoutInfoLattice *operand : operands)
916 propagateIfChanged(operand, operand->meet(storeLayout));
921void LayoutInfoPropagation::visitLoadNdOp(
922 xegpu::LoadNdOp
load, ArrayRef<LayoutInfoLattice *> operands,
923 ArrayRef<const LayoutInfoLattice *> results) {
924 LayoutInfo loadLayout;
929 LayoutInfo valueLayout = results[0]->getValue();
930 if (!valueLayout.isAssigned())
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)
946 anchorLayout, consumerLayoutAttr,
load.getType().getElementType(),
947 uArchInstruction, uArch->getSubgroupSize());
950 "Failed to identify lane layouts for the specified inst_data.");
953 load.setLayoutAttr(*completed);
954 loadLayout = LayoutInfo(*completed);
958 getNumSg(
load, uArch->getSubgroupSize(), consumerLayoutAttr);
959 if (layoutKind == xegpu::LayoutKind::Subgroup &&
failed(numSgOrErr)) {
961 "Unable to determine the number of subgroups for the operation.");
965 layoutKind,
load.getType(), consumerLayoutAttr, numSgOrErr.value_or(0),
968 load.emitWarning(
"Failed to determine required layout for load_nd.");
971 loadLayout = LayoutInfo(layoutAttr);
972 load.setLayoutAttr(layoutAttr);
975 propagateIfChanged(operands[0], operands[0]->meet(loadLayout));
980void LayoutInfoPropagation::visitConvertLayoutOp(
981 xegpu::ConvertLayoutOp convert, ArrayRef<LayoutInfoLattice *> operands,
982 ArrayRef<const LayoutInfoLattice *> results) {
984 LayoutInfo resultLayout = results[0]->getValue();
987 auto targetLayoutAttr =
988 dyn_cast<xegpu::LayoutAttr>(convert.getTargetLayoutAttr());
989 auto inputLayoutAttr =
990 dyn_cast<xegpu::LayoutAttr>(convert.getInputLayoutAttr());
996 auto resultLayoutAttr = resultLayout.isAssigned()
997 ? dyn_cast<xegpu::LayoutAttr>(resultLayout.get())
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);
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);
1026 xegpu::DistributeLayoutAttr anchorLayout = convert.getInputLayoutAttr();
1027 LayoutInfo convertLayout(anchorLayout);
1029 propagateIfChanged(operands[0], operands[0]->meet(convertLayout));
1034void LayoutInfoPropagation::visitTransposeOp(
1035 vector::TransposeOp transpose, ArrayRef<LayoutInfoLattice *> operands,
1036 ArrayRef<const LayoutInfoLattice *> results) {
1038 LayoutInfo resultLayout = results[0]->getValue();
1039 if (!resultLayout.isAssigned())
1042 auto consumerLayoutAttr =
1043 dyn_cast<xegpu::DistributeLayoutAttr>(resultLayout.get());
1045 consumerLayoutAttr, transpose.getPermutation());
1048 propagateIfChanged(operands[0], operands[0]->meet(LayoutInfo(srcLayoutAttr)));
1053void LayoutInfoPropagation::visitVectorBitcastOp(
1054 vector::BitCastOp bitcast, ArrayRef<LayoutInfoLattice *> operands,
1055 ArrayRef<const LayoutInfoLattice *> results) {
1057 LayoutInfo resLayoutInfo = results[0]->getValue();
1058 if (!resLayoutInfo.isAssigned())
1061 auto srcVecType = bitcast.getSourceVectorType();
1062 auto resVecType = bitcast.getResultVectorType();
1064 auto consumerLayoutAttr =
1065 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1071 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1075 int inElemTyBitWidth = srcVecType.getElementType().getIntOrFloatBitWidth();
1076 int outElemTyBitWidth = resVecType.getElementType().getIntOrFloatBitWidth();
1080 requiredResLayoutAttr, outElemTyBitWidth, inElemTyBitWidth);
1082 propagateIfChanged(operands[0], operands[0]->meet(LayoutInfo(srcLayoutAttr)));
1088void LayoutInfoPropagation::visitVectorInterleaveOp(
1089 vector::InterleaveOp interleave, ArrayRef<LayoutInfoLattice *> operands,
1090 ArrayRef<const LayoutInfoLattice *> results) {
1092 LayoutInfo resLayoutInfo = results[0]->getValue();
1093 if (!resLayoutInfo.isAssigned())
1096 auto srcVecType = interleave.getSourceVectorType();
1097 auto resVecType = interleave.getResultVectorType();
1099 auto consumerLayoutAttr =
1100 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1108 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1113 auto srcLayoutAttr =
1117 propagateIfChanged(operands[0], operands[0]->meet(LayoutInfo(srcLayoutAttr)));
1118 propagateIfChanged(operands[1], operands[1]->meet(LayoutInfo(srcLayoutAttr)));
1124void LayoutInfoPropagation::visitVectorDeinterleaveOp(
1125 vector::DeinterleaveOp deinterleave, ArrayRef<LayoutInfoLattice *> operands,
1126 ArrayRef<const LayoutInfoLattice *> results) {
1129 LayoutInfo resLayoutInfo = results[0]->getValue();
1130 if (!resLayoutInfo.isAssigned())
1133 auto consumerLayoutAttr =
1134 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1140 propagateIfChanged(operands[0], operands[0]->meet(LayoutInfo(srcLayoutAttr)));
1143void LayoutInfoPropagation::visitInsertStridedSliceOp(
1144 vector::InsertStridedSliceOp insertStridedSlice,
1145 ArrayRef<LayoutInfoLattice *> operands,
1146 ArrayRef<const LayoutInfoLattice *> results) {
1148 LayoutInfo resLayoutInfo = results[0]->getValue();
1149 if (!resLayoutInfo.isAssigned())
1152 auto srcVecType = insertStridedSlice.getSourceVectorType();
1153 auto resVecType = insertStridedSlice.getDestVectorType();
1155 auto consumerLayoutAttr =
1156 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1163 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1165 requiredResLayoutAttr);
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)));
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();
1184 VectorType resVecTy =
load.getValueType();
1185 int chunkSize =
load.getChunkSize().value_or(1);
1187 LayoutInfo resLayoutInfo = results[0]->getValue();
1188 if (!resLayoutInfo.isAssigned())
1190 auto consumerLayoutAttr =
1191 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
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)
1203 anchorLayoutAttr, consumerLayoutAttr, resVecTy.getElementType(),
1204 uArchInstruction, uArch->getSubgroupSize());
1207 "Failed to identify lane layouts for the specified inst_data.");
1210 requiredAnchorLayoutAttr = *completed;
1211 load.setLayoutAttr(requiredAnchorLayoutAttr);
1215 load.emitWarning(
"Not propagating, non-vector payload supplied.");
1219 layoutKind, resVecTy, chunkSize, consumerLayoutAttr, uArch);
1220 load.setLayoutAttr(requiredAnchorLayoutAttr);
1223 assert((chunkSize <= 1) || (layoutKind != xegpu::LayoutKind::Subgroup));
1225 requiredAnchorLayoutAttr, chunkSize);
1226 LayoutInfo maskLayoutInfo = LayoutInfo(maskLayoutAttr);
1227 auto loadLayoutInfo = LayoutInfo(requiredAnchorLayoutAttr);
1230 if (isa<xegpu::TensorDescType>(
load.getSourceType()))
1231 propagateIfChanged(operands[0], operands[0]->meet(loadLayoutInfo));
1233 propagateIfChanged(operands[1], operands[1]->meet(maskLayoutInfo));
1234 propagateIfChanged(operands[2], operands[2]->meet(maskLayoutInfo));
1239void LayoutInfoPropagation::visitStoreScatterOp(
1240 xegpu::StoreScatterOp storeScatter, ArrayRef<LayoutInfoLattice *> operands,
1241 ArrayRef<const LayoutInfoLattice *> results) {
1243 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1244 xegpu::DistributeLayoutAttr anchorLayoutAttr = storeScatter.getLayoutAttr();
1249 VectorType srcVecTy = storeScatter.getValueType();
1250 int chunkSize = storeScatter.getChunkSize().value_or(1);
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)
1261 anchorLayoutAttr, srcVecTy.getElementType(), uArchInstruction,
1262 uArch->getSubgroupSize());
1264 storeScatter.emitWarning(
1265 "Failed to identify lane layouts for the specified inst_data.");
1268 requiredAnchorLayoutAttr = *completed;
1269 storeScatter.setLayoutAttr(requiredAnchorLayoutAttr);
1273 storeScatter.emitWarning(
"Not propagating, non-vector payload supplied.");
1277 layoutKind, srcVecTy, chunkSize, uArch);
1278 storeScatter.setLayoutAttr(requiredAnchorLayoutAttr);
1281 LayoutInfo srcLayoutInfo = LayoutInfo(requiredAnchorLayoutAttr);
1282 assert((chunkSize <= 1) || (layoutKind != xegpu::LayoutKind::Subgroup));
1284 requiredAnchorLayoutAttr, chunkSize);
1285 LayoutInfo maskLayoutInfo = LayoutInfo(maskLayoutAttr);
1288 propagateIfChanged(operands[0], operands[0]->meet(srcLayoutInfo));
1290 if (isa<xegpu::TensorDescType>(storeScatter.getDestType()))
1291 propagateIfChanged(operands[1], operands[1]->meet(srcLayoutInfo));
1293 propagateIfChanged(operands[2], operands[2]->meet(maskLayoutInfo));
1294 propagateIfChanged(operands[3], operands[3]->meet(maskLayoutInfo));
1297void LayoutInfoPropagation::visitLoadMatrixOp(
1298 xegpu::LoadMatrixOp loadMatrixOp, ArrayRef<LayoutInfoLattice *> operands,
1299 ArrayRef<const LayoutInfoLattice *> results) {
1301 LayoutInfo resLayoutInfo = results[0]->getValue();
1302 if (!resLayoutInfo.isAssigned())
1305 auto consumerLayoutAttr =
1306 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1308 xegpu::DistributeLayoutAttr anchorLayout = loadMatrixOp.getLayoutAttr();
1312 if (!hasParamsOfLayoutKind(anchorLayout)) {
1313 VectorType resVecTy =
1314 llvm::cast<VectorType>(loadMatrixOp.getRes().getType());
1322 layoutKind, resVecTy, chunkSize, consumerLayoutAttr, uArch);
1323 loadMatrixOp.setLayoutAttr(requiredAnchorLayoutAttr);
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();
1333 VectorType srcVecTy = llvm::cast<VectorType>(storeMatrix.getData().getType());
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)
1347 anchorLayoutAttr, srcVecTy.getElementType(), uArchInstruction,
1348 uArch->getSubgroupSize());
1350 storeMatrix.emitWarning(
1351 "Failed to identify lane layouts for the specified inst_data.");
1354 requiredAnchorLayoutAttr = *completed;
1355 storeMatrix.setLayoutAttr(requiredAnchorLayoutAttr);
1361 layoutKind, srcVecTy, chunkSize, uArch);
1362 storeMatrix.setLayoutAttr(requiredAnchorLayoutAttr);
1364 layout = LayoutInfo(requiredAnchorLayoutAttr);
1365 propagateIfChanged(operands[0], operands[0]->meet(layout));
1374class RunLayoutInfoPropagation {
1379 unsigned indexBitWidth)
1381 SymbolTableCollection symbolTable;
1383 solver.
load<LayoutInfoPropagation>(symbolTable, layoutKind, indexBitWidth);
1387 LayoutInfo getLayoutInfo(Value val);
1389 void printAnalysisResult(llvm::raw_ostream &os);
1392 DataFlowSolver solver;
1397LayoutInfo RunLayoutInfoPropagation::getLayoutInfo(Value val) {
1398 auto *state = solver.
lookupState<LayoutInfoLattice>(val);
1401 return state->getValue();
1405void RunLayoutInfoPropagation::printAnalysisResult(llvm::raw_ostream &os) {
1406 auto printFunctionResult = [&](FunctionOpInterface funcOp) {
1407 os <<
"function: " << funcOp.getName() <<
":\n";
1409 for (BlockArgument arg : funcOp.getArguments()) {
1410 LayoutInfo layout = getLayoutInfo(arg);
1411 os <<
"argument: " << arg <<
"\n";
1417 funcOp.walk([&](Operation *op) {
1423 if (isa<BranchOpInterface>(op) || isa<RegionBranchOpInterface>(op))
1429 for (
auto [i, r] : llvm::enumerate(op->
getResults())) {
1430 LayoutInfo layout = getLayoutInfo(r);
1431 os <<
"layout for result #" << i <<
": ";
1438 SmallVector<FunctionOpInterface> funcOps;
1439 if (
auto modOp = dyn_cast<ModuleOp>(
target)) {
1440 for (
auto funcOp : modOp.getOps<FunctionOpInterface>())
1441 funcOps.push_back(funcOp);
1444 for (
auto gpuModOp : modOp.getOps<gpu::GPUModuleOp>()) {
1445 for (
auto gpuFuncOp : gpuModOp.getOps<FunctionOpInterface>())
1446 funcOps.push_back(gpuFuncOp);
1450 for (FunctionOpInterface funcOp : funcOps)
1451 printFunctionResult(funcOp);
1463static xegpu::CreateNdDescOp getDefiningCreateNdDescOp(Value tdescValue) {
1465 auto definingOp = tdescValue.
getDefiningOp<xegpu::CreateNdDescOp>();
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);
1475 return getDefiningCreateNdDescOp(tiedInit->
get());
1482struct ResolveLayoutConflicts {
1483 ResolveLayoutConflicts(Operation *parentOp)
1484 : parentOp(parentOp), builder(parentOp->
getContext()) {}
1485 LogicalResult run();
1488 Operation *parentOp;
1490 LogicalResult resolveTensorDescConsumer(OpOperand &operand);
1491 LogicalResult resolveVectorConsumer(OpOperand &operand);
1492 LogicalResult assignResultLayout(OpResult &
result);
1497LogicalResult ResolveLayoutConflicts::run() {
1500 auto r = parentOp->
walk([&](Operation *op) -> WalkResult {
1504 if (isa<vector::MultiDimReductionOp>(op) || isa<vector::ReductionOp>(op)) {
1506 if (
result.getType().isIntOrFloat()) {
1507 auto res = assignResultLayout(
result);
1509 DBGS() <<
"Failed to resolve vector consumer for multi-reduction "
1518 Type operandType = operand.get().getType();
1519 if (isa<xegpu::AnchorLayoutInterface>(op) &&
1520 isa<xegpu::TensorDescType>(operandType)) {
1521 auto res = resolveTensorDescConsumer(operand);
1523 DBGS() <<
"Failed to resolve tensor descriptor consumer: " << *op
1529 if (isa<VectorType>(operandType)) {
1530 auto res = resolveVectorConsumer(operand);
1532 DBGS() <<
"Failed to resolve vector consumer: " << *op <<
"\n";
1541 DBGS() <<
"IR after resolving layout conflicts:\n";
1545 return r.wasInterrupted() ? failure() :
success();
1548LogicalResult ResolveLayoutConflicts::assignResultLayout(OpResult &
result) {
1549 Operation *producerOp =
result.getDefiningOp();
1553 auto convertOp = xegpu::ConvertLayoutOp::create(
1556 result.replaceAllUsesExcept(convertOp.getResult(), convertOp);
1561ResolveLayoutConflicts::resolveVectorConsumer(OpOperand &operand) {
1562 Value vectorValue = operand.
get();
1563 Operation *consumerOp = operand.
getOwner();
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.");
1577 if (isa<RegionBranchOpInterface, RegionBranchTerminatorOpInterface>(
1582 if (!consumerLayout)
1584 "No consumer layout found for vector operand.");
1587 if (consumerLayout.isEqualTo(producerLayout))
1593 if (
auto consumerConvert = dyn_cast<xegpu::ConvertLayoutOp>(consumerOp)) {
1594 consumerConvert.setInputLayoutAttr(producerLayout);
1600 if (
auto producerConvert =
1602 producerConvert && vectorValue.
hasOneUse()) {
1603 producerConvert.setTargetLayoutAttr(consumerLayout);
1615 isa<OpResult>(vectorValue) &&
1618 Operation *
clone = builder.
clone(*producerOp);
1623 operand.
set(cloneResult);
1629 auto convertOp = xegpu::ConvertLayoutOp::create(
1630 builder, consumerOp->
getLoc(), vectorValue.
getType(), vectorValue,
1631 producerLayout, consumerLayout);
1634 operand.
set(convertOp.getResult());
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();
1650 if (expectedLayout && currLayout && expectedLayout != currLayout) {
1652 auto conflictingCreateNdOp = getDefiningCreateNdDescOp(tdescValue);
1653 if (!conflictingCreateNdOp) {
1654 DBGS() <<
"Unable to find defining CreateNdDescOp for tensor descriptor: "
1655 << tdescValue <<
"\n";
1660 auto newTensorDescType = xegpu::TensorDescType::get(
1661 conflictingCreateNdOp.getContext(), currTDescType.getShape(),
1662 currTDescType.getElementType(), currTDescType.getEncoding(),
1664 xegpu::CreateNdDescOp newOp = xegpu::CreateNdDescOp::create(
1665 builder, consumerOp->
getLoc(), newTensorDescType,
1666 conflictingCreateNdOp->getOperands(),
1667 conflictingCreateNdOp->getAttrs());
1685 if (mlir::isa<mlir::RegionBranchOpInterface>(op))
1692 if (!isa<VectorType, xegpu::TensorDescType>(resultType))
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");
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);
1740 mlir::RegionBranchTerminatorOpInterface terminator,
1743 auto branchOp = dyn_cast<RegionBranchOpInterface>(terminator->getParentOp());
1748 branchOp.getSuccessorOperandInputMapping(mapping,
1750 for (
const auto &[successorOperand, successorInputs] : mapping) {
1751 for (
Value successorInput : successorInputs) {
1752 Type inputType = successorInput.getType();
1754 if (!isa<xegpu::TensorDescType, VectorType>(inputType))
1756 xegpu::DistributeLayoutAttr successorOperandLayout =
1757 getLayoutOfValue(successorOperand->get());
1760 if (!successorOperandLayout) {
1761 LLVM_DEBUG(
DBGS() <<
"No layout assigned for forwarded operand in "
1762 "branch terminator: "
1763 << successorOperand->get() <<
"\n");
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);
1776 if (
auto result = dyn_cast<OpResult>(successorInput))
1785 mlir::FunctionOpInterface funcOp,
1791 if (!isa<FunctionType>(funcOp.getFunctionType()))
1796 Type argType = arg.getType();
1797 newArgTypes.push_back(argType);
1798 if (!isa<VectorType, xegpu::TensorDescType>(argType))
1800 xegpu::DistributeLayoutAttr layout = getLayoutOfValue(arg);
1802 LLVM_DEBUG(
DBGS() <<
"Expecting layout for function argument: " << arg
1803 <<
" but got none.\n");
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;
1816 funcOp.setType(FunctionType::get(funcOp.getContext(), newArgTypes,
1817 funcOp.getResultTypes()));
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;
1835 unsigned indexBitWidth,
bool printOnly) {
1836 RunLayoutInfoPropagation analysis(
target, layoutKind, indexBitWidth);
1839 auto &os = llvm::outs();
1840 analysis.printAnalysisResult(os);
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;
1854 xegpu::DistributeLayoutAttr requiredResLayoutAttr =
1856 if (requiredResLayoutAttr !=
nullptr)
1857 return requiredResLayoutAttr;
1859 if (!layout.isAssigned())
1861 xegpu::DistributeLayoutAttr layoutAttr =
1862 cast<xegpu::DistributeLayoutAttr>(layout.get());
1863 if (layout.isSliceLayout())
1864 return cast<xegpu::SliceAttr>(layoutAttr);
1866 return cast<xegpu::LayoutAttr>(layoutAttr);
1874 .Case([&](mlir::RegionBranchTerminatorOpInterface branchTermOp) {
1876 getLayoutFromPropagation);
1878 .Case([&](mlir::RegionBranchOpInterface branchOp) {
1880 getLayoutFromPropagation);
1882 .Case([&](mlir::FunctionOpInterface funcOp) {
1884 getLayoutFromPropagation);
1887 r =
updateOp(builder, op, getLayoutFromPropagation);
1890 op.
emitError(
"Failed to update operation with the layout.");
1896 if (walkResult.wasInterrupted())
1903 ResolveLayoutConflicts resolver(
target);
1904 return resolver.run();
1907void XeGPUPropagateLayoutPass::runOnOperation() {
1912 if (this->layoutKind ==
"lane") {
1914 }
else if (this->layoutKind ==
"inst") {
1916 }
else if (this->layoutKind ==
"subgroup") {
1917 layoutKind = xegpu::LayoutKind::Subgroup;
1919 getOperation()->emitError(
"Unsupported layout kind option: " +
1921 signalPassFailure();
1926 this->indexBitWidth, this->printOnly))) {
1927 signalPassFailure();
1932 signalPassFailure();
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
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)
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.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
OpListType & getOperations()
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.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointAfterValue(Value val)
Sets the insertion point to the node after the specified value.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents an operand of an operation.
This is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
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.
Location getLoc()
The source location the operation was defined or derived from.
MutableArrayRef< OpOperand > getOpOperands()
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'.
OperationName getName()
The name of an operation is the key identifier for it.
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),...
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
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...
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
bool hasOneUse() const
Returns true if this value has exactly one use.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
static WalkResult interrupt()
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.
void loadBaselineAnalyses(DataFlowSolver &solver)
Populates a DataFlowSolver with analyses that are required to ensure user-defined analyses are run pr...
const uArch * getUArch(llvm::StringRef archName)
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.
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
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