29#include "llvm/ADT/STLExtras.h"
30#include "llvm/Support/FormatVariadic.h"
35#include "llvm/ADT/TypeSwitch.h"
40#define GEN_PASS_DEF_CONVERTXEGPUTOXEVMPASS
41#include "mlir/Conversion/Passes.h.inc"
49static constexpr int32_t systolicDepth{8};
50static constexpr int32_t executionSize{16};
53enum class NdTdescOffset : uint32_t {
64static constexpr int64_t maxNdTdescLeadingDims{3};
65static constexpr int64_t maxNdTdescRank{2 + maxNdTdescLeadingDims};
67static int32_t getNumericXeVMAddrSpace(xegpu::MemorySpace xeGpuMemspace) {
68 switch (xeGpuMemspace) {
69 case xegpu::MemorySpace::Global:
70 return static_cast<int>(xevm::AddrSpace::GLOBAL);
71 case xegpu::MemorySpace::SLM:
72 return static_cast<int>(xevm::AddrSpace::SHARED);
74 llvm_unreachable(
"Unknown XeGPU memory space");
84static FailureOr<unsigned> getNumericMemorySpace(
Attribute memSpace) {
87 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(memSpace))
88 return static_cast<unsigned>(intAttr.getInt());
89 if (
auto xevmSpace = llvm::dyn_cast<xevm::AddrSpaceAttr>(memSpace))
90 return static_cast<unsigned>(xevmSpace.getValue());
91 if (
auto gpuSpace = llvm::dyn_cast<gpu::AddressSpaceAttr>(memSpace)) {
92 switch (gpuSpace.getValue()) {
93 case gpu::AddressSpace::Global:
94 return static_cast<unsigned>(xevm::AddrSpace::GLOBAL);
95 case gpu::AddressSpace::Workgroup:
96 return static_cast<unsigned>(xevm::AddrSpace::SHARED);
97 case gpu::AddressSpace::Private:
98 return static_cast<unsigned>(xevm::AddrSpace::PRIVATE);
99 case gpu::AddressSpace::Constant:
100 return static_cast<unsigned>(xevm::AddrSpace::CONSTANT);
102 llvm_unreachable(
"Unknown GPU address space");
108static bool isSharedMemRef(
const MemRefType &memrefTy) {
109 FailureOr<unsigned> addrSpace =
110 getNumericMemorySpace(memrefTy.getMemorySpace());
111 return succeeded(addrSpace) &&
112 *addrSpace ==
static_cast<unsigned>(xevm::AddrSpace::SHARED);
116static VectorType encodeVectorTypeTo(VectorType currentVecType,
118 auto elemType = currentVecType.getElementType();
119 auto currentBitWidth = elemType.getIntOrFloatBitWidth();
122 currentVecType.getNumElements() * currentBitWidth / newBitWidth;
123 return VectorType::get(size, toElemType);
126static xevm::LoadCacheControl
127translateLoadXeGPUCacheHint(std::optional<xegpu::CachePolicy> L1hint,
128 std::optional<xegpu::CachePolicy> L3hint) {
130 if (!L1hint && !L3hint)
131 return xevm::LoadCacheControl::USE_DEFAULT;
133 auto L1hintVal = L1hint.value_or(xegpu::CachePolicy::CACHED);
134 auto L3hintVal = L3hint.value_or(xegpu::CachePolicy::CACHED);
136 case xegpu::CachePolicy::CACHED:
137 if (L3hintVal == xegpu::CachePolicy::CACHED)
138 return xevm::LoadCacheControl::L1C_L2UC_L3C;
139 else if (L3hintVal == xegpu::CachePolicy::UNCACHED)
140 return xevm::LoadCacheControl::L1C_L2UC_L3UC;
142 llvm_unreachable(
"Unsupported cache control.");
143 case xegpu::CachePolicy::UNCACHED:
144 if (L3hintVal == xegpu::CachePolicy::CACHED)
145 return xevm::LoadCacheControl::L1UC_L2UC_L3C;
146 else if (L3hintVal == xegpu::CachePolicy::UNCACHED)
147 return xevm::LoadCacheControl::L1UC_L2UC_L3UC;
149 llvm_unreachable(
"Unsupported cache control.");
150 case xegpu::CachePolicy::STREAMING:
151 if (L3hintVal == xegpu::CachePolicy::CACHED)
152 return xevm::LoadCacheControl::L1S_L2UC_L3C;
153 else if (L3hintVal == xegpu::CachePolicy::UNCACHED)
154 return xevm::LoadCacheControl::L1S_L2UC_L3UC;
156 llvm_unreachable(
"Unsupported cache control.");
157 case xegpu::CachePolicy::READ_INVALIDATE:
158 return xevm::LoadCacheControl::INVALIDATE_READ;
160 llvm_unreachable(
"Unsupported cache control.");
164static xevm::StoreCacheControl
165translateStoreXeGPUCacheHint(std::optional<xegpu::CachePolicy> L1hint,
166 std::optional<xegpu::CachePolicy> L3hint) {
168 if (!L1hint && !L3hint)
169 return xevm::StoreCacheControl::USE_DEFAULT;
171 auto L1hintVal = L1hint.value_or(xegpu::CachePolicy::UNCACHED);
172 auto L3hintVal = L3hint.value_or(xegpu::CachePolicy::WRITE_BACK);
174 case xegpu::CachePolicy::UNCACHED:
175 if (L3hintVal == xegpu::CachePolicy::UNCACHED)
176 return xevm::StoreCacheControl::L1UC_L2UC_L3UC;
177 else if (L3hintVal == xegpu::CachePolicy::WRITE_BACK)
178 return xevm::StoreCacheControl::L1UC_L2UC_L3WB;
180 llvm_unreachable(
"Unsupported cache control.");
181 case xegpu::CachePolicy::STREAMING:
182 if (L3hintVal == xegpu::CachePolicy::UNCACHED)
183 return xevm::StoreCacheControl::L1S_L2UC_L3UC;
184 else if (L3hintVal == xegpu::CachePolicy::WRITE_BACK)
185 return xevm::StoreCacheControl::L1S_L2UC_L3WB;
187 llvm_unreachable(
"Unsupported cache control.");
188 case xegpu::CachePolicy::WRITE_BACK:
189 if (L3hintVal == xegpu::CachePolicy::UNCACHED)
190 return xevm::StoreCacheControl::L1WB_L2UC_L3UC;
191 else if (L3hintVal == xegpu::CachePolicy::WRITE_BACK)
192 return xevm::StoreCacheControl::L1WB_L2UC_L3WB;
194 llvm_unreachable(
"Unsupported cache control.");
195 case xegpu::CachePolicy::WRITE_THROUGH:
196 if (L3hintVal == xegpu::CachePolicy::UNCACHED)
197 return xevm::StoreCacheControl::L1WT_L2UC_L3UC;
198 else if (L3hintVal == xegpu::CachePolicy::WRITE_BACK)
199 return xevm::StoreCacheControl::L1WT_L2UC_L3WB;
201 llvm_unreachable(
"Unsupported cache control.");
203 llvm_unreachable(
"Unsupported cache control.");
245class CreateNdDescToXeVMPattern
246 :
public OpConversionPattern<xegpu::CreateNdDescOp> {
260 LogicalResult checkMemoryRestrictions(xegpu::CreateNdDescOp op,
261 int64_t rank)
const {
268 const xegpu::uArch::BlockIOMemoryRestrictions &limits =
269 uArch->getBlockIOMemoryRestrictions();
277 std::optional<int64_t> widthElems;
278 std::optional<int64_t> pitchElems;
279 if (
auto memrefTy = dyn_cast<MemRefType>(op.getSource().getType())) {
280 if (!ShapedType::isDynamic(memrefTy.getDimSize(rank - 1)))
281 widthElems = memrefTy.getDimSize(rank - 1);
283 SmallVector<int64_t> staticStrides;
284 int64_t staticOffset;
286 memrefTy.getStridesAndOffset(staticStrides, staticOffset)) &&
287 !ShapedType::isDynamic(staticStrides[rank - 2]))
288 pitchElems = staticStrides[rank - 2];
294 unsigned elemBitSize =
295 op.getType().getElementType().getIntOrFloatBitWidth();
300 auto describe = [](int64_t bits) -> std::string {
302 return llvm::formatv(
"{0} bytes", bits / 8).str();
303 return llvm::formatv(
"{0} bits", bits).str();
305 auto check = [&](std::optional<int64_t> elems, int64_t minBytes,
307 llvm::StringRef what) -> LogicalResult {
310 int64_t bits = *elems * elemBitSize;
311 if (bits < minBytes * 8)
312 return op.emitError(llvm::formatv(
313 "2D block surface {0} must be at least {1} bytes on this "
315 what, minBytes, describe(bits)));
316 if (alignBytes > 0 && bits % (alignBytes * 8) != 0)
317 return op.emitError(llvm::formatv(
318 "2D block surface {0} must be a multiple of {1} bytes on this "
320 what, alignBytes, describe(bits)));
333 if (widthElems && pitchElems && *pitchElems < *widthElems)
335 llvm::formatv(
"2D block surface pitch must be at least the surface "
336 "width, got a pitch of {0} for a surface {1} wide.",
337 describe(*pitchElems * elemBitSize),
338 describe(*widthElems * elemBitSize)));
343 matchAndRewrite(xegpu::CreateNdDescOp op,
344 xegpu::CreateNdDescOp::Adaptor adaptor,
345 ConversionPatternRewriter &rewriter)
const override {
346 auto loc = op.getLoc();
347 auto source = op.getSource();
351 int64_t rank = op.getType().getRank();
353 auto memrefTy = dyn_cast<MemRefType>(source.getType());
355 if (!memrefTy.isStrided())
356 return rewriter.notifyMatchFailure(op,
"Expected strided Memref.");
357 sourceRank = memrefTy.getRank();
358 }
else if (isa<IntegerType>(source.getType())) {
359 sourceRank = op.getMixedSizes().size();
361 return rewriter.notifyMatchFailure(op,
362 "Expected ranked Memref or integer.");
364 if (sourceRank != rank)
365 return rewriter.notifyMatchFailure(
366 op,
"Expected descriptor rank to match source rank; subview the "
367 "source down to the descriptor rank.");
368 if (rank > maxNdTdescRank)
369 return rewriter.notifyMatchFailure(
370 op,
"Batched nd descriptor supports at most " +
371 std::to_string(maxNdTdescLeadingDims) +
372 " leading dims (rank <= " + std::to_string(maxNdTdescRank) +
376 SmallVector<std::optional<int64_t>> constStrides(rank, std::nullopt);
378 SmallVector<int64_t> staticStrides;
379 int64_t staticOffset;
381 memrefTy.getStridesAndOffset(staticStrides, staticOffset)))
382 for (int64_t d = 0; d < rank; ++d)
383 if (!ShapedType::isDynamic(staticStrides[d]))
384 constStrides[d] = staticStrides[d];
386 SmallVector<OpFoldResult> mixed = op.getMixedStrides();
387 for (int64_t d = 0; d < rank; ++d)
390 if (std::optional<int64_t> pitch = constStrides[rank - 2]) {
391 for (int64_t d = 0; d < rank - 2; ++d) {
392 std::optional<int64_t> leading = constStrides[d];
393 if (leading && (*pitch == 0 || *leading % *pitch != 0))
394 return rewriter.notifyMatchFailure(
395 op,
"Expected each leading (batch) stride to be a multiple of "
396 "the row stride; the source has gaps between planes.");
403 if (rank >= 2 &&
failed(checkMemoryRestrictions(op, rank)))
406 Type payloadElemTy = rewriter.getI32Type();
407 Type i64Ty = rewriter.getI64Type();
411 Value baseAddr = adaptor.getSource();
412 if (isa<IntegerType>(source.getType()) && baseAddr.
getType() != i64Ty) {
414 baseAddr = arith::ExtUIOp::create(rewriter, loc, i64Ty, baseAddr);
418 rewriter.replaceOp(op, baseAddr);
422 SmallVector<OpFoldResult> mixedSizes;
423 SmallVector<OpFoldResult> mixedStrides;
426 memref::ExtractStridedMetadataOp::create(rewriter, loc, source);
427 mixedSizes = meta.getConstifiedMixedSizes();
428 mixedStrides = meta.getConstifiedMixedStrides();
430 mixedSizes = op.getMixedSizes();
431 mixedStrides = op.getMixedStrides();
437 VectorType payloadTy = VectorType::get(8, payloadElemTy);
439 VectorType payloadI64Ty = VectorType::get(4, i64Ty);
441 Value payload = arith::ConstantOp::create(
446 auto createOffset = [&](SmallVector<OpFoldResult> &ofrVec,
447 unsigned idx) -> Value {
453 Value baseShapeW = createOffset(mixedSizes, rank - 1);
455 Value basePitch = createOffset(mixedStrides, rank - 2);
457 SmallVector<Value> leadingRowStrides;
458 for (int64_t d = 0; d < rank - 2; ++d) {
460 std::optional<int64_t> pitch =
462 if (leading && pitch && *pitch != 0)
464 rewriter, loc, payloadElemTy, *leading / *pitch));
466 leadingRowStrides.push_back(arith::DivUIOp::create(
467 rewriter, loc, createOffset(mixedStrides, d), basePitch));
473 Value baseShapeH = createOffset(mixedSizes, rank - 2);
476 for (int64_t d = 0; d < rank - 2; ++d) {
477 Value planesBelow = rewriter.createOrFold<arith::SubIOp>(
478 loc, createOffset(mixedSizes, d), one);
479 Value rows = rewriter.createOrFold<arith::MulIOp>(loc, planesBelow,
480 leadingRowStrides[d]);
482 rewriter.createOrFold<arith::AddIOp>(loc, baseShapeH, rows);
487 vector::BitCastOp::create(rewriter, loc, payloadI64Ty, payload);
489 vector::InsertOp::create(rewriter, loc, baseAddr, payLoadAsI64,
490 static_cast<int>(NdTdescOffset::BasePtr));
491 payload = vector::BitCastOp::create(rewriter, loc, payloadTy, payLoadAsI64);
493 vector::InsertOp::create(rewriter, loc, baseShapeW, payload,
494 static_cast<int>(NdTdescOffset::BaseShapeW));
496 vector::InsertOp::create(rewriter, loc, baseShapeH, payload,
497 static_cast<int>(NdTdescOffset::BaseShapeH));
499 vector::InsertOp::create(rewriter, loc, basePitch, payload,
500 static_cast<int>(NdTdescOffset::BasePitch));
504 for (int64_t d = 0; d < rank - 2; ++d)
505 payload = vector::InsertOp::create(
506 rewriter, loc, leadingRowStrides[d], payload,
507 static_cast<int>(NdTdescOffset::LeadingStride0) + d);
508 rewriter.replaceOp(op, payload);
515 typename = std::enable_if_t<llvm::is_one_of<
516 OpType, xegpu::LoadNdOp, xegpu::StoreNdOp, xegpu::PrefetchNdOp>::value>>
517class LoadStorePrefetchNdToXeVMPattern :
public OpConversionPattern<OpType> {
518 using OpConversionPattern<OpType>::OpConversionPattern;
520 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
521 ConversionPatternRewriter &rewriter)
const override {
522 auto mixedOffsets = op.getMixedOffsets();
523 int64_t opOffsetsSize = mixedOffsets.size();
524 auto loc = op.getLoc();
525 auto ctxt = rewriter.getContext();
527 auto tdesc = adaptor.getTensorDesc();
528 auto tdescTy = op.getTensorDescType();
529 auto tileRank = tdescTy.getRank();
530 if (opOffsetsSize != tileRank)
531 return rewriter.notifyMatchFailure(
532 op,
"Expected offset rank to match descriptor rank.");
533 if (tileRank > 2 && llvm::any_of(tdescTy.getShape().drop_back(2),
534 [](int64_t d) { return d != 1; }))
535 return rewriter.notifyMatchFailure(
536 op,
"Expected leading (batch) descriptor dims to be unit.");
537 if (tileRank > maxNdTdescRank)
538 return rewriter.notifyMatchFailure(
539 op,
"Expected descriptor rank <= " + std::to_string(maxNdTdescRank) +
541 auto elemType = tdescTy.getElementType();
542 auto elemBitSize = elemType.getIntOrFloatBitWidth();
543 bool isSubByte = elemBitSize < 8;
544 uint64_t wScaleFactor = 1;
546 if (!isSubByte && (elemBitSize % 8 != 0))
547 return rewriter.notifyMatchFailure(
548 op,
"Expected element type bit width to be multiple of 8.");
549 auto tileW = tdescTy.getDimSize(tileRank - 1);
552 if (elemBitSize != 4)
553 return rewriter.notifyMatchFailure(
554 op,
"Only sub byte types of 4bits are supported.");
556 return rewriter.notifyMatchFailure(
557 op,
"Sub byte types are only supported for 2D tensor descriptors.");
558 auto subByteFactor = 8 / elemBitSize;
559 auto tileH = tdescTy.getDimSize(0);
561 if constexpr (std::is_same_v<OpType, xegpu::LoadNdOp>) {
562 if (op.getPacked().value_or(
false)) {
564 if (tileH == systolicDepth * 4 &&
565 tileW == executionSize * subByteFactor) {
570 elemType = rewriter.getIntegerType(8);
571 tileW = executionSize;
572 wScaleFactor = subByteFactor;
577 if (wScaleFactor == 1) {
578 auto sub16BitFactor = subByteFactor * 2;
579 if (tileW == executionSize * sub16BitFactor) {
583 elemType = rewriter.getIntegerType(16);
584 tileW = executionSize;
585 wScaleFactor = sub16BitFactor;
587 return rewriter.notifyMatchFailure(
588 op,
"Unsupported tile shape for sub byte types.");
592 elemBitSize = elemType.getIntOrFloatBitWidth();
596 auto ptrTypeLLVM = LLVM::LLVMPointerType::get(
597 ctxt, getNumericXeVMAddrSpace(tdescTy.getMemorySpace()));
601 rewriter, loc, rewriter.getI32Type(), elemBitSize / 8);
602 VectorType payloadI64Ty = VectorType::get(4, rewriter.getI64Type());
604 vector::BitCastOp::create(rewriter, loc, payloadI64Ty, tdesc);
606 vector::ExtractOp::create(rewriter, loc, payLoadAsI64,
607 static_cast<int>(NdTdescOffset::BasePtr));
608 Value baseShapeW = vector::ExtractOp::create(
609 rewriter, loc, tdesc,
static_cast<int>(NdTdescOffset::BaseShapeW));
610 Value baseShapeH = vector::ExtractOp::create(
611 rewriter, loc, tdesc,
static_cast<int>(NdTdescOffset::BaseShapeH));
612 Value basePitch = vector::ExtractOp::create(
613 rewriter, loc, tdesc,
static_cast<int>(NdTdescOffset::BasePitch));
616 mixedOffsets[tileRank - 1]);
618 rewriter.getI32Type(), offsetW);
620 mixedOffsets[tileRank - 2]);
622 rewriter.getI32Type(), offsetH);
629 for (int64_t d = 0; d < tileRank - 2; ++d) {
633 rewriter.getI32Type(), off);
634 Value rowStride = vector::ExtractOp::create(
635 rewriter, loc, tdesc,
636 static_cast<int>(NdTdescOffset::LeadingStride0) + d);
637 Value term = arith::MulIOp::create(rewriter, loc, off, rowStride);
638 offsetH = arith::AddIOp::create(rewriter, loc, offsetH, term);
642 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtr);
646 Value baseShapeWInBytes =
647 arith::MulIOp::create(rewriter, loc, baseShapeW, elemByteSize);
649 Value basePitchBytes =
650 arith::MulIOp::create(rewriter, loc, basePitch, elemByteSize);
652 if (wScaleFactor > 1) {
656 rewriter, loc, rewriter.getI32Type(), llvm::Log2_64(wScaleFactor));
657 baseShapeWInBytes = arith::ShRSIOp::create(
658 rewriter, loc, baseShapeWInBytes, wScaleFactorValLog2);
659 basePitchBytes = arith::ShRSIOp::create(rewriter, loc, basePitchBytes,
660 wScaleFactorValLog2);
662 arith::ShRSIOp::create(rewriter, loc, offsetW, wScaleFactorValLog2);
665 auto tileH = tdescTy.getDimSize(tileRank - 2);
667 int32_t vblocks = tdescTy.getArrayLength();
668 if constexpr (std::is_same_v<OpType, xegpu::StoreNdOp>) {
669 Value src = adaptor.getValue();
675 VectorType srcVecTy = dyn_cast<VectorType>(src.
getType());
677 return rewriter.notifyMatchFailure(
678 op,
"Expected store value to be a vector type.");
680 VectorType newSrcVecTy =
681 encodeVectorTypeTo(srcVecTy, rewriter.getIntegerType(elemBitSize));
682 if (srcVecTy != newSrcVecTy)
683 src = vector::BitCastOp::create(rewriter, loc, newSrcVecTy, src);
684 auto storeCacheControl =
685 translateStoreXeGPUCacheHint(op.getL1Hint(), op.getL3Hint());
686 xevm::BlockStore2dOp::create(
687 rewriter, loc, basePtrLLVM, baseShapeWInBytes, baseShapeH,
688 basePitchBytes, offsetW, offsetH, elemBitSize, tileW, tileH, src,
689 xevm::StoreCacheControlAttr::get(ctxt, storeCacheControl));
690 rewriter.eraseOp(op);
692 auto loadCacheControl =
693 translateLoadXeGPUCacheHint(op.getL1Hint(), op.getL3Hint());
694 if constexpr (std::is_same_v<OpType, xegpu::PrefetchNdOp>) {
695 xevm::BlockPrefetch2dOp::create(
696 rewriter, loc, basePtrLLVM, baseShapeWInBytes, baseShapeH,
697 basePitchBytes, offsetW, offsetH, elemBitSize, tileW, tileH,
698 vblocks, xevm::LoadCacheControlAttr::get(ctxt, loadCacheControl));
699 rewriter.eraseOp(op);
701 VectorType dstVecTy = cast<VectorType>(op.getValue().getType());
702 bool vnni = op.getPacked().value_or(
false);
703 auto transposeValue = op.getTranspose();
705 transposeValue.has_value() && transposeValue.value()[0] == 1;
711 if (elemBitSize == 8 && tileW == 16 && tileH == 32 && !vnni &&
719 if (transpose && elemBitSize < 32) {
720 int32_t scale = 32 / elemBitSize;
722 rewriter, loc, rewriter.getI32Type(), llvm::Log2_64(scale));
723 offsetW = arith::ShRSIOp::create(rewriter, loc, offsetW, scaleLog2);
724 tileW = tileW * elemBitSize / 32;
727 VectorType loadedTy = encodeVectorTypeTo(
728 dstVecTy, vnni ? rewriter.getI32Type()
729 : rewriter.getIntegerType(elemBitSize));
731 Value resultFlatVec = xevm::BlockLoad2dOp::create(
732 rewriter, loc, loadedTy, basePtrLLVM, baseShapeWInBytes,
733 baseShapeH, basePitchBytes, offsetW, offsetH, elemBitSize, tileW,
734 tileH, vblocks, transpose, vnni,
735 xevm::LoadCacheControlAttr::get(ctxt, loadCacheControl));
736 resultFlatVec = vector::BitCastOp::create(
738 encodeVectorTypeTo(loadedTy, dstVecTy.getElementType()),
740 rewriter.replaceOp(op, resultFlatVec);
752 rewriter.getI64Type(), offset);
755 rewriter, loc, rewriter.getI64Type(), elemBitSize / 8);
757 rewriter.createOrFold<arith::MulIOp>(loc, offset, elemByteSize);
759 Value finalAddrI64 = rewriter.createOrFold<arith::AddIOp>(
765 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, finalAddrI64);
766 if constexpr (std::is_same_v<OpType, xegpu::StoreNdOp>) {
767 Value src = adaptor.getValue();
773 VectorType srcVecTy = dyn_cast<VectorType>(src.
getType());
775 return rewriter.notifyMatchFailure(
776 op,
"Expected store value to be a vector type.");
778 VectorType newSrcVecTy =
779 encodeVectorTypeTo(srcVecTy, rewriter.getIntegerType(elemBitSize));
780 if (srcVecTy != newSrcVecTy)
781 src = vector::BitCastOp::create(rewriter, loc, newSrcVecTy, src);
782 auto storeCacheControl =
783 translateStoreXeGPUCacheHint(op.getL1Hint(), op.getL3Hint());
784 rewriter.replaceOpWithNewOp<xevm::BlockStoreOp>(
785 op, finalPtrLLVM, src,
786 xevm::StoreCacheControlAttr::get(ctxt, storeCacheControl));
787 }
else if constexpr (std::is_same_v<OpType, xegpu::LoadNdOp>) {
788 auto loadCacheControl =
789 translateLoadXeGPUCacheHint(op.getL1Hint(), op.getL3Hint());
790 VectorType resTy = cast<VectorType>(op.getValue().getType());
791 VectorType loadedTy =
792 encodeVectorTypeTo(resTy, rewriter.getIntegerType(elemBitSize));
793 Value
load = xevm::BlockLoadOp::create(
794 rewriter, loc, loadedTy, finalPtrLLVM,
795 xevm::LoadCacheControlAttr::get(ctxt, loadCacheControl));
796 if (loadedTy != resTy)
797 load = vector::BitCastOp::create(rewriter, loc, resTy,
load);
798 rewriter.replaceOp(op,
load);
800 return rewriter.notifyMatchFailure(
801 op,
"Unsupported operation: xegpu.prefetch_nd with tensor "
802 "descriptor rank == 1");
811static Value addOffsetToBaseAddr(ConversionPatternRewriter &rewriter,
815 rewriter, loc, baseAddr.
getType(), elemByteSize);
816 Value byteOffset = arith::MulIOp::create(rewriter, loc, offset, byteSize);
817 Value newAddr = arith::AddIOp::create(rewriter, loc, baseAddr, byteOffset);
829static bool isUniformMask(
Value mask) {
830 if (!isa<VectorType>(mask.getType()))
835 if (
auto constantMask = mask.getDefiningOp<vector::ConstantMaskOp>()) {
838 return constantMask.isAllOnesMask() ||
839 llvm::all_of(constantMask.getMaskDimSizes(),
840 [](
int64_t size) { return size == 0; });
842 if (
auto broadcast = mask.getDefiningOp<vector::BroadcastOp>())
843 return !isa<VectorType>(
broadcast.getSource().getType());
844 if (
auto fromElements = mask.getDefiningOp<vector::FromElementsOp>())
845 return llvm::all_equal(fromElements.getElements());
848 if (
auto shapeCast = mask.getDefiningOp<vector::ShapeCastOp>())
849 return isUniformMask(shapeCast.getSource());
853template <
typename OpType,
854 typename = std::enable_if_t<llvm::is_one_of<
855 OpType, xegpu::LoadGatherOp, xegpu::StoreScatterOp>::value>>
856class LoadStoreToXeVMPattern :
public OpConversionPattern<OpType> {
857 using OpConversionPattern<OpType>::OpConversionPattern;
859 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
860 ConversionPatternRewriter &rewriter)
const override {
861 Value offset = adaptor.getOffsets();
863 return rewriter.notifyMatchFailure(op,
"Expected offset to be provided.");
864 auto loc = op.getLoc();
865 auto ctxt = rewriter.getContext();
869 if constexpr (std::is_same_v<OpType, xegpu::LoadGatherOp>)
871 this->getTypeConverter()->convertType(op.getResult().getType());
873 valOrResTy = adaptor.getValue().getType();
874 VectorType valOrResVecTy = dyn_cast<VectorType>(valOrResTy);
875 bool hasScalarVal = !valOrResVecTy;
876 int64_t elemBitWidth =
878 : valOrResVecTy.getElementType().getIntOrFloatBitWidth();
880 if (elemBitWidth % 8 != 0)
881 return rewriter.notifyMatchFailure(
882 op,
"Expected element type bit width to be multiple of 8.");
883 int64_t elemByteSize = elemBitWidth / 8;
885 LLVM::LLVMPointerType ptrTypeLLVM = LLVM::LLVMPointerType::get(
886 ctxt, getNumericXeVMAddrSpace(xegpu::MemorySpace::Global));
889 if constexpr (std::is_same_v<OpType, xegpu::LoadGatherOp>) {
890 basePtrI64 = adaptor.getSource();
891 if (
auto memRefTy = dyn_cast<MemRefType>(op.getSource().getType())) {
892 FailureOr<unsigned> addrSpace =
893 getNumericMemorySpace(memRefTy.getMemorySpace());
895 return rewriter.notifyMatchFailure(
896 op,
"Unsupported memref memory space attribute.");
898 ptrTypeLLVM = LLVM::LLVMPointerType::get(ctxt, *addrSpace);
901 basePtrI64 = adaptor.getDest();
902 if (
auto memRefTy = dyn_cast<MemRefType>(op.getDest().getType())) {
903 FailureOr<unsigned> addrSpace =
904 getNumericMemorySpace(memRefTy.getMemorySpace());
906 return rewriter.notifyMatchFailure(
907 op,
"Unsupported memref memory space attribute.");
909 ptrTypeLLVM = LLVM::LLVMPointerType::get(ctxt, *addrSpace);
913 if (basePtrI64.
getType() != rewriter.getI64Type()) {
914 basePtrI64 = arith::ExtUIOp::create(rewriter, loc, rewriter.getI64Type(),
917 Value mask = adaptor.getMask();
926 auto origOffsetsTy = dyn_cast<VectorType>(op.getOffsets().getType());
927 if (isa<VectorType>(offset.
getType()) && origOffsetsTy && valOrResVecTy &&
928 origOffsetsTy.getNumElements() == valOrResVecTy.getNumElements() &&
929 isUniformMask(op.getMask())) {
930 offset = vector::ExtractOp::create(rewriter, loc, offset,
931 ArrayRef<int64_t>{0});
932 if (isa<VectorType>(mask.
getType()))
933 mask = vector::ExtractOp::create(rewriter, loc, mask,
934 ArrayRef<int64_t>{0});
937 if (dyn_cast<VectorType>(offset.
getType())) {
940 return rewriter.notifyMatchFailure(op,
"Expected offset to be a scalar.");
946 addOffsetToBaseAddr(rewriter, loc, basePtrI64, offset, elemByteSize);
950 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtrI64);
953 VectorType maskVecTy = dyn_cast<VectorType>(mask.
getType());
957 return rewriter.notifyMatchFailure(op,
"Expected mask to be a scalar.");
960 if constexpr (std::is_same_v<OpType, xegpu::LoadGatherOp>) {
961 scf::IfOp ifOp = scf::IfOp::create(rewriter, loc, {valOrResTy},
962 maskForLane,
true,
true);
964 rewriter.setInsertionPointToStart(&ifOp.getThenRegion().front());
966 valOrResTy = VectorType::get({valOrResVecTy.getNumElements()},
967 valOrResVecTy.getElementType());
969 LLVM::LoadOp::create(rewriter, loc, valOrResTy, basePtrLLVM);
972 "cache_control", xevm::LoadCacheControlAttr::get(
973 ctxt, translateLoadXeGPUCacheHint(
974 op.getL1Hint(), op.getL3Hint())));
975 scf::YieldOp::create(rewriter, loc,
ValueRange{loaded});
976 rewriter.setInsertionPointToStart(&ifOp.getElseRegion().front());
978 auto eTy = hasScalarVal ? valOrResTy : valOrResVecTy.getElementType();
981 eVal = FloatAttr::get(eTy, 0.0);
983 eVal = IntegerAttr::get(eTy, 0);
985 loaded = arith::ConstantOp::create(rewriter, loc, eVal);
987 loaded = arith::ConstantOp::create(
989 scf::YieldOp::create(rewriter, loc,
ValueRange{loaded});
990 rewriter.replaceOp(op, ifOp.getResult(0));
993 scf::IfOp ifOp = scf::IfOp::create(rewriter, loc, maskForLane,
false);
994 auto body = ifOp.getBody();
995 rewriter.setInsertionPointToStart(body);
997 LLVM::StoreOp::create(rewriter, loc, adaptor.getValue(), basePtrLLVM);
999 storeOp.getOperation()->setDiscardableAttr(
1000 "cache_control", xevm::StoreCacheControlAttr::get(
1001 ctxt, translateStoreXeGPUCacheHint(
1002 op.getL1Hint(), op.getL3Hint())));
1003 rewriter.eraseOp(op);
1009class CreateMemDescOpPattern final
1010 :
public OpConversionPattern<xegpu::CreateMemDescOp> {
1012 using OpConversionPattern<xegpu::CreateMemDescOp>::OpConversionPattern;
1014 matchAndRewrite(xegpu::CreateMemDescOp op, OpAdaptor adaptor,
1015 ConversionPatternRewriter &rewriter)
const override {
1017 rewriter.replaceOp(op, adaptor.getSource());
1022template <
typename OpType,
1023 typename = std::enable_if_t<llvm::is_one_of<
1024 OpType, xegpu::LoadMatrixOp, xegpu::StoreMatrixOp>::value>>
1025class LoadStoreMatrixToXeVMPattern :
public OpConversionPattern<OpType> {
1026 using OpConversionPattern<OpType>::OpConversionPattern;
1028 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1029 ConversionPatternRewriter &rewriter)
const override {
1031 SmallVector<OpFoldResult> offsets = op.getMixedOffsets();
1032 if (offsets.empty())
1033 return rewriter.notifyMatchFailure(op,
"Expected offset to be provided.");
1035 auto loc = op.getLoc();
1036 auto ctxt = rewriter.getContext();
1037 Value baseAddr32 = adaptor.getMemDesc();
1038 Value mdescVal = op.getMemDesc();
1041 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>) {
1042 Type resType = op.getResult().getType();
1045 if (
auto vecType = dyn_cast<VectorType>(resType)) {
1046 assert(llvm::count_if(vecType.getShape(),
1047 [](int64_t d) { return d != 1; }) <= 1 &&
1048 "Expected either 1D vector or nD with unit dimensions");
1049 resType = VectorType::get({vecType.getNumElements()},
1050 vecType.getElementType());
1054 dataTy = adaptor.getData().getType();
1055 VectorType valOrResVecTy = dyn_cast<VectorType>(dataTy);
1057 valOrResVecTy = VectorType::get(1, dataTy);
1059 int64_t elemBitWidth =
1060 valOrResVecTy.getElementType().getIntOrFloatBitWidth();
1062 if (elemBitWidth % 8 != 0)
1063 return rewriter.notifyMatchFailure(
1064 op,
"Expected element type bit width to be multiple of 8.");
1065 int64_t elemByteSize = elemBitWidth / 8;
1068 LLVM::LLVMPointerType ptrTypeLLVM = LLVM::LLVMPointerType::get(
1069 ctxt, getNumericXeVMAddrSpace(xegpu::MemorySpace::SLM));
1071 auto mdescTy = cast<xegpu::MemDescType>(mdescVal.
getType());
1073 Value linearOffset = mdescTy.getLinearOffsets(rewriter, loc, offsets);
1074 linearOffset = arith::IndexCastUIOp::create(
1075 rewriter, loc, rewriter.getI32Type(), linearOffset);
1076 Value basePtrI32 = addOffsetToBaseAddr(rewriter, loc, baseAddr32,
1077 linearOffset, elemByteSize);
1081 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtrI32);
1083 if (op.getSubgroupBlockIoAttr()) {
1087 Type intElemTy = rewriter.getIntegerType(elemBitWidth);
1088 VectorType intVecTy =
1089 VectorType::get(valOrResVecTy.getShape(), intElemTy);
1091 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>) {
1092 Value loadOp = xevm::BlockLoadOp::create(
1093 rewriter, loc, intVecTy, basePtrLLVM,
nullptr);
1094 if (intVecTy != valOrResVecTy) {
1096 vector::BitCastOp::create(rewriter, loc, valOrResVecTy, loadOp);
1098 rewriter.replaceOp(op, loadOp);
1100 Value dataToStore = adaptor.getData();
1101 if (valOrResVecTy != intVecTy) {
1103 vector::BitCastOp::create(rewriter, loc, intVecTy, dataToStore);
1105 xevm::BlockStoreOp::create(rewriter, loc, basePtrLLVM, dataToStore,
1107 rewriter.eraseOp(op);
1112 if (valOrResVecTy.getNumElements() >= 1) {
1115 (*chipOpt !=
"pvc" && *chipOpt !=
"bmg" && *chipOpt !=
"cri")) {
1117 return rewriter.notifyMatchFailure(
1118 op,
"The lowering is specific to pvc, bmg or cri.");
1122 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>) {
1129 this->getTypeConverter()->convertType(op.getResult().getType());
1130 auto loadOp = LLVM::LoadOp::create(rewriter, loc, loadTy, basePtrLLVM);
1131 rewriter.replaceOp(op, loadOp);
1133 LLVM::StoreOp::create(rewriter, loc, adaptor.getData(), basePtrLLVM);
1134 rewriter.eraseOp(op);
1140class PrefetchToXeVMPattern :
public OpConversionPattern<xegpu::PrefetchOp> {
1143 matchAndRewrite(xegpu::PrefetchOp op, xegpu::PrefetchOp::Adaptor adaptor,
1144 ConversionPatternRewriter &rewriter)
const override {
1145 auto loc = op.getLoc();
1146 auto ctxt = rewriter.getContext();
1147 Value basePtrI64 = adaptor.getSource();
1149 if (basePtrI64.
getType() != rewriter.getI64Type())
1150 basePtrI64 = arith::ExtUIOp::create(rewriter, loc, rewriter.getI64Type(),
1152 Value offsets = adaptor.getOffsets();
1154 VectorType offsetsVecTy = dyn_cast<VectorType>(offsets.
getType());
1157 return rewriter.notifyMatchFailure(op,
1158 "Expected offsets to be a scalar.");
1160 int64_t elemBitWidth{0};
1161 int64_t elemByteSize;
1163 if (
auto memRefTy = dyn_cast<MemRefType>(op.getSourceType())) {
1166 elemBitWidth = memRefTy.getElementType().getIntOrFloatBitWidth();
1169 elemByteSize = *op.getOffsetAlignByte();
1171 if (elemBitWidth != 0) {
1172 if (elemBitWidth % 8 != 0)
1173 return rewriter.notifyMatchFailure(
1174 op,
"Expected element type bit width to be multiple of 8.");
1175 elemByteSize = elemBitWidth / 8;
1177 basePtrI64 = addOffsetToBaseAddr(rewriter, loc, basePtrI64, offsets,
1182 LLVM::LLVMPointerType ptrTypeLLVM = LLVM::LLVMPointerType::get(
1183 ctxt, getNumericXeVMAddrSpace(xegpu::MemorySpace::Global));
1185 if (
auto memRefTy = dyn_cast<MemRefType>(op.getSource().getType())) {
1186 FailureOr<unsigned> addrSpace =
1187 getNumericMemorySpace(memRefTy.getMemorySpace());
1189 return rewriter.notifyMatchFailure(
1190 op,
"Unsupported memref memory space attribute.");
1191 if (*addrSpace != 0)
1192 ptrTypeLLVM = LLVM::LLVMPointerType::get(ctxt, *addrSpace);
1196 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtrI64);
1198 xevm::PrefetchOp::create(
1199 rewriter, loc, ptrLLVM,
1200 xevm::LoadCacheControlAttr::get(
1201 ctxt, translateLoadXeGPUCacheHint(op.getL1Hint(), op.getL3Hint())));
1202 rewriter.eraseOp(op);
1207class FenceToXeVMPattern :
public OpConversionPattern<xegpu::FenceOp> {
1210 matchAndRewrite(xegpu::FenceOp op, xegpu::FenceOp::Adaptor adaptor,
1211 ConversionPatternRewriter &rewriter)
const override {
1212 auto loc = op.getLoc();
1213 xevm::MemScope memScope{xevm::MemScope::WORKGROUP};
1214 switch (op.getFenceScope()) {
1215 case xegpu::FenceScope::Workgroup:
1216 memScope = xevm::MemScope::WORKGROUP;
1218 case xegpu::FenceScope::GPU:
1219 memScope = xevm::MemScope::DEVICE;
1222 xevm::AddrSpace addrSpace{xevm::AddrSpace::GLOBAL};
1223 switch (op.getMemoryKind()) {
1224 case xegpu::MemorySpace::Global:
1225 addrSpace = xevm::AddrSpace::GLOBAL;
1227 case xegpu::MemorySpace::SLM:
1228 addrSpace = xevm::AddrSpace::SHARED;
1231 xevm::MemfenceOp::create(rewriter, loc, memScope, addrSpace);
1232 rewriter.eraseOp(op);
1237static auto encodePrecision = [](
Type type) -> xevm::ElemType {
1239 return xevm::ElemType::BF16;
1240 else if (type.isF16())
1241 return xevm::ElemType::F16;
1242 else if (type.isTF32())
1243 return xevm::ElemType::TF32;
1244 else if (type.isInteger(8)) {
1245 if (type.isUnsignedInteger())
1246 return xevm::ElemType::U8;
1247 return xevm::ElemType::S8;
1248 }
else if (type.isF32())
1249 return xevm::ElemType::F32;
1250 else if (type.isInteger(32))
1251 return xevm::ElemType::S32;
1252 else if (type.isF8E5M2())
1253 return xevm::ElemType::BF8;
1254 else if (type.isF8E4M3FN())
1255 return xevm::ElemType::F8;
1256 else if (mlir::isa<Float4E2M1FNType>(type))
1257 return xevm::ElemType::E2M1;
1258 llvm_unreachable(
"add more support for ElemType");
1261static unsigned getNumOperandsPerDword(xevm::ElemType pTy) {
1263 case xevm::ElemType::TF32:
1265 case xevm::ElemType::BF16:
1266 case xevm::ElemType::F16:
1268 case xevm::ElemType::U8:
1269 case xevm::ElemType::S8:
1270 case xevm::ElemType::F8:
1271 case xevm::ElemType::BF8:
1273 case xevm::ElemType::E2M1:
1276 llvm_unreachable(
"unsupported xevm::ElemType");
1280class DpasToXeVMPattern :
public OpConversionPattern<xegpu::DpasOp> {
1283 matchAndRewrite(xegpu::DpasOp op, xegpu::DpasOp::Adaptor adaptor,
1284 ConversionPatternRewriter &rewriter)
const override {
1285 auto loc = op.getLoc();
1286 auto ctxt = rewriter.getContext();
1287 auto aTy = cast<VectorType>(op.getLhs().getType());
1288 auto bTy = cast<VectorType>(op.getRhs().getType());
1289 auto resultType = cast<VectorType>(op.getResultType());
1294 return rewriter.notifyMatchFailure(op,
"cannot determine target chip");
1298 return rewriter.notifyMatchFailure(op,
"unsupported target uArch");
1301 llvm::dyn_cast_or_null<xegpu::uArch::SubgroupMatrixMultiplyAcc>(
1302 uArch->getInstruction(
1303 xegpu::uArch::InstructionKind::SubgroupMatrixMultiplyAcc)));
1305 return rewriter.notifyMatchFailure(op,
1306 "DPAS not supported by target uArch");
1308 auto checkSupportedTypes = [&](VectorType vecTy,
1310 auto supported = dpasInst->getSupportedTypes(*ctxt, kind);
1311 return llvm::find(supported, vecTy.getElementType()) != supported.end();
1314 if (!checkSupportedTypes(aTy, xegpu::uArch::MMAOpndKind::MatrixA))
1315 return rewriter.notifyMatchFailure(
1316 op,
"A-matrix element type not supported by target uArch");
1317 if (!checkSupportedTypes(bTy, xegpu::uArch::MMAOpndKind::MatrixB))
1318 return rewriter.notifyMatchFailure(
1319 op,
"B-matrix element type not supported by target uArch");
1321 if (!checkSupportedTypes(resultType, xegpu::uArch::MMAOpndKind::MatrixD))
1322 return rewriter.notifyMatchFailure(
1323 op,
"result/accumulator element type not supported by target uArch");
1325 xevm::ElemType precATy = encodePrecision(aTy.getElementType());
1326 xevm::ElemType precBTy = encodePrecision(bTy.getElementType());
1327 Value c = op.getAcc();
1329 auto elementTy = resultType.getElementType();
1330 Attribute initValueAttr;
1331 if (isa<FloatType>(elementTy))
1332 initValueAttr = FloatAttr::get(elementTy, 0.0);
1334 initValueAttr = IntegerAttr::get(elementTy, 0);
1335 c = arith::ConstantOp::create(
1339 Value aVec = op.getLhs();
1340 Value bVec = op.getRhs();
1341 auto cvecty = cast<VectorType>(c.
getType());
1342 xevm::ElemType precCTy = encodePrecision(cvecty.getElementType());
1343 xevm::ElemType precDTy = encodePrecision(resultType.getElementType());
1345 VectorType::get(cvecty.getNumElements(), cvecty.getElementType());
1347 c = vector::ShapeCastOp::create(rewriter, loc, cNty, c);
1348 Value dpasRes = xevm::MMAOp::create(
1349 rewriter, loc, cNty, aVec, bVec, c,
1350 xevm::MMAShapeAttr::get(ctxt, cvecty.getNumElements(), executionSize,
1352 getNumOperandsPerDword(precATy)),
1353 xevm::MMATypesAttr::get(ctxt, precDTy, precATy, precBTy, precCTy));
1355 dpasRes = vector::ShapeCastOp::create(rewriter, loc, resultType, dpasRes);
1356 rewriter.replaceOp(op, dpasRes);
1361static std::optional<LLVM::AtomicBinOp>
1362matchSimpleAtomicOp(arith::AtomicRMWKind arithKind) {
1363 switch (arithKind) {
1364 case arith::AtomicRMWKind::addf:
1365 return LLVM::AtomicBinOp::fadd;
1366 case arith::AtomicRMWKind::addi:
1367 return LLVM::AtomicBinOp::add;
1368 case arith::AtomicRMWKind::assign:
1369 return LLVM::AtomicBinOp::xchg;
1370 case arith::AtomicRMWKind::maximumf:
1371 return LLVM::AtomicBinOp::fmax;
1372 case arith::AtomicRMWKind::maxs:
1373 return LLVM::AtomicBinOp::max;
1374 case arith::AtomicRMWKind::maxu:
1375 return LLVM::AtomicBinOp::umax;
1376 case arith::AtomicRMWKind::minimumf:
1377 return LLVM::AtomicBinOp::fmin;
1378 case arith::AtomicRMWKind::mins:
1379 return LLVM::AtomicBinOp::min;
1380 case arith::AtomicRMWKind::minu:
1381 return LLVM::AtomicBinOp::umin;
1382 case arith::AtomicRMWKind::ori:
1383 return LLVM::AtomicBinOp::_or;
1384 case arith::AtomicRMWKind::andi:
1385 return LLVM::AtomicBinOp::_and;
1387 return std::nullopt;
1391class AtomicRMWToXeVMPattern :
public OpConversionPattern<xegpu::AtomicRMWOp> {
1394 matchAndRewrite(xegpu::AtomicRMWOp op, xegpu::AtomicRMWOp::Adaptor adaptor,
1395 ConversionPatternRewriter &rewriter)
const override {
1396 auto loc = op.getLoc();
1397 auto ctxt = rewriter.getContext();
1398 auto tdesc = op.getTensorDesc().getType();
1399 auto ptrTypeLLVM = LLVM::LLVMPointerType::get(
1400 ctxt, getNumericXeVMAddrSpace(tdesc.getMemorySpace()));
1401 Value basePtrI64 = arith::IndexCastOp::create(
1402 rewriter, loc, rewriter.getI64Type(), adaptor.getTensorDesc());
1404 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtrI64);
1405 VectorType srcOrDstVecTy = cast<VectorType>(op.getValue().getType());
1406 VectorType srcOrDstFlatVecTy = VectorType::get(
1407 srcOrDstVecTy.getNumElements(), srcOrDstVecTy.getElementType());
1408 Value srcFlatVec = vector::ShapeCastOp::create(
1409 rewriter, loc, srcOrDstFlatVecTy, op.getValue());
1410 auto atomicKind = matchSimpleAtomicOp(op.getKind());
1411 assert(atomicKind.has_value());
1412 Value resVec = srcFlatVec;
1413 for (
int i = 0; i < srcOrDstVecTy.getNumElements(); i++) {
1414 auto val = vector::ExtractOp::create(rewriter, loc, resVec, i);
1415 Value idx = LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(),
1416 rewriter.getI64IntegerAttr(i));
1418 LLVM::GEPOp::create(rewriter, loc, ptrTypeLLVM,
1419 srcOrDstVecTy.getElementType(), basePtrLLVM, idx);
1421 LLVM::AtomicRMWOp::create(rewriter, loc, atomicKind.value(), currPtr,
1422 val, LLVM::AtomicOrdering::seq_cst);
1423 resVec = vector::InsertOp::create(rewriter, loc, newVal, resVec, i);
1425 rewriter.replaceOp(op, resVec);
1430class DpasMxToXeVMPattern :
public OpConversionPattern<xegpu::DpasMxOp> {
1433 matchAndRewrite(xegpu::DpasMxOp op, xegpu::DpasMxOp::Adaptor adaptor,
1434 ConversionPatternRewriter &rewriter)
const override {
1435 auto loc = op.getLoc();
1436 auto ctxt = rewriter.getContext();
1437 auto aTy = op.getA().getType();
1438 auto bTy = op.getB().getType();
1440 cast<VectorType>(getTypeConverter()->convertType(op.getType()));
1444 return rewriter.notifyMatchFailure(op,
"cannot determine target chip");
1448 return rewriter.notifyMatchFailure(op,
"unsupported target uArch");
1452 xevm::ElemType precATy = encodePrecision(aTy.getElementType());
1453 xevm::ElemType precBTy = encodePrecision(bTy.getElementType());
1454 Value c = adaptor.getAcc();
1456 auto elementTy = resVecTy.getElementType();
1457 Attribute initValueAttr;
1458 if (isa<FloatType>(elementTy))
1459 initValueAttr = FloatAttr::get(elementTy, 0.0);
1461 initValueAttr = IntegerAttr::get(elementTy, 0);
1462 c = arith::ConstantOp::create(
1466 Value aVec = adaptor.getA();
1467 Value bVec = adaptor.getB();
1468 auto aVecTy = cast<VectorType>(aVec.
getType());
1469 auto bVecTy = cast<VectorType>(bVec.
getType());
1470 if (aVecTy.getElementTypeBitWidth() == 4)
1471 aVec = vector::BitCastOp::create(
1473 VectorType::get(aVecTy.getNumElements() / 2, rewriter.getI8Type()),
1475 if (bVecTy.getElementTypeBitWidth() == 4)
1476 bVec = vector::BitCastOp::create(
1478 VectorType::get(bVecTy.getNumElements() / 2, rewriter.getI8Type()),
1480 auto cVecTy = cast<VectorType>(c.
getType());
1481 xevm::ElemType precCTy = encodePrecision(cVecTy.getElementType());
1482 xevm::ElemType precDTy = encodePrecision(resVecTy.getElementType());
1483 Value scaleA = adaptor.getScaleA();
1484 Value scaleB = adaptor.getScaleB();
1485 Value dpasMxRes = xevm::MMAMxOp::create(
1486 rewriter, loc, resVecTy, aVec, bVec, scaleA, scaleB, c,
1487 xevm::MMAShapeAttr::get(ctxt, cVecTy.getNumElements(), executionSize,
1489 getNumOperandsPerDword(precATy)),
1490 xevm::MMATypesAttr::get(ctxt, precDTy, precATy, precBTy, precCTy));
1491 rewriter.replaceOp(op, dpasMxRes);
1510static constexpr int64_t kXeVMExtfTruncfNumElems = 16;
1513static std::optional<xevm::ExtfSrcElemTypes> getExtfNarrowType(
Type etype) {
1514 if (isa<Float8E5M2Type>(etype))
1515 return xevm::ExtfSrcElemTypes::BF8;
1516 if (isa<Float8E4M3FNType>(etype))
1517 return xevm::ExtfSrcElemTypes::F8;
1518 if (isa<Float4E2M1FNType>(etype))
1519 return xevm::ExtfSrcElemTypes::E2M1;
1520 return std::nullopt;
1524static std::optional<xevm::TruncfDstElemTypes> getTruncfNarrowType(
Type etype) {
1525 if (isa<Float8E5M2Type>(etype))
1526 return xevm::TruncfDstElemTypes::BF8;
1527 if (isa<Float8E4M3FNType>(etype))
1528 return xevm::TruncfDstElemTypes::F8;
1529 if (isa<Float4E2M1FNType>(etype))
1530 return xevm::TruncfDstElemTypes::E2M1;
1531 return std::nullopt;
1536static bool isXeVMExtf(arith::ExtFOp op) {
1537 auto srcTy = dyn_cast<VectorType>(op.getIn().getType());
1538 auto dstTy = dyn_cast<VectorType>(op.getType());
1539 if (!srcTy || !dstTy || srcTy.getRank() != 1 || dstTy.getRank() != 1)
1541 if (dstTy.getNumElements() != kXeVMExtfTruncfNumElems)
1543 Type dstETy = dstTy.getElementType();
1546 return getExtfNarrowType(srcTy.getElementType()).has_value();
1553static bool isXeVMTruncf(arith::TruncFOp op) {
1554 auto srcTy = dyn_cast<VectorType>(op.getIn().getType());
1555 auto dstTy = dyn_cast<VectorType>(op.getType());
1556 if (!srcTy || !dstTy || srcTy.getRank() != 1 || dstTy.getRank() != 1)
1558 int64_t numElems = srcTy.getNumElements();
1559 if (numElems == 0 || numElems % kXeVMExtfTruncfNumElems != 0)
1561 Type srcETy = srcTy.getElementType();
1564 return getTruncfNarrowType(dstTy.getElementType()).has_value();
1567class ExtfToXeVMPattern :
public OpConversionPattern<arith::ExtFOp> {
1570 matchAndRewrite(arith::ExtFOp op, OpAdaptor adaptor,
1571 ConversionPatternRewriter &rewriter)
const override {
1572 if (!isXeVMExtf(op))
1573 return rewriter.notifyMatchFailure(op,
"not a xevm.extf compatible extf");
1574 Location loc = op.getLoc();
1575 MLIRContext *ctx = op.getContext();
1576 auto srcVecTy = cast<VectorType>(op.getIn().getType());
1577 auto dstVecTy = cast<VectorType>(op.getType());
1578 xevm::ExtfSrcElemTypes srcEnum =
1579 *getExtfNarrowType(srcVecTy.getElementType());
1580 xevm::ExtfDstElemTypes dstEnum = dstVecTy.getElementType().isF16()
1581 ? xevm::ExtfDstElemTypes::F16
1582 : xevm::ExtfDstElemTypes::BF16;
1586 Value src = adaptor.getIn();
1587 auto convSrcTy = cast<VectorType>(src.
getType());
1588 if (convSrcTy.getElementTypeBitWidth() == 4)
1589 src = vector::BitCastOp::create(
1591 VectorType::get(convSrcTy.getNumElements() / 2, rewriter.getI8Type()),
1593 Type resTy = getTypeConverter()->convertType(dstVecTy);
1594 Value res = xevm::ExtfOp::create(
1595 rewriter, loc, resTy, src, xevm::ExtfSrcElemTypeAttr::get(ctx, srcEnum),
1596 xevm::ExtfDstElemTypeAttr::get(ctx, dstEnum));
1597 rewriter.replaceOp(op, res);
1602class TruncfToXeVMPattern :
public OpConversionPattern<arith::TruncFOp> {
1605 matchAndRewrite(arith::TruncFOp op, OpAdaptor adaptor,
1606 ConversionPatternRewriter &rewriter)
const override {
1607 if (!isXeVMTruncf(op))
1608 return rewriter.notifyMatchFailure(op,
1609 "not a xevm.truncf compatible truncf");
1610 Location loc = op.getLoc();
1611 MLIRContext *ctx = op.getContext();
1612 auto srcVecTy = cast<VectorType>(op.getIn().getType());
1613 auto dstVecTy = cast<VectorType>(op.getType());
1614 xevm::TruncfSrcElemTypes srcEnum = srcVecTy.getElementType().isF16()
1615 ? xevm::TruncfSrcElemTypes::F16
1616 : xevm::TruncfSrcElemTypes::BF16;
1617 xevm::TruncfDstElemTypes dstEnum =
1618 *getTruncfNarrowType(dstVecTy.getElementType());
1619 auto srcEnumAttr = xevm::TruncfSrcElemTypeAttr::get(ctx, srcEnum);
1620 auto dstEnumAttr = xevm::TruncfDstElemTypeAttr::get(ctx, dstEnum);
1626 int64_t numGroups = srcVecTy.getNumElements() / kXeVMExtfTruncfNumElems;
1627 int64_t groupBytes =
1628 kXeVMExtfTruncfNumElems * dstVecTy.getElementTypeBitWidth() / 8;
1629 Type groupTy = VectorType::get(groupBytes, rewriter.getI8Type());
1631 Value src = adaptor.getIn();
1633 if (numGroups == 1) {
1634 packed = xevm::TruncfOp::create(rewriter, loc, groupTy, src, srcEnumAttr,
1638 VectorType::get(groupBytes * numGroups, rewriter.getI8Type());
1639 packed = arith::ConstantOp::create(rewriter, loc, packedTy,
1640 rewriter.getZeroAttr(packedTy));
1641 for (int64_t group = 0; group < numGroups; group++) {
1642 Value
slice = vector::ExtractStridedSliceOp::create(
1643 rewriter, loc, src, group * kXeVMExtfTruncfNumElems,
1644 kXeVMExtfTruncfNumElems, 1);
1645 Value converted = xevm::TruncfOp::create(rewriter, loc, groupTy,
slice,
1646 srcEnumAttr, dstEnumAttr);
1647 packed = vector::InsertStridedSliceOp::create(
1648 rewriter, loc, converted, packed, group * groupBytes,
1653 Type resTy = getTypeConverter()->convertType(dstVecTy);
1654 if (packed.
getType() != resTy)
1655 packed = vector::BitCastOp::create(rewriter, loc, resTy, packed);
1656 rewriter.replaceOp(op, packed);
1680class LaneShuffleToXeVMPattern
1681 :
public OpConversionPattern<xegpu::LaneShuffleOp> {
1684 matchAndRewrite(xegpu::LaneShuffleOp op, OpAdaptor adaptor,
1685 ConversionPatternRewriter &rewriter)
const override {
1686 auto vecTy = dyn_cast<VectorType>(adaptor.getSource().getType());
1688 return rewriter.notifyMatchFailure(op,
"Expected a vector fragment.");
1692 unsigned elemBits = vecTy.getElementTypeBitWidth();
1693 if (elemBits != 8 && elemBits != 16 && elemBits != 32 && elemBits != 64)
1694 return rewriter.notifyMatchFailure(
1695 op,
"Expected an element type of 8, 16, 32 or 64 bits.");
1696 int64_t fragmentBits = vecTy.getNumElements() * elemBits;
1697 if (fragmentBits > 64 || !llvm::isPowerOf2_64(fragmentBits))
1698 return rewriter.notifyMatchFailure(
1699 op,
"Expected a fragment of 8, 16, 32 or 64 bits.");
1701 Location loc = op.getLoc();
1702 Type packedTy = rewriter.getIntegerType(fragmentBits);
1705 VectorType shuffleTy =
1706 VectorType::get(vecTy.getShape(), rewriter.getIntegerType(elemBits));
1709 if (op.getMode() == xegpu::LaneShuffleMode::Pack) {
1710 Value src = adaptor.getSource();
1711 if (shuffleTy != vecTy)
1712 src = LLVM::BitcastOp::create(rewriter, loc, shuffleTy, src);
1713 res = xevm::BitcastShuffleOp::create(rewriter, loc, packedTy, src);
1714 res = LLVM::BitcastOp::create(rewriter, loc, vecTy, res);
1717 LLVM::BitcastOp::create(rewriter, loc, packedTy, adaptor.getSource());
1718 res = xevm::BitcastShuffleOp::create(rewriter, loc, shuffleTy, packed);
1719 if (shuffleTy != vecTy)
1720 res = LLVM::BitcastOp::create(rewriter, loc, vecTy, res);
1722 rewriter.replaceOp(op, res);
1731struct ConvertXeGPUToXeVMPass
1735 void runOnOperation()
override {
1745 LowerToLLVMOptions
options(context);
1746 options.overrideIndexBitwidth(this->use64bitIndex ? 64 : 32);
1747 LLVMTypeConverter typeConverter(context,
options);
1749 Type xevmIndexType = typeConverter.convertType(IndexType::get(context));
1750 Type i32Type = IntegerType::get(context, 32);
1751 typeConverter.addConversion([&](VectorType type) -> Type {
1752 auto elemType = typeConverter.convertType(type.getElementType());
1754 unsigned rank = type.getRank();
1755 if (rank == 0 || type.getNumElements() == 1)
1758 int64_t sum = llvm::product_of(type.getShape());
1759 return VectorType::get(sum, elemType);
1761 typeConverter.addConversion([&](xegpu::TensorDescType type) -> Type {
1762 if (type.getRank() == 1)
1763 return xevmIndexType;
1764 return VectorType::get(8, i32Type);
1773 typeConverter.addConversion(
1774 [&](xegpu::MemDescType type) -> Type {
return i32Type; });
1776 typeConverter.addConversion([&](MemRefType type) -> Type {
1777 return isSharedMemRef(type) ? i32Type : xevmIndexType;
1787 auto memrefToIntMaterializationCast = [](OpBuilder &builder, Type type,
1789 Location loc) -> Value {
1790 if (inputs.size() != 1)
1792 auto input = inputs.front();
1793 if (
auto memrefTy = dyn_cast<MemRefType>(input.getType())) {
1794 unsigned rank = memrefTy.getRank();
1798 SmallVector<int64_t> intStrides;
1801 if (succeeded(memrefTy.getStridesAndOffset(intStrides, intOffsets)) &&
1802 ShapedType::isStatic(intOffsets)) {
1803 addr = memref::ExtractAlignedPointerAsIndexOp::create(builder, loc,
1805 offset = arith::ConstantOp::create(builder, loc,
1811 SmallVector<Type> resultTypes{
1812 MemRefType::get({}, memrefTy.getElementType(),
1813 MemRefLayoutAttrInterface(),
1814 memrefTy.getMemorySpace()),
1817 resultTypes.append(2 * rank, indexType);
1819 auto meta = memref::ExtractStridedMetadataOp::create(
1820 builder, loc, resultTypes, input);
1822 addr = memref::ExtractAlignedPointerAsIndexOp::create(
1823 builder, loc, meta.getBaseBuffer());
1824 offset = meta.getOffset();
1828 arith::IndexCastUIOp::create(builder, loc, type, addr);
1830 arith::IndexCastUIOp::create(builder, loc, type, offset);
1833 auto byteSize = arith::ConstantOp::create(
1836 memrefTy.getElementTypeBitWidth() / 8));
1838 arith::MulIOp::create(builder, loc, offsetCasted, byteSize);
1839 auto addrWithOffset =
1840 arith::AddIOp::create(builder, loc, addrCasted, byteOffset);
1842 return addrWithOffset.getResult();
1851 auto ui64ToI64MaterializationCast = [](OpBuilder &builder, Type type,
1853 Location loc) -> Value {
1854 if (inputs.size() != 1)
1856 auto input = inputs.front();
1859 index::CastUOp::create(builder, loc, builder.
getIndexType(), input)
1861 return arith::IndexCastUIOp::create(builder, loc, type, cast)
1871 auto ui32ToI32MaterializationCast = [](OpBuilder &builder, Type type,
1873 Location loc) -> Value {
1874 if (inputs.size() != 1)
1876 auto input = inputs.front();
1879 index::CastUOp::create(builder, loc, builder.
getIndexType(), input)
1881 return arith::IndexCastUIOp::create(builder, loc, type, cast)
1891 auto vectorToVectorMaterializationCast = [](OpBuilder &builder, Type type,
1893 Location loc) -> Value {
1894 if (inputs.size() != 1)
1896 auto input = inputs.front();
1897 if (
auto vecTy = dyn_cast<VectorType>(input.getType())) {
1898 if (
auto targetVecTy = dyn_cast<VectorType>(type)) {
1902 if (targetVecTy.getShape() != vecTy.getShape()) {
1903 cast = vector::ShapeCastOp::create(
1905 VectorType::get(targetVecTy.getShape(),
1906 vecTy.getElementType()),
1910 if (targetVecTy.getElementType() != vecTy.getElementType()) {
1911 cast = vector::BitCastOp::create(builder, loc, targetVecTy, cast)
1923 auto vectorToSingleElementMaterializationCast =
1924 [](OpBuilder &builder, Type type,
ValueRange inputs,
1925 Location loc) -> Value {
1926 if (inputs.size() != 1)
1928 auto input = inputs.front();
1929 if (
auto vecTy = dyn_cast<VectorType>(input.getType())) {
1931 auto rank = vecTy.getRank();
1932 if (rank != 0 && vecTy.getNumElements() != 1)
1934 auto inElemTy = vecTy.getElementType();
1938 cast = vector::ExtractOp::create(builder, loc, cast, {}).getResult();
1940 cast = vector::ExtractOp::create(builder, loc, cast,
1941 SmallVector<int64_t>(rank, 0))
1948 if (inElemTy.isIndex()) {
1949 cast = arith::IndexCastUIOp::create(builder, loc, type, cast)
1951 }
else if (inElemTy != type) {
1952 cast = arith::BitcastOp::create(builder, loc, type, cast).getResult();
1966 auto singleElementToVectorMaterializationCast =
1967 [](OpBuilder &builder, Type type,
ValueRange inputs,
1968 Location loc) -> Value {
1969 if (inputs.size() != 1)
1971 auto input = inputs.front();
1972 auto inTy = input.getType();
1973 if (!inTy.isIntOrFloat())
1977 if (
auto vecTy = dyn_cast<VectorType>(type)) {
1978 if (vecTy.getRank() != 0 && vecTy.getNumElements() != 1)
1980 auto outElemTy = vecTy.getElementType();
1982 if (outElemTy.isIndex()) {
1983 cast = arith::IndexCastUIOp::create(builder, loc,
1986 }
else if (inTy != outElemTy) {
1987 cast = arith::BitcastOp::create(builder, loc, outElemTy, cast)
1990 return vector::BroadcastOp::create(builder, loc, vecTy, cast)
1995 typeConverter.addSourceMaterialization(
1996 singleElementToVectorMaterializationCast);
1997 typeConverter.addSourceMaterialization(vectorToVectorMaterializationCast);
1998 typeConverter.addTargetMaterialization(memrefToIntMaterializationCast);
1999 typeConverter.addTargetMaterialization(ui32ToI32MaterializationCast);
2000 typeConverter.addTargetMaterialization(ui64ToI64MaterializationCast);
2001 typeConverter.addTargetMaterialization(
2002 vectorToSingleElementMaterializationCast);
2003 typeConverter.addTargetMaterialization(vectorToVectorMaterializationCast);
2004 ConversionTarget
target(*context);
2005 target.addLegalDialect<xevm::XeVMDialect, LLVM::LLVMDialect,
2006 vector::VectorDialect, arith::ArithDialect,
2007 memref::MemRefDialect, gpu::GPUDialect,
2008 index::IndexDialect>();
2009 target.addIllegalDialect<xegpu::XeGPUDialect>();
2012 target.addDynamicallyLegalOp<arith::ExtFOp>(
2013 [](arith::ExtFOp op) {
return !isXeVMExtf(op); });
2014 target.addDynamicallyLegalOp<arith::TruncFOp>(
2015 [](arith::TruncFOp op) {
return !isXeVMTruncf(op); });
2017 RewritePatternSet patterns(context);
2021 if (
failed(applyPartialConversion(getOperation(),
target,
2022 std::move(patterns))))
2023 signalPassFailure();
2033 patterns.
add<CreateNdDescToXeVMPattern,
2034 LoadStorePrefetchNdToXeVMPattern<xegpu::LoadNdOp>,
2035 LoadStorePrefetchNdToXeVMPattern<xegpu::StoreNdOp>,
2036 LoadStorePrefetchNdToXeVMPattern<xegpu::PrefetchNdOp>>(
2038 patterns.
add<AtomicRMWToXeVMPattern, PrefetchToXeVMPattern,
2039 LoadStoreToXeVMPattern<xegpu::LoadGatherOp>,
2040 LoadStoreToXeVMPattern<xegpu::StoreScatterOp>>(
2042 patterns.
add<LoadStoreMatrixToXeVMPattern<xegpu::LoadMatrixOp>,
2043 LoadStoreMatrixToXeVMPattern<xegpu::StoreMatrixOp>,
2044 CreateMemDescOpPattern>(typeConverter, patterns.
getContext());
2045 patterns.
add<FenceToXeVMPattern, DpasToXeVMPattern>(typeConverter,
2047 patterns.
add<DpasMxToXeVMPattern>(typeConverter, patterns.
getContext());
2048 patterns.
add<ExtfToXeVMPattern, TruncfToXeVMPattern>(typeConverter,
2050 patterns.
add<LaneShuffleToXeVMPattern>(typeConverter, patterns.
getContext());
static llvm::ManagedStatic< PassManagerOptions > options
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
Attributes are known-constant values of operations.
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
IntegerType getIntegerType(unsigned width)
An attribute that represents a reference to a dense vector or tensor object.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
Conversion from types to the LLVM IR dialect.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
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.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
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.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
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...
@ SubgroupMatrixMultiplyAcc
const uArch * getUArch(llvm::StringRef archName)
bool hasStaticShapeAndStrides(MemRefType type)
Returns true if type has a static shape and static strides.
std::optional< std::string > getChipStr(Operation *op)
Retrieves the chip string from the XeVM target attribute of the parent GPU module operation.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Value getValueOrCreateConstantIntOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Value getValueOrCreateCastToIndexLike(OpBuilder &b, Location loc, Type targetType, Value value)
Create a cast from an index-like value (index or integer) to another index-like value.
void populateXeGPUToXeVMConversionPatterns(const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns)
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
int32_t minBaseWidthBytes
int32_t basePitchAlignmentBytes
int32_t baseWidthAlignmentBytes
int32_t minBasePitchBytes