30#include "llvm/ADT/SetVector.h"
31#include "llvm/Support/LogicalResult.h"
32#include "llvm/Support/raw_ostream.h"
37#define GEN_PASS_DEF_XEGPUSGTOLANEDISTRIBUTE
38#include "mlir/Dialect/XeGPU/Transforms/Passes.h.inc"
44#define DEBUG_TYPE "xegpu-sg-to-lane-distribute"
45#define DBGS() (llvm::dbgs() << "[" DEBUG_TYPE "]: ")
50static Value castValueTo(ConversionPatternRewriter &rewriter,
53 if (v.getType() == expectedTy)
56 if (isa<VectorType>(v.getType()) &&
57 v.getType().getNumElements() == expectedTy.getNumElements())
58 return vector::ShapeCastOp::create(rewriter, v.getLoc(), expectedTy, v);
61 auto newOp = UnrealizedConversionCastOp::create(rewriter, v.getLoc(),
63 return newOp.getResult(0);
69static bool isValidSubgroupMultiReductionOp(vector::MultiDimReductionOp op) {
72 if (!resLayout || !resLayout.isForSubgroup())
75 if (op.getType().isIntOrFloat())
76 return op.getReductionDims().size() == 1;
77 VectorType resTy = dyn_cast<VectorType>(op.getType());
81 FailureOr<VectorType> resDistTypeOrFailure =
82 getDistVecTypeBasedOnLaneLayout(resLayout, resTy);
83 if (failed(resDistTypeOrFailure))
85 return op.getReductionDims().size() == 1;
92static bool isReductionLaneLocal(vector::MultiDimReductionOp op) {
94 assert(isValidSubgroupMultiReductionOp(op) &&
"Expecting a valid subgroup "
95 "MultiDimReductionOp");
97 VectorType resTy = dyn_cast<VectorType>(op.getType());
98 auto resDistTypeOrFailure = getDistVecTypeBasedOnLaneLayout(resLayout, resTy);
99 return resTy != resDistTypeOrFailure.value();
105 VectorType distributedType) {
106 assert(originalType.getRank() == distributedType.getRank() &&
107 "original and distributed vector types must have the same rank");
109 for (
int64_t i = 0; i < originalType.getRank(); ++i) {
110 if (distributedType.getDimSize(i) != originalType.getDimSize(i))
111 distributedDims.push_back(i);
113 return distributedDims;
118struct SgToLaneCreateNdDesc
119 :
public OpConversionPattern<xegpu::CreateNdDescOp> {
120 using OpConversionPattern<xegpu::CreateNdDescOp>::OpConversionPattern;
123 matchAndRewrite(xegpu::CreateNdDescOp op, OpAdaptor adaptor,
124 ConversionPatternRewriter &rewriter)
const override {
125 xegpu::TensorDescType resultType = op.getType();
127 if (!resultType.getLayout())
130 auto newOp = xegpu::CreateNdDescOp::create(
131 rewriter, op.getLoc(),
TypeRange{resultType.dropLayouts()},
132 op.getOperands(), op.getProperties(),
133 op->getDiscardableAttrDictionary().getValue());
134 rewriter.replaceOp(op, newOp.getResult());
142struct SgToLaneLoadNd :
public OpConversionPattern<xegpu::LoadNdOp> {
143 using OpConversionPattern<xegpu::LoadNdOp>::OpConversionPattern;
146 matchAndRewrite(xegpu::LoadNdOp op, OpAdaptor adaptor,
147 ConversionPatternRewriter &rewriter)
const override {
148 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
154 if (op.getTensorDescType().getLayout() != layout)
155 return rewriter.notifyMatchFailure(
156 op,
"conflicting layout attributes on tensor descriptor and anchor");
160 return rewriter.notifyMatchFailure(
161 op,
"xegpu::LoadNdOp require target attribute attached to "
162 "determine transpose "
164 auto supportedLaneResultTyOrFailure =
166 auto expectedLaneResultTyOrFailure =
168 if (failed(supportedLaneResultTyOrFailure))
169 return rewriter.notifyMatchFailure(
170 op,
"unable to compute the lane vector type for LoadNdOp");
171 if (failed(expectedLaneResultTyOrFailure))
172 return rewriter.notifyMatchFailure(
173 op,
"unable to compute expected lane vector type from lane layout");
174 auto newOp = xegpu::LoadNdOp::create(
175 rewriter, op.getLoc(), supportedLaneResultTyOrFailure.value(),
176 adaptor.getTensorDesc(), op.getMixedOffsets(), op.getPackedAttr(),
177 op.getTransposeAttr(), op.getL1HintAttr(), op.getL2HintAttr(),
178 op.getL3HintAttr(),
nullptr);
184 rewriter.replaceOp(op, castValueTo(rewriter, newOp.getResult(),
185 expectedLaneResultTyOrFailure.value()));
193struct SgToLaneStoreNd :
public OpConversionPattern<xegpu::StoreNdOp> {
194 using OpConversionPattern<xegpu::StoreNdOp>::OpConversionPattern;
197 matchAndRewrite(xegpu::StoreNdOp op, OpAdaptor adaptor,
198 ConversionPatternRewriter &rewriter)
const override {
199 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
205 if (op.getTensorDescType().getLayout() != layout)
206 return rewriter.notifyMatchFailure(
207 op,
"conflicting layout attributes on tensor descriptor and anchor");
209 if (valueLayout != layout)
210 return rewriter.notifyMatchFailure(
211 op,
"conflicting layout attributes on value and anchor");
212 auto supportedLaneValueTyOrFailure =
214 if (failed(supportedLaneValueTyOrFailure))
215 return rewriter.notifyMatchFailure(
217 "unable to compute lane vector type for StoreNdOp value from tensor "
220 xegpu::StoreNdOp::create(
221 rewriter, op.getLoc(),
223 supportedLaneValueTyOrFailure.value()),
224 adaptor.getTensorDesc(), op.getMixedOffsets(), op.getL1HintAttr(),
225 op.getL2HintAttr(), op.getL3HintAttr(),
nullptr);
226 rewriter.eraseOp(op);
234struct SgToLaneDpas :
public OpConversionPattern<xegpu::DpasOp> {
235 using OpConversionPattern<xegpu::DpasOp>::OpConversionPattern;
238 matchAndRewrite(xegpu::DpasOp op, OpAdaptor adaptor,
239 ConversionPatternRewriter &rewriter)
const override {
241 auto layoutA = cast<xegpu::LayoutAttr>(op.getLayoutAAttr());
242 auto layoutB = cast<xegpu::LayoutAttr>(op.getLayoutBAttr());
243 auto layoutCd = cast<xegpu::LayoutAttr>(op.getLayoutCdAttr());
244 if (!layoutA || !layoutB || !layoutCd)
246 auto laneResultTyOrFailure =
248 auto laneATypeOrFailure =
250 auto laneBTypeOrFailure =
252 auto expectedLaneResultTyOrFailure =
254 if (failed(laneResultTyOrFailure) || failed(laneATypeOrFailure) ||
255 failed(laneBTypeOrFailure))
256 return rewriter.notifyMatchFailure(
257 op,
"failed to calculate supported lane vector types for DpasOp "
259 if (failed(expectedLaneResultTyOrFailure))
260 return rewriter.notifyMatchFailure(
261 op,
"unable to compute expected lane vector type for DpasOp from "
268 const auto *uArchInstruction =
269 dyn_cast<xegpu::uArch::SubgroupMatrixMultiplyAcc>(
270 uArch->getInstruction(
272 if (uArchInstruction) {
273 auto laneAType = laneATypeOrFailure.value();
274 auto laneBType = laneBTypeOrFailure.value();
276 unsigned aPackedBitWidth =
277 laneAType.getElementTypeBitWidth() * laneAType.getNumElements();
278 unsigned bPackedBitWidth =
279 laneBType.getElementTypeBitWidth() * laneBType.getNumElements();
280 unsigned expectedABitSize = uArchInstruction->getPackedFormatBitSizeA();
281 unsigned expectedBBitSize = uArchInstruction->getPackedFormatBitSizeB();
283 if (aPackedBitWidth % expectedABitSize != 0)
284 return rewriter.notifyMatchFailure(
286 "A operand packed bit width must be a multiple of uArch packed "
287 "format requirement");
288 if (bPackedBitWidth % expectedBBitSize != 0)
289 return rewriter.notifyMatchFailure(
291 "B operand packed bit width must be a multiple of uArch packed "
292 "format requirement");
296 auto newOp = xegpu::DpasOp::create(
297 rewriter, op->getLoc(), laneResultTyOrFailure.value(),
299 laneATypeOrFailure.value()),
301 laneBTypeOrFailure.value()),
303 laneResultTyOrFailure.value()),
307 rewriter.replaceOp(op, castValueTo(rewriter, newOp.getResult(),
308 expectedLaneResultTyOrFailure.value()));
321 ConversionPatternRewriter &rewriter)
const override {
328 return rewriter.notifyMatchFailure(
329 op,
"operation result is not a vector type");
331 xegpu::DistributeLayoutAttr layout =
333 if (!layout || !layout.isForSubgroup())
334 return rewriter.notifyMatchFailure(
335 op,
"operation result does not have subgroup distribute layout");
337 auto laneShapeOrFailure =
340 if (failed(laneShapeOrFailure))
341 return rewriter.notifyMatchFailure(
342 op,
"unable to compute lane vector type from the layout");
344 VectorType newResultType = laneShapeOrFailure.value();
350 if (!isa<xegpu::DistributeLayoutAttr>(attr.getValue()))
354 Operation *newOp = rewriter.create(state);
356 rewriter.replaceOp(op, newOp->
getResult(0));
368struct SgToLaneArithConstant :
public OpConversionPattern<arith::ConstantOp> {
369 using OpConversionPattern<arith::ConstantOp>::OpConversionPattern;
372 matchAndRewrite(arith::ConstantOp op, OpAdaptor adaptor,
373 ConversionPatternRewriter &rewriter)
const override {
374 auto resultType = dyn_cast<VectorType>(op.getType());
379 auto denseAttr = dyn_cast<DenseElementsAttr>(op.getValue());
381 return rewriter.notifyMatchFailure(
382 op,
"only dense vector constants are supported");
384 xegpu::DistributeLayoutAttr layout =
386 if (!layout || !layout.isForSubgroup())
387 return rewriter.notifyMatchFailure(
388 op,
"operation result does not have subgroup distribute layout");
390 auto laneShapeOrFailure =
393 if (failed(laneShapeOrFailure))
394 return rewriter.notifyMatchFailure(
395 op,
"unable to compute lane vector type from the layout");
397 VectorType newResultType = laneShapeOrFailure.value();
402 if (denseAttr.isSplat()) {
403 auto scalarValue = denseAttr.getSplatValue<
Attribute>();
406 arith::ConstantOp::create(rewriter, loc, newResultType, newDenseAttr);
407 rewriter.replaceOp(op, newOp.getResult());
414 arith::ConstantOp::create(rewriter, loc, resultType, denseAttr);
416 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
417 mlir::IntegerAttr());
418 auto maybeCoordsVec = layout.computeDistributedCoords(
419 rewriter, loc, laneId, resultType.getShape());
420 if (failed(maybeCoordsVec))
421 return rewriter.notifyMatchFailure(
422 op,
"failed to compute distributed coordinates from layout");
427 int64_t rank = newResultType.getRank();
433 for (
int64_t d = 0; d < rank; d++)
434 blockGridShape[d] = distShape[d] / laneData[d];
437 auto blockType = VectorType::get(laneData, newResultType.getElementType());
442 rewriter, loc, newResultType, rewriter.getZeroAttr(newResultType));
444 for (
auto [blockIdx, blockStart] : llvm::enumerate(coordsVec)) {
452 for (
int64_t d = 0; d < rank; d++)
454 rewriter, loc, blockStart[d],
456 blockElems.push_back(vector::ExtractOp::create(
457 rewriter, loc, fullConst.getResult(), pos));
464 vector::FromElementsOp::create(rewriter, loc, blockType, blockElems);
468 for (
int64_t d = 0; d < rank; d++)
469 offsets[d] = blockGridPos[d] * laneData[d];
470 result = vector::InsertStridedSliceOp::create(rewriter, loc, block,
471 result, offsets, strides);
474 rewriter.replaceOp(op,
result);
480struct SgToLanePrefetchNd :
public OpConversionPattern<xegpu::PrefetchNdOp> {
481 using OpConversionPattern<xegpu::PrefetchNdOp>::OpConversionPattern;
484 matchAndRewrite(xegpu::PrefetchNdOp op, OpAdaptor adaptor,
485 ConversionPatternRewriter &rewriter)
const override {
486 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
491 xegpu::PrefetchNdOp::create(rewriter, op.getLoc(), adaptor.getTensorDesc(),
492 op.getMixedOffsets(), op.getL1HintAttr(),
493 op.getL2HintAttr(), op.getL3HintAttr(),
495 rewriter.eraseOp(op);
533struct SgToLaneLoadGather :
public OpConversionPattern<xegpu::LoadGatherOp> {
534 using OpConversionPattern<xegpu::LoadGatherOp>::OpConversionPattern;
537 matchAndRewrite(xegpu::LoadGatherOp op, OpAdaptor adaptor,
538 ConversionPatternRewriter &rewriter)
const override {
539 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
543 VectorType origResultTy = op.getValueType();
548 int chunkSize = op.getChunkSize().value_or(1);
549 int effectiveVecRank = (chunkSize == 1) ? 1 : 2;
552 shape.take_front(origResultTy.getRank() - effectiveVecRank),
553 [](
int64_t d) { return d != 1; }))
554 return rewriter.notifyMatchFailure(
555 op,
"Only unit dimensions allowed for the leading "
556 "dimensions of the load vector!");
558 auto distResultTyOrFailure =
560 if (failed(distResultTyOrFailure))
561 return rewriter.notifyMatchFailure(
562 op,
"unable to compute expected lane vector type from lane layout");
564 VectorType distResultTy = distResultTyOrFailure.value();
565 VectorType distResultTy1D = VectorType::get({distResultTy.getNumElements()},
566 distResultTy.getElementType());
569 Value distOffsets = adaptor.getOffsets();
570 auto distOffsetsTy = cast<VectorType>(distOffsets.
getType());
571 VectorType offsetsTy1D = VectorType::get({distOffsetsTy.getNumElements()},
572 distOffsetsTy.getElementType());
573 distOffsets = castValueTo(
576 Value distMask = adaptor.getMask();
577 auto distMaskTy = cast<VectorType>(distMask.
getType());
578 VectorType maskTy1D = VectorType::get({distMaskTy.getNumElements()},
579 distMaskTy.getElementType());
583 Value distSource = adaptor.getSource();
584 auto newOp = xegpu::LoadGatherOp::create(
585 rewriter, op.getLoc(), distResultTy1D, distSource, distOffsets,
586 distMask, op.getChunkSizeAttr(), op.getL1HintAttr(), op.getL2HintAttr(),
587 op.getL3HintAttr(),
nullptr,
nullptr);
590 if (distResultTy1D != distResultTy)
593 rewriter.replaceOp(op,
result);
602struct SgToLaneVectorReduction
603 :
public OpConversionPattern<vector::ReductionOp> {
604 using OpConversionPattern<vector::ReductionOp>::OpConversionPattern;
607 matchAndRewrite(vector::ReductionOp op, OpAdaptor adaptor,
608 ConversionPatternRewriter &rewriter)
const override {
612 if (!layout || !layout.isForSubgroup())
615 VectorType srcVecType = op.getSourceVectorType();
617 if (srcVecType.getRank() != 1)
618 return rewriter.notifyMatchFailure(
619 op,
"Only rank 1 reductions can be distributed.");
621 if (layout.getRank() != srcVecType.getRank())
622 return rewriter.notifyMatchFailure(
623 op,
"Layout rank does not match vector rank.");
626 int64_t sgSize = layout.getEffectiveLaneLayoutAsInt()[0];
630 return rewriter.notifyMatchFailure(
631 op,
"xegpu::ReductionOp require target attribute attached to "
632 "determine subgroup size");
635 if (sgSize != uArch->getSubgroupSize() ||
636 srcVecType.getShape()[0] % sgSize != 0)
637 return rewriter.notifyMatchFailure(op,
638 "Invalid layout or reduction vector "
639 "dimension must match subgroup size.");
641 if (!op.getType().isIntOrFloat())
642 return rewriter.notifyMatchFailure(
643 op,
"Reduction distribution currently only supports floats and "
647 Value laneValVec = adaptor.getVector();
651 op.getLoc(), rewriter, laneValVec, op.getKind(), sgSize);
654 if (adaptor.getAcc())
656 rewriter, op.getLoc(), op.getKind(), fullReduce, adaptor.getAcc());
658 rewriter.replaceOp(op, fullReduce);
667struct SgToLaneMultiDimReduction
668 :
public OpConversionPattern<vector::MultiDimReductionOp> {
669 using OpConversionPattern<vector::MultiDimReductionOp>::OpConversionPattern;
672 matchAndRewrite(vector::MultiDimReductionOp op, OpAdaptor adaptor,
673 ConversionPatternRewriter &rewriter)
const override {
676 assert(reductionDims.size() == 1 &&
677 "Expecting single reduction dimension for subgroup multi "
680 VectorType sourceType = op.getSourceVectorType();
681 int64_t rank = sourceType.getRank();
684 if (llvm::any_of(
shape.take_front(rank - 2),
685 [](
int64_t d) { return d != 1; }))
686 return rewriter.notifyMatchFailure(
687 op,
"only unit leading dimensions are supported for "
688 "multi_reduction with rank > 2");
692 if (op.getType().isIntOrFloat()) {
693 auto reductionDim = reductionDims[0];
694 VectorType origSourceType = op.getSourceVectorType();
695 int64_t reductionDimSize = origSourceType.getShape()[reductionDim];
699 op.getKind(), reductionDimSize);
701 if (adaptor.getAcc())
703 result, adaptor.getAcc());
704 }
else if (isReductionLaneLocal(op)) {
708 auto reductionDim = reductionDims[0];
712 reductionDim, op.getLoc(), rewriter);
714 auto reductionDim = reductionDims[0];
715 VectorType sourceType = op.getSourceVectorType();
716 int64_t reductionDimSize = sourceType.getShape()[reductionDim];
720 reductionDim, reductionDimSize, op.getLoc(), rewriter);
722 rewriter.replaceOp(op,
result);
731 ConversionPatternRewriter &rewriter,
Location loc,
734 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
735 mlir::IntegerAttr());
737 layout.computeDistributedCoords(rewriter, loc, laneId, payloadShape);
738 if (failed(maybeCoords))
740 assert(maybeCoords.value().size() == 1 &&
741 "Expected one set of distributed offsets");
745 return llvm::map_to_vector(ofrVec, llvm::CastTo<Value>);
749struct SgToLaneLoadMatrix :
public OpConversionPattern<xegpu::LoadMatrixOp> {
750 using OpConversionPattern<xegpu::LoadMatrixOp>::OpConversionPattern;
753 matchAndRewrite(xegpu::LoadMatrixOp op, OpAdaptor adaptor,
754 ConversionPatternRewriter &rewriter)
const override {
755 auto layout = op.getLayoutAttr();
760 VectorType sgPayloadTy = dyn_cast<VectorType>(op.getResult().getType());
762 return rewriter.notifyMatchFailure(
763 op,
"the matrix op payload must be a vector type");
765 auto loc = op.getLoc();
766 auto offsets = op.getMixedOffsets();
768 return rewriter.notifyMatchFailure(op,
"the load op must have offsets");
770 FailureOr<VectorType> distPayloadTyOrFailure =
771 getDistVecTypeBasedOnLaneLayout(layout, sgPayloadTy);
772 if (failed(distPayloadTyOrFailure))
773 return rewriter.notifyMatchFailure(
774 op,
"Failed to distribute matrix op payload based on layout.");
780 if (!op.getSubgroupBlockIoAttr()) {
781 newCoords = computeDistributedCoordsForMatrixOp(
782 rewriter, loc, layout, sgPayloadTy.getShape(), offsetsAsValues);
783 if (newCoords.empty())
784 return rewriter.notifyMatchFailure(
785 op,
"Failed to compute distributed coordinates.");
789 ShapedType::kDynamic);
791 rewriter.getDenseI64ArrayAttr(newConstOffsets);
793 auto newOp = xegpu::LoadMatrixOp::create(
794 rewriter, loc, *distPayloadTyOrFailure, adaptor.getMemDesc(),
795 ValueRange(newCoords), newConstOffsetsAttr, op.getSubgroupBlockIoAttr(),
796 xegpu::DistributeLayoutAttr{});
797 rewriter.replaceOp(op, newOp.getResult());
803struct SgToLaneVectorTranspose
804 :
public OpConversionPattern<vector::TransposeOp> {
805 using OpConversionPattern<vector::TransposeOp>::OpConversionPattern;
808 matchAndRewrite(vector::TransposeOp op, OpAdaptor adaptor,
809 ConversionPatternRewriter &rewriter)
const override {
810 xegpu::DistributeLayoutAttr sourceLayout =
812 xegpu::DistributeLayoutAttr resultLayout =
814 if (!sourceLayout || !resultLayout)
815 return rewriter.notifyMatchFailure(
816 op,
"the source or result vector of the transpose op lacks layout "
820 if (!resultLayout.isTransposeOf(sourceLayout, perm,
822 return rewriter.notifyMatchFailure(
823 op,
"the source or result vector layouts must be transposes of "
825 FailureOr<VectorType> distributedResultTypeOrFailure =
826 getDistVecTypeBasedOnLaneLayout(resultLayout, op.getResultVectorType());
827 if (failed(distributedResultTypeOrFailure))
828 return rewriter.notifyMatchFailure(
829 op,
"Failed to distribute the result vector type in "
830 "vector::Transpose op");
831 auto newOp = vector::TransposeOp::create(rewriter, op.getLoc(),
832 adaptor.getVector(), perm);
833 rewriter.replaceOp(op, castValueTo(rewriter, newOp.getResult(),
834 distributedResultTypeOrFailure.value()));
841struct SgToLaneVectorBitcast :
public OpConversionPattern<vector::BitCastOp> {
842 using OpConversionPattern<vector::BitCastOp>::OpConversionPattern;
845 matchAndRewrite(vector::BitCastOp op, OpAdaptor adaptor,
846 ConversionPatternRewriter &rewriter)
const override {
847 xegpu::DistributeLayoutAttr resultLayout =
850 return rewriter.notifyMatchFailure(
851 op,
"result vector of the bitcast op lacks layout attribute");
852 FailureOr<VectorType> distributedResultTypeOrFailure =
853 getDistVecTypeBasedOnLaneLayout(resultLayout, op.getResultVectorType());
854 if (failed(distributedResultTypeOrFailure))
855 return rewriter.notifyMatchFailure(
856 op,
"Failed to distribute the result vector type in "
857 "vector::BitCast op");
858 auto newOp = vector::BitCastOp::create(
859 rewriter, op.getLoc(), distributedResultTypeOrFailure.value(),
860 adaptor.getSource());
861 rewriter.replaceOp(op, newOp.getResult());
889template <
typename OpType,
890 typename = std::enable_if_t<llvm::is_one_of<
891 OpType, vector::CreateMaskOp, vector::ConstantMaskOp>::value>>
892struct SgToLaneCreateMask :
public OpConversionPattern<OpType> {
893 using OpConversionPattern<OpType>::OpConversionPattern;
896 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
897 ConversionPatternRewriter &rewriter)
const override {
898 xegpu::DistributeLayoutAttr layout =
900 if (!layout || !layout.isForSubgroup())
901 return rewriter.notifyMatchFailure(
902 op,
"operation result does not have subgroup distribute layout");
904 VectorType origType = op.getType();
905 FailureOr<VectorType> distTypeOrFailure =
906 getDistVecTypeBasedOnLaneLayout(layout, origType);
907 if (failed(distTypeOrFailure))
908 return rewriter.notifyMatchFailure(
909 op,
"unable to compute lane vector type from the layout");
911 VectorType distType = distTypeOrFailure.value();
916 if constexpr (std::is_same_v<OpType, vector::CreateMaskOp>) {
917 origBounds.append(op.getOperands().begin(), op.getOperands().end());
919 auto dimSizes = op.getMaskDimSizesAttr().asArrayRef();
920 for (
auto dimSize : dimSizes)
921 origBounds.push_back(
928 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
929 mlir::IntegerAttr());
930 auto maybeCoordsVec =
931 layout.computeDistributedCoords(rewriter, loc, laneId, origShape);
932 if (failed(maybeCoordsVec))
933 return rewriter.notifyMatchFailure(
934 op,
"failed to compute distributed coordinates from layout");
937 int64_t numElements = distType.getNumElements();
938 assert(
static_cast<int64_t>(coordsVec.size()) == numElements &&
939 "number of coordinate sets must match number of distributed "
946 for (
auto &coords : coordsVec) {
947 Value inBounds = trueVal;
948 for (
size_t i = 0; i < coords.size(); ++i) {
949 Value cmp = arith::CmpIOp::create(
950 rewriter, loc, arith::CmpIPredicate::slt, coords[i], origBounds[i]);
951 inBounds = arith::AndIOp::create(rewriter, loc, inBounds, cmp);
953 maskBits.push_back(inBounds);
958 if (numElements == 1) {
960 vector::BroadcastOp::create(rewriter, loc, distType, maskBits[0]);
963 vector::FromElementsOp::create(rewriter, loc, distType, maskBits);
965 rewriter.replaceOp(op,
result);
971struct SgToLaneStoreMatrix :
public OpConversionPattern<xegpu::StoreMatrixOp> {
972 using OpConversionPattern<xegpu::StoreMatrixOp>::OpConversionPattern;
975 matchAndRewrite(xegpu::StoreMatrixOp op, OpAdaptor adaptor,
976 ConversionPatternRewriter &rewriter)
const override {
977 auto layout = op.getLayoutAttr();
982 VectorType sgPayloadTy = dyn_cast<VectorType>(op.getData().getType());
984 return rewriter.notifyMatchFailure(
985 op,
"the matrix op payload must be a vector type");
987 auto loc = op.getLoc();
988 auto offsets = op.getMixedOffsets();
990 return rewriter.notifyMatchFailure(op,
"the store op must have offsets");
992 FailureOr<VectorType> distPayloadTyOrFailure =
993 getDistVecTypeBasedOnLaneLayout(layout, sgPayloadTy);
994 if (failed(distPayloadTyOrFailure))
995 return rewriter.notifyMatchFailure(
996 op,
"Failed to distribute matrix op payload based on layout.");
1002 if (!op.getSubgroupBlockIoAttr()) {
1003 newCoords = computeDistributedCoordsForMatrixOp(
1004 rewriter, loc, layout, sgPayloadTy.getShape(), offsetsAsValues);
1005 if (newCoords.empty())
1006 return rewriter.notifyMatchFailure(
1007 op,
"Failed to compute distributed coordinates.");
1011 ShapedType::kDynamic);
1013 rewriter.getDenseI64ArrayAttr(newConstOffsets);
1015 xegpu::StoreMatrixOp::create(
1018 distPayloadTyOrFailure.value()),
1019 adaptor.getMemDesc(),
ValueRange(newCoords), newConstOffsetsAttr,
1020 op.getSubgroupBlockIoAttr(), xegpu::DistributeLayoutAttr{});
1021 rewriter.eraseOp(op);
1060struct SgToLaneStoreScatter
1061 :
public OpConversionPattern<xegpu::StoreScatterOp> {
1062 using OpConversionPattern<xegpu::StoreScatterOp>::OpConversionPattern;
1065 matchAndRewrite(xegpu::StoreScatterOp op, OpAdaptor adaptor,
1066 ConversionPatternRewriter &rewriter)
const override {
1067 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
1071 VectorType origValueTy = op.getValueType();
1076 int chunkSize = op.getChunkSize().value_or(1);
1077 int effectiveVecRank = (chunkSize == 1) ? 1 : 2;
1079 if (llvm::any_of(
shape.take_front(origValueTy.getRank() - effectiveVecRank),
1080 [](
int64_t d) { return d != 1; }))
1081 return rewriter.notifyMatchFailure(
1082 op,
"Only unit dimensions allowed for the leading "
1083 "dimensions of the store vector!");
1085 auto distValueTyOrFailure =
1087 if (failed(distValueTyOrFailure))
1088 return rewriter.notifyMatchFailure(
1089 op,
"unable to compute expected lane vector type from lane layout");
1091 VectorType distValueTy = distValueTyOrFailure.value();
1092 VectorType distValueTy1D = VectorType::get({distValueTy.getNumElements()},
1093 distValueTy.getElementType());
1095 Value distValue = adaptor.getValue();
1096 if (distValue.
getType() != distValueTy1D)
1101 Value distOffsets = adaptor.getOffsets();
1102 auto distOffsetsTy = cast<VectorType>(distOffsets.
getType());
1103 VectorType offsetsTy1D = VectorType::get({distOffsetsTy.getNumElements()},
1104 distOffsetsTy.getElementType());
1105 distOffsets = castValueTo(
1108 Value distMask = adaptor.getMask();
1109 auto distMaskTy = cast<VectorType>(distMask.
getType());
1110 VectorType maskTy1D = VectorType::get({distMaskTy.getNumElements()},
1111 distMaskTy.getElementType());
1115 Value distDest = adaptor.getDest();
1116 xegpu::StoreScatterOp::create(rewriter, op.getLoc(), distValue, distDest,
1117 distOffsets, distMask, op.getChunkSizeAttr(),
1118 op.getL1HintAttr(), op.getL2HintAttr(),
1119 op.getL3HintAttr(),
nullptr,
1121 rewriter.eraseOp(op);
1130struct SgToLaneVectorStep :
public OpConversionPattern<vector::StepOp> {
1131 using OpConversionPattern<vector::StepOp>::OpConversionPattern;
1134 matchAndRewrite(vector::StepOp op, OpAdaptor adaptor,
1135 ConversionPatternRewriter &rewriter)
const override {
1136 xegpu::DistributeLayoutAttr resultLayout =
1138 if (!resultLayout || !resultLayout.isForSubgroup())
1139 return rewriter.notifyMatchFailure(
1140 op,
"the result vector of the step op lacks subgroup layout");
1142 auto loc = op.getLoc();
1143 auto stepResultVecTy = op.getResult().getType();
1144 auto laneShapeOrFailure =
1146 if (failed(laneShapeOrFailure))
1147 return rewriter.notifyMatchFailure(
1148 op,
"unable to compute lane vector type from the layout");
1149 VectorType newVecTy = laneShapeOrFailure.value();
1151 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
1152 mlir::IntegerAttr());
1153 auto laneDataBlockCoords = resultLayout.computeDistributedCoords(
1154 rewriter, loc, laneId, stepResultVecTy.getShape());
1155 if (failed(laneDataBlockCoords))
1156 return rewriter.notifyMatchFailure(
1157 op,
"failed to compute lane data block coordinates");
1159 auto laneDataBlockCoordsVec = laneDataBlockCoords.value();
1160 auto laneDataBlockLength = resultLayout.getEffectiveLaneDataAsInt()[0];
1161 assert(
static_cast<int64_t>(laneDataBlockCoordsVec.size()) ==
1162 newVecTy.getNumElements() / laneDataBlockLength);
1171 for (
auto &laneDataBlockCoords : laneDataBlockCoordsVec) {
1172 auto laneDataBlockStartCoord = laneDataBlockCoords[0];
1173 stepVals.push_back(laneDataBlockStartCoord);
1174 for (
int i = 1; i < laneDataBlockLength; ++i) {
1176 stepVals.push_back(arith::AddIOp::create(
1177 rewriter, loc, laneDataBlockStartCoord, offset));
1180 assert(
static_cast<int64_t>(stepVals.size()) == newVecTy.getNumElements() &&
1181 "Expecting the number of step values to match the number of "
1182 "elements in the vector");
1184 vector::FromElementsOp::create(rewriter, loc, newVecTy, stepVals);
1185 rewriter.replaceOp(op, stepOpVal);
1192struct SgToLaneVectorExtract :
public OpConversionPattern<vector::ExtractOp> {
1193 using OpConversionPattern<vector::ExtractOp>::OpConversionPattern;
1196 matchAndRewrite(vector::ExtractOp op, OpAdaptor adaptor,
1197 ConversionPatternRewriter &rewriter)
const override {
1199 auto resultType = dyn_cast<VectorType>(op.getType());
1201 return rewriter.notifyMatchFailure(op,
"scalar extract not supported");
1203 xegpu::DistributeLayoutAttr layout =
1205 if (!layout || !layout.isForSubgroup())
1210 auto laneLayout = layout.getEffectiveLaneLayoutAsInt();
1212 [](
int64_t v) {
return v != 1; }))
1213 return rewriter.notifyMatchFailure(
1214 op,
"only innermost dimension distribution is supported for "
1217 auto newOp = vector::ExtractOp::create(
1218 rewriter, op.getLoc(), adaptor.getSource(), op.getMixedPosition());
1219 rewriter.replaceOp(op, newOp.getResult());
1225struct SgToLaneVectorShapeCast
1226 :
public OpConversionPattern<vector::ShapeCastOp> {
1227 using OpConversionPattern<vector::ShapeCastOp>::OpConversionPattern;
1230 matchAndRewrite(vector::ShapeCastOp op, OpAdaptor adaptor,
1231 ConversionPatternRewriter &rewriter)
const override {
1232 xegpu::DistributeLayoutAttr resultLayout =
1234 if (!resultLayout || !resultLayout.isForSubgroup())
1235 return rewriter.notifyMatchFailure(
1236 op,
"the result vector of the shape_cast op lacks subgroup layout");
1239 resultLayout, op.getResultVectorType());
1240 if (failed(resultDistTypeOrFailure))
1241 return rewriter.notifyMatchFailure(
1242 op,
"failed to get distributed vector type for result");
1244 Value source = adaptor.getSource();
1245 auto newShapeCast = vector::ShapeCastOp::create(
1246 rewriter, op.getLoc(), resultDistTypeOrFailure.value(), source);
1247 rewriter.replaceOp(op, newShapeCast);
1255struct SgToLaneVectorExtractStridedSlice
1256 :
public OpConversionPattern<vector::ExtractStridedSliceOp> {
1257 using OpConversionPattern<vector::ExtractStridedSliceOp>::OpConversionPattern;
1260 matchAndRewrite(vector::ExtractStridedSliceOp op, OpAdaptor adaptor,
1261 ConversionPatternRewriter &rewriter)
const override {
1262 xegpu::DistributeLayoutAttr resultLayout =
1264 if (!resultLayout || !resultLayout.isForSubgroup())
1267 VectorType resultType = op.getType();
1268 auto distResultTyOrFailure =
1270 if (failed(distResultTyOrFailure))
1271 return rewriter.notifyMatchFailure(
1272 op,
"unable to compute distributed vector type from lane layout");
1273 VectorType distResultTy = *distResultTyOrFailure;
1276 getDistributedDims(resultType, distResultTy);
1279 int64_t sourceRank = op.getSourceVectorType().getRank();
1281 llvm::map_to_vector(op.getSizes(), [](
Attribute attr) { return attr; });
1283 op.getOffsets(), [](
Attribute attr) { return attr; });
1285 op.getStrides(), [](
Attribute attr) { return attr; });
1286 for (
int64_t i = op.getSizes().size(); i < sourceRank; ++i) {
1287 updatedSizes.push_back(
1288 rewriter.getI64IntegerAttr(op.getSourceVectorType().getDimSize(i)));
1289 updatedOffsets.push_back(rewriter.getI64IntegerAttr(0));
1290 updatedStrides.push_back(rewriter.getI64IntegerAttr(1));
1295 if (!distributedDims.empty()) {
1296 if (distributedDims.size() != 1)
1297 return rewriter.notifyMatchFailure(
1298 op,
"only single dimension distribution is supported");
1299 int64_t distDim = distributedDims[0];
1303 return rewriter.notifyMatchFailure(
1304 op,
"target attribute required to determine subgroup size");
1305 int subgroupSize = uArch->getSubgroupSize();
1307 if (!sourceLayout || sourceLayout.getEffectiveLaneLayoutAsInt().empty())
1308 return rewriter.notifyMatchFailure(
1309 op,
"source of extract_strided_slice lacks distribution layout");
1310 int sourceDistrDimSize = op.getSourceVectorType().getShape()[distDim];
1311 auto laneLayout = sourceLayout.getEffectiveLaneLayoutAsInt();
1314 if (laneLayout[distDim] < subgroupSize &&
1315 subgroupSize % laneLayout[distDim] == 0)
1316 subgroupSize = laneLayout[distDim];
1317 if (sourceDistrDimSize % subgroupSize != 0)
1318 return rewriter.notifyMatchFailure(
1319 op,
"source size along distributed dim is not a multiple of "
1321 auto sourceLaneData = sourceLayout.getEffectiveLaneDataAsInt();
1324 if (distDim <
static_cast<int64_t>(sourceLaneData.size()) &&
1325 sourceLaneData[distDim] != 1)
1326 return rewriter.notifyMatchFailure(
1327 op,
"expecting unit lane data along the distributed dimension");
1329 cast<IntegerAttr>(updatedOffsets[distDim]).getInt();
1330 if (distrDimOffset % subgroupSize != 0)
1331 return rewriter.notifyMatchFailure(
1332 op,
"offset along distributed dim is not a multiple of "
1335 updatedSizes[distDim] =
1336 rewriter.getI64IntegerAttr(distResultTy.getDimSize(distDim));
1337 updatedOffsets[distDim] =
1338 rewriter.getI64IntegerAttr(distrDimOffset / subgroupSize);
1341 auto newOp = vector::ExtractStridedSliceOp::create(
1342 rewriter, op.getLoc(), distResultTy, adaptor.getSource(),
1343 ArrayAttr::get(rewriter.getContext(), updatedOffsets),
1344 ArrayAttr::get(rewriter.getContext(), updatedSizes),
1345 ArrayAttr::get(rewriter.getContext(), updatedStrides));
1346 rewriter.replaceOp(op, newOp.getResult());
1408struct SgToLaneBroadcast :
public OpConversionPattern<vector::BroadcastOp> {
1409 using OpConversionPattern<vector::BroadcastOp>::OpConversionPattern;
1412 matchAndRewrite(vector::BroadcastOp op, OpAdaptor adaptor,
1413 ConversionPatternRewriter &rewriter)
const override {
1414 xegpu::DistributeLayoutAttr resultLayout =
1416 if (!resultLayout || !resultLayout.isForSubgroup())
1417 return rewriter.notifyMatchFailure(
1418 op,
"result does not have subgroup distribute layout");
1420 VectorType destType = op.getResultVectorType();
1421 VectorType sourceType = dyn_cast<VectorType>(op.getSourceType());
1423 xegpu::DistributeLayoutAttr sourceLayout =
1427 int64_t rankDiff = destType.getRank() - sourceType.getRank();
1430 if (!sourceLayout || !sourceLayout.isSliceOf(resultLayout))
1432 "broadcast source layout must be a slice of result layout");
1433 }
else if (rankDiff == 0) {
1435 auto broadcastUnitDimsSet = op.computeBroadcastedUnitDims();
1437 broadcastUnitDimsSet.end());
1438 assert(sourceLayout.isEqualTo(
1439 sourceLayout.setUnitDimData(broadcastUnitDims)) &&
1440 "The sg_data for unit dimensions should be set as 1");
1441 sourceLayout = sourceLayout.setUnitDimLayout(broadcastUnitDims);
1446 return rewriter.notifyMatchFailure(
1447 op,
"broadcast from scalar must not have a layout attribute");
1452 if (failed(destDistType))
1453 return rewriter.notifyMatchFailure(
1454 op,
"failed to distribute the result vector type");
1456 Value source = adaptor.getSource();
1458 if (source.
getType() == destDistType.value()) {
1459 rewriter.replaceOp(op, source);
1463 auto newOp = vector::BroadcastOp::create(rewriter, op.getLoc(),
1464 destDistType.value(), source);
1465 rewriter.replaceOp(op, newOp);
1473struct SgToLaneVectorInsertStridedSlice
1474 :
public OpConversionPattern<vector::InsertStridedSliceOp> {
1475 using OpConversionPattern<vector::InsertStridedSliceOp>::OpConversionPattern;
1478 matchAndRewrite(vector::InsertStridedSliceOp op, OpAdaptor adaptor,
1479 ConversionPatternRewriter &rewriter)
const override {
1480 xegpu::DistributeLayoutAttr resultLayout =
1482 if (!resultLayout || !resultLayout.isForSubgroup())
1485 VectorType destType = op.getDestVectorType();
1486 auto distDestTyOrFailure =
1488 if (failed(distDestTyOrFailure))
1489 return rewriter.notifyMatchFailure(
1490 op,
"unable to compute distributed vector type from lane layout");
1491 VectorType distDestTy = *distDestTyOrFailure;
1494 getDistributedDims(destType, distDestTy);
1497 op.getOffsets(), [](
Attribute attr) { return attr; });
1499 if (!destDistributedDims.empty()) {
1500 if (destDistributedDims.size() != 1)
1501 return rewriter.notifyMatchFailure(
1502 op,
"only single dimension distribution is supported");
1503 int64_t destDistDim = destDistributedDims[0];
1508 return rewriter.notifyMatchFailure(
1509 op,
"target attribute required to determine subgroup size");
1510 int subgroupSize = uArch->getSubgroupSize();
1512 VectorType srcType = op.getSourceVectorType();
1515 destDistDim - (destType.getRank() - srcType.getRank());
1516 if (sourceDistDim < 0)
1517 return rewriter.notifyMatchFailure(
1518 op,
"distributed dimension must be in the last k dims of dest");
1522 if (!destLayout || !sourceLayout ||
1523 destLayout.getEffectiveLaneLayoutAsInt().empty() ||
1524 sourceLayout.getEffectiveLaneLayoutAsInt().empty())
1525 return rewriter.notifyMatchFailure(
1526 op,
"source or dest of insert_strided_slice lacks distribution "
1529 auto destLaneData = destLayout.getEffectiveLaneDataAsInt();
1530 auto sourceLaneData = sourceLayout.getEffectiveLaneDataAsInt();
1533 if ((destDistDim <
static_cast<int64_t>(destLaneData.size()) &&
1534 destLaneData[destDistDim] != 1) ||
1535 (sourceDistDim <
static_cast<int64_t>(sourceLaneData.size()) &&
1536 sourceLaneData[sourceDistDim] != 1))
1537 return rewriter.notifyMatchFailure(
1538 op,
"expecting unit lane data along the distributed dimension");
1540 int64_t srcDistrDimSize = srcType.getDimSize(sourceDistDim);
1541 if (srcDistrDimSize % subgroupSize != 0)
1542 return rewriter.notifyMatchFailure(
1543 op,
"source distributed dim size is not a multiple of "
1547 cast<IntegerAttr>(op.getOffsets()[destDistDim]).getInt();
1548 if (destDistrDimOffset % subgroupSize != 0)
1549 return rewriter.notifyMatchFailure(
1550 op,
"offset along distributed dim is not a multiple of "
1553 updatedOffsets[destDistDim] =
1554 rewriter.getI64IntegerAttr(destDistrDimOffset / subgroupSize);
1557 auto newOp = vector::InsertStridedSliceOp::create(
1558 rewriter, op.getLoc(), distDestTy, adaptor.getValueToStore(),
1560 ArrayAttr::get(rewriter.getContext(), updatedOffsets), op.getStrides());
1561 rewriter.replaceOp(op, newOp.getResult());
1568struct SgToLaneVectorInsert :
public OpConversionPattern<vector::InsertOp> {
1569 using OpConversionPattern<vector::InsertOp>::OpConversionPattern;
1572 matchAndRewrite(vector::InsertOp op, OpAdaptor adaptor,
1573 ConversionPatternRewriter &rewriter)
const override {
1575 auto valueType = dyn_cast<VectorType>(op.getValueToStoreType());
1577 return rewriter.notifyMatchFailure(op,
"scalar insert not supported");
1579 xegpu::DistributeLayoutAttr layout =
1581 if (!layout || !layout.isForSubgroup())
1586 auto laneLayout = layout.getEffectiveLaneLayoutAsInt();
1588 [](
int64_t v) {
return v != 1; }))
1589 return rewriter.notifyMatchFailure(
1590 op,
"only innermost dimension distribution is supported for "
1593 auto newOp = vector::InsertOp::create(
1594 rewriter, op.getLoc(), adaptor.getValueToStore(), adaptor.getDest(),
1595 op.getMixedPosition());
1596 rewriter.replaceOp(op, newOp.getResult());
1614static FailureOr<Value>
1615shuffleDataAsLaneLayoutChange(ConversionPatternRewriter &rewriter,
Location loc,
1618 VectorType srcTy = dyn_cast<VectorType>(src.
getType());
1619 if (!srcTy || srcTy.getRank() != 2)
1622 if (targetLaneNum <= 0 || currentLaneNum != targetLaneNum * 2)
1626 srcTy.getNumElements() * srcTy.getElementTypeBitWidth();
1627 if (vectorBitWidth % 32 != 0)
1639 Type shuffleElemTy = rewriter.getI32Type();
1640 int64_t numShuffles = vectorBitWidth / 32;
1641 VectorType shuffleBundleTy = VectorType::get({numShuffles}, shuffleElemTy);
1643 Value temp = arith::ConstantOp::create(
1646 IntegerAttr::get(shuffleElemTy, 0)));
1647 VectorType flatSrcTy =
1648 VectorType::get({srcTy.getNumElements()}, srcTy.getElementType());
1649 Value flatSrc = vector::ShapeCastOp::create(rewriter, loc, flatSrcTy, src);
1650 Value shuffleBundle =
1651 vector::BitCastOp::create(rewriter, loc, shuffleBundleTy, flatSrc);
1652 for (
int64_t i = 0; i < numShuffles; i++) {
1654 vector::ExtractOp::create(rewriter, loc, shuffleBundle, i);
1655 shuffleElem = gpu::ShuffleOp::create(rewriter, loc, shuffleElem, 0,
1656 targetLaneNum, gpu::ShuffleMode::UP)
1658 temp = vector::InsertOp::create(rewriter, loc, shuffleElem, temp, i);
1660 temp = vector::BitCastOp::create(rewriter, loc, flatSrcTy, temp);
1661 temp = vector::ShapeCastOp::create(rewriter, loc, srcTy, temp);
1666 Value res = vector::ShuffleOp::create(rewriter, loc, src, temp,
indices);
1677static FailureOr<Value> repackLaneData(ConversionPatternRewriter &rewriter,
1681 auto srcTy = dyn_cast<VectorType>(src.
getType());
1684 int64_t rank = srcTy.getRank();
1685 Type elemTy = srcTy.getElementType();
1686 int64_t k = srcTy.getShape()[repackDim];
1688 bool roundRobinToContig = inputData == 1 && targetData == k;
1689 bool contigToRoundRobin = inputData == k && targetData == 1;
1690 if (!roundRobinToContig && !contigToRoundRobin)
1695 xegpu::LaneShuffleMode mode = roundRobinToContig
1696 ? xegpu::LaneShuffleMode::Pack
1697 : xegpu::LaneShuffleMode::Unpack;
1698 VectorType runTy = VectorType::get({k}, elemTy);
1702 if (srcTy.getNumElements() == k) {
1705 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, src, mode));
1706 Value flat = vector::ShapeCastOp::create(rewriter, loc, runTy, src);
1708 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, flat, mode);
1709 return Value(vector::ShapeCastOp::create(rewriter, loc, srcTy, shuffled));
1714 if (repackDim == rank - 1) {
1718 Value result = arith::ConstantOp::create(rewriter, loc, srcTy,
1719 rewriter.getZeroAttr(srcTy));
1720 for (
int64_t i = 0; i < numRuns; ++i) {
1722 Value run = vector::ExtractOp::create(rewriter, loc, src, pos);
1724 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, run, mode);
1725 result = vector::InsertOp::create(rewriter, loc, shuffled,
result, pos);
1735 for (
int64_t d = 0; d < rank; ++d)
1736 if (d != repackDim) {
1737 keptShape.push_back(srcTy.getShape()[d]);
1738 keptDims.push_back(d);
1743 sliceSizes[repackDim] = k;
1745 VectorType sliceTy = VectorType::get(sliceSizes, elemTy);
1746 Value result = arith::ConstantOp::create(rewriter, loc, srcTy,
1747 rewriter.getZeroAttr(srcTy));
1748 for (
int64_t i = 0; i < numRuns; ++i) {
1751 for (
auto [dim, coord] : llvm::zip_equal(keptDims, keptPos))
1752 offsets[dim] = coord;
1753 Value slice = vector::ExtractStridedSliceOp::create(
1754 rewriter, loc, src, offsets, sliceSizes, sliceStrides);
1755 Value run = vector::ShapeCastOp::create(rewriter, loc, runTy, slice);
1757 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, run, mode);
1758 Value repackedSlice =
1759 vector::ShapeCastOp::create(rewriter, loc, sliceTy, repacked);
1760 result = vector::InsertStridedSliceOp::create(
1761 rewriter, loc, repackedSlice,
result, offsets, sliceStrides);
1767struct SgToLaneConvertLayout
1768 :
public OpConversionPattern<xegpu::ConvertLayoutOp> {
1769 using OpConversionPattern<xegpu::ConvertLayoutOp>::OpConversionPattern;
1772 matchAndRewrite(xegpu::ConvertLayoutOp op, OpAdaptor adaptor,
1773 ConversionPatternRewriter &rewriter)
const override {
1774 auto inputLayout = op.getEffectiveInputLayout();
1775 auto targetLayout = op.getTargetLayoutAttr();
1776 Type valType = op.getResult().getType();
1779 rewriter.replaceOp(op, op.getSource());
1783 auto resShape = cast<VectorType>(valType).getShape();
1788 if (inputLayout.isCompatibleWith(targetLayout, resShapeVec,
1790 rewriter.replaceOp(op, adaptor.getSource());
1801 if (inputLayout.getEffectiveOrderAsInt() ==
1802 targetLayout.getEffectiveOrderAsInt() &&
1803 inputLayout.getRank() == 2 && targetLayout.getRank() == 2) {
1804 auto laneLayout = inputLayout.getEffectiveLaneLayoutAsInt();
1805 auto targetLaneLayout = targetLayout.getEffectiveLaneLayoutAsInt();
1806 auto laneData = inputLayout.getEffectiveLaneDataAsInt();
1807 auto targetLaneData = targetLayout.getEffectiveLaneDataAsInt();
1808 if (laneLayout.size() == 2 && targetLaneLayout.size() == 2 &&
1809 laneData == targetLaneData && laneLayout[1] == 1 &&
1810 targetLaneLayout[1] == 1 && laneLayout[0] > 1 &&
1811 laneLayout[0] != targetLaneLayout[0]) {
1812 FailureOr<Value> res = shuffleDataAsLaneLayoutChange(
1813 rewriter, op.getLoc(), adaptor.getSource(), laneLayout[0],
1814 targetLaneLayout[0]);
1815 if (succeeded(res)) {
1816 rewriter.replaceOp(op, *res);
1828 if (inputLayout.getEffectiveOrderAsInt() ==
1829 targetLayout.getEffectiveOrderAsInt() &&
1830 inputLayout.getEffectiveLaneLayoutAsInt() ==
1831 targetLayout.getEffectiveLaneLayoutAsInt()) {
1832 auto laneLayout = inputLayout.getEffectiveLaneLayoutAsInt();
1833 auto laneData = inputLayout.getEffectiveLaneDataAsInt();
1834 auto targetLaneData = targetLayout.getEffectiveLaneDataAsInt();
1837 int64_t rank = laneData.size();
1839 bool multipleChanged =
false;
1840 for (
int64_t d = 0; d < rank; ++d)
1841 if (laneData[d] != targetLaneData[d]) {
1842 if (repackDim != -1)
1843 multipleChanged =
true;
1849 int64_t otherDim = repackDim == rank - 1 ? rank - 2 : rank - 1;
1850 bool laneLayoutOk = repackDim != -1 && laneLayout[repackDim] != 1 &&
1851 (rank < 2 || laneLayout[otherDim] == 1);
1855 if (repackDim != -1 && repackDim >= rank - 2 && !multipleChanged &&
1857 FailureOr<Value> res = repackLaneData(
1858 rewriter, op.getLoc(), adaptor.getSource(), repackDim,
1859 laneData[repackDim], targetLaneData[repackDim]);
1860 if (succeeded(res)) {
1861 rewriter.replaceOp(op, *res);
1867 return rewriter.notifyMatchFailure(
1868 op,
"lowering incompatible convert_layout not yet supported");
1873struct SgToLaneVectorInterleave
1874 :
public OpConversionPattern<vector::InterleaveOp> {
1875 using OpConversionPattern<vector::InterleaveOp>::OpConversionPattern;
1878 matchAndRewrite(vector::InterleaveOp op, OpAdaptor adaptor,
1879 ConversionPatternRewriter &rewriter)
const override {
1881 auto newOp = vector::InterleaveOp::create(
1882 rewriter, op.getLoc(), adaptor.getLhs(), adaptor.getRhs());
1883 rewriter.replaceOp(op, newOp.getResult());
1889struct SgToLaneVectorDeinterleave
1890 :
public OpConversionPattern<vector::DeinterleaveOp> {
1891 using OpConversionPattern<vector::DeinterleaveOp>::OpConversionPattern;
1894 matchAndRewrite(vector::DeinterleaveOp op, OpAdaptor adaptor,
1895 ConversionPatternRewriter &rewriter)
const override {
1897 auto newOp = vector::DeinterleaveOp::create(rewriter, op.getLoc(),
1898 adaptor.getSource());
1899 rewriter.replaceOp(op, newOp.getResults());
1904struct SgToLaneDpasMx :
public OpConversionPattern<xegpu::DpasMxOp> {
1905 using OpConversionPattern<xegpu::DpasMxOp>::OpConversionPattern;
1908 matchAndRewrite(xegpu::DpasMxOp op, OpAdaptor adaptor,
1909 ConversionPatternRewriter &rewriter)
const override {
1914 if (!uArch->isSupportedInstruction(
1916 return rewriter.notifyMatchFailure(
1917 op,
"target uArch does not support scaled subgroup mma");
1919 auto layoutA = cast<xegpu::LayoutAttr>(op.getLayoutAAttr());
1920 auto layoutB = cast<xegpu::LayoutAttr>(op.getLayoutBAttr());
1921 auto layoutCd = cast<xegpu::LayoutAttr>(op.getLayoutCdAttr());
1922 if (!layoutA || !layoutB || !layoutCd)
1923 return rewriter.notifyMatchFailure(
1924 op,
"missing required layout attributes for DpasMxOp distribution");
1927 auto expected1DTypeResult =
1929 auto expected1DTypeA =
1931 auto expected1DTypeB =
1934 VectorType expected1DTypeScaleA, expected1DTypeScaleB;
1935 if (op.getScaleA()) {
1936 auto layoutScaleA = cast<xegpu::LayoutAttr>(op.getLayoutAScaleAttr());
1938 cast<VectorType>(op.getScaleA().getType()), layoutScaleA);
1939 if (failed(expected1DTypeScaleAOrFailure))
1940 return rewriter.notifyMatchFailure(
1941 op,
"failed to calculate expected 1D vector type for scale A");
1942 expected1DTypeScaleA = expected1DTypeScaleAOrFailure.value();
1944 if (op.getScaleB()) {
1945 auto layoutScaleB = cast<xegpu::LayoutAttr>(op.getLayoutBScaleAttr());
1947 cast<VectorType>(op.getScaleB().getType()), layoutScaleB);
1948 if (failed(expected1DTypeScaleBOrFailure))
1949 return rewriter.notifyMatchFailure(
1950 op,
"failed to calculate expected 1D vector type for scale B");
1951 expected1DTypeScaleB = expected1DTypeScaleBOrFailure.value();
1954 auto expectedNDTypeResult =
1956 if (failed(expected1DTypeResult) || failed(expected1DTypeA) ||
1957 failed(expected1DTypeB))
1958 return rewriter.notifyMatchFailure(
1960 "failed to calculate supported workitem 1D vector types for DpasOp "
1962 if (failed(expectedNDTypeResult))
1963 return rewriter.notifyMatchFailure(
1964 op,
"unable to compute expected workitem vector type for DpasOp from "
1968 const auto *uArchInstruction = dyn_cast<
1971 assert(uArchInstruction);
1972 auto wiAType = expected1DTypeA.value();
1973 auto wiBType = expected1DTypeB.value();
1975 unsigned aPackedBitWidth =
1976 wiAType.getElementTypeBitWidth() * wiAType.getNumElements();
1977 unsigned bPackedBitWidth =
1978 wiBType.getElementTypeBitWidth() * wiBType.getNumElements();
1979 if (aPackedBitWidth % uArchInstruction->getPackedFormatBitSizeA())
1980 return rewriter.notifyMatchFailure(
1981 op,
"A operand packed bit width must be a multiple of uArch packed "
1982 "format requirement");
1983 if (bPackedBitWidth % uArchInstruction->getPackedFormatBitSizeB())
1984 return rewriter.notifyMatchFailure(
1985 op,
"B operand packed bit width must be a multiple of uArch packed "
1986 "format requirement");
1988 auto newOp = xegpu::DpasMxOp::create(
1989 rewriter, op->getLoc(), expected1DTypeResult.value(),
1991 expected1DTypeA.value()),
1993 expected1DTypeB.value()),
1995 ? castValueTo(rewriter,
1997 expected1DTypeResult.value())
2001 ? castValueTo(rewriter,
2003 expected1DTypeScaleA)
2006 ? castValueTo(rewriter,
2008 expected1DTypeScaleB)
2014 rewriter.replaceOp(op, castValueTo(rewriter, newOp.getResult(),
2015 expectedNDTypeResult.value()));
2020struct XeGPUSgToLaneDistributePass
2021 :
public xegpu::impl::XeGPUSgToLaneDistributeBase<
2022 XeGPUSgToLaneDistributePass> {
2023 void runOnOperation()
override;
2028void XeGPUSgToLaneDistributePass::runOnOperation() {
2031 Operation *root = getOperation();
2033 signalPassFailure();
2038 llvm::SmallSetVector<UnrealizedConversionCastOp, 8> existingCasts;
2040 [&](UnrealizedConversionCastOp castOp) { existingCasts.insert(castOp); });
2046 TypeConverter typeConverter;
2050 auto materializeCast = [](OpBuilder &builder, Type type,
ValueRange inputs,
2051 Location loc) -> Value {
2052 return UnrealizedConversionCastOp::create(builder, loc, type, inputs)
2055 typeConverter.addSourceMaterialization(materializeCast);
2056 typeConverter.addTargetMaterialization(materializeCast);
2061 typeConverter, patterns,
target, root);
2062 target.addLegalOp<UnrealizedConversionCastOp>();
2063 (void)applyPartialConversion(root,
target, std::move(patterns));
2074 typeConverter.addConversion([](
Type type) ->
Type {
return type; });
2076 typeConverter.addConversion([](TensorDescType type) ->
Type {
2077 if (type.getLayoutAttr()) {
2078 return type.dropLayouts();
2086 auto getSubShapeAndCount = [](VectorType vecTy,
2087 xegpu::DistributeLayoutAttr layout)
2090 if (failed(distTyOrFailure))
2097 std::move(loopArgTypes));
2105 target.addDynamicallyLegalOp<xegpu::CreateNdDescOp>(
2106 [&](xegpu::CreateNdDescOp op) {
return !op.getType().getLayoutAttr(); });
2108 target.addDynamicallyLegalDialect<xegpu::XeGPUDialect>([](
Operation *op) {
2109 if (isa<xegpu::ConvertLayoutOp>(op))
2111 auto anchorOp = dyn_cast<AnchorLayoutInterface>(op);
2114 return !anchorOp.getAnchorLayout();
2117 target.addDynamicallyLegalOp<arith::ConstantOp>(
2118 [=](arith::ConstantOp op) ->
bool {
2120 if (!isa<VectorType>(op.getResult().getType()))
2126 target.addDynamicallyLegalDialect<math::MathDialect, arith::ArithDialect>(
2127 [=](
Operation *op) -> std::optional<bool> {
2132 if (op->getNumResults() != 1)
2135 VectorType resultType =
2136 dyn_cast<VectorType>(op->getResult(0).getType());
2141 for (
Value operand : op->getOperands()) {
2142 VectorType operandType = dyn_cast<VectorType>(operand.getType());
2143 if (!operandType || operandType.getShape() != resultType.getShape()) {
2151 target.addDynamicallyLegalOp<vector::ReductionOp>(
2152 [=](vector::ReductionOp op) ->
bool {
2157 target.addDynamicallyLegalOp<vector::MultiDimReductionOp>(
2158 [=](vector::MultiDimReductionOp op) ->
bool {
2159 return !isValidSubgroupMultiReductionOp(op);
2161 target.addDynamicallyLegalOp<vector::CreateMaskOp, vector::ConstantMaskOp,
2162 vector::TransposeOp, vector::BitCastOp,
2163 vector::ShapeCastOp, vector::StepOp,
2164 vector::BroadcastOp>([=](
Operation *op) ->
bool {
2167 target.addDynamicallyLegalOp<vector::ExtractOp>(
2168 [=](vector::ExtractOp op) ->
bool {
2169 if (!isa<VectorType>(op.getType()))
2173 target.addDynamicallyLegalOp<vector::InsertOp>(
2174 [=](vector::InsertOp op) ->
bool {
2177 target.addDynamicallyLegalOp<vector::ExtractStridedSliceOp>(
2178 [=](vector::ExtractStridedSliceOp op) ->
bool {
2181 target.addDynamicallyLegalOp<vector::InsertStridedSliceOp>(
2182 [=](vector::InsertStridedSliceOp op) ->
bool {
2185 target.addDynamicallyLegalOp<vector::InterleaveOp, vector::DeinterleaveOp>(
2189 target.markUnknownOpDynamicallyLegal([](
Operation *op) {
return true; });
2191 SgToLaneCreateNdDesc, SgToLaneLoadNd, SgToLaneStoreNd, SgToLaneDpas,
2192 SgToLaneElementWise, SgToLaneArithConstant, SgToLanePrefetchNd,
2193 SgToLaneLoadGather, SgToLaneStoreScatter, SgToLaneVectorReduction,
2194 SgToLaneMultiDimReduction, SgToLaneVectorExtract, SgToLaneVectorInsert,
2195 SgToLaneVectorExtractStridedSlice, SgToLaneVectorInsertStridedSlice,
2196 SgToLaneLoadMatrix, SgToLaneStoreMatrix, SgToLaneConvertLayout,
2197 SgToLaneVectorTranspose, SgToLaneVectorBitcast, SgToLaneVectorStep,
2198 SgToLaneVectorShapeCast, SgToLaneBroadcast,
2199 SgToLaneCreateMask<vector::CreateMaskOp>,
2200 SgToLaneCreateMask<vector::ConstantMaskOp>, SgToLaneVectorDeinterleave,
2201 SgToLaneVectorInterleave, SgToLaneDpasMx>(typeConverter,
Attributes are known-constant values of operations.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
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),...
unsigned getNumResults()
Return the number of results held by this operation.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
A range-style iterator that allows for iterating over the offsets of all potential tiles of size tile...
This class provides an abstraction over the various different ranges of value types.
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 provides an abstraction over the different types of ranges over Values.
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.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int64_t > content)
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
void populateSCFStructuralTypeConversionsAndLegality(const TypeConverter &typeConverter, RewritePatternSet &patterns, ConversionTarget &target, PatternBenefit benefit=1)
Populates patterns for SCF structural type conversions and sets up the provided ConversionTarget with...
Value makeArithReduction(OpBuilder &b, Location loc, CombiningKind kind, Value v1, Value acc, arith::FastMathFlagsAttr fastmath=nullptr, Value mask=nullptr)
Returns the result value of reducing two scalar/vector values with the corresponding arith operation.
SmallVector< Value > getAsValues(OpBuilder &builder, Location loc, ArrayRef< OpFoldResult > foldResults)
Convert foldResults into Values.
@ SubgroupMatrixMultiplyAcc
@ SubgroupScaledMatrixMultiplyAcc
const uArch * getUArch(llvm::StringRef archName)
void populateXeGPUSgToLaneDistributeTypeConversionAndLegality(TypeConverter &typeConverter, RewritePatternSet &patterns, ConversionTarget &target, Operation *topLevelOp)
Defines type conversions and legality for XeGPU subgroup to lane distribution and appends the require...
bool requirePacked(const DistributeLayoutAttr layout)
Helper function to check if the layout is packed.
void removeTemporaryLayoutAttrs(Operation *op)
Removes the temporary layout attributes for each OpOperand and OpResult of the given operation.
Value subgroupReduction(Location loc, OpBuilder &builder, Value input, vector::CombiningKind kind, uint32_t size)
Given an input value representing per-lane data, this function returns the result after performing a ...
bool recoverTemporaryLayouts(Operation *rootOp)
Attach layout attributes to all vector-type operands of operations within the given operation's neste...
FailureOr< VectorType > getDistVecTypeBasedOnLaneLayout(DistributeLayoutAttr layout, VectorType originalType)
Helper function to get distributed vector type for a source vector type according to the lane_layout.
Value lowerToVectorReductions(TypedValue< VectorType > src, TypedValue< VectorType > acc, vector::CombiningKind kind, int64_t reductionDim, Location loc, PatternRewriter &rewriter)
Given a src and an acc argumments from a vector::MultiDimReductionOp, lower to a set of vector::Reduc...
bool requireTranspose(const DistributeLayoutAttr layout, const uArch::uArch *uArch)
Helper function to check if the layout requires a transpose effect.
DistributeLayoutAttr getDistributeLayoutAttr(const Value value)
Retrieves the DistributeLayoutAttr associated with a given Value, or nullptr if none is found.
DenseMap< Value, SmallVector< Type > > precomputeLoopBlockArgTypes(Operation *topLevelOp, SubShapeAndCountFn getSubShapeAndCount)
Pre-computes distributed VectorType mappings for every value carried through an SCF loop under topLev...
std::optional< std::string > getChipStr(Operation *op)
Retrieves the chip string from the XeVM target attribute of the parent GPU module operation.
void addVectorTypeConversion(TypeConverter &converter, SubShapeAndCountFn getSubShapeAndCount, DenseMap< Value, SmallVector< Type > > loopArgTypes)
Adds a context-aware VectorType conversion to converter (1:1 shape-changing or 1:N,...
DistributeLayoutAttr getTemporaryLayout(const T &operandOrResult)
get and set distribute layout attribute for non-anchor operations (and offsets/masks of load/store op...
void populateXeGPUSgToLaneDistributeTypeConversions(TypeConverter &typeConverter, Operation *topLevelOp)
Define only the type conversions needed for XeGPU subgroup to lane distribution.
Value lowerCrossLaneReductionToShuffles(TypedValue< VectorType > src, TypedValue< VectorType > acc, vector::CombiningKind kind, int64_t reductionDim, int64_t reductionSize, Location loc, PatternRewriter &rewriter)
Lowers cross-lane reductions to shuffle operations on a 2D vector.
void cleanupUnrealizedConversionCasts(Operation *root, const llvm::SmallSetVector< UnrealizedConversionCastOp, 8 > &existingCasts)
Cleans up UnrealizedConversionCastOps inserted during SCF structural type conversion and/or XeGPU unr...
SmallVector< OpFoldResult > addWithRightAligned(OpBuilder &builder, Location loc, ArrayRef< OpFoldResult > lhs, ArrayRef< OpFoldResult > rhs)
Generates element-wise addition ops of two arrays with automatic alignment.
FailureOr< VectorType > getDistributedVectorType(xegpu::TensorDescType tdescTy)
If tensor descriptor has a layout attribute it is used in SIMT mode.
Include the generated interface declarations.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
SmallVector< int64_t > computeStrides(ArrayRef< int64_t > sizes)
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
int64_t computeProduct(ArrayRef< int64_t > basis)
Self-explicit.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addOperands(ValueRange newOperands)
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
void addTypes(ArrayRef< Type > newTypes)
Attribute propertiesAttr
This Attribute is used to opaquely construct the properties of the operation.