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(), resultType.dropLayouts(), op.getOperands(),
133 rewriter.replaceOp(op, newOp.getResult());
141struct SgToLaneLoadNd :
public OpConversionPattern<xegpu::LoadNdOp> {
142 using OpConversionPattern<xegpu::LoadNdOp>::OpConversionPattern;
145 matchAndRewrite(xegpu::LoadNdOp op, OpAdaptor adaptor,
146 ConversionPatternRewriter &rewriter)
const override {
147 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
153 if (op.getTensorDescType().getLayout() != layout)
154 return rewriter.notifyMatchFailure(
155 op,
"conflicting layout attributes on tensor descriptor and anchor");
159 return rewriter.notifyMatchFailure(
160 op,
"xegpu::LoadNdOp require target attribute attached to "
161 "determine transpose "
163 auto supportedLaneResultTyOrFailure =
165 auto expectedLaneResultTyOrFailure =
167 if (failed(supportedLaneResultTyOrFailure))
168 return rewriter.notifyMatchFailure(
169 op,
"unable to compute the lane vector type for LoadNdOp");
170 if (failed(expectedLaneResultTyOrFailure))
171 return rewriter.notifyMatchFailure(
172 op,
"unable to compute expected lane vector type from lane layout");
173 auto newOp = xegpu::LoadNdOp::create(
174 rewriter, op.getLoc(), supportedLaneResultTyOrFailure.value(),
175 adaptor.getTensorDesc(), op.getMixedOffsets(), op.getPackedAttr(),
176 op.getTransposeAttr(), op.getL1HintAttr(), op.getL2HintAttr(),
177 op.getL3HintAttr(),
nullptr);
183 rewriter.replaceOp(op, castValueTo(rewriter, newOp.getResult(),
184 expectedLaneResultTyOrFailure.value()));
192struct SgToLaneStoreNd :
public OpConversionPattern<xegpu::StoreNdOp> {
193 using OpConversionPattern<xegpu::StoreNdOp>::OpConversionPattern;
196 matchAndRewrite(xegpu::StoreNdOp op, OpAdaptor adaptor,
197 ConversionPatternRewriter &rewriter)
const override {
198 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
204 if (op.getTensorDescType().getLayout() != layout)
205 return rewriter.notifyMatchFailure(
206 op,
"conflicting layout attributes on tensor descriptor and anchor");
208 if (valueLayout != layout)
209 return rewriter.notifyMatchFailure(
210 op,
"conflicting layout attributes on value and anchor");
211 auto supportedLaneValueTyOrFailure =
213 if (failed(supportedLaneValueTyOrFailure))
214 return rewriter.notifyMatchFailure(
216 "unable to compute lane vector type for StoreNdOp value from tensor "
219 xegpu::StoreNdOp::create(
220 rewriter, op.getLoc(),
222 supportedLaneValueTyOrFailure.value()),
223 adaptor.getTensorDesc(), op.getMixedOffsets(), op.getL1HintAttr(),
224 op.getL2HintAttr(), op.getL3HintAttr(),
nullptr);
225 rewriter.eraseOp(op);
233struct SgToLaneDpas :
public OpConversionPattern<xegpu::DpasOp> {
234 using OpConversionPattern<xegpu::DpasOp>::OpConversionPattern;
237 matchAndRewrite(xegpu::DpasOp op, OpAdaptor adaptor,
238 ConversionPatternRewriter &rewriter)
const override {
240 auto layoutA = cast<xegpu::LayoutAttr>(op.getLayoutAAttr());
241 auto layoutB = cast<xegpu::LayoutAttr>(op.getLayoutBAttr());
242 auto layoutCd = cast<xegpu::LayoutAttr>(op.getLayoutCdAttr());
243 if (!layoutA || !layoutB || !layoutCd)
245 auto laneResultTyOrFailure =
247 auto laneATypeOrFailure =
249 auto laneBTypeOrFailure =
251 auto expectedLaneResultTyOrFailure =
253 if (failed(laneResultTyOrFailure) || failed(laneATypeOrFailure) ||
254 failed(laneBTypeOrFailure))
255 return rewriter.notifyMatchFailure(
256 op,
"failed to calculate supported lane vector types for DpasOp "
258 if (failed(expectedLaneResultTyOrFailure))
259 return rewriter.notifyMatchFailure(
260 op,
"unable to compute expected lane vector type for DpasOp from "
267 const auto *uArchInstruction =
268 dyn_cast<xegpu::uArch::SubgroupMatrixMultiplyAcc>(
269 uArch->getInstruction(
271 if (uArchInstruction) {
272 auto laneAType = laneATypeOrFailure.value();
273 auto laneBType = laneBTypeOrFailure.value();
275 unsigned aPackedBitWidth =
276 laneAType.getElementTypeBitWidth() * laneAType.getNumElements();
277 unsigned bPackedBitWidth =
278 laneBType.getElementTypeBitWidth() * laneBType.getNumElements();
279 unsigned expectedABitSize = uArchInstruction->getPackedFormatBitSizeA();
280 unsigned expectedBBitSize = uArchInstruction->getPackedFormatBitSizeB();
282 if (aPackedBitWidth % expectedABitSize != 0)
283 return rewriter.notifyMatchFailure(
285 "A operand packed bit width must be a multiple of uArch packed "
286 "format requirement");
287 if (bPackedBitWidth % expectedBBitSize != 0)
288 return rewriter.notifyMatchFailure(
290 "B operand packed bit width must be a multiple of uArch packed "
291 "format requirement");
295 auto newOp = xegpu::DpasOp::create(
296 rewriter, op->getLoc(), laneResultTyOrFailure.value(),
298 laneATypeOrFailure.value()),
300 laneBTypeOrFailure.value()),
302 laneResultTyOrFailure.value()),
306 rewriter.replaceOp(op, castValueTo(rewriter, newOp.getResult(),
307 expectedLaneResultTyOrFailure.value()));
314struct SgToLaneElementWise :
public ConversionPattern {
316 : ConversionPattern(MatchAnyOpTypeTag(), 1, ctx) {}
320 ConversionPatternRewriter &rewriter)
const override {
327 return rewriter.notifyMatchFailure(
328 op,
"operation result is not a vector type");
330 xegpu::DistributeLayoutAttr layout =
332 if (!layout || !layout.isForSubgroup())
333 return rewriter.notifyMatchFailure(
334 op,
"operation result does not have subgroup distribute layout");
336 auto laneShapeOrFailure =
339 if (failed(laneShapeOrFailure))
340 return rewriter.notifyMatchFailure(
341 op,
"unable to compute lane vector type from the layout");
343 VectorType newResultType = laneShapeOrFailure.value();
345 state.addOperands(operands);
346 state.addTypes(newResultType);
349 if (!isa<xegpu::DistributeLayoutAttr>(attr.getValue()))
350 state.addAttribute(attr.getName(), attr.getValue());
352 Operation *newOp = rewriter.create(state);
354 rewriter.replaceOp(op, newOp->
getResult(0));
366struct SgToLaneArithConstant :
public OpConversionPattern<arith::ConstantOp> {
367 using OpConversionPattern<arith::ConstantOp>::OpConversionPattern;
370 matchAndRewrite(arith::ConstantOp op, OpAdaptor adaptor,
371 ConversionPatternRewriter &rewriter)
const override {
372 auto resultType = dyn_cast<VectorType>(op.getType());
377 auto denseAttr = dyn_cast<DenseElementsAttr>(op.getValue());
379 return rewriter.notifyMatchFailure(
380 op,
"only dense vector constants are supported");
382 xegpu::DistributeLayoutAttr layout =
384 if (!layout || !layout.isForSubgroup())
385 return rewriter.notifyMatchFailure(
386 op,
"operation result does not have subgroup distribute layout");
388 auto laneShapeOrFailure =
391 if (
failed(laneShapeOrFailure))
392 return rewriter.notifyMatchFailure(
393 op,
"unable to compute lane vector type from the layout");
395 VectorType newResultType = laneShapeOrFailure.value();
396 Location loc = op.getLoc();
400 if (denseAttr.isSplat()) {
401 auto scalarValue = denseAttr.getSplatValue<Attribute>();
404 arith::ConstantOp::create(rewriter, loc, newResultType, newDenseAttr);
405 rewriter.replaceOp(op, newOp.
getResult());
412 arith::ConstantOp::create(rewriter, loc, resultType, denseAttr);
414 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
415 mlir::IntegerAttr());
416 auto maybeCoordsVec = layout.computeDistributedCoords(
417 rewriter, loc, laneId, resultType.getShape());
418 if (
failed(maybeCoordsVec))
419 return rewriter.notifyMatchFailure(
420 op,
"failed to compute distributed coordinates from layout");
422 SmallVector<SmallVector<Value>> coordsVec = maybeCoordsVec.value();
423 SmallVector<int64_t> laneData = layout.getEffectiveLaneDataAsInt();
424 ArrayRef<int64_t> distShape = newResultType.getShape();
425 int64_t rank = newResultType.getRank();
430 SmallVector<int64_t> blockGridShape(rank);
431 for (int64_t d = 0; d < rank; d++)
432 blockGridShape[d] = distShape[d] / laneData[d];
433 SmallVector<int64_t> blockGridStrides =
computeStrides(blockGridShape);
435 auto blockType = VectorType::get(laneData, newResultType.getElementType());
436 SmallVector<int64_t> unitTile(rank, 1);
437 SmallVector<int64_t> strides(rank, 1);
439 Value
result = arith::ConstantOp::create(
440 rewriter, loc, newResultType, rewriter.getZeroAttr(newResultType));
442 for (
auto [blockIdx, blockStart] : llvm::enumerate(coordsVec)) {
446 SmallVector<Value> blockElems;
447 for (SmallVector<int64_t> off :
448 StaticTileOffsetRange(laneData, unitTile)) {
449 SmallVector<OpFoldResult> pos(rank);
450 for (int64_t d = 0; d < rank; d++)
452 rewriter, loc, blockStart[d],
454 blockElems.push_back(vector::ExtractOp::create(
455 rewriter, loc, fullConst.getResult(), pos));
462 vector::FromElementsOp::create(rewriter, loc, blockType, blockElems);
463 SmallVector<int64_t> blockGridPos =
465 SmallVector<int64_t> offsets(rank);
466 for (int64_t d = 0; d < rank; d++)
467 offsets[d] = blockGridPos[d] * laneData[d];
468 result = vector::InsertStridedSliceOp::create(rewriter, loc, block,
469 result, offsets, strides);
472 rewriter.replaceOp(op,
result);
478struct SgToLanePrefetchNd :
public OpConversionPattern<xegpu::PrefetchNdOp> {
479 using OpConversionPattern<xegpu::PrefetchNdOp>::OpConversionPattern;
482 matchAndRewrite(xegpu::PrefetchNdOp op, OpAdaptor adaptor,
483 ConversionPatternRewriter &rewriter)
const override {
484 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
489 xegpu::PrefetchNdOp::create(rewriter, op.getLoc(), adaptor.getTensorDesc(),
490 op.getMixedOffsets(), op.getL1HintAttr(),
491 op.getL2HintAttr(), op.getL3HintAttr(),
493 rewriter.eraseOp(op);
531struct SgToLaneLoadGather :
public OpConversionPattern<xegpu::LoadGatherOp> {
532 using OpConversionPattern<xegpu::LoadGatherOp>::OpConversionPattern;
535 matchAndRewrite(xegpu::LoadGatherOp op, OpAdaptor adaptor,
536 ConversionPatternRewriter &rewriter)
const override {
537 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
541 VectorType origResultTy = op.getValueType();
546 int chunkSize = op.getChunkSize().value_or(1);
547 int effectiveVecRank = (chunkSize == 1) ? 1 : 2;
548 ArrayRef<int64_t> shape = origResultTy.getShape();
550 shape.take_front(origResultTy.getRank() - effectiveVecRank),
551 [](int64_t d) { return d != 1; }))
552 return rewriter.notifyMatchFailure(
553 op,
"Only unit dimensions allowed for the leading "
554 "dimensions of the load vector!");
556 auto distResultTyOrFailure =
558 if (
failed(distResultTyOrFailure))
559 return rewriter.notifyMatchFailure(
560 op,
"unable to compute expected lane vector type from lane layout");
562 VectorType distResultTy = distResultTyOrFailure.value();
563 VectorType distResultTy1D = VectorType::get({distResultTy.getNumElements()},
564 distResultTy.getElementType());
567 Value distOffsets = adaptor.getOffsets();
568 auto distOffsetsTy = cast<VectorType>(distOffsets.
getType());
569 VectorType offsetsTy1D = VectorType::get({distOffsetsTy.getNumElements()},
570 distOffsetsTy.getElementType());
571 distOffsets = castValueTo(
574 Value distMask = adaptor.getMask();
575 auto distMaskTy = cast<VectorType>(distMask.
getType());
576 VectorType maskTy1D = VectorType::get({distMaskTy.getNumElements()},
577 distMaskTy.getElementType());
581 Value distSource = adaptor.getSource();
582 auto newOp = xegpu::LoadGatherOp::create(
583 rewriter, op.getLoc(), distResultTy1D, distSource, distOffsets,
584 distMask, op.getChunkSizeAttr(), op.getL1HintAttr(), op.getL2HintAttr(),
585 op.getL3HintAttr(),
nullptr,
nullptr);
588 if (distResultTy1D != distResultTy)
591 rewriter.replaceOp(op,
result);
600struct SgToLaneVectorReduction
601 :
public OpConversionPattern<vector::ReductionOp> {
602 using OpConversionPattern<vector::ReductionOp>::OpConversionPattern;
605 matchAndRewrite(vector::ReductionOp op, OpAdaptor adaptor,
606 ConversionPatternRewriter &rewriter)
const override {
610 if (!layout || !layout.isForSubgroup())
613 VectorType srcVecType = op.getSourceVectorType();
615 if (srcVecType.getRank() != 1)
616 return rewriter.notifyMatchFailure(
617 op,
"Only rank 1 reductions can be distributed.");
619 if (layout.getRank() != srcVecType.getRank())
620 return rewriter.notifyMatchFailure(
621 op,
"Layout rank does not match vector rank.");
624 int64_t sgSize = layout.getEffectiveLaneLayoutAsInt()[0];
628 return rewriter.notifyMatchFailure(
629 op,
"xegpu::ReductionOp require target attribute attached to "
630 "determine subgroup size");
633 if (sgSize != uArch->getSubgroupSize() ||
634 srcVecType.getShape()[0] % sgSize != 0)
635 return rewriter.notifyMatchFailure(op,
636 "Invalid layout or reduction vector "
637 "dimension must match subgroup size.");
639 if (!op.getType().isIntOrFloat())
640 return rewriter.notifyMatchFailure(
641 op,
"Reduction distribution currently only supports floats and "
645 Value laneValVec = adaptor.getVector();
649 op.getLoc(), rewriter, laneValVec, op.getKind(), sgSize);
652 if (adaptor.getAcc())
654 rewriter, op.getLoc(), op.getKind(), fullReduce, adaptor.getAcc());
656 rewriter.replaceOp(op, fullReduce);
665struct SgToLaneMultiDimReduction
666 :
public OpConversionPattern<vector::MultiDimReductionOp> {
667 using OpConversionPattern<vector::MultiDimReductionOp>::OpConversionPattern;
670 matchAndRewrite(vector::MultiDimReductionOp op, OpAdaptor adaptor,
671 ConversionPatternRewriter &rewriter)
const override {
673 ArrayRef<int64_t> reductionDims = op.getReductionDims();
674 assert(reductionDims.size() == 1 &&
675 "Expecting single reduction dimension for subgroup multi "
678 VectorType sourceType = op.getSourceVectorType();
679 int64_t rank = sourceType.getRank();
681 ArrayRef<int64_t> shape = sourceType.getShape();
682 if (llvm::any_of(shape.take_front(rank - 2),
683 [](int64_t d) { return d != 1; }))
684 return rewriter.notifyMatchFailure(
685 op,
"only unit leading dimensions are supported for "
686 "multi_reduction with rank > 2");
690 if (op.getType().isIntOrFloat()) {
691 auto reductionDim = reductionDims[0];
692 VectorType origSourceType = op.getSourceVectorType();
693 int64_t reductionDimSize = origSourceType.getShape()[reductionDim];
697 op.getKind(), reductionDimSize);
699 if (adaptor.getAcc())
701 result, adaptor.getAcc());
702 }
else if (isReductionLaneLocal(op)) {
706 auto reductionDim = reductionDims[0];
710 reductionDim, op.getLoc(), rewriter);
712 auto reductionDim = reductionDims[0];
713 VectorType sourceType = op.getSourceVectorType();
714 int64_t reductionDimSize = sourceType.getShape()[reductionDim];
718 reductionDim, reductionDimSize, op.getLoc(), rewriter);
720 rewriter.replaceOp(op,
result);
729 ConversionPatternRewriter &rewriter,
Location loc,
732 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
733 mlir::IntegerAttr());
735 layout.computeDistributedCoords(rewriter, loc, laneId, payloadShape);
738 assert(maybeCoords.value().size() == 1 &&
739 "Expected one set of distributed offsets");
743 return llvm::map_to_vector(ofrVec, llvm::CastTo<Value>);
747struct SgToLaneLoadMatrix :
public OpConversionPattern<xegpu::LoadMatrixOp> {
748 using OpConversionPattern<xegpu::LoadMatrixOp>::OpConversionPattern;
751 matchAndRewrite(xegpu::LoadMatrixOp op, OpAdaptor adaptor,
752 ConversionPatternRewriter &rewriter)
const override {
753 auto layout = op.getLayoutAttr();
758 VectorType sgPayloadTy = dyn_cast<VectorType>(op.getResult().getType());
760 return rewriter.notifyMatchFailure(
761 op,
"the matrix op payload must be a vector type");
763 auto loc = op.getLoc();
764 auto offsets = op.getMixedOffsets();
766 return rewriter.notifyMatchFailure(op,
"the load op must have offsets");
768 FailureOr<VectorType> distPayloadTyOrFailure =
770 if (
failed(distPayloadTyOrFailure))
771 return rewriter.notifyMatchFailure(
772 op,
"Failed to distribute matrix op payload based on layout.");
774 SmallVector<Value> offsetsAsValues =
777 SmallVector<Value> newCoords = offsetsAsValues;
778 if (!op.getSubgroupBlockIoAttr()) {
779 newCoords = computeDistributedCoordsForMatrixOp(
780 rewriter, loc, layout, sgPayloadTy.getShape(), offsetsAsValues);
781 if (newCoords.empty())
782 return rewriter.notifyMatchFailure(
783 op,
"Failed to compute distributed coordinates.");
786 SmallVector<int64_t> newConstOffsets(op.getConstOffsets().size(),
787 ShapedType::kDynamic);
789 rewriter.getDenseI64ArrayAttr(newConstOffsets);
791 auto newOp = xegpu::LoadMatrixOp::create(
792 rewriter, loc, *distPayloadTyOrFailure, adaptor.getMemDesc(),
793 ValueRange(newCoords), newConstOffsetsAttr, op.getSubgroupBlockIoAttr(),
794 xegpu::DistributeLayoutAttr{});
795 rewriter.replaceOp(op, newOp.
getResult());
801struct SgToLaneVectorTranspose
802 :
public OpConversionPattern<vector::TransposeOp> {
803 using OpConversionPattern<vector::TransposeOp>::OpConversionPattern;
806 matchAndRewrite(vector::TransposeOp op, OpAdaptor adaptor,
807 ConversionPatternRewriter &rewriter)
const override {
808 xegpu::DistributeLayoutAttr sourceLayout =
810 xegpu::DistributeLayoutAttr resultLayout =
812 if (!sourceLayout || !resultLayout)
813 return rewriter.notifyMatchFailure(
814 op,
"the source or result vector of the transpose op lacks layout "
816 ArrayRef<int64_t> perm = op.getPermutation();
818 if (!resultLayout.isTransposeOf(sourceLayout, perm,
819 xegpu::LayoutKind::Lane))
820 return rewriter.notifyMatchFailure(
821 op,
"the source or result vector layouts must be transposes of "
823 FailureOr<VectorType> distributedResultTypeOrFailure =
825 if (
failed(distributedResultTypeOrFailure))
826 return rewriter.notifyMatchFailure(
827 op,
"Failed to distribute the result vector type in "
828 "vector::Transpose op");
829 auto newOp = vector::TransposeOp::create(rewriter, op.getLoc(),
830 adaptor.getVector(), perm);
831 rewriter.replaceOp(op, castValueTo(rewriter, newOp.
getResult(),
832 distributedResultTypeOrFailure.value()));
839struct SgToLaneVectorBitcast :
public OpConversionPattern<vector::BitCastOp> {
840 using OpConversionPattern<vector::BitCastOp>::OpConversionPattern;
843 matchAndRewrite(vector::BitCastOp op, OpAdaptor adaptor,
844 ConversionPatternRewriter &rewriter)
const override {
845 xegpu::DistributeLayoutAttr resultLayout =
848 return rewriter.notifyMatchFailure(
849 op,
"result vector of the bitcast op lacks layout attribute");
850 FailureOr<VectorType> distributedResultTypeOrFailure =
852 if (
failed(distributedResultTypeOrFailure))
853 return rewriter.notifyMatchFailure(
854 op,
"Failed to distribute the result vector type in "
855 "vector::BitCast op");
856 auto newOp = vector::BitCastOp::create(
857 rewriter, op.getLoc(), distributedResultTypeOrFailure.value(),
858 adaptor.getSource());
859 rewriter.replaceOp(op, newOp.
getResult());
887template <
typename OpType,
888 typename = std::enable_if_t<llvm::is_one_of<
889 OpType, vector::CreateMaskOp, vector::ConstantMaskOp>::value>>
890struct SgToLaneCreateMask :
public OpConversionPattern<OpType> {
891 using OpConversionPattern<OpType>::OpConversionPattern;
894 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
895 ConversionPatternRewriter &rewriter)
const override {
896 xegpu::DistributeLayoutAttr layout =
898 if (!layout || !layout.isForSubgroup())
899 return rewriter.notifyMatchFailure(
900 op,
"operation result does not have subgroup distribute layout");
902 VectorType origType = op.getType();
903 FailureOr<VectorType> distTypeOrFailure =
905 if (
failed(distTypeOrFailure))
906 return rewriter.notifyMatchFailure(
907 op,
"unable to compute lane vector type from the layout");
909 VectorType distType = distTypeOrFailure.value();
910 Location loc = op.getLoc();
913 SmallVector<Value> origBounds;
914 if constexpr (std::is_same_v<OpType, vector::CreateMaskOp>) {
915 origBounds.append(op.getOperands().begin(), op.getOperands().end());
917 auto dimSizes = op.getMaskDimSizesAttr().asArrayRef();
918 for (
auto dimSize : dimSizes)
919 origBounds.push_back(
923 ArrayRef<int64_t> origShape = origType.getShape();
926 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
927 mlir::IntegerAttr());
928 auto maybeCoordsVec =
929 layout.computeDistributedCoords(rewriter, loc, laneId, origShape);
930 if (
failed(maybeCoordsVec))
931 return rewriter.notifyMatchFailure(
932 op,
"failed to compute distributed coordinates from layout");
934 SmallVector<SmallVector<Value>> coordsVec = maybeCoordsVec.value();
935 int64_t numElements = distType.getNumElements();
936 assert(
static_cast<int64_t
>(coordsVec.size()) == numElements &&
937 "number of coordinate sets must match number of distributed "
943 SmallVector<Value> maskBits;
944 for (
auto &coords : coordsVec) {
945 Value inBounds = trueVal;
946 for (
size_t i = 0; i < coords.size(); ++i) {
947 Value cmp = arith::CmpIOp::create(
948 rewriter, loc, arith::CmpIPredicate::slt, coords[i], origBounds[i]);
949 inBounds = arith::AndIOp::create(rewriter, loc, inBounds, cmp);
951 maskBits.push_back(inBounds);
956 if (numElements == 1) {
958 vector::BroadcastOp::create(rewriter, loc, distType, maskBits[0]);
961 vector::FromElementsOp::create(rewriter, loc, distType, maskBits);
963 rewriter.replaceOp(op,
result);
969struct SgToLaneStoreMatrix :
public OpConversionPattern<xegpu::StoreMatrixOp> {
970 using OpConversionPattern<xegpu::StoreMatrixOp>::OpConversionPattern;
973 matchAndRewrite(xegpu::StoreMatrixOp op, OpAdaptor adaptor,
974 ConversionPatternRewriter &rewriter)
const override {
975 auto layout = op.getLayoutAttr();
980 VectorType sgPayloadTy = dyn_cast<VectorType>(op.getData().getType());
982 return rewriter.notifyMatchFailure(
983 op,
"the matrix op payload must be a vector type");
985 auto loc = op.getLoc();
986 auto offsets = op.getMixedOffsets();
988 return rewriter.notifyMatchFailure(op,
"the store op must have offsets");
990 FailureOr<VectorType> distPayloadTyOrFailure =
992 if (
failed(distPayloadTyOrFailure))
993 return rewriter.notifyMatchFailure(
994 op,
"Failed to distribute matrix op payload based on layout.");
996 SmallVector<Value> offsetsAsValues =
999 SmallVector<Value> newCoords = offsetsAsValues;
1000 if (!op.getSubgroupBlockIoAttr()) {
1001 newCoords = computeDistributedCoordsForMatrixOp(
1002 rewriter, loc, layout, sgPayloadTy.getShape(), offsetsAsValues);
1003 if (newCoords.empty())
1004 return rewriter.notifyMatchFailure(
1005 op,
"Failed to compute distributed coordinates.");
1008 SmallVector<int64_t> newConstOffsets(op.getConstOffsets().size(),
1009 ShapedType::kDynamic);
1011 rewriter.getDenseI64ArrayAttr(newConstOffsets);
1013 xegpu::StoreMatrixOp::create(
1016 distPayloadTyOrFailure.value()),
1017 adaptor.getMemDesc(),
ValueRange(newCoords), newConstOffsetsAttr,
1018 op.getSubgroupBlockIoAttr(), xegpu::DistributeLayoutAttr{});
1019 rewriter.eraseOp(op);
1058struct SgToLaneStoreScatter
1059 :
public OpConversionPattern<xegpu::StoreScatterOp> {
1060 using OpConversionPattern<xegpu::StoreScatterOp>::OpConversionPattern;
1063 matchAndRewrite(xegpu::StoreScatterOp op, OpAdaptor adaptor,
1064 ConversionPatternRewriter &rewriter)
const override {
1065 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
1069 VectorType origValueTy = op.getValueType();
1074 int chunkSize = op.getChunkSize().value_or(1);
1075 int effectiveVecRank = (chunkSize == 1) ? 1 : 2;
1076 ArrayRef<int64_t> shape = origValueTy.getShape();
1077 if (llvm::any_of(shape.take_front(origValueTy.getRank() - effectiveVecRank),
1078 [](int64_t d) { return d != 1; }))
1079 return rewriter.notifyMatchFailure(
1080 op,
"Only unit dimensions allowed for the leading "
1081 "dimensions of the store vector!");
1083 auto distValueTyOrFailure =
1085 if (
failed(distValueTyOrFailure))
1086 return rewriter.notifyMatchFailure(
1087 op,
"unable to compute expected lane vector type from lane layout");
1089 VectorType distValueTy = distValueTyOrFailure.value();
1090 VectorType distValueTy1D = VectorType::get({distValueTy.getNumElements()},
1091 distValueTy.getElementType());
1093 Value distValue = adaptor.getValue();
1094 if (distValue.
getType() != distValueTy1D)
1099 Value distOffsets = adaptor.getOffsets();
1100 auto distOffsetsTy = cast<VectorType>(distOffsets.
getType());
1101 VectorType offsetsTy1D = VectorType::get({distOffsetsTy.getNumElements()},
1102 distOffsetsTy.getElementType());
1103 distOffsets = castValueTo(
1106 Value distMask = adaptor.getMask();
1107 auto distMaskTy = cast<VectorType>(distMask.
getType());
1108 VectorType maskTy1D = VectorType::get({distMaskTy.getNumElements()},
1109 distMaskTy.getElementType());
1113 Value distDest = adaptor.getDest();
1114 xegpu::StoreScatterOp::create(rewriter, op.getLoc(), distValue, distDest,
1115 distOffsets, distMask, op.getChunkSizeAttr(),
1116 op.getL1HintAttr(), op.getL2HintAttr(),
1117 op.getL3HintAttr(),
nullptr,
1119 rewriter.eraseOp(op);
1128struct SgToLaneVectorStep :
public OpConversionPattern<vector::StepOp> {
1129 using OpConversionPattern<vector::StepOp>::OpConversionPattern;
1132 matchAndRewrite(vector::StepOp op, OpAdaptor adaptor,
1133 ConversionPatternRewriter &rewriter)
const override {
1134 xegpu::DistributeLayoutAttr resultLayout =
1136 if (!resultLayout || !resultLayout.isForSubgroup())
1137 return rewriter.notifyMatchFailure(
1138 op,
"the result vector of the step op lacks subgroup layout");
1140 auto loc = op.getLoc();
1141 auto stepResultVecTy = op.getResult().getType();
1142 auto laneShapeOrFailure =
1144 if (
failed(laneShapeOrFailure))
1145 return rewriter.notifyMatchFailure(
1146 op,
"unable to compute lane vector type from the layout");
1147 VectorType newVecTy = laneShapeOrFailure.value();
1149 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
1150 mlir::IntegerAttr());
1151 auto laneDataBlockCoords = resultLayout.computeDistributedCoords(
1152 rewriter, loc, laneId, stepResultVecTy.getShape());
1153 if (
failed(laneDataBlockCoords))
1154 return rewriter.notifyMatchFailure(
1155 op,
"failed to compute lane data block coordinates");
1157 auto laneDataBlockCoordsVec = laneDataBlockCoords.value();
1158 auto laneDataBlockLength = resultLayout.getEffectiveLaneDataAsInt()[0];
1159 assert(
static_cast<int64_t
>(laneDataBlockCoordsVec.size()) ==
1160 newVecTy.getNumElements() / laneDataBlockLength);
1161 SmallVector<Value> stepVals;
1169 for (
auto &laneDataBlockCoords : laneDataBlockCoordsVec) {
1170 auto laneDataBlockStartCoord = laneDataBlockCoords[0];
1171 stepVals.push_back(laneDataBlockStartCoord);
1172 for (
int i = 1; i < laneDataBlockLength; ++i) {
1174 stepVals.push_back(arith::AddIOp::create(
1175 rewriter, loc, laneDataBlockStartCoord, offset));
1178 assert(
static_cast<int64_t
>(stepVals.size()) == newVecTy.getNumElements() &&
1179 "Expecting the number of step values to match the number of "
1180 "elements in the vector");
1182 vector::FromElementsOp::create(rewriter, loc, newVecTy, stepVals);
1183 rewriter.replaceOp(op, stepOpVal);
1190struct SgToLaneVectorExtract :
public OpConversionPattern<vector::ExtractOp> {
1191 using OpConversionPattern<vector::ExtractOp>::OpConversionPattern;
1194 matchAndRewrite(vector::ExtractOp op, OpAdaptor adaptor,
1195 ConversionPatternRewriter &rewriter)
const override {
1197 auto resultType = dyn_cast<VectorType>(op.getType());
1199 return rewriter.notifyMatchFailure(op,
"scalar extract not supported");
1201 xegpu::DistributeLayoutAttr layout =
1203 if (!layout || !layout.isForSubgroup())
1208 auto laneLayout = layout.getEffectiveLaneLayoutAsInt();
1209 if (llvm::any_of(ArrayRef<int64_t>(laneLayout).drop_back(1),
1210 [](int64_t v) {
return v != 1; }))
1211 return rewriter.notifyMatchFailure(
1212 op,
"only innermost dimension distribution is supported for "
1215 auto newOp = vector::ExtractOp::create(
1216 rewriter, op.getLoc(), adaptor.getSource(), op.getMixedPosition());
1217 rewriter.replaceOp(op, newOp.
getResult());
1223struct SgToLaneVectorShapeCast
1224 :
public OpConversionPattern<vector::ShapeCastOp> {
1225 using OpConversionPattern<vector::ShapeCastOp>::OpConversionPattern;
1228 matchAndRewrite(vector::ShapeCastOp op, OpAdaptor adaptor,
1229 ConversionPatternRewriter &rewriter)
const override {
1230 xegpu::DistributeLayoutAttr resultLayout =
1232 if (!resultLayout || !resultLayout.isForSubgroup())
1233 return rewriter.notifyMatchFailure(
1234 op,
"the result vector of the shape_cast op lacks subgroup layout");
1237 resultLayout, op.getResultVectorType());
1238 if (
failed(resultDistTypeOrFailure))
1239 return rewriter.notifyMatchFailure(
1240 op,
"failed to get distributed vector type for result");
1242 Value source = adaptor.getSource();
1243 auto newShapeCast = vector::ShapeCastOp::create(
1244 rewriter, op.getLoc(), resultDistTypeOrFailure.value(), source);
1245 rewriter.replaceOp(op, newShapeCast);
1253struct SgToLaneVectorExtractStridedSlice
1254 :
public OpConversionPattern<vector::ExtractStridedSliceOp> {
1255 using OpConversionPattern<vector::ExtractStridedSliceOp>::OpConversionPattern;
1258 matchAndRewrite(vector::ExtractStridedSliceOp op, OpAdaptor adaptor,
1259 ConversionPatternRewriter &rewriter)
const override {
1260 xegpu::DistributeLayoutAttr resultLayout =
1262 if (!resultLayout || !resultLayout.isForSubgroup())
1265 VectorType resultType = op.getType();
1266 auto distResultTyOrFailure =
1268 if (
failed(distResultTyOrFailure))
1269 return rewriter.notifyMatchFailure(
1270 op,
"unable to compute distributed vector type from lane layout");
1271 VectorType distResultTy = *distResultTyOrFailure;
1273 SmallVector<int64_t> distributedDims =
1274 getDistributedDims(resultType, distResultTy);
1277 int64_t sourceRank = op.getSourceVectorType().getRank();
1278 SmallVector<Attribute> updatedSizes =
1279 llvm::map_to_vector(op.getSizes(), [](Attribute attr) { return attr; });
1280 SmallVector<Attribute> updatedOffsets = llvm::map_to_vector(
1281 op.getOffsets(), [](Attribute attr) { return attr; });
1282 SmallVector<Attribute> updatedStrides = llvm::map_to_vector(
1283 op.getStrides(), [](Attribute attr) { return attr; });
1284 for (int64_t i = op.getSizes().size(); i < sourceRank; ++i) {
1285 updatedSizes.push_back(
1286 rewriter.getI64IntegerAttr(op.getSourceVectorType().getDimSize(i)));
1287 updatedOffsets.push_back(rewriter.getI64IntegerAttr(0));
1288 updatedStrides.push_back(rewriter.getI64IntegerAttr(1));
1293 if (!distributedDims.empty()) {
1294 if (distributedDims.size() != 1)
1295 return rewriter.notifyMatchFailure(
1296 op,
"only single dimension distribution is supported");
1297 int64_t distDim = distributedDims[0];
1301 return rewriter.notifyMatchFailure(
1302 op,
"target attribute required to determine subgroup size");
1303 int subgroupSize = uArch->getSubgroupSize();
1305 if (!sourceLayout || sourceLayout.getEffectiveLaneLayoutAsInt().empty())
1306 return rewriter.notifyMatchFailure(
1307 op,
"source of extract_strided_slice lacks distribution layout");
1308 int sourceDistrDimSize = op.getSourceVectorType().getShape()[distDim];
1309 auto laneLayout = sourceLayout.getEffectiveLaneLayoutAsInt();
1312 if (laneLayout[distDim] < subgroupSize &&
1313 subgroupSize % laneLayout[distDim] == 0)
1314 subgroupSize = laneLayout[distDim];
1315 if (sourceDistrDimSize % subgroupSize != 0)
1316 return rewriter.notifyMatchFailure(
1317 op,
"source size along distributed dim is not a multiple of "
1319 auto sourceLaneData = sourceLayout.getEffectiveLaneDataAsInt();
1322 if (distDim <
static_cast<int64_t
>(sourceLaneData.size()) &&
1323 sourceLaneData[distDim] != 1)
1324 return rewriter.notifyMatchFailure(
1325 op,
"expecting unit lane data along the distributed dimension");
1326 int64_t distrDimOffset =
1327 cast<IntegerAttr>(updatedOffsets[distDim]).getInt();
1328 if (distrDimOffset % subgroupSize != 0)
1329 return rewriter.notifyMatchFailure(
1330 op,
"offset along distributed dim is not a multiple of "
1333 updatedSizes[distDim] =
1334 rewriter.getI64IntegerAttr(distResultTy.getDimSize(distDim));
1335 updatedOffsets[distDim] =
1336 rewriter.getI64IntegerAttr(distrDimOffset / subgroupSize);
1339 auto newOp = vector::ExtractStridedSliceOp::create(
1340 rewriter, op.getLoc(), distResultTy, adaptor.getSource(),
1341 ArrayAttr::get(rewriter.getContext(), updatedOffsets),
1342 ArrayAttr::get(rewriter.getContext(), updatedSizes),
1343 ArrayAttr::get(rewriter.getContext(), updatedStrides));
1344 rewriter.replaceOp(op, newOp.
getResult());
1406struct SgToLaneBroadcast :
public OpConversionPattern<vector::BroadcastOp> {
1407 using OpConversionPattern<vector::BroadcastOp>::OpConversionPattern;
1410 matchAndRewrite(vector::BroadcastOp op, OpAdaptor adaptor,
1411 ConversionPatternRewriter &rewriter)
const override {
1412 xegpu::DistributeLayoutAttr resultLayout =
1414 if (!resultLayout || !resultLayout.isForSubgroup())
1415 return rewriter.notifyMatchFailure(
1416 op,
"result does not have subgroup distribute layout");
1418 VectorType destType = op.getResultVectorType();
1419 VectorType sourceType = dyn_cast<VectorType>(op.getSourceType());
1421 xegpu::DistributeLayoutAttr sourceLayout =
1425 int64_t rankDiff = destType.getRank() - sourceType.getRank();
1428 if (!sourceLayout || !sourceLayout.isSliceOf(resultLayout))
1430 "broadcast source layout must be a slice of result layout");
1431 }
else if (rankDiff == 0) {
1433 auto broadcastUnitDimsSet = op.computeBroadcastedUnitDims();
1434 SmallVector<int64_t> broadcastUnitDims(broadcastUnitDimsSet.begin(),
1435 broadcastUnitDimsSet.end());
1436 assert(sourceLayout.isEqualTo(
1437 sourceLayout.setUnitDimData(broadcastUnitDims)) &&
1438 "The sg_data for unit dimensions should be set as 1");
1439 sourceLayout = sourceLayout.setUnitDimLayout(broadcastUnitDims);
1444 return rewriter.notifyMatchFailure(
1445 op,
"broadcast from scalar must not have a layout attribute");
1450 if (
failed(destDistType))
1451 return rewriter.notifyMatchFailure(
1452 op,
"failed to distribute the result vector type");
1454 Value source = adaptor.getSource();
1456 if (source.
getType() == destDistType.value()) {
1457 rewriter.replaceOp(op, source);
1461 auto newOp = vector::BroadcastOp::create(rewriter, op.getLoc(),
1462 destDistType.value(), source);
1463 rewriter.replaceOp(op, newOp);
1471struct SgToLaneVectorInsertStridedSlice
1472 :
public OpConversionPattern<vector::InsertStridedSliceOp> {
1473 using OpConversionPattern<vector::InsertStridedSliceOp>::OpConversionPattern;
1476 matchAndRewrite(vector::InsertStridedSliceOp op, OpAdaptor adaptor,
1477 ConversionPatternRewriter &rewriter)
const override {
1478 xegpu::DistributeLayoutAttr resultLayout =
1480 if (!resultLayout || !resultLayout.isForSubgroup())
1483 VectorType destType = op.getDestVectorType();
1484 auto distDestTyOrFailure =
1486 if (
failed(distDestTyOrFailure))
1487 return rewriter.notifyMatchFailure(
1488 op,
"unable to compute distributed vector type from lane layout");
1489 VectorType distDestTy = *distDestTyOrFailure;
1491 SmallVector<int64_t> destDistributedDims =
1492 getDistributedDims(destType, distDestTy);
1494 SmallVector<Attribute> updatedOffsets = llvm::map_to_vector(
1495 op.getOffsets(), [](Attribute attr) { return attr; });
1497 if (!destDistributedDims.empty()) {
1498 if (destDistributedDims.size() != 1)
1499 return rewriter.notifyMatchFailure(
1500 op,
"only single dimension distribution is supported");
1501 int64_t destDistDim = destDistributedDims[0];
1506 return rewriter.notifyMatchFailure(
1507 op,
"target attribute required to determine subgroup size");
1508 int subgroupSize = uArch->getSubgroupSize();
1510 VectorType srcType = op.getSourceVectorType();
1512 int64_t sourceDistDim =
1513 destDistDim - (destType.getRank() - srcType.getRank());
1514 if (sourceDistDim < 0)
1515 return rewriter.notifyMatchFailure(
1516 op,
"distributed dimension must be in the last k dims of dest");
1520 if (!destLayout || !sourceLayout ||
1521 destLayout.getEffectiveLaneLayoutAsInt().empty() ||
1522 sourceLayout.getEffectiveLaneLayoutAsInt().empty())
1523 return rewriter.notifyMatchFailure(
1524 op,
"source or dest of insert_strided_slice lacks distribution "
1527 auto destLaneData = destLayout.getEffectiveLaneDataAsInt();
1528 auto sourceLaneData = sourceLayout.getEffectiveLaneDataAsInt();
1531 if ((destDistDim <
static_cast<int64_t
>(destLaneData.size()) &&
1532 destLaneData[destDistDim] != 1) ||
1533 (sourceDistDim <
static_cast<int64_t
>(sourceLaneData.size()) &&
1534 sourceLaneData[sourceDistDim] != 1))
1535 return rewriter.notifyMatchFailure(
1536 op,
"expecting unit lane data along the distributed dimension");
1538 int64_t srcDistrDimSize = srcType.getDimSize(sourceDistDim);
1539 if (srcDistrDimSize % subgroupSize != 0)
1540 return rewriter.notifyMatchFailure(
1541 op,
"source distributed dim size is not a multiple of "
1544 int64_t destDistrDimOffset =
1545 cast<IntegerAttr>(op.getOffsets()[destDistDim]).getInt();
1546 if (destDistrDimOffset % subgroupSize != 0)
1547 return rewriter.notifyMatchFailure(
1548 op,
"offset along distributed dim is not a multiple of "
1551 updatedOffsets[destDistDim] =
1552 rewriter.getI64IntegerAttr(destDistrDimOffset / subgroupSize);
1555 auto newOp = vector::InsertStridedSliceOp::create(
1556 rewriter, op.getLoc(), distDestTy, adaptor.getValueToStore(),
1558 ArrayAttr::get(rewriter.getContext(), updatedOffsets), op.getStrides());
1559 rewriter.replaceOp(op, newOp.
getResult());
1566struct SgToLaneVectorInsert :
public OpConversionPattern<vector::InsertOp> {
1567 using OpConversionPattern<vector::InsertOp>::OpConversionPattern;
1570 matchAndRewrite(vector::InsertOp op, OpAdaptor adaptor,
1571 ConversionPatternRewriter &rewriter)
const override {
1573 auto valueType = dyn_cast<VectorType>(op.getValueToStoreType());
1575 return rewriter.notifyMatchFailure(op,
"scalar insert not supported");
1577 xegpu::DistributeLayoutAttr layout =
1579 if (!layout || !layout.isForSubgroup())
1584 auto laneLayout = layout.getEffectiveLaneLayoutAsInt();
1585 if (llvm::any_of(ArrayRef<int64_t>(laneLayout).drop_back(1),
1586 [](int64_t v) {
return v != 1; }))
1587 return rewriter.notifyMatchFailure(
1588 op,
"only innermost dimension distribution is supported for "
1591 auto newOp = vector::InsertOp::create(
1592 rewriter, op.getLoc(), adaptor.getValueToStore(), adaptor.getDest(),
1593 op.getMixedPosition());
1594 rewriter.replaceOp(op, newOp.
getResult());
1612static FailureOr<Value>
1613shuffleDataAsLaneLayoutChange(ConversionPatternRewriter &rewriter,
Location loc,
1616 VectorType srcTy = dyn_cast<VectorType>(src.
getType());
1617 if (!srcTy || srcTy.getRank() != 2)
1620 if (targetLaneNum <= 0 || currentLaneNum != targetLaneNum * 2)
1624 srcTy.getNumElements() * srcTy.getElementTypeBitWidth();
1625 if (vectorBitWidth % 32 != 0)
1637 Type shuffleElemTy = rewriter.getI32Type();
1638 int64_t numShuffles = vectorBitWidth / 32;
1639 VectorType shuffleBundleTy = VectorType::get({numShuffles}, shuffleElemTy);
1641 Value temp = arith::ConstantOp::create(
1644 IntegerAttr::get(shuffleElemTy, 0)));
1645 VectorType flatSrcTy =
1646 VectorType::get({srcTy.getNumElements()}, srcTy.getElementType());
1647 Value flatSrc = vector::ShapeCastOp::create(rewriter, loc, flatSrcTy, src);
1648 Value shuffleBundle =
1649 vector::BitCastOp::create(rewriter, loc, shuffleBundleTy, flatSrc);
1650 for (
int64_t i = 0; i < numShuffles; i++) {
1652 vector::ExtractOp::create(rewriter, loc, shuffleBundle, i);
1653 shuffleElem = gpu::ShuffleOp::create(rewriter, loc, shuffleElem, 0,
1654 targetLaneNum, gpu::ShuffleMode::UP)
1656 temp = vector::InsertOp::create(rewriter, loc, shuffleElem, temp, i);
1658 temp = vector::BitCastOp::create(rewriter, loc, flatSrcTy, temp);
1659 temp = vector::ShapeCastOp::create(rewriter, loc, srcTy, temp);
1664 Value res = vector::ShuffleOp::create(rewriter, loc, src, temp,
indices);
1675static FailureOr<Value> repackLaneData(ConversionPatternRewriter &rewriter,
1679 auto srcTy = dyn_cast<VectorType>(src.
getType());
1682 int64_t rank = srcTy.getRank();
1683 Type elemTy = srcTy.getElementType();
1684 int64_t k = srcTy.getShape()[repackDim];
1686 bool roundRobinToContig = inputData == 1 && targetData == k;
1687 bool contigToRoundRobin = inputData == k && targetData == 1;
1688 if (!roundRobinToContig && !contigToRoundRobin)
1693 xegpu::LaneShuffleMode mode = roundRobinToContig
1694 ? xegpu::LaneShuffleMode::Pack
1695 : xegpu::LaneShuffleMode::Unpack;
1696 VectorType runTy = VectorType::get({k}, elemTy);
1700 if (srcTy.getNumElements() == k) {
1703 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, src, mode));
1704 Value flat = vector::ShapeCastOp::create(rewriter, loc, runTy, src);
1706 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, flat, mode);
1707 return Value(vector::ShapeCastOp::create(rewriter, loc, srcTy, shuffled));
1712 if (repackDim == rank - 1) {
1716 Value result = arith::ConstantOp::create(rewriter, loc, srcTy,
1717 rewriter.getZeroAttr(srcTy));
1718 for (
int64_t i = 0; i < numRuns; ++i) {
1720 Value run = vector::ExtractOp::create(rewriter, loc, src, pos);
1722 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, run, mode);
1723 result = vector::InsertOp::create(rewriter, loc, shuffled,
result, pos);
1733 for (
int64_t d = 0; d < rank; ++d)
1734 if (d != repackDim) {
1735 keptShape.push_back(srcTy.getShape()[d]);
1736 keptDims.push_back(d);
1741 sliceSizes[repackDim] = k;
1743 VectorType sliceTy = VectorType::get(sliceSizes, elemTy);
1744 Value result = arith::ConstantOp::create(rewriter, loc, srcTy,
1745 rewriter.getZeroAttr(srcTy));
1746 for (
int64_t i = 0; i < numRuns; ++i) {
1749 for (
auto [dim, coord] : llvm::zip_equal(keptDims, keptPos))
1750 offsets[dim] = coord;
1751 Value slice = vector::ExtractStridedSliceOp::create(
1752 rewriter, loc, src, offsets, sliceSizes, sliceStrides);
1753 Value run = vector::ShapeCastOp::create(rewriter, loc, runTy, slice);
1755 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, run, mode);
1756 Value repackedSlice =
1757 vector::ShapeCastOp::create(rewriter, loc, sliceTy, repacked);
1758 result = vector::InsertStridedSliceOp::create(
1759 rewriter, loc, repackedSlice,
result, offsets, sliceStrides);
1765struct SgToLaneConvertLayout
1766 :
public OpConversionPattern<xegpu::ConvertLayoutOp> {
1767 using OpConversionPattern<xegpu::ConvertLayoutOp>::OpConversionPattern;
1770 matchAndRewrite(xegpu::ConvertLayoutOp op, OpAdaptor adaptor,
1771 ConversionPatternRewriter &rewriter)
const override {
1772 auto inputLayout = op.getEffectiveInputLayout();
1773 auto targetLayout = op.getTargetLayoutAttr();
1774 Type valType = op.getResult().getType();
1777 rewriter.replaceOp(op, op.getSource());
1781 auto resShape = cast<VectorType>(valType).getShape();
1782 SmallVector<int64_t> resShapeVec(resShape.begin(), resShape.end());
1786 if (inputLayout.isCompatibleWith(targetLayout, resShapeVec,
1787 xegpu::LayoutKind::Lane)) {
1788 rewriter.replaceOp(op, adaptor.getSource());
1799 if (inputLayout.getEffectiveOrderAsInt() ==
1800 targetLayout.getEffectiveOrderAsInt() &&
1801 inputLayout.getRank() == 2 && targetLayout.getRank() == 2) {
1802 auto laneLayout = inputLayout.getEffectiveLaneLayoutAsInt();
1803 auto targetLaneLayout = targetLayout.getEffectiveLaneLayoutAsInt();
1804 auto laneData = inputLayout.getEffectiveLaneDataAsInt();
1805 auto targetLaneData = targetLayout.getEffectiveLaneDataAsInt();
1806 if (laneLayout.size() == 2 && targetLaneLayout.size() == 2 &&
1807 laneData == targetLaneData && laneLayout[1] == 1 &&
1808 targetLaneLayout[1] == 1 && laneLayout[0] > 1 &&
1809 laneLayout[0] != targetLaneLayout[0]) {
1810 FailureOr<Value> res = shuffleDataAsLaneLayoutChange(
1811 rewriter, op.getLoc(), adaptor.getSource(), laneLayout[0],
1812 targetLaneLayout[0]);
1813 if (succeeded(res)) {
1814 rewriter.replaceOp(op, *res);
1826 if (inputLayout.getEffectiveOrderAsInt() ==
1827 targetLayout.getEffectiveOrderAsInt() &&
1828 inputLayout.getEffectiveLaneLayoutAsInt() ==
1829 targetLayout.getEffectiveLaneLayoutAsInt()) {
1830 auto laneLayout = inputLayout.getEffectiveLaneLayoutAsInt();
1831 auto laneData = inputLayout.getEffectiveLaneDataAsInt();
1832 auto targetLaneData = targetLayout.getEffectiveLaneDataAsInt();
1835 int64_t rank = laneData.size();
1836 int64_t repackDim = -1;
1837 bool multipleChanged =
false;
1838 for (int64_t d = 0; d < rank; ++d)
1839 if (laneData[d] != targetLaneData[d]) {
1840 if (repackDim != -1)
1841 multipleChanged =
true;
1847 int64_t otherDim = repackDim == rank - 1 ? rank - 2 : rank - 1;
1848 bool laneLayoutOk = repackDim != -1 && laneLayout[repackDim] != 1 &&
1849 (rank < 2 || laneLayout[otherDim] == 1);
1853 if (repackDim != -1 && repackDim >= rank - 2 && !multipleChanged &&
1855 FailureOr<Value> res = repackLaneData(
1856 rewriter, op.getLoc(), adaptor.getSource(), repackDim,
1857 laneData[repackDim], targetLaneData[repackDim]);
1858 if (succeeded(res)) {
1859 rewriter.replaceOp(op, *res);
1865 return rewriter.notifyMatchFailure(
1866 op,
"lowering incompatible convert_layout not yet supported");
1871struct SgToLaneVectorInterleave
1872 :
public OpConversionPattern<vector::InterleaveOp> {
1873 using OpConversionPattern<vector::InterleaveOp>::OpConversionPattern;
1876 matchAndRewrite(vector::InterleaveOp op, OpAdaptor adaptor,
1877 ConversionPatternRewriter &rewriter)
const override {
1879 auto newOp = vector::InterleaveOp::create(
1880 rewriter, op.getLoc(), adaptor.getLhs(), adaptor.getRhs());
1881 rewriter.replaceOp(op, newOp.
getResult());
1887struct SgToLaneVectorDeinterleave
1888 :
public OpConversionPattern<vector::DeinterleaveOp> {
1889 using OpConversionPattern<vector::DeinterleaveOp>::OpConversionPattern;
1892 matchAndRewrite(vector::DeinterleaveOp op, OpAdaptor adaptor,
1893 ConversionPatternRewriter &rewriter)
const override {
1895 auto newOp = vector::DeinterleaveOp::create(rewriter, op.getLoc(),
1896 adaptor.getSource());
1902struct SgToLaneDpasMx :
public OpConversionPattern<xegpu::DpasMxOp> {
1903 using OpConversionPattern<xegpu::DpasMxOp>::OpConversionPattern;
1906 matchAndRewrite(xegpu::DpasMxOp op, OpAdaptor adaptor,
1907 ConversionPatternRewriter &rewriter)
const override {
1912 if (!uArch->isSupportedInstruction(
1913 xegpu::uArch::InstructionKind::SubgroupScaledMatrixMultiplyAcc))
1914 return rewriter.notifyMatchFailure(
1915 op,
"target uArch does not support scaled subgroup mma");
1917 auto layoutA = cast<xegpu::LayoutAttr>(op.getLayoutAAttr());
1918 auto layoutB = cast<xegpu::LayoutAttr>(op.getLayoutBAttr());
1919 auto layoutCd = cast<xegpu::LayoutAttr>(op.getLayoutCdAttr());
1920 if (!layoutA || !layoutB || !layoutCd)
1921 return rewriter.notifyMatchFailure(
1922 op,
"missing required layout attributes for DpasMxOp distribution");
1925 auto expected1DTypeResult =
1927 auto expected1DTypeA =
1929 auto expected1DTypeB =
1932 VectorType expected1DTypeScaleA, expected1DTypeScaleB;
1933 if (op.getScaleA()) {
1934 auto layoutScaleA = cast<xegpu::LayoutAttr>(op.getLayoutAScaleAttr());
1936 cast<VectorType>(op.getScaleA().getType()), layoutScaleA);
1937 if (
failed(expected1DTypeScaleAOrFailure))
1938 return rewriter.notifyMatchFailure(
1939 op,
"failed to calculate expected 1D vector type for scale A");
1940 expected1DTypeScaleA = expected1DTypeScaleAOrFailure.value();
1942 if (op.getScaleB()) {
1943 auto layoutScaleB = cast<xegpu::LayoutAttr>(op.getLayoutBScaleAttr());
1945 cast<VectorType>(op.getScaleB().getType()), layoutScaleB);
1946 if (
failed(expected1DTypeScaleBOrFailure))
1947 return rewriter.notifyMatchFailure(
1948 op,
"failed to calculate expected 1D vector type for scale B");
1949 expected1DTypeScaleB = expected1DTypeScaleBOrFailure.value();
1952 auto expectedNDTypeResult =
1954 if (
failed(expected1DTypeResult) ||
failed(expected1DTypeA) ||
1956 return rewriter.notifyMatchFailure(
1958 "failed to calculate supported workitem 1D vector types for DpasOp "
1960 if (
failed(expectedNDTypeResult))
1961 return rewriter.notifyMatchFailure(
1962 op,
"unable to compute expected workitem vector type for DpasOp from "
1966 const auto *uArchInstruction = dyn_cast<
1968 xegpu::uArch::InstructionKind::SubgroupScaledMatrixMultiplyAcc));
1969 assert(uArchInstruction);
1970 auto wiAType = expected1DTypeA.value();
1971 auto wiBType = expected1DTypeB.value();
1973 unsigned aPackedBitWidth =
1974 wiAType.getElementTypeBitWidth() * wiAType.getNumElements();
1975 unsigned bPackedBitWidth =
1976 wiBType.getElementTypeBitWidth() * wiBType.getNumElements();
1977 if (aPackedBitWidth % uArchInstruction->getPackedFormatBitSizeA())
1978 return rewriter.notifyMatchFailure(
1979 op,
"A operand packed bit width must be a multiple of uArch packed "
1980 "format requirement");
1981 if (bPackedBitWidth % uArchInstruction->getPackedFormatBitSizeB())
1982 return rewriter.notifyMatchFailure(
1983 op,
"B operand packed bit width must be a multiple of uArch packed "
1984 "format requirement");
1986 auto newOp = xegpu::DpasMxOp::create(
1987 rewriter, op->getLoc(), expected1DTypeResult.value(),
1989 expected1DTypeA.value()),
1991 expected1DTypeB.value()),
1993 ? castValueTo(rewriter,
1995 expected1DTypeResult.value())
1999 ? castValueTo(rewriter,
2001 expected1DTypeScaleA)
2004 ? castValueTo(rewriter,
2006 expected1DTypeScaleB)
2012 rewriter.replaceOp(op, castValueTo(rewriter, newOp.
getResult(),
2013 expectedNDTypeResult.value()));
2018struct XeGPUSgToLaneDistributePass
2020 XeGPUSgToLaneDistributePass> {
2021 void runOnOperation()
override;
2026void XeGPUSgToLaneDistributePass::runOnOperation() {
2029 Operation *root = getOperation();
2031 signalPassFailure();
2036 llvm::SmallSetVector<UnrealizedConversionCastOp, 8> existingCasts;
2038 [&](UnrealizedConversionCastOp castOp) { existingCasts.insert(castOp); });
2044 TypeConverter typeConverter;
2048 auto materializeCast = [](OpBuilder &builder, Type type,
ValueRange inputs,
2049 Location loc) -> Value {
2050 return UnrealizedConversionCastOp::create(builder, loc, type, inputs)
2053 typeConverter.addSourceMaterialization(materializeCast);
2054 typeConverter.addTargetMaterialization(materializeCast);
2059 typeConverter, patterns,
target, root);
2060 target.addLegalOp<UnrealizedConversionCastOp>();
2061 (void)applyPartialConversion(root,
target, std::move(patterns));
2072 typeConverter.addConversion([](
Type type) ->
Type {
return type; });
2074 typeConverter.addConversion([](TensorDescType type) ->
Type {
2075 if (type.getLayoutAttr()) {
2076 return type.dropLayouts();
2084 auto getSubShapeAndCount = [](VectorType vecTy,
2085 xegpu::DistributeLayoutAttr layout)
2088 if (failed(distTyOrFailure))
2095 std::move(loopArgTypes));
2103 target.addDynamicallyLegalOp<xegpu::CreateNdDescOp>(
2104 [&](xegpu::CreateNdDescOp op) {
return !op.getType().getLayoutAttr(); });
2106 target.addDynamicallyLegalDialect<xegpu::XeGPUDialect>([](
Operation *op) {
2107 if (isa<xegpu::ConvertLayoutOp>(op))
2109 auto anchorOp = dyn_cast<AnchorLayoutInterface>(op);
2112 return !anchorOp.getAnchorLayout();
2115 target.addDynamicallyLegalOp<arith::ConstantOp>(
2116 [=](arith::ConstantOp op) ->
bool {
2118 if (!isa<VectorType>(op.getResult().getType()))
2124 target.addDynamicallyLegalDialect<math::MathDialect, arith::ArithDialect>(
2125 [=](
Operation *op) -> std::optional<bool> {
2130 if (op->getNumResults() != 1)
2133 VectorType resultType =
2134 dyn_cast<VectorType>(op->getResult(0).getType());
2139 for (
Value operand : op->getOperands()) {
2140 VectorType operandType = dyn_cast<VectorType>(operand.getType());
2141 if (!operandType || operandType.getShape() != resultType.getShape()) {
2149 target.addDynamicallyLegalOp<vector::ReductionOp>(
2150 [=](vector::ReductionOp op) ->
bool {
2155 target.addDynamicallyLegalOp<vector::MultiDimReductionOp>(
2156 [=](vector::MultiDimReductionOp op) ->
bool {
2157 return !isValidSubgroupMultiReductionOp(op);
2159 target.addDynamicallyLegalOp<vector::CreateMaskOp, vector::ConstantMaskOp,
2160 vector::TransposeOp, vector::BitCastOp,
2161 vector::ShapeCastOp, vector::StepOp,
2162 vector::BroadcastOp>([=](
Operation *op) ->
bool {
2165 target.addDynamicallyLegalOp<vector::ExtractOp>(
2166 [=](vector::ExtractOp op) ->
bool {
2167 if (!isa<VectorType>(op.getType()))
2171 target.addDynamicallyLegalOp<vector::InsertOp>(
2172 [=](vector::InsertOp op) ->
bool {
2175 target.addDynamicallyLegalOp<vector::ExtractStridedSliceOp>(
2176 [=](vector::ExtractStridedSliceOp op) ->
bool {
2179 target.addDynamicallyLegalOp<vector::InsertStridedSliceOp>(
2180 [=](vector::InsertStridedSliceOp op) ->
bool {
2183 target.addDynamicallyLegalOp<vector::InterleaveOp, vector::DeinterleaveOp>(
2187 target.markUnknownOpDynamicallyLegal([](
Operation *op) {
return true; });
2189 SgToLaneCreateNdDesc, SgToLaneLoadNd, SgToLaneStoreNd, SgToLaneDpas,
2190 SgToLaneElementWise, SgToLaneArithConstant, SgToLanePrefetchNd,
2191 SgToLaneLoadGather, SgToLaneStoreScatter, SgToLaneVectorReduction,
2192 SgToLaneMultiDimReduction, SgToLaneVectorExtract, SgToLaneVectorInsert,
2193 SgToLaneVectorExtractStridedSlice, SgToLaneVectorInsertStridedSlice,
2194 SgToLaneLoadMatrix, SgToLaneStoreMatrix, SgToLaneConvertLayout,
2195 SgToLaneVectorTranspose, SgToLaneVectorBitcast, SgToLaneVectorStep,
2196 SgToLaneVectorShapeCast, SgToLaneBroadcast,
2197 SgToLaneCreateMask<vector::CreateMaskOp>,
2198 SgToLaneCreateMask<vector::ConstantMaskOp>, SgToLaneVectorDeinterleave,
2199 SgToLaneVectorInterleave, SgToLaneDpasMx>(typeConverter,
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.
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
OperationName getName()
The name of an operation is the key identifier for it.
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.
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.
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.
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.