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;
91static bool isReductionLaneLocal(vector::MultiDimReductionOp op) {
93 assert(isValidSubgroupMultiReductionOp(op) &&
"Expecting a valid subgroup "
94 "MultiDimReductionOp");
97 assert(reductionDims.size() == 1 &&
98 "Expecting single reduction dimension for subgroup multi "
100 int64_t reductionDim = reductionDims[0];
102 assert(reductionDim <
static_cast<int64_t>(srcLaneLayout.size()) &&
103 "Expecting a source lane_layout covering the reduction dimension");
104 return srcLaneLayout[reductionDim] == 1;
110 VectorType distributedType) {
111 assert(originalType.getRank() == distributedType.getRank() &&
112 "original and distributed vector types must have the same rank");
114 for (
int64_t i = 0; i < originalType.getRank(); ++i) {
115 if (distributedType.getDimSize(i) != originalType.getDimSize(i))
116 distributedDims.push_back(i);
118 return distributedDims;
123struct SgToLaneCreateNdDesc
124 :
public OpConversionPattern<xegpu::CreateNdDescOp> {
125 using OpConversionPattern<xegpu::CreateNdDescOp>::OpConversionPattern;
128 matchAndRewrite(xegpu::CreateNdDescOp op, OpAdaptor adaptor,
129 ConversionPatternRewriter &rewriter)
const override {
130 xegpu::TensorDescType resultType = op.getType();
132 if (!resultType.getLayout())
135 auto newOp = xegpu::CreateNdDescOp::create(
136 rewriter, op.getLoc(),
TypeRange{resultType.dropLayouts()},
137 op.getOperands(), op.getProperties(),
138 op->getDiscardableAttrDictionary().getValue());
139 rewriter.replaceOp(op, newOp.getResult());
147struct SgToLaneLoadNd :
public OpConversionPattern<xegpu::LoadNdOp> {
148 using OpConversionPattern<xegpu::LoadNdOp>::OpConversionPattern;
151 matchAndRewrite(xegpu::LoadNdOp op, OpAdaptor adaptor,
152 ConversionPatternRewriter &rewriter)
const override {
153 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
159 if (op.getTensorDescType().getLayout() != layout)
160 return rewriter.notifyMatchFailure(
161 op,
"conflicting layout attributes on tensor descriptor and anchor");
165 return rewriter.notifyMatchFailure(
166 op,
"xegpu::LoadNdOp require target attribute attached to "
167 "determine transpose "
169 auto supportedLaneResultTyOrFailure =
171 auto expectedLaneResultTyOrFailure =
173 if (failed(supportedLaneResultTyOrFailure))
174 return rewriter.notifyMatchFailure(
175 op,
"unable to compute the lane vector type for LoadNdOp");
176 if (failed(expectedLaneResultTyOrFailure))
177 return rewriter.notifyMatchFailure(
178 op,
"unable to compute expected lane vector type from lane layout");
179 auto newOp = xegpu::LoadNdOp::create(
180 rewriter, op.getLoc(), supportedLaneResultTyOrFailure.value(),
181 adaptor.getTensorDesc(), op.getMixedOffsets(), op.getPackedAttr(),
182 op.getTransposeAttr(), op.getL1HintAttr(), op.getL2HintAttr(),
183 op.getL3HintAttr(),
nullptr);
189 rewriter.replaceOp(op, castValueTo(rewriter, newOp.getResult(),
190 expectedLaneResultTyOrFailure.value()));
198struct SgToLaneStoreNd :
public OpConversionPattern<xegpu::StoreNdOp> {
199 using OpConversionPattern<xegpu::StoreNdOp>::OpConversionPattern;
202 matchAndRewrite(xegpu::StoreNdOp op, OpAdaptor adaptor,
203 ConversionPatternRewriter &rewriter)
const override {
204 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
210 if (op.getTensorDescType().getLayout() != layout)
211 return rewriter.notifyMatchFailure(
212 op,
"conflicting layout attributes on tensor descriptor and anchor");
214 if (valueLayout != layout)
215 return rewriter.notifyMatchFailure(
216 op,
"conflicting layout attributes on value and anchor");
217 auto supportedLaneValueTyOrFailure =
219 if (failed(supportedLaneValueTyOrFailure))
220 return rewriter.notifyMatchFailure(
222 "unable to compute lane vector type for StoreNdOp value from tensor "
225 xegpu::StoreNdOp::create(
226 rewriter, op.getLoc(),
228 supportedLaneValueTyOrFailure.value()),
229 adaptor.getTensorDesc(), op.getMixedOffsets(), op.getL1HintAttr(),
230 op.getL2HintAttr(), op.getL3HintAttr(),
nullptr);
231 rewriter.eraseOp(op);
239struct SgToLaneDpas :
public OpConversionPattern<xegpu::DpasOp> {
240 using OpConversionPattern<xegpu::DpasOp>::OpConversionPattern;
243 matchAndRewrite(xegpu::DpasOp op, OpAdaptor adaptor,
244 ConversionPatternRewriter &rewriter)
const override {
246 auto layoutA = cast<xegpu::LayoutAttr>(op.getLayoutAAttr());
247 auto layoutB = cast<xegpu::LayoutAttr>(op.getLayoutBAttr());
248 auto layoutCd = cast<xegpu::LayoutAttr>(op.getLayoutCdAttr());
249 if (!layoutA || !layoutB || !layoutCd)
251 auto laneResultTyOrFailure =
253 auto laneATypeOrFailure =
255 auto laneBTypeOrFailure =
257 auto expectedLaneResultTyOrFailure =
259 if (failed(laneResultTyOrFailure) || failed(laneATypeOrFailure) ||
260 failed(laneBTypeOrFailure))
261 return rewriter.notifyMatchFailure(
262 op,
"failed to calculate supported lane vector types for DpasOp "
264 if (failed(expectedLaneResultTyOrFailure))
265 return rewriter.notifyMatchFailure(
266 op,
"unable to compute expected lane vector type for DpasOp from "
273 const auto *uArchInstruction =
274 dyn_cast<xegpu::uArch::SubgroupMatrixMultiplyAcc>(
275 uArch->getInstruction(
277 if (uArchInstruction) {
278 auto laneAType = laneATypeOrFailure.value();
279 auto laneBType = laneBTypeOrFailure.value();
281 unsigned aPackedBitWidth =
282 laneAType.getElementTypeBitWidth() * laneAType.getNumElements();
283 unsigned bPackedBitWidth =
284 laneBType.getElementTypeBitWidth() * laneBType.getNumElements();
285 unsigned expectedABitSize = uArchInstruction->getPackedFormatBitSizeA();
286 unsigned expectedBBitSize = uArchInstruction->getPackedFormatBitSizeB();
288 if (aPackedBitWidth % expectedABitSize != 0)
289 return rewriter.notifyMatchFailure(
291 "A operand packed bit width must be a multiple of uArch packed "
292 "format requirement");
293 if (bPackedBitWidth % expectedBBitSize != 0)
294 return rewriter.notifyMatchFailure(
296 "B operand packed bit width must be a multiple of uArch packed "
297 "format requirement");
301 auto newOp = xegpu::DpasOp::create(
302 rewriter, op->getLoc(), laneResultTyOrFailure.value(),
304 laneATypeOrFailure.value()),
306 laneBTypeOrFailure.value()),
308 laneResultTyOrFailure.value()),
312 rewriter.replaceOp(op, castValueTo(rewriter, newOp.getResult(),
313 expectedLaneResultTyOrFailure.value()));
320struct SgToLaneElementWise :
public ConversionPattern {
322 : ConversionPattern(MatchAnyOpTypeTag(), 1, ctx) {}
326 ConversionPatternRewriter &rewriter)
const override {
333 return rewriter.notifyMatchFailure(
334 op,
"operation result is not a vector type");
336 xegpu::DistributeLayoutAttr layout =
338 if (!layout || !layout.isForSubgroup())
339 return rewriter.notifyMatchFailure(
340 op,
"operation result does not have subgroup distribute layout");
342 auto laneShapeOrFailure =
345 if (failed(laneShapeOrFailure))
346 return rewriter.notifyMatchFailure(
347 op,
"unable to compute lane vector type from the layout");
349 VectorType newResultType = laneShapeOrFailure.value();
351 state.addOperands(operands);
352 state.addTypes(newResultType);
355 if (!isa<xegpu::DistributeLayoutAttr>(attr.getValue()))
356 state.addAttribute(attr.getName(), attr.getValue());
359 Operation *newOp = rewriter.create(state);
361 rewriter.replaceOp(op, newOp->
getResult(0));
373struct SgToLaneArithConstant :
public OpConversionPattern<arith::ConstantOp> {
374 using OpConversionPattern<arith::ConstantOp>::OpConversionPattern;
377 matchAndRewrite(arith::ConstantOp op, OpAdaptor adaptor,
378 ConversionPatternRewriter &rewriter)
const override {
379 auto resultType = dyn_cast<VectorType>(op.getType());
384 auto denseAttr = dyn_cast<DenseElementsAttr>(op.getValue());
386 return rewriter.notifyMatchFailure(
387 op,
"only dense vector constants are supported");
389 xegpu::DistributeLayoutAttr layout =
391 if (!layout || !layout.isForSubgroup())
392 return rewriter.notifyMatchFailure(
393 op,
"operation result does not have subgroup distribute layout");
395 auto laneShapeOrFailure =
398 if (
failed(laneShapeOrFailure))
399 return rewriter.notifyMatchFailure(
400 op,
"unable to compute lane vector type from the layout");
402 VectorType newResultType = laneShapeOrFailure.value();
403 Location loc = op.getLoc();
407 if (denseAttr.isSplat()) {
408 auto scalarValue = denseAttr.getSplatValue<Attribute>();
411 arith::ConstantOp::create(rewriter, loc, newResultType, newDenseAttr);
412 rewriter.replaceOp(op, newOp.
getResult());
419 arith::ConstantOp::create(rewriter, loc, resultType, denseAttr);
421 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
422 mlir::IntegerAttr());
423 auto maybeCoordsVec = layout.computeDistributedCoords(
424 rewriter, loc, laneId, resultType.getShape());
425 if (
failed(maybeCoordsVec))
426 return rewriter.notifyMatchFailure(
427 op,
"failed to compute distributed coordinates from layout");
429 SmallVector<SmallVector<Value>> coordsVec = maybeCoordsVec.value();
430 SmallVector<int64_t> laneData = layout.getEffectiveLaneDataAsInt();
431 ArrayRef<int64_t> distShape = newResultType.getShape();
432 int64_t rank = newResultType.getRank();
437 SmallVector<int64_t> blockGridShape(rank);
438 for (int64_t d = 0; d < rank; d++)
439 blockGridShape[d] = distShape[d] / laneData[d];
440 SmallVector<int64_t> blockGridStrides =
computeStrides(blockGridShape);
442 auto blockType = VectorType::get(laneData, newResultType.getElementType());
443 SmallVector<int64_t> unitTile(rank, 1);
444 SmallVector<int64_t> strides(rank, 1);
446 Value
result = arith::ConstantOp::create(
447 rewriter, loc, newResultType, rewriter.getZeroAttr(newResultType));
449 for (
auto [blockIdx, blockStart] : llvm::enumerate(coordsVec)) {
453 SmallVector<Value> blockElems;
454 for (SmallVector<int64_t> off :
455 StaticTileOffsetRange(laneData, unitTile)) {
456 SmallVector<OpFoldResult> pos(rank);
457 for (int64_t d = 0; d < rank; d++)
459 rewriter, loc, blockStart[d],
461 blockElems.push_back(vector::ExtractOp::create(
462 rewriter, loc, fullConst.getResult(), pos));
469 vector::FromElementsOp::create(rewriter, loc, blockType, blockElems);
470 SmallVector<int64_t> blockGridPos =
472 SmallVector<int64_t> offsets(rank);
473 for (int64_t d = 0; d < rank; d++)
474 offsets[d] = blockGridPos[d] * laneData[d];
475 result = vector::InsertStridedSliceOp::create(rewriter, loc, block,
476 result, offsets, strides);
479 rewriter.replaceOp(op,
result);
485struct SgToLanePrefetchNd :
public OpConversionPattern<xegpu::PrefetchNdOp> {
486 using OpConversionPattern<xegpu::PrefetchNdOp>::OpConversionPattern;
489 matchAndRewrite(xegpu::PrefetchNdOp op, OpAdaptor adaptor,
490 ConversionPatternRewriter &rewriter)
const override {
491 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
496 xegpu::PrefetchNdOp::create(rewriter, op.getLoc(), adaptor.getTensorDesc(),
497 op.getMixedOffsets(), op.getL1HintAttr(),
498 op.getL2HintAttr(), op.getL3HintAttr(),
500 rewriter.eraseOp(op);
530struct SgToLaneLoadGather :
public OpConversionPattern<xegpu::LoadGatherOp> {
531 using OpConversionPattern<xegpu::LoadGatherOp>::OpConversionPattern;
534 matchAndRewrite(xegpu::LoadGatherOp op, OpAdaptor adaptor,
535 ConversionPatternRewriter &rewriter)
const override {
536 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
540 VectorType origResultTy = op.getValueType();
545 int effectiveVecRank = 1;
546 ArrayRef<int64_t> shape = origResultTy.getShape();
548 shape.take_front(origResultTy.getRank() - effectiveVecRank),
549 [](int64_t d) { return d != 1; }))
550 return rewriter.notifyMatchFailure(
551 op,
"Only unit dimensions allowed for the leading "
552 "dimensions of the load vector!");
554 auto distResultTyOrFailure =
556 if (
failed(distResultTyOrFailure))
557 return rewriter.notifyMatchFailure(
558 op,
"unable to compute expected lane vector type from lane layout");
560 VectorType distResultTy = distResultTyOrFailure.value();
561 VectorType distResultTy1D = VectorType::get({distResultTy.getNumElements()},
562 distResultTy.getElementType());
565 Value distOffsets = adaptor.getOffsets();
566 auto distOffsetsTy = cast<VectorType>(distOffsets.
getType());
567 VectorType offsetsTy1D = VectorType::get({distOffsetsTy.getNumElements()},
568 distOffsetsTy.getElementType());
569 distOffsets = castValueTo(
572 Value distMask = adaptor.getMask();
573 auto distMaskTy = cast<VectorType>(distMask.
getType());
574 VectorType maskTy1D = VectorType::get({distMaskTy.getNumElements()},
575 distMaskTy.getElementType());
579 Value distSource = adaptor.getSource();
580 auto newOp = xegpu::LoadGatherOp::create(
581 rewriter, op.getLoc(), distResultTy1D, distSource, distOffsets,
582 distMask, op.getL1HintAttr(), op.getL2HintAttr(), op.getL3HintAttr(),
586 if (distResultTy1D != distResultTy)
589 rewriter.replaceOp(op,
result);
598struct SgToLaneVectorReduction
599 :
public OpConversionPattern<vector::ReductionOp> {
600 using OpConversionPattern<vector::ReductionOp>::OpConversionPattern;
603 matchAndRewrite(vector::ReductionOp op, OpAdaptor adaptor,
604 ConversionPatternRewriter &rewriter)
const override {
608 if (!layout || !layout.isForSubgroup())
611 VectorType srcVecType = op.getSourceVectorType();
613 if (srcVecType.getRank() != 1)
614 return rewriter.notifyMatchFailure(
615 op,
"Only rank 1 reductions can be distributed.");
617 if (layout.getRank() != srcVecType.getRank())
618 return rewriter.notifyMatchFailure(
619 op,
"Layout rank does not match vector rank.");
622 int64_t sgSize = layout.getEffectiveLaneLayoutAsInt()[0];
626 return rewriter.notifyMatchFailure(
627 op,
"xegpu::ReductionOp require target attribute attached to "
628 "determine subgroup size");
631 if (sgSize != uArch->getSubgroupSize() ||
632 srcVecType.getShape()[0] % sgSize != 0)
633 return rewriter.notifyMatchFailure(op,
634 "Invalid layout or reduction vector "
635 "dimension must match subgroup size.");
637 if (!op.getType().isIntOrFloat())
638 return rewriter.notifyMatchFailure(
639 op,
"Reduction distribution currently only supports floats and "
643 Value laneValVec = adaptor.getVector();
648 op.getKind(), sgSize, op.getFastmathAttr());
651 if (adaptor.getAcc())
653 rewriter, op.getLoc(), op.getKind(), fullReduce, adaptor.getAcc(),
654 op.getFastmathAttr());
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];
696 adaptor.getSource(), op.getKind(),
697 reductionDimSize, op.getFastmathAttr());
699 if (adaptor.getAcc())
702 op.getFastmathAttr());
703 }
else if (isReductionLaneLocal(op)) {
707 auto reductionDim = reductionDims[0];
711 reductionDim, op.getLoc(), rewriter, op.getFastmathAttr());
713 auto reductionDim = reductionDims[0];
714 VectorType sourceType = op.getSourceVectorType();
715 int64_t reductionDimSize = sourceType.getShape()[reductionDim];
719 reductionDim, reductionDimSize, op.getLoc(), rewriter,
720 op.getFastmathAttr());
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);
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 =
772 if (
failed(distPayloadTyOrFailure))
773 return rewriter.notifyMatchFailure(
774 op,
"Failed to distribute matrix op payload based on layout.");
776 SmallVector<Value> offsetsAsValues =
779 SmallVector<Value> newCoords = offsetsAsValues;
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.");
788 SmallVector<int64_t> newConstOffsets(op.getConstOffsets().size(),
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 "
818 ArrayRef<int64_t> perm = op.getPermutation();
820 if (!resultLayout.isTransposeOf(sourceLayout, perm,
821 xegpu::LayoutKind::Lane))
822 return rewriter.notifyMatchFailure(
823 op,
"the source or result vector layouts must be transposes of "
825 FailureOr<VectorType> distributedResultTypeOrFailure =
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 =
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());
904template <
typename OpType,
905 typename = std::enable_if_t<llvm::is_one_of<
906 OpType, vector::CreateMaskOp, vector::ConstantMaskOp>::value>>
907struct SgToLaneCreateMask :
public OpConversionPattern<OpType> {
908 using OpConversionPattern<OpType>::OpConversionPattern;
911 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
912 ConversionPatternRewriter &rewriter)
const override {
913 xegpu::DistributeLayoutAttr layout =
915 if (!layout || !layout.isForSubgroup())
916 return rewriter.notifyMatchFailure(
917 op,
"operation result does not have subgroup distribute layout");
919 VectorType origType = op.getType();
920 FailureOr<VectorType> distTypeOrFailure =
922 if (
failed(distTypeOrFailure))
923 return rewriter.notifyMatchFailure(
924 op,
"unable to compute lane vector type from the layout");
926 VectorType distType = distTypeOrFailure.value();
927 Location loc = op.getLoc();
930 SmallVector<Value> origBounds;
931 if constexpr (std::is_same_v<OpType, vector::CreateMaskOp>) {
932 origBounds.append(op.getOperands().begin(), op.getOperands().end());
934 auto dimSizes = op.getMaskDimSizesAttr().asArrayRef();
935 for (
auto dimSize : dimSizes)
936 origBounds.push_back(
940 ArrayRef<int64_t> origShape = origType.getShape();
943 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
944 mlir::IntegerAttr());
945 auto maybeCoordsVec =
946 layout.computeDistributedCoords(rewriter, loc, laneId, origShape);
947 if (
failed(maybeCoordsVec))
948 return rewriter.notifyMatchFailure(
949 op,
"failed to compute distributed coordinates from layout");
951 SmallVector<SmallVector<Value>> laneDataCoords = maybeCoordsVec.value();
952 SmallVector<int64_t> laneData = layout.getEffectiveLaneDataAsInt();
953 ArrayRef<int64_t> distShape = distType.getShape();
954 int64_t rank = distType.getRank();
955 int64_t numElements = distType.getNumElements();
957 if (
static_cast<int64_t
>(laneData.size()) != rank ||
959 return rewriter.notifyMatchFailure(
960 op,
"lane_data does not tile the distributed vector");
963 assert(
static_cast<int64_t
>(laneDataCoords.size()) *
966 "number of coordinate sets must match number of lane_data blocks");
969 SmallVector<SmallVector<int64_t>> staticLaneDataOffsetsOrig =
970 layout.computeStaticDistributedCoords(0, origShape);
971 if (staticLaneDataOffsetsOrig.empty() ||
972 staticLaneDataOffsetsOrig.size() != laneDataCoords.size())
973 return rewriter.notifyMatchFailure(
974 op,
"static and dynamic coordinates disagree on the distribution "
977 SmallVector<int64_t> unitTile(rank, 1);
978 int64_t linearLaneDataIdx = 0;
983 SmallVector<SmallVector<int64_t>> staticElemOffset(
984 rank, SmallVector<int64_t>(numElements));
986 for (SmallVector<int64_t> laneDataOffsetDist :
987 StaticTileOffsetRange(distShape, laneData)) {
988 ArrayRef<int64_t> staticLaneDataOffsetOrig =
989 staticLaneDataOffsetsOrig[linearLaneDataIdx++];
991 for (SmallVector<int64_t> elemOffsetInLaneData :
992 StaticTileOffsetRange(laneData, unitTile)) {
994 SmallVector<int64_t> elementOffsetDist(rank);
995 for (int64_t d = 0; d < rank; d++)
996 elementOffsetDist[d] =
997 laneDataOffsetDist[d] + elemOffsetInLaneData[d];
998 int64_t elemLinearizedIdxDist =
999 linearize(elementOffsetDist, distStrides);
1000 for (int64_t d = 0; d < rank; d++)
1001 staticElemOffset[d][elemLinearizedIdxDist] =
1002 staticLaneDataOffsetOrig[d] + elemOffsetInLaneData[d];
1011 auto flatIndexType = VectorType::get(numElements, rewriter.getIndexType());
1013 for (int64_t d = 0; d < rank; d++) {
1015 if (constBound && *constBound >= origShape[d])
1018 Value materializedStaticOffset = arith::ConstantOp::create(
1019 rewriter, loc, rewriter.getIndexVectorAttr(staticElemOffset[d]));
1022 Value validDimExtent = arith::SubIOp::create(rewriter, loc, origBounds[d],
1023 laneDataCoords[0][d]);
1025 Value validDimExtentPerElement = vector::BroadcastOp::create(
1026 rewriter, loc, flatIndexType, validDimExtent);
1027 Value elementMaskInDim = arith::CmpIOp::create(
1028 rewriter, loc, arith::CmpIPredicate::slt, materializedStaticOffset,
1029 validDimExtentPerElement);
1032 inBounds = inBounds ? arith::AndIOp::create(rewriter, loc, inBounds,
1040 rewriter.replaceOp(op, arith::ConstantOp::create(
1041 rewriter, loc, distType,
1046 rewriter.createOrFold<vector::ShapeCastOp>(loc, distType, inBounds);
1047 rewriter.replaceOp(op, resMask);
1053struct SgToLaneStoreMatrix :
public OpConversionPattern<xegpu::StoreMatrixOp> {
1054 using OpConversionPattern<xegpu::StoreMatrixOp>::OpConversionPattern;
1057 matchAndRewrite(xegpu::StoreMatrixOp op, OpAdaptor adaptor,
1058 ConversionPatternRewriter &rewriter)
const override {
1059 auto layout = op.getLayoutAttr();
1064 VectorType sgPayloadTy = dyn_cast<VectorType>(op.getData().getType());
1066 return rewriter.notifyMatchFailure(
1067 op,
"the matrix op payload must be a vector type");
1069 auto loc = op.getLoc();
1070 auto offsets = op.getMixedOffsets();
1071 if (offsets.empty())
1072 return rewriter.notifyMatchFailure(op,
"the store op must have offsets");
1074 FailureOr<VectorType> distPayloadTyOrFailure =
1076 if (
failed(distPayloadTyOrFailure))
1077 return rewriter.notifyMatchFailure(
1078 op,
"Failed to distribute matrix op payload based on layout.");
1080 SmallVector<Value> offsetsAsValues =
1083 SmallVector<Value> newCoords = offsetsAsValues;
1084 if (!op.getSubgroupBlockIoAttr()) {
1085 newCoords = computeDistributedCoordsForMatrixOp(
1086 rewriter, loc, layout, sgPayloadTy.getShape(), offsetsAsValues);
1087 if (newCoords.empty())
1088 return rewriter.notifyMatchFailure(
1089 op,
"Failed to compute distributed coordinates.");
1092 SmallVector<int64_t> newConstOffsets(op.getConstOffsets().size(),
1093 ShapedType::kDynamic);
1095 rewriter.getDenseI64ArrayAttr(newConstOffsets);
1097 xegpu::StoreMatrixOp::create(
1100 distPayloadTyOrFailure.value()),
1101 adaptor.getMemDesc(),
ValueRange(newCoords), newConstOffsetsAttr,
1102 op.getSubgroupBlockIoAttr(), xegpu::DistributeLayoutAttr{});
1103 rewriter.eraseOp(op);
1134struct SgToLaneStoreScatter
1135 :
public OpConversionPattern<xegpu::StoreScatterOp> {
1136 using OpConversionPattern<xegpu::StoreScatterOp>::OpConversionPattern;
1139 matchAndRewrite(xegpu::StoreScatterOp op, OpAdaptor adaptor,
1140 ConversionPatternRewriter &rewriter)
const override {
1141 xegpu::DistributeLayoutAttr layout = op.getAnchorLayout();
1145 VectorType origValueTy = op.getValueType();
1150 int effectiveVecRank = 1;
1151 ArrayRef<int64_t> shape = origValueTy.getShape();
1152 if (llvm::any_of(shape.take_front(origValueTy.getRank() - effectiveVecRank),
1153 [](int64_t d) { return d != 1; }))
1154 return rewriter.notifyMatchFailure(
1155 op,
"Only unit dimensions allowed for the leading "
1156 "dimensions of the store vector!");
1158 auto distValueTyOrFailure =
1160 if (
failed(distValueTyOrFailure))
1161 return rewriter.notifyMatchFailure(
1162 op,
"unable to compute expected lane vector type from lane layout");
1164 VectorType distValueTy = distValueTyOrFailure.value();
1165 VectorType distValueTy1D = VectorType::get({distValueTy.getNumElements()},
1166 distValueTy.getElementType());
1168 Value distValue = adaptor.getValue();
1169 if (distValue.
getType() != distValueTy1D)
1174 Value distOffsets = adaptor.getOffsets();
1175 auto distOffsetsTy = cast<VectorType>(distOffsets.
getType());
1176 VectorType offsetsTy1D = VectorType::get({distOffsetsTy.getNumElements()},
1177 distOffsetsTy.getElementType());
1178 distOffsets = castValueTo(
1181 Value distMask = adaptor.getMask();
1182 auto distMaskTy = cast<VectorType>(distMask.
getType());
1183 VectorType maskTy1D = VectorType::get({distMaskTy.getNumElements()},
1184 distMaskTy.getElementType());
1188 Value distDest = adaptor.getDest();
1189 xegpu::StoreScatterOp::create(rewriter, op.getLoc(), distValue, distDest,
1190 distOffsets, distMask, op.getL1HintAttr(),
1191 op.getL2HintAttr(), op.getL3HintAttr(),
1194 rewriter.eraseOp(op);
1203struct SgToLaneVectorStep :
public OpConversionPattern<vector::StepOp> {
1204 using OpConversionPattern<vector::StepOp>::OpConversionPattern;
1207 matchAndRewrite(vector::StepOp op, OpAdaptor adaptor,
1208 ConversionPatternRewriter &rewriter)
const override {
1209 xegpu::DistributeLayoutAttr resultLayout =
1211 if (!resultLayout || !resultLayout.isForSubgroup())
1212 return rewriter.notifyMatchFailure(
1213 op,
"the result vector of the step op lacks subgroup layout");
1215 auto loc = op.getLoc();
1216 auto stepResultVecTy = op.getResult().getType();
1217 auto laneShapeOrFailure =
1219 if (
failed(laneShapeOrFailure))
1220 return rewriter.notifyMatchFailure(
1221 op,
"unable to compute lane vector type from the layout");
1222 VectorType newVecTy = laneShapeOrFailure.value();
1224 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
1225 mlir::IntegerAttr());
1226 auto laneDataBlockCoords = resultLayout.computeDistributedCoords(
1227 rewriter, loc, laneId, stepResultVecTy.getShape());
1228 if (
failed(laneDataBlockCoords))
1229 return rewriter.notifyMatchFailure(
1230 op,
"failed to compute lane data block coordinates");
1232 auto laneDataBlockCoordsVec = laneDataBlockCoords.value();
1233 auto laneDataBlockLength = resultLayout.getEffectiveLaneDataAsInt()[0];
1234 assert(
static_cast<int64_t
>(laneDataBlockCoordsVec.size()) ==
1235 newVecTy.getNumElements() / laneDataBlockLength);
1236 SmallVector<Value> stepVals;
1244 for (
auto &laneDataBlockCoords : laneDataBlockCoordsVec) {
1245 auto laneDataBlockStartCoord = laneDataBlockCoords[0];
1246 stepVals.push_back(laneDataBlockStartCoord);
1247 for (
int i = 1; i < laneDataBlockLength; ++i) {
1249 stepVals.push_back(arith::AddIOp::create(
1250 rewriter, loc, laneDataBlockStartCoord, offset));
1253 assert(
static_cast<int64_t
>(stepVals.size()) == newVecTy.getNumElements() &&
1254 "Expecting the number of step values to match the number of "
1255 "elements in the vector");
1257 vector::FromElementsOp::create(rewriter, loc, newVecTy, stepVals);
1258 rewriter.replaceOp(op, stepOpVal);
1265struct SgToLaneVectorExtract :
public OpConversionPattern<vector::ExtractOp> {
1266 using OpConversionPattern<vector::ExtractOp>::OpConversionPattern;
1269 matchAndRewrite(vector::ExtractOp op, OpAdaptor adaptor,
1270 ConversionPatternRewriter &rewriter)
const override {
1272 auto resultType = dyn_cast<VectorType>(op.getType());
1274 return rewriter.notifyMatchFailure(op,
"scalar extract not supported");
1276 xegpu::DistributeLayoutAttr layout =
1278 if (!layout || !layout.isForSubgroup())
1283 auto laneLayout = layout.getEffectiveLaneLayoutAsInt();
1284 if (llvm::any_of(ArrayRef<int64_t>(laneLayout).drop_back(1),
1285 [](int64_t v) {
return v != 1; }))
1286 return rewriter.notifyMatchFailure(
1287 op,
"only innermost dimension distribution is supported for "
1290 auto newOp = vector::ExtractOp::create(
1291 rewriter, op.getLoc(), adaptor.getSource(), op.getMixedPosition());
1292 rewriter.replaceOp(op, newOp.
getResult());
1298struct SgToLaneVectorShapeCast
1299 :
public OpConversionPattern<vector::ShapeCastOp> {
1300 using OpConversionPattern<vector::ShapeCastOp>::OpConversionPattern;
1303 matchAndRewrite(vector::ShapeCastOp op, OpAdaptor adaptor,
1304 ConversionPatternRewriter &rewriter)
const override {
1305 xegpu::DistributeLayoutAttr resultLayout =
1307 if (!resultLayout || !resultLayout.isForSubgroup())
1308 return rewriter.notifyMatchFailure(
1309 op,
"the result vector of the shape_cast op lacks subgroup layout");
1312 resultLayout, op.getResultVectorType());
1313 if (
failed(resultDistTypeOrFailure))
1314 return rewriter.notifyMatchFailure(
1315 op,
"failed to get distributed vector type for result");
1317 Value source = adaptor.getSource();
1318 auto newShapeCast = vector::ShapeCastOp::create(
1319 rewriter, op.getLoc(), resultDistTypeOrFailure.value(), source);
1320 rewriter.replaceOp(op, newShapeCast);
1328struct SgToLaneVectorExtractStridedSlice
1329 :
public OpConversionPattern<vector::ExtractStridedSliceOp> {
1330 using OpConversionPattern<vector::ExtractStridedSliceOp>::OpConversionPattern;
1333 matchAndRewrite(vector::ExtractStridedSliceOp op, OpAdaptor adaptor,
1334 ConversionPatternRewriter &rewriter)
const override {
1335 xegpu::DistributeLayoutAttr resultLayout =
1337 if (!resultLayout || !resultLayout.isForSubgroup())
1340 VectorType resultType = op.getType();
1341 auto distResultTyOrFailure =
1343 if (
failed(distResultTyOrFailure))
1344 return rewriter.notifyMatchFailure(
1345 op,
"unable to compute distributed vector type from lane layout");
1346 VectorType distResultTy = *distResultTyOrFailure;
1348 SmallVector<int64_t> distributedDims =
1349 getDistributedDims(resultType, distResultTy);
1352 int64_t sourceRank = op.getSourceVectorType().getRank();
1353 SmallVector<Attribute> updatedSizes =
1354 llvm::map_to_vector(op.getSizes(), [](Attribute attr) { return attr; });
1355 SmallVector<Attribute> updatedOffsets = llvm::map_to_vector(
1356 op.getOffsets(), [](Attribute attr) { return attr; });
1357 SmallVector<Attribute> updatedStrides = llvm::map_to_vector(
1358 op.getStrides(), [](Attribute attr) { return attr; });
1359 for (int64_t i = op.getSizes().size(); i < sourceRank; ++i) {
1360 updatedSizes.push_back(
1361 rewriter.getI64IntegerAttr(op.getSourceVectorType().getDimSize(i)));
1362 updatedOffsets.push_back(rewriter.getI64IntegerAttr(0));
1363 updatedStrides.push_back(rewriter.getI64IntegerAttr(1));
1368 if (!distributedDims.empty()) {
1370 if (!sourceLayout || sourceLayout.getEffectiveLaneLayoutAsInt().empty())
1371 return rewriter.notifyMatchFailure(
1372 op,
"source of extract_strided_slice lacks distribution layout");
1373 SmallVector<int64_t> laneLayout =
1374 sourceLayout.getEffectiveLaneLayoutAsInt();
1375 SmallVector<int64_t> laneData = sourceLayout.getEffectiveLaneDataAsInt();
1376 ArrayRef<int64_t> sourceShape = op.getSourceVectorType().getShape();
1377 for (int64_t distDim : distributedDims) {
1378 int64_t lanes = laneLayout[distDim];
1379 if (lanes == 0 || sourceShape[distDim] % lanes != 0)
1380 return rewriter.notifyMatchFailure(
1381 op,
"source size along a distributed dim is not a multiple of "
1383 int64_t distrDimOffset =
1384 cast<IntegerAttr>(updatedOffsets[distDim]).getInt();
1385 if (distrDimOffset % (lanes * laneData[distDim]) != 0)
1386 return rewriter.notifyMatchFailure(
1387 op,
"offset along a distributed dim is not a multiple of its "
1389 updatedSizes[distDim] =
1390 rewriter.getI64IntegerAttr(distResultTy.getDimSize(distDim));
1391 updatedOffsets[distDim] =
1392 rewriter.getI64IntegerAttr(distrDimOffset / lanes);
1396 auto newOp = vector::ExtractStridedSliceOp::create(
1397 rewriter, op.getLoc(), distResultTy, adaptor.getSource(),
1398 ArrayAttr::get(rewriter.getContext(), updatedOffsets),
1399 ArrayAttr::get(rewriter.getContext(), updatedSizes),
1400 ArrayAttr::get(rewriter.getContext(), updatedStrides));
1401 rewriter.replaceOp(op, newOp.
getResult());
1463struct SgToLaneBroadcast :
public OpConversionPattern<vector::BroadcastOp> {
1464 using OpConversionPattern<vector::BroadcastOp>::OpConversionPattern;
1467 matchAndRewrite(vector::BroadcastOp op, OpAdaptor adaptor,
1468 ConversionPatternRewriter &rewriter)
const override {
1469 xegpu::DistributeLayoutAttr resultLayout =
1471 if (!resultLayout || !resultLayout.isForSubgroup())
1472 return rewriter.notifyMatchFailure(
1473 op,
"result does not have subgroup distribute layout");
1475 VectorType destType = op.getResultVectorType();
1476 VectorType sourceType = dyn_cast<VectorType>(op.getSourceType());
1478 xegpu::DistributeLayoutAttr sourceLayout =
1482 int64_t rankDiff = destType.getRank() - sourceType.getRank();
1485 if (!sourceLayout || !sourceLayout.isSliceOf(resultLayout))
1487 "broadcast source layout must be a slice of result layout");
1488 }
else if (rankDiff == 0) {
1490 auto broadcastUnitDimsSet = op.computeBroadcastedUnitDims();
1491 SmallVector<int64_t> broadcastUnitDims(broadcastUnitDimsSet.begin(),
1492 broadcastUnitDimsSet.end());
1493 assert(sourceLayout.isEqualTo(
1494 sourceLayout.setUnitDimData(broadcastUnitDims)) &&
1495 "The sg_data for unit dimensions should be set as 1");
1496 sourceLayout = sourceLayout.setUnitDimLayout(broadcastUnitDims);
1501 return rewriter.notifyMatchFailure(
1502 op,
"broadcast from scalar must not have a layout attribute");
1507 if (
failed(destDistType))
1508 return rewriter.notifyMatchFailure(
1509 op,
"failed to distribute the result vector type");
1511 Value source = adaptor.getSource();
1513 if (source.
getType() == destDistType.value()) {
1514 rewriter.replaceOp(op, source);
1518 auto newOp = vector::BroadcastOp::create(rewriter, op.getLoc(),
1519 destDistType.value(), source);
1520 rewriter.replaceOp(op, newOp);
1528struct SgToLaneVectorInsertStridedSlice
1529 :
public OpConversionPattern<vector::InsertStridedSliceOp> {
1530 using OpConversionPattern<vector::InsertStridedSliceOp>::OpConversionPattern;
1533 matchAndRewrite(vector::InsertStridedSliceOp op, OpAdaptor adaptor,
1534 ConversionPatternRewriter &rewriter)
const override {
1535 xegpu::DistributeLayoutAttr resultLayout =
1537 if (!resultLayout || !resultLayout.isForSubgroup())
1540 VectorType destType = op.getDestVectorType();
1541 auto distDestTyOrFailure =
1543 if (
failed(distDestTyOrFailure))
1544 return rewriter.notifyMatchFailure(
1545 op,
"unable to compute distributed vector type from lane layout");
1546 VectorType distDestTy = *distDestTyOrFailure;
1548 SmallVector<int64_t> destDistributedDims =
1549 getDistributedDims(destType, distDestTy);
1551 SmallVector<Attribute> updatedOffsets = llvm::map_to_vector(
1552 op.getOffsets(), [](Attribute attr) { return attr; });
1554 if (!destDistributedDims.empty()) {
1555 if (destDistributedDims.size() != 1)
1556 return rewriter.notifyMatchFailure(
1557 op,
"only single dimension distribution is supported");
1558 int64_t destDistDim = destDistributedDims[0];
1560 VectorType srcType = op.getSourceVectorType();
1562 int64_t sourceDistDim =
1563 destDistDim - (destType.getRank() - srcType.getRank());
1564 if (sourceDistDim < 0)
1565 return rewriter.notifyMatchFailure(
1566 op,
"distributed dimension must be in the last k dims of dest");
1570 if (!destLayout || !sourceLayout ||
1571 destLayout.getEffectiveLaneLayoutAsInt().empty() ||
1572 sourceLayout.getEffectiveLaneLayoutAsInt().empty())
1573 return rewriter.notifyMatchFailure(
1574 op,
"source or dest of insert_strided_slice lacks distribution "
1577 auto destLaneData = destLayout.getEffectiveLaneDataAsInt();
1578 auto sourceLaneData = sourceLayout.getEffectiveLaneDataAsInt();
1581 if ((destDistDim <
static_cast<int64_t
>(destLaneData.size()) &&
1582 destLaneData[destDistDim] != 1) ||
1583 (sourceDistDim <
static_cast<int64_t
>(sourceLaneData.size()) &&
1584 sourceLaneData[sourceDistDim] != 1))
1585 return rewriter.notifyMatchFailure(
1586 op,
"expecting unit lane data along the distributed dimension");
1590 auto destLaneLayout = destLayout.getEffectiveLaneLayoutAsInt();
1591 int64_t numLanesAlongDim =
1592 destDistDim < static_cast<int64_t>(destLaneLayout.size())
1593 ? destLaneLayout[destDistDim]
1596 int64_t srcDistrDimSize = srcType.getDimSize(sourceDistDim);
1597 if (srcDistrDimSize % numLanesAlongDim != 0)
1598 return rewriter.notifyMatchFailure(
1599 op,
"source distributed dim size is not a multiple of "
1600 "the number of lanes along that dimension");
1602 int64_t destDistrDimOffset =
1603 cast<IntegerAttr>(op.getOffsets()[destDistDim]).getInt();
1604 if (destDistrDimOffset % numLanesAlongDim != 0)
1605 return rewriter.notifyMatchFailure(
1606 op,
"offset along distributed dim is not a multiple of "
1607 "the number of lanes along that dimension");
1609 updatedOffsets[destDistDim] =
1610 rewriter.getI64IntegerAttr(destDistrDimOffset / numLanesAlongDim);
1613 auto newOp = vector::InsertStridedSliceOp::create(
1614 rewriter, op.getLoc(), distDestTy, adaptor.getValueToStore(),
1616 ArrayAttr::get(rewriter.getContext(), updatedOffsets), op.getStrides());
1617 rewriter.replaceOp(op, newOp.
getResult());
1624struct SgToLaneVectorInsert :
public OpConversionPattern<vector::InsertOp> {
1625 using OpConversionPattern<vector::InsertOp>::OpConversionPattern;
1628 matchAndRewrite(vector::InsertOp op, OpAdaptor adaptor,
1629 ConversionPatternRewriter &rewriter)
const override {
1631 auto valueType = dyn_cast<VectorType>(op.getValueToStoreType());
1633 return rewriter.notifyMatchFailure(op,
"scalar insert not supported");
1635 xegpu::DistributeLayoutAttr layout =
1637 if (!layout || !layout.isForSubgroup())
1642 auto laneLayout = layout.getEffectiveLaneLayoutAsInt();
1643 if (llvm::any_of(ArrayRef<int64_t>(laneLayout).drop_back(1),
1644 [](int64_t v) {
return v != 1; }))
1645 return rewriter.notifyMatchFailure(
1646 op,
"only innermost dimension distribution is supported for "
1649 auto newOp = vector::InsertOp::create(
1650 rewriter, op.getLoc(), adaptor.getValueToStore(), adaptor.getDest(),
1651 op.getMixedPosition());
1652 rewriter.replaceOp(op, newOp.
getResult());
1670static FailureOr<Value>
1671shuffleDataAsLaneLayoutChange(ConversionPatternRewriter &rewriter,
Location loc,
1674 VectorType srcTy = dyn_cast<VectorType>(src.
getType());
1675 if (!srcTy || srcTy.getRank() != 2)
1678 if (targetLaneNum <= 0 || currentLaneNum != targetLaneNum * 2)
1682 srcTy.getNumElements() * srcTy.getElementTypeBitWidth();
1683 if (vectorBitWidth % 32 != 0)
1695 Type shuffleElemTy = rewriter.getI32Type();
1696 int64_t numShuffles = vectorBitWidth / 32;
1697 VectorType shuffleBundleTy = VectorType::get({numShuffles}, shuffleElemTy);
1699 Value temp = arith::ConstantOp::create(
1702 IntegerAttr::get(shuffleElemTy, 0)));
1703 VectorType flatSrcTy =
1704 VectorType::get({srcTy.getNumElements()}, srcTy.getElementType());
1705 Value flatSrc = vector::ShapeCastOp::create(rewriter, loc, flatSrcTy, src);
1706 Value shuffleBundle =
1707 vector::BitCastOp::create(rewriter, loc, shuffleBundleTy, flatSrc);
1708 for (
int64_t i = 0; i < numShuffles; i++) {
1710 vector::ExtractOp::create(rewriter, loc, shuffleBundle, i);
1711 shuffleElem = gpu::ShuffleOp::create(rewriter, loc, shuffleElem, 0,
1712 targetLaneNum, gpu::ShuffleMode::UP)
1714 temp = vector::InsertOp::create(rewriter, loc, shuffleElem, temp, i);
1716 temp = vector::BitCastOp::create(rewriter, loc, flatSrcTy, temp);
1717 temp = vector::ShapeCastOp::create(rewriter, loc, srcTy, temp);
1722 Value res = vector::ShuffleOp::create(rewriter, loc, src, temp,
indices);
1733static FailureOr<Value> repackLaneData(ConversionPatternRewriter &rewriter,
1737 auto srcTy = dyn_cast<VectorType>(src.
getType());
1740 int64_t rank = srcTy.getRank();
1741 Type elemTy = srcTy.getElementType();
1742 int64_t k = srcTy.getShape()[repackDim];
1744 bool roundRobinToContig = inputData == 1 && targetData == k;
1745 bool contigToRoundRobin = inputData == k && targetData == 1;
1746 if (!roundRobinToContig && !contigToRoundRobin)
1751 xegpu::LaneShuffleMode mode = roundRobinToContig
1752 ? xegpu::LaneShuffleMode::Pack
1753 : xegpu::LaneShuffleMode::Unpack;
1754 VectorType runTy = VectorType::get({k}, elemTy);
1758 if (srcTy.getNumElements() == k) {
1761 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, src, mode));
1762 Value flat = vector::ShapeCastOp::create(rewriter, loc, runTy, src);
1764 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, flat, mode);
1765 return Value(vector::ShapeCastOp::create(rewriter, loc, srcTy, shuffled));
1770 if (repackDim == rank - 1) {
1774 Value result = arith::ConstantOp::create(rewriter, loc, srcTy,
1775 rewriter.getZeroAttr(srcTy));
1776 for (
int64_t i = 0; i < numRuns; ++i) {
1778 Value run = vector::ExtractOp::create(rewriter, loc, src, pos);
1780 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, run, mode);
1781 result = vector::InsertOp::create(rewriter, loc, shuffled,
result, pos);
1791 for (
int64_t d = 0; d < rank; ++d)
1792 if (d != repackDim) {
1793 keptShape.push_back(srcTy.getShape()[d]);
1794 keptDims.push_back(d);
1799 sliceSizes[repackDim] = k;
1801 VectorType sliceTy = VectorType::get(sliceSizes, elemTy);
1802 Value result = arith::ConstantOp::create(rewriter, loc, srcTy,
1803 rewriter.getZeroAttr(srcTy));
1804 for (
int64_t i = 0; i < numRuns; ++i) {
1807 for (
auto [dim, coord] : llvm::zip_equal(keptDims, keptPos))
1808 offsets[dim] = coord;
1809 Value slice = vector::ExtractStridedSliceOp::create(
1810 rewriter, loc, src, offsets, sliceSizes, sliceStrides);
1811 Value run = vector::ShapeCastOp::create(rewriter, loc, runTy,
slice);
1813 xegpu::LaneShuffleOp::create(rewriter, loc, runTy, run, mode);
1814 Value repackedSlice =
1815 vector::ShapeCastOp::create(rewriter, loc, sliceTy, repacked);
1816 result = vector::InsertStridedSliceOp::create(
1817 rewriter, loc, repackedSlice,
result, offsets, sliceStrides);
1823struct SgToLaneConvertLayout
1824 :
public OpConversionPattern<xegpu::ConvertLayoutOp> {
1825 using OpConversionPattern<xegpu::ConvertLayoutOp>::OpConversionPattern;
1828 matchAndRewrite(xegpu::ConvertLayoutOp op, OpAdaptor adaptor,
1829 ConversionPatternRewriter &rewriter)
const override {
1830 auto inputLayout = op.getEffectiveInputLayout();
1831 auto targetLayout = op.getTargetLayoutAttr();
1832 Type valType = op.getResult().getType();
1835 rewriter.replaceOp(op, op.getSource());
1839 auto resShape = cast<VectorType>(valType).getShape();
1840 SmallVector<int64_t> resShapeVec(resShape.begin(), resShape.end());
1844 if (inputLayout.isCompatibleWith(targetLayout, resShapeVec,
1845 xegpu::LayoutKind::Lane)) {
1846 rewriter.replaceOp(op, adaptor.getSource());
1857 if (inputLayout.getEffectiveOrderAsInt() ==
1858 targetLayout.getEffectiveOrderAsInt() &&
1859 inputLayout.getRank() == 2 && targetLayout.getRank() == 2) {
1860 auto laneLayout = inputLayout.getEffectiveLaneLayoutAsInt();
1861 auto targetLaneLayout = targetLayout.getEffectiveLaneLayoutAsInt();
1862 auto laneData = inputLayout.getEffectiveLaneDataAsInt();
1863 auto targetLaneData = targetLayout.getEffectiveLaneDataAsInt();
1864 if (laneLayout.size() == 2 && targetLaneLayout.size() == 2 &&
1865 laneData == targetLaneData && laneLayout[1] == 1 &&
1866 targetLaneLayout[1] == 1 && laneLayout[0] > 1 &&
1867 laneLayout[0] != targetLaneLayout[0]) {
1868 FailureOr<Value> res = shuffleDataAsLaneLayoutChange(
1869 rewriter, op.getLoc(), adaptor.getSource(), laneLayout[0],
1870 targetLaneLayout[0]);
1871 if (succeeded(res)) {
1872 rewriter.replaceOp(op, *res);
1884 if (inputLayout.getEffectiveOrderAsInt() ==
1885 targetLayout.getEffectiveOrderAsInt() &&
1886 inputLayout.getEffectiveLaneLayoutAsInt() ==
1887 targetLayout.getEffectiveLaneLayoutAsInt()) {
1888 auto laneLayout = inputLayout.getEffectiveLaneLayoutAsInt();
1889 auto laneData = inputLayout.getEffectiveLaneDataAsInt();
1890 auto targetLaneData = targetLayout.getEffectiveLaneDataAsInt();
1893 int64_t rank = laneData.size();
1894 int64_t repackDim = -1;
1895 bool multipleChanged =
false;
1896 for (int64_t d = 0; d < rank; ++d)
1897 if (laneData[d] != targetLaneData[d]) {
1898 if (repackDim != -1)
1899 multipleChanged =
true;
1905 int64_t otherDim = repackDim == rank - 1 ? rank - 2 : rank - 1;
1906 bool laneLayoutOk = repackDim != -1 && laneLayout[repackDim] != 1 &&
1907 (rank < 2 || laneLayout[otherDim] == 1);
1911 if (repackDim != -1 && repackDim >= rank - 2 && !multipleChanged &&
1913 FailureOr<Value> res = repackLaneData(
1914 rewriter, op.getLoc(), adaptor.getSource(), repackDim,
1915 laneData[repackDim], targetLaneData[repackDim]);
1916 if (succeeded(res)) {
1917 rewriter.replaceOp(op, *res);
1923 return rewriter.notifyMatchFailure(
1924 op,
"lowering incompatible convert_layout not yet supported");
1932struct LaneFragmentPiece {
1933 SmallVector<int64_t> offsets;
1934 SmallVector<int64_t> sizes;
1937static LaneFragmentPiece getLaneFragmentPiece(
int64_t unit,
1943 for (
int64_t d = 0; d < rank; ++d)
1944 units[d] =
shape[d] / std::min(
shape[d], laneLayout[d] * laneData[d]);
1946 LaneFragmentPiece piece;
1947 for (
int64_t d = 0; d < rank; ++d) {
1948 piece.offsets.push_back(unitCoords[d] * laneData[d]);
1949 piece.sizes.push_back(laneData[d]);
1968struct SgToLaneConvertLayoutViaSLM
1969 :
public OpConversionPattern<xegpu::ConvertLayoutOp> {
1970 using OpConversionPattern<xegpu::ConvertLayoutOp>::OpConversionPattern;
1973 matchAndRewrite(xegpu::ConvertLayoutOp op, OpAdaptor adaptor,
1974 ConversionPatternRewriter &rewriter)
const override {
1975 xegpu::DistributeLayoutAttr inputLayout = op.getEffectiveInputLayout();
1976 xegpu::DistributeLayoutAttr targetLayout = op.getTargetLayoutAttr();
1977 if (!inputLayout || !targetLayout || !inputLayout.isForSubgroup() ||
1978 !targetLayout.isForSubgroup())
1979 return rewriter.notifyMatchFailure(op,
"both layouts must be lane level");
1981 auto valueTy = dyn_cast<VectorType>(op.getResult().getType());
1983 return rewriter.notifyMatchFailure(op,
"value type must be a vector");
1984 Type elemTy = valueTy.getElementType();
1988 return rewriter.notifyMatchFailure(
1989 op,
"element type must be a whole number of bytes");
1991 ArrayRef<int64_t> sgShape = valueTy.getShape();
1992 int64_t rank = sgShape.size();
1993 if (rank != inputLayout.getRank() || rank != targetLayout.getRank())
1994 return rewriter.notifyMatchFailure(
1995 op,
"both layouts must have the rank of the value");
1997 FailureOr<VectorType> distInputTy =
1999 FailureOr<VectorType> distTargetTy =
2002 return rewriter.notifyMatchFailure(
2003 op,
"value type must be distributable by both layouts");
2008 return rewriter.notifyMatchFailure(
2009 op,
"target attribute is required to determine the subgroup size");
2010 FailureOr<int64_t> numSubgroups =
2012 if (
failed(numSubgroups))
2013 return rewriter.notifyMatchFailure(
2014 op,
"the scratch holds one tile per subgroup, so @known_block_size "
2015 "must be attached to the kernel, with power-of-two dimensions "
2016 "covering at least one subgroup");
2018 Location loc = op.getLoc();
2021 SmallVector<int64_t> slmShape(sgShape);
2022 slmShape[0] *= *numSubgroups;
2025 auto slmTy = MemRefType::get({slmBytes}, rewriter.getI8Type(), {}, 3);
2026 Value slm = memref::AllocaOp::create(rewriter, loc, slmTy);
2027 Value memDesc = xegpu::CreateMemDescOp::create(
2029 xegpu::MemDescType::get(rewriter.getContext(), slmShape, elemTy,
2034 Value sgId = gpu::SubgroupIdOp::create(rewriter, loc,
2035 rewriter.getIndexType(),
nullptr);
2036 SmallVector<Value> base(rank);
2037 base[0] = arith::MulIOp::create(
2038 rewriter, loc, sgId,
2040 for (int64_t d = 1; d < rank; ++d)
2043 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
2044 mlir::IntegerAttr());
2045 auto dynamicOffsets = rewriter.getDenseI64ArrayAttr(
2046 SmallVector<int64_t>(rank, ShapedType::kDynamic));
2049 auto withBase = [&](ArrayRef<Value> coords) {
2050 SmallVector<Value> offsets;
2051 for (
auto [coord,
b] : llvm::zip_equal(coords, base))
2052 offsets.push_back(arith::AddIOp::create(rewriter, loc, coord,
b));
2058 inputLayout.computeDistributedCoords(rewriter, loc, laneId, sgShape);
2060 return rewriter.notifyMatchFailure(
2061 op,
"failed to compute the input_layout coordinates");
2062 SmallVector<int64_t> inputLaneLayout =
2063 inputLayout.getEffectiveLaneLayoutAsInt();
2064 SmallVector<int64_t> inputLaneData =
2065 inputLayout.getEffectiveLaneDataAsInt();
2069 for (
auto [unit, coords] : llvm::enumerate(*storeCoords)) {
2070 LaneFragmentPiece piece =
2071 getLaneFragmentPiece(unit, sgShape, inputLaneLayout, inputLaneData);
2072 Value data = fragment;
2073 if (piece.sizes != SmallVector<int64_t>(distInputTy->getShape()))
2074 data = vector::ExtractStridedSliceOp::create(
2075 rewriter, loc, fragment, piece.offsets, piece.sizes,
2076 SmallVector<int64_t>(rank, 1));
2077 xegpu::StoreMatrixOp::create(rewriter, loc,
TypeRange{}, data, memDesc,
2078 ValueRange(withBase(coords)), dynamicOffsets,
2079 nullptr, xegpu::DistributeLayoutAttr{});
2085 xegpu::FenceOp::create(rewriter, loc, xegpu::MemorySpace::SLM,
2086 xegpu::FenceScope::Workgroup);
2090 targetLayout.computeDistributedCoords(rewriter, loc, laneId, sgShape);
2092 return rewriter.notifyMatchFailure(
2093 op,
"failed to compute the target_layout coordinates");
2094 SmallVector<int64_t> targetLaneLayout =
2095 targetLayout.getEffectiveLaneLayoutAsInt();
2096 SmallVector<int64_t> targetLaneData =
2097 targetLayout.getEffectiveLaneDataAsInt();
2099 if (loadCoords->size() > 1)
2100 result = arith::ConstantOp::create(rewriter, loc, *distTargetTy,
2101 rewriter.getZeroAttr(*distTargetTy));
2102 for (
auto [unit, coords] : llvm::enumerate(*loadCoords)) {
2103 LaneFragmentPiece piece =
2104 getLaneFragmentPiece(unit, sgShape, targetLaneLayout, targetLaneData);
2105 auto pieceTy = VectorType::get(piece.sizes, elemTy);
2106 Value loaded = xegpu::LoadMatrixOp::create(
2107 rewriter, loc, pieceTy, memDesc,
ValueRange(withBase(coords)),
2108 dynamicOffsets,
nullptr, xegpu::DistributeLayoutAttr{});
2113 result = vector::InsertStridedSliceOp::create(
2114 rewriter, loc, loaded,
result, piece.offsets,
2115 SmallVector<int64_t>(rank, 1));
2118 rewriter.replaceOp(op, castValueTo(rewriter,
2127static bool hasDefaultOrderAndUnitLaneData(xegpu::DistributeLayoutAttr layout) {
2128 if (layout.getRank() != 2)
2136struct ElementLaneRedistribution {
2138 VectorType valueType;
2140 VectorType distributedInput;
2141 VectorType distributedTarget;
2144 SmallVector<int64_t> inputLaneLayout;
2145 SmallVector<int64_t> targetLaneLayout;
2146 int64_t subgroupSize;
2155static FailureOr<ElementLaneRedistribution>
2156matchElementLaneRedistribution(xegpu::ConvertLayoutOp op,
2157 ConversionPatternRewriter &rewriter) {
2158 xegpu::DistributeLayoutAttr inputLayout = op.getEffectiveInputLayout();
2159 xegpu::DistributeLayoutAttr targetLayout = op.getTargetLayoutAttr();
2160 if (!hasDefaultOrderAndUnitLaneData(inputLayout) ||
2161 !hasDefaultOrderAndUnitLaneData(targetLayout))
2162 return rewriter.notifyMatchFailure(
2163 op,
"both layouts must be rank 2 with effective lane_data [1, 1] and "
2164 "effective order [1, 0]");
2166 auto valueType = dyn_cast<VectorType>(op.getResult().getType());
2168 return rewriter.notifyMatchFailure(op,
"value type must be a vector");
2170 FailureOr<VectorType> distributedInput =
2172 FailureOr<VectorType> distributedTarget =
2174 if (
failed(distributedInput) ||
failed(distributedTarget))
2175 return rewriter.notifyMatchFailure(
2176 op,
"value type must be distributable by both layouts");
2181 return rewriter.notifyMatchFailure(
2182 op,
"target attribute is required to determine the subgroup size");
2184 ElementLaneRedistribution redistribution;
2185 redistribution.valueType = valueType;
2186 redistribution.distributedInput = *distributedInput;
2187 redistribution.distributedTarget = *distributedTarget;
2188 redistribution.inputLaneLayout = inputLayout.getEffectiveLaneLayoutAsInt();
2189 redistribution.targetLaneLayout = targetLayout.getEffectiveLaneLayoutAsInt();
2190 redistribution.subgroupSize = uArch->getSubgroupSize();
2191 return redistribution;
2206static FailureOr<int64_t> getDistributedDimLaneStride(xegpu::SliceAttr
slice) {
2207 xegpu::SliceAttr flattened =
slice.flatten();
2208 auto parent = dyn_cast<xegpu::LayoutAttr>(flattened.getParent());
2214 if (parentLaneLayout.size() != parentOrder.size())
2218 std::optional<int64_t> distributedDim;
2219 for (
int64_t dim = 0, rank =
static_cast<int64_t>(parentLaneLayout.size());
2220 dim < rank; ++dim) {
2221 if (llvm::is_contained(slicedDims, dim) || parentLaneLayout[dim] == 1)
2225 distributedDim = dim;
2227 if (!distributedDim)
2231 for (
int64_t dim : parentOrder) {
2232 if (dim == *distributedDim)
2234 stride *= parentLaneLayout[dim];
2250struct SgToLaneConvertLayoutBroadcastExtract
2251 :
public OpConversionPattern<xegpu::ConvertLayoutOp> {
2252 using OpConversionPattern<xegpu::ConvertLayoutOp>::OpConversionPattern;
2255 matchAndRewrite(xegpu::ConvertLayoutOp op, OpAdaptor adaptor,
2256 ConversionPatternRewriter &rewriter)
const override {
2257 xegpu::DistributeLayoutAttr inputLayout = op.getEffectiveInputLayout();
2258 xegpu::DistributeLayoutAttr targetLayout = op.getTargetLayoutAttr();
2260 if (!isa<xegpu::SliceAttr>(inputLayout))
2261 return rewriter.notifyMatchFailure(op,
2262 "input_layout must be #xegpu.slice");
2263 if (!isa<xegpu::LayoutAttr>(targetLayout))
2264 return rewriter.notifyMatchFailure(op,
2265 "target_layout must be #xegpu.layout");
2267 FailureOr<ElementLaneRedistribution> redistribution =
2268 matchElementLaneRedistribution(op, rewriter);
2269 if (
failed(redistribution))
2272 if (redistribution->inputLaneLayout != SmallVector<int64_t>{1, 1})
2273 return rewriter.notifyMatchFailure(
2274 op,
"input_layout effective lane_layout must be [1, 1]");
2275 SmallVector<int64_t> targetLaneLayout = redistribution->targetLaneLayout;
2276 if (targetLaneLayout[0] <= 1 || targetLaneLayout[1] != 1)
2277 return rewriter.notifyMatchFailure(
2278 op,
"target_layout effective lane_layout must be [n, 1] with n > 1");
2279 if (redistribution->distributedInput != redistribution->valueType)
2280 return rewriter.notifyMatchFailure(
2281 op,
"distributed input_layout type must equal the value type");
2282 if (redistribution->distributedTarget.getShape() != ArrayRef<int64_t>{1, 1})
2283 return rewriter.notifyMatchFailure(
2284 op,
"distributed target_layout type must be vector<1x1>");
2285 if (targetLaneLayout[0] > redistribution->subgroupSize)
2286 return rewriter.notifyMatchFailure(
2287 op,
"target_layout effective lane_layout[0] must not exceed the "
2290 VectorType valueType = redistribution->valueType;
2291 Location loc = op.getLoc();
2294 redistribution->distributedInput);
2295 auto flatType = VectorType::get({valueType.getNumElements()},
2296 valueType.getElementType());
2297 Value flat = vector::ShapeCastOp::create(rewriter, loc, flatType, src);
2298 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
2299 mlir::IntegerAttr());
2302 Value row = arith::RemUIOp::create(rewriter, loc, laneId, rowCount);
2303 Value element = vector::ExtractOp::create(rewriter, loc, flat,
2304 ArrayRef<OpFoldResult>{row});
2305 rewriter.replaceOpWithNewOp<vector::FromElementsOp>(
2306 op, redistribution->distributedTarget, element);
2352struct SgToLaneConvertLayoutPartialBroadcastExtractShuffle
2353 :
public OpConversionPattern<xegpu::ConvertLayoutOp> {
2354 using OpConversionPattern<xegpu::ConvertLayoutOp>::OpConversionPattern;
2357 matchAndRewrite(xegpu::ConvertLayoutOp op, OpAdaptor adaptor,
2358 ConversionPatternRewriter &rewriter)
const override {
2359 xegpu::DistributeLayoutAttr inputLayout = op.getEffectiveInputLayout();
2360 xegpu::DistributeLayoutAttr targetLayout = op.getTargetLayoutAttr();
2362 auto inputSlice = dyn_cast<xegpu::SliceAttr>(inputLayout);
2364 return rewriter.notifyMatchFailure(op,
2365 "input_layout must be #xegpu.slice");
2366 if (!isa<xegpu::LayoutAttr>(targetLayout))
2367 return rewriter.notifyMatchFailure(op,
2368 "target_layout must be #xegpu.layout");
2370 FailureOr<ElementLaneRedistribution> redistribution =
2371 matchElementLaneRedistribution(op, rewriter);
2372 if (
failed(redistribution))
2375 VectorType valueType = redistribution->valueType;
2376 Type elementType = valueType.getElementType();
2378 !llvm::is_contained({8u, 16u, 32u, 64u},
2380 return rewriter.notifyMatchFailure(
2381 op,
"element type must be an int or float of bit width 8, 16, 32 or "
2382 "64 to be carried by gpu.shuffle");
2384 if (redistribution->inputLaneLayout != SmallVector<int64_t>{1, 2})
2385 return rewriter.notifyMatchFailure(
2386 op,
"input_layout effective lane_layout must be [1, 2]");
2387 SmallVector<int64_t> targetLaneLayout = redistribution->targetLaneLayout;
2388 if (targetLaneLayout[0] <= 1 || targetLaneLayout[1] != 1)
2389 return rewriter.notifyMatchFailure(
2390 op,
"target_layout effective lane_layout must be [n, 1] with n > 1");
2391 if (redistribution->distributedInput.getShape() !=
2392 ArrayRef<int64_t>{valueType.getDimSize(0), 1})
2393 return rewriter.notifyMatchFailure(
2394 op,
"distributed input_layout type must be vector<shape[0]x1>");
2395 if (redistribution->distributedTarget.getShape() != ArrayRef<int64_t>{1, 2})
2396 return rewriter.notifyMatchFailure(
2397 op,
"distributed target_layout type must be vector<1x2>");
2399 FailureOr<int64_t> stride = getDistributedDimLaneStride(inputSlice);
2401 return rewriter.notifyMatchFailure(
2402 op,
"input_layout parent must have exactly one non-sliced dimension "
2403 "with lane_layout extent greater than one");
2406 if (*stride * redistribution->inputLaneLayout[1] !=
2407 redistribution->subgroupSize)
2408 return rewriter.notifyMatchFailure(
2409 op,
"input_layout distributed dimension lane stride times its extent "
2410 "must equal the subgroup size");
2414 if (*stride % targetLaneLayout[0] != 0)
2415 return rewriter.notifyMatchFailure(
2416 op,
"target_layout effective lane_layout[0] must divide the "
2417 "input_layout distributed dimension lane stride");
2419 Location loc = op.getLoc();
2422 redistribution->distributedInput);
2424 VectorType::get({redistribution->distributedInput.getNumElements()},
2425 valueType.getElementType());
2426 Value flat = vector::ShapeCastOp::create(rewriter, loc, flatType, src);
2427 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
2428 mlir::IntegerAttr());
2431 Value row = arith::RemUIOp::create(rewriter, loc, laneId, rowCount);
2432 Value own = vector::ExtractOp::create(rewriter, loc, flat,
2433 ArrayRef<OpFoldResult>{row});
2435 Type i32Type = rewriter.getI32Type();
2437 redistribution->subgroupSize);
2438 Value rowI32 = arith::IndexCastOp::create(rewriter, loc, i32Type, row);
2441 Value otherOwner = arith::AddIOp::create(rewriter, loc, rowI32, strideI32);
2442 Value column0 = gpu::ShuffleOp::create(rewriter, loc, own, rowI32, width,
2443 gpu::ShuffleMode::IDX)
2444 .getShuffleResult();
2445 Value column1 = gpu::ShuffleOp::create(rewriter, loc, own, otherOwner,
2446 width, gpu::ShuffleMode::IDX)
2447 .getShuffleResult();
2448 rewriter.replaceOpWithNewOp<vector::FromElementsOp>(
2449 op, redistribution->distributedTarget,
ValueRange{column0, column1});
2464struct SgToLaneConvertLayoutDeinterleaveSelect
2465 :
public OpConversionPattern<xegpu::ConvertLayoutOp> {
2466 using OpConversionPattern<xegpu::ConvertLayoutOp>::OpConversionPattern;
2469 matchAndRewrite(xegpu::ConvertLayoutOp op, OpAdaptor adaptor,
2470 ConversionPatternRewriter &rewriter)
const override {
2471 xegpu::DistributeLayoutAttr inputLayout = op.getEffectiveInputLayout();
2472 xegpu::DistributeLayoutAttr targetLayout = op.getTargetLayoutAttr();
2474 if (!isa<xegpu::SliceAttr>(inputLayout))
2475 return rewriter.notifyMatchFailure(op,
2476 "input_layout must be #xegpu.slice");
2477 auto targetSlice = dyn_cast<xegpu::SliceAttr>(targetLayout);
2479 return rewriter.notifyMatchFailure(op,
2480 "target_layout must be #xegpu.slice");
2482 FailureOr<ElementLaneRedistribution> redistribution =
2483 matchElementLaneRedistribution(op, rewriter);
2484 if (
failed(redistribution))
2487 VectorType valueType = redistribution->valueType;
2488 if (redistribution->inputLaneLayout != SmallVector<int64_t>{1, 1})
2489 return rewriter.notifyMatchFailure(
2490 op,
"input_layout effective lane_layout must be [1, 1]");
2491 SmallVector<int64_t> targetLaneLayout = redistribution->targetLaneLayout;
2492 if (targetLaneLayout != SmallVector<int64_t>{1, 2})
2493 return rewriter.notifyMatchFailure(
2494 op,
"target_layout effective lane_layout must be [1, 2]");
2495 if (redistribution->distributedInput != valueType)
2496 return rewriter.notifyMatchFailure(
2497 op,
"distributed input_layout type must equal the value type");
2498 if (redistribution->distributedTarget.getShape() !=
2499 ArrayRef<int64_t>{valueType.getDimSize(0), 1})
2500 return rewriter.notifyMatchFailure(
2501 op,
"distributed target_layout type must be vector<shape[0]x1>");
2503 FailureOr<int64_t> stride = getDistributedDimLaneStride(targetSlice);
2505 return rewriter.notifyMatchFailure(
2506 op,
"target_layout parent must have exactly one non-sliced dimension "
2507 "with lane_layout extent greater than one");
2510 if (*stride * targetLaneLayout[1] != redistribution->subgroupSize)
2511 return rewriter.notifyMatchFailure(
2512 op,
"target_layout distributed dimension lane stride times its "
2513 "extent must equal the subgroup size");
2515 Location loc = op.getLoc();
2518 redistribution->distributedInput);
2519 auto flatType = VectorType::get({valueType.getNumElements()},
2520 valueType.getElementType());
2521 Value flat = vector::ShapeCastOp::create(rewriter, loc, flatType, src);
2522 auto deinterleaved = vector::DeinterleaveOp::create(rewriter, loc, flat);
2523 Value laneId = gpu::LaneIdOp::create(rewriter, loc, rewriter.getIndexType(),
2524 mlir::IntegerAttr());
2527 Value group = arith::DivUIOp::create(rewriter, loc, laneId, strideVal);
2528 Value isFirstGroup = arith::CmpIOp::create(
2529 rewriter, loc, arith::CmpIPredicate::eq, group, zero);
2530 Value selected = arith::SelectOp::create(rewriter, loc, isFirstGroup,
2531 deinterleaved.getResultEven(),
2532 deinterleaved.getResultOdd());
2533 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(
2534 op, redistribution->distributedTarget, selected);
2540struct SgToLaneVectorInterleave
2541 :
public OpConversionPattern<vector::InterleaveOp> {
2542 using OpConversionPattern<vector::InterleaveOp>::OpConversionPattern;
2545 matchAndRewrite(vector::InterleaveOp op, OpAdaptor adaptor,
2546 ConversionPatternRewriter &rewriter)
const override {
2548 auto newOp = vector::InterleaveOp::create(
2549 rewriter, op.getLoc(), adaptor.getLhs(), adaptor.getRhs());
2550 rewriter.replaceOp(op, newOp.
getResult());
2556struct SgToLaneVectorDeinterleave
2557 :
public OpConversionPattern<vector::DeinterleaveOp> {
2558 using OpConversionPattern<vector::DeinterleaveOp>::OpConversionPattern;
2561 matchAndRewrite(vector::DeinterleaveOp op, OpAdaptor adaptor,
2562 ConversionPatternRewriter &rewriter)
const override {
2564 auto newOp = vector::DeinterleaveOp::create(rewriter, op.getLoc(),
2565 adaptor.getSource());
2571struct SgToLaneDpasMx :
public OpConversionPattern<xegpu::DpasMxOp> {
2572 using OpConversionPattern<xegpu::DpasMxOp>::OpConversionPattern;
2575 matchAndRewrite(xegpu::DpasMxOp op, OpAdaptor adaptor,
2576 ConversionPatternRewriter &rewriter)
const override {
2581 if (!uArch->isSupportedInstruction(
2582 xegpu::uArch::InstructionKind::SubgroupScaledMatrixMultiplyAcc))
2583 return rewriter.notifyMatchFailure(
2584 op,
"target uArch does not support scaled subgroup mma");
2586 auto layoutA = cast<xegpu::LayoutAttr>(op.getLayoutAAttr());
2587 auto layoutB = cast<xegpu::LayoutAttr>(op.getLayoutBAttr());
2588 auto layoutCd = cast<xegpu::LayoutAttr>(op.getLayoutCdAttr());
2589 if (!layoutA || !layoutB || !layoutCd)
2590 return rewriter.notifyMatchFailure(
2591 op,
"missing required layout attributes for DpasMxOp distribution");
2594 auto expected1DTypeResult =
2596 auto expected1DTypeA =
2598 auto expected1DTypeB =
2601 VectorType expected1DTypeScaleA, expected1DTypeScaleB;
2602 if (op.getScaleA()) {
2603 auto layoutScaleA = cast<xegpu::LayoutAttr>(op.getLayoutAScaleAttr());
2605 cast<VectorType>(op.getScaleA().getType()), layoutScaleA);
2606 if (
failed(expected1DTypeScaleAOrFailure))
2607 return rewriter.notifyMatchFailure(
2608 op,
"failed to calculate expected 1D vector type for scale A");
2609 expected1DTypeScaleA = expected1DTypeScaleAOrFailure.value();
2611 if (op.getScaleB()) {
2612 auto layoutScaleB = cast<xegpu::LayoutAttr>(op.getLayoutBScaleAttr());
2614 cast<VectorType>(op.getScaleB().getType()), layoutScaleB);
2615 if (
failed(expected1DTypeScaleBOrFailure))
2616 return rewriter.notifyMatchFailure(
2617 op,
"failed to calculate expected 1D vector type for scale B");
2618 expected1DTypeScaleB = expected1DTypeScaleBOrFailure.value();
2621 auto expectedNDTypeResult =
2623 if (
failed(expected1DTypeResult) ||
failed(expected1DTypeA) ||
2625 return rewriter.notifyMatchFailure(
2627 "failed to calculate supported workitem 1D vector types for DpasOp "
2629 if (
failed(expectedNDTypeResult))
2630 return rewriter.notifyMatchFailure(
2631 op,
"unable to compute expected workitem vector type for DpasOp from "
2635 const auto *uArchInstruction = dyn_cast<
2637 xegpu::uArch::InstructionKind::SubgroupScaledMatrixMultiplyAcc));
2638 assert(uArchInstruction);
2639 auto wiAType = expected1DTypeA.value();
2640 auto wiBType = expected1DTypeB.value();
2642 unsigned aPackedBitWidth =
2643 wiAType.getElementTypeBitWidth() * wiAType.getNumElements();
2644 unsigned bPackedBitWidth =
2645 wiBType.getElementTypeBitWidth() * wiBType.getNumElements();
2646 if (aPackedBitWidth % uArchInstruction->getPackedFormatBitSizeA())
2647 return rewriter.notifyMatchFailure(
2648 op,
"A operand packed bit width must be a multiple of uArch packed "
2649 "format requirement");
2650 if (bPackedBitWidth % uArchInstruction->getPackedFormatBitSizeB())
2651 return rewriter.notifyMatchFailure(
2652 op,
"B operand packed bit width must be a multiple of uArch packed "
2653 "format requirement");
2655 auto newOp = xegpu::DpasMxOp::create(
2656 rewriter, op->getLoc(), expected1DTypeResult.value(),
2658 expected1DTypeA.value()),
2660 expected1DTypeB.value()),
2662 ? castValueTo(rewriter,
2664 expected1DTypeResult.value())
2668 ? castValueTo(rewriter,
2670 expected1DTypeScaleA)
2673 ? castValueTo(rewriter,
2675 expected1DTypeScaleB)
2681 rewriter.replaceOp(op, castValueTo(rewriter, newOp.
getResult(),
2682 expectedNDTypeResult.value()));
2687struct XeGPUSgToLaneDistributePass
2689 XeGPUSgToLaneDistributePass> {
2690 void runOnOperation()
override;
2695void XeGPUSgToLaneDistributePass::runOnOperation() {
2698 Operation *root = getOperation();
2700 signalPassFailure();
2705 llvm::SmallSetVector<UnrealizedConversionCastOp, 8> existingCasts;
2707 [&](UnrealizedConversionCastOp castOp) { existingCasts.insert(castOp); });
2713 TypeConverter typeConverter;
2717 auto materializeCast = [](OpBuilder &builder, Type type,
ValueRange inputs,
2718 Location loc) -> Value {
2719 return UnrealizedConversionCastOp::create(builder, loc, type, inputs)
2722 typeConverter.addSourceMaterialization(materializeCast);
2723 typeConverter.addTargetMaterialization(materializeCast);
2728 typeConverter, patterns,
target, root);
2729 target.addLegalOp<UnrealizedConversionCastOp>();
2734 ConversionConfig config;
2735 config.foldingMode = DialectConversionFoldingMode::AfterPatterns;
2737 applyPartialConversion(root,
target, std::move(patterns), config)))
2738 return signalPassFailure();
2749 typeConverter.addConversion([](
Type type) ->
Type {
return type; });
2751 typeConverter.addConversion([](TensorDescType type) ->
Type {
2752 if (type.getLayoutAttr()) {
2753 return type.dropLayouts();
2761 auto getSubShapeAndCount = [](VectorType vecTy,
2762 xegpu::DistributeLayoutAttr layout)
2765 if (failed(distTyOrFailure))
2772 std::move(loopArgTypes));
2780 target.addDynamicallyLegalOp<xegpu::CreateNdDescOp>(
2781 [&](xegpu::CreateNdDescOp op) {
return !op.getType().getLayoutAttr(); });
2783 target.addDynamicallyLegalDialect<xegpu::XeGPUDialect>([](
Operation *op) {
2784 if (isa<xegpu::ConvertLayoutOp>(op))
2786 auto anchorOp = dyn_cast<AnchorLayoutInterface>(op);
2789 return !anchorOp.getAnchorLayout();
2792 target.addDynamicallyLegalOp<arith::ConstantOp>(
2793 [=](arith::ConstantOp op) ->
bool {
2795 if (!isa<VectorType>(op.getResult().getType()))
2801 target.addDynamicallyLegalDialect<math::MathDialect, arith::ArithDialect>(
2802 [=](
Operation *op) -> std::optional<bool> {
2807 if (op->getNumResults() != 1)
2810 VectorType resultType =
2811 dyn_cast<VectorType>(op->getResult(0).getType());
2816 for (
Value operand : op->getOperands()) {
2817 VectorType operandType = dyn_cast<VectorType>(operand.getType());
2818 if (!operandType || operandType.getShape() != resultType.getShape()) {
2826 target.addDynamicallyLegalOp<vector::ReductionOp>(
2827 [=](vector::ReductionOp op) ->
bool {
2832 target.addDynamicallyLegalOp<vector::MultiDimReductionOp>(
2833 [=](vector::MultiDimReductionOp op) ->
bool {
2834 return !isValidSubgroupMultiReductionOp(op);
2836 target.addDynamicallyLegalOp<vector::CreateMaskOp, vector::ConstantMaskOp,
2837 vector::TransposeOp, vector::BitCastOp,
2838 vector::ShapeCastOp, vector::StepOp,
2839 vector::BroadcastOp>([=](
Operation *op) ->
bool {
2842 target.addDynamicallyLegalOp<vector::ExtractOp>(
2843 [=](vector::ExtractOp op) ->
bool {
2844 if (!isa<VectorType>(op.getType()))
2848 target.addDynamicallyLegalOp<vector::InsertOp>(
2849 [=](vector::InsertOp op) ->
bool {
2852 target.addDynamicallyLegalOp<vector::ExtractStridedSliceOp>(
2853 [=](vector::ExtractStridedSliceOp op) ->
bool {
2856 target.addDynamicallyLegalOp<vector::InsertStridedSliceOp>(
2857 [=](vector::InsertStridedSliceOp op) ->
bool {
2860 target.addDynamicallyLegalOp<vector::InterleaveOp, vector::DeinterleaveOp>(
2864 target.markUnknownOpDynamicallyLegal([](
Operation *op) {
return true; });
2866 .
add<SgToLaneCreateNdDesc, SgToLaneLoadNd, SgToLaneStoreNd, SgToLaneDpas,
2867 SgToLaneElementWise, SgToLaneArithConstant, SgToLanePrefetchNd,
2868 SgToLaneLoadGather, SgToLaneStoreScatter, SgToLaneVectorReduction,
2869 SgToLaneMultiDimReduction, SgToLaneVectorExtract,
2870 SgToLaneVectorInsert, SgToLaneVectorExtractStridedSlice,
2871 SgToLaneVectorInsertStridedSlice, SgToLaneLoadMatrix,
2872 SgToLaneStoreMatrix, SgToLaneConvertLayout, SgToLaneVectorTranspose,
2873 SgToLaneVectorBitcast, SgToLaneVectorStep, SgToLaneVectorShapeCast,
2874 SgToLaneBroadcast, SgToLaneCreateMask<vector::CreateMaskOp>,
2875 SgToLaneCreateMask<vector::ConstantMaskOp>,
2876 SgToLaneVectorDeinterleave, SgToLaneVectorInterleave, SgToLaneDpasMx,
2877 SgToLaneConvertLayoutBroadcastExtract,
2878 SgToLaneConvertLayoutPartialBroadcastExtractShuffle,
2879 SgToLaneConvertLayoutDeinterleaveSelect>(typeConverter,
2883 patterns.
add<SgToLaneConvertLayoutViaSLM>(typeConverter,
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
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),...
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
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.
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.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
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.
bool recoverTemporaryLayouts(Operation *rootOp)
Attach layout attributes to all vector-type operands of operations within the given operation's neste...
FailureOr< int64_t > getNumSubgroupsFromBlockSize(Operation *op, int64_t subgroupSize)
Returns the number of subgroups the kernel enclosing op runs, derived from the known_block_size of it...
FailureOr< VectorType > getDistVecTypeBasedOnLaneLayout(DistributeLayoutAttr layout, VectorType originalType)
Helper function to get distributed vector type for a source vector type according to the lane_layout.
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,...
Value lowerToVectorReductions(TypedValue< VectorType > src, TypedValue< VectorType > acc, vector::CombiningKind kind, int64_t reductionDim, Location loc, PatternRewriter &rewriter, arith::FastMathFlagsAttr fastmath=nullptr)
Given a src and an acc argumments from a vector::MultiDimReductionOp, lower to a set of vector::Reduc...
DistributeLayoutAttr getTemporaryLayout(const T &operandOrResult)
get and set distribute layout attribute for non-anchor operations (and offsets/masks of load/store op...
Value lowerCrossLaneReductionToShuffles(TypedValue< VectorType > src, TypedValue< VectorType > acc, vector::CombiningKind kind, int64_t reductionDim, int64_t reductionSize, Location loc, PatternRewriter &rewriter, arith::FastMathFlagsAttr fastmath=nullptr)
Lowers cross-lane reductions to shuffle operations on a 2D vector.
void populateXeGPUSgToLaneDistributeTypeConversions(TypeConverter &typeConverter, Operation *topLevelOp)
Define only the type conversions needed for XeGPU subgroup to lane distribution.
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.
Value subgroupReduction(Location loc, OpBuilder &builder, Value input, vector::CombiningKind kind, uint32_t size, arith::FastMathFlagsAttr fastmath=nullptr)
Given an input value representing per-lane data, this function returns the result after performing a ...
Include the generated interface declarations.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
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.
int64_t linearize(ArrayRef< int64_t > offsets, ArrayRef< int64_t > basis)
Return the linearized index of 'offsets' w.r.t.
std::optional< SmallVector< int64_t > > computeShapeRatio(ArrayRef< int64_t > shape, ArrayRef< int64_t > subShape)
Return the multi-dimensional integral ratio of subShape to the trailing dimensions of shape.
This represents an operation in an abstracted form, suitable for use with the builder APIs.