33#include "llvm/ADT/ArrayRef.h"
34#include "llvm/ADT/DenseMap.h"
35#include "llvm/ADT/STLExtras.h"
36#include "llvm/ADT/SmallSet.h"
37#include "llvm/ADT/SmallVector.h"
38#include "llvm/ADT/TypeSwitch.h"
39#include "llvm/Support/Casting.h"
40#include "llvm/Support/Debug.h"
41#include "llvm/Support/LogicalResult.h"
42#include "llvm/Support/MathExtras.h"
43#include "llvm/Support/raw_ostream.h"
48#define GEN_PASS_DEF_XEGPUPROPAGATELAYOUT
49#include "mlir/Dialect/XeGPU/Transforms/Passes.h.inc"
53#define DEBUG_TYPE "xegpu-propagate-layout"
54#define DBGS() (llvm::dbgs() << "[" DEBUG_TYPE "]: ")
98 xegpu::DistributeLayoutAttr storage =
nullptr;
101 int64_t programOrder = std::numeric_limits<int64_t>::max();
104 LayoutInfo() =
default;
105 LayoutInfo(
const xegpu::DistributeLayoutAttr &layout,
int64_t programOrder)
106 : storage(layout), programOrder(programOrder) {}
112 bool operator==(
const LayoutInfo &other)
const {
113 if (isAssigned() != other.isAssigned())
117 return storage.isEqualTo(other.storage);
120 static LayoutInfo meet(
const LayoutInfo &lhs,
const LayoutInfo &rhs);
122 static LayoutInfo
join(
const LayoutInfo &lhs,
const LayoutInfo &rhs);
126 bool isAssigned()
const {
return storage !=
nullptr; }
140 bool isSliceLayout()
const {
143 return isa<xegpu::SliceAttr>(storage);
149 return storage.getRank();
153 void set(
const xegpu::DistributeLayoutAttr &layout) { storage = layout; }
160 os <<
"Not assigned.";
164LayoutInfo LayoutInfo::meet(
const LayoutInfo &lhs,
const LayoutInfo &rhs) {
165 if (!lhs.isAssigned())
167 if (!rhs.isAssigned())
172 if (rhs.programOrder < lhs.programOrder)
178LayoutInfo LayoutInfo::join(
const LayoutInfo &lhs,
const LayoutInfo &rhs) {
179 llvm_unreachable(
"Join should not be triggered by layout propagation.");
187struct LayoutInfoLattice :
public Lattice<LayoutInfo> {
189 using Lattice::Lattice;
201class LayoutInfoPropagation
208 unsigned indexBitWidth;
223 int64_t currentProgramOrder = std::numeric_limits<int64_t>::max();
224 LayoutInfo makeLayoutInfo(
const xegpu::DistributeLayoutAttr &layout) {
225 return LayoutInfo(layout, currentProgramOrder);
231 void visitDpasMxOp(xegpu::DpasMxOp dpasMx,
235 void visitStoreNdOp(xegpu::StoreNdOp store,
239 void visitStoreScatterOp(xegpu::StoreScatterOp storeScatter,
243 void visitLoadNdOp(xegpu::LoadNdOp
load,
247 void visitLoadGatherOp(xegpu::LoadGatherOp
load,
251 void visitTransposeOp(vector::TransposeOp transpose,
255 void visitVectorBitcastOp(vector::BitCastOp bitcast,
259 void visitVectorInterleaveOp(vector::InterleaveOp interleave,
263 void visitVectorDeinterleaveOp(vector::DeinterleaveOp deinterleave,
267 void visitPrefetchNdOp(xegpu::PrefetchNdOp prefetch,
271 void visitVectorMultiReductionOp(vector::MultiDimReductionOp reduction,
275 void visitVectorReductionOp(vector::ReductionOp reduction,
279 void visitVectorBroadCastOp(vector::BroadcastOp
broadcast,
282 void visitShapeCastOp(vector::ShapeCastOp shapeCast,
286 visitInsertStridedSliceOp(vector::InsertStridedSliceOp insertStridedSlice,
290 void visitLoadMatrixOp(xegpu::LoadMatrixOp
load,
294 void visitStoreMatrixOp(xegpu::StoreMatrixOp store,
298 void visitLoadGatherOp(xegpu::LoadMatrixOp
load,
302 void visitStoreScatterOp(xegpu::StoreMatrixOp store,
306 void visitConvertLayoutOp(xegpu::ConvertLayoutOp convertLayout,
310 bool hasParamsOfLayoutKind(xegpu::DistributeLayoutAttr anchorLayout);
313 FailureOr<int64_t> getNumSgOrFail(
Operation *op,
int sgSize,
314 xegpu::DistributeLayoutAttr consumerLayout);
317 bool propagationFailed =
false;
321 void markFailure(
Operation *op,
const llvm::Twine &message) {
323 propagationFailed =
true;
327 bool hasFailed()
const {
return propagationFailed; }
334 layoutKind(layoutKind), indexBitWidth(indexBitWidth),
335 scopeRoot(scopeRoot) {}
342 void visitBranchOperand(
OpOperand &operand)
override {};
344 void visitCallOperand(
OpOperand &operand)
override {};
350 void visitExternalCall(CallOpInterface call,
355 void setToExitState(LayoutInfoLattice *lattice)
override {
356 (
void)lattice->meet(LayoutInfo());
361int64_t LayoutInfoPropagation::getProgramOrder(Operation *op) {
362 auto it = programOrder.find(op);
363 if (it != programOrder.end())
372 scopeRoot->
walk<WalkOrder::PreOrder>(
373 [&](Operation *o) { programOrder[o] = counter++; });
374 return programOrder.lookup(op);
377LogicalResult LayoutInfoPropagation::visitOperation(
378 Operation *op, ArrayRef<LayoutInfoLattice *> operands,
379 ArrayRef<const LayoutInfoLattice *> results) {
382 currentProgramOrder = getProgramOrder(op);
385 [&](xegpu::DpasOp dpasOp) { visitDpasOp(dpasOp, operands, results); })
386 .Case([&](xegpu::DpasMxOp dpasMxOp) {
387 visitDpasMxOp(dpasMxOp, operands, results);
389 .Case([&](xegpu::StoreNdOp storeNdOp) {
390 visitStoreNdOp(storeNdOp, operands, results);
392 .Case([&](xegpu::StoreScatterOp storeScatterOp) {
393 visitStoreScatterOp(storeScatterOp, operands, results);
395 .Case([&](xegpu::LoadNdOp loadNdOp) {
396 visitLoadNdOp(loadNdOp, operands, results);
398 .Case([&](xegpu::LoadGatherOp loadGatherOp) {
399 visitLoadGatherOp(loadGatherOp, operands, results);
401 .Case([&](xegpu::PrefetchNdOp prefetchNdOp) {
402 visitPrefetchNdOp(prefetchNdOp, operands, results);
404 .Case([&](vector::TransposeOp transposeOp) {
405 visitTransposeOp(transposeOp, operands, results);
407 .Case([&](vector::BitCastOp bitcastOp) {
408 visitVectorBitcastOp(bitcastOp, operands, results);
410 .Case([&](vector::InterleaveOp interleaveOp) {
411 visitVectorInterleaveOp(interleaveOp, operands, results);
413 .Case([&](vector::DeinterleaveOp deinterleaveOp) {
414 visitVectorDeinterleaveOp(deinterleaveOp, operands, results);
416 .Case([&](vector::MultiDimReductionOp reductionOp) {
417 visitVectorMultiReductionOp(reductionOp, operands, results);
419 .Case([&](vector::ReductionOp reductionOp) {
420 visitVectorReductionOp(reductionOp, operands, results);
422 .Case([&](vector::BroadcastOp broadcastOp) {
423 visitVectorBroadCastOp(broadcastOp, operands, results);
425 .Case([&](vector::ShapeCastOp shapeCastOp) {
426 visitShapeCastOp(shapeCastOp, operands, results);
428 .Case([&](vector::InsertStridedSliceOp insertStridedSliceOp) {
429 visitInsertStridedSliceOp(insertStridedSliceOp, operands, results);
431 .Case([&](xegpu::LoadMatrixOp loadMatrixOp) {
432 visitLoadMatrixOp(loadMatrixOp, operands, results);
434 .Case([&](xegpu::StoreMatrixOp storeMatrixOp) {
435 visitStoreMatrixOp(storeMatrixOp, operands, results);
437 .Case([&](xegpu::ConvertLayoutOp convertLayoutOp) {
438 visitConvertLayoutOp(convertLayoutOp, operands, results);
441 .Default([&](Operation *op) {
442 for (
const LayoutInfoLattice *resultInfo : results) {
443 if (!resultInfo->getValue().isAssigned())
445 for (
auto [operandInfo, operand] :
449 if (!isa<xegpu::TensorDescType, VectorType>(
450 operand.get().getType()))
453 meet(operandInfo, *resultInfo);
461bool LayoutInfoPropagation::hasParamsOfLayoutKind(
462 xegpu::DistributeLayoutAttr anchorLayout) {
463 if (anchorLayout ==
nullptr) {
466 if (layoutKind == xegpu::LayoutKind::InstData) {
467 return !(anchorLayout.getEffectiveInstDataAsInt().empty());
469 if (layoutKind == xegpu::LayoutKind::Lane) {
470 return !(anchorLayout.getEffectiveLaneLayoutAsInt().empty() ||
471 anchorLayout.getEffectiveLaneDataAsInt().empty());
473 if (layoutKind == xegpu::LayoutKind::Subgroup) {
474 return !(anchorLayout.getEffectiveSgLayoutAsInt().empty() ||
475 anchorLayout.getEffectiveSgDataAsInt().empty());
480FailureOr<int64_t> LayoutInfoPropagation::getNumSgOrFail(
481 Operation *op,
int sgSize, xegpu::DistributeLayoutAttr consumerLayout) {
483 if (consumerLayout) {
484 auto sgLayout = consumerLayout.getEffectiveSgLayoutAsInt();
485 if (!sgLayout.empty())
486 return llvm::product_of(sgLayout);
490 std::optional<ArrayRef<int32_t>> knownBlockSize =
491 gpuFunc.getKnownBlockSize();
492 if (knownBlockSize) {
493 bool isPowerOf2Block = llvm::all_of(*knownBlockSize, [](int32_t dim) {
494 return dim > 0 && llvm::isPowerOf2_32(dim);
496 int64_t numSg = llvm::product_of(*knownBlockSize) / sgSize;
497 if (isPowerOf2Block && numSg > 0)
502 if (layoutKind == xegpu::LayoutKind::Subgroup) {
503 markFailure(op,
"Unable to determine the number of subgroups for the "
504 "operation. Please check @known_block_size is properly "
505 "attached as kernel attributes, with power-of-two "
506 "dimensions covering at least one subgroup.");
512void LayoutInfoPropagation::visitPrefetchNdOp(
513 xegpu::PrefetchNdOp prefetch, ArrayRef<LayoutInfoLattice *> operands,
514 ArrayRef<const LayoutInfoLattice *> results) {
516 LayoutInfo prefetchLayout;
520 xegpu::DistributeLayoutAttr anchorLayout = prefetch.getLayoutAttr();
521 if (hasParamsOfLayoutKind(anchorLayout)) {
522 prefetchLayout = makeLayoutInfo(anchorLayout);
523 if (layoutKind == xegpu::LayoutKind::InstData) {
524 const auto *uArchInstruction =
525 dyn_cast<xegpu::uArch::Subgroup2DBlockPrefetchInstruction>(
526 uArch->getInstruction(
527 xegpu::uArch::InstructionKind::Subgroup2DBlockPrefetch));
528 if (!uArchInstruction)
531 anchorLayout, prefetch.getTensorDescType().getElementType(),
532 uArchInstruction, uArch->getSubgroupSize());
534 prefetch.emitWarning(
535 "Failed to identify lane layouts for the specified inst_data.");
538 prefetch.setLayoutAttr(*completed);
539 prefetchLayout = makeLayoutInfo(*completed);
542 auto tdescTy = prefetch.getTensorDescType();
544 getNumSgOrFail(prefetch, uArch->getSubgroupSize(),
nullptr);
549 layoutKind, tdescTy, numSgOrErr.value_or(0), uArch);
551 prefetch.emitWarning(
552 "Failed to determine required layout for prefetch_nd.");
555 prefetchLayout = makeLayoutInfo(layoutAttr);
556 prefetch.setLayoutAttr(layoutAttr);
559 propagateIfChanged(operands[0], operands[0]->meet(prefetchLayout));
562void LayoutInfoPropagation::visitVectorMultiReductionOp(
563 vector::MultiDimReductionOp reduction,
564 ArrayRef<LayoutInfoLattice *> operands,
565 ArrayRef<const LayoutInfoLattice *> results) {
566 Type resultTy = reduction.getDestType();
568 LayoutInfo resLayoutInfo = results[0]->getValue();
570 xegpu::DistributeLayoutAttr consumerLayoutAttr;
572 if (!resLayoutInfo.isAssigned())
575 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
578 VectorType sourceTy = reduction.getSourceVectorType();
579 SmallVector<int64_t> reductionDims(reduction.getReductionDims());
587 getNumSgOrFail(reduction, uArch->getSubgroupSize(), consumerLayoutAttr);
597 layoutKind, sourceTy, consumerLayoutAttr, reductionDims,
598 numSgOrErr.value_or(0), uArch);
604 requiredResLayoutAttr, reductionDims);
606 propagateIfChanged(operands[0],
607 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
609 propagateIfChanged(operands[1],
610 operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
613void LayoutInfoPropagation::visitVectorReductionOp(
614 vector::ReductionOp reduction, ArrayRef<LayoutInfoLattice *> operands,
615 ArrayRef<const LayoutInfoLattice *> results) {
617 VectorType sourceTy = reduction.getSourceVectorType();
623 auto requiredResLayoutAttr =
628 propagateIfChanged(operands[0],
629 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
630 if (reduction.getAcc())
632 operands[1], operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
635void LayoutInfoPropagation::visitVectorBroadCastOp(
636 vector::BroadcastOp
broadcast, ArrayRef<LayoutInfoLattice *> operands,
637 ArrayRef<const LayoutInfoLattice *> results) {
639 LayoutInfo resLayoutInfo = results[0]->getValue();
640 if (!resLayoutInfo.isAssigned())
644 VectorType resultTy =
broadcast.getResultVectorType();
645 VectorType sourceTy = dyn_cast<VectorType>(
broadcast.getSourceType());
650 auto srcShape = sourceTy.getShape();
651 auto resShape = resultTy.getShape();
653 auto resultLayoutAttr =
654 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
656 xegpu::DistributeLayoutAttr srcLayoutAttr =
659 propagateIfChanged(operands[0],
660 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
663void LayoutInfoPropagation::visitShapeCastOp(
664 vector::ShapeCastOp shapeCast, ArrayRef<LayoutInfoLattice *> operands,
665 ArrayRef<const LayoutInfoLattice *> results) {
667 LayoutInfo resLayoutInfo = results[0]->getValue();
668 if (!resLayoutInfo.isAssigned())
670 ArrayRef<int64_t> resShape = shapeCast.getResultVectorType().getShape();
671 ArrayRef<int64_t> srcShape = shapeCast.getSourceVectorType().getShape();
672 auto resultLayoutAttr =
673 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
675 xegpu::DistributeLayoutAttr srcLayoutAttr =
679 if (!srcLayoutAttr) {
680 shapeCast.emitWarning(
"Failed to infer source layout for shape_cast; "
681 "unsupported shape-cast pattern.");
685 propagateIfChanged(operands[0],
686 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
690void LayoutInfoPropagation::visitDpasOp(
691 xegpu::DpasOp dpas, ArrayRef<LayoutInfoLattice *> operands,
692 ArrayRef<const LayoutInfoLattice *> results) {
693 LayoutInfo dpasALayout;
694 LayoutInfo dpasBLayout;
695 LayoutInfo dpasCDLayout;
700 VectorType aTy = dpas.getLhsType();
701 VectorType bTy = dpas.getRhsType();
702 VectorType cdTy = dpas.getResultType();
704 xegpu::DistributeLayoutAttr anchorLayoutCD = dpas.getLayoutCdAttr();
705 if (hasParamsOfLayoutKind(anchorLayoutCD)) {
706 xegpu::DistributeLayoutAttr anchorLayoutA = dpas.getLayoutAAttr();
707 xegpu::DistributeLayoutAttr anchorLayoutB = dpas.getLayoutBAttr();
708 assert(hasParamsOfLayoutKind(anchorLayoutA) &&
709 "Expected anchor layout for DPAS A operand.");
710 assert(hasParamsOfLayoutKind(anchorLayoutB) &&
711 "Expected anchor layout for DPAS B operand.");
712 dpasALayout = makeLayoutInfo(anchorLayoutA);
713 dpasBLayout = makeLayoutInfo(anchorLayoutB);
714 dpasCDLayout = makeLayoutInfo(anchorLayoutCD);
715 if (layoutKind == xegpu::LayoutKind::InstData) {
717 anchorLayoutA, anchorLayoutB, anchorLayoutCD, aTy, bTy, cdTy, uArch);
721 "Failed to identify lane layouts for the specified inst_data.");
724 auto [completedA, completedB, completedCD] = *completed;
725 dpas.setLayoutAAttr(completedA);
726 dpas.setLayoutBAttr(completedB);
727 dpas.setLayoutCdAttr(completedCD);
728 dpasALayout = makeLayoutInfo(completedA);
729 dpasBLayout = makeLayoutInfo(completedB);
730 dpasCDLayout = makeLayoutInfo(completedCD);
734 xegpu::DistributeLayoutAttr consumerLayoutAttr =
nullptr;
735 xegpu::DistributeLayoutAttr requiredCDLayoutAttr, requiredALayout,
738 LayoutInfo consumerLayout = results[0]->getValue();
739 if (!consumerLayout.isAssigned())
742 dyn_cast<xegpu::DistributeLayoutAttr>(consumerLayout.get());
745 getNumSgOrFail(dpas, uArch->getSubgroupSize(), consumerLayoutAttr);
751 numSgOrErr.value_or(0), uArch);
752 if (!layouts.has_value()) {
754 "Failed to determine required layouts for DPAS operands.");
758 std::tie(requiredALayout, requiredBLayout, requiredCDLayoutAttr) = *layouts;
760 dpas.setLayoutAAttr(requiredALayout);
761 dpas.setLayoutBAttr(requiredBLayout);
762 dpas.setLayoutCdAttr(requiredCDLayoutAttr);
763 dpasALayout = makeLayoutInfo(requiredALayout);
764 dpasBLayout = makeLayoutInfo(requiredBLayout);
765 dpasCDLayout = makeLayoutInfo(requiredCDLayoutAttr);
767 propagateIfChanged(operands[0], operands[0]->meet(dpasALayout));
768 propagateIfChanged(operands[1], operands[1]->meet(dpasBLayout));
769 if (operands.size() > 2)
770 propagateIfChanged(operands[2], operands[2]->meet(dpasCDLayout));
776void LayoutInfoPropagation::visitDpasMxOp(
777 xegpu::DpasMxOp dpasMx, ArrayRef<LayoutInfoLattice *> operands,
778 ArrayRef<const LayoutInfoLattice *> results) {
781 LayoutInfo dpasMxALayout, dpasMxBLayout, dpasMxCDLayout;
782 LayoutInfo dpasMxAScaleLayout, dpasMxBScaleLayout;
785 xegpu::DistributeLayoutAttr anchorLayoutA = dpasMx.getLayoutAAttr();
786 xegpu::DistributeLayoutAttr anchorLayoutB = dpasMx.getLayoutBAttr();
787 xegpu::DistributeLayoutAttr anchorLayoutCD = dpasMx.getLayoutCdAttr();
793 VectorType aTy = dpasMx.getAType();
794 VectorType bTy = dpasMx.getBType();
795 VectorType cdTy = dpasMx.getResultType();
800 Value scaleA = dpasMx.getScaleA();
801 Value scaleB = dpasMx.getScaleB();
803 aScaleTy = dyn_cast<VectorType>(scaleA.
getType());
805 bScaleTy = dyn_cast<VectorType>(scaleB.
getType());
808 if (anchorLayoutA && anchorLayoutB && anchorLayoutCD &&
809 hasParamsOfLayoutKind(anchorLayoutA) &&
810 hasParamsOfLayoutKind(anchorLayoutB) &&
811 hasParamsOfLayoutKind(anchorLayoutCD)) {
812 dpasMxALayout = makeLayoutInfo(anchorLayoutA);
813 dpasMxBLayout = makeLayoutInfo(anchorLayoutB);
814 dpasMxCDLayout = makeLayoutInfo(anchorLayoutCD);
817 xegpu::DistributeLayoutAttr anchorLayoutAScale =
818 dpasMx.getLayoutAScaleAttr();
819 xegpu::DistributeLayoutAttr anchorLayoutBScale =
820 dpasMx.getLayoutBScaleAttr();
821 if (anchorLayoutAScale)
822 dpasMxAScaleLayout = makeLayoutInfo(anchorLayoutAScale);
823 if (anchorLayoutBScale)
824 dpasMxBScaleLayout = makeLayoutInfo(anchorLayoutBScale);
826 if (layoutKind == xegpu::LayoutKind::InstData) {
828 anchorLayoutA, anchorLayoutB, anchorLayoutCD, aTy, bTy, cdTy,
829 aScaleTy, bScaleTy, uArch);
833 "Failed to identify lane layouts for the specified inst_data.");
836 auto [completedA, completedB, completedCD, completedAScale,
837 completedBScale] = *completed;
838 dpasMx.setLayoutAAttr(completedA);
839 dpasMx.setLayoutBAttr(completedB);
840 dpasMx.setLayoutCdAttr(completedCD);
841 dpasMxALayout = makeLayoutInfo(completedA);
842 dpasMxBLayout = makeLayoutInfo(completedB);
843 dpasMxCDLayout = makeLayoutInfo(completedCD);
844 if (completedAScale) {
845 dpasMx.setLayoutAScaleAttr(completedAScale);
846 dpasMxAScaleLayout = makeLayoutInfo(completedAScale);
848 if (completedBScale) {
849 dpasMx.setLayoutBScaleAttr(completedBScale);
850 dpasMxBScaleLayout = makeLayoutInfo(completedBScale);
854 xegpu::DistributeLayoutAttr consumerLayoutAttr =
nullptr;
855 xegpu::DistributeLayoutAttr requiredCDLayoutAttr, requiredALayout,
856 requiredBLayout, requiredAScaleLayout, requiredBScaleLayout;
858 LayoutInfo consumerLayout = results[0]->getValue();
859 if (!consumerLayout.isAssigned())
862 dyn_cast<xegpu::DistributeLayoutAttr>(consumerLayout.get());
865 getNumSgOrFail(dpasMx, uArch->getSubgroupSize(), consumerLayoutAttr);
870 layoutKind, aTy, bTy, cdTy, aScaleTy, bScaleTy, consumerLayoutAttr,
871 numSgOrErr.value_or(0), uArch);
872 if (!layouts.has_value()) {
874 "Failed to determine required layouts for DPAS_MX operands.");
878 std::tie(requiredALayout, requiredBLayout, requiredCDLayoutAttr,
879 requiredAScaleLayout, requiredBScaleLayout) = *layouts;
881 dpasMx.setLayoutAAttr(requiredALayout);
882 dpasMx.setLayoutBAttr(requiredBLayout);
883 dpasMx.setLayoutCdAttr(requiredCDLayoutAttr);
884 if (requiredAScaleLayout)
885 dpasMx.setLayoutAScaleAttr(requiredAScaleLayout);
886 if (requiredBScaleLayout)
887 dpasMx.setLayoutBScaleAttr(requiredBScaleLayout);
889 dpasMxALayout = makeLayoutInfo(requiredALayout);
890 dpasMxBLayout = makeLayoutInfo(requiredBLayout);
891 dpasMxCDLayout = makeLayoutInfo(requiredCDLayoutAttr);
892 if (requiredAScaleLayout)
893 dpasMxAScaleLayout = makeLayoutInfo(requiredAScaleLayout);
894 if (requiredBScaleLayout)
895 dpasMxBScaleLayout = makeLayoutInfo(requiredBScaleLayout);
902 propagateIfChanged(operands[0], operands[0]->meet(dpasMxALayout));
903 propagateIfChanged(operands[1], operands[1]->meet(dpasMxBLayout));
905 if (dpasMx.getAcc()) {
906 propagateIfChanged(operands[idx], operands[idx]->meet(dpasMxCDLayout));
909 if (dpasMx.getScaleA()) {
910 if (dpasMxAScaleLayout.isAssigned())
911 propagateIfChanged(operands[idx],
912 operands[idx]->meet(dpasMxAScaleLayout));
915 if (dpasMx.getScaleB()) {
916 if (dpasMxBScaleLayout.isAssigned())
917 propagateIfChanged(operands[idx],
918 operands[idx]->meet(dpasMxBScaleLayout));
924void LayoutInfoPropagation::visitStoreNdOp(
925 xegpu::StoreNdOp store, ArrayRef<LayoutInfoLattice *> operands,
926 ArrayRef<const LayoutInfoLattice *> results) {
927 LayoutInfo storeLayout;
931 xegpu::DistributeLayoutAttr anchorLayout = store.getLayoutAttr();
932 if (hasParamsOfLayoutKind(anchorLayout)) {
933 storeLayout = makeLayoutInfo(anchorLayout);
934 if (layoutKind == xegpu::LayoutKind::InstData) {
936 const auto *uArchInstruction =
937 dyn_cast<xegpu::uArch::Subgroup2DBlockStoreInstruction>(
938 uArch->getInstruction(
939 xegpu::uArch::InstructionKind::Subgroup2DBlockStore));
940 if (!uArchInstruction)
943 anchorLayout, store.getValueType().getElementType(), uArchInstruction,
944 uArch->getSubgroupSize());
948 "Failed to identify lane layouts for the specified inst_data.");
951 store.setLayoutAttr(*completed);
952 storeLayout = makeLayoutInfo(*completed);
955 auto numSgOrErr = getNumSgOrFail(store, uArch->getSubgroupSize(),
nullptr);
960 layoutKind, store.getValueType(), numSgOrErr.value_or(0), uArch);
962 markFailure(store,
"Failed to determine required layout for store_nd.");
965 storeLayout = makeLayoutInfo(layoutAttr);
966 store.setLayoutAttr(layoutAttr);
970 for (LayoutInfoLattice *operand : operands)
971 propagateIfChanged(operand, operand->meet(storeLayout));
976void LayoutInfoPropagation::visitLoadNdOp(
977 xegpu::LoadNdOp
load, ArrayRef<LayoutInfoLattice *> operands,
978 ArrayRef<const LayoutInfoLattice *> results) {
979 LayoutInfo loadLayout;
984 LayoutInfo valueLayout = results[0]->getValue();
985 if (!valueLayout.isAssigned())
987 auto consumerLayoutAttr =
988 dyn_cast<xegpu::DistributeLayoutAttr>(valueLayout.get());
989 xegpu::DistributeLayoutAttr anchorLayout =
load.getLayoutAttr();
990 if (hasParamsOfLayoutKind(anchorLayout)) {
991 loadLayout = makeLayoutInfo(anchorLayout);
992 if (layoutKind == xegpu::LayoutKind::InstData &&
993 !consumerLayoutAttr.getEffectiveLaneLayoutAsInt().empty()) {
994 const auto *uArchInstruction =
995 dyn_cast<xegpu::uArch::Subgroup2DBlockLoadInstruction>(
996 uArch->getInstruction(
997 xegpu::uArch::InstructionKind::Subgroup2DBlockLoad));
998 if (!uArchInstruction)
1001 anchorLayout, consumerLayoutAttr,
load.getType().getElementType(),
1002 uArchInstruction, uArch->getSubgroupSize());
1005 "Failed to identify lane layouts for the specified inst_data.");
1008 load.setLayoutAttr(*completed);
1009 loadLayout = makeLayoutInfo(*completed);
1013 getNumSgOrFail(
load, uArch->getSubgroupSize(), consumerLayoutAttr);
1017 layoutKind,
load.getType(), consumerLayoutAttr, numSgOrErr.value_or(0),
1020 load.emitWarning(
"Failed to determine required layout for load_nd.");
1023 loadLayout = makeLayoutInfo(layoutAttr);
1024 load.setLayoutAttr(layoutAttr);
1027 propagateIfChanged(operands[0], operands[0]->meet(loadLayout));
1032void LayoutInfoPropagation::visitConvertLayoutOp(
1033 xegpu::ConvertLayoutOp convert, ArrayRef<LayoutInfoLattice *> operands,
1034 ArrayRef<const LayoutInfoLattice *> results) {
1036 LayoutInfo resultLayout = results[0]->getValue();
1039 auto targetLayoutAttr =
1040 dyn_cast<xegpu::LayoutAttr>(convert.getTargetLayoutAttr());
1042 auto inputLayoutAttr =
1043 dyn_cast_if_present<xegpu::LayoutAttr>(convert.getInputLayoutAttr());
1049 auto resultLayoutAttr = resultLayout.isAssigned()
1050 ? dyn_cast<xegpu::LayoutAttr>(resultLayout.get())
1052 if (resultLayoutAttr && targetLayoutAttr) {
1053 if (layoutKind == xegpu::LayoutKind::InstData &&
1054 !targetLayoutAttr.getLaneLayout()) {
1055 targetLayoutAttr = xegpu::LayoutAttr::get(
1056 convert.getContext(), targetLayoutAttr.getSgLayout(),
1057 targetLayoutAttr.getSgData(), targetLayoutAttr.getInstData(),
1058 resultLayoutAttr.getLaneLayout(), resultLayoutAttr.getLaneData(),
1059 resultLayoutAttr.getOrder());
1060 convert.setTargetLayoutAttr(targetLayoutAttr);
1067 if (inputLayoutAttr && targetLayoutAttr) {
1068 if (layoutKind == xegpu::LayoutKind::InstData &&
1069 !inputLayoutAttr.getLaneLayout()) {
1070 auto merged = xegpu::LayoutAttr::get(
1071 convert.getContext(), inputLayoutAttr.getSgLayout(),
1072 inputLayoutAttr.getSgData(), inputLayoutAttr.getInstData(),
1073 targetLayoutAttr.getLaneLayout(), targetLayoutAttr.getLaneData(),
1074 targetLayoutAttr.getOrder());
1075 convert.setInputLayoutAttr(merged);
1079 xegpu::DistributeLayoutAttr anchorLayout = convert.getEffectiveInputLayout();
1080 LayoutInfo convertLayout = makeLayoutInfo(anchorLayout);
1082 propagateIfChanged(operands[0], operands[0]->meet(convertLayout));
1087void LayoutInfoPropagation::visitTransposeOp(
1088 vector::TransposeOp transpose, ArrayRef<LayoutInfoLattice *> operands,
1089 ArrayRef<const LayoutInfoLattice *> results) {
1091 LayoutInfo resultLayout = results[0]->getValue();
1092 if (!resultLayout.isAssigned())
1095 auto consumerLayoutAttr =
1096 dyn_cast<xegpu::DistributeLayoutAttr>(resultLayout.get());
1098 consumerLayoutAttr, transpose.getPermutation());
1101 propagateIfChanged(operands[0],
1102 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1107void LayoutInfoPropagation::visitVectorBitcastOp(
1108 vector::BitCastOp bitcast, ArrayRef<LayoutInfoLattice *> operands,
1109 ArrayRef<const LayoutInfoLattice *> results) {
1111 LayoutInfo resLayoutInfo = results[0]->getValue();
1112 if (!resLayoutInfo.isAssigned())
1115 auto srcVecType = bitcast.getSourceVectorType();
1116 auto resVecType = bitcast.getResultVectorType();
1118 auto consumerLayoutAttr =
1119 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1125 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1129 int inElemTyBitWidth = srcVecType.getElementType().getIntOrFloatBitWidth();
1130 int outElemTyBitWidth = resVecType.getElementType().getIntOrFloatBitWidth();
1134 requiredResLayoutAttr, outElemTyBitWidth, inElemTyBitWidth);
1136 propagateIfChanged(operands[0],
1137 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1143void LayoutInfoPropagation::visitVectorInterleaveOp(
1144 vector::InterleaveOp interleave, ArrayRef<LayoutInfoLattice *> operands,
1145 ArrayRef<const LayoutInfoLattice *> results) {
1147 LayoutInfo resLayoutInfo = results[0]->getValue();
1148 if (!resLayoutInfo.isAssigned())
1151 auto srcVecType = interleave.getSourceVectorType();
1152 auto resVecType = interleave.getResultVectorType();
1154 auto consumerLayoutAttr =
1155 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1163 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1168 auto srcLayoutAttr =
1172 propagateIfChanged(operands[0],
1173 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1174 propagateIfChanged(operands[1],
1175 operands[1]->meet(makeLayoutInfo(srcLayoutAttr)));
1181void LayoutInfoPropagation::visitVectorDeinterleaveOp(
1182 vector::DeinterleaveOp deinterleave, ArrayRef<LayoutInfoLattice *> operands,
1183 ArrayRef<const LayoutInfoLattice *> results) {
1186 LayoutInfo resLayoutInfo = results[0]->getValue();
1187 if (!resLayoutInfo.isAssigned())
1190 auto consumerLayoutAttr =
1191 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1197 propagateIfChanged(operands[0],
1198 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1201void LayoutInfoPropagation::visitInsertStridedSliceOp(
1202 vector::InsertStridedSliceOp insertStridedSlice,
1203 ArrayRef<LayoutInfoLattice *> operands,
1204 ArrayRef<const LayoutInfoLattice *> results) {
1206 LayoutInfo resLayoutInfo = results[0]->getValue();
1207 if (!resLayoutInfo.isAssigned())
1210 auto srcVecType = insertStridedSlice.getSourceVectorType();
1211 auto resVecType = insertStridedSlice.getDestVectorType();
1213 auto consumerLayoutAttr =
1214 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1221 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1223 requiredResLayoutAttr);
1226 requiredResLayoutAttr, resVecType.getShape(), srcVecType.getShape());
1227 propagateIfChanged(operands[0],
1228 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1229 propagateIfChanged(operands[1],
1230 operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
1235void LayoutInfoPropagation::visitLoadGatherOp(
1236 xegpu::LoadGatherOp
load, ArrayRef<LayoutInfoLattice *> operands,
1237 ArrayRef<const LayoutInfoLattice *> results) {
1238 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1239 xegpu::DistributeLayoutAttr anchorLayoutAttr =
load.getLayoutAttr();
1243 VectorType resVecTy =
load.getValueType();
1244 int chunkSize =
load.getChunkSize().value_or(1);
1246 LayoutInfo resLayoutInfo = results[0]->getValue();
1247 if (!resLayoutInfo.isAssigned())
1249 auto consumerLayoutAttr =
1250 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1252 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1253 requiredAnchorLayoutAttr = anchorLayoutAttr;
1254 if (layoutKind == xegpu::LayoutKind::InstData &&
1255 !consumerLayoutAttr.getEffectiveLaneLayoutAsInt().empty()) {
1256 const auto uArchInstruction =
1257 dyn_cast<xegpu::uArch::LoadGatherInstruction>(
1258 uArch->getInstruction(xegpu::uArch::InstructionKind::LoadGather));
1259 if (!uArchInstruction)
1262 anchorLayoutAttr, consumerLayoutAttr, resVecTy.getElementType(),
1263 uArchInstruction, uArch->getSubgroupSize());
1266 "Failed to identify lane layouts for the specified inst_data.");
1269 requiredAnchorLayoutAttr = *completed;
1270 load.setLayoutAttr(requiredAnchorLayoutAttr);
1274 load.emitWarning(
"Not propagating, non-vector payload supplied.");
1278 layoutKind, resVecTy, chunkSize, consumerLayoutAttr, uArch);
1279 load.setLayoutAttr(requiredAnchorLayoutAttr);
1282 assert((chunkSize <= 1) || (layoutKind != xegpu::LayoutKind::Subgroup));
1284 requiredAnchorLayoutAttr, chunkSize);
1285 LayoutInfo maskLayoutInfo = makeLayoutInfo(maskLayoutAttr);
1286 auto loadLayoutInfo = makeLayoutInfo(requiredAnchorLayoutAttr);
1289 if (isa<xegpu::TensorDescType>(
load.getSourceType()))
1290 propagateIfChanged(operands[0], operands[0]->meet(loadLayoutInfo));
1292 propagateIfChanged(operands[1], operands[1]->meet(maskLayoutInfo));
1293 propagateIfChanged(operands[2], operands[2]->meet(maskLayoutInfo));
1298void LayoutInfoPropagation::visitStoreScatterOp(
1299 xegpu::StoreScatterOp storeScatter, ArrayRef<LayoutInfoLattice *> operands,
1300 ArrayRef<const LayoutInfoLattice *> results) {
1302 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1303 xegpu::DistributeLayoutAttr anchorLayoutAttr = storeScatter.getLayoutAttr();
1308 VectorType srcVecTy = storeScatter.getValueType();
1309 int chunkSize = storeScatter.getChunkSize().value_or(1);
1311 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1312 requiredAnchorLayoutAttr = anchorLayoutAttr;
1313 if (layoutKind == xegpu::LayoutKind::InstData) {
1314 const auto uArchInstruction =
1315 dyn_cast<xegpu::uArch::StoreScatterInstruction>(uArch->getInstruction(
1316 xegpu::uArch::InstructionKind::StoreScatter));
1317 if (!uArchInstruction)
1320 anchorLayoutAttr, srcVecTy.getElementType(), uArchInstruction,
1321 uArch->getSubgroupSize());
1325 "Failed to identify lane layouts for the specified inst_data.");
1328 requiredAnchorLayoutAttr = *completed;
1329 storeScatter.setLayoutAttr(requiredAnchorLayoutAttr);
1333 storeScatter.emitWarning(
"Not propagating, non-vector payload supplied.");
1337 getNumSgOrFail(storeScatter, uArch->getSubgroupSize(),
nullptr);
1341 layoutKind, srcVecTy, chunkSize, numSgOrErr.value_or(0), uArch);
1342 if (!requiredAnchorLayoutAttr) {
1343 markFailure(storeScatter,
1344 "Failed to determine required layout for store scatter.");
1347 storeScatter.setLayoutAttr(requiredAnchorLayoutAttr);
1350 LayoutInfo srcLayoutInfo = makeLayoutInfo(requiredAnchorLayoutAttr);
1351 assert((chunkSize <= 1) || (layoutKind != xegpu::LayoutKind::Subgroup));
1353 requiredAnchorLayoutAttr, chunkSize);
1354 LayoutInfo maskLayoutInfo = makeLayoutInfo(maskLayoutAttr);
1357 propagateIfChanged(operands[0], operands[0]->meet(srcLayoutInfo));
1359 if (isa<xegpu::TensorDescType>(storeScatter.getDestType()))
1360 propagateIfChanged(operands[1], operands[1]->meet(srcLayoutInfo));
1362 propagateIfChanged(operands[2], operands[2]->meet(maskLayoutInfo));
1363 propagateIfChanged(operands[3], operands[3]->meet(maskLayoutInfo));
1366void LayoutInfoPropagation::visitLoadMatrixOp(
1367 xegpu::LoadMatrixOp loadMatrixOp, ArrayRef<LayoutInfoLattice *> operands,
1368 ArrayRef<const LayoutInfoLattice *> results) {
1370 LayoutInfo resLayoutInfo = results[0]->getValue();
1371 if (!resLayoutInfo.isAssigned())
1374 auto consumerLayoutAttr =
1375 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1377 xegpu::DistributeLayoutAttr anchorLayout = loadMatrixOp.getLayoutAttr();
1381 if (!hasParamsOfLayoutKind(anchorLayout)) {
1382 VectorType resVecTy =
1383 llvm::cast<VectorType>(loadMatrixOp.getRes().getType());
1391 layoutKind, resVecTy, chunkSize, consumerLayoutAttr, uArch);
1392 loadMatrixOp.setLayoutAttr(requiredAnchorLayoutAttr);
1396void LayoutInfoPropagation::visitStoreMatrixOp(
1397 xegpu::StoreMatrixOp storeMatrix, ArrayRef<LayoutInfoLattice *> operands,
1398 ArrayRef<const LayoutInfoLattice *> results) {
1399 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1400 xegpu::DistributeLayoutAttr anchorLayoutAttr = storeMatrix.getLayoutAttr();
1402 VectorType srcVecTy = llvm::cast<VectorType>(storeMatrix.getData().getType());
1407 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1408 requiredAnchorLayoutAttr = anchorLayoutAttr;
1409 if (layoutKind == xegpu::LayoutKind::InstData) {
1410 const auto uArchInstruction =
1411 dyn_cast<xegpu::uArch::StoreScatterInstruction>(uArch->getInstruction(
1412 xegpu::uArch::InstructionKind::StoreScatter));
1413 if (!uArchInstruction)
1416 anchorLayoutAttr, srcVecTy.getElementType(), uArchInstruction,
1417 uArch->getSubgroupSize());
1421 "Failed to identify lane layouts for the specified inst_data.");
1424 requiredAnchorLayoutAttr = *completed;
1425 storeMatrix.setLayoutAttr(requiredAnchorLayoutAttr);
1431 getNumSgOrFail(storeMatrix, uArch->getSubgroupSize(),
nullptr);
1435 layoutKind, srcVecTy, chunkSize, numSgOrErr.value_or(0), uArch);
1436 if (!requiredAnchorLayoutAttr) {
1437 markFailure(storeMatrix,
1438 "Failed to determine required layout for store matrix.");
1441 storeMatrix.setLayoutAttr(requiredAnchorLayoutAttr);
1443 layout = makeLayoutInfo(requiredAnchorLayoutAttr);
1444 propagateIfChanged(operands[0], operands[0]->meet(layout));
1453class RunLayoutInfoPropagation {
1458 unsigned indexBitWidth)
1460 SymbolTableCollection symbolTable;
1462 analysis = solver.
load<LayoutInfoPropagation>(symbolTable, layoutKind,
1467 LayoutInfo getLayoutInfo(Value val);
1469 void printAnalysisResult(llvm::raw_ostream &os);
1471 bool hasFailed()
const {
return analysis && analysis->hasFailed(); }
1474 DataFlowSolver solver;
1476 LayoutInfoPropagation *analysis =
nullptr;
1480LayoutInfo RunLayoutInfoPropagation::getLayoutInfo(Value val) {
1481 auto *state = solver.
lookupState<LayoutInfoLattice>(val);
1484 return state->getValue();
1488void RunLayoutInfoPropagation::printAnalysisResult(llvm::raw_ostream &os) {
1489 auto printFunctionResult = [&](FunctionOpInterface funcOp) {
1490 os <<
"function: " << funcOp.getName() <<
":\n";
1492 for (BlockArgument arg : funcOp.getArguments()) {
1493 LayoutInfo layout = getLayoutInfo(arg);
1494 os <<
"argument: " << arg <<
"\n";
1500 funcOp.walk([&](Operation *op) {
1506 if (isa<BranchOpInterface>(op) || isa<RegionBranchOpInterface>(op))
1512 for (
auto [i, r] : llvm::enumerate(op->
getResults())) {
1513 LayoutInfo layout = getLayoutInfo(r);
1514 os <<
"layout for result #" << i <<
": ";
1521 SmallVector<FunctionOpInterface> funcOps;
1522 if (
auto modOp = dyn_cast<ModuleOp>(
target)) {
1523 for (
auto funcOp : modOp.getOps<FunctionOpInterface>())
1524 funcOps.push_back(funcOp);
1527 for (
auto gpuModOp : modOp.getOps<gpu::GPUModuleOp>()) {
1528 for (
auto gpuFuncOp : gpuModOp.getOps<FunctionOpInterface>())
1529 funcOps.push_back(gpuFuncOp);
1533 for (FunctionOpInterface funcOp : funcOps)
1534 printFunctionResult(funcOp);
1546static xegpu::CreateNdDescOp getDefiningCreateNdDescOp(Value tdescValue) {
1548 auto definingOp = tdescValue.
getDefiningOp<xegpu::CreateNdDescOp>();
1553 if (
auto arg = dyn_cast<BlockArgument>(tdescValue)) {
1554 auto *parentOp = arg.getOwner()->getParentOp();
1555 if (
auto loop = dyn_cast<LoopLikeOpInterface>(parentOp)) {
1556 OpOperand *tiedInit = loop.getTiedLoopInit(arg);
1558 return getDefiningCreateNdDescOp(tiedInit->
get());
1565struct ResolveLayoutConflicts {
1566 ResolveLayoutConflicts(Operation *parentOp)
1567 : parentOp(parentOp), builder(parentOp->
getContext()) {}
1568 LogicalResult run();
1571 Operation *parentOp;
1573 LogicalResult resolveTensorDescConsumer(OpOperand &operand);
1574 LogicalResult resolveVectorConsumer(OpOperand &operand);
1575 LogicalResult assignResultLayout(OpResult &
result);
1580LogicalResult ResolveLayoutConflicts::run() {
1583 auto r = parentOp->
walk([&](Operation *op) -> WalkResult {
1588 if (
result.getType().isIntOrFloat() &&
1589 (isa<vector::MultiDimReductionOp>(op) ||
1590 isa<vector::ReductionOp>(op))) {
1591 auto res = assignResultLayout(
result);
1593 DBGS() <<
"Failed to assign layout for scalar consumer of reduction "
1601 if (isa<VectorType>(
result.getType()) &&
result.use_empty() &&
1602 isa<RegionBranchOpInterface>(op)) {
1603 auto res = assignResultLayout(
result);
1605 DBGS() <<
"Failed to assign layout for vector consumer of region op "
1613 Type operandType = operand.get().getType();
1614 if (isa<xegpu::AnchorLayoutInterface>(op) &&
1615 isa<xegpu::TensorDescType>(operandType)) {
1616 auto res = resolveTensorDescConsumer(operand);
1618 DBGS() <<
"Failed to resolve tensor descriptor consumer: " << *op
1624 if (isa<VectorType>(operandType)) {
1625 auto res = resolveVectorConsumer(operand);
1627 DBGS() <<
"Failed to resolve vector consumer: " << *op <<
"\n";
1636 DBGS() <<
"IR after resolving layout conflicts:\n";
1640 return r.wasInterrupted() ? failure() :
success();
1643LogicalResult ResolveLayoutConflicts::assignResultLayout(OpResult &
result) {
1644 Operation *producerOp =
result.getDefiningOp();
1648 auto convertOp = xegpu::ConvertLayoutOp::create(
1651 result.replaceAllUsesExcept(convertOp.getResult(), convertOp);
1656ResolveLayoutConflicts::resolveVectorConsumer(OpOperand &operand) {
1657 Value vectorValue = operand.
get();
1658 Operation *consumerOp = operand.
getOwner();
1661 if (!producerLayout) {
1662 if (
auto vectorTy = dyn_cast<VectorType>(vectorValue.
getType());
1663 vectorTy && vectorTy.getRank() > 1)
1664 consumerOp->
emitWarning(
"Expected layout for non-1D vectors.");
1672 if (!consumerLayout) {
1673 if (isa<func::ReturnOp>(consumerOp) || isa<gpu::ReturnOp>(consumerOp))
1676 "No consumer layout found for vector operand.");
1680 if (consumerLayout.isEqualTo(producerLayout))
1686 if (
auto consumerConvert = dyn_cast<xegpu::ConvertLayoutOp>(consumerOp)) {
1687 consumerConvert.setInputLayoutAttr(producerLayout);
1693 if (
auto producerConvert =
1695 producerConvert && vectorValue.
hasOneUse()) {
1698 producerConvert.setInputLayoutAttr(
1699 producerConvert.getEffectiveInputLayout());
1700 producerConvert.setTargetLayoutAttr(consumerLayout);
1712 isa<OpResult>(vectorValue) &&
1715 Operation *
clone = builder.
clone(*producerOp);
1720 operand.
set(cloneResult);
1726 auto convertOp = xegpu::ConvertLayoutOp::create(
1727 builder, consumerOp->
getLoc(), vectorValue.
getType(), vectorValue,
1728 producerLayout, consumerLayout);
1731 operand.
set(convertOp.getResult());
1736ResolveLayoutConflicts::resolveTensorDescConsumer(OpOperand &operand) {
1737 Operation *consumerOp = operand.
getOwner();
1738 Value tdescValue = operand.
get();
1739 auto anchorOp = dyn_cast<xegpu::AnchorLayoutInterface>(consumerOp);
1740 auto currTDescType = dyn_cast<xegpu::TensorDescType>(tdescValue.
getType());
1741 assert(anchorOp && currTDescType &&
1742 "Expected anchor layout op and tensor descriptor consumer.");
1743 Attribute currLayout = currTDescType.getLayout();
1744 Attribute expectedLayout = anchorOp.getAnchorLayout();
1747 if (expectedLayout && currLayout && expectedLayout != currLayout) {
1749 auto conflictingCreateNdOp = getDefiningCreateNdDescOp(tdescValue);
1750 if (!conflictingCreateNdOp) {
1751 DBGS() <<
"Unable to find defining CreateNdDescOp for tensor descriptor: "
1752 << tdescValue <<
"\n";
1757 auto newTensorDescType = xegpu::TensorDescType::get(
1758 conflictingCreateNdOp.getContext(), currTDescType.getShape(),
1759 currTDescType.getElementType(), currTDescType.getEncoding(),
1761 auto newOp = xegpu::CreateNdDescOp::create(
1763 conflictingCreateNdOp->getOperands(),
1764 conflictingCreateNdOp.getProperties(),
1765 conflictingCreateNdOp->getDiscardableAttrDictionary().getValue());
1788 if (!isa<VectorType, xegpu::TensorDescType>(resultType))
1791 xegpu::DistributeLayoutAttr layout = getLayoutOfValue(
result);
1796 bool anyAssigned =
false;
1799 srcLayouts.push_back(srclayout);
1800 anyAssigned |= (srclayout !=
nullptr);
1807 if (!layout &&
result.getNumUses() > 0) {
1808 op->
emitWarning(
"op has users but no layout assigned for its result");
1812 if (
auto tensorDescTy = dyn_cast<xegpu::TensorDescType>(resultType)) {
1813 auto typeWithLayout = xegpu::TensorDescType::get(
1814 tensorDescTy.getContext(), tensorDescTy.getShape(),
1815 tensorDescTy.getElementType(), tensorDescTy.getEncoding(), layout);
1816 result.setType(typeWithLayout);
1828 mlir::FunctionOpInterface funcOp,
1834 if (!isa<FunctionType>(funcOp.getFunctionType()))
1839 Type argType = arg.getType();
1840 newArgTypes.push_back(argType);
1841 if (!isa<VectorType, xegpu::TensorDescType>(argType))
1843 xegpu::DistributeLayoutAttr layout = getLayoutOfValue(arg);
1845 LLVM_DEBUG(
DBGS() <<
"Expecting layout for function argument: " << arg
1846 <<
" but got none.\n");
1849 if (
auto tensorDescTy = dyn_cast<xegpu::TensorDescType>(argType)) {
1850 auto newTdescTy = xegpu::TensorDescType::get(
1851 tensorDescTy.getContext(), tensorDescTy.getShape(),
1852 tensorDescTy.getElementType(), tensorDescTy.getEncoding(), layout);
1853 arg.setType(newTdescTy);
1854 newArgTypes.back() = newTdescTy;
1859 funcOp.setType(FunctionType::get(funcOp.getContext(), newArgTypes,
1860 funcOp.getResultTypes()));
1865struct XeGPUPropagateLayoutPass final
1866 :
public xegpu::impl::XeGPUPropagateLayoutBase<XeGPUPropagateLayoutPass> {
1867 XeGPUPropagateLayoutPass() =
default;
1868 XeGPUPropagateLayoutPass(
const XeGPUPropagateLayoutPass &other) =
default;
1869 XeGPUPropagateLayoutPass(xegpu::XeGPUPropagateLayoutOptions
options)
1870 : XeGPUPropagateLayoutBase(std::move(
options)) {}
1871 void runOnOperation()
override;
1878 unsigned indexBitWidth,
bool printOnly) {
1879 RunLayoutInfoPropagation analysis(
target, layoutKind, indexBitWidth);
1882 auto &os = llvm::outs();
1883 analysis.printAnalysisResult(os);
1888 if (analysis.hasFailed())
1891 auto getLayoutFromPropagation =
1892 [&](
Value val) -> xegpu::DistributeLayoutAttr {
1893 LayoutInfo layout = analysis.getLayoutInfo(val);
1894 if (
auto opResult = dyn_cast<OpResult>(val)) {
1895 Operation *defOp = opResult.getDefiningOp();
1896 if (
auto anchorOp = dyn_cast<xegpu::AnchorLayoutInterface>(defOp)) {
1897 auto anchorLayout = anchorOp.getAnchorLayout();
1898 if (anchorLayout !=
nullptr)
1899 return anchorLayout;
1901 xegpu::DistributeLayoutAttr requiredResLayoutAttr =
1903 if (requiredResLayoutAttr !=
nullptr)
1904 return requiredResLayoutAttr;
1906 if (!layout.isAssigned())
1908 xegpu::DistributeLayoutAttr layoutAttr =
1909 cast<xegpu::DistributeLayoutAttr>(layout.get());
1910 if (layout.isSliceLayout())
1911 return cast<xegpu::SliceAttr>(layoutAttr);
1913 return cast<xegpu::LayoutAttr>(layoutAttr);
1921 .Case([&](mlir::RegionBranchTerminatorOpInterface branchTermOp) {
1923 branchTermOp, getLayoutFromPropagation);
1925 .Case([&](mlir::RegionBranchOpInterface branchOp) {
1927 getLayoutFromPropagation);
1929 .Case([&](mlir::FunctionOpInterface funcOp) {
1931 getLayoutFromPropagation);
1937 op.
emitError(
"Failed to update operation with the layout.");
1943 if (walkResult.wasInterrupted())
1950 ResolveLayoutConflicts resolver(
target);
1951 return resolver.run();
1954void XeGPUPropagateLayoutPass::runOnOperation() {
1959 if (this->layoutKind ==
"lane") {
1961 }
else if (this->layoutKind ==
"inst") {
1963 }
else if (this->layoutKind ==
"subgroup") {
1964 layoutKind = xegpu::LayoutKind::Subgroup;
1966 getOperation()->emitError(
"Unsupported layout kind option: " +
1968 signalPassFailure();
1973 this->indexBitWidth, this->printOnly))) {
1974 signalPassFailure();
1979 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)
function_ref< xegpu::DistributeLayoutAttr(Value)> GetLayoutFnTy
static LogicalResult updateOpWithForwardFill(mlir::OpBuilder &builder, mlir::Operation *op, GetLayoutFnTy getLayoutOfValue)
Update an operation with the layout of its results.
static LogicalResult updateFunctionOpInterface(mlir::OpBuilder &builder, mlir::FunctionOpInterface funcOp, GetLayoutFnTy getLayoutOfValue)
Update the function arguments and results with the layouts.
Attributes are known-constant values of operations.
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()
unsigned getNumOperands()
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={})
operand_range getOperands()
Returns an iterator on the underlying Value's.
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 successor of a region.
This class represents a collection of SymbolTables.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
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 inferResultLayoutFromSourceForNonAnchorOp(Operation *op, ArrayRef< DistributeLayoutAttr > operandLayouts)
Infers the result layout attribute for a non-anchor operation from the layouts of its source operands...
DistributeLayoutAttr setupLoadMatrixAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Sets up the anchor layout for load matrix operation.
DistributeLayoutAttr setupInterleaveResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, VectorType resVectorTy, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Sets up the result layout for an interleave operation to ensure the source layout can be safely deriv...
DistributeLayoutAttr inferTransposeSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > permutation)
Infers the source layout attribute for a transpose operation given the result layout attribute and pe...
std::optional< std::tuple< DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr > > completeDpasMxLaneLayoutFromInstData(DistributeLayoutAttr aLayout, DistributeLayoutAttr bLayout, DistributeLayoutAttr cdLayout, VectorType aTy, VectorType bTy, VectorType cdTy, VectorType aScaleTy, VectorType bScaleTy, const uArch::uArch *uArch)
Like completeDpasLaneLayoutFromInstData, but for dpas_mx: additionally re-derives the A_scale / B_sca...
DistributeLayoutAttr inferInsertStridedSliceSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > resShape, ArrayRef< int64_t > srcShape)
Infers the source layout attribute for an insert strided slice operation given the result layout attr...
DistributeLayoutAttr setupStoreMatrixAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a store matrix operation.
void removeTemporaryLayoutAttrs(Operation *op)
Removes the temporary layout attributes for each OpOperand and OpResult of the given operation.
std::optional< std::tuple< DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr > > completeDpasLaneLayoutFromInstData(DistributeLayoutAttr aLayout, DistributeLayoutAttr bLayout, DistributeLayoutAttr cdLayout, VectorType aTy, VectorType bTy, VectorType cdTy, const uArch::uArch *uArch)
Completes user-provided DPAS A/B/C-D anchors that carry only inst_data by filling in lane_layout / la...
void setTemporaryLayout(const T &operandOrResult, const DistributeLayoutAttr layout)
LayoutKind
Specifies the level of a layout hierarchy for comparison or propagation.
void setDistributeLayoutAttr(const OpResult &Result, const DistributeLayoutAttr layout)
[to-be-deprecated] Sets the DistributeLayoutAttr for a given OpResult user should use setAnchorLayout...
DistributeLayoutAttr inferInterleaveSourceLayout(DistributeLayoutAttr resLayout)
Infers the source layout attribute for an interleave operation given the result layout attribute.
DistributeLayoutAttr inferBroadcastSourceLayout(DistributeLayoutAttr resLayout, ArrayRef< int64_t > resShape, ArrayRef< int64_t > srcShape)
Infers the source layout attribute for a broadcast operation given the result layout attribute,...
std::optional< std::tuple< DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr > > setupDpasMxLayout(LayoutKind layoutKind, VectorType aTy, VectorType bTy, VectorType cdTy, VectorType aScaleTy, VectorType bScaleTy, DistributeLayoutAttr consumerLayout, int numSg, const uArch::uArch *uArch)
Sets up the anchor layouts for dpas_mx operands (A, B, C/D, A_scale, and B_scale).
SliceAttr setupMultiReductionResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, DistributeLayoutAttr consumerLayout, SmallVector< int64_t > reductionDims, int numSg, const uArch::uArch *uArch)
Note on the consumerLayout argument used by the consumer-driven setup* / complete* helpers below:
DistributeLayoutAttr setupLoadGatherAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Sets up the anchor layout for a load gather operation.
std::optional< DistributeLayoutAttr > completeScatterLoadLaneLayoutFromInstData(DistributeLayoutAttr userSpecifiedLayout, DistributeLayoutAttr consumerLayout, Type elemTy, const xegpu::uArch::LoadGatherInstruction *uArchInstruction, const int subgroupSize)
If the consumer layout has only inst_data (no lane_layout/lane_data), completes it by running the cor...
DistributeLayoutAttr setupStoreScatterAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int contigChunkSize, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a store scatter operation.
DistributeLayoutAttr setupBitCastResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, VectorType resVectorTy, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Setup the result layout attribute for a bitcast operation based on element type bitwidths.
void removeLayoutAttr(const T &operandOrResult)
Removes the LayoutAttr for a given OpOperand or OpResult if it exists.
DistributeLayoutAttr inferMaskOffsetLayoutForScatterIO(DistributeLayoutAttr payloadLayout, int chunkSize)
Infers the layout attribute for mask and offset operand for Chunked load and store,...
DistributeLayoutAttr getDistributeLayoutAttr(const Value value)
Retrieves the DistributeLayoutAttr associated with a given Value, or nullptr if none is found.
DistributeLayoutAttr setupPrefetchNdAnchorLayout(LayoutKind layoutKind, TensorDescType tdescTy, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a prefetch_nd operation.
LogicalResult propagateYieldOperandsToRegionResults(RegionBranchTerminatorOpInterface terminator, GetLayoutFnTy getLayoutOfValue)
Propagate layouts from a region branch terminator's forwarded operands to the matching region results...
LogicalResult resolveLayoutConflicts(Operation *target)
DistributeLayoutAttr inferBitCastSourceLayout(DistributeLayoutAttr resLayout, int resElemTyBitWidth, int srcElemTyBitWidth)
Infers the source layout attribute for a bitcast operation given the result layout attribute,...
DistributeLayoutAttr setupInsertStridedSliceResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, VectorType resVectorTy, DistributeLayoutAttr consumerLayout, const uArch::uArch *uArch)
Sets up the result layout for an insert strided slice operation.
std::optional< std::string > getChipStr(Operation *op)
Retrieves the chip string from the XeVM target attribute of the parent GPU module operation.
DistributeLayoutAttr inferReductionSourceLayout(DistributeLayoutAttr resLayout)
Infers the source layout attribute for a reduction operation given the result layout attribute and re...
std::optional< DistributeLayoutAttr > completeScatterStoreLaneLayoutFromInstData(DistributeLayoutAttr specifiedLayout, Type elemTy, const xegpu::uArch::StoreScatterInstruction *uArchInstruction, const int subgroupSize)
Like completeScatterLoadLaneLayoutFromInstData, but for scatter stores (store_scatter / store_matrix)...
DistributeLayoutAttr getTemporaryLayout(const T &operandOrResult)
get and set distribute layout attribute for non-anchor operations (and offsets/masks of load/store op...
std::optional< DistributeLayoutAttr > completeBlockStoreLaneLayoutFromInstData(DistributeLayoutAttr specifiedLayout, Type elemTy, const xegpu::uArch::BlockIOInstructionInterface *uArchInstruction, const int subgroupSize)
Completes a user-provided 2D-block store_nd / prefetch_nd anchor that has only inst_data.
DistributeLayoutAttr inferDeinterleaveSourceLayout(DistributeLayoutAttr resLayout)
Infers the source layout attribute for a deinterleave operation given the result layout attribute.
DistributeLayoutAttr getConsumerLayoutAt(OpOperand &operand)
Gets the expected layout for a given consumer operand.
DistributeLayoutAttr inferMultiReductionSourceLayout(DistributeLayoutAttr resLayout, SmallVector< int64_t > reduceDims)
Infers the source layout attribute for a reduction operation given the result layout attribute and re...
bool isTriviallyRematerializable(Operation *op)
Returns true if op is safe and cheap to clone: it has no side effects, no regions,...
LogicalResult propagateLayouts(OpBuilder &builder, Operation *target, LayoutKind layoutKind, unsigned indexBitWidth, bool printOnly=false)
DistributeLayoutAttr setupStoreNdAnchorLayout(LayoutKind layoutKind, VectorType vectorTy, int numSg, const uArch::uArch *uArch)
Sets up the anchor layout for a store_nd operation.
std::optional< DistributeLayoutAttr > completeBlockLoadLaneLayoutFromInstData(DistributeLayoutAttr specifiedLayout, DistributeLayoutAttr consumerLayout, Type elemTy, const xegpu::uArch::BlockIOInstructionInterface *uArchInstruction, const int subgroupSize)
Like completeBlockStoreLaneLayoutFromInstData, but for load_nd.
LogicalResult propagateRegionArgsToInits(RegionBranchOpInterface regionOp, GetLayoutFnTy getLayoutOfValue)
Propagate layouts from a region branch op's region entry block arguments back to its init operands.
std::optional< std::tuple< DistributeLayoutAttr, DistributeLayoutAttr, DistributeLayoutAttr > > setupDpasLayout(LayoutKind layoutKind, VectorType aTy, VectorType bTy, VectorType cdTy, DistributeLayoutAttr consumerLayout, int numSg, const uArch::uArch *uArch)
Sets up the anchor layouts for a dpas operands (A, B, and C/D).
SliceAttr setupReductionResultLayout(LayoutKind layoutKind, VectorType srcVectorTy, const uArch::uArch *uArch)
Sets up layout for Reduction operations by creating a SliceAttr for the result.
Include the generated interface declarations.
bool operator==(StringAttr lhs, std::nullptr_t)
Define comparisons for StringAttr against nullptr and itself to avoid the StringRef overloads from be...
llvm::TypeSwitch< T, ResultT > TypeSwitch
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
llvm::function_ref< Fn > function_ref