32#include "llvm/ADT/ArrayRef.h"
33#include "llvm/ADT/DenseMap.h"
34#include "llvm/ADT/STLExtras.h"
35#include "llvm/ADT/SmallSet.h"
36#include "llvm/ADT/SmallVector.h"
37#include "llvm/ADT/TypeSwitch.h"
38#include "llvm/Support/Casting.h"
39#include "llvm/Support/Debug.h"
40#include "llvm/Support/LogicalResult.h"
41#include "llvm/Support/MathExtras.h"
42#include "llvm/Support/raw_ostream.h"
47#define GEN_PASS_DEF_XEGPUPROPAGATELAYOUT
48#include "mlir/Dialect/XeGPU/Transforms/Passes.h.inc"
52#define DEBUG_TYPE "xegpu-propagate-layout"
53#define DBGS() (llvm::dbgs() << "[" DEBUG_TYPE "]: ")
97 xegpu::DistributeLayoutAttr storage =
nullptr;
100 int64_t programOrder = std::numeric_limits<int64_t>::max();
103 LayoutInfo() =
default;
104 LayoutInfo(
const xegpu::DistributeLayoutAttr &layout,
int64_t programOrder)
105 : storage(layout), programOrder(programOrder) {}
111 bool operator==(
const LayoutInfo &other)
const {
112 if (isAssigned() != other.isAssigned())
116 return storage.isEqualTo(other.storage);
119 static LayoutInfo meet(
const LayoutInfo &
lhs,
const LayoutInfo &
rhs);
121 static LayoutInfo
join(
const LayoutInfo &
lhs,
const LayoutInfo &
rhs);
125 bool isAssigned()
const {
return storage !=
nullptr; }
139 bool isSliceLayout()
const {
142 return isa<xegpu::SliceAttr>(storage);
148 return storage.getRank();
152 void set(
const xegpu::DistributeLayoutAttr &layout) { storage = layout; }
159 os <<
"Not assigned.";
163LayoutInfo LayoutInfo::meet(
const LayoutInfo &
lhs,
const LayoutInfo &
rhs) {
164 if (!
lhs.isAssigned())
166 if (!
rhs.isAssigned())
171 if (
rhs.programOrder <
lhs.programOrder)
177LayoutInfo LayoutInfo::join(
const LayoutInfo &
lhs,
const LayoutInfo &
rhs) {
178 llvm_unreachable(
"Join should not be triggered by layout propagation.");
186struct LayoutInfoLattice :
public Lattice<LayoutInfo> {
188 using Lattice::Lattice;
200class LayoutInfoPropagation
207 unsigned indexBitWidth;
217 int64_t currentProgramOrder = std::numeric_limits<int64_t>::max();
218 LayoutInfo makeLayoutInfo(
const xegpu::DistributeLayoutAttr &layout) {
219 return LayoutInfo(layout, currentProgramOrder);
225 void visitDpasMxOp(xegpu::DpasMxOp dpasMx,
229 void visitStoreNdOp(xegpu::StoreNdOp store,
233 void visitStoreScatterOp(xegpu::StoreScatterOp storeScatter,
237 void visitLoadNdOp(xegpu::LoadNdOp
load,
241 void visitLoadGatherOp(xegpu::LoadGatherOp
load,
245 void visitTransposeOp(vector::TransposeOp transpose,
249 void visitVectorBitcastOp(vector::BitCastOp bitcast,
253 void visitVectorInterleaveOp(vector::InterleaveOp interleave,
257 void visitVectorDeinterleaveOp(vector::DeinterleaveOp deinterleave,
261 void visitPrefetchNdOp(xegpu::PrefetchNdOp prefetch,
265 void visitVectorMultiReductionOp(vector::MultiDimReductionOp reduction,
269 void visitVectorReductionOp(vector::ReductionOp reduction,
273 void visitVectorBroadCastOp(vector::BroadcastOp
broadcast,
276 void visitShapeCastOp(vector::ShapeCastOp shapeCast,
280 visitInsertStridedSliceOp(vector::InsertStridedSliceOp insertStridedSlice,
284 void visitLoadMatrixOp(xegpu::LoadMatrixOp
load,
288 void visitStoreMatrixOp(xegpu::StoreMatrixOp store,
292 void visitLoadGatherOp(xegpu::LoadMatrixOp
load,
296 void visitStoreScatterOp(xegpu::StoreMatrixOp store,
300 void visitConvertLayoutOp(xegpu::ConvertLayoutOp convertLayout,
304 bool hasParamsOfLayoutKind(xegpu::DistributeLayoutAttr anchorLayout);
307 FailureOr<int64_t> getNumSgOrFail(
Operation *op,
int sgSize,
308 xegpu::DistributeLayoutAttr consumerLayout);
311 bool propagationFailed =
false;
315 void markFailure(
Operation *op,
const llvm::Twine &message) {
317 propagationFailed =
true;
321 bool hasFailed()
const {
return propagationFailed; }
327 layoutKind(layoutKind), indexBitWidth(indexBitWidth) {}
334 void visitBranchOperand(
OpOperand &operand)
override {};
336 void visitCallOperand(
OpOperand &operand)
override {};
342 void visitExternalCall(CallOpInterface call,
347 void setToExitState(LayoutInfoLattice *lattice)
override {
348 (
void)lattice->meet(LayoutInfo());
353int64_t LayoutInfoPropagation::getProgramOrder(Operation *op) {
354 auto it = programOrder.find(op);
355 if (it != programOrder.end())
361 Operation *root = op;
365 root->
walk<WalkOrder::PreOrder>(
366 [&](Operation *o) { programOrder[o] = counter++; });
367 return programOrder.lookup(op);
370LogicalResult LayoutInfoPropagation::visitOperation(
371 Operation *op, ArrayRef<LayoutInfoLattice *> operands,
372 ArrayRef<const LayoutInfoLattice *> results) {
375 currentProgramOrder = getProgramOrder(op);
378 [&](xegpu::DpasOp dpasOp) { visitDpasOp(dpasOp, operands, results); })
379 .Case([&](xegpu::DpasMxOp dpasMxOp) {
380 visitDpasMxOp(dpasMxOp, operands, results);
382 .Case([&](xegpu::StoreNdOp storeNdOp) {
383 visitStoreNdOp(storeNdOp, operands, results);
385 .Case([&](xegpu::StoreScatterOp storeScatterOp) {
386 visitStoreScatterOp(storeScatterOp, operands, results);
388 .Case([&](xegpu::LoadNdOp loadNdOp) {
389 visitLoadNdOp(loadNdOp, operands, results);
391 .Case([&](xegpu::LoadGatherOp loadGatherOp) {
392 visitLoadGatherOp(loadGatherOp, operands, results);
394 .Case([&](xegpu::PrefetchNdOp prefetchNdOp) {
395 visitPrefetchNdOp(prefetchNdOp, operands, results);
397 .Case([&](vector::TransposeOp transposeOp) {
398 visitTransposeOp(transposeOp, operands, results);
400 .Case([&](vector::BitCastOp bitcastOp) {
401 visitVectorBitcastOp(bitcastOp, operands, results);
403 .Case([&](vector::InterleaveOp interleaveOp) {
404 visitVectorInterleaveOp(interleaveOp, operands, results);
406 .Case([&](vector::DeinterleaveOp deinterleaveOp) {
407 visitVectorDeinterleaveOp(deinterleaveOp, operands, results);
409 .Case([&](vector::MultiDimReductionOp reductionOp) {
410 visitVectorMultiReductionOp(reductionOp, operands, results);
412 .Case([&](vector::ReductionOp reductionOp) {
413 visitVectorReductionOp(reductionOp, operands, results);
415 .Case([&](vector::BroadcastOp broadcastOp) {
416 visitVectorBroadCastOp(broadcastOp, operands, results);
418 .Case([&](vector::ShapeCastOp shapeCastOp) {
419 visitShapeCastOp(shapeCastOp, operands, results);
421 .Case([&](vector::InsertStridedSliceOp insertStridedSliceOp) {
422 visitInsertStridedSliceOp(insertStridedSliceOp, operands, results);
424 .Case([&](xegpu::LoadMatrixOp loadMatrixOp) {
425 visitLoadMatrixOp(loadMatrixOp, operands, results);
427 .Case([&](xegpu::StoreMatrixOp storeMatrixOp) {
428 visitStoreMatrixOp(storeMatrixOp, operands, results);
430 .Case([&](xegpu::ConvertLayoutOp convertLayoutOp) {
431 visitConvertLayoutOp(convertLayoutOp, operands, results);
434 .Default([&](Operation *op) {
435 for (
const LayoutInfoLattice *resultInfo : results) {
436 if (!resultInfo->getValue().isAssigned())
438 for (
auto [operandInfo, operand] :
442 if (!isa<xegpu::TensorDescType, VectorType>(
443 operand.get().getType()))
446 meet(operandInfo, *resultInfo);
454bool LayoutInfoPropagation::hasParamsOfLayoutKind(
455 xegpu::DistributeLayoutAttr anchorLayout) {
456 if (anchorLayout ==
nullptr) {
459 if (layoutKind == xegpu::LayoutKind::InstData) {
460 return !(anchorLayout.getEffectiveInstDataAsInt().empty());
462 if (layoutKind == xegpu::LayoutKind::Lane) {
463 return !(anchorLayout.getEffectiveLaneLayoutAsInt().empty() ||
464 anchorLayout.getEffectiveLaneDataAsInt().empty());
466 if (layoutKind == xegpu::LayoutKind::Subgroup) {
467 return !(anchorLayout.getEffectiveSgLayoutAsInt().empty() ||
468 anchorLayout.getEffectiveSgDataAsInt().empty());
473FailureOr<int64_t> LayoutInfoPropagation::getNumSgOrFail(
474 Operation *op,
int sgSize, xegpu::DistributeLayoutAttr consumerLayout) {
476 if (consumerLayout) {
477 auto sgLayout = consumerLayout.getEffectiveSgLayoutAsInt();
478 if (!sgLayout.empty())
479 return llvm::product_of(sgLayout);
483 std::optional<ArrayRef<int32_t>> knownBlockSize =
484 gpuFunc.getKnownBlockSize();
485 if (knownBlockSize) {
486 bool isPowerOf2Block = llvm::all_of(*knownBlockSize, [](int32_t dim) {
487 return dim > 0 && llvm::isPowerOf2_32(dim);
489 int64_t numSg = llvm::product_of(*knownBlockSize) / sgSize;
490 if (isPowerOf2Block && numSg > 0)
495 if (layoutKind == xegpu::LayoutKind::Subgroup) {
496 markFailure(op,
"Unable to determine the number of subgroups for the "
497 "operation. Please check @known_block_size is properly "
498 "attached as kernel attributes, with power-of-two "
499 "dimensions covering at least one subgroup.");
505void LayoutInfoPropagation::visitPrefetchNdOp(
506 xegpu::PrefetchNdOp prefetch, ArrayRef<LayoutInfoLattice *> operands,
507 ArrayRef<const LayoutInfoLattice *> results) {
509 LayoutInfo prefetchLayout;
513 xegpu::DistributeLayoutAttr anchorLayout = prefetch.getLayoutAttr();
514 if (hasParamsOfLayoutKind(anchorLayout)) {
515 prefetchLayout = makeLayoutInfo(anchorLayout);
516 if (layoutKind == xegpu::LayoutKind::InstData) {
517 const auto *uArchInstruction =
518 dyn_cast<xegpu::uArch::Subgroup2DBlockPrefetchInstruction>(
519 uArch->getInstruction(
520 xegpu::uArch::InstructionKind::Subgroup2DBlockPrefetch));
521 if (!uArchInstruction)
524 anchorLayout, prefetch.getTensorDescType().getElementType(),
525 uArchInstruction, uArch->getSubgroupSize());
527 prefetch.emitWarning(
528 "Failed to identify lane layouts for the specified inst_data.");
531 prefetch.setLayoutAttr(*completed);
532 prefetchLayout = makeLayoutInfo(*completed);
535 auto tdescTy = prefetch.getTensorDescType();
537 getNumSgOrFail(prefetch, uArch->getSubgroupSize(),
nullptr);
542 layoutKind, tdescTy, numSgOrErr.value_or(0), uArch);
544 prefetch.emitWarning(
545 "Failed to determine required layout for prefetch_nd.");
548 prefetchLayout = makeLayoutInfo(layoutAttr);
549 prefetch.setLayoutAttr(layoutAttr);
552 propagateIfChanged(operands[0], operands[0]->meet(prefetchLayout));
555void LayoutInfoPropagation::visitVectorMultiReductionOp(
556 vector::MultiDimReductionOp reduction,
557 ArrayRef<LayoutInfoLattice *> operands,
558 ArrayRef<const LayoutInfoLattice *> results) {
559 Type resultTy = reduction.getDestType();
561 LayoutInfo resLayoutInfo = results[0]->getValue();
563 xegpu::DistributeLayoutAttr consumerLayoutAttr;
565 if (!resLayoutInfo.isAssigned())
568 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
571 VectorType sourceTy = reduction.getSourceVectorType();
572 SmallVector<int64_t> reductionDims(reduction.getReductionDims());
580 getNumSgOrFail(reduction, uArch->getSubgroupSize(), consumerLayoutAttr);
590 layoutKind, sourceTy, consumerLayoutAttr, reductionDims,
591 numSgOrErr.value_or(0), uArch);
597 requiredResLayoutAttr, reductionDims);
599 propagateIfChanged(operands[0],
600 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
602 propagateIfChanged(operands[1],
603 operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
606void LayoutInfoPropagation::visitVectorReductionOp(
607 vector::ReductionOp reduction, ArrayRef<LayoutInfoLattice *> operands,
608 ArrayRef<const LayoutInfoLattice *> results) {
610 VectorType sourceTy = reduction.getSourceVectorType();
616 auto requiredResLayoutAttr =
621 propagateIfChanged(operands[0],
622 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
623 if (reduction.getAcc())
625 operands[1], operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
628void LayoutInfoPropagation::visitVectorBroadCastOp(
629 vector::BroadcastOp
broadcast, ArrayRef<LayoutInfoLattice *> operands,
630 ArrayRef<const LayoutInfoLattice *> results) {
632 LayoutInfo resLayoutInfo = results[0]->getValue();
633 if (!resLayoutInfo.isAssigned())
637 VectorType resultTy =
broadcast.getResultVectorType();
638 VectorType sourceTy = dyn_cast<VectorType>(
broadcast.getSourceType());
643 auto srcShape = sourceTy.getShape();
644 auto resShape = resultTy.getShape();
646 auto resultLayoutAttr =
647 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
649 xegpu::DistributeLayoutAttr srcLayoutAttr =
652 propagateIfChanged(operands[0],
653 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
656void LayoutInfoPropagation::visitShapeCastOp(
657 vector::ShapeCastOp shapeCast, ArrayRef<LayoutInfoLattice *> operands,
658 ArrayRef<const LayoutInfoLattice *> results) {
660 LayoutInfo resLayoutInfo = results[0]->getValue();
661 if (!resLayoutInfo.isAssigned())
663 ArrayRef<int64_t> resShape = shapeCast.getResultVectorType().getShape();
664 ArrayRef<int64_t> srcShape = shapeCast.getSourceVectorType().getShape();
665 auto resultLayoutAttr =
666 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
668 xegpu::DistributeLayoutAttr srcLayoutAttr =
672 if (!srcLayoutAttr) {
673 shapeCast.emitWarning(
"Failed to infer source layout for shape_cast; "
674 "unsupported shape-cast pattern.");
678 propagateIfChanged(operands[0],
679 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
683void LayoutInfoPropagation::visitDpasOp(
684 xegpu::DpasOp dpas, ArrayRef<LayoutInfoLattice *> operands,
685 ArrayRef<const LayoutInfoLattice *> results) {
686 LayoutInfo dpasALayout;
687 LayoutInfo dpasBLayout;
688 LayoutInfo dpasCDLayout;
693 VectorType aTy = dpas.getLhsType();
694 VectorType bTy = dpas.getRhsType();
695 VectorType cdTy = dpas.getResultType();
697 xegpu::DistributeLayoutAttr anchorLayoutCD = dpas.getLayoutCdAttr();
698 if (hasParamsOfLayoutKind(anchorLayoutCD)) {
699 xegpu::DistributeLayoutAttr anchorLayoutA = dpas.getLayoutAAttr();
700 xegpu::DistributeLayoutAttr anchorLayoutB = dpas.getLayoutBAttr();
701 assert(hasParamsOfLayoutKind(anchorLayoutA) &&
702 "Expected anchor layout for DPAS A operand.");
703 assert(hasParamsOfLayoutKind(anchorLayoutB) &&
704 "Expected anchor layout for DPAS B operand.");
705 dpasALayout = makeLayoutInfo(anchorLayoutA);
706 dpasBLayout = makeLayoutInfo(anchorLayoutB);
707 dpasCDLayout = makeLayoutInfo(anchorLayoutCD);
708 if (layoutKind == xegpu::LayoutKind::InstData) {
710 anchorLayoutA, anchorLayoutB, anchorLayoutCD, aTy, bTy, cdTy, uArch);
714 "Failed to identify lane layouts for the specified inst_data.");
717 auto [completedA, completedB, completedCD] = *completed;
718 dpas.setLayoutAAttr(completedA);
719 dpas.setLayoutBAttr(completedB);
720 dpas.setLayoutCdAttr(completedCD);
721 dpasALayout = makeLayoutInfo(completedA);
722 dpasBLayout = makeLayoutInfo(completedB);
723 dpasCDLayout = makeLayoutInfo(completedCD);
727 xegpu::DistributeLayoutAttr consumerLayoutAttr =
nullptr;
728 xegpu::DistributeLayoutAttr requiredCDLayoutAttr, requiredALayout,
731 LayoutInfo consumerLayout = results[0]->getValue();
732 if (!consumerLayout.isAssigned())
735 dyn_cast<xegpu::DistributeLayoutAttr>(consumerLayout.get());
738 getNumSgOrFail(dpas, uArch->getSubgroupSize(), consumerLayoutAttr);
744 numSgOrErr.value_or(0), uArch);
745 if (!layouts.has_value()) {
747 "Failed to determine required layouts for DPAS operands.");
751 std::tie(requiredALayout, requiredBLayout, requiredCDLayoutAttr) = *layouts;
753 dpas.setLayoutAAttr(requiredALayout);
754 dpas.setLayoutBAttr(requiredBLayout);
755 dpas.setLayoutCdAttr(requiredCDLayoutAttr);
756 dpasALayout = makeLayoutInfo(requiredALayout);
757 dpasBLayout = makeLayoutInfo(requiredBLayout);
758 dpasCDLayout = makeLayoutInfo(requiredCDLayoutAttr);
760 propagateIfChanged(operands[0], operands[0]->meet(dpasALayout));
761 propagateIfChanged(operands[1], operands[1]->meet(dpasBLayout));
762 if (operands.size() > 2)
763 propagateIfChanged(operands[2], operands[2]->meet(dpasCDLayout));
769void LayoutInfoPropagation::visitDpasMxOp(
770 xegpu::DpasMxOp dpasMx, ArrayRef<LayoutInfoLattice *> operands,
771 ArrayRef<const LayoutInfoLattice *> results) {
774 LayoutInfo dpasMxALayout, dpasMxBLayout, dpasMxCDLayout;
775 LayoutInfo dpasMxAScaleLayout, dpasMxBScaleLayout;
778 xegpu::DistributeLayoutAttr anchorLayoutA = dpasMx.getLayoutAAttr();
779 xegpu::DistributeLayoutAttr anchorLayoutB = dpasMx.getLayoutBAttr();
780 xegpu::DistributeLayoutAttr anchorLayoutCD = dpasMx.getLayoutCdAttr();
786 VectorType aTy = dpasMx.getAType();
787 VectorType bTy = dpasMx.getBType();
788 VectorType cdTy = dpasMx.getResultType();
793 Value scaleA = dpasMx.getScaleA();
794 Value scaleB = dpasMx.getScaleB();
796 aScaleTy = dyn_cast<VectorType>(scaleA.
getType());
798 bScaleTy = dyn_cast<VectorType>(scaleB.
getType());
801 if (anchorLayoutA && anchorLayoutB && anchorLayoutCD &&
802 hasParamsOfLayoutKind(anchorLayoutA) &&
803 hasParamsOfLayoutKind(anchorLayoutB) &&
804 hasParamsOfLayoutKind(anchorLayoutCD)) {
805 dpasMxALayout = makeLayoutInfo(anchorLayoutA);
806 dpasMxBLayout = makeLayoutInfo(anchorLayoutB);
807 dpasMxCDLayout = makeLayoutInfo(anchorLayoutCD);
810 xegpu::DistributeLayoutAttr anchorLayoutAScale =
811 dpasMx.getLayoutAScaleAttr();
812 xegpu::DistributeLayoutAttr anchorLayoutBScale =
813 dpasMx.getLayoutBScaleAttr();
814 if (anchorLayoutAScale)
815 dpasMxAScaleLayout = makeLayoutInfo(anchorLayoutAScale);
816 if (anchorLayoutBScale)
817 dpasMxBScaleLayout = makeLayoutInfo(anchorLayoutBScale);
819 if (layoutKind == xegpu::LayoutKind::InstData) {
821 anchorLayoutA, anchorLayoutB, anchorLayoutCD, aTy, bTy, cdTy,
822 aScaleTy, bScaleTy, uArch);
826 "Failed to identify lane layouts for the specified inst_data.");
829 auto [completedA, completedB, completedCD, completedAScale,
830 completedBScale] = *completed;
831 dpasMx.setLayoutAAttr(completedA);
832 dpasMx.setLayoutBAttr(completedB);
833 dpasMx.setLayoutCdAttr(completedCD);
834 dpasMxALayout = makeLayoutInfo(completedA);
835 dpasMxBLayout = makeLayoutInfo(completedB);
836 dpasMxCDLayout = makeLayoutInfo(completedCD);
837 if (completedAScale) {
838 dpasMx.setLayoutAScaleAttr(completedAScale);
839 dpasMxAScaleLayout = makeLayoutInfo(completedAScale);
841 if (completedBScale) {
842 dpasMx.setLayoutBScaleAttr(completedBScale);
843 dpasMxBScaleLayout = makeLayoutInfo(completedBScale);
847 xegpu::DistributeLayoutAttr consumerLayoutAttr =
nullptr;
848 xegpu::DistributeLayoutAttr requiredCDLayoutAttr, requiredALayout,
849 requiredBLayout, requiredAScaleLayout, requiredBScaleLayout;
851 LayoutInfo consumerLayout = results[0]->getValue();
852 if (!consumerLayout.isAssigned())
855 dyn_cast<xegpu::DistributeLayoutAttr>(consumerLayout.get());
858 getNumSgOrFail(dpasMx, uArch->getSubgroupSize(), consumerLayoutAttr);
863 layoutKind, aTy, bTy, cdTy, aScaleTy, bScaleTy, consumerLayoutAttr,
864 numSgOrErr.value_or(0), uArch);
865 if (!layouts.has_value()) {
867 "Failed to determine required layouts for DPAS_MX operands.");
871 std::tie(requiredALayout, requiredBLayout, requiredCDLayoutAttr,
872 requiredAScaleLayout, requiredBScaleLayout) = *layouts;
874 dpasMx.setLayoutAAttr(requiredALayout);
875 dpasMx.setLayoutBAttr(requiredBLayout);
876 dpasMx.setLayoutCdAttr(requiredCDLayoutAttr);
877 if (requiredAScaleLayout)
878 dpasMx.setLayoutAScaleAttr(requiredAScaleLayout);
879 if (requiredBScaleLayout)
880 dpasMx.setLayoutBScaleAttr(requiredBScaleLayout);
882 dpasMxALayout = makeLayoutInfo(requiredALayout);
883 dpasMxBLayout = makeLayoutInfo(requiredBLayout);
884 dpasMxCDLayout = makeLayoutInfo(requiredCDLayoutAttr);
885 if (requiredAScaleLayout)
886 dpasMxAScaleLayout = makeLayoutInfo(requiredAScaleLayout);
887 if (requiredBScaleLayout)
888 dpasMxBScaleLayout = makeLayoutInfo(requiredBScaleLayout);
895 propagateIfChanged(operands[0], operands[0]->meet(dpasMxALayout));
896 propagateIfChanged(operands[1], operands[1]->meet(dpasMxBLayout));
898 if (dpasMx.getAcc()) {
899 propagateIfChanged(operands[idx], operands[idx]->meet(dpasMxCDLayout));
902 if (dpasMx.getScaleA()) {
903 if (dpasMxAScaleLayout.isAssigned())
904 propagateIfChanged(operands[idx],
905 operands[idx]->meet(dpasMxAScaleLayout));
908 if (dpasMx.getScaleB()) {
909 if (dpasMxBScaleLayout.isAssigned())
910 propagateIfChanged(operands[idx],
911 operands[idx]->meet(dpasMxBScaleLayout));
917void LayoutInfoPropagation::visitStoreNdOp(
918 xegpu::StoreNdOp store, ArrayRef<LayoutInfoLattice *> operands,
919 ArrayRef<const LayoutInfoLattice *> results) {
920 LayoutInfo storeLayout;
924 xegpu::DistributeLayoutAttr anchorLayout = store.getLayoutAttr();
925 if (hasParamsOfLayoutKind(anchorLayout)) {
926 storeLayout = makeLayoutInfo(anchorLayout);
927 if (layoutKind == xegpu::LayoutKind::InstData) {
929 const auto *uArchInstruction =
930 dyn_cast<xegpu::uArch::Subgroup2DBlockStoreInstruction>(
931 uArch->getInstruction(
932 xegpu::uArch::InstructionKind::Subgroup2DBlockStore));
933 if (!uArchInstruction)
936 anchorLayout, store.getValueType().getElementType(), uArchInstruction,
937 uArch->getSubgroupSize());
941 "Failed to identify lane layouts for the specified inst_data.");
944 store.setLayoutAttr(*completed);
945 storeLayout = makeLayoutInfo(*completed);
948 auto numSgOrErr = getNumSgOrFail(store, uArch->getSubgroupSize(),
nullptr);
953 layoutKind, store.getValueType(), numSgOrErr.value_or(0), uArch);
955 markFailure(store,
"Failed to determine required layout for store_nd.");
958 storeLayout = makeLayoutInfo(layoutAttr);
959 store.setLayoutAttr(layoutAttr);
963 for (LayoutInfoLattice *operand : operands)
964 propagateIfChanged(operand, operand->meet(storeLayout));
969void LayoutInfoPropagation::visitLoadNdOp(
970 xegpu::LoadNdOp
load, ArrayRef<LayoutInfoLattice *> operands,
971 ArrayRef<const LayoutInfoLattice *> results) {
972 LayoutInfo loadLayout;
977 LayoutInfo valueLayout = results[0]->getValue();
978 if (!valueLayout.isAssigned())
980 auto consumerLayoutAttr =
981 dyn_cast<xegpu::DistributeLayoutAttr>(valueLayout.get());
982 xegpu::DistributeLayoutAttr anchorLayout =
load.getLayoutAttr();
983 if (hasParamsOfLayoutKind(anchorLayout)) {
984 loadLayout = makeLayoutInfo(anchorLayout);
985 if (layoutKind == xegpu::LayoutKind::InstData &&
986 !consumerLayoutAttr.getEffectiveLaneLayoutAsInt().empty()) {
987 const auto *uArchInstruction =
988 dyn_cast<xegpu::uArch::Subgroup2DBlockLoadInstruction>(
989 uArch->getInstruction(
990 xegpu::uArch::InstructionKind::Subgroup2DBlockLoad));
991 if (!uArchInstruction)
994 anchorLayout, consumerLayoutAttr,
load.getType().getElementType(),
995 uArchInstruction, uArch->getSubgroupSize());
998 "Failed to identify lane layouts for the specified inst_data.");
1001 load.setLayoutAttr(*completed);
1002 loadLayout = makeLayoutInfo(*completed);
1006 getNumSgOrFail(
load, uArch->getSubgroupSize(), consumerLayoutAttr);
1010 layoutKind,
load.getType(), consumerLayoutAttr, numSgOrErr.value_or(0),
1013 load.emitWarning(
"Failed to determine required layout for load_nd.");
1016 loadLayout = makeLayoutInfo(layoutAttr);
1017 load.setLayoutAttr(layoutAttr);
1020 propagateIfChanged(operands[0], operands[0]->meet(loadLayout));
1025void LayoutInfoPropagation::visitConvertLayoutOp(
1026 xegpu::ConvertLayoutOp convert, ArrayRef<LayoutInfoLattice *> operands,
1027 ArrayRef<const LayoutInfoLattice *> results) {
1029 LayoutInfo resultLayout = results[0]->getValue();
1032 auto targetLayoutAttr =
1033 dyn_cast<xegpu::LayoutAttr>(convert.getTargetLayoutAttr());
1035 auto inputLayoutAttr =
1036 dyn_cast_if_present<xegpu::LayoutAttr>(convert.getInputLayoutAttr());
1042 auto resultLayoutAttr = resultLayout.isAssigned()
1043 ? dyn_cast<xegpu::LayoutAttr>(resultLayout.get())
1045 if (resultLayoutAttr && targetLayoutAttr) {
1046 if (layoutKind == xegpu::LayoutKind::InstData &&
1047 !targetLayoutAttr.getLaneLayout()) {
1048 targetLayoutAttr = xegpu::LayoutAttr::get(
1049 convert.getContext(), targetLayoutAttr.getSgLayout(),
1050 targetLayoutAttr.getSgData(), targetLayoutAttr.getInstData(),
1051 resultLayoutAttr.getLaneLayout(), resultLayoutAttr.getLaneData(),
1052 resultLayoutAttr.getOrder());
1053 convert.setTargetLayoutAttr(targetLayoutAttr);
1060 if (inputLayoutAttr && targetLayoutAttr) {
1061 if (layoutKind == xegpu::LayoutKind::InstData &&
1062 !inputLayoutAttr.getLaneLayout()) {
1063 auto merged = xegpu::LayoutAttr::get(
1064 convert.getContext(), inputLayoutAttr.getSgLayout(),
1065 inputLayoutAttr.getSgData(), inputLayoutAttr.getInstData(),
1066 targetLayoutAttr.getLaneLayout(), targetLayoutAttr.getLaneData(),
1067 targetLayoutAttr.getOrder());
1068 convert.setInputLayoutAttr(merged);
1072 xegpu::DistributeLayoutAttr anchorLayout = convert.getEffectiveInputLayout();
1073 LayoutInfo convertLayout = makeLayoutInfo(anchorLayout);
1075 propagateIfChanged(operands[0], operands[0]->meet(convertLayout));
1080void LayoutInfoPropagation::visitTransposeOp(
1081 vector::TransposeOp transpose, ArrayRef<LayoutInfoLattice *> operands,
1082 ArrayRef<const LayoutInfoLattice *> results) {
1084 LayoutInfo resultLayout = results[0]->getValue();
1085 if (!resultLayout.isAssigned())
1088 auto consumerLayoutAttr =
1089 dyn_cast<xegpu::DistributeLayoutAttr>(resultLayout.get());
1091 consumerLayoutAttr, transpose.getPermutation());
1094 propagateIfChanged(operands[0],
1095 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1100void LayoutInfoPropagation::visitVectorBitcastOp(
1101 vector::BitCastOp bitcast, ArrayRef<LayoutInfoLattice *> operands,
1102 ArrayRef<const LayoutInfoLattice *> results) {
1104 LayoutInfo resLayoutInfo = results[0]->getValue();
1105 if (!resLayoutInfo.isAssigned())
1108 auto srcVecType = bitcast.getSourceVectorType();
1109 auto resVecType = bitcast.getResultVectorType();
1111 auto consumerLayoutAttr =
1112 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1118 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1122 int inElemTyBitWidth = srcVecType.getElementType().getIntOrFloatBitWidth();
1123 int outElemTyBitWidth = resVecType.getElementType().getIntOrFloatBitWidth();
1127 requiredResLayoutAttr, outElemTyBitWidth, inElemTyBitWidth);
1129 propagateIfChanged(operands[0],
1130 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1136void LayoutInfoPropagation::visitVectorInterleaveOp(
1137 vector::InterleaveOp interleave, ArrayRef<LayoutInfoLattice *> operands,
1138 ArrayRef<const LayoutInfoLattice *> results) {
1140 LayoutInfo resLayoutInfo = results[0]->getValue();
1141 if (!resLayoutInfo.isAssigned())
1144 auto srcVecType = interleave.getSourceVectorType();
1145 auto resVecType = interleave.getResultVectorType();
1147 auto consumerLayoutAttr =
1148 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1156 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1161 auto srcLayoutAttr =
1165 propagateIfChanged(operands[0],
1166 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1167 propagateIfChanged(operands[1],
1168 operands[1]->meet(makeLayoutInfo(srcLayoutAttr)));
1174void LayoutInfoPropagation::visitVectorDeinterleaveOp(
1175 vector::DeinterleaveOp deinterleave, ArrayRef<LayoutInfoLattice *> operands,
1176 ArrayRef<const LayoutInfoLattice *> results) {
1179 LayoutInfo resLayoutInfo = results[0]->getValue();
1180 if (!resLayoutInfo.isAssigned())
1183 auto consumerLayoutAttr =
1184 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1190 propagateIfChanged(operands[0],
1191 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1194void LayoutInfoPropagation::visitInsertStridedSliceOp(
1195 vector::InsertStridedSliceOp insertStridedSlice,
1196 ArrayRef<LayoutInfoLattice *> operands,
1197 ArrayRef<const LayoutInfoLattice *> results) {
1199 LayoutInfo resLayoutInfo = results[0]->getValue();
1200 if (!resLayoutInfo.isAssigned())
1203 auto srcVecType = insertStridedSlice.getSourceVectorType();
1204 auto resVecType = insertStridedSlice.getDestVectorType();
1206 auto consumerLayoutAttr =
1207 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1214 layoutKind, srcVecType, resVecType, consumerLayoutAttr, uArch);
1216 requiredResLayoutAttr);
1219 requiredResLayoutAttr, resVecType.getShape(), srcVecType.getShape());
1220 propagateIfChanged(operands[0],
1221 operands[0]->meet(makeLayoutInfo(srcLayoutAttr)));
1222 propagateIfChanged(operands[1],
1223 operands[1]->meet(makeLayoutInfo(requiredResLayoutAttr)));
1228void LayoutInfoPropagation::visitLoadGatherOp(
1229 xegpu::LoadGatherOp
load, ArrayRef<LayoutInfoLattice *> operands,
1230 ArrayRef<const LayoutInfoLattice *> results) {
1231 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1232 xegpu::DistributeLayoutAttr anchorLayoutAttr =
load.getLayoutAttr();
1236 VectorType resVecTy =
load.getValueType();
1237 int chunkSize =
load.getChunkSize().value_or(1);
1239 LayoutInfo resLayoutInfo = results[0]->getValue();
1240 if (!resLayoutInfo.isAssigned())
1242 auto consumerLayoutAttr =
1243 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1245 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1246 requiredAnchorLayoutAttr = anchorLayoutAttr;
1247 if (layoutKind == xegpu::LayoutKind::InstData &&
1248 !consumerLayoutAttr.getEffectiveLaneLayoutAsInt().empty()) {
1249 const auto uArchInstruction =
1250 dyn_cast<xegpu::uArch::LoadGatherInstruction>(
1251 uArch->getInstruction(xegpu::uArch::InstructionKind::LoadGather));
1252 if (!uArchInstruction)
1255 anchorLayoutAttr, consumerLayoutAttr, resVecTy.getElementType(),
1256 uArchInstruction, uArch->getSubgroupSize());
1259 "Failed to identify lane layouts for the specified inst_data.");
1262 requiredAnchorLayoutAttr = *completed;
1263 load.setLayoutAttr(requiredAnchorLayoutAttr);
1267 load.emitWarning(
"Not propagating, non-vector payload supplied.");
1271 layoutKind, resVecTy, chunkSize, consumerLayoutAttr, uArch);
1272 load.setLayoutAttr(requiredAnchorLayoutAttr);
1275 assert((chunkSize <= 1) || (layoutKind != xegpu::LayoutKind::Subgroup));
1277 requiredAnchorLayoutAttr, chunkSize);
1278 LayoutInfo maskLayoutInfo = makeLayoutInfo(maskLayoutAttr);
1279 auto loadLayoutInfo = makeLayoutInfo(requiredAnchorLayoutAttr);
1282 if (isa<xegpu::TensorDescType>(
load.getSourceType()))
1283 propagateIfChanged(operands[0], operands[0]->meet(loadLayoutInfo));
1285 propagateIfChanged(operands[1], operands[1]->meet(maskLayoutInfo));
1286 propagateIfChanged(operands[2], operands[2]->meet(maskLayoutInfo));
1291void LayoutInfoPropagation::visitStoreScatterOp(
1292 xegpu::StoreScatterOp storeScatter, ArrayRef<LayoutInfoLattice *> operands,
1293 ArrayRef<const LayoutInfoLattice *> results) {
1295 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1296 xegpu::DistributeLayoutAttr anchorLayoutAttr = storeScatter.getLayoutAttr();
1301 VectorType srcVecTy = storeScatter.getValueType();
1302 int chunkSize = storeScatter.getChunkSize().value_or(1);
1304 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1305 requiredAnchorLayoutAttr = anchorLayoutAttr;
1306 if (layoutKind == xegpu::LayoutKind::InstData) {
1307 const auto uArchInstruction =
1308 dyn_cast<xegpu::uArch::StoreScatterInstruction>(uArch->getInstruction(
1309 xegpu::uArch::InstructionKind::StoreScatter));
1310 if (!uArchInstruction)
1313 anchorLayoutAttr, srcVecTy.getElementType(), uArchInstruction,
1314 uArch->getSubgroupSize());
1318 "Failed to identify lane layouts for the specified inst_data.");
1321 requiredAnchorLayoutAttr = *completed;
1322 storeScatter.setLayoutAttr(requiredAnchorLayoutAttr);
1326 storeScatter.emitWarning(
"Not propagating, non-vector payload supplied.");
1330 getNumSgOrFail(storeScatter, uArch->getSubgroupSize(),
nullptr);
1334 layoutKind, srcVecTy, chunkSize, numSgOrErr.value_or(0), uArch);
1335 if (!requiredAnchorLayoutAttr) {
1336 markFailure(storeScatter,
1337 "Failed to determine required layout for store scatter.");
1340 storeScatter.setLayoutAttr(requiredAnchorLayoutAttr);
1343 LayoutInfo srcLayoutInfo = makeLayoutInfo(requiredAnchorLayoutAttr);
1344 assert((chunkSize <= 1) || (layoutKind != xegpu::LayoutKind::Subgroup));
1346 requiredAnchorLayoutAttr, chunkSize);
1347 LayoutInfo maskLayoutInfo = makeLayoutInfo(maskLayoutAttr);
1350 propagateIfChanged(operands[0], operands[0]->meet(srcLayoutInfo));
1352 if (isa<xegpu::TensorDescType>(storeScatter.getDestType()))
1353 propagateIfChanged(operands[1], operands[1]->meet(srcLayoutInfo));
1355 propagateIfChanged(operands[2], operands[2]->meet(maskLayoutInfo));
1356 propagateIfChanged(operands[3], operands[3]->meet(maskLayoutInfo));
1359void LayoutInfoPropagation::visitLoadMatrixOp(
1360 xegpu::LoadMatrixOp loadMatrixOp, ArrayRef<LayoutInfoLattice *> operands,
1361 ArrayRef<const LayoutInfoLattice *> results) {
1363 LayoutInfo resLayoutInfo = results[0]->getValue();
1364 if (!resLayoutInfo.isAssigned())
1367 auto consumerLayoutAttr =
1368 dyn_cast<xegpu::DistributeLayoutAttr>(resLayoutInfo.get());
1370 xegpu::DistributeLayoutAttr anchorLayout = loadMatrixOp.getLayoutAttr();
1374 if (!hasParamsOfLayoutKind(anchorLayout)) {
1375 VectorType resVecTy =
1376 llvm::cast<VectorType>(loadMatrixOp.getRes().getType());
1384 layoutKind, resVecTy, chunkSize, consumerLayoutAttr, uArch);
1385 loadMatrixOp.setLayoutAttr(requiredAnchorLayoutAttr);
1389void LayoutInfoPropagation::visitStoreMatrixOp(
1390 xegpu::StoreMatrixOp storeMatrix, ArrayRef<LayoutInfoLattice *> operands,
1391 ArrayRef<const LayoutInfoLattice *> results) {
1392 xegpu::DistributeLayoutAttr requiredAnchorLayoutAttr;
1393 xegpu::DistributeLayoutAttr anchorLayoutAttr = storeMatrix.getLayoutAttr();
1395 VectorType srcVecTy = llvm::cast<VectorType>(storeMatrix.getData().getType());
1400 if (hasParamsOfLayoutKind(anchorLayoutAttr)) {
1401 requiredAnchorLayoutAttr = anchorLayoutAttr;
1402 if (layoutKind == xegpu::LayoutKind::InstData) {
1403 const auto uArchInstruction =
1404 dyn_cast<xegpu::uArch::StoreScatterInstruction>(uArch->getInstruction(
1405 xegpu::uArch::InstructionKind::StoreScatter));
1406 if (!uArchInstruction)
1409 anchorLayoutAttr, srcVecTy.getElementType(), uArchInstruction,
1410 uArch->getSubgroupSize());
1414 "Failed to identify lane layouts for the specified inst_data.");
1417 requiredAnchorLayoutAttr = *completed;
1418 storeMatrix.setLayoutAttr(requiredAnchorLayoutAttr);
1424 getNumSgOrFail(storeMatrix, uArch->getSubgroupSize(),
nullptr);
1428 layoutKind, srcVecTy, chunkSize, numSgOrErr.value_or(0), uArch);
1429 if (!requiredAnchorLayoutAttr) {
1430 markFailure(storeMatrix,
1431 "Failed to determine required layout for store matrix.");
1434 storeMatrix.setLayoutAttr(requiredAnchorLayoutAttr);
1436 layout = makeLayoutInfo(requiredAnchorLayoutAttr);
1437 propagateIfChanged(operands[0], operands[0]->meet(layout));
1446class RunLayoutInfoPropagation {
1451 unsigned indexBitWidth)
1453 SymbolTableCollection symbolTable;
1455 analysis = solver.
load<LayoutInfoPropagation>(symbolTable, layoutKind,
1460 LayoutInfo getLayoutInfo(Value val);
1462 void printAnalysisResult(llvm::raw_ostream &os);
1464 bool hasFailed()
const {
return analysis && analysis->hasFailed(); }
1467 DataFlowSolver solver;
1469 LayoutInfoPropagation *analysis =
nullptr;
1473LayoutInfo RunLayoutInfoPropagation::getLayoutInfo(Value val) {
1474 auto *state = solver.
lookupState<LayoutInfoLattice>(val);
1477 return state->getValue();
1481void RunLayoutInfoPropagation::printAnalysisResult(llvm::raw_ostream &os) {
1482 auto printFunctionResult = [&](FunctionOpInterface funcOp) {
1483 os <<
"function: " << funcOp.getName() <<
":\n";
1485 for (BlockArgument arg : funcOp.getArguments()) {
1486 LayoutInfo layout = getLayoutInfo(arg);
1487 os <<
"argument: " << arg <<
"\n";
1493 funcOp.walk([&](Operation *op) {
1499 if (isa<BranchOpInterface>(op) || isa<RegionBranchOpInterface>(op))
1505 for (
auto [i, r] : llvm::enumerate(op->
getResults())) {
1506 LayoutInfo layout = getLayoutInfo(r);
1507 os <<
"layout for result #" << i <<
": ";
1514 SmallVector<FunctionOpInterface> funcOps;
1515 if (
auto modOp = dyn_cast<ModuleOp>(
target)) {
1516 for (
auto funcOp : modOp.getOps<FunctionOpInterface>())
1517 funcOps.push_back(funcOp);
1520 for (
auto gpuModOp : modOp.getOps<gpu::GPUModuleOp>()) {
1521 for (
auto gpuFuncOp : gpuModOp.getOps<FunctionOpInterface>())
1522 funcOps.push_back(gpuFuncOp);
1526 for (FunctionOpInterface funcOp : funcOps)
1527 printFunctionResult(funcOp);
1539static xegpu::CreateNdDescOp getDefiningCreateNdDescOp(Value tdescValue) {
1541 auto definingOp = tdescValue.
getDefiningOp<xegpu::CreateNdDescOp>();
1546 if (
auto arg = dyn_cast<BlockArgument>(tdescValue)) {
1547 auto *parentOp = arg.getOwner()->getParentOp();
1548 if (
auto loop = dyn_cast<LoopLikeOpInterface>(parentOp)) {
1549 OpOperand *tiedInit = loop.getTiedLoopInit(arg);
1551 return getDefiningCreateNdDescOp(tiedInit->
get());
1558struct ResolveLayoutConflicts {
1559 ResolveLayoutConflicts(Operation *parentOp)
1560 : parentOp(parentOp), builder(parentOp->
getContext()) {}
1561 LogicalResult run();
1564 Operation *parentOp;
1566 LogicalResult resolveTensorDescConsumer(OpOperand &operand);
1567 LogicalResult resolveVectorConsumer(OpOperand &operand);
1568 LogicalResult assignResultLayout(OpResult &
result);
1573LogicalResult ResolveLayoutConflicts::run() {
1576 auto r = parentOp->
walk([&](Operation *op) -> WalkResult {
1581 if (
result.getType().isIntOrFloat() &&
1582 (isa<vector::MultiDimReductionOp>(op) ||
1583 isa<vector::ReductionOp>(op))) {
1584 auto res = assignResultLayout(
result);
1586 DBGS() <<
"Failed to assign layout for scalar consumer of reduction "
1594 if (isa<VectorType>(
result.getType()) &&
result.use_empty() &&
1595 isa<RegionBranchOpInterface>(op)) {
1596 auto res = assignResultLayout(
result);
1598 DBGS() <<
"Failed to assign layout for vector consumer of region op "
1606 Type operandType = operand.get().getType();
1607 if (isa<xegpu::AnchorLayoutInterface>(op) &&
1608 isa<xegpu::TensorDescType>(operandType)) {
1609 auto res = resolveTensorDescConsumer(operand);
1611 DBGS() <<
"Failed to resolve tensor descriptor consumer: " << *op
1617 if (isa<VectorType>(operandType)) {
1618 auto res = resolveVectorConsumer(operand);
1620 DBGS() <<
"Failed to resolve vector consumer: " << *op <<
"\n";
1629 DBGS() <<
"IR after resolving layout conflicts:\n";
1633 return r.wasInterrupted() ? failure() :
success();
1636LogicalResult ResolveLayoutConflicts::assignResultLayout(OpResult &
result) {
1637 Operation *producerOp =
result.getDefiningOp();
1641 auto convertOp = xegpu::ConvertLayoutOp::create(
1644 result.replaceAllUsesExcept(convertOp.getResult(), convertOp);
1649ResolveLayoutConflicts::resolveVectorConsumer(OpOperand &operand) {
1650 Value vectorValue = operand.
get();
1651 Operation *consumerOp = operand.
getOwner();
1654 if (!producerLayout) {
1655 if (
auto vectorTy = dyn_cast<VectorType>(vectorValue.
getType());
1656 vectorTy && vectorTy.getRank() > 1)
1657 consumerOp->
emitWarning(
"Expected layout for non-1D vectors.");
1665 if (isa<RegionBranchOpInterface, RegionBranchTerminatorOpInterface>(
1670 if (!consumerLayout)
1672 "No consumer layout found for vector operand.");
1675 if (consumerLayout.isEqualTo(producerLayout))
1681 if (
auto consumerConvert = dyn_cast<xegpu::ConvertLayoutOp>(consumerOp)) {
1682 consumerConvert.setInputLayoutAttr(producerLayout);
1688 if (
auto producerConvert =
1690 producerConvert && vectorValue.
hasOneUse()) {
1693 producerConvert.setInputLayoutAttr(
1694 producerConvert.getEffectiveInputLayout());
1695 producerConvert.setTargetLayoutAttr(consumerLayout);
1707 isa<OpResult>(vectorValue) &&
1710 Operation *
clone = builder.
clone(*producerOp);
1715 operand.
set(cloneResult);
1721 auto convertOp = xegpu::ConvertLayoutOp::create(
1722 builder, consumerOp->
getLoc(), vectorValue.
getType(), vectorValue,
1723 producerLayout, consumerLayout);
1726 operand.
set(convertOp.getResult());
1731ResolveLayoutConflicts::resolveTensorDescConsumer(OpOperand &operand) {
1732 Operation *consumerOp = operand.
getOwner();
1733 Value tdescValue = operand.
get();
1734 auto anchorOp = dyn_cast<xegpu::AnchorLayoutInterface>(consumerOp);
1735 auto currTDescType = dyn_cast<xegpu::TensorDescType>(tdescValue.
getType());
1736 assert(anchorOp && currTDescType &&
1737 "Expected anchor layout op and tensor descriptor consumer.");
1738 Attribute currLayout = currTDescType.getLayout();
1739 Attribute expectedLayout = anchorOp.getAnchorLayout();
1742 if (expectedLayout && currLayout && expectedLayout != currLayout) {
1744 auto conflictingCreateNdOp = getDefiningCreateNdDescOp(tdescValue);
1745 if (!conflictingCreateNdOp) {
1746 DBGS() <<
"Unable to find defining CreateNdDescOp for tensor descriptor: "
1747 << tdescValue <<
"\n";
1752 auto newTensorDescType = xegpu::TensorDescType::get(
1753 conflictingCreateNdOp.getContext(), currTDescType.getShape(),
1754 currTDescType.getElementType(), currTDescType.getEncoding(),
1756 xegpu::CreateNdDescOp newOp = xegpu::CreateNdDescOp::create(
1757 builder, consumerOp->
getLoc(), newTensorDescType,
1758 conflictingCreateNdOp->getOperands(),
1759 conflictingCreateNdOp->getAttrs());
1782 if (mlir::isa<mlir::RegionBranchOpInterface>(op))
1789 if (!isa<VectorType, xegpu::TensorDescType>(resultType))
1792 xegpu::DistributeLayoutAttr layout = getLayoutOfValue(
result);
1797 bool anyAssigned =
false;
1800 srcLayouts.push_back(srclayout);
1801 anyAssigned |= (srclayout !=
nullptr);
1808 if (!layout &&
result.getNumUses() > 0) {
1809 op->
emitWarning(
"op has users but no layout assigned for its result");
1813 if (
auto tensorDescTy = dyn_cast<xegpu::TensorDescType>(resultType)) {
1814 auto typeWithLayout = xegpu::TensorDescType::get(
1815 tensorDescTy.getContext(), tensorDescTy.getShape(),
1816 tensorDescTy.getElementType(), tensorDescTy.getEncoding(), layout);
1817 result.setType(typeWithLayout);
1829 mlir::FunctionOpInterface funcOp,
1835 if (!isa<FunctionType>(funcOp.getFunctionType()))
1840 Type argType = arg.getType();
1841 newArgTypes.push_back(argType);
1842 if (!isa<VectorType, xegpu::TensorDescType>(argType))
1844 xegpu::DistributeLayoutAttr layout = getLayoutOfValue(arg);
1846 LLVM_DEBUG(
DBGS() <<
"Expecting layout for function argument: " << arg
1847 <<
" but got none.\n");
1850 if (
auto tensorDescTy = dyn_cast<xegpu::TensorDescType>(argType)) {
1851 auto newTdescTy = xegpu::TensorDescType::get(
1852 tensorDescTy.getContext(), tensorDescTy.getShape(),
1853 tensorDescTy.getElementType(), tensorDescTy.getEncoding(), layout);
1854 arg.setType(newTdescTy);
1855 newArgTypes.back() = newTdescTy;
1860 funcOp.setType(FunctionType::get(funcOp.getContext(), newArgTypes,
1861 funcOp.getResultTypes()));
1866struct XeGPUPropagateLayoutPass final
1867 :
public xegpu::impl::XeGPUPropagateLayoutBase<XeGPUPropagateLayoutPass> {
1868 XeGPUPropagateLayoutPass() =
default;
1869 XeGPUPropagateLayoutPass(
const XeGPUPropagateLayoutPass &other) =
default;
1870 XeGPUPropagateLayoutPass(xegpu::XeGPUPropagateLayoutOptions
options)
1871 : XeGPUPropagateLayoutBase(std::move(
options)) {}
1872 void runOnOperation()
override;
1879 unsigned indexBitWidth,
bool printOnly) {
1880 RunLayoutInfoPropagation analysis(
target, layoutKind, indexBitWidth);
1883 auto &os = llvm::outs();
1884 analysis.printAnalysisResult(os);
1889 if (analysis.hasFailed())
1892 auto getLayoutFromPropagation =
1893 [&](
Value val) -> xegpu::DistributeLayoutAttr {
1894 LayoutInfo layout = analysis.getLayoutInfo(val);
1895 if (
auto opResult = dyn_cast<OpResult>(val)) {
1896 Operation *defOp = opResult.getDefiningOp();
1897 if (
auto anchorOp = dyn_cast<xegpu::AnchorLayoutInterface>(defOp)) {
1898 auto anchorLayout = anchorOp.getAnchorLayout();
1899 if (anchorLayout !=
nullptr)
1900 return anchorLayout;
1902 xegpu::DistributeLayoutAttr requiredResLayoutAttr =
1904 if (requiredResLayoutAttr !=
nullptr)
1905 return requiredResLayoutAttr;
1907 if (!layout.isAssigned())
1909 xegpu::DistributeLayoutAttr layoutAttr =
1910 cast<xegpu::DistributeLayoutAttr>(layout.get());
1911 if (layout.isSliceLayout())
1912 return cast<xegpu::SliceAttr>(layoutAttr);
1914 return cast<xegpu::LayoutAttr>(layoutAttr);
1922 .Case([&](mlir::RegionBranchTerminatorOpInterface branchTermOp) {
1924 branchTermOp, getLayoutFromPropagation);
1926 .Case([&](mlir::RegionBranchOpInterface branchOp) {
1928 getLayoutFromPropagation);
1930 .Case([&](mlir::FunctionOpInterface funcOp) {
1932 getLayoutFromPropagation);
1938 op.
emitError(
"Failed to update operation with the layout.");
1944 if (walkResult.wasInterrupted())
1951 ResolveLayoutConflicts resolver(
target);
1952 return resolver.run();
1955void XeGPUPropagateLayoutPass::runOnOperation() {
1960 if (this->layoutKind ==
"lane") {
1962 }
else if (this->layoutKind ==
"inst") {
1964 }
else if (this->layoutKind ==
"subgroup") {
1965 layoutKind = xegpu::LayoutKind::Subgroup;
1967 getOperation()->emitError(
"Unsupported layout kind option: " +
1969 signalPassFailure();
1974 this->indexBitWidth, this->printOnly))) {
1975 signalPassFailure();
1980 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.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
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.
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