18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/Support/FormatVariadic.h"
21#include "llvm/Support/MathExtras.h"
29#include "llvm/ADT/TypeSwitch.h"
32#define GEN_PASS_DEF_CONVERTXEVMTOLLVMPASS
33#include "mlir/Conversion/Passes.h.inc"
41struct LLVMFuncAttributeOptions {
42 bool isConvergent =
false;
43 bool isNoUnwind =
false;
44 bool isWillReturn =
false;
45 LLVM::MemoryEffectsAttr memEffectsAttr{};
47static constexpr LLVMFuncAttributeOptions noUnwindAttrs = {
48 false,
true,
false, {}};
49static constexpr LLVMFuncAttributeOptions noUnwindWillReturnAttrs = {
50 false,
true,
true, {}};
51static constexpr LLVMFuncAttributeOptions convergentNoUnwindWillReturnAttrs = {
52 true,
true,
true, {}};
54std::string getTypeMangling(
Type ty,
bool isUnsigned =
false) {
56 .Case([isUnsigned](VectorType ty) -> std::string {
57 return "Dv" + std::to_string(ty.getNumElements()) +
"_" +
58 getTypeMangling(ty.getElementType(), isUnsigned);
60 .Case([](Float16Type) -> std::string {
return "Dh"; })
61 .Case([](Float32Type) -> std::string {
return "f"; })
62 .Case([](Float64Type) -> std::string {
return "d"; })
63 .Case([isUnsigned](IntegerType ty) -> std::string {
64 switch (ty.getWidth()) {
66 return isUnsigned ?
"h" :
"c";
68 return isUnsigned ?
"t" :
"s";
70 return isUnsigned ?
"j" :
"i";
72 return isUnsigned ?
"m" :
"l";
74 llvm_unreachable(
"unhandled integer type");
77 .DefaultUnreachable(
"unhandled type for mangling");
82 assert((isUnsigned.empty() || isUnsigned.size() == types.size()) &&
83 "Signedness info doesn't match");
85 llvm::raw_string_ostream os(s);
86 llvm::SmallDenseMap<Type, unsigned> substitutions;
87 os <<
"_Z" << baseName.size() << baseName;
88 for (
auto [idx, type] : llvm::enumerate(types)) {
89 auto it = substitutions.find(type);
90 if (it != substitutions.end()) {
93 if (
unsigned firstIdx = it->getSecond(); firstIdx > 0)
97 if (!type.isIntOrFloat())
98 substitutions[type] = substitutions.size();
99 os << getTypeMangling(type, isUnsigned.empty() ?
false : isUnsigned[idx]);
109std::string getGenISATypeMangling(
Type ty) {
111 .Case([](VectorType ty) -> std::string {
112 return "v" + std::to_string(ty.getNumElements()) +
113 getGenISATypeMangling(ty.getElementType());
115 .Case([](IntegerType ty) -> std::string {
116 return "i" + std::to_string(ty.getWidth());
118 .DefaultUnreachable(
"unhandled type for GenISA mangling");
121std::string builtinElemType(ElemType elemType) {
134 return stringifyElemType(elemType).str();
138static int32_t getL1CacheControl(LoadCacheControl cc) {
141 case LoadCacheControl::USE_DEFAULT:
144 case LoadCacheControl::L1C_L2UC_L3UC:
145 case LoadCacheControl::L1C_L2UC_L3C:
146 case LoadCacheControl::L1C_L2C_L3UC:
147 case LoadCacheControl::L1C_L2C_L3C:
150 case LoadCacheControl::L1S_L2UC_L3UC:
151 case LoadCacheControl::L1S_L2UC_L3C:
152 case LoadCacheControl::L1S_L2C_L3UC:
153 case LoadCacheControl::L1S_L2C_L3C:
156 case LoadCacheControl::INVALIDATE_READ:
165static int32_t getL1CacheControl(StoreCacheControl cc) {
168 case StoreCacheControl::USE_DEFAULT:
171 case StoreCacheControl::L1WT_L2UC_L3UC:
172 case StoreCacheControl::L1WT_L2UC_L3WB:
173 case StoreCacheControl::L1WT_L2WB_L3UC:
174 case StoreCacheControl::L1WT_L2WB_L3WB:
177 case StoreCacheControl::L1WB_L2UC_L3UC:
178 case StoreCacheControl::L1WB_L2WB_L3UC:
179 case StoreCacheControl::L1WB_L2UC_L3WB:
182 case StoreCacheControl::L1S_L2UC_L3UC:
183 case StoreCacheControl::L1S_L2UC_L3WB:
184 case StoreCacheControl::L1S_L2WB_L3UC:
185 case StoreCacheControl::L1S_L2WB_L3WB:
194static int32_t getL3CacheControl(LoadCacheControl cc) {
197 case LoadCacheControl::USE_DEFAULT:
200 case LoadCacheControl::L1UC_L2UC_L3C:
201 case LoadCacheControl::L1UC_L2C_L3C:
202 case LoadCacheControl::L1C_L2UC_L3C:
203 case LoadCacheControl::L1C_L2C_L3C:
204 case LoadCacheControl::L1S_L2UC_L3C:
205 case LoadCacheControl::L1S_L2C_L3C:
208 case LoadCacheControl::INVALIDATE_READ:
217static int32_t getL3CacheControl(StoreCacheControl cc) {
220 case StoreCacheControl::USE_DEFAULT:
223 case StoreCacheControl::L1UC_L2UC_L3WB:
224 case StoreCacheControl::L1UC_L2WB_L3WB:
225 case StoreCacheControl::L1WT_L2UC_L3WB:
226 case StoreCacheControl::L1WT_L2WB_L3WB:
227 case StoreCacheControl::L1S_L2UC_L3WB:
228 case StoreCacheControl::L1S_L2WB_L3WB:
229 case StoreCacheControl::L1WB_L2UC_L3WB:
238static std::optional<LoadCacheControl> getCacheControl(PrefetchOp op) {
239 return op.getCacheControl();
242static std::optional<LoadCacheControl> getCacheControl(BlockLoad2dOp op) {
243 return op.getCacheControl();
246static std::optional<LoadCacheControl> getCacheControl(BlockLoadOp op) {
247 return op.getCacheControl();
250static std::optional<LoadCacheControl> getCacheControl(BlockPrefetch2dOp op) {
251 return op.getCacheControl();
254static std::optional<StoreCacheControl> getCacheControl(BlockStore2dOp op) {
255 return op.getCacheControl();
258static std::optional<StoreCacheControl> getCacheControl(BlockStoreOp op) {
259 return op.getCacheControl();
262static std::optional<LoadCacheControl> getCacheControl(LLVM::LoadOp op) {
263 if (op->hasDiscardableAttr(
"cache_control")) {
264 auto attr = op->getDiscardableAttrOfType<xevm::LoadCacheControlAttr>(
268 return std::optional<LoadCacheControl>(attr.getValue());
273static std::optional<StoreCacheControl> getCacheControl(LLVM::StoreOp op) {
274 if (op->hasDiscardableAttr(
"cache_control")) {
275 auto attr = op->getDiscardableAttrOfType<xevm::StoreCacheControlAttr>(
279 return std::optional<StoreCacheControl>(attr.getValue());
284template <
typename OpType>
285int32_t getL1CacheControl(OpType op) {
286 return getL1CacheControl(*getCacheControl(op));
289template <
typename OpType>
290int32_t getL3CacheControl(OpType op) {
291 return getL3CacheControl(*getCacheControl(op));
294template <
typename OpType>
295static std::optional<ArrayAttr>
296getCacheControlMetadata(ConversionPatternRewriter &rewriter, OpType op) {
297 if (!getCacheControl(op))
300 constexpr int32_t decorationCacheControlArity{3};
301 constexpr int32_t loadCacheControlKey{6442};
302 constexpr int32_t storeCacheControlKey{6443};
303 constexpr bool isLoad = std::is_same_v<OpType, BlockLoad2dOp> ||
304 std::is_same_v<OpType, BlockPrefetch2dOp> ||
305 std::is_same_v<OpType, LLVM::LoadOp> ||
306 std::is_same_v<OpType, BlockLoadOp> ||
307 std::is_same_v<OpType, PrefetchOp>;
313 assert(((getL1CacheControl<OpType>(op) == -1) ==
314 (getL3CacheControl<OpType>(op) == -1)) &&
315 "If one of L1 or L3 cache control is USE_DEFAULT, both must be "
318 if (getL1CacheControl<OpType>(op) == -1 &&
319 getL3CacheControl<OpType>(op) == -1)
321 const int32_t controlKey{isLoad ? loadCacheControlKey : storeCacheControlKey};
323 controlKey, 0, getL1CacheControl<OpType>(op)};
325 controlKey, 1, getL3CacheControl<OpType>(op)};
326 auto arrayAttrL1 = rewriter.getI32ArrayAttr(decorationsL1);
327 auto arrayAttrL3 = rewriter.getI32ArrayAttr(decorationsL3);
330 return rewriter.getArrayAttr(combinedAttrs);
352 llvm::StringMap<bool> seen;
354 for (
auto arr : llvm::make_isa_range<ArrayAttr>(attrs)) {
355 auto vals = arr.getValue();
356 assert(vals.size() == 3 &&
357 "Expected exactly 3 integer values (Token, CacheLevel, "
358 "ControlValue) in cache control attribute.");
360 auto tokenAttr = dyn_cast<IntegerAttr>(vals[0]);
361 auto secondAttr = dyn_cast<IntegerAttr>(vals[1]);
362 auto thirdAttr = dyn_cast<IntegerAttr>(vals[2]);
364 if (!tokenAttr || !secondAttr || !thirdAttr)
370 llvm::formatv(
"{{{0}:\"{1},{2}\"}", tokenAttr.getValue().getZExtValue(),
371 secondAttr.getValue().getZExtValue(),
372 thirdAttr.getValue().getZExtValue());
375 if (!seen.insert({entry, true}).second)
378 payloads.push_back(std::move(entry));
383static std::atomic<uint64_t> globalNameCounter{0};
388static Value createMetadataStringPtr(ConversionPatternRewriter &rewriter,
390 StringRef value, StringRef nameHint) {
392 std::string strWithNull = value.str();
393 strWithNull.push_back(
'\0');
394 StringRef strRef(strWithNull.data(), strWithNull.size());
396 auto as1PtrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), 1);
400 if (
auto existingGlobal = dyn_cast<LLVM::GlobalOp>(&op)) {
401 if (!existingGlobal.getSection() ||
402 *existingGlobal.getSection() !=
"llvm.metadata")
405 dyn_cast_or_null<StringAttr>(existingGlobal.getValueOrNull())) {
406 if (strAttr.getValue() == strRef) {
407 return LLVM::AddressOfOp::create(rewriter, loc, as1PtrTy,
408 existingGlobal.getSymName());
415 auto i8Type = rewriter.getI8Type();
416 auto arrayType = LLVM::LLVMArrayType::get(i8Type, strWithNull.size());
417 std::string globalName =
418 llvm::formatv(
"{0}.{1}", nameHint,
419 globalNameCounter.fetch_add(1, std::memory_order_relaxed))
424 rewriter.setInsertionPointToStart(&moduleOp->
getRegion(0).
front());
427 LLVM::GlobalOp::create(rewriter, loc, arrayType,
428 true, LLVM::Linkage::Private,
429 globalName, rewriter.getStringAttr(strRef));
430 globalOp.setSection(StringRef(
"llvm.metadata"));
431 globalOp.setUnnamedAddr(LLVM::UnnamedAddr::Global);
432 globalOp.setAlignment(1);
433 globalOp.setAddrSpace(1);
437 return LLVM::AddressOfOp::create(rewriter, loc, as1PtrTy, globalName);
460static Value annotatePtrWithCacheControl(ConversionPatternRewriter &rewriter,
465 buildCacheControlPayloads(cacheControls.getValue());
466 if (payloads.empty())
469 auto ptrType = cast<LLVM::LLVMPointerType>(
ptr.getType());
470 auto as1PtrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), 1);
471 auto i32Ty = rewriter.getI32Type();
475 createMetadataStringPtr(rewriter, moduleOp, loc,
"",
".str.file");
476 Value lineVal = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 0);
477 Value nullAS1 = LLVM::ZeroOp::create(rewriter, loc, as1PtrTy);
482 for (
const std::string &payload : payloads) {
483 Value annStr = createMetadataStringPtr(rewriter, moduleOp, loc, payload,
484 ".str.cachecontrol");
485 auto annOp = LLVM::PtrAnnotation::create(rewriter, loc, ptrType, curPtr,
486 annStr, fileStr, lineVal, nullAS1);
487 curPtr = annOp.getResult();
509template <
typename OpType>
511applyCacheControlAnnotation(ConversionPatternRewriter &rewriter,
Location loc,
513 Operation *moduleOp,
unsigned ptrIdx = 0) {
514 std::optional<ArrayAttr> optCacheControls =
515 getCacheControlMetadata(rewriter, op);
516 if (!optCacheControls)
519 Value annotatedPtr = annotatePtrWithCacheControl(rewriter, loc, args[ptrIdx],
520 *optCacheControls, moduleOp);
521 args[ptrIdx] = annotatedPtr;
528static LLVM::CallOp createDeviceFunctionCall(
529 ConversionPatternRewriter &rewriter, StringRef funcName,
Type retType,
532 LLVMFuncAttributeOptions funcAttributeOptions,
Operation *op) {
534 assert(moduleOp &&
"Expecting module");
539 assert(!
failed(funcOpRes));
540 LLVM::LLVMFuncOp funcOp = funcOpRes.value();
541 funcOp.setCConv(LLVM::cconv::CConv::SPIR_FUNC);
542 funcOp.setConvergent(funcAttributeOptions.isConvergent);
543 funcOp.setNoUnwind(funcAttributeOptions.isNoUnwind);
544 funcOp.setWillReturn(funcAttributeOptions.isWillReturn);
546 if (funcAttributeOptions.memEffectsAttr)
547 funcOp.setMemoryEffectsAttr(funcAttributeOptions.memEffectsAttr);
549 for (
auto [idx, attrName] : paramAttrs)
550 funcOp.setArgAttr(idx, attrName, rewriter.getUnitAttr());
552 auto callOp = LLVM::CallOp::create(rewriter, loc, funcOp, args);
554 auto copyAttr = [&](StringAttr name,
Attribute attr) {
555 if (callOp->getInherentAttr(name).has_value())
556 callOp->setInherentAttr(name, attr);
558 discardableAttrs.emplace_back(name, attr);
560 for (
NamedAttribute attr : funcOp->getDiscardableAttrDictionary())
561 copyAttr(attr.getName(), attr.getValue());
562 funcOp->getName().walkInherentAttrs(
563 funcOp, [&](StringRef name,
Attribute &attr) {
564 copyAttr(rewriter.getStringAttr(name), attr);
566 callOp->setDiscardableAttrs(discardableAttrs);
571static unsigned getNumOperandsPerDword(xevm::ElemType pTy) {
573 case xevm::ElemType::F32:
574 case xevm::ElemType::TF32:
576 case xevm::ElemType::BF16:
577 case xevm::ElemType::F16:
579 case xevm::ElemType::U8:
580 case xevm::ElemType::S8:
581 case xevm::ElemType::BF8:
582 case xevm::ElemType::F8:
584 case xevm::ElemType::E2M1:
585 case xevm::ElemType::U4:
586 case xevm::ElemType::S4:
589 llvm_unreachable(
"unsupported xevm::ElemType");
593class MMAToOCLPattern :
public OpConversionPattern<xevm::MMAOp> {
596 matchAndRewrite(xevm::MMAOp op, xevm::MMAOp::Adaptor adaptor,
597 ConversionPatternRewriter &rewriter)
const override {
599 return rewriter.notifyMatchFailure(op,
"OCL requires C operand");
601 auto precisionA = op.getTypes().getA();
602 auto precisionB = op.getTypes().getB();
603 auto precisionC = op.getTypes().getC();
604 auto precisionD = op.getTypes().getD();
605 if (precisionC != precisionD) {
606 return rewriter.notifyMatchFailure(op,
"type of C and D need to match");
608 if (precisionC != xevm::ElemType::S32 &&
609 precisionC != xevm::ElemType::F32 &&
610 precisionC != xevm::ElemType::F16 &&
611 precisionC != xevm::ElemType::BF16) {
612 return rewriter.notifyMatchFailure(
613 op,
"type of C and D must be S32, F32, F16 or BF16");
615 if (precisionA == xevm::ElemType::S32 ||
616 precisionA == xevm::ElemType::F32) {
617 return rewriter.notifyMatchFailure(op,
"type of A cannot be S32 or F32");
619 if (precisionB == xevm::ElemType::S32 ||
620 precisionB == xevm::ElemType::F32) {
621 return rewriter.notifyMatchFailure(op,
"type of B cannot be S32 or F32");
623 constexpr uint32_t bitWidthPackedA{16};
624 constexpr uint32_t bitWidthPackedB{32};
625 auto loc = op.getLoc();
627 auto castIfNeeded = [&](Value val, Type packedType) -> Value {
628 VectorType origTy = cast<VectorType>(val.
getType());
629 const uint32_t vecBitSize =
630 origTy.getNumElements() *
631 origTy.getElementType().getIntOrFloatBitWidth();
632 VectorType newTy = VectorType::get(
633 vecBitSize / packedType.getIntOrFloatBitWidth(), packedType);
635 val = LLVM::BitcastOp::create(rewriter, loc, newTy, val);
640 Type packedAType = (op.getTypes().getA() == xevm::ElemType::TF32)
641 ? cast<Type>(rewriter.getF32Type())
642 : rewriter.getIntegerType(bitWidthPackedA);
643 a = castIfNeeded(a, packedAType);
646 Type packedBType = (op.getTypes().getB() == xevm::ElemType::TF32)
647 ? cast<Type>(rewriter.getF32Type())
648 : rewriter.getIntegerType(bitWidthPackedB);
649 b = castIfNeeded(
b, packedBType);
652 VectorType cOrigTy = cast<VectorType>(c.
getType());
653 VectorType resOrigTy = cast<VectorType>(op->getResultTypes()[0]);
654 assert(cOrigTy == resOrigTy &&
"Accumulator and result type mismatch");
657 cOrigTy.getElementType().isBF16()
658 ? VectorType::get(cOrigTy.getShape(), rewriter.getIntegerType(16))
660 VectorType resTy = cTy;
662 c = LLVM::BitcastOp::create(rewriter, loc, cTy, c);
664 constexpr int32_t systolicDepth{8};
666 llvm::formatv(
"intel_sub_group_{0}_{1}_matrix_mad_k{2}",
667 stringifyElemType(op.getTypes().getA()).str(),
668 stringifyElemType(op.getTypes().getB()).str(),
670 getNumOperandsPerDword(op.getTypes().getA()))
672 SmallVector<Type> argTypes{a.
getType(),
b.getType(), cTy};
673 fnName = mangle(fnName, argTypes);
674 SmallVector<Value> args{a,
b, c};
676 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
677 LLVM::ModRefInfo::NoModRef,
678 LLVM::ModRefInfo::NoModRef,
679 LLVM::ModRefInfo::NoModRef,
680 LLVM::ModRefInfo::NoModRef,
681 LLVM::ModRefInfo::NoModRef,
682 LLVM::ModRefInfo::NoModRef);
683 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
684 funcAttrs.memEffectsAttr = memAttr;
686 createDeviceFunctionCall(rewriter, fnName, resTy, argTypes, args, {},
687 funcAttrs, op.getOperation())
690 if (resOrigTy != resTy)
691 result = LLVM::BitcastOp::create(rewriter, loc, resOrigTy,
result);
693 rewriter.replaceOp(op,
result);
698class PrefetchToOCLPattern :
public OpConversionPattern<PrefetchOp> {
701 matchAndRewrite(PrefetchOp op, PrefetchOp::Adaptor adaptor,
702 ConversionPatternRewriter &rewriter)
const override {
703 auto loc = op.getLoc();
706 const std::string fnName{
"_Z8prefetchPU3AS1Kcm"};
708 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), 1);
709 SmallVector<Value> args{op.getPtr(), one};
712 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
715 SmallVector<Type> argTypes;
716 for (
auto arg : args)
717 argTypes.push_back(arg.getType());
718 auto funcAttr = noUnwindAttrs;
719 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
720 LLVM::ModRefInfo::NoModRef,
721 LLVM::ModRefInfo::Ref,
722 LLVM::ModRefInfo::NoModRef,
723 LLVM::ModRefInfo::NoModRef,
724 LLVM::ModRefInfo::NoModRef,
725 LLVM::ModRefInfo::NoModRef);
726 funcAttr.memEffectsAttr = memAttr;
728 createDeviceFunctionCall(rewriter, fnName,
729 LLVM::LLVMVoidType::get(rewriter.getContext()),
730 argTypes, args, {}, funcAttr, op.getOperation());
731 rewriter.eraseOp(op);
736class MemfenceToOCLPattern :
public OpConversionPattern<MemfenceOp> {
739 matchAndRewrite(MemfenceOp op, MemfenceOp::Adaptor adaptor,
740 ConversionPatternRewriter &rewriter)
const override {
741 auto loc = op.getLoc();
742 const std::string fnName{
"atomic_work_item_fence"};
743 int memScope, addrSpace;
744 switch (op.getAddrspace()) {
745 case xevm::AddrSpace::SHARED:
748 case xevm::AddrSpace::GLOBAL:
753 return rewriter.notifyMatchFailure(
754 op,
"Fence only supports global and shared address spaces.");
756 switch (op.getScope()) {
757 case xevm::MemScope::WORKGROUP:
760 case xevm::MemScope::DEVICE:
765 return rewriter.notifyMatchFailure(
766 op,
"Fence only supports workgroup and device memory scopes.");
768 Type i32Type = rewriter.getI32Type();
769 Value acqRel = LLVM::ConstantOp::create(rewriter, loc, i32Type, 4);
770 Value memScopeConst =
771 LLVM::ConstantOp::create(rewriter, loc, i32Type, memScope);
772 Value addrSpaceConst =
773 LLVM::ConstantOp::create(rewriter, loc, i32Type, addrSpace);
774 SmallVector<Value> args{addrSpaceConst, acqRel, memScopeConst};
775 SmallVector<Type> argTypes{3, i32Type};
776 createDeviceFunctionCall(rewriter, mangle(fnName, argTypes),
777 LLVM::LLVMVoidType::get(rewriter.getContext()),
778 argTypes, args, {}, noUnwindAttrs,
780 rewriter.eraseOp(op);
784template <
typename OpType>
785class LoadStorePrefetchToOCLPattern :
public OpConversionPattern<OpType> {
786 using OpConversionPattern<OpType>::OpConversionPattern;
788 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
789 ConversionPatternRewriter &rewriter)
const override {
790 constexpr bool isLoad = std::is_same_v<OpType, BlockLoad2dOp>;
791 constexpr bool isPrefetch = std::is_same_v<OpType, BlockPrefetch2dOp>;
793 auto loc = op.getLoc();
794 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
796 bool packReg =
false;
797 bool transpose =
false;
798 if constexpr (isLoad) {
799 vecType = op.getRes().getType();
800 packReg = op.getPackRegister();
801 transpose = op.getTranspose();
802 }
else if constexpr (!isPrefetch) {
803 vecType = op.getStoredVal().getType();
806 auto i32Type = rewriter.getI32Type();
808 LLVM::UndefOp::create(rewriter, loc, VectorType::get(2, i32Type));
809 Value zero = LLVM::ConstantOp::create(rewriter, loc, i32Type, 0);
810 Value one = LLVM::ConstantOp::create(rewriter, loc, i32Type, 1);
811 byteCoord = LLVM::InsertElementOp::create(
812 rewriter, loc, VectorType::get(2, i32Type), byteCoord, op.getX(), zero);
813 byteCoord = LLVM::InsertElementOp::create(
814 rewriter, loc, VectorType::get(2, i32Type), byteCoord, op.getY(), one);
815 SmallVector<Value> args{op.getPtr(), op.getBaseWidth(), op.getBaseHeight(),
816 op.getBasePitch(), byteCoord};
819 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
822 SmallVector<Type> retTypes;
824 std::string funcName{
"intel_sub_group_2d_block_"};
825 std::string bitWidthId;
826 LLVMFuncAttributeOptions funcAttr{noUnwindWillReturnAttrs};
827 SmallVector<std::pair<unsigned, StringRef>, 4> paramAttrs;
828 if constexpr (isPrefetch) {
829 funcName +=
"prefetch";
830 paramAttrs = {std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName())};
831 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
832 LLVM::ModRefInfo::NoModRef,
833 LLVM::ModRefInfo::Ref,
834 LLVM::ModRefInfo::NoModRef,
835 LLVM::ModRefInfo::NoModRef,
836 LLVM::ModRefInfo::NoModRef,
837 LLVM::ModRefInfo::NoModRef);
838 funcAttr = noUnwindAttrs;
839 funcAttr.memEffectsAttr = memAttr;
841 auto vecElemType = vecType.getElementType();
842 auto vecElemBitWidth = vecElemType.getIntOrFloatBitWidth();
843 auto vecNumElems = vecType.getNumElements();
849 if (op.getElemSizeInBits() == 8 && op.getTileWidth() == 32) {
850 vecElemBitWidth = 16;
851 vecElemType = rewriter.getI16Type();
852 vecNumElems = vecNumElems / 2;
855 LLVM::ConstantOp::create(rewriter, loc, i32Type, vecNumElems);
856 auto dstOrSrcPtr = LLVM::AllocaOp::create(
857 rewriter, loc, LLVM::LLVMPointerType::get(rewriter.getContext()),
858 vecElemType, numElems);
859 args.push_back(dstOrSrcPtr);
860 if constexpr (isLoad) {
862 bitWidthId = getTypeMangling(vecElemType,
true);
864 funcName +=
"_transform";
866 funcName +=
"_transpose";
867 spvLoadDstPtr = dstOrSrcPtr;
868 retTypes.push_back(vecType);
870 std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName()),
871 std::make_pair(0, LLVM::LLVMDialect::getReadonlyAttrName()),
872 std::make_pair(5, LLVM::LLVMDialect::getNonNullAttrName()),
873 std::make_pair(5, LLVM::LLVMDialect::getWriteOnlyAttrName()),
877 bitWidthId = (vecElemBitWidth == 32)
879 : ((vecElemBitWidth == 16) ?
"t" :
"h");
880 LLVM::StoreOp::create(rewriter, loc, op.getStoredVal(), dstOrSrcPtr);
882 std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName()),
883 std::make_pair(0, LLVM::LLVMDialect::getWriteOnlyAttrName()),
884 std::make_pair(5, LLVM::LLVMDialect::getNonNullAttrName()),
885 std::make_pair(5, LLVM::LLVMDialect::getReadonlyAttrName()),
891 llvm::formatv(
"{0}_{1}b_{2}r{3}x{4}c", funcName, op.getElemSizeInBits(),
892 op.getTileHeight(), op.getTileWidth(), op.getVBlocks())
894 std::string prefetchCode(
"");
897 funcName = llvm::formatv(
"_Z{0}{1}PU3AS1viiiDv2_i{2}{3}", funcName.size(),
898 funcName, prefetchCode, bitWidthId)
900 SmallVector<Type> argTypes;
901 for (
auto arg : args) {
902 argTypes.push_back(arg.getType());
904 createDeviceFunctionCall(
905 rewriter, funcName, LLVM::LLVMVoidType::get(rewriter.getContext()),
906 argTypes, args, paramAttrs, funcAttr, op.getOperation());
908 if constexpr (isLoad)
910 op, LLVM::LoadOp::create(rewriter, loc, vecType, spvLoadDstPtr));
912 rewriter.eraseOp(op);
917template <
typename OpType>
918class BlockLoadStore1DToOCLPattern :
public OpConversionPattern<OpType> {
919 using OpConversionPattern<OpType>::OpConversionPattern;
921 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
922 ConversionPatternRewriter &rewriter)
const override {
923 constexpr bool isStore = std::is_same_v<OpType, xevm::BlockStoreOp>;
924 auto loc = op.getLoc();
925 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
930 std::string funcName{
"intel_sub_group_block_"};
933 if constexpr (isStore) {
934 funcName +=
"write_u";
935 valOrResTy = op.getVal().getType();
937 funcName +=
"read_u";
938 valOrResTy = op.getType();
941 VectorType vecTy = dyn_cast<VectorType>(valOrResTy);
942 Type elemType = vecTy ? vecTy.getElementType() : valOrResTy;
943 funcName += getTypeMangling(elemType);
945 funcName += std::to_string(vecTy.getNumElements());
946 SmallVector<Type, 2> argTypes{};
950 SmallVector<bool, 2> isUnsigned{};
954 SmallVector<Value, 2> args{};
955 args.push_back(op.getPtr());
956 argTypes.push_back(op.getPtr().getType());
957 isUnsigned.push_back(
true);
960 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
964 argTypes[0] = args[0].getType();
967 if constexpr (isStore) {
968 args.push_back(op.getVal());
969 argTypes.push_back(op.getVal().getType());
970 isUnsigned.push_back(
true);
971 retType = LLVM::LLVMVoidType::get(rewriter.getContext());
973 retType = valOrResTy;
975 funcName = std::string(
"_Z") + std::to_string(funcName.size()) + funcName +
977 std::to_string(op.getPtr().getType().getAddressSpace());
978 funcName += getTypeMangling(elemType,
true);
979 if constexpr (isStore)
980 funcName += getTypeMangling(valOrResTy,
true);
981 LLVMFuncAttributeOptions funcAttr{noUnwindWillReturnAttrs};
984 createDeviceFunctionCall(rewriter, funcName, retType, argTypes, args,
985 {}, funcAttr, op.getOperation());
987 if constexpr (isStore)
988 rewriter.eraseOp(op);
990 rewriter.replaceOp(op, call->getResult(0));
995template <
typename OpType>
996class LLVMLoadStoreToOCLPattern :
public OpConversionPattern<OpType> {
997 using OpConversionPattern<OpType>::OpConversionPattern;
999 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1000 ConversionPatternRewriter &rewriter)
const override {
1001 if (!op->hasDiscardableAttr(
"cache_control"))
1004 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
1005 std::optional<ArrayAttr> optCacheControls =
1006 getCacheControlMetadata(rewriter, op);
1007 if (!optCacheControls) {
1008 rewriter.modifyOpInPlace(
1009 op, [&]() { op->removeDiscardableAttr(
"cache_control"); });
1014 constexpr bool isStore = std::is_same_v<OpType, LLVM::StoreOp>;
1015 unsigned ptrIdx = isStore ? 1 : 0;
1016 Value ptr = op->getOperand(ptrIdx);
1019 Value annotatedPtr = annotatePtrWithCacheControl(
1020 rewriter, op->getLoc(), ptr, *optCacheControls, moduleOp);
1023 rewriter.modifyOpInPlace(op, [&]() {
1024 op->setOperand(ptrIdx, annotatedPtr);
1025 op->removeDiscardableAttr(
"cache_control");
1058static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdXOp) {
1059 return {
"get_local_id", 0};
1061static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdYOp) {
1062 return {
"get_local_id", 1};
1064static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdZOp) {
1065 return {
"get_local_id", 2};
1067static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimXOp) {
1068 return {
"get_local_size", 0};
1070static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimYOp) {
1071 return {
"get_local_size", 1};
1073static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimZOp) {
1074 return {
"get_local_size", 2};
1076static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdXOp) {
1077 return {
"get_group_id", 0};
1079static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdYOp) {
1080 return {
"get_group_id", 1};
1082static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdZOp) {
1083 return {
"get_group_id", 2};
1085static std::pair<StringRef, int64_t> getConfig(xevm::GridDimXOp) {
1086 return {
"get_num_groups", 0};
1088static std::pair<StringRef, int64_t> getConfig(xevm::GridDimYOp) {
1089 return {
"get_num_groups", 1};
1091static std::pair<StringRef, int64_t> getConfig(xevm::GridDimZOp) {
1092 return {
"get_num_groups", 2};
1096template <
typename OpType>
1097class LaunchConfigOpToOCLPattern :
public OpConversionPattern<OpType> {
1098 using OpConversionPattern<OpType>::OpConversionPattern;
1100 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1101 ConversionPatternRewriter &rewriter)
const override {
1102 Location loc = op->getLoc();
1103 auto [baseName, dim] = getConfig(op);
1104 Type dimTy = rewriter.getI32Type();
1105 Value dimVal = LLVM::ConstantOp::create(rewriter, loc, dimTy,
1106 static_cast<int64_t
>(dim));
1107 std::string func = mangle(baseName, {dimTy}, {
true});
1108 Type resTy = op.getType();
1110 createDeviceFunctionCall(rewriter, func, resTy, {dimTy}, {dimVal}, {},
1111 noUnwindWillReturnAttrs, op.getOperation());
1112 constexpr auto noModRef = LLVM::ModRefInfo::NoModRef;
1113 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1119 call.setMemoryEffectsAttr(memAttr);
1120 rewriter.replaceOp(op, call);
1137static StringRef getConfig(xevm::LaneIdOp) {
return "get_sub_group_local_id"; }
1138static StringRef getConfig(xevm::SubgroupIdOp) {
return "get_sub_group_id"; }
1139static StringRef getConfig(xevm::SubgroupSizeOp) {
1140 return "get_sub_group_size";
1142template <
typename OpType>
1143class SubgroupOpWorkitemOpToOCLPattern :
public OpConversionPattern<OpType> {
1144 using OpConversionPattern<OpType>::OpConversionPattern;
1146 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1147 ConversionPatternRewriter &rewriter)
const override {
1148 std::string func = mangle(getConfig(op).str(), {});
1149 Type resTy = op.getType();
1151 createDeviceFunctionCall(rewriter, func, resTy, {}, {}, {},
1152 noUnwindWillReturnAttrs, op.getOperation());
1153 constexpr auto noModRef = LLVM::ModRefInfo::NoModRef;
1154 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1160 call.setMemoryEffectsAttr(memAttr);
1161 rewriter.replaceOp(op, call);
1168static bool isSupportedSPIRVVectorLength(
int64_t numElements) {
1169 return llvm::is_contained({2, 3, 4, 8, 16}, numElements);
1173static Value castIfNeeded(ConversionPatternRewriter &rewriter,
Location loc,
1177 return LLVM::BitcastOp::create(rewriter, loc, ty, val);
1183static Value takeLeadingElements(ConversionPatternRewriter &rewriter,
1185 auto vecTy = cast<VectorType>(val.
getType());
1186 if (vecTy.getNumElements() == numElements)
1189 llvm::to_vector(llvm::seq<int32_t>(0,
static_cast<int32_t
>(numElements)));
1190 return LLVM::ShuffleVectorOp::create(rewriter, loc, val, val, mask);
1204class TruncfToOCLPattern :
public OpConversionPattern<TruncfOp> {
1207 matchAndRewrite(TruncfOp op, TruncfOp::Adaptor adaptor,
1208 ConversionPatternRewriter &rewriter)
const override {
1210 auto srcEtype = op.getSrcEtype().getEtype();
1211 auto dstEtype = op.getDstEtype().getEtype();
1217 auto vecSrcTy = dyn_cast<VectorType>(op.getSrc().getType());
1219 return rewriter.notifyMatchFailure(op,
"Scalar src is not supported.");
1221 int64_t numElements = vecSrcTy.getNumElements();
1222 if (!isSupportedSPIRVVectorLength(numElements))
1223 return rewriter.notifyMatchFailure(
1224 op,
"src vector length must be 2, 3, 4, 8 or 16");
1227 Type dstTy = op.getDst().getType();
1228 Location loc = op.getLoc();
1229 Value src = op.getSrc();
1230 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1231 LLVM::ModRefInfo::NoModRef,
1232 LLVM::ModRefInfo::NoModRef,
1233 LLVM::ModRefInfo::NoModRef,
1234 LLVM::ModRefInfo::NoModRef,
1235 LLVM::ModRefInfo::NoModRef,
1236 LLVM::ModRefInfo::NoModRef);
1237 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1238 funcAttrs.memEffectsAttr = memAttr;
1241 if (dstEtype == TruncfDstElemTypes::E2M1) {
1251 constexpr int kDnsclConvertToE2M1 = 1;
1252 constexpr int kDnsclModeBytes02 = 0;
1253 constexpr int kDnsclModeBytes13 = 2;
1256 int64_t numLanes = llvm::divideCeil(numElements, 2);
1258 Type i32Ty = rewriter.getI32Type();
1259 Type i8Ty = rewriter.getI8Type();
1263 if (numElements != numLanes * 2) {
1264 SmallVector<int32_t> mask = llvm::to_vector(
1265 llvm::seq<int32_t>(0,
static_cast<int32_t
>(numElements)));
1267 mask.append(
static_cast<size_t>(numLanes * 2 - numElements), 0);
1268 padded = LLVM::ShuffleVectorOp::create(rewriter, loc, src, src, mask);
1274 laneVec = LLVM::BitcastOp::create(
1275 rewriter, loc, VectorType::get(numLanes, i32Ty), padded);
1277 laneVec = LLVM::BitcastOp::create(rewriter, loc, i32Ty, padded);
1278 auto getLane = [&](int64_t idx) -> Value {
1282 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), idx);
1283 return LLVM::ExtractElementOp::create(rewriter, loc, laneVec, pos)
1287 std::string fnName =
"__builtin_IB_dnscl_";
1288 fnName += (srcEtype == TruncfSrcElemTypes::F16) ?
"hf16" :
"bf16";
1290 LLVM::ConstantOp::create(rewriter, loc, i32Ty, kDnsclConvertToE2M1);
1291 auto genDnscl = [&](Value lo, Value hi,
int mode) -> Value {
1292 Value modeVal = LLVM::ConstantOp::create(rewriter, loc, i32Ty, mode);
1293 SmallVector<Type> argTypes{lo.
getType(), hi.getType(),
1295 SmallVector<Value> args{lo, hi, convertTo, modeVal};
1296 return createDeviceFunctionCall(rewriter, fnName, i32Ty, argTypes, args,
1297 {}, funcAttrs, op.getOperation())
1302 if (numLanes <= 2) {
1305 Value lo = getLane(0);
1309 : LLVM::UndefOp::create(rewriter, loc, i32Ty)->getResult(0);
1310 Value dword = genDnscl(lo, hi, kDnsclModeBytes02);
1311 if (numLanes == 1) {
1313 result = LLVM::TruncOp::create(rewriter, loc, i8Ty, dword);
1315 Value bytes = LLVM::BitcastOp::create(
1316 rewriter, loc, VectorType::get(4, i8Ty), dword);
1317 result = LLVM::ShuffleVectorOp::create(rewriter, loc, bytes, bytes,
1318 ArrayRef<int32_t>{0, 2});
1322 SmallVector<Value> dwords;
1323 for (int64_t base = 0; base < numLanes; base += 4) {
1328 Value lane0 = getLane(base);
1329 Value lane2 = getLane(base + 2);
1330 Value even = genDnscl(lane0, lane2, kDnsclModeBytes02);
1331 Value lane1 = getLane(base + 1);
1332 Value lane3 = getLane(base + 3);
1333 Value odd = genDnscl(lane1, lane3, kDnsclModeBytes13);
1334 dwords.push_back(LLVM::OrOp::create(rewriter, loc, even, odd));
1336 if (dwords.size() == 1) {
1339 Type packedTy = VectorType::get(dwords.size(), i32Ty);
1340 result = LLVM::UndefOp::create(rewriter, loc, packedTy);
1341 for (
auto [idx, dword] : llvm::enumerate(dwords)) {
1342 Value pos = LLVM::ConstantOp::create(rewriter, loc, i32Ty, idx);
1344 LLVM::InsertElementOp::create(rewriter, loc,
result, dword, pos)
1349 rewriter.replaceOp(op, castIfNeeded(rewriter, loc, dstTy,
result));
1372 bool srcIsBF16 = srcEtype == TruncfSrcElemTypes::BF16;
1374 src = LLVM::BitcastOp::create(
1376 VectorType::get(vecSrcTy.getShape(), rewriter.getI16Type()), src);
1377 StringRef convertName;
1378 if (dstEtype == TruncfDstElemTypes::BF8) {
1379 convertName = srcIsBF16 ?
"__builtin_spirv_ConvertBF16ToE5M2EXT"
1380 :
"__builtin_spirv_ConvertFP16ToE5M2EXT";
1381 }
else if (dstEtype == TruncfDstElemTypes::F8) {
1382 convertName = srcIsBF16 ?
"__builtin_spirv_ConvertBF16ToE4M3EXT"
1383 :
"__builtin_spirv_ConvertFP16ToE4M3EXT";
1385 return rewriter.notifyMatchFailure(
1386 op,
"Unsupported src, dst element type pair.");
1388 SmallVector<Type> argTypes{src.
getType()};
1389 SmallVector<Value> args{src};
1390 Value
result = createDeviceFunctionCall(
1391 rewriter, mangle(convertName, argTypes), dstTy, argTypes,
1392 args, {}, funcAttrs, op.getOperation())
1394 rewriter.replaceOp(op,
result);
1399class ExtfToOCLPattern :
public OpConversionPattern<ExtfOp> {
1402 matchAndRewrite(ExtfOp op, ExtfOp::Adaptor adaptor,
1403 ConversionPatternRewriter &rewriter)
const override {
1406 auto srcEtype = op.getSrcEtype().getEtype();
1407 auto dstEtype = op.getDstEtype().getEtype();
1410 Type srcTy = op.getSrc().getType();
1412 auto vecDstTy = dyn_cast<VectorType>(op.getDst().getType());
1414 return rewriter.notifyMatchFailure(op,
"Scalar dst is not supported.");
1416 int64_t numElements = vecDstTy.getNumElements();
1417 if (!isSupportedSPIRVVectorLength(numElements))
1418 return rewriter.notifyMatchFailure(
1419 op,
"dst vector length must be 2, 3, 4, 8 or 16");
1420 Location loc = op.getLoc();
1421 Value src = op.getSrc();
1422 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1423 LLVM::ModRefInfo::NoModRef,
1424 LLVM::ModRefInfo::NoModRef,
1425 LLVM::ModRefInfo::NoModRef,
1426 LLVM::ModRefInfo::NoModRef,
1427 LLVM::ModRefInfo::NoModRef,
1428 LLVM::ModRefInfo::NoModRef);
1429 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1430 funcAttrs.memEffectsAttr = memAttr;
1433 if (srcEtype == ExtfSrcElemTypes::E2M1) {
1446 int64_t numBytes = llvm::divideCeil(numElements, 2);
1447 constexpr int kLutE2M1ToF16 = 7;
1448 constexpr int kLutE2M1ToBF16 = 5;
1450 (dstEtype == ExtfDstElemTypes::F16) ? kLutE2M1ToF16 : kLutE2M1ToBF16;
1451 Value lutIdx = LLVM::ConstantOp::create(rewriter, loc,
1452 rewriter.getI32Type(), lutIndex);
1453 Type lutTy = VectorType::get(16, rewriter.getI32Type());
1455 createDeviceFunctionCall(rewriter,
"__builtin_IB_shfl_idx4_lut",
1456 lutTy, {lutIdx.
getType()}, {lutIdx}, {},
1457 funcAttrs, op.getOperation())
1461 Type i8Ty = rewriter.getI8Type();
1462 Type i32Ty = rewriter.getI32Type();
1463 std::string fnName =
"__builtin_IB_shfl_idx4_to_fp16_";
1464 Type argTy, packedResTy;
1465 if (numBytes == 1) {
1467 packedResTy = i32Ty;
1469 fnName += std::to_string(numBytes) +
"_";
1470 argTy = VectorType::get(numBytes, i8Ty);
1471 packedResTy = VectorType::get(numBytes, i32Ty);
1474 SmallVector<Type> convArgTypes{lut.
getType(), argTy};
1475 SmallVector<Value> convArgs{lut, castIfNeeded(rewriter, loc, argTy, src)};
1477 createDeviceFunctionCall(rewriter, fnName, packedResTy, convArgTypes,
1478 convArgs, {}, funcAttrs, op.getOperation())
1482 Type wideTy = VectorType::get(numBytes * 2, vecDstTy.getElementType());
1483 result = LLVM::BitcastOp::create(rewriter, loc, wideTy,
result);
1484 result = takeLeadingElements(rewriter, loc,
result, numElements);
1485 rewriter.replaceOp(op,
result);
1491 auto vecSrcTy = dyn_cast<VectorType>(srcTy);
1492 if (!vecSrcTy || vecSrcTy.getNumElements() != numElements)
1493 return rewriter.notifyMatchFailure(
1494 op,
"fp8 src and dst must have the same number of elements");
1502 bool dstIsBF16 = dstEtype == ExtfDstElemTypes::BF16;
1503 StringRef convertName;
1504 if (srcEtype == ExtfSrcElemTypes::BF8) {
1505 convertName = dstIsBF16 ?
"__builtin_spirv_ConvertE5M2ToBF16EXT"
1506 :
"__builtin_spirv_ConvertE5M2ToFP16EXT";
1508 convertName = dstIsBF16 ?
"__builtin_spirv_ConvertE4M3ToBF16EXT"
1509 :
"__builtin_spirv_ConvertE4M3ToFP16EXT";
1511 Type resElemTy = dstIsBF16 ? cast<Type>(rewriter.getI16Type())
1512 : cast<Type>(rewriter.getF16Type());
1513 Type resTy = VectorType::get(vecSrcTy.getShape(), resElemTy);
1514 SmallVector<Type> argTypes{src.
getType()};
1515 SmallVector<Value> args{src};
1516 Value
result = createDeviceFunctionCall(
1517 rewriter, mangle(convertName, argTypes), resTy, argTypes,
1518 args, {}, funcAttrs, op.getOperation())
1522 rewriter.replaceOp(op, castIfNeeded(rewriter, loc, vecDstTy,
result));
1527class MMAMxToOCLPattern :
public OpConversionPattern<MMAMxOp> {
1530 matchAndRewrite(MMAMxOp op, MMAMxOp::Adaptor adaptor,
1531 ConversionPatternRewriter &rewriter)
const override {
1533 return rewriter.notifyMatchFailure(op,
"OCL requires C operand");
1535 auto precisionC = op.getTypes().getC();
1536 auto precisionD = op.getTypes().getD();
1537 if (precisionC != precisionD) {
1538 return rewriter.notifyMatchFailure(op,
"type of C and D need to match");
1541 constexpr uint32_t bitWidthPackedA{16};
1542 constexpr uint32_t bitWidthPackedB{32};
1543 auto loc = op.getLoc();
1545 auto castIfNeeded = [&](Value val, Type packedType) -> Value {
1546 VectorType origTy = cast<VectorType>(val.
getType());
1547 const uint32_t vecBitSize =
1548 origTy.getNumElements() *
1549 origTy.getElementType().getIntOrFloatBitWidth();
1550 VectorType newTy = VectorType::get(
1551 vecBitSize / packedType.getIntOrFloatBitWidth(), packedType);
1552 if (origTy != newTy)
1553 val = LLVM::BitcastOp::create(rewriter, loc, newTy, val);
1557 Value a = op.getA();
1558 Type packedAType = (op.getTypes().getA() == xevm::ElemType::TF32)
1559 ? cast<Type>(rewriter.getF32Type())
1560 : rewriter.getIntegerType(bitWidthPackedA);
1561 a = castIfNeeded(a, packedAType);
1563 Value
b = op.getB();
1564 Type packedBType = (op.getTypes().getB() == xevm::ElemType::TF32)
1565 ? cast<Type>(rewriter.getF32Type())
1566 : rewriter.getIntegerType(bitWidthPackedB);
1567 b = castIfNeeded(
b, packedBType);
1569 Value c = op.getC();
1570 VectorType cOrigTy = cast<VectorType>(c.
getType());
1571 VectorType resOrigTy = cast<VectorType>(op->getResultTypes()[0]);
1572 assert(cOrigTy == resOrigTy &&
"Accumulator and result type mismatch");
1575 cOrigTy.getElementType().isBF16()
1576 ? VectorType::get(cOrigTy.getShape(), rewriter.getIntegerType(16))
1578 VectorType resTy = cTy;
1580 c = LLVM::BitcastOp::create(rewriter, loc, cTy, c);
1582 std::string fnName =
1583 llvm::formatv(
"__builtin_IB_sub_group16_bdpas_{0}_{1}_{2}_{3}_8_8",
1584 builtinElemType(op.getTypes().getD()),
1585 builtinElemType(op.getTypes().getC()),
1586 builtinElemType(op.getTypes().getA()),
1587 builtinElemType(op.getTypes().getB()))
1589 auto scaleA = op.getScaleA();
1590 auto scaleB = op.getScaleB();
1591 SmallVector<Type> argTypes{cTy, a.
getType(),
b.getType(), scaleA.getType(),
1593 SmallVector<Value> args{c, a,
b, scaleA, scaleB};
1595 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1596 LLVM::ModRefInfo::NoModRef,
1597 LLVM::ModRefInfo::NoModRef,
1598 LLVM::ModRefInfo::NoModRef,
1599 LLVM::ModRefInfo::NoModRef,
1600 LLVM::ModRefInfo::NoModRef,
1601 LLVM::ModRefInfo::NoModRef);
1602 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1603 funcAttrs.memEffectsAttr = memAttr;
1605 createDeviceFunctionCall(rewriter, fnName, resTy, argTypes, args, {},
1606 funcAttrs, op.getOperation())
1609 if (resOrigTy != resTy)
1610 result = LLVM::BitcastOp::create(rewriter, loc, resOrigTy,
result);
1612 rewriter.replaceOp(op,
result);
1625class BitcastShuffleToGenISAPattern
1626 :
public OpConversionPattern<BitcastShuffleOp> {
1629 matchAndRewrite(BitcastShuffleOp op, BitcastShuffleOp::Adaptor adaptor,
1630 ConversionPatternRewriter &rewriter)
const override {
1631 Type srcTy = op.getSrc().getType();
1632 Type resTy = op.getRes().getType();
1634 std::string fnName =
"llvm.genx.GenISA.SubgroupBitcastShuffle." +
1635 getGenISATypeMangling(resTy) +
"." +
1636 getGenISATypeMangling(srcTy);
1638 Value
result = createDeviceFunctionCall(
1639 rewriter, fnName, resTy, {srcTy}, {adaptor.getSrc()}, {},
1640 convergentNoUnwindWillReturnAttrs, op.getOperation())
1643 rewriter.replaceOp(op,
result);
1648class AllocaToGlobalPattern :
public OpConversionPattern<LLVM::AllocaOp> {
1651 matchAndRewrite(LLVM::AllocaOp op, LLVM::AllocaOp::Adaptor adaptor,
1652 ConversionPatternRewriter &rewriter)
const override {
1653 auto ptrType = cast<LLVM::LLVMPointerType>(op.getType());
1654 auto addrSpace = ptrType.getAddressSpace();
1657 auto symTable = op->getParentWithTrait<OpTrait::SymbolTable>();
1661 if (ModuleOp mod = dyn_cast<ModuleOp>(*symTable)) {
1662 moduleBody = mod.getBody();
1663 }
else if (gpu::GPUModuleOp gpuMod =
1664 dyn_cast<gpu::GPUModuleOp>(*symTable)) {
1665 moduleBody = gpuMod.getBody();
1669 auto val = op.getArraySize();
1673 auto loc = op.getLoc();
1674 auto globalType = LLVM::LLVMArrayType::get(
1675 rewriter.getContext(), op.getElemType(), cst.getZExtValue());
1676 LLVM::GlobalOp globalVar;
1678 OpBuilder::InsertionGuard guard(rewriter);
1679 rewriter.setInsertionPointToStart(moduleBody);
1680 auto alignment = op.getAlignment();
1681 globalVar = LLVM::GlobalOp::create(
1682 rewriter, loc, globalType,
false,
1683 LLVM::Linkage::Internal,
1684 std::string(
"__global_alloca_") +
1685 std::to_string(getNextGlobalIdx()),
1687 alignment ? *alignment : 0, addrSpace);
1689 rewriter.replaceOpWithNewOp<LLVM::AddressOfOp>(op, globalVar);
1694 static unsigned getNextGlobalIdx() {
1695 static unsigned globalIdx = 0;
1708static bool isExtractingContiguousSlice(LLVM::ShuffleVectorOp op) {
1709 if (op.getV1() != op.getV2() &&
1710 !isa_and_present<LLVM::PoisonOp, LLVM::UndefOp>(
1711 op.getV2().getDefiningOp()))
1713 auto maskAttr = op.getMask();
1715 int64_t sourceSize = op.getV1().getType().getNumElements();
1716 if (maskSize > sourceSize)
1718 int64_t firstIndex = maskAttr[0];
1719 if (firstIndex < 0 || firstIndex >= sourceSize)
1721 for (
int64_t i = 1; i < maskSize; ++i) {
1723 if (
index != firstIndex + i)
1725 if (
index >= sourceSize)
1741 return rewriter.
create(state);
1756class HandleVectorExtractPattern
1758 using OpRewritePattern<LLVM::ShuffleVectorOp>::OpRewritePattern;
1760 void initialize() { setHasBoundedRewriteRecursion(); }
1762 LogicalResult matchAndRewrite(LLVM::ShuffleVectorOp op,
1763 PatternRewriter &rewriter)
const override {
1765 if (!isExtractingContiguousSlice(op))
1768 auto mask = op.getMask();
1769 auto loc = op.getLoc();
1770 auto ty = op.getType();
1772 auto src = op.getV1();
1775 if (isa<LLVM::FPExtOp>(srcOp) || isa<LLVM::FPTruncOp>(srcOp)) {
1776 Value srcInput = srcOp->getOperand(0);
1778 auto srcVecTy = dyn_cast<VectorType>(srcInput.
getType());
1781 auto newShuffleVecTy =
1782 VectorType::get(mask.size(), srcVecTy.getElementType());
1783 auto newShuffle = LLVM::ShuffleVectorOp::create(
1784 rewriter, loc, newShuffleVecTy, srcInput, srcInput, mask);
1787 if (isa<LLVM::FPExtOp>(srcOp)) {
1788 newUnaryOp = LLVM::FPExtOp::create(rewriter, loc, ty, newShuffle);
1790 newUnaryOp = LLVM::FPTruncOp::create(rewriter, loc, ty, newShuffle);
1793 }
else if (isa<LLVM::BitcastOp>(srcOp)) {
1794 Value srcInput = srcOp->getOperand(0);
1797 auto srcInputVecTy = dyn_cast<VectorType>(srcInput.
getType());
1798 auto srcResVecTy = dyn_cast<VectorType>(srcOp->getResult(0).getType());
1799 if (!srcInputVecTy || !srcResVecTy)
1801 auto srcInputSize = srcInputVecTy.getNumElements();
1802 auto srcResSize = srcResVecTy.getNumElements();
1803 auto maskSize =
static_cast<int32_t
>(mask.size());
1804 if (srcInputSize > srcResSize) {
1807 if (srcResSize % srcInputSize != 0) {
1810 auto maskScale = srcResSize / srcInputSize;
1815 SmallVector<int32_t> newMask;
1816 if (maskScale != 1) {
1819 if (mask[0] % maskScale != 0 || maskSize % maskScale != 0) {
1823 int32_t newMaskSize = maskSize / maskScale;
1824 int32_t maskStart = mask[0] / maskScale;
1825 for (int32_t i = 0; i < newMaskSize; ++i) {
1826 newMask.push_back(maskStart + i);
1830 auto newShuffleVecTy = VectorType::get(
1831 static_cast<int64_t
>(mask.size()), srcInputVecTy.getElementType());
1832 auto newShuffle = LLVM::ShuffleVectorOp::create(
1833 rewriter, loc, newShuffleVecTy, srcInput, srcInput, mask);
1836 LLVM::BitcastOp::create(rewriter, loc, ty, newShuffle);
1838 }
else if (isa<LLVM::ShuffleVectorOp>(srcOp)) {
1843 auto srcShuffle = cast<LLVM::ShuffleVectorOp>(srcOp);
1844 if (!isExtractingContiguousSlice(srcShuffle))
1846 auto srcMask = srcShuffle.getMask();
1847 SmallVector<int32_t> combinedMask;
1848 for (
auto index : mask) {
1849 combinedMask.push_back(srcMask[index]);
1851 auto newShuffle = LLVM::ShuffleVectorOp::create(
1852 rewriter, loc, ty, srcShuffle.getV1(), srcShuffle.getV1(),
1855 }
else if (isa<LLVM::LoadOp>(srcOp)) {
1857 auto loadOp = cast<LLVM::LoadOp>(srcOp);
1858 auto loadPtr = loadOp.getAddr();
1859 auto loadAddrSpace = loadPtr.getType().getAddressSpace();
1860 if (loadAddrSpace != 0)
1862 auto loadTy = dyn_cast<VectorType>(loadOp.getType());
1865 auto elemTy = loadTy.getElementType();
1866 auto firstIndex = mask[0];
1867 auto newVecTy = VectorType::get(mask.size(), elemTy);
1870 auto newPtr = LLVM::GEPOp::create(
1872 LLVM::LLVMPointerType::get(rewriter.
getContext(), loadAddrSpace),
1873 elemTy, loadPtr, ArrayRef<LLVM::GEPArg>{firstIndex});
1874 auto newLoad = LLVM::LoadOp::create(rewriter, loc, newVecTy, newPtr);
1877 auto newLoad = LLVM::LoadOp::create(rewriter, loc, newVecTy, loadPtr);
1881 srcOp->getNumOperands() >= 1 &&
1885 cast<VectorType>(src.
getType()).getNumElements() > 16 &&
1886 llvm::all_of(srcOp->getOperands(), [&](Value operand) {
1887 auto operandTy = dyn_cast<VectorType>(operand.getType());
1888 auto srcTy = cast<VectorType>(src.getType());
1889 return operandTy && operandTy.getRank() == 1 &&
1890 operandTy.getNumElements() == srcTy.getNumElements();
1897 SmallVector<Value> newOperands;
1898 newOperands.reserve(srcOp->getNumOperands());
1899 for (Value operand : srcOp->getOperands()) {
1900 auto operandTy = cast<VectorType>(operand.
getType());
1902 VectorType::get(mask.size(), operandTy.getElementType());
1903 newOperands.push_back(LLVM::ShuffleVectorOp::create(
1904 rewriter, loc, sliceTy, operand, operand, mask));
1924struct ConvertXeVMToLLVMPass
1928 void getDependentDialects(DialectRegistry ®istry)
const override {
1929 registry.
insert<LLVM::LLVMDialect, XeVMDialect>();
1932 void runOnOperation()
override {
1936 if (
failed(applyPartialConversion(getOperation(),
target,
1937 std::move(patterns))))
1938 signalPassFailure();
1942 RewritePatternSet vectorPatterns(&
getContext());
1943 vectorPatterns.add<HandleVectorExtractPattern>(&
getContext());
1944 GreedyRewriteConfig config{};
1949 config.enableFolding(
false);
1966 target.addDynamicallyLegalDialect<LLVM::LLVMDialect>([](
Operation *op) {
1970 if (isa<LLVM::AllocaOp>(op)) {
1971 LLVM::AllocaOp aOp = cast<LLVM::AllocaOp>(op);
1972 LLVM::LLVMPointerType pTy = cast<LLVM::LLVMPointerType>(aOp.getType());
1973 auto addrSpace = pTy.getAddressSpace();
1974 return addrSpace != 3;
1977 return !op->hasDiscardableAttr(
"cache_control");
1979 target.addIllegalDialect<XeVMDialect>();
1980 patterns.
add<LoadStorePrefetchToOCLPattern<BlockLoad2dOp>,
1981 LoadStorePrefetchToOCLPattern<BlockStore2dOp>,
1982 LoadStorePrefetchToOCLPattern<BlockPrefetch2dOp>,
1983 MMAToOCLPattern, MemfenceToOCLPattern, PrefetchToOCLPattern,
1984 LLVMLoadStoreToOCLPattern<LLVM::LoadOp>,
1985 LLVMLoadStoreToOCLPattern<LLVM::StoreOp>,
1986 BlockLoadStore1DToOCLPattern<BlockLoadOp>,
1987 BlockLoadStore1DToOCLPattern<BlockStoreOp>,
1988 LaunchConfigOpToOCLPattern<WorkitemIdXOp>,
1989 LaunchConfigOpToOCLPattern<WorkitemIdYOp>,
1990 LaunchConfigOpToOCLPattern<WorkitemIdZOp>,
1991 LaunchConfigOpToOCLPattern<WorkgroupDimXOp>,
1992 LaunchConfigOpToOCLPattern<WorkgroupDimYOp>,
1993 LaunchConfigOpToOCLPattern<WorkgroupDimZOp>,
1994 LaunchConfigOpToOCLPattern<WorkgroupIdXOp>,
1995 LaunchConfigOpToOCLPattern<WorkgroupIdYOp>,
1996 LaunchConfigOpToOCLPattern<WorkgroupIdZOp>,
1997 LaunchConfigOpToOCLPattern<GridDimXOp>,
1998 LaunchConfigOpToOCLPattern<GridDimYOp>,
1999 LaunchConfigOpToOCLPattern<GridDimZOp>,
2000 SubgroupOpWorkitemOpToOCLPattern<LaneIdOp>,
2001 SubgroupOpWorkitemOpToOCLPattern<SubgroupIdOp>,
2002 SubgroupOpWorkitemOpToOCLPattern<SubgroupSizeOp>,
2003 TruncfToOCLPattern, ExtfToOCLPattern, MMAMxToOCLPattern,
2004 BitcastShuffleToGenISAPattern, AllocaToGlobalPattern>(
LogicalResult initialize(unsigned origNumLoops, ArrayRef< ReassociationIndices > foldedIterationDims)
static Operation * cloneOpWithOperandsAndTypes(RewriterBase &rewriter, Location loc, Operation *op, ArrayRef< Value > operands, ArrayRef< Type > resultTypes)
Attributes are known-constant values of operations.
MLIRContext * getContext() const
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
NamedAttribute represents a combination of a name and an Attribute value.
RAII guard to reset the insertion point of the builder when destroyed.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
A trait used to provide symbol table functionalities to a region operation.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
Location getLoc()
The source location the operation was defined or derived from.
DictionaryAttr getRawDictionaryAttrs()
Return all attributes that are not stored as properties.
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
OperationName getName()
The name of an operation is the key identifier for it.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
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 DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int32_t > content)
FailureOr< LLVM::LLVMFuncOp > lookupOrCreateFn(OpBuilder &b, Operation *moduleOp, StringRef name, ArrayRef< Type > paramTypes={}, Type resultType={}, bool isVarArg=false, bool isReserved=false, SymbolTableCollection *symbolTables=nullptr)
Create a FuncOp with signature resultType(paramTypes) and name name`.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
void populateXeVMToLLVMConversionPatterns(ConversionTarget &target, RewritePatternSet &patterns)
llvm::TypeSwitch< T, ResultT > TypeSwitch
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.