18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/Support/FormatVariadic.h"
26#include "llvm/ADT/TypeSwitch.h"
29#define GEN_PASS_DEF_CONVERTXEVMTOLLVMPASS
30#include "mlir/Conversion/Passes.h.inc"
38struct LLVMFuncAttributeOptions {
39 bool isConvergent =
false;
40 bool isNoUnwind =
false;
41 bool isWillReturn =
false;
42 LLVM::MemoryEffectsAttr memEffectsAttr{};
44static constexpr LLVMFuncAttributeOptions noUnwindAttrs = {
45 false,
true,
false, {}};
46static constexpr LLVMFuncAttributeOptions noUnwindWillReturnAttrs = {
47 false,
true,
true, {}};
48static constexpr LLVMFuncAttributeOptions convergentNoUnwindWillReturnAttrs = {
49 true,
true,
true, {}};
51std::string getTypeMangling(
Type ty,
bool isUnsigned =
false) {
53 .Case([isUnsigned](VectorType ty) -> std::string {
54 return "Dv" + std::to_string(ty.getNumElements()) +
"_" +
55 getTypeMangling(ty.getElementType(), isUnsigned);
57 .Case([](Float16Type) -> std::string {
return "Dh"; })
58 .Case([](Float32Type) -> std::string {
return "f"; })
59 .Case([](Float64Type) -> std::string {
return "d"; })
60 .Case([isUnsigned](IntegerType ty) -> std::string {
61 switch (ty.getWidth()) {
63 return isUnsigned ?
"h" :
"c";
65 return isUnsigned ?
"t" :
"s";
67 return isUnsigned ?
"j" :
"i";
69 return isUnsigned ?
"m" :
"l";
71 llvm_unreachable(
"unhandled integer type");
74 .DefaultUnreachable(
"unhandled type for mangling");
79 assert((isUnsigned.empty() || isUnsigned.size() == types.size()) &&
80 "Signedness info doesn't match");
82 llvm::raw_string_ostream os(s);
83 llvm::SmallDenseMap<Type, unsigned> substitutions;
84 os <<
"_Z" << baseName.size() << baseName;
85 for (
auto [idx, type] : llvm::enumerate(types)) {
86 auto it = substitutions.find(type);
87 if (it != substitutions.end()) {
90 if (
unsigned firstIdx = it->getSecond(); firstIdx > 0)
94 if (!type.isIntOrFloat())
95 substitutions[type] = substitutions.size();
96 os << getTypeMangling(type, isUnsigned.empty() ?
false : isUnsigned[idx]);
106std::string getGenISATypeMangling(
Type ty) {
108 .Case([](VectorType ty) -> std::string {
109 return "v" + std::to_string(ty.getNumElements()) +
110 getGenISATypeMangling(ty.getElementType());
112 .Case([](IntegerType ty) -> std::string {
113 return "i" + std::to_string(ty.getWidth());
115 .DefaultUnreachable(
"unhandled type for GenISA mangling");
118std::string builtinElemType(ElemType elemType) {
131 return stringifyElemType(elemType).str();
135static int32_t getL1CacheControl(LoadCacheControl cc) {
138 case LoadCacheControl::USE_DEFAULT:
141 case LoadCacheControl::L1C_L2UC_L3UC:
142 case LoadCacheControl::L1C_L2UC_L3C:
143 case LoadCacheControl::L1C_L2C_L3UC:
144 case LoadCacheControl::L1C_L2C_L3C:
147 case LoadCacheControl::L1S_L2UC_L3UC:
148 case LoadCacheControl::L1S_L2UC_L3C:
149 case LoadCacheControl::L1S_L2C_L3UC:
150 case LoadCacheControl::L1S_L2C_L3C:
153 case LoadCacheControl::INVALIDATE_READ:
162static int32_t getL1CacheControl(StoreCacheControl cc) {
165 case StoreCacheControl::USE_DEFAULT:
168 case StoreCacheControl::L1WT_L2UC_L3UC:
169 case StoreCacheControl::L1WT_L2UC_L3WB:
170 case StoreCacheControl::L1WT_L2WB_L3UC:
171 case StoreCacheControl::L1WT_L2WB_L3WB:
174 case StoreCacheControl::L1WB_L2UC_L3UC:
175 case StoreCacheControl::L1WB_L2WB_L3UC:
176 case StoreCacheControl::L1WB_L2UC_L3WB:
179 case StoreCacheControl::L1S_L2UC_L3UC:
180 case StoreCacheControl::L1S_L2UC_L3WB:
181 case StoreCacheControl::L1S_L2WB_L3UC:
182 case StoreCacheControl::L1S_L2WB_L3WB:
191static int32_t getL3CacheControl(LoadCacheControl cc) {
194 case LoadCacheControl::USE_DEFAULT:
197 case LoadCacheControl::L1UC_L2UC_L3C:
198 case LoadCacheControl::L1UC_L2C_L3C:
199 case LoadCacheControl::L1C_L2UC_L3C:
200 case LoadCacheControl::L1C_L2C_L3C:
201 case LoadCacheControl::L1S_L2UC_L3C:
202 case LoadCacheControl::L1S_L2C_L3C:
205 case LoadCacheControl::INVALIDATE_READ:
214static int32_t getL3CacheControl(StoreCacheControl cc) {
217 case StoreCacheControl::USE_DEFAULT:
220 case StoreCacheControl::L1UC_L2UC_L3WB:
221 case StoreCacheControl::L1UC_L2WB_L3WB:
222 case StoreCacheControl::L1WT_L2UC_L3WB:
223 case StoreCacheControl::L1WT_L2WB_L3WB:
224 case StoreCacheControl::L1S_L2UC_L3WB:
225 case StoreCacheControl::L1S_L2WB_L3WB:
226 case StoreCacheControl::L1WB_L2UC_L3WB:
235static std::optional<LoadCacheControl> getCacheControl(PrefetchOp op) {
236 return op.getCacheControl();
239static std::optional<LoadCacheControl> getCacheControl(BlockLoad2dOp op) {
240 return op.getCacheControl();
243static std::optional<LoadCacheControl> getCacheControl(BlockLoadOp op) {
244 return op.getCacheControl();
247static std::optional<LoadCacheControl> getCacheControl(BlockPrefetch2dOp op) {
248 return op.getCacheControl();
251static std::optional<StoreCacheControl> getCacheControl(BlockStore2dOp op) {
252 return op.getCacheControl();
255static std::optional<StoreCacheControl> getCacheControl(BlockStoreOp op) {
256 return op.getCacheControl();
259static std::optional<LoadCacheControl> getCacheControl(LLVM::LoadOp op) {
260 if (op->hasAttr(
"cache_control")) {
261 auto attr = op->getAttrOfType<xevm::LoadCacheControlAttr>(
"cache_control");
264 return std::optional<LoadCacheControl>(attr.getValue());
269static std::optional<StoreCacheControl> getCacheControl(LLVM::StoreOp op) {
270 if (op->hasAttr(
"cache_control")) {
271 auto attr = op->getAttrOfType<xevm::StoreCacheControlAttr>(
"cache_control");
274 return std::optional<StoreCacheControl>(attr.getValue());
279template <
typename OpType>
280int32_t getL1CacheControl(OpType op) {
281 return getL1CacheControl(*getCacheControl(op));
284template <
typename OpType>
285int32_t getL3CacheControl(OpType op) {
286 return getL3CacheControl(*getCacheControl(op));
289template <
typename OpType>
290static std::optional<ArrayAttr>
291getCacheControlMetadata(ConversionPatternRewriter &rewriter, OpType op) {
292 if (!getCacheControl(op))
295 constexpr int32_t decorationCacheControlArity{3};
296 constexpr int32_t loadCacheControlKey{6442};
297 constexpr int32_t storeCacheControlKey{6443};
298 constexpr bool isLoad = std::is_same_v<OpType, BlockLoad2dOp> ||
299 std::is_same_v<OpType, BlockPrefetch2dOp> ||
300 std::is_same_v<OpType, LLVM::LoadOp> ||
301 std::is_same_v<OpType, BlockLoadOp> ||
302 std::is_same_v<OpType, PrefetchOp>;
308 assert(((getL1CacheControl<OpType>(op) == -1) ==
309 (getL3CacheControl<OpType>(op) == -1)) &&
310 "If one of L1 or L3 cache control is USE_DEFAULT, both must be "
313 if (getL1CacheControl<OpType>(op) == -1 &&
314 getL3CacheControl<OpType>(op) == -1)
316 const int32_t controlKey{isLoad ? loadCacheControlKey : storeCacheControlKey};
318 controlKey, 0, getL1CacheControl<OpType>(op)};
320 controlKey, 1, getL3CacheControl<OpType>(op)};
321 auto arrayAttrL1 = rewriter.getI32ArrayAttr(decorationsL1);
322 auto arrayAttrL3 = rewriter.getI32ArrayAttr(decorationsL3);
325 return rewriter.getArrayAttr(combinedAttrs);
347 llvm::StringMap<bool> seen;
350 auto arr = dyn_cast<ArrayAttr>(a);
354 auto vals = arr.getValue();
355 assert(vals.size() == 3 &&
356 "Expected exactly 3 integer values (Token, CacheLevel, "
357 "ControlValue) in cache control attribute.");
359 auto tokenAttr = dyn_cast<IntegerAttr>(vals[0]);
360 auto secondAttr = dyn_cast<IntegerAttr>(vals[1]);
361 auto thirdAttr = dyn_cast<IntegerAttr>(vals[2]);
363 if (!tokenAttr || !secondAttr || !thirdAttr)
369 llvm::formatv(
"{{{0}:\"{1},{2}\"}", tokenAttr.getValue().getZExtValue(),
370 secondAttr.getValue().getZExtValue(),
371 thirdAttr.getValue().getZExtValue());
374 if (!seen.insert({entry, true}).second)
377 payloads.push_back(std::move(entry));
382static std::atomic<uint64_t> globalNameCounter{0};
387static Value createMetadataStringPtr(ConversionPatternRewriter &rewriter,
389 StringRef value, StringRef nameHint) {
391 std::string strWithNull = value.str();
392 strWithNull.push_back(
'\0');
393 StringRef strRef(strWithNull.data(), strWithNull.size());
395 auto as1PtrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), 1);
399 if (
auto existingGlobal = dyn_cast<LLVM::GlobalOp>(&op)) {
400 if (!existingGlobal.getSection() ||
401 *existingGlobal.getSection() !=
"llvm.metadata")
404 dyn_cast_or_null<StringAttr>(existingGlobal.getValueOrNull())) {
405 if (strAttr.getValue() == strRef) {
406 return LLVM::AddressOfOp::create(rewriter, loc, as1PtrTy,
407 existingGlobal.getSymName());
414 auto i8Type = rewriter.getI8Type();
415 auto arrayType = LLVM::LLVMArrayType::get(i8Type, strWithNull.size());
416 std::string globalName =
417 llvm::formatv(
"{0}.{1}", nameHint,
418 globalNameCounter.fetch_add(1, std::memory_order_relaxed))
423 rewriter.setInsertionPointToStart(&moduleOp->
getRegion(0).
front());
426 LLVM::GlobalOp::create(rewriter, loc, arrayType,
427 true, LLVM::Linkage::Private,
428 globalName, rewriter.getStringAttr(strRef));
429 globalOp.setSection(StringRef(
"llvm.metadata"));
430 globalOp.setUnnamedAddr(LLVM::UnnamedAddr::Global);
431 globalOp.setAlignment(1);
432 globalOp.setAddrSpace(1);
436 return LLVM::AddressOfOp::create(rewriter, loc, as1PtrTy, globalName);
459static Value annotatePtrWithCacheControl(ConversionPatternRewriter &rewriter,
464 buildCacheControlPayloads(cacheControls.getValue());
465 if (payloads.empty())
468 auto ptrType = cast<LLVM::LLVMPointerType>(
ptr.getType());
469 auto as1PtrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), 1);
470 auto i32Ty = rewriter.getI32Type();
474 createMetadataStringPtr(rewriter, moduleOp, loc,
"",
".str.file");
475 Value lineVal = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 0);
476 Value nullAS1 = LLVM::ZeroOp::create(rewriter, loc, as1PtrTy);
481 for (
const std::string &payload : payloads) {
482 Value annStr = createMetadataStringPtr(rewriter, moduleOp, loc, payload,
483 ".str.cachecontrol");
484 auto annOp = LLVM::PtrAnnotation::create(rewriter, loc, ptrType, curPtr,
485 annStr, fileStr, lineVal, nullAS1);
486 curPtr = annOp.getResult();
508template <
typename OpType>
510applyCacheControlAnnotation(ConversionPatternRewriter &rewriter,
Location loc,
512 Operation *moduleOp,
unsigned ptrIdx = 0) {
513 std::optional<ArrayAttr> optCacheControls =
514 getCacheControlMetadata(rewriter, op);
515 if (!optCacheControls)
518 Value annotatedPtr = annotatePtrWithCacheControl(rewriter, loc, args[ptrIdx],
519 *optCacheControls, moduleOp);
520 args[ptrIdx] = annotatedPtr;
527static LLVM::CallOp createDeviceFunctionCall(
528 ConversionPatternRewriter &rewriter, StringRef funcName,
Type retType,
531 LLVMFuncAttributeOptions funcAttributeOptions,
Operation *op) {
533 assert(moduleOp &&
"Expecting module");
538 assert(!
failed(funcOpRes));
539 LLVM::LLVMFuncOp funcOp = funcOpRes.value();
540 funcOp.setCConv(LLVM::cconv::CConv::SPIR_FUNC);
541 funcOp.setConvergent(funcAttributeOptions.isConvergent);
542 funcOp.setNoUnwind(funcAttributeOptions.isNoUnwind);
543 funcOp.setWillReturn(funcAttributeOptions.isWillReturn);
545 if (funcAttributeOptions.memEffectsAttr)
546 funcOp.setMemoryEffectsAttr(funcAttributeOptions.memEffectsAttr);
548 for (
auto [idx, attrName] : paramAttrs)
549 funcOp.setArgAttr(idx, attrName, rewriter.getUnitAttr());
551 auto callOp = LLVM::CallOp::create(rewriter, loc, funcOp, args);
552 callOp->setAttrs(funcOp->getAttrs());
557static unsigned getNumOperandsPerDword(xevm::ElemType pTy) {
559 case xevm::ElemType::F32:
560 case xevm::ElemType::TF32:
562 case xevm::ElemType::BF16:
563 case xevm::ElemType::F16:
565 case xevm::ElemType::U8:
566 case xevm::ElemType::S8:
567 case xevm::ElemType::BF8:
568 case xevm::ElemType::F8:
570 case xevm::ElemType::E2M1:
571 case xevm::ElemType::U4:
572 case xevm::ElemType::S4:
575 llvm_unreachable(
"unsupported xevm::ElemType");
579class MMAToOCLPattern :
public OpConversionPattern<xevm::MMAOp> {
580 using OpConversionPattern::OpConversionPattern;
582 matchAndRewrite(xevm::MMAOp op, xevm::MMAOp::Adaptor adaptor,
583 ConversionPatternRewriter &rewriter)
const override {
585 return rewriter.notifyMatchFailure(op,
"OCL requires C operand");
587 auto precisionA = op.getTypes().getA();
588 auto precisionB = op.getTypes().getB();
589 auto precisionC = op.getTypes().getC();
590 auto precisionD = op.getTypes().getD();
591 if (precisionC != precisionD) {
592 return rewriter.notifyMatchFailure(op,
"type of C and D need to match");
594 if (precisionC != xevm::ElemType::S32 &&
595 precisionC != xevm::ElemType::F32 &&
596 precisionC != xevm::ElemType::F16 &&
597 precisionC != xevm::ElemType::BF16) {
598 return rewriter.notifyMatchFailure(
599 op,
"type of C and D must be S32, F32, F16 or BF16");
601 if (precisionA == xevm::ElemType::S32 ||
602 precisionA == xevm::ElemType::F32) {
603 return rewriter.notifyMatchFailure(op,
"type of A cannot be S32 or F32");
605 if (precisionB == xevm::ElemType::S32 ||
606 precisionB == xevm::ElemType::F32) {
607 return rewriter.notifyMatchFailure(op,
"type of B cannot be S32 or F32");
609 constexpr uint32_t bitWidthPackedA{16};
610 constexpr uint32_t bitWidthPackedB{32};
611 auto loc = op.getLoc();
613 auto castIfNeeded = [&](Value val, Type packedType) -> Value {
614 VectorType origTy = cast<VectorType>(val.
getType());
615 const uint32_t vecBitSize =
616 origTy.getNumElements() *
617 origTy.getElementType().getIntOrFloatBitWidth();
618 VectorType newTy = VectorType::get(
619 vecBitSize / packedType.getIntOrFloatBitWidth(), packedType);
621 val = LLVM::BitcastOp::create(rewriter, loc, newTy, val);
626 Type packedAType = (op.getTypes().getA() == xevm::ElemType::TF32)
627 ? cast<Type>(rewriter.getF32Type())
628 : rewriter.getIntegerType(bitWidthPackedA);
629 a = castIfNeeded(a, packedAType);
632 Type packedBType = (op.getTypes().getB() == xevm::ElemType::TF32)
633 ? cast<Type>(rewriter.getF32Type())
634 : rewriter.getIntegerType(bitWidthPackedB);
635 b = castIfNeeded(
b, packedBType);
638 VectorType cOrigTy = cast<VectorType>(c.
getType());
639 VectorType resOrigTy = cast<VectorType>(op->getResultTypes()[0]);
640 assert(cOrigTy == resOrigTy &&
"Accumulator and result type mismatch");
643 cOrigTy.getElementType().isBF16()
644 ? VectorType::get(cOrigTy.getShape(), rewriter.getIntegerType(16))
646 VectorType resTy = cTy;
648 c = LLVM::BitcastOp::create(rewriter, loc, cTy, c);
650 constexpr int32_t systolicDepth{8};
652 llvm::formatv(
"intel_sub_group_{0}_{1}_matrix_mad_k{2}",
653 stringifyElemType(op.getTypes().getA()).str(),
654 stringifyElemType(op.getTypes().getB()).str(),
656 getNumOperandsPerDword(op.getTypes().getA()))
658 SmallVector<Type> argTypes{a.
getType(),
b.getType(), cTy};
659 fnName = mangle(fnName, argTypes);
660 SmallVector<Value> args{a,
b, c};
662 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
663 LLVM::ModRefInfo::NoModRef,
664 LLVM::ModRefInfo::NoModRef,
665 LLVM::ModRefInfo::NoModRef,
666 LLVM::ModRefInfo::NoModRef,
667 LLVM::ModRefInfo::NoModRef,
668 LLVM::ModRefInfo::NoModRef);
669 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
670 funcAttrs.memEffectsAttr = memAttr;
672 createDeviceFunctionCall(rewriter, fnName, resTy, argTypes, args, {},
673 funcAttrs, op.getOperation())
676 if (resOrigTy != resTy)
677 result = LLVM::BitcastOp::create(rewriter, loc, resOrigTy,
result);
679 rewriter.replaceOp(op,
result);
684class PrefetchToOCLPattern :
public OpConversionPattern<PrefetchOp> {
685 using OpConversionPattern::OpConversionPattern;
687 matchAndRewrite(PrefetchOp op, PrefetchOp::Adaptor adaptor,
688 ConversionPatternRewriter &rewriter)
const override {
689 auto loc = op.getLoc();
692 const std::string fnName{
"_Z8prefetchPU3AS1Kcm"};
694 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), 1);
695 SmallVector<Value> args{op.getPtr(), one};
698 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
701 SmallVector<Type> argTypes;
702 for (
auto arg : args)
703 argTypes.push_back(arg.getType());
704 auto funcAttr = noUnwindAttrs;
705 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
706 LLVM::ModRefInfo::NoModRef,
707 LLVM::ModRefInfo::Ref,
708 LLVM::ModRefInfo::NoModRef,
709 LLVM::ModRefInfo::NoModRef,
710 LLVM::ModRefInfo::NoModRef,
711 LLVM::ModRefInfo::NoModRef);
712 funcAttr.memEffectsAttr = memAttr;
714 createDeviceFunctionCall(rewriter, fnName,
715 LLVM::LLVMVoidType::get(rewriter.getContext()),
716 argTypes, args, {}, funcAttr, op.getOperation());
717 rewriter.eraseOp(op);
722class MemfenceToOCLPattern :
public OpConversionPattern<MemfenceOp> {
723 using OpConversionPattern::OpConversionPattern;
725 matchAndRewrite(MemfenceOp op, MemfenceOp::Adaptor adaptor,
726 ConversionPatternRewriter &rewriter)
const override {
727 auto loc = op.getLoc();
728 const std::string fnName{
"atomic_work_item_fence"};
729 int memScope, addrSpace;
730 switch (op.getAddrspace()) {
731 case xevm::AddrSpace::SHARED:
734 case xevm::AddrSpace::GLOBAL:
739 return rewriter.notifyMatchFailure(
740 op,
"Fence only supports global and shared address spaces.");
742 switch (op.getScope()) {
743 case xevm::MemScope::WORKGROUP:
746 case xevm::MemScope::DEVICE:
751 return rewriter.notifyMatchFailure(
752 op,
"Fence only supports workgroup and device memory scopes.");
754 Type i32Type = rewriter.getI32Type();
755 Value acqRel = LLVM::ConstantOp::create(rewriter, loc, i32Type, 4);
756 Value memScopeConst =
757 LLVM::ConstantOp::create(rewriter, loc, i32Type, memScope);
758 Value addrSpaceConst =
759 LLVM::ConstantOp::create(rewriter, loc, i32Type, addrSpace);
760 SmallVector<Value> args{addrSpaceConst, acqRel, memScopeConst};
761 SmallVector<Type> argTypes{3, i32Type};
762 createDeviceFunctionCall(rewriter, mangle(fnName, argTypes),
763 LLVM::LLVMVoidType::get(rewriter.getContext()),
764 argTypes, args, {}, noUnwindAttrs,
766 rewriter.eraseOp(op);
770template <
typename OpType>
771class LoadStorePrefetchToOCLPattern :
public OpConversionPattern<OpType> {
772 using OpConversionPattern<OpType>::OpConversionPattern;
774 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
775 ConversionPatternRewriter &rewriter)
const override {
776 constexpr bool isLoad = std::is_same_v<OpType, BlockLoad2dOp>;
777 constexpr bool isPrefetch = std::is_same_v<OpType, BlockPrefetch2dOp>;
779 auto loc = op.getLoc();
780 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
782 bool packReg =
false;
783 bool transpose =
false;
784 if constexpr (isLoad) {
785 vecType = op.getRes().getType();
786 packReg = op.getPackRegister();
787 transpose = op.getTranspose();
788 }
else if constexpr (!isPrefetch) {
789 vecType = op.getStoredVal().getType();
792 auto i32Type = rewriter.getI32Type();
794 LLVM::UndefOp::create(rewriter, loc, VectorType::get(2, i32Type));
795 Value zero = LLVM::ConstantOp::create(rewriter, loc, i32Type, 0);
796 Value one = LLVM::ConstantOp::create(rewriter, loc, i32Type, 1);
797 byteCoord = LLVM::InsertElementOp::create(
798 rewriter, loc, VectorType::get(2, i32Type), byteCoord, op.getX(), zero);
799 byteCoord = LLVM::InsertElementOp::create(
800 rewriter, loc, VectorType::get(2, i32Type), byteCoord, op.getY(), one);
801 SmallVector<Value> args{op.getPtr(), op.getBaseWidth(), op.getBaseHeight(),
802 op.getBasePitch(), byteCoord};
805 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
808 SmallVector<Type> retTypes;
810 std::string funcName{
"intel_sub_group_2d_block_"};
811 std::string bitWidthId;
812 LLVMFuncAttributeOptions funcAttr{noUnwindWillReturnAttrs};
813 SmallVector<std::pair<unsigned, StringRef>, 4> paramAttrs;
814 if constexpr (isPrefetch) {
815 funcName +=
"prefetch";
816 paramAttrs = {std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName())};
817 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
818 LLVM::ModRefInfo::NoModRef,
819 LLVM::ModRefInfo::Ref,
820 LLVM::ModRefInfo::NoModRef,
821 LLVM::ModRefInfo::NoModRef,
822 LLVM::ModRefInfo::NoModRef,
823 LLVM::ModRefInfo::NoModRef);
824 funcAttr = noUnwindAttrs;
825 funcAttr.memEffectsAttr = memAttr;
827 auto vecElemType = vecType.getElementType();
828 auto vecElemBitWidth = vecElemType.getIntOrFloatBitWidth();
829 auto vecNumElems = vecType.getNumElements();
835 if (op.getElemSizeInBits() == 8 && op.getTileWidth() == 32) {
836 vecElemBitWidth = 16;
837 vecElemType = rewriter.getI16Type();
838 vecNumElems = vecNumElems / 2;
841 LLVM::ConstantOp::create(rewriter, loc, i32Type, vecNumElems);
842 auto dstOrSrcPtr = LLVM::AllocaOp::create(
843 rewriter, loc, LLVM::LLVMPointerType::get(rewriter.getContext()),
844 vecElemType, numElems);
845 args.push_back(dstOrSrcPtr);
846 if constexpr (isLoad) {
848 bitWidthId = getTypeMangling(vecElemType,
true);
850 funcName +=
"_transform";
852 funcName +=
"_transpose";
853 spvLoadDstPtr = dstOrSrcPtr;
854 retTypes.push_back(vecType);
856 std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName()),
857 std::make_pair(0, LLVM::LLVMDialect::getReadonlyAttrName()),
858 std::make_pair(5, LLVM::LLVMDialect::getNonNullAttrName()),
859 std::make_pair(5, LLVM::LLVMDialect::getWriteOnlyAttrName()),
863 bitWidthId = (vecElemBitWidth == 32)
865 : ((vecElemBitWidth == 16) ?
"t" :
"h");
866 LLVM::StoreOp::create(rewriter, loc, op.getStoredVal(), dstOrSrcPtr);
868 std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName()),
869 std::make_pair(0, LLVM::LLVMDialect::getWriteOnlyAttrName()),
870 std::make_pair(5, LLVM::LLVMDialect::getNonNullAttrName()),
871 std::make_pair(5, LLVM::LLVMDialect::getReadonlyAttrName()),
877 llvm::formatv(
"{0}_{1}b_{2}r{3}x{4}c", funcName, op.getElemSizeInBits(),
878 op.getTileHeight(), op.getTileWidth(), op.getVBlocks())
880 std::string prefetchCode(
"");
883 funcName = llvm::formatv(
"_Z{0}{1}PU3AS1viiiDv2_i{2}{3}", funcName.size(),
884 funcName, prefetchCode, bitWidthId)
886 SmallVector<Type> argTypes;
887 for (
auto arg : args) {
888 argTypes.push_back(arg.getType());
890 createDeviceFunctionCall(
891 rewriter, funcName, LLVM::LLVMVoidType::get(rewriter.getContext()),
892 argTypes, args, paramAttrs, funcAttr, op.getOperation());
894 if constexpr (isLoad)
896 op, LLVM::LoadOp::create(rewriter, loc, vecType, spvLoadDstPtr));
898 rewriter.eraseOp(op);
903template <
typename OpType>
904class BlockLoadStore1DToOCLPattern :
public OpConversionPattern<OpType> {
905 using OpConversionPattern<OpType>::OpConversionPattern;
907 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
908 ConversionPatternRewriter &rewriter)
const override {
909 constexpr bool isStore = std::is_same_v<OpType, xevm::BlockStoreOp>;
910 auto loc = op.getLoc();
911 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
916 std::string funcName{
"intel_sub_group_block_"};
919 if constexpr (isStore) {
920 funcName +=
"write_u";
921 valOrResTy = op.getVal().getType();
923 funcName +=
"read_u";
924 valOrResTy = op.getType();
927 VectorType vecTy = dyn_cast<VectorType>(valOrResTy);
928 Type elemType = vecTy ? vecTy.getElementType() : valOrResTy;
929 funcName += getTypeMangling(elemType);
931 funcName += std::to_string(vecTy.getNumElements());
932 SmallVector<Type, 2> argTypes{};
936 SmallVector<bool, 2> isUnsigned{};
940 SmallVector<Value, 2> args{};
941 args.push_back(op.getPtr());
942 argTypes.push_back(op.getPtr().getType());
943 isUnsigned.push_back(
true);
946 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
950 argTypes[0] = args[0].getType();
953 if constexpr (isStore) {
954 args.push_back(op.getVal());
955 argTypes.push_back(op.getVal().getType());
956 isUnsigned.push_back(
true);
957 retType = LLVM::LLVMVoidType::get(rewriter.getContext());
959 retType = valOrResTy;
961 funcName = std::string(
"_Z") + std::to_string(funcName.size()) + funcName +
963 std::to_string(op.getPtr().getType().getAddressSpace());
964 funcName += getTypeMangling(elemType,
true);
965 if constexpr (isStore)
966 funcName += getTypeMangling(valOrResTy,
true);
967 LLVMFuncAttributeOptions funcAttr{noUnwindWillReturnAttrs};
970 createDeviceFunctionCall(rewriter, funcName, retType, argTypes, args,
971 {}, funcAttr, op.getOperation());
973 if constexpr (isStore)
974 rewriter.eraseOp(op);
976 rewriter.replaceOp(op, call->getResult(0));
981template <
typename OpType>
982class LLVMLoadStoreToOCLPattern :
public OpConversionPattern<OpType> {
983 using OpConversionPattern<OpType>::OpConversionPattern;
985 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
986 ConversionPatternRewriter &rewriter)
const override {
987 if (!op->hasAttr(
"cache_control"))
990 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
991 std::optional<ArrayAttr> optCacheControls =
992 getCacheControlMetadata(rewriter, op);
993 if (!optCacheControls) {
994 rewriter.modifyOpInPlace(op, [&]() { op->removeAttr(
"cache_control"); });
999 constexpr bool isStore = std::is_same_v<OpType, LLVM::StoreOp>;
1000 unsigned ptrIdx = isStore ? 1 : 0;
1001 Value ptr = op->getOperand(ptrIdx);
1004 Value annotatedPtr = annotatePtrWithCacheControl(
1005 rewriter, op->getLoc(), ptr, *optCacheControls, moduleOp);
1008 rewriter.modifyOpInPlace(op, [&]() {
1009 op->setOperand(ptrIdx, annotatedPtr);
1010 op->removeAttr(
"cache_control");
1043static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdXOp) {
1044 return {
"get_local_id", 0};
1046static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdYOp) {
1047 return {
"get_local_id", 1};
1049static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdZOp) {
1050 return {
"get_local_id", 2};
1052static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimXOp) {
1053 return {
"get_local_size", 0};
1055static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimYOp) {
1056 return {
"get_local_size", 1};
1058static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimZOp) {
1059 return {
"get_local_size", 2};
1061static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdXOp) {
1062 return {
"get_group_id", 0};
1064static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdYOp) {
1065 return {
"get_group_id", 1};
1067static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdZOp) {
1068 return {
"get_group_id", 2};
1070static std::pair<StringRef, int64_t> getConfig(xevm::GridDimXOp) {
1071 return {
"get_num_groups", 0};
1073static std::pair<StringRef, int64_t> getConfig(xevm::GridDimYOp) {
1074 return {
"get_num_groups", 1};
1076static std::pair<StringRef, int64_t> getConfig(xevm::GridDimZOp) {
1077 return {
"get_num_groups", 2};
1081template <
typename OpType>
1082class LaunchConfigOpToOCLPattern :
public OpConversionPattern<OpType> {
1083 using OpConversionPattern<OpType>::OpConversionPattern;
1085 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1086 ConversionPatternRewriter &rewriter)
const override {
1087 Location loc = op->getLoc();
1088 auto [baseName, dim] = getConfig(op);
1089 Type dimTy = rewriter.getI32Type();
1090 Value dimVal = LLVM::ConstantOp::create(rewriter, loc, dimTy,
1091 static_cast<int64_t
>(dim));
1092 std::string func = mangle(baseName, {dimTy}, {
true});
1093 Type resTy = op.getType();
1095 createDeviceFunctionCall(rewriter, func, resTy, {dimTy}, {dimVal}, {},
1096 noUnwindWillReturnAttrs, op.getOperation());
1097 constexpr auto noModRef = LLVM::ModRefInfo::NoModRef;
1098 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1104 call.setMemoryEffectsAttr(memAttr);
1105 rewriter.replaceOp(op, call);
1122static StringRef getConfig(xevm::LaneIdOp) {
return "get_sub_group_local_id"; }
1123static StringRef getConfig(xevm::SubgroupIdOp) {
return "get_sub_group_id"; }
1124static StringRef getConfig(xevm::SubgroupSizeOp) {
1125 return "get_sub_group_size";
1127template <
typename OpType>
1128class SubgroupOpWorkitemOpToOCLPattern :
public OpConversionPattern<OpType> {
1129 using OpConversionPattern<OpType>::OpConversionPattern;
1131 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1132 ConversionPatternRewriter &rewriter)
const override {
1133 std::string func = mangle(getConfig(op).str(), {});
1134 Type resTy = op.getType();
1136 createDeviceFunctionCall(rewriter, func, resTy, {}, {}, {},
1137 noUnwindWillReturnAttrs, op.getOperation());
1138 constexpr auto noModRef = LLVM::ModRefInfo::NoModRef;
1139 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1145 call.setMemoryEffectsAttr(memAttr);
1146 rewriter.replaceOp(op, call);
1151class TruncfToOCLPattern :
public OpConversionPattern<TruncfOp> {
1152 using OpConversionPattern::OpConversionPattern;
1154 matchAndRewrite(TruncfOp op, TruncfOp::Adaptor adaptor,
1155 ConversionPatternRewriter &rewriter)
const override {
1157 auto srcEtype = op.getSrcEtype().getEtype();
1158 auto dstEtype = op.getDstEtype().getEtype();
1177 auto vecSrcTy = dyn_cast<VectorType>(op.getSrc().getType());
1179 return rewriter.notifyMatchFailure(op,
"Scalar src is not supported.");
1181 if (vecSrcTy.getNumElements() != 16)
1182 return rewriter.notifyMatchFailure(
1183 op,
"Only vector src of 16 elements is supported");
1184 auto vecDstTy = dyn_cast<VectorType>(op.getDst().getType());
1186 return rewriter.notifyMatchFailure(op,
"Scalar dst is not supported.");
1187 Value src = op.getSrc();
1188 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1189 LLVM::ModRefInfo::NoModRef,
1190 LLVM::ModRefInfo::NoModRef,
1191 LLVM::ModRefInfo::NoModRef,
1192 LLVM::ModRefInfo::NoModRef,
1193 LLVM::ModRefInfo::NoModRef,
1194 LLVM::ModRefInfo::NoModRef);
1195 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1196 funcAttrs.memEffectsAttr = memAttr;
1199 if (dstEtype == TruncfDstElemTypes::E2M1) {
1206 Value cast = LLVM::BitcastOp::create(
1207 rewriter, op.getLoc(), VectorType::get(8, rewriter.getI32Type()),
1210 std::string fnName =
"__builtin_IB_dnscl_";
1211 fnName += (srcEtype == TruncfSrcElemTypes::F16) ?
"hf16" :
"bf16";
1212 auto genDnscl = [&](Value input, Value idx0, Value idx1, Value dstTy,
1213 Value mode) -> Value {
1215 LLVM::ExtractElementOp::create(rewriter, op.getLoc(), input, idx0)
1218 LLVM::ExtractElementOp::create(rewriter, op.getLoc(), input, idx1)
1221 dstTy.getType(), mode.getType()};
1222 SmallVector<Value> args{arg1, arg2, dstTy, mode};
1223 Value dnscl = createDeviceFunctionCall(
1224 rewriter, fnName, rewriter.getI32Type(), argTypes,
1225 args, {}, funcAttrs, op.getOperation())
1230 Value zero = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1231 rewriter.getI32Type(), 0);
1232 Value one = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1233 rewriter.getI32Type(), 1);
1234 Value two = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1235 rewriter.getI32Type(), 2);
1236 Value three = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1237 rewriter.getI32Type(), 3);
1238 Value even = genDnscl(cast, zero, two, one, zero);
1239 Value odd = genDnscl(cast, one, three, one, two);
1240 Value firstHalf = LLVM::OrOp::create(rewriter, op.getLoc(), even, odd);
1241 Value four = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1242 rewriter.getI32Type(), 4);
1243 Value five = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1244 rewriter.getI32Type(), 5);
1245 Value six = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1246 rewriter.getI32Type(), 6);
1247 Value seven = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1248 rewriter.getI32Type(), 7);
1249 even = genDnscl(cast, four, six, one, zero);
1250 odd = genDnscl(cast, five, seven, one, two);
1251 Value secondHalf = LLVM::OrOp::create(rewriter, op.getLoc(), even, odd);
1254 Value combined = LLVM::UndefOp::create(
1255 rewriter, op.getLoc(), VectorType::get(2, rewriter.getI32Type()));
1256 combined = LLVM::InsertElementOp::create(rewriter, op.getLoc(), combined,
1259 combined = LLVM::InsertElementOp::create(rewriter, op.getLoc(), combined,
1263 LLVM::BitcastOp::create(rewriter, op.getLoc(), vecDstTy, combined);
1264 rewriter.replaceOp(op,
result);
1271 if (srcEtype == TruncfSrcElemTypes::BF16) {
1274 src = LLVM::BitcastOp::create(
1275 rewriter, op.getLoc(),
1276 VectorType::get(vecSrcTy.getShape(), rewriter.getI16Type()), src);
1277 std::string fnName =
"__builtin_IB_bftof_16";
1278 SmallVector<Type> argTypes{src.
getType()};
1279 SmallVector<Value> args{src};
1280 Type resTy = VectorType::get(vecSrcTy.getShape(), rewriter.getF32Type());
1281 src = createDeviceFunctionCall(rewriter, fnName, resTy, argTypes, args,
1282 {}, funcAttrs, op.getOperation())
1286 std::string truncFnName =
"convert_half16";
1287 SmallVector<Type> truncArgTypes{src.
getType()};
1288 SmallVector<Value> truncArgs{src};
1289 truncFnName = mangle(truncFnName, truncArgTypes);
1290 resTy = VectorType::get(vecSrcTy.getShape(), rewriter.getF16Type());
1292 createDeviceFunctionCall(rewriter, truncFnName, resTy, truncArgTypes,
1293 truncArgs, {}, funcAttrs, op.getOperation())
1296 if (dstEtype == TruncfDstElemTypes::BF8) {
1298 std::string fnName =
"__builtin_IB_hftobf8_16";
1299 SmallVector<Type> argTypes{src.
getType()};
1300 SmallVector<Value> args{src};
1302 createDeviceFunctionCall(rewriter, fnName, vecDstTy, argTypes, args,
1303 {}, funcAttrs, op.getOperation())
1306 rewriter.replaceOp(op,
result);
1307 }
else if (dstEtype == TruncfDstElemTypes::F8) {
1309 std::string fnName =
"__builtin_IB_hftohf8_16";
1310 SmallVector<Type> argTypes{src.
getType()};
1311 SmallVector<Value> args{src};
1313 createDeviceFunctionCall(rewriter, fnName, vecDstTy, argTypes, args,
1314 {}, funcAttrs, op.getOperation())
1317 rewriter.replaceOp(op,
result);
1319 return rewriter.notifyMatchFailure(
1320 op,
"Unsupported src, dst element type pair.");
1326class ExtfToOCLPattern :
public OpConversionPattern<ExtfOp> {
1327 using OpConversionPattern::OpConversionPattern;
1329 matchAndRewrite(ExtfOp op, ExtfOp::Adaptor adaptor,
1330 ConversionPatternRewriter &rewriter)
const override {
1333 auto srcEtype = op.getSrcEtype().getEtype();
1334 auto dstEtype = op.getDstEtype().getEtype();
1336 auto vecSrcTy = dyn_cast<VectorType>(op.getSrc().getType());
1338 return rewriter.notifyMatchFailure(op,
"Scalar src is not supported.");
1339 auto vecDstTy = dyn_cast<VectorType>(op.getDst().getType());
1341 return rewriter.notifyMatchFailure(op,
"Scalar dst is not supported.");
1342 Value src = op.getSrc();
1343 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1344 LLVM::ModRefInfo::NoModRef,
1345 LLVM::ModRefInfo::NoModRef,
1346 LLVM::ModRefInfo::NoModRef,
1347 LLVM::ModRefInfo::NoModRef,
1348 LLVM::ModRefInfo::NoModRef,
1349 LLVM::ModRefInfo::NoModRef);
1350 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1351 funcAttrs.memEffectsAttr = memAttr;
1354 if (srcEtype == ExtfSrcElemTypes::E2M1) {
1363 if (vecSrcTy.getNumElements() != 8 || vecDstTy.getNumElements() != 16)
1364 return rewriter.notifyMatchFailure(
1365 op,
"fp4 src expects a vector<8xi8> src and a 16 element dst");
1366 constexpr int kLutE2M1ToF16 = 7;
1367 constexpr int kLutE2M1ToBF16 = 5;
1369 (dstEtype == ExtfDstElemTypes::F16) ? kLutE2M1ToF16 : kLutE2M1ToBF16;
1370 Value lutIdx = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1371 rewriter.getI32Type(), lutIndex);
1372 Type lutTy = VectorType::get(16, rewriter.getI32Type());
1374 createDeviceFunctionCall(rewriter,
"__builtin_IB_shfl_idx4_lut",
1375 lutTy, {lutIdx.
getType()}, {lutIdx}, {},
1376 funcAttrs, op.getOperation())
1378 Type packedResTy = VectorType::get(8, rewriter.getI32Type());
1380 SmallVector<Value> convArgs{lut, src};
1382 createDeviceFunctionCall(
1383 rewriter,
"__builtin_IB_shfl_idx4_to_fp16_8_packed", packedResTy,
1384 convArgTypes, convArgs, {}, funcAttrs, op.getOperation())
1388 result = LLVM::BitcastOp::create(rewriter, op.getLoc(), vecDstTy,
result);
1389 rewriter.replaceOp(op,
result);
1395 if (vecSrcTy.getNumElements() != 16)
1396 return rewriter.notifyMatchFailure(
1397 op,
"Only vector src of 16 elements is supported");
1402 std::string fnName = (srcEtype == ExtfSrcElemTypes::BF8)
1403 ?
"__builtin_IB_bf8tohf_16"
1404 :
"__builtin_IB_hf8tohf_16";
1405 Type f16Ty = VectorType::get(vecSrcTy.getShape(), rewriter.getF16Type());
1406 SmallVector<Type> argTypes{src.
getType()};
1407 SmallVector<Value> args{src};
1409 createDeviceFunctionCall(rewriter, fnName, f16Ty, argTypes, args, {},
1410 funcAttrs, op.getOperation())
1414 if (dstEtype == ExtfDstElemTypes::F16) {
1415 rewriter.replaceOp(op,
result);
1423 std::string convFnName =
"convert_float16";
1424 SmallVector<Type> convArgTypes{
result.getType()};
1425 SmallVector<Value> convArgs{
result};
1426 convFnName = mangle(convFnName, convArgTypes);
1427 Type f32Ty = VectorType::get(vecSrcTy.getShape(), rewriter.getF32Type());
1429 createDeviceFunctionCall(rewriter, convFnName, f32Ty, convArgTypes,
1430 convArgs, {}, funcAttrs, op.getOperation())
1434 constexpr StringRef ftobfFnName =
"__builtin_IB_ftobf_16";
1435 SmallVector<Type> ftobfArgTypes{
result.getType()};
1436 SmallVector<Value> ftobfArgs{
result};
1437 Type i16Ty = VectorType::get(vecSrcTy.getShape(), rewriter.getI16Type());
1439 createDeviceFunctionCall(rewriter, ftobfFnName, i16Ty, ftobfArgTypes,
1440 ftobfArgs, {}, funcAttrs, op.getOperation())
1443 result = LLVM::BitcastOp::create(rewriter, op.getLoc(), vecDstTy,
result);
1444 rewriter.replaceOp(op,
result);
1449class MMAMxToOCLPattern :
public OpConversionPattern<MMAMxOp> {
1450 using OpConversionPattern::OpConversionPattern;
1452 matchAndRewrite(MMAMxOp op, MMAMxOp::Adaptor adaptor,
1453 ConversionPatternRewriter &rewriter)
const override {
1455 return rewriter.notifyMatchFailure(op,
"OCL requires C operand");
1457 auto precisionC = op.getTypes().getC();
1458 auto precisionD = op.getTypes().getD();
1459 if (precisionC != precisionD) {
1460 return rewriter.notifyMatchFailure(op,
"type of C and D need to match");
1463 constexpr uint32_t bitWidthPackedA{16};
1464 constexpr uint32_t bitWidthPackedB{32};
1465 auto loc = op.getLoc();
1467 auto castIfNeeded = [&](Value val, Type packedType) -> Value {
1468 VectorType origTy = cast<VectorType>(val.
getType());
1469 const uint32_t vecBitSize =
1470 origTy.getNumElements() *
1471 origTy.getElementType().getIntOrFloatBitWidth();
1472 VectorType newTy = VectorType::get(
1473 vecBitSize / packedType.getIntOrFloatBitWidth(), packedType);
1474 if (origTy != newTy)
1475 val = LLVM::BitcastOp::create(rewriter, loc, newTy, val);
1479 Value a = op.getA();
1480 Type packedAType = (op.getTypes().getA() == xevm::ElemType::TF32)
1481 ? cast<Type>(rewriter.getF32Type())
1482 : rewriter.getIntegerType(bitWidthPackedA);
1483 a = castIfNeeded(a, packedAType);
1485 Value
b = op.getB();
1486 Type packedBType = (op.getTypes().getB() == xevm::ElemType::TF32)
1487 ? cast<Type>(rewriter.getF32Type())
1488 : rewriter.getIntegerType(bitWidthPackedB);
1489 b = castIfNeeded(
b, packedBType);
1491 Value c = op.getC();
1492 VectorType cOrigTy = cast<VectorType>(c.
getType());
1493 VectorType resOrigTy = cast<VectorType>(op->getResultTypes()[0]);
1494 assert(cOrigTy == resOrigTy &&
"Accumulator and result type mismatch");
1497 cOrigTy.getElementType().isBF16()
1498 ? VectorType::get(cOrigTy.getShape(), rewriter.getIntegerType(16))
1500 VectorType resTy = cTy;
1502 c = LLVM::BitcastOp::create(rewriter, loc, cTy, c);
1504 std::string fnName =
1505 llvm::formatv(
"__builtin_IB_sub_group16_bdpas_{0}_{1}_{2}_{3}_8_8",
1506 builtinElemType(op.getTypes().getD()),
1507 builtinElemType(op.getTypes().getC()),
1508 builtinElemType(op.getTypes().getA()),
1509 builtinElemType(op.getTypes().getB()))
1511 auto scaleA = op.getScaleA();
1512 auto scaleB = op.getScaleB();
1513 SmallVector<Type> argTypes{cTy, a.
getType(),
b.getType(), scaleA.getType(),
1515 SmallVector<Value> args{c, a,
b, scaleA, scaleB};
1517 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1518 LLVM::ModRefInfo::NoModRef,
1519 LLVM::ModRefInfo::NoModRef,
1520 LLVM::ModRefInfo::NoModRef,
1521 LLVM::ModRefInfo::NoModRef,
1522 LLVM::ModRefInfo::NoModRef,
1523 LLVM::ModRefInfo::NoModRef);
1524 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1525 funcAttrs.memEffectsAttr = memAttr;
1527 createDeviceFunctionCall(rewriter, fnName, resTy, argTypes, args, {},
1528 funcAttrs, op.getOperation())
1531 if (resOrigTy != resTy)
1532 result = LLVM::BitcastOp::create(rewriter, loc, resOrigTy,
result);
1534 rewriter.replaceOp(op,
result);
1547class BitcastShuffleToGenISAPattern
1548 :
public OpConversionPattern<BitcastShuffleOp> {
1549 using OpConversionPattern::OpConversionPattern;
1551 matchAndRewrite(BitcastShuffleOp op, BitcastShuffleOp::Adaptor adaptor,
1552 ConversionPatternRewriter &rewriter)
const override {
1553 Type srcTy = op.getSrc().getType();
1554 Type resTy = op.getRes().getType();
1556 std::string fnName =
"llvm.genx.GenISA.SubgroupBitcastShuffle." +
1557 getGenISATypeMangling(resTy) +
"." +
1558 getGenISATypeMangling(srcTy);
1560 Value
result = createDeviceFunctionCall(
1561 rewriter, fnName, resTy, {srcTy}, {adaptor.getSrc()}, {},
1562 convergentNoUnwindWillReturnAttrs, op.getOperation())
1565 rewriter.replaceOp(op,
result);
1570class AllocaToGlobalPattern :
public OpConversionPattern<LLVM::AllocaOp> {
1571 using OpConversionPattern::OpConversionPattern;
1573 matchAndRewrite(LLVM::AllocaOp op, LLVM::AllocaOp::Adaptor adaptor,
1574 ConversionPatternRewriter &rewriter)
const override {
1575 auto ptrType = cast<LLVM::LLVMPointerType>(op.getType());
1576 auto addrSpace = ptrType.getAddressSpace();
1579 auto symTable = op->getParentWithTrait<OpTrait::SymbolTable>();
1583 if (ModuleOp mod = dyn_cast<ModuleOp>(*symTable)) {
1584 moduleBody = mod.getBody();
1585 }
else if (gpu::GPUModuleOp gpuMod =
1586 dyn_cast<gpu::GPUModuleOp>(*symTable)) {
1587 moduleBody = gpuMod.getBody();
1591 auto val = op.getArraySize();
1595 auto loc = op.getLoc();
1596 auto globalType = LLVM::LLVMArrayType::get(
1597 rewriter.getContext(), op.getElemType(), cst.getZExtValue());
1598 LLVM::GlobalOp globalVar;
1600 OpBuilder::InsertionGuard guard(rewriter);
1601 rewriter.setInsertionPointToStart(moduleBody);
1602 auto alignment = op.getAlignment();
1603 globalVar = LLVM::GlobalOp::create(
1604 rewriter, loc, globalType,
false,
1605 LLVM::Linkage::Internal,
1606 std::string(
"__global_alloca_") +
1607 std::to_string(getNextGlobalIdx()),
1609 alignment ? *alignment : 0, addrSpace);
1611 rewriter.replaceOpWithNewOp<LLVM::AddressOfOp>(op, globalVar);
1616 static unsigned getNextGlobalIdx() {
1617 static unsigned globalIdx = 0;
1628static bool isExtractingContiguousSlice(LLVM::ShuffleVectorOp op) {
1629 if (op.getV1() != op.getV2())
1631 auto maskAttr = op.getMask();
1633 int64_t sourceSize = op.getV1().getType().getNumElements();
1634 if (maskSize > sourceSize)
1636 int64_t firstIndex = maskAttr[0];
1637 for (
int64_t i = 1; i < maskSize; ++i) {
1639 if (
index != firstIndex + i)
1641 if (
index >= sourceSize)
1655class HandleVectorExtractPattern
1657 using OpRewritePattern<LLVM::ShuffleVectorOp>::OpRewritePattern;
1659 void initialize() { setHasBoundedRewriteRecursion(); }
1661 LogicalResult matchAndRewrite(LLVM::ShuffleVectorOp op,
1662 PatternRewriter &rewriter)
const override {
1664 if (!isExtractingContiguousSlice(op))
1667 auto mask = op.getMask();
1668 auto loc = op.getLoc();
1669 auto ty = op.getType();
1671 auto src = op.getV1();
1674 if (isa<LLVM::FPExtOp>(srcOp) || isa<LLVM::FPTruncOp>(srcOp)) {
1675 Value srcInput = srcOp->getOperand(0);
1677 auto srcVecTy = dyn_cast<VectorType>(srcInput.
getType());
1678 auto newShuffleVecTy =
1679 VectorType::get(mask.size(), srcVecTy.getElementType());
1680 auto newShuffle = LLVM::ShuffleVectorOp::create(
1681 rewriter, loc, newShuffleVecTy, srcInput, srcInput, mask);
1684 if (isa<LLVM::FPExtOp>(srcOp)) {
1685 newUnaryOp = LLVM::FPExtOp::create(rewriter, loc, ty, newShuffle);
1687 newUnaryOp = LLVM::FPTruncOp::create(rewriter, loc, ty, newShuffle);
1690 }
else if (isa<LLVM::BitcastOp>(srcOp)) {
1691 Value srcInput = srcOp->getOperand(0);
1693 auto srcInputVecTy = dyn_cast<VectorType>(srcInput.
getType());
1694 auto srcInputSize = srcInputVecTy.getNumElements();
1695 auto srcResVecTy = dyn_cast<VectorType>(srcOp->getResult(0).getType());
1696 auto srcResSize = srcResVecTy.getNumElements();
1697 auto maskSize =
static_cast<int32_t
>(mask.size());
1698 if (srcInputSize > srcResSize) {
1701 if (srcResSize % srcInputSize != 0) {
1704 auto maskScale = srcResSize / srcInputSize;
1705 if (maskScale != 1) {
1706 if (mask[0] % maskScale != 0) {
1710 SmallVector<int32_t> newMask;
1711 int32_t newMaskSize = maskSize / maskScale;
1712 int32_t maskStart = mask[0] / maskScale;
1713 for (int32_t i = 0; i < newMaskSize; ++i) {
1714 newMask.push_back(maskStart + i);
1718 auto newShuffleVecTy =
1719 VectorType::get(srcInputSize, srcInputVecTy.getElementType());
1720 auto newShuffle = LLVM::ShuffleVectorOp::create(
1721 rewriter, loc, newShuffleVecTy, srcInput, srcInput, mask);
1724 LLVM::BitcastOp::create(rewriter, loc, ty, newShuffle);
1726 }
else if (isa<LLVM::ShuffleVectorOp>(srcOp)) {
1731 auto srcShuffle = cast<LLVM::ShuffleVectorOp>(srcOp);
1732 if (!isExtractingContiguousSlice(srcShuffle))
1734 auto srcMask = srcShuffle.getMask();
1735 SmallVector<int32_t> combinedMask;
1736 for (
auto index : mask) {
1737 combinedMask.push_back(srcMask[index]);
1739 auto newShuffle = LLVM::ShuffleVectorOp::create(
1740 rewriter, loc, ty, srcShuffle.getV1(), srcShuffle.getV1(),
1743 }
else if (isa<LLVM::LoadOp>(srcOp)) {
1745 auto loadOp = cast<LLVM::LoadOp>(srcOp);
1746 auto loadPtr = loadOp.getAddr();
1747 auto loadAddrSpace = loadPtr.getType().getAddressSpace();
1748 if (loadAddrSpace != 0)
1750 auto loadTy = dyn_cast<VectorType>(loadOp.getType());
1751 auto elemTy = loadTy.getElementType();
1752 auto firstIndex = mask[0];
1753 auto newVecTy = VectorType::get(mask.size(), elemTy);
1756 auto newPtr = LLVM::GEPOp::create(
1758 LLVM::LLVMPointerType::get(rewriter.
getContext(), loadAddrSpace),
1759 elemTy, loadPtr, ArrayRef<LLVM::GEPArg>{firstIndex});
1760 auto newLoad = LLVM::LoadOp::create(rewriter, loc, newVecTy, newPtr);
1763 auto newLoad = LLVM::LoadOp::create(rewriter, loc, newVecTy, loadPtr);
1781struct ConvertXeVMToLLVMPass
1785 void getDependentDialects(DialectRegistry ®istry)
const override {
1786 registry.
insert<LLVM::LLVMDialect, XeVMDialect>();
1789 void runOnOperation()
override {
1793 if (
failed(applyPartialConversion(getOperation(),
target,
1794 std::move(patterns))))
1795 signalPassFailure();
1799 RewritePatternSet vectorPatterns(&
getContext());
1800 vectorPatterns.add<HandleVectorExtractPattern>(&
getContext());
1801 GreedyRewriteConfig config{};
1806 config.enableFolding(
false);
1823 target.addDynamicallyLegalDialect<LLVM::LLVMDialect>([](
Operation *op) {
1827 if (isa<LLVM::AllocaOp>(op)) {
1828 LLVM::AllocaOp aOp = cast<LLVM::AllocaOp>(op);
1829 LLVM::LLVMPointerType pTy = cast<LLVM::LLVMPointerType>(aOp.getType());
1830 auto addrSpace = pTy.getAddressSpace();
1831 return addrSpace != 3;
1834 return !op->hasAttr(
"cache_control");
1836 target.addIllegalDialect<XeVMDialect>();
1837 patterns.
add<LoadStorePrefetchToOCLPattern<BlockLoad2dOp>,
1838 LoadStorePrefetchToOCLPattern<BlockStore2dOp>,
1839 LoadStorePrefetchToOCLPattern<BlockPrefetch2dOp>,
1840 MMAToOCLPattern, MemfenceToOCLPattern, PrefetchToOCLPattern,
1841 LLVMLoadStoreToOCLPattern<LLVM::LoadOp>,
1842 LLVMLoadStoreToOCLPattern<LLVM::StoreOp>,
1843 BlockLoadStore1DToOCLPattern<BlockLoadOp>,
1844 BlockLoadStore1DToOCLPattern<BlockStoreOp>,
1845 LaunchConfigOpToOCLPattern<WorkitemIdXOp>,
1846 LaunchConfigOpToOCLPattern<WorkitemIdYOp>,
1847 LaunchConfigOpToOCLPattern<WorkitemIdZOp>,
1848 LaunchConfigOpToOCLPattern<WorkgroupDimXOp>,
1849 LaunchConfigOpToOCLPattern<WorkgroupDimYOp>,
1850 LaunchConfigOpToOCLPattern<WorkgroupDimZOp>,
1851 LaunchConfigOpToOCLPattern<WorkgroupIdXOp>,
1852 LaunchConfigOpToOCLPattern<WorkgroupIdYOp>,
1853 LaunchConfigOpToOCLPattern<WorkgroupIdZOp>,
1854 LaunchConfigOpToOCLPattern<GridDimXOp>,
1855 LaunchConfigOpToOCLPattern<GridDimYOp>,
1856 LaunchConfigOpToOCLPattern<GridDimZOp>,
1857 SubgroupOpWorkitemOpToOCLPattern<LaneIdOp>,
1858 SubgroupOpWorkitemOpToOCLPattern<SubgroupIdOp>,
1859 SubgroupOpWorkitemOpToOCLPattern<SubgroupSizeOp>,
1860 TruncfToOCLPattern, ExtfToOCLPattern, MMAMxToOCLPattern,
1861 BitcastShuffleToGenISAPattern, AllocaToGlobalPattern>(
LogicalResult initialize(unsigned origNumLoops, ArrayRef< ReassociationIndices > foldedIterationDims)
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...
RAII guard to reset the insertion point of the builder when destroyed.
A trait used to provide symbol table functionalities to a region operation.
Operation is the basic unit of execution within MLIR.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
Location getLoc()
The source location the operation was defined or derived from.
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...
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...
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...