34#include "llvm/ADT/STLExtras.h"
35#include "llvm/ADT/SmallPtrSet.h"
36#include "llvm/Support/FormatVariadic.h"
41#include "llvm/ADT/TypeSwitch.h"
46#define GEN_PASS_DEF_CONVERTXEGPUTOXEVMPASS
47#include "mlir/Conversion/Passes.h.inc"
54static constexpr int32_t systolicDepth{8};
55static constexpr int32_t executionSize{16};
58enum class NdTdescOffset : uint32_t {
69static constexpr int64_t maxNdTdescLeadingDims{3};
70static constexpr int64_t maxNdTdescRank{2 + maxNdTdescLeadingDims};
72static int32_t getNumericXeVMAddrSpace(xegpu::MemorySpace xeGpuMemspace) {
73 switch (xeGpuMemspace) {
74 case xegpu::MemorySpace::Global:
75 return static_cast<int>(xevm::AddrSpace::GLOBAL);
76 case xegpu::MemorySpace::SLM:
77 return static_cast<int>(xevm::AddrSpace::SHARED);
79 llvm_unreachable(
"Unknown XeGPU memory space");
89static FailureOr<unsigned> getNumericMemorySpace(
Attribute memSpace) {
92 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(memSpace))
93 return static_cast<unsigned>(intAttr.getInt());
94 if (
auto xevmSpace = llvm::dyn_cast<xevm::AddrSpaceAttr>(memSpace))
95 return static_cast<unsigned>(xevmSpace.getValue());
96 if (
auto gpuSpace = llvm::dyn_cast<gpu::AddressSpaceAttr>(memSpace)) {
97 switch (gpuSpace.getValue()) {
98 case gpu::AddressSpace::Global:
99 return static_cast<unsigned>(xevm::AddrSpace::GLOBAL);
100 case gpu::AddressSpace::Workgroup:
101 return static_cast<unsigned>(xevm::AddrSpace::SHARED);
102 case gpu::AddressSpace::Private:
103 return static_cast<unsigned>(xevm::AddrSpace::PRIVATE);
104 case gpu::AddressSpace::Constant:
105 return static_cast<unsigned>(xevm::AddrSpace::CONSTANT);
107 llvm_unreachable(
"Unknown GPU address space");
113static bool isSharedMemRef(
const MemRefType &memrefTy) {
114 FailureOr<unsigned> addrSpace =
115 getNumericMemorySpace(memrefTy.getMemorySpace());
116 return succeeded(addrSpace) &&
117 *addrSpace ==
static_cast<unsigned>(xevm::AddrSpace::SHARED);
121static VectorType encodeVectorTypeTo(VectorType currentVecType,
123 auto elemType = currentVecType.getElementType();
124 auto currentBitWidth = elemType.getIntOrFloatBitWidth();
127 currentVecType.getNumElements() * currentBitWidth / newBitWidth;
128 return VectorType::get(size, toElemType);
131static xevm::LoadCacheControl
132translateLoadXeGPUCacheHint(std::optional<xegpu::CachePolicy> L1hint,
133 std::optional<xegpu::CachePolicy> L3hint) {
135 if (!L1hint && !L3hint)
136 return xevm::LoadCacheControl::USE_DEFAULT;
138 auto L1hintVal = L1hint.value_or(xegpu::CachePolicy::CACHED);
139 auto L3hintVal = L3hint.value_or(xegpu::CachePolicy::CACHED);
141 case xegpu::CachePolicy::CACHED:
142 if (L3hintVal == xegpu::CachePolicy::CACHED)
143 return xevm::LoadCacheControl::L1C_L2UC_L3C;
144 else if (L3hintVal == xegpu::CachePolicy::UNCACHED)
145 return xevm::LoadCacheControl::L1C_L2UC_L3UC;
147 llvm_unreachable(
"Unsupported cache control.");
148 case xegpu::CachePolicy::UNCACHED:
149 if (L3hintVal == xegpu::CachePolicy::CACHED)
150 return xevm::LoadCacheControl::L1UC_L2UC_L3C;
151 else if (L3hintVal == xegpu::CachePolicy::UNCACHED)
152 return xevm::LoadCacheControl::L1UC_L2UC_L3UC;
154 llvm_unreachable(
"Unsupported cache control.");
155 case xegpu::CachePolicy::STREAMING:
156 if (L3hintVal == xegpu::CachePolicy::CACHED)
157 return xevm::LoadCacheControl::L1S_L2UC_L3C;
158 else if (L3hintVal == xegpu::CachePolicy::UNCACHED)
159 return xevm::LoadCacheControl::L1S_L2UC_L3UC;
161 llvm_unreachable(
"Unsupported cache control.");
162 case xegpu::CachePolicy::READ_INVALIDATE:
163 return xevm::LoadCacheControl::INVALIDATE_READ;
165 llvm_unreachable(
"Unsupported cache control.");
169static xevm::StoreCacheControl
170translateStoreXeGPUCacheHint(std::optional<xegpu::CachePolicy> L1hint,
171 std::optional<xegpu::CachePolicy> L3hint) {
173 if (!L1hint && !L3hint)
174 return xevm::StoreCacheControl::USE_DEFAULT;
176 auto L1hintVal = L1hint.value_or(xegpu::CachePolicy::UNCACHED);
177 auto L3hintVal = L3hint.value_or(xegpu::CachePolicy::WRITE_BACK);
179 case xegpu::CachePolicy::UNCACHED:
180 if (L3hintVal == xegpu::CachePolicy::UNCACHED)
181 return xevm::StoreCacheControl::L1UC_L2UC_L3UC;
182 else if (L3hintVal == xegpu::CachePolicy::WRITE_BACK)
183 return xevm::StoreCacheControl::L1UC_L2UC_L3WB;
185 llvm_unreachable(
"Unsupported cache control.");
186 case xegpu::CachePolicy::STREAMING:
187 if (L3hintVal == xegpu::CachePolicy::UNCACHED)
188 return xevm::StoreCacheControl::L1S_L2UC_L3UC;
189 else if (L3hintVal == xegpu::CachePolicy::WRITE_BACK)
190 return xevm::StoreCacheControl::L1S_L2UC_L3WB;
192 llvm_unreachable(
"Unsupported cache control.");
193 case xegpu::CachePolicy::WRITE_BACK:
194 if (L3hintVal == xegpu::CachePolicy::UNCACHED)
195 return xevm::StoreCacheControl::L1WB_L2UC_L3UC;
196 else if (L3hintVal == xegpu::CachePolicy::WRITE_BACK)
197 return xevm::StoreCacheControl::L1WB_L2UC_L3WB;
199 llvm_unreachable(
"Unsupported cache control.");
200 case xegpu::CachePolicy::WRITE_THROUGH:
201 if (L3hintVal == xegpu::CachePolicy::UNCACHED)
202 return xevm::StoreCacheControl::L1WT_L2UC_L3UC;
203 else if (L3hintVal == xegpu::CachePolicy::WRITE_BACK)
204 return xevm::StoreCacheControl::L1WT_L2UC_L3WB;
206 llvm_unreachable(
"Unsupported cache control.");
208 llvm_unreachable(
"Unsupported cache control.");
250class CreateNdDescToXeVMPattern
251 :
public OpConversionPattern<xegpu::CreateNdDescOp> {
265 LogicalResult checkMemoryRestrictions(xegpu::CreateNdDescOp op,
266 int64_t rank)
const {
273 const xegpu::uArch::BlockIOMemoryRestrictions &limits =
274 uArch->getBlockIOMemoryRestrictions();
282 std::optional<int64_t> widthElems;
283 std::optional<int64_t> pitchElems;
284 if (
auto memrefTy = dyn_cast<MemRefType>(op.getSource().getType())) {
285 if (!ShapedType::isDynamic(memrefTy.getDimSize(rank - 1)))
286 widthElems = memrefTy.getDimSize(rank - 1);
288 SmallVector<int64_t> staticStrides;
289 int64_t staticOffset;
291 memrefTy.getStridesAndOffset(staticStrides, staticOffset)) &&
292 !ShapedType::isDynamic(staticStrides[rank - 2]))
293 pitchElems = staticStrides[rank - 2];
299 unsigned elemBitSize =
300 op.getType().getElementType().getIntOrFloatBitWidth();
305 auto describe = [](int64_t bits) -> std::string {
307 return llvm::formatv(
"{0} bytes", bits / 8).str();
308 return llvm::formatv(
"{0} bits", bits).str();
310 auto check = [&](std::optional<int64_t> elems, int64_t minBytes,
312 llvm::StringRef what) -> LogicalResult {
315 int64_t bits = *elems * elemBitSize;
316 if (bits < minBytes * 8)
317 return op.emitError(llvm::formatv(
318 "2D block surface {0} must be at least {1} bytes on this "
320 what, minBytes, describe(bits)));
321 if (alignBytes > 0 && bits % (alignBytes * 8) != 0)
322 return op.emitError(llvm::formatv(
323 "2D block surface {0} must be a multiple of {1} bytes on this "
325 what, alignBytes, describe(bits)));
338 if (widthElems && pitchElems && *pitchElems < *widthElems)
340 llvm::formatv(
"2D block surface pitch must be at least the surface "
341 "width, got a pitch of {0} for a surface {1} wide.",
342 describe(*pitchElems * elemBitSize),
343 describe(*widthElems * elemBitSize)));
348 matchAndRewrite(xegpu::CreateNdDescOp op,
349 xegpu::CreateNdDescOp::Adaptor adaptor,
350 ConversionPatternRewriter &rewriter)
const override {
351 auto loc = op.getLoc();
352 auto source = op.getSource();
356 int64_t rank = op.getType().getRank();
358 auto memrefTy = dyn_cast<MemRefType>(source.getType());
360 if (!memrefTy.isStrided())
361 return rewriter.notifyMatchFailure(op,
"Expected strided Memref.");
362 sourceRank = memrefTy.getRank();
363 }
else if (isa<IntegerType>(source.getType())) {
364 sourceRank = op.getMixedSizes().size();
366 return rewriter.notifyMatchFailure(op,
367 "Expected ranked Memref or integer.");
369 if (sourceRank != rank)
370 return rewriter.notifyMatchFailure(
371 op,
"Expected descriptor rank to match source rank; subview the "
372 "source down to the descriptor rank.");
373 if (rank > maxNdTdescRank)
374 return rewriter.notifyMatchFailure(
375 op,
"Batched nd descriptor supports at most " +
376 std::to_string(maxNdTdescLeadingDims) +
377 " leading dims (rank <= " + std::to_string(maxNdTdescRank) +
381 SmallVector<std::optional<int64_t>> constStrides(rank, std::nullopt);
383 SmallVector<int64_t> staticStrides;
384 int64_t staticOffset;
386 memrefTy.getStridesAndOffset(staticStrides, staticOffset)))
387 for (int64_t d = 0; d < rank; ++d)
388 if (!ShapedType::isDynamic(staticStrides[d]))
389 constStrides[d] = staticStrides[d];
391 SmallVector<OpFoldResult> mixed = op.getMixedStrides();
392 for (int64_t d = 0; d < rank; ++d)
395 if (std::optional<int64_t> pitch = constStrides[rank - 2]) {
396 for (int64_t d = 0; d < rank - 2; ++d) {
397 std::optional<int64_t> leading = constStrides[d];
398 if (leading && (*pitch == 0 || *leading % *pitch != 0))
399 return rewriter.notifyMatchFailure(
400 op,
"Expected each leading (batch) stride to be a multiple of "
401 "the row stride; the source has gaps between planes.");
408 if (rank >= 2 &&
failed(checkMemoryRestrictions(op, rank)))
411 Type payloadElemTy = rewriter.getI32Type();
412 Type i64Ty = rewriter.getI64Type();
416 Value baseAddr = adaptor.getSource();
417 if (isa<IntegerType>(source.getType()) && baseAddr.
getType() != i64Ty) {
419 baseAddr = arith::ExtUIOp::create(rewriter, loc, i64Ty, baseAddr);
423 rewriter.replaceOp(op, baseAddr);
427 SmallVector<OpFoldResult> mixedSizes;
428 SmallVector<OpFoldResult> mixedStrides;
431 memref::ExtractStridedMetadataOp::create(rewriter, loc, source);
432 mixedSizes = meta.getConstifiedMixedSizes();
433 mixedStrides = meta.getConstifiedMixedStrides();
435 mixedSizes = op.getMixedSizes();
436 mixedStrides = op.getMixedStrides();
442 VectorType payloadTy = VectorType::get(8, payloadElemTy);
444 VectorType payloadI64Ty = VectorType::get(4, i64Ty);
446 Value payload = arith::ConstantOp::create(
451 auto createOffset = [&](SmallVector<OpFoldResult> &ofrVec,
452 unsigned idx) -> Value {
458 Value baseShapeW = createOffset(mixedSizes, rank - 1);
460 Value basePitch = createOffset(mixedStrides, rank - 2);
462 SmallVector<Value> leadingRowStrides;
463 for (int64_t d = 0; d < rank - 2; ++d) {
465 std::optional<int64_t> pitch =
467 if (leading && pitch && *pitch != 0)
469 rewriter, loc, payloadElemTy, *leading / *pitch));
471 leadingRowStrides.push_back(arith::DivUIOp::create(
472 rewriter, loc, createOffset(mixedStrides, d), basePitch));
478 Value baseShapeH = createOffset(mixedSizes, rank - 2);
481 for (int64_t d = 0; d < rank - 2; ++d) {
482 Value planesBelow = rewriter.createOrFold<arith::SubIOp>(
483 loc, createOffset(mixedSizes, d), one);
484 Value rows = rewriter.createOrFold<arith::MulIOp>(loc, planesBelow,
485 leadingRowStrides[d]);
487 rewriter.createOrFold<arith::AddIOp>(loc, baseShapeH, rows);
492 vector::BitCastOp::create(rewriter, loc, payloadI64Ty, payload);
494 vector::InsertOp::create(rewriter, loc, baseAddr, payLoadAsI64,
495 static_cast<int>(NdTdescOffset::BasePtr));
496 payload = vector::BitCastOp::create(rewriter, loc, payloadTy, payLoadAsI64);
498 vector::InsertOp::create(rewriter, loc, baseShapeW, payload,
499 static_cast<int>(NdTdescOffset::BaseShapeW));
501 vector::InsertOp::create(rewriter, loc, baseShapeH, payload,
502 static_cast<int>(NdTdescOffset::BaseShapeH));
504 vector::InsertOp::create(rewriter, loc, basePitch, payload,
505 static_cast<int>(NdTdescOffset::BasePitch));
509 for (int64_t d = 0; d < rank - 2; ++d)
510 payload = vector::InsertOp::create(
511 rewriter, loc, leadingRowStrides[d], payload,
512 static_cast<int>(NdTdescOffset::LeadingStride0) + d);
513 rewriter.replaceOp(op, payload);
520 typename = std::enable_if_t<llvm::is_one_of<
521 OpType, xegpu::LoadNdOp, xegpu::StoreNdOp, xegpu::PrefetchNdOp>::value>>
522class LoadStorePrefetchNdToXeVMPattern :
public OpConversionPattern<OpType> {
523 using OpConversionPattern<OpType>::OpConversionPattern;
525 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
526 ConversionPatternRewriter &rewriter)
const override {
527 auto mixedOffsets = op.getMixedOffsets();
528 int64_t opOffsetsSize = mixedOffsets.size();
529 auto loc = op.getLoc();
530 auto ctxt = rewriter.getContext();
532 auto tdesc = adaptor.getTensorDesc();
533 auto tdescTy = op.getTensorDescType();
534 auto tileRank = tdescTy.getRank();
535 if (opOffsetsSize != tileRank)
536 return rewriter.notifyMatchFailure(
537 op,
"Expected offset rank to match descriptor rank.");
538 if (tileRank > 2 && llvm::any_of(tdescTy.getShape().drop_back(2),
539 [](int64_t d) { return d != 1; }))
540 return rewriter.notifyMatchFailure(
541 op,
"Expected leading (batch) descriptor dims to be unit.");
542 if (tileRank > maxNdTdescRank)
543 return rewriter.notifyMatchFailure(
544 op,
"Expected descriptor rank <= " + std::to_string(maxNdTdescRank) +
546 auto elemType = tdescTy.getElementType();
547 auto elemBitSize = elemType.getIntOrFloatBitWidth();
548 bool isSubByte = elemBitSize < 8;
549 uint64_t wScaleFactor = 1;
551 if (!isSubByte && (elemBitSize % 8 != 0))
552 return rewriter.notifyMatchFailure(
553 op,
"Expected element type bit width to be multiple of 8.");
554 auto tileW = tdescTy.getDimSize(tileRank - 1);
557 if (elemBitSize != 4)
558 return rewriter.notifyMatchFailure(
559 op,
"Only sub byte types of 4bits are supported.");
561 return rewriter.notifyMatchFailure(
562 op,
"Sub byte types are only supported for 2D tensor descriptors.");
563 auto subByteFactor = 8 / elemBitSize;
564 auto tileH = tdescTy.getDimSize(0);
566 if constexpr (std::is_same_v<OpType, xegpu::LoadNdOp>) {
567 if (op.getPacked().value_or(
false)) {
569 if (tileH == systolicDepth * 4 &&
570 tileW == executionSize * subByteFactor) {
575 elemType = rewriter.getIntegerType(8);
576 tileW = executionSize;
577 wScaleFactor = subByteFactor;
582 if (wScaleFactor == 1) {
583 auto sub16BitFactor = subByteFactor * 2;
584 if (tileW == executionSize * sub16BitFactor) {
588 elemType = rewriter.getIntegerType(16);
589 tileW = executionSize;
590 wScaleFactor = sub16BitFactor;
592 return rewriter.notifyMatchFailure(
593 op,
"Unsupported tile shape for sub byte types.");
597 elemBitSize = elemType.getIntOrFloatBitWidth();
601 auto ptrTypeLLVM = LLVM::LLVMPointerType::get(
602 ctxt, getNumericXeVMAddrSpace(tdescTy.getMemorySpace()));
606 rewriter, loc, rewriter.getI32Type(), elemBitSize / 8);
607 VectorType payloadI64Ty = VectorType::get(4, rewriter.getI64Type());
609 vector::BitCastOp::create(rewriter, loc, payloadI64Ty, tdesc);
611 vector::ExtractOp::create(rewriter, loc, payLoadAsI64,
612 static_cast<int>(NdTdescOffset::BasePtr));
613 Value baseShapeW = vector::ExtractOp::create(
614 rewriter, loc, tdesc,
static_cast<int>(NdTdescOffset::BaseShapeW));
615 Value baseShapeH = vector::ExtractOp::create(
616 rewriter, loc, tdesc,
static_cast<int>(NdTdescOffset::BaseShapeH));
617 Value basePitch = vector::ExtractOp::create(
618 rewriter, loc, tdesc,
static_cast<int>(NdTdescOffset::BasePitch));
621 mixedOffsets[tileRank - 1]);
623 rewriter.getI32Type(), offsetW);
625 mixedOffsets[tileRank - 2]);
627 rewriter.getI32Type(), offsetH);
634 for (int64_t d = 0; d < tileRank - 2; ++d) {
638 rewriter.getI32Type(), off);
639 Value rowStride = vector::ExtractOp::create(
640 rewriter, loc, tdesc,
641 static_cast<int>(NdTdescOffset::LeadingStride0) + d);
642 Value term = arith::MulIOp::create(rewriter, loc, off, rowStride);
643 offsetH = arith::AddIOp::create(rewriter, loc, offsetH, term);
647 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtr);
651 Value baseShapeWInBytes =
652 arith::MulIOp::create(rewriter, loc, baseShapeW, elemByteSize);
654 Value basePitchBytes =
655 arith::MulIOp::create(rewriter, loc, basePitch, elemByteSize);
657 if (wScaleFactor > 1) {
661 rewriter, loc, rewriter.getI32Type(), llvm::Log2_64(wScaleFactor));
662 baseShapeWInBytes = arith::ShRSIOp::create(
663 rewriter, loc, baseShapeWInBytes, wScaleFactorValLog2);
664 basePitchBytes = arith::ShRSIOp::create(rewriter, loc, basePitchBytes,
665 wScaleFactorValLog2);
667 arith::ShRSIOp::create(rewriter, loc, offsetW, wScaleFactorValLog2);
670 auto tileH = tdescTy.getDimSize(tileRank - 2);
672 int32_t vblocks = tdescTy.getArrayLength();
673 if constexpr (std::is_same_v<OpType, xegpu::StoreNdOp>) {
674 Value src = adaptor.getValue();
680 VectorType srcVecTy = dyn_cast<VectorType>(src.
getType());
682 return rewriter.notifyMatchFailure(
683 op,
"Expected store value to be a vector type.");
685 VectorType newSrcVecTy =
686 encodeVectorTypeTo(srcVecTy, rewriter.getIntegerType(elemBitSize));
687 if (srcVecTy != newSrcVecTy)
688 src = vector::BitCastOp::create(rewriter, loc, newSrcVecTy, src);
689 auto storeCacheControl =
690 translateStoreXeGPUCacheHint(op.getL1Hint(), op.getL3Hint());
691 xevm::BlockStore2dOp::create(
692 rewriter, loc, basePtrLLVM, baseShapeWInBytes, baseShapeH,
693 basePitchBytes, offsetW, offsetH, elemBitSize, tileW, tileH, src,
694 xevm::StoreCacheControlAttr::get(ctxt, storeCacheControl));
695 rewriter.eraseOp(op);
697 auto loadCacheControl =
698 translateLoadXeGPUCacheHint(op.getL1Hint(), op.getL3Hint());
699 if constexpr (std::is_same_v<OpType, xegpu::PrefetchNdOp>) {
700 xevm::BlockPrefetch2dOp::create(
701 rewriter, loc, basePtrLLVM, baseShapeWInBytes, baseShapeH,
702 basePitchBytes, offsetW, offsetH, elemBitSize, tileW, tileH,
703 vblocks, xevm::LoadCacheControlAttr::get(ctxt, loadCacheControl));
704 rewriter.eraseOp(op);
706 VectorType dstVecTy = cast<VectorType>(op.getValue().getType());
707 bool vnni = op.getPacked().value_or(
false);
708 auto transposeValue = op.getTranspose();
710 transposeValue.has_value() && transposeValue.value()[0] == 1;
716 if (elemBitSize == 8 && tileW == 16 && tileH == 32 && !vnni &&
724 if (transpose && elemBitSize < 32) {
725 int32_t scale = 32 / elemBitSize;
727 rewriter, loc, rewriter.getI32Type(), llvm::Log2_64(scale));
728 offsetW = arith::ShRSIOp::create(rewriter, loc, offsetW, scaleLog2);
729 tileW = tileW * elemBitSize / 32;
732 VectorType loadedTy = encodeVectorTypeTo(
733 dstVecTy, vnni ? rewriter.getI32Type()
734 : rewriter.getIntegerType(elemBitSize));
736 Value resultFlatVec = xevm::BlockLoad2dOp::create(
737 rewriter, loc, loadedTy, basePtrLLVM, baseShapeWInBytes,
738 baseShapeH, basePitchBytes, offsetW, offsetH, elemBitSize, tileW,
739 tileH, vblocks, transpose, vnni,
740 xevm::LoadCacheControlAttr::get(ctxt, loadCacheControl));
741 resultFlatVec = vector::BitCastOp::create(
743 encodeVectorTypeTo(loadedTy, dstVecTy.getElementType()),
745 rewriter.replaceOp(op, resultFlatVec);
757 rewriter.getI64Type(), offset);
760 rewriter, loc, rewriter.getI64Type(), elemBitSize / 8);
762 rewriter.createOrFold<arith::MulIOp>(loc, offset, elemByteSize);
764 Value finalAddrI64 = rewriter.createOrFold<arith::AddIOp>(
770 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, finalAddrI64);
771 if constexpr (std::is_same_v<OpType, xegpu::StoreNdOp>) {
772 Value src = adaptor.getValue();
778 VectorType srcVecTy = dyn_cast<VectorType>(src.
getType());
780 return rewriter.notifyMatchFailure(
781 op,
"Expected store value to be a vector type.");
783 VectorType newSrcVecTy =
784 encodeVectorTypeTo(srcVecTy, rewriter.getIntegerType(elemBitSize));
785 if (srcVecTy != newSrcVecTy)
786 src = vector::BitCastOp::create(rewriter, loc, newSrcVecTy, src);
787 auto storeCacheControl =
788 translateStoreXeGPUCacheHint(op.getL1Hint(), op.getL3Hint());
789 rewriter.replaceOpWithNewOp<xevm::BlockStoreOp>(
790 op, finalPtrLLVM, src,
791 xevm::StoreCacheControlAttr::get(ctxt, storeCacheControl));
792 }
else if constexpr (std::is_same_v<OpType, xegpu::LoadNdOp>) {
793 auto loadCacheControl =
794 translateLoadXeGPUCacheHint(op.getL1Hint(), op.getL3Hint());
795 VectorType resTy = cast<VectorType>(op.getValue().getType());
796 VectorType loadedTy =
797 encodeVectorTypeTo(resTy, rewriter.getIntegerType(elemBitSize));
798 Value
load = xevm::BlockLoadOp::create(
799 rewriter, loc, loadedTy, finalPtrLLVM,
800 xevm::LoadCacheControlAttr::get(ctxt, loadCacheControl));
801 if (loadedTy != resTy)
802 load = vector::BitCastOp::create(rewriter, loc, resTy,
load);
803 rewriter.replaceOp(op,
load);
805 return rewriter.notifyMatchFailure(
806 op,
"Unsupported operation: xegpu.prefetch_nd with tensor "
807 "descriptor rank == 1");
816static Value addOffsetToBaseAddr(ConversionPatternRewriter &rewriter,
820 rewriter, loc, baseAddr.
getType(), elemByteSize);
821 Value byteOffset = arith::MulIOp::create(rewriter, loc, offset, byteSize);
822 Value newAddr = arith::AddIOp::create(rewriter, loc, baseAddr, byteOffset);
834static bool isUniformMask(
Value mask) {
835 if (!isa<VectorType>(mask.getType()))
840 if (
auto constantMask = mask.getDefiningOp<vector::ConstantMaskOp>()) {
843 return constantMask.isAllOnesMask() ||
844 llvm::all_of(constantMask.getMaskDimSizes(),
845 [](
int64_t size) { return size == 0; });
847 if (
auto broadcast = mask.getDefiningOp<vector::BroadcastOp>())
848 return !isa<VectorType>(
broadcast.getSource().getType());
849 if (
auto fromElements = mask.getDefiningOp<vector::FromElementsOp>())
850 return llvm::all_equal(fromElements.getElements());
853 if (
auto shapeCast = mask.getDefiningOp<vector::ShapeCastOp>())
854 return isUniformMask(shapeCast.getSource());
858template <
typename OpType,
859 typename = std::enable_if_t<llvm::is_one_of<
860 OpType, xegpu::LoadGatherOp, xegpu::StoreScatterOp>::value>>
861class LoadStoreToXeVMPattern :
public OpConversionPattern<OpType> {
862 using OpConversionPattern<OpType>::OpConversionPattern;
864 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
865 ConversionPatternRewriter &rewriter)
const override {
866 Value offset = adaptor.getOffsets();
868 return rewriter.notifyMatchFailure(op,
"Expected offset to be provided.");
869 auto loc = op.getLoc();
870 auto ctxt = rewriter.getContext();
874 if constexpr (std::is_same_v<OpType, xegpu::LoadGatherOp>)
876 this->getTypeConverter()->convertType(op.getResult().getType());
878 valOrResTy = adaptor.getValue().getType();
879 VectorType valOrResVecTy = dyn_cast<VectorType>(valOrResTy);
880 bool hasScalarVal = !valOrResVecTy;
881 int64_t elemBitWidth =
883 : valOrResVecTy.getElementType().getIntOrFloatBitWidth();
885 if (elemBitWidth % 8 != 0)
886 return rewriter.notifyMatchFailure(
887 op,
"Expected element type bit width to be multiple of 8.");
888 int64_t elemByteSize = elemBitWidth / 8;
890 LLVM::LLVMPointerType ptrTypeLLVM = LLVM::LLVMPointerType::get(
891 ctxt, getNumericXeVMAddrSpace(xegpu::MemorySpace::Global));
894 if constexpr (std::is_same_v<OpType, xegpu::LoadGatherOp>) {
895 basePtrI64 = adaptor.getSource();
896 if (
auto memRefTy = dyn_cast<MemRefType>(op.getSource().getType())) {
897 FailureOr<unsigned> addrSpace =
898 getNumericMemorySpace(memRefTy.getMemorySpace());
900 return rewriter.notifyMatchFailure(
901 op,
"Unsupported memref memory space attribute.");
903 ptrTypeLLVM = LLVM::LLVMPointerType::get(ctxt, *addrSpace);
906 basePtrI64 = adaptor.getDest();
907 if (
auto memRefTy = dyn_cast<MemRefType>(op.getDest().getType())) {
908 FailureOr<unsigned> addrSpace =
909 getNumericMemorySpace(memRefTy.getMemorySpace());
911 return rewriter.notifyMatchFailure(
912 op,
"Unsupported memref memory space attribute.");
914 ptrTypeLLVM = LLVM::LLVMPointerType::get(ctxt, *addrSpace);
918 if (basePtrI64.
getType() != rewriter.getI64Type()) {
919 basePtrI64 = arith::ExtUIOp::create(rewriter, loc, rewriter.getI64Type(),
922 Value mask = adaptor.getMask();
931 auto origOffsetsTy = dyn_cast<VectorType>(op.getOffsets().getType());
932 if (isa<VectorType>(offset.
getType()) && origOffsetsTy && valOrResVecTy &&
933 origOffsetsTy.getNumElements() == valOrResVecTy.getNumElements() &&
934 isUniformMask(op.getMask())) {
935 offset = vector::ExtractOp::create(rewriter, loc, offset,
936 ArrayRef<int64_t>{0});
937 if (isa<VectorType>(mask.
getType()))
938 mask = vector::ExtractOp::create(rewriter, loc, mask,
939 ArrayRef<int64_t>{0});
942 if (dyn_cast<VectorType>(offset.
getType())) {
945 return rewriter.notifyMatchFailure(op,
"Expected offset to be a scalar.");
951 addOffsetToBaseAddr(rewriter, loc, basePtrI64, offset, elemByteSize);
955 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtrI64);
958 VectorType maskVecTy = dyn_cast<VectorType>(mask.
getType());
962 return rewriter.notifyMatchFailure(op,
"Expected mask to be a scalar.");
965 if constexpr (std::is_same_v<OpType, xegpu::LoadGatherOp>) {
966 scf::IfOp ifOp = scf::IfOp::create(rewriter, loc, {valOrResTy},
967 maskForLane,
true,
true);
969 rewriter.setInsertionPointToStart(&ifOp.getThenRegion().front());
971 valOrResTy = VectorType::get({valOrResVecTy.getNumElements()},
972 valOrResVecTy.getElementType());
974 LLVM::LoadOp::create(rewriter, loc, valOrResTy, basePtrLLVM);
977 "cache_control", xevm::LoadCacheControlAttr::get(
978 ctxt, translateLoadXeGPUCacheHint(
979 op.getL1Hint(), op.getL3Hint())));
980 scf::YieldOp::create(rewriter, loc,
ValueRange{loaded});
981 rewriter.setInsertionPointToStart(&ifOp.getElseRegion().front());
983 auto eTy = hasScalarVal ? valOrResTy : valOrResVecTy.getElementType();
986 eVal = FloatAttr::get(eTy, 0.0);
988 eVal = IntegerAttr::get(eTy, 0);
990 loaded = arith::ConstantOp::create(rewriter, loc, eVal);
992 loaded = arith::ConstantOp::create(
994 scf::YieldOp::create(rewriter, loc,
ValueRange{loaded});
995 rewriter.replaceOp(op, ifOp.getResult(0));
998 scf::IfOp ifOp = scf::IfOp::create(rewriter, loc, maskForLane,
false);
999 auto body = ifOp.getBody();
1000 rewriter.setInsertionPointToStart(body);
1002 LLVM::StoreOp::create(rewriter, loc, adaptor.getValue(), basePtrLLVM);
1004 storeOp.getOperation()->setDiscardableAttr(
1005 "cache_control", xevm::StoreCacheControlAttr::get(
1006 ctxt, translateStoreXeGPUCacheHint(
1007 op.getL1Hint(), op.getL3Hint())));
1008 rewriter.eraseOp(op);
1014class CreateMemDescOpPattern final
1015 :
public OpConversionPattern<xegpu::CreateMemDescOp> {
1017 using OpConversionPattern<xegpu::CreateMemDescOp>::OpConversionPattern;
1019 matchAndRewrite(xegpu::CreateMemDescOp op, OpAdaptor adaptor,
1020 ConversionPatternRewriter &rewriter)
const override {
1022 rewriter.replaceOp(op, adaptor.getSource());
1044 xegpu::MemDescType mdescTy) {
1047 if (rank !=
static_cast<int64_t>(numOffsets))
1051 for (
auto [dim, strideAttr] :
1052 llvm::enumerate(mdescTy.getStrideAttr().getValue())) {
1053 if (cast<IntegerAttr>(strideAttr).getInt() == 1) {
1061 bool spansFastestDim =
1062 llvm::all_of(llvm::seq<int64_t>(fastestDim + 1, rank),
1063 [&](
int64_t d) {
return payloadShape[d] == 1; });
1064 for (
int64_t d = 0; d < rank; ++d)
1065 shape[d] = spansFastestDim && d == fastestDim ? payloadShape[d] : 1;
1070template <
typename OpType,
1071 typename = std::enable_if_t<llvm::is_one_of<
1072 OpType, xegpu::LoadMatrixOp, xegpu::StoreMatrixOp>::value>>
1073class LoadStoreMatrixToXeVMPattern :
public OpConversionPattern<OpType> {
1074 using OpConversionPattern<OpType>::OpConversionPattern;
1080 LogicalResult emitContiguousAccesses(
1081 OpType op,
typename OpType::Adaptor adaptor,
1082 ConversionPatternRewriter &rewriter, xegpu::MemDescType mdescTy,
1083 ArrayRef<OpFoldResult> offsets, ArrayRef<int64_t> payloadShape,
1084 ArrayRef<int64_t> sliceShape, Value baseAddr32, Value basePtrLLVM,
1085 LLVM::LLVMPointerType ptrTypeLLVM, int64_t elemByteSize)
const {
1086 Location loc = op.getLoc();
1087 int64_t rank = payloadShape.size();
1088 SmallVector<int64_t> sliceGrid(rank);
1089 for (int64_t d = 0; d < rank; ++d)
1090 sliceGrid[d] = payloadShape[d] / sliceShape[d];
1093 SmallVector<int64_t> payloadStrides =
computeStrides(payloadShape);
1100 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>)
1102 this->getTypeConverter()->convertType(op.getResult().getType());
1104 payloadTy = adaptor.getData().getType();
1105 auto payloadVecTy = dyn_cast<VectorType>(payloadTy);
1107 Type sliceTy = payloadTy;
1108 if (numSlices != 1) {
1110 return rewriter.notifyMatchFailure(
1111 op,
"Expected a vector payload to slice.");
1112 sliceTy = sliceLen == 1 ? payloadVecTy.getElementType()
1113 : VectorType::get({sliceLen},
1114 payloadVecTy.getElementType());
1118 auto slicePtr = [&](ArrayRef<int64_t> delta) -> Value {
1119 if (llvm::all_of(delta, [](int64_t d) {
return d == 0; }))
1121 SmallVector<OpFoldResult> sliceOffsets;
1122 for (
auto [base, d] : llvm::zip_equal(offsets, delta)) {
1124 sliceOffsets.push_back(base);
1125 else if (
auto attr = dyn_cast<Attribute>(base))
1126 sliceOffsets.push_back(
1127 rewriter.getIndexAttr(cast<IntegerAttr>(attr).getInt() + d));
1129 sliceOffsets.push_back(
1130 OpFoldResult(arith::AddIOp::create(
1131 rewriter, loc, cast<Value>(base),
1135 Value sliceOffset = mdescTy.getLinearOffsets(rewriter, loc, sliceOffsets);
1136 sliceOffset = arith::IndexCastUIOp::create(
1137 rewriter, loc, rewriter.getI32Type(), sliceOffset);
1138 Value slicePtrI32 = addOffsetToBaseAddr(rewriter, loc, baseAddr32,
1139 sliceOffset, elemByteSize);
1140 return LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, slicePtrI32);
1144 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>)
1146 result = arith::ConstantOp::create(rewriter, loc, payloadVecTy,
1147 rewriter.getZeroAttr(payloadVecTy));
1151 SmallVector<int64_t> delta(rank);
1152 for (int64_t d = 0; d < rank; ++d)
1153 delta[d] = gridCoords[d] * sliceShape[d];
1154 int64_t payloadOffset =
linearize(delta, payloadStrides);
1155 Value ptr = slicePtr(delta);
1157 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>) {
1158 Value loaded = LLVM::LoadOp::create(rewriter, loc, sliceTy, ptr);
1161 else if (sliceLen == 1)
1162 result = vector::InsertOp::create(rewriter, loc, loaded,
result,
1163 ArrayRef<int64_t>{payloadOffset});
1165 result = vector::InsertStridedSliceOp::create(
1166 rewriter, loc, loaded,
result, ArrayRef<int64_t>{payloadOffset},
1167 ArrayRef<int64_t>{1});
1169 Value data = adaptor.getData();
1170 if (numSlices != 1) {
1172 data = vector::ExtractOp::create(rewriter, loc, data,
1173 ArrayRef<int64_t>{payloadOffset});
1175 data = vector::ExtractStridedSliceOp::create(
1176 rewriter, loc, data, ArrayRef<int64_t>{payloadOffset},
1177 ArrayRef<int64_t>{sliceLen}, ArrayRef<int64_t>{1});
1179 LLVM::StoreOp::create(rewriter, loc, data, ptr);
1183 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>)
1184 rewriter.replaceOp(op,
result);
1186 rewriter.eraseOp(op);
1190 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1191 ConversionPatternRewriter &rewriter)
const override {
1193 SmallVector<OpFoldResult> offsets = op.getMixedOffsets();
1194 if (offsets.empty())
1195 return rewriter.notifyMatchFailure(op,
"Expected offset to be provided.");
1197 auto loc = op.getLoc();
1198 auto ctxt = rewriter.getContext();
1199 Value baseAddr32 = adaptor.getMemDesc();
1200 Value mdescVal = op.getMemDesc();
1203 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>) {
1204 Type resType = op.getResult().getType();
1207 if (
auto vecType = dyn_cast<VectorType>(resType))
1208 resType = VectorType::get({vecType.getNumElements()},
1209 vecType.getElementType());
1212 dataTy = adaptor.getData().getType();
1213 VectorType valOrResVecTy = dyn_cast<VectorType>(dataTy);
1215 valOrResVecTy = VectorType::get(1, dataTy);
1217 int64_t elemBitWidth =
1218 valOrResVecTy.getElementType().getIntOrFloatBitWidth();
1220 if (elemBitWidth % 8 != 0)
1221 return rewriter.notifyMatchFailure(
1222 op,
"Expected element type bit width to be multiple of 8.");
1223 int64_t elemByteSize = elemBitWidth / 8;
1226 LLVM::LLVMPointerType ptrTypeLLVM = LLVM::LLVMPointerType::get(
1227 ctxt, getNumericXeVMAddrSpace(xegpu::MemorySpace::SLM));
1229 auto mdescTy = cast<xegpu::MemDescType>(mdescVal.
getType());
1231 Value linearOffset = mdescTy.getLinearOffsets(rewriter, loc, offsets);
1232 linearOffset = arith::IndexCastUIOp::create(
1233 rewriter, loc, rewriter.getI32Type(), linearOffset);
1234 Value basePtrI32 = addOffsetToBaseAddr(rewriter, loc, baseAddr32,
1235 linearOffset, elemByteSize);
1239 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtrI32);
1241 if (op.getSubgroupBlockIoAttr()) {
1245 Type intElemTy = rewriter.getIntegerType(elemBitWidth);
1246 VectorType intVecTy =
1247 VectorType::get(valOrResVecTy.getShape(), intElemTy);
1249 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>) {
1250 Value loadOp = xevm::BlockLoadOp::create(
1251 rewriter, loc, intVecTy, basePtrLLVM,
nullptr);
1252 if (intVecTy != valOrResVecTy) {
1254 vector::BitCastOp::create(rewriter, loc, valOrResVecTy, loadOp);
1256 rewriter.replaceOp(op, loadOp);
1258 Value dataToStore = adaptor.getData();
1259 if (valOrResVecTy != intVecTy) {
1261 vector::BitCastOp::create(rewriter, loc, intVecTy, dataToStore);
1263 xevm::BlockStoreOp::create(rewriter, loc, basePtrLLVM, dataToStore,
1265 rewriter.eraseOp(op);
1270 if (valOrResVecTy.getNumElements() >= 1) {
1273 (*chipOpt !=
"pvc" && *chipOpt !=
"bmg" && *chipOpt !=
"cri")) {
1275 return rewriter.notifyMatchFailure(
1276 op,
"The lowering is specific to pvc, bmg or cri.");
1282 ArrayRef<int64_t> payloadShape;
1284 if constexpr (std::is_same_v<OpType, xegpu::LoadMatrixOp>)
1285 opPayloadTy = op.getResult().getType();
1287 opPayloadTy = op.getData().getType();
1288 if (
auto opPayloadVecTy = dyn_cast<VectorType>(opPayloadTy))
1289 payloadShape = opPayloadVecTy.getShape();
1291 return emitContiguousAccesses(
1292 op, adaptor, rewriter, mdescTy, offsets, payloadShape,
1293 getContiguousPayloadShape(payloadShape, offsets.size(), mdescTy),
1294 baseAddr32, basePtrLLVM, ptrTypeLLVM, elemByteSize);
1298class PrefetchToXeVMPattern :
public OpConversionPattern<xegpu::PrefetchOp> {
1301 matchAndRewrite(xegpu::PrefetchOp op, xegpu::PrefetchOp::Adaptor adaptor,
1302 ConversionPatternRewriter &rewriter)
const override {
1303 auto loc = op.getLoc();
1304 auto ctxt = rewriter.getContext();
1305 Value basePtrI64 = adaptor.getSource();
1307 if (basePtrI64.
getType() != rewriter.getI64Type())
1308 basePtrI64 = arith::ExtUIOp::create(rewriter, loc, rewriter.getI64Type(),
1310 Value offsets = adaptor.getOffsets();
1312 VectorType offsetsVecTy = dyn_cast<VectorType>(offsets.
getType());
1315 return rewriter.notifyMatchFailure(op,
1316 "Expected offsets to be a scalar.");
1318 int64_t elemBitWidth{0};
1319 int64_t elemByteSize;
1321 if (
auto memRefTy = dyn_cast<MemRefType>(op.getSourceType())) {
1324 elemBitWidth = memRefTy.getElementType().getIntOrFloatBitWidth();
1327 elemByteSize = *op.getOffsetAlignByte();
1329 if (elemBitWidth != 0) {
1330 if (elemBitWidth % 8 != 0)
1331 return rewriter.notifyMatchFailure(
1332 op,
"Expected element type bit width to be multiple of 8.");
1333 elemByteSize = elemBitWidth / 8;
1335 basePtrI64 = addOffsetToBaseAddr(rewriter, loc, basePtrI64, offsets,
1340 LLVM::LLVMPointerType ptrTypeLLVM = LLVM::LLVMPointerType::get(
1341 ctxt, getNumericXeVMAddrSpace(xegpu::MemorySpace::Global));
1343 if (
auto memRefTy = dyn_cast<MemRefType>(op.getSource().getType())) {
1344 FailureOr<unsigned> addrSpace =
1345 getNumericMemorySpace(memRefTy.getMemorySpace());
1347 return rewriter.notifyMatchFailure(
1348 op,
"Unsupported memref memory space attribute.");
1349 if (*addrSpace != 0)
1350 ptrTypeLLVM = LLVM::LLVMPointerType::get(ctxt, *addrSpace);
1354 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtrI64);
1356 xevm::PrefetchOp::create(
1357 rewriter, loc, ptrLLVM,
1358 xevm::LoadCacheControlAttr::get(
1359 ctxt, translateLoadXeGPUCacheHint(op.getL1Hint(), op.getL3Hint())));
1360 rewriter.eraseOp(op);
1365class FenceToXeVMPattern :
public OpConversionPattern<xegpu::FenceOp> {
1368 matchAndRewrite(xegpu::FenceOp op, xegpu::FenceOp::Adaptor adaptor,
1369 ConversionPatternRewriter &rewriter)
const override {
1370 auto loc = op.getLoc();
1371 xevm::MemScope memScope{xevm::MemScope::WORKGROUP};
1372 switch (op.getFenceScope()) {
1373 case xegpu::FenceScope::Workgroup:
1374 memScope = xevm::MemScope::WORKGROUP;
1376 case xegpu::FenceScope::GPU:
1377 memScope = xevm::MemScope::DEVICE;
1380 xevm::AddrSpace addrSpace{xevm::AddrSpace::GLOBAL};
1381 switch (op.getMemoryKind()) {
1382 case xegpu::MemorySpace::Global:
1383 addrSpace = xevm::AddrSpace::GLOBAL;
1385 case xegpu::MemorySpace::SLM:
1386 addrSpace = xevm::AddrSpace::SHARED;
1389 xevm::MemfenceOp::create(rewriter, loc, memScope, addrSpace);
1390 rewriter.eraseOp(op);
1395static auto encodePrecision = [](
Type type) -> xevm::ElemType {
1397 return xevm::ElemType::BF16;
1398 else if (type.isF16())
1399 return xevm::ElemType::F16;
1400 else if (type.isTF32())
1401 return xevm::ElemType::TF32;
1402 else if (type.isInteger(8)) {
1403 if (type.isUnsignedInteger())
1404 return xevm::ElemType::U8;
1405 return xevm::ElemType::S8;
1406 }
else if (type.isF32())
1407 return xevm::ElemType::F32;
1408 else if (type.isInteger(32))
1409 return xevm::ElemType::S32;
1410 else if (type.isF8E5M2())
1411 return xevm::ElemType::BF8;
1412 else if (type.isF8E4M3FN())
1413 return xevm::ElemType::F8;
1414 else if (mlir::isa<Float4E2M1FNType>(type))
1415 return xevm::ElemType::E2M1;
1416 llvm_unreachable(
"add more support for ElemType");
1419static unsigned getNumOperandsPerDword(xevm::ElemType pTy) {
1421 case xevm::ElemType::TF32:
1423 case xevm::ElemType::BF16:
1424 case xevm::ElemType::F16:
1426 case xevm::ElemType::U8:
1427 case xevm::ElemType::S8:
1428 case xevm::ElemType::F8:
1429 case xevm::ElemType::BF8:
1431 case xevm::ElemType::E2M1:
1434 llvm_unreachable(
"unsupported xevm::ElemType");
1438class DpasToXeVMPattern :
public OpConversionPattern<xegpu::DpasOp> {
1441 matchAndRewrite(xegpu::DpasOp op, xegpu::DpasOp::Adaptor adaptor,
1442 ConversionPatternRewriter &rewriter)
const override {
1443 auto loc = op.getLoc();
1444 auto ctxt = rewriter.getContext();
1445 auto aTy = cast<VectorType>(op.getLhs().getType());
1446 auto bTy = cast<VectorType>(op.getRhs().getType());
1447 auto resultType = cast<VectorType>(op.getResultType());
1452 return rewriter.notifyMatchFailure(op,
"cannot determine target chip");
1456 return rewriter.notifyMatchFailure(op,
"unsupported target uArch");
1459 llvm::dyn_cast_or_null<xegpu::uArch::SubgroupMatrixMultiplyAcc>(
1460 uArch->getInstruction(
1461 xegpu::uArch::InstructionKind::SubgroupMatrixMultiplyAcc)));
1463 return rewriter.notifyMatchFailure(op,
1464 "DPAS not supported by target uArch");
1466 auto checkSupportedTypes = [&](VectorType vecTy,
1468 auto supported = dpasInst->getSupportedTypes(*ctxt, kind);
1469 return llvm::find(supported, vecTy.getElementType()) != supported.end();
1472 if (!checkSupportedTypes(aTy, xegpu::uArch::MMAOpndKind::MatrixA))
1473 return rewriter.notifyMatchFailure(
1474 op,
"A-matrix element type not supported by target uArch");
1475 if (!checkSupportedTypes(bTy, xegpu::uArch::MMAOpndKind::MatrixB))
1476 return rewriter.notifyMatchFailure(
1477 op,
"B-matrix element type not supported by target uArch");
1479 if (!checkSupportedTypes(resultType, xegpu::uArch::MMAOpndKind::MatrixD))
1480 return rewriter.notifyMatchFailure(
1481 op,
"result/accumulator element type not supported by target uArch");
1483 xevm::ElemType precATy = encodePrecision(aTy.getElementType());
1484 xevm::ElemType precBTy = encodePrecision(bTy.getElementType());
1485 Value c = op.getAcc();
1487 auto elementTy = resultType.getElementType();
1488 Attribute initValueAttr;
1489 if (isa<FloatType>(elementTy))
1490 initValueAttr = FloatAttr::get(elementTy, 0.0);
1492 initValueAttr = IntegerAttr::get(elementTy, 0);
1493 c = arith::ConstantOp::create(
1497 Value aVec = op.getLhs();
1498 Value bVec = op.getRhs();
1499 auto cvecty = cast<VectorType>(c.
getType());
1500 xevm::ElemType precCTy = encodePrecision(cvecty.getElementType());
1501 xevm::ElemType precDTy = encodePrecision(resultType.getElementType());
1503 VectorType::get(cvecty.getNumElements(), cvecty.getElementType());
1505 c = vector::ShapeCastOp::create(rewriter, loc, cNty, c);
1506 Value dpasRes = xevm::MMAOp::create(
1507 rewriter, loc, cNty, aVec, bVec, c,
1508 xevm::MMAShapeAttr::get(ctxt, cvecty.getNumElements(), executionSize,
1510 getNumOperandsPerDword(precATy)),
1511 xevm::MMATypesAttr::get(ctxt, precDTy, precATy, precBTy, precCTy));
1513 dpasRes = vector::ShapeCastOp::create(rewriter, loc, resultType, dpasRes);
1514 rewriter.replaceOp(op, dpasRes);
1519static std::optional<LLVM::AtomicBinOp>
1520matchSimpleAtomicOp(arith::AtomicRMWKind arithKind) {
1521 switch (arithKind) {
1522 case arith::AtomicRMWKind::addf:
1523 return LLVM::AtomicBinOp::fadd;
1524 case arith::AtomicRMWKind::addi:
1525 return LLVM::AtomicBinOp::add;
1526 case arith::AtomicRMWKind::assign:
1527 return LLVM::AtomicBinOp::xchg;
1528 case arith::AtomicRMWKind::maximumf:
1529 return LLVM::AtomicBinOp::fmax;
1530 case arith::AtomicRMWKind::maxs:
1531 return LLVM::AtomicBinOp::max;
1532 case arith::AtomicRMWKind::maxu:
1533 return LLVM::AtomicBinOp::umax;
1534 case arith::AtomicRMWKind::minimumf:
1535 return LLVM::AtomicBinOp::fmin;
1536 case arith::AtomicRMWKind::mins:
1537 return LLVM::AtomicBinOp::min;
1538 case arith::AtomicRMWKind::minu:
1539 return LLVM::AtomicBinOp::umin;
1540 case arith::AtomicRMWKind::ori:
1541 return LLVM::AtomicBinOp::_or;
1542 case arith::AtomicRMWKind::andi:
1543 return LLVM::AtomicBinOp::_and;
1545 return std::nullopt;
1549class AtomicRMWToXeVMPattern :
public OpConversionPattern<xegpu::AtomicRMWOp> {
1552 matchAndRewrite(xegpu::AtomicRMWOp op, xegpu::AtomicRMWOp::Adaptor adaptor,
1553 ConversionPatternRewriter &rewriter)
const override {
1554 auto loc = op.getLoc();
1555 auto ctxt = rewriter.getContext();
1556 auto tdesc = op.getTensorDesc().getType();
1557 auto ptrTypeLLVM = LLVM::LLVMPointerType::get(
1558 ctxt, getNumericXeVMAddrSpace(tdesc.getMemorySpace()));
1559 Value basePtrI64 = arith::IndexCastOp::create(
1560 rewriter, loc, rewriter.getI64Type(), adaptor.getTensorDesc());
1562 LLVM::IntToPtrOp::create(rewriter, loc, ptrTypeLLVM, basePtrI64);
1563 VectorType srcOrDstVecTy = cast<VectorType>(op.getValue().getType());
1564 VectorType srcOrDstFlatVecTy = VectorType::get(
1565 srcOrDstVecTy.getNumElements(), srcOrDstVecTy.getElementType());
1566 Value srcFlatVec = vector::ShapeCastOp::create(
1567 rewriter, loc, srcOrDstFlatVecTy, op.getValue());
1568 auto atomicKind = matchSimpleAtomicOp(op.getKind());
1569 assert(atomicKind.has_value());
1570 Value resVec = srcFlatVec;
1571 for (
int i = 0; i < srcOrDstVecTy.getNumElements(); i++) {
1572 auto val = vector::ExtractOp::create(rewriter, loc, resVec, i);
1573 Value idx = LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(),
1574 rewriter.getI64IntegerAttr(i));
1576 LLVM::GEPOp::create(rewriter, loc, ptrTypeLLVM,
1577 srcOrDstVecTy.getElementType(), basePtrLLVM, idx);
1579 LLVM::AtomicRMWOp::create(rewriter, loc, atomicKind.value(), currPtr,
1580 val, LLVM::AtomicOrdering::seq_cst);
1581 resVec = vector::InsertOp::create(rewriter, loc, newVal, resVec, i);
1583 rewriter.replaceOp(op, resVec);
1588class DpasMxToXeVMPattern :
public OpConversionPattern<xegpu::DpasMxOp> {
1591 matchAndRewrite(xegpu::DpasMxOp op, xegpu::DpasMxOp::Adaptor adaptor,
1592 ConversionPatternRewriter &rewriter)
const override {
1593 auto loc = op.getLoc();
1594 auto ctxt = rewriter.getContext();
1595 auto aTy = op.getA().getType();
1596 auto bTy = op.getB().getType();
1598 cast<VectorType>(getTypeConverter()->convertType(op.getType()));
1602 return rewriter.notifyMatchFailure(op,
"cannot determine target chip");
1606 return rewriter.notifyMatchFailure(op,
"unsupported target uArch");
1610 xevm::ElemType precATy = encodePrecision(aTy.getElementType());
1611 xevm::ElemType precBTy = encodePrecision(bTy.getElementType());
1612 Value c = adaptor.getAcc();
1614 auto elementTy = resVecTy.getElementType();
1615 Attribute initValueAttr;
1616 if (isa<FloatType>(elementTy))
1617 initValueAttr = FloatAttr::get(elementTy, 0.0);
1619 initValueAttr = IntegerAttr::get(elementTy, 0);
1620 c = arith::ConstantOp::create(
1624 Value aVec = adaptor.getA();
1625 Value bVec = adaptor.getB();
1626 auto aVecTy = cast<VectorType>(aVec.
getType());
1627 auto bVecTy = cast<VectorType>(bVec.
getType());
1628 if (aVecTy.getElementTypeBitWidth() == 4)
1629 aVec = vector::BitCastOp::create(
1631 VectorType::get(aVecTy.getNumElements() / 2, rewriter.getI8Type()),
1633 if (bVecTy.getElementTypeBitWidth() == 4)
1634 bVec = vector::BitCastOp::create(
1636 VectorType::get(bVecTy.getNumElements() / 2, rewriter.getI8Type()),
1638 auto cVecTy = cast<VectorType>(c.
getType());
1639 xevm::ElemType precCTy = encodePrecision(cVecTy.getElementType());
1640 xevm::ElemType precDTy = encodePrecision(resVecTy.getElementType());
1641 Value scaleA = adaptor.getScaleA();
1642 Value scaleB = adaptor.getScaleB();
1643 Value dpasMxRes = xevm::MMAMxOp::create(
1644 rewriter, loc, resVecTy, aVec, bVec, scaleA, scaleB, c,
1645 xevm::MMAShapeAttr::get(ctxt, cVecTy.getNumElements(), executionSize,
1647 getNumOperandsPerDword(precATy)),
1648 xevm::MMATypesAttr::get(ctxt, precDTy, precATy, precBTy, precCTy));
1649 rewriter.replaceOp(op, dpasMxRes);
1668static constexpr int64_t kXeVMExtfTruncfNumElems = 16;
1671static std::optional<xevm::ExtfSrcElemTypes> getExtfNarrowType(
Type etype) {
1672 if (isa<Float8E5M2Type>(etype))
1673 return xevm::ExtfSrcElemTypes::BF8;
1674 if (isa<Float8E4M3FNType>(etype))
1675 return xevm::ExtfSrcElemTypes::F8;
1676 if (isa<Float4E2M1FNType>(etype))
1677 return xevm::ExtfSrcElemTypes::E2M1;
1678 return std::nullopt;
1682static std::optional<xevm::TruncfDstElemTypes> getTruncfNarrowType(
Type etype) {
1683 if (isa<Float8E5M2Type>(etype))
1684 return xevm::TruncfDstElemTypes::BF8;
1685 if (isa<Float8E4M3FNType>(etype))
1686 return xevm::TruncfDstElemTypes::F8;
1687 if (isa<Float4E2M1FNType>(etype))
1688 return xevm::TruncfDstElemTypes::E2M1;
1689 return std::nullopt;
1694static bool isXeVMExtf(arith::ExtFOp op) {
1695 auto srcTy = dyn_cast<VectorType>(op.getIn().getType());
1696 auto dstTy = dyn_cast<VectorType>(op.getType());
1697 if (!srcTy || !dstTy || srcTy.getRank() != 1 || dstTy.getRank() != 1)
1699 if (dstTy.getNumElements() != kXeVMExtfTruncfNumElems)
1701 Type dstETy = dstTy.getElementType();
1704 return getExtfNarrowType(srcTy.getElementType()).has_value();
1711static bool isXeVMTruncf(arith::TruncFOp op) {
1712 auto srcTy = dyn_cast<VectorType>(op.getIn().getType());
1713 auto dstTy = dyn_cast<VectorType>(op.getType());
1714 if (!srcTy || !dstTy || srcTy.getRank() != 1 || dstTy.getRank() != 1)
1716 int64_t numElems = srcTy.getNumElements();
1717 if (numElems == 0 || numElems % kXeVMExtfTruncfNumElems != 0)
1719 Type srcETy = srcTy.getElementType();
1722 return getTruncfNarrowType(dstTy.getElementType()).has_value();
1725class ExtfToXeVMPattern :
public OpConversionPattern<arith::ExtFOp> {
1728 matchAndRewrite(arith::ExtFOp op, OpAdaptor adaptor,
1729 ConversionPatternRewriter &rewriter)
const override {
1730 if (!isXeVMExtf(op))
1731 return rewriter.notifyMatchFailure(op,
"not a xevm.extf compatible extf");
1732 Location loc = op.getLoc();
1734 auto srcVecTy = cast<VectorType>(op.getIn().getType());
1735 auto dstVecTy = cast<VectorType>(op.getType());
1736 xevm::ExtfSrcElemTypes srcEnum =
1737 *getExtfNarrowType(srcVecTy.getElementType());
1738 xevm::ExtfDstElemTypes dstEnum = dstVecTy.getElementType().isF16()
1739 ? xevm::ExtfDstElemTypes::F16
1740 : xevm::ExtfDstElemTypes::BF16;
1744 Value src = adaptor.getIn();
1745 auto convSrcTy = cast<VectorType>(src.
getType());
1746 if (convSrcTy.getElementTypeBitWidth() == 4)
1747 src = vector::BitCastOp::create(
1749 VectorType::get(convSrcTy.getNumElements() / 2, rewriter.getI8Type()),
1751 Type resTy = getTypeConverter()->convertType(dstVecTy);
1752 Value res = xevm::ExtfOp::create(
1753 rewriter, loc, resTy, src, xevm::ExtfSrcElemTypeAttr::get(ctx, srcEnum),
1754 xevm::ExtfDstElemTypeAttr::get(ctx, dstEnum));
1755 rewriter.replaceOp(op, res);
1760class TruncfToXeVMPattern :
public OpConversionPattern<arith::TruncFOp> {
1763 matchAndRewrite(arith::TruncFOp op, OpAdaptor adaptor,
1764 ConversionPatternRewriter &rewriter)
const override {
1765 if (!isXeVMTruncf(op))
1766 return rewriter.notifyMatchFailure(op,
1767 "not a xevm.truncf compatible truncf");
1768 Location loc = op.getLoc();
1769 MLIRContext *ctx = op.getContext();
1770 auto srcVecTy = cast<VectorType>(op.getIn().getType());
1771 auto dstVecTy = cast<VectorType>(op.getType());
1772 xevm::TruncfSrcElemTypes srcEnum = srcVecTy.getElementType().isF16()
1773 ? xevm::TruncfSrcElemTypes::F16
1774 : xevm::TruncfSrcElemTypes::BF16;
1775 xevm::TruncfDstElemTypes dstEnum =
1776 *getTruncfNarrowType(dstVecTy.getElementType());
1777 auto srcEnumAttr = xevm::TruncfSrcElemTypeAttr::get(ctx, srcEnum);
1778 auto dstEnumAttr = xevm::TruncfDstElemTypeAttr::get(ctx, dstEnum);
1784 int64_t numGroups = srcVecTy.getNumElements() / kXeVMExtfTruncfNumElems;
1785 int64_t groupBytes =
1786 kXeVMExtfTruncfNumElems * dstVecTy.getElementTypeBitWidth() / 8;
1787 Type groupTy = VectorType::get(groupBytes, rewriter.getI8Type());
1789 Value src = adaptor.getIn();
1791 if (numGroups == 1) {
1792 packed = xevm::TruncfOp::create(rewriter, loc, groupTy, src, srcEnumAttr,
1796 VectorType::get(groupBytes * numGroups, rewriter.getI8Type());
1797 packed = arith::ConstantOp::create(rewriter, loc, packedTy,
1798 rewriter.getZeroAttr(packedTy));
1799 for (int64_t group = 0; group < numGroups; group++) {
1800 Value
slice = vector::ExtractStridedSliceOp::create(
1801 rewriter, loc, src, group * kXeVMExtfTruncfNumElems,
1802 kXeVMExtfTruncfNumElems, 1);
1803 Value converted = xevm::TruncfOp::create(rewriter, loc, groupTy,
slice,
1804 srcEnumAttr, dstEnumAttr);
1805 packed = vector::InsertStridedSliceOp::create(
1806 rewriter, loc, converted, packed, group * groupBytes,
1811 Type resTy = getTypeConverter()->convertType(dstVecTy);
1812 if (packed.
getType() != resTy)
1813 packed = vector::BitCastOp::create(rewriter, loc, resTy, packed);
1814 rewriter.replaceOp(op, packed);
1824template <
typename ExtOp>
1825class HoistI4ExtensionAboveLaneShuffle final :
public OpRewritePattern<ExtOp> {
1827 using OpRewritePattern<ExtOp>::OpRewritePattern;
1829 LogicalResult matchAndRewrite(ExtOp op,
1830 PatternRewriter &rewriter)
const override {
1831 auto srcTy = dyn_cast<VectorType>(op.getIn().getType());
1832 auto dstTy = dyn_cast<VectorType>(op.getType());
1833 if (!srcTy || !srcTy.getElementType().isInteger(4) || !dstTy ||
1834 dstTy.getElementTypeBitWidth() < 8)
1837 auto isMover = [](Operation *def) {
1838 return isa<vector::ShapeCastOp, vector::ExtractStridedSliceOp,
1839 vector::ExtractOp, vector::BroadcastOp, vector::TransposeOp>(
1842 Value source = op.getIn();
1844 if (isa<xegpu::LaneShuffleOp>(def))
1848 source = def->getOperand(0);
1850 auto shuffle = source.
getDefiningOp<xegpu::LaneShuffleOp>();
1853 VectorType fragmentTy = shuffle.getSourceType();
1854 int64_t numElements = fragmentTy.getNumElements();
1855 if (!fragmentTy.getElementType().isInteger(4) || numElements < 2 ||
1856 numElements > 8 || !llvm::isPowerOf2_64(numElements))
1861 SmallVector<Operation *> movers;
1862 SmallVector<ExtOp> extensions;
1863 SmallPtrSet<Operation *, 16> visited;
1864 auto visit = [&](
auto &&
visit, Value value) -> LogicalResult {
1865 for (Operation *user : value.getUsers()) {
1866 if (!visited.insert(user).second)
1868 if (
auto ext = dyn_cast<ExtOp>(user)) {
1869 auto type = dyn_cast<VectorType>(ext.getType());
1870 if (!type || type.getElementTypeBitWidth() < 8)
1872 extensions.push_back(ext);
1875 if (!isMover(user) || user->getOperand(0) != value ||
1876 user->getNumResults() != 1 ||
1879 movers.push_back(user);
1887 VectorType wideTy = fragmentTy.clone(rewriter.
getI8Type());
1889 ExtOp::create(rewriter, op.getLoc(), wideTy, shuffle.getSource());
1890 Value
result = xegpu::LaneShuffleOp::create(
1891 rewriter, shuffle.getLoc(), wideTy, wide, shuffle.getMode());
1893 mapping.
map(shuffle.getResult(),
result);
1894 for (Operation *mover : llvm::reverse(movers)) {
1896 Operation *
clone = rewriter.
clone(*mover, mapping);
1898 if (
auto vectorTy = dyn_cast<VectorType>(mover->getResult(0).getType()))
1899 type = vectorTy.clone(type);
1902 for (ExtOp ext : extensions) {
1903 Value input = mapping.
lookup(ext.getIn());
1904 if (input.
getType() == ext.getType())
1908 [&] { ext.getInMutable().assign(input); });
1910 for (Operation *mover : movers)
1936class LaneShuffleToXeVMPattern
1937 :
public OpConversionPattern<xegpu::LaneShuffleOp> {
1940 matchAndRewrite(xegpu::LaneShuffleOp op, OpAdaptor adaptor,
1941 ConversionPatternRewriter &rewriter)
const override {
1942 auto vecTy = dyn_cast<VectorType>(adaptor.getSource().getType());
1944 return rewriter.notifyMatchFailure(op,
"Expected a vector fragment.");
1948 unsigned elemBits = vecTy.getElementTypeBitWidth();
1949 if (elemBits != 8 && elemBits != 16 && elemBits != 32 && elemBits != 64)
1950 return rewriter.notifyMatchFailure(
1951 op,
"Expected an element type of 8, 16, 32 or 64 bits.");
1952 int64_t fragmentBits = vecTy.getNumElements() * elemBits;
1953 if (fragmentBits > 64 || !llvm::isPowerOf2_64(fragmentBits))
1954 return rewriter.notifyMatchFailure(
1955 op,
"Expected a fragment of 8, 16, 32 or 64 bits.");
1957 Location loc = op.getLoc();
1958 Type packedTy = rewriter.getIntegerType(fragmentBits);
1961 VectorType shuffleTy =
1962 VectorType::get(vecTy.getShape(), rewriter.getIntegerType(elemBits));
1965 if (op.getMode() == xegpu::LaneShuffleMode::Pack) {
1966 Value src = adaptor.getSource();
1967 if (shuffleTy != vecTy)
1968 src = LLVM::BitcastOp::create(rewriter, loc, shuffleTy, src);
1969 res = xevm::BitcastShuffleOp::create(rewriter, loc, packedTy, src);
1970 res = LLVM::BitcastOp::create(rewriter, loc, vecTy, res);
1973 LLVM::BitcastOp::create(rewriter, loc, packedTy, adaptor.getSource());
1974 res = xevm::BitcastShuffleOp::create(rewriter, loc, shuffleTy, packed);
1975 if (shuffleTy != vecTy)
1976 res = LLVM::BitcastOp::create(rewriter, loc, vecTy, res);
1978 rewriter.replaceOp(op, res);
1987struct ConvertXeGPUToXeVMPass
1991 void runOnOperation()
override {
1996 GreedyRewriteConfig config;
1997 config.
setStrictness(GreedyRewriteStrictness::ExistingAndNewOps);
2001 RewritePatternSet patterns(context);
2002 patterns.add<HoistI4ExtensionAboveLaneShuffle<arith::ExtUIOp>,
2003 HoistI4ExtensionAboveLaneShuffle<arith::ExtSIOp>>(context);
2004 SmallVector<Operation *> extensions;
2005 getOperation()->walk([&](Operation *op) {
2006 if (isa<arith::ExtUIOp, arith::ExtSIOp>(op))
2007 extensions.push_back(op);
2011 return signalPassFailure();
2016 RewritePatternSet patterns(context);
2017 vector::populateVectorNarrowTypeRewritePatterns(patterns);
2020 SmallVector<Operation *> casts;
2021 getOperation()->walk([&](Operation *op) {
2022 if (isa<arith::ExtUIOp, arith::ExtSIOp, arith::TruncIOp,
2023 vector::BitCastOp>(op))
2024 casts.push_back(op);
2027 return signalPassFailure();
2037 LowerToLLVMOptions
options(context);
2038 options.overrideIndexBitwidth(this->use64bitIndex ? 64 : 32);
2039 LLVMTypeConverter typeConverter(context,
options);
2041 Type xevmIndexType = typeConverter.convertType(IndexType::get(context));
2042 Type i32Type = IntegerType::get(context, 32);
2043 typeConverter.addConversion([&](VectorType type) -> Type {
2044 auto elemType = typeConverter.convertType(type.getElementType());
2046 unsigned rank = type.getRank();
2047 if (rank == 0 || type.getNumElements() == 1)
2050 int64_t sum = llvm::product_of(type.getShape());
2051 return VectorType::get(sum, elemType);
2053 typeConverter.addConversion([&](xegpu::TensorDescType type) -> Type {
2054 if (type.getRank() == 1)
2055 return xevmIndexType;
2056 return VectorType::get(8, i32Type);
2065 typeConverter.addConversion(
2066 [&](xegpu::MemDescType type) -> Type {
return i32Type; });
2068 typeConverter.addConversion([&](MemRefType type) -> Type {
2069 return isSharedMemRef(type) ? i32Type : xevmIndexType;
2079 auto memrefToIntMaterializationCast = [](OpBuilder &builder, Type type,
2081 Location loc) -> Value {
2082 if (inputs.size() != 1)
2084 auto input = inputs.front();
2085 if (
auto memrefTy = dyn_cast<MemRefType>(input.
getType())) {
2086 unsigned rank = memrefTy.getRank();
2090 SmallVector<int64_t> intStrides;
2093 if (succeeded(memrefTy.getStridesAndOffset(intStrides, intOffsets)) &&
2094 ShapedType::isStatic(intOffsets)) {
2095 addr = memref::ExtractAlignedPointerAsIndexOp::create(builder, loc,
2097 offset = arith::ConstantOp::create(builder, loc,
2103 SmallVector<Type> resultTypes{
2104 MemRefType::get({}, memrefTy.getElementType(),
2105 MemRefLayoutAttrInterface(),
2106 memrefTy.getMemorySpace()),
2109 resultTypes.append(2 * rank, indexType);
2111 auto meta = memref::ExtractStridedMetadataOp::create(
2112 builder, loc, resultTypes, input);
2114 addr = memref::ExtractAlignedPointerAsIndexOp::create(
2115 builder, loc, meta.getBaseBuffer());
2116 offset = meta.getOffset();
2120 arith::IndexCastUIOp::create(builder, loc, type, addr);
2122 arith::IndexCastUIOp::create(builder, loc, type, offset);
2126 offsetCasted, memrefTy);
2135 auto ui64ToI64MaterializationCast = [](OpBuilder &builder, Type type,
2137 Location loc) -> Value {
2138 if (inputs.size() != 1)
2140 auto input = inputs.front();
2143 index::CastUOp::create(builder, loc, builder.
getIndexType(), input)
2145 return arith::IndexCastUIOp::create(builder, loc, type, cast)
2155 auto ui32ToI32MaterializationCast = [](OpBuilder &builder, Type type,
2157 Location loc) -> Value {
2158 if (inputs.size() != 1)
2160 auto input = inputs.front();
2163 index::CastUOp::create(builder, loc, builder.
getIndexType(), input)
2165 return arith::IndexCastUIOp::create(builder, loc, type, cast)
2175 auto vectorToVectorMaterializationCast = [](OpBuilder &builder, Type type,
2177 Location loc) -> Value {
2178 if (inputs.size() != 1)
2180 auto input = inputs.front();
2181 if (
auto vecTy = dyn_cast<VectorType>(input.
getType())) {
2182 if (
auto targetVecTy = dyn_cast<VectorType>(type)) {
2186 if (targetVecTy.getShape() != vecTy.getShape()) {
2187 cast = vector::ShapeCastOp::create(
2189 VectorType::get(targetVecTy.getShape(),
2190 vecTy.getElementType()),
2194 if (targetVecTy.getElementType() != vecTy.getElementType()) {
2195 cast = vector::BitCastOp::create(builder, loc, targetVecTy, cast)
2207 auto vectorToSingleElementMaterializationCast =
2208 [](OpBuilder &builder, Type type,
ValueRange inputs,
2209 Location loc) -> Value {
2210 if (inputs.size() != 1)
2212 auto input = inputs.front();
2213 if (
auto vecTy = dyn_cast<VectorType>(input.
getType())) {
2215 auto rank = vecTy.getRank();
2216 if (rank != 0 && vecTy.getNumElements() != 1)
2218 auto inElemTy = vecTy.getElementType();
2222 cast = vector::ExtractOp::create(builder, loc, cast, {}).getResult();
2224 cast = vector::ExtractOp::create(builder, loc, cast,
2225 SmallVector<int64_t>(rank, 0))
2232 if (inElemTy.isIndex()) {
2233 cast = arith::IndexCastUIOp::create(builder, loc, type, cast)
2235 }
else if (inElemTy != type) {
2236 cast = arith::BitcastOp::create(builder, loc, type, cast).getResult();
2250 auto singleElementToVectorMaterializationCast =
2251 [](OpBuilder &builder, Type type,
ValueRange inputs,
2252 Location loc) -> Value {
2253 if (inputs.size() != 1)
2255 auto input = inputs.front();
2257 if (!inTy.isIntOrFloat())
2261 if (
auto vecTy = dyn_cast<VectorType>(type)) {
2262 if (vecTy.getRank() != 0 && vecTy.getNumElements() != 1)
2264 auto outElemTy = vecTy.getElementType();
2266 if (outElemTy.isIndex()) {
2267 cast = arith::IndexCastUIOp::create(builder, loc,
2270 }
else if (inTy != outElemTy) {
2271 cast = arith::BitcastOp::create(builder, loc, outElemTy, cast)
2274 return vector::BroadcastOp::create(builder, loc, vecTy, cast)
2279 typeConverter.addSourceMaterialization(
2280 singleElementToVectorMaterializationCast);
2281 typeConverter.addSourceMaterialization(vectorToVectorMaterializationCast);
2282 typeConverter.addTargetMaterialization(memrefToIntMaterializationCast);
2283 typeConverter.addTargetMaterialization(ui32ToI32MaterializationCast);
2284 typeConverter.addTargetMaterialization(ui64ToI64MaterializationCast);
2285 typeConverter.addTargetMaterialization(
2286 vectorToSingleElementMaterializationCast);
2287 typeConverter.addTargetMaterialization(vectorToVectorMaterializationCast);
2288 ConversionTarget
target(*context);
2289 target.addLegalDialect<xevm::XeVMDialect, LLVM::LLVMDialect,
2290 vector::VectorDialect, arith::ArithDialect,
2291 memref::MemRefDialect, gpu::GPUDialect,
2292 index::IndexDialect>();
2293 target.addIllegalDialect<xegpu::XeGPUDialect>();
2296 target.addDynamicallyLegalOp<arith::ExtFOp>(
2297 [](arith::ExtFOp op) {
return !isXeVMExtf(op); });
2298 target.addDynamicallyLegalOp<arith::TruncFOp>(
2299 [](arith::TruncFOp op) {
return !isXeVMTruncf(op); });
2301 RewritePatternSet patterns(context);
2305 if (
failed(applyPartialConversion(getOperation(),
target,
2306 std::move(patterns))))
2307 signalPassFailure();
2317 patterns.
add<CreateNdDescToXeVMPattern,
2318 LoadStorePrefetchNdToXeVMPattern<xegpu::LoadNdOp>,
2319 LoadStorePrefetchNdToXeVMPattern<xegpu::StoreNdOp>,
2320 LoadStorePrefetchNdToXeVMPattern<xegpu::PrefetchNdOp>>(
2322 patterns.
add<AtomicRMWToXeVMPattern, PrefetchToXeVMPattern,
2323 LoadStoreToXeVMPattern<xegpu::LoadGatherOp>,
2324 LoadStoreToXeVMPattern<xegpu::StoreScatterOp>>(
2326 patterns.
add<LoadStoreMatrixToXeVMPattern<xegpu::LoadMatrixOp>,
2327 LoadStoreMatrixToXeVMPattern<xegpu::StoreMatrixOp>,
2328 CreateMemDescOpPattern>(typeConverter, patterns.
getContext());
2329 patterns.
add<FenceToXeVMPattern, DpasToXeVMPattern>(typeConverter,
2331 patterns.
add<DpasMxToXeVMPattern>(typeConverter, patterns.
getContext());
2332 patterns.
add<ExtfToXeVMPattern, TruncfToXeVMPattern>(typeConverter,
2334 patterns.
add<LaneShuffleToXeVMPattern>(typeConverter, patterns.
getContext());
static void visit(Operation *op, DenseSet< Operation * > &visited)
Visits all the pdl.operand(s), pdl.result(s), and pdl.operation(s) connected to the given operation.
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)
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.
GreedyRewriteConfig & setStrictness(GreedyRewriteStrictness mode)
auto lookup(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
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...
MLIRContext * getContext() const
Return the context this location is uniqued in.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
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...
void setType(Type newType)
Mutate the type of this Value to be of the specified type.
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 ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
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...
void populateVectorInterleaveToShufflePatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
void populateVectorDeinterleaveToShufflePatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
@ SubgroupMatrixMultiplyAcc
const uArch * getUArch(llvm::StringRef archName)
bool hasStaticShapeAndStrides(MemRefType type)
Returns true if type has a static shape and static strides.
Value addElemOffsetInBytes(OpBuilder &builder, Location loc, Value baseAddr, Value elemOffset, MemRefType memrefTy)
Returns baseAddr + elemOffset * bitWidth / 8, i.e.
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.
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...
Value getValueOrCreateConstantIntOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
LogicalResult applyOpPatternsGreedily(ArrayRef< Operation * > ops, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr, bool *allErased=nullptr)
Rewrite the specified ops by repeatedly applying the highest benefit patterns in a greedy worklist dr...
int64_t computeProduct(ArrayRef< int64_t > basis)
Self-explicit.
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)
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
int64_t linearize(ArrayRef< int64_t > offsets, ArrayRef< int64_t > basis)
Return the linearized index of 'offsets' w.r.t.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
int32_t minBaseWidthBytes
int32_t basePitchAlignmentBytes
int32_t baseWidthAlignmentBytes
int32_t minBasePitchBytes