18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/Support/FormatVariadic.h"
21#include "llvm/Support/MathExtras.h"
28#include "llvm/ADT/TypeSwitch.h"
31#define GEN_PASS_DEF_CONVERTXEVMTOLLVMPASS
32#include "mlir/Conversion/Passes.h.inc"
40struct LLVMFuncAttributeOptions {
41 bool isConvergent =
false;
42 bool isNoUnwind =
false;
43 bool isWillReturn =
false;
44 LLVM::MemoryEffectsAttr memEffectsAttr{};
46static constexpr LLVMFuncAttributeOptions noUnwindAttrs = {
47 false,
true,
false, {}};
48static constexpr LLVMFuncAttributeOptions noUnwindWillReturnAttrs = {
49 false,
true,
true, {}};
50static constexpr LLVMFuncAttributeOptions convergentNoUnwindWillReturnAttrs = {
51 true,
true,
true, {}};
53std::string getTypeMangling(
Type ty,
bool isUnsigned =
false) {
55 .Case([isUnsigned](VectorType ty) -> std::string {
56 return "Dv" + std::to_string(ty.getNumElements()) +
"_" +
57 getTypeMangling(ty.getElementType(), isUnsigned);
59 .Case([](Float16Type) -> std::string {
return "Dh"; })
60 .Case([](Float32Type) -> std::string {
return "f"; })
61 .Case([](Float64Type) -> std::string {
return "d"; })
62 .Case([isUnsigned](IntegerType ty) -> std::string {
63 switch (ty.getWidth()) {
65 return isUnsigned ?
"h" :
"c";
67 return isUnsigned ?
"t" :
"s";
69 return isUnsigned ?
"j" :
"i";
71 return isUnsigned ?
"m" :
"l";
73 llvm_unreachable(
"unhandled integer type");
76 .DefaultUnreachable(
"unhandled type for mangling");
81 assert((isUnsigned.empty() || isUnsigned.size() == types.size()) &&
82 "Signedness info doesn't match");
84 llvm::raw_string_ostream os(s);
85 llvm::SmallDenseMap<Type, unsigned> substitutions;
86 os <<
"_Z" << baseName.size() << baseName;
87 for (
auto [idx, type] : llvm::enumerate(types)) {
88 auto it = substitutions.find(type);
89 if (it != substitutions.end()) {
92 if (
unsigned firstIdx = it->getSecond(); firstIdx > 0)
96 if (!type.isIntOrFloat())
97 substitutions[type] = substitutions.size();
98 os << getTypeMangling(type, isUnsigned.empty() ?
false : isUnsigned[idx]);
108std::string getGenISATypeMangling(
Type ty) {
110 .Case([](VectorType ty) -> std::string {
111 return "v" + std::to_string(ty.getNumElements()) +
112 getGenISATypeMangling(ty.getElementType());
114 .Case([](IntegerType ty) -> std::string {
115 return "i" + std::to_string(ty.getWidth());
117 .DefaultUnreachable(
"unhandled type for GenISA mangling");
120std::string builtinElemType(ElemType elemType) {
133 return stringifyElemType(elemType).str();
137static int32_t getL1CacheControl(LoadCacheControl cc) {
140 case LoadCacheControl::USE_DEFAULT:
143 case LoadCacheControl::L1C_L2UC_L3UC:
144 case LoadCacheControl::L1C_L2UC_L3C:
145 case LoadCacheControl::L1C_L2C_L3UC:
146 case LoadCacheControl::L1C_L2C_L3C:
149 case LoadCacheControl::L1S_L2UC_L3UC:
150 case LoadCacheControl::L1S_L2UC_L3C:
151 case LoadCacheControl::L1S_L2C_L3UC:
152 case LoadCacheControl::L1S_L2C_L3C:
155 case LoadCacheControl::INVALIDATE_READ:
164static int32_t getL1CacheControl(StoreCacheControl cc) {
167 case StoreCacheControl::USE_DEFAULT:
170 case StoreCacheControl::L1WT_L2UC_L3UC:
171 case StoreCacheControl::L1WT_L2UC_L3WB:
172 case StoreCacheControl::L1WT_L2WB_L3UC:
173 case StoreCacheControl::L1WT_L2WB_L3WB:
176 case StoreCacheControl::L1WB_L2UC_L3UC:
177 case StoreCacheControl::L1WB_L2WB_L3UC:
178 case StoreCacheControl::L1WB_L2UC_L3WB:
181 case StoreCacheControl::L1S_L2UC_L3UC:
182 case StoreCacheControl::L1S_L2UC_L3WB:
183 case StoreCacheControl::L1S_L2WB_L3UC:
184 case StoreCacheControl::L1S_L2WB_L3WB:
193static int32_t getL3CacheControl(LoadCacheControl cc) {
196 case LoadCacheControl::USE_DEFAULT:
199 case LoadCacheControl::L1UC_L2UC_L3C:
200 case LoadCacheControl::L1UC_L2C_L3C:
201 case LoadCacheControl::L1C_L2UC_L3C:
202 case LoadCacheControl::L1C_L2C_L3C:
203 case LoadCacheControl::L1S_L2UC_L3C:
204 case LoadCacheControl::L1S_L2C_L3C:
207 case LoadCacheControl::INVALIDATE_READ:
216static int32_t getL3CacheControl(StoreCacheControl cc) {
219 case StoreCacheControl::USE_DEFAULT:
222 case StoreCacheControl::L1UC_L2UC_L3WB:
223 case StoreCacheControl::L1UC_L2WB_L3WB:
224 case StoreCacheControl::L1WT_L2UC_L3WB:
225 case StoreCacheControl::L1WT_L2WB_L3WB:
226 case StoreCacheControl::L1S_L2UC_L3WB:
227 case StoreCacheControl::L1S_L2WB_L3WB:
228 case StoreCacheControl::L1WB_L2UC_L3WB:
237static std::optional<LoadCacheControl> getCacheControl(PrefetchOp op) {
238 return op.getCacheControl();
241static std::optional<LoadCacheControl> getCacheControl(BlockLoad2dOp op) {
242 return op.getCacheControl();
245static std::optional<LoadCacheControl> getCacheControl(BlockLoadOp op) {
246 return op.getCacheControl();
249static std::optional<LoadCacheControl> getCacheControl(BlockPrefetch2dOp op) {
250 return op.getCacheControl();
253static std::optional<StoreCacheControl> getCacheControl(BlockStore2dOp op) {
254 return op.getCacheControl();
257static std::optional<StoreCacheControl> getCacheControl(BlockStoreOp op) {
258 return op.getCacheControl();
261static std::optional<LoadCacheControl> getCacheControl(LLVM::LoadOp op) {
262 if (op->hasDiscardableAttr(
"cache_control")) {
263 auto attr = op->getDiscardableAttrOfType<xevm::LoadCacheControlAttr>(
267 return std::optional<LoadCacheControl>(attr.getValue());
272static std::optional<StoreCacheControl> getCacheControl(LLVM::StoreOp op) {
273 if (op->hasDiscardableAttr(
"cache_control")) {
274 auto attr = op->getDiscardableAttrOfType<xevm::StoreCacheControlAttr>(
278 return std::optional<StoreCacheControl>(attr.getValue());
283template <
typename OpType>
284int32_t getL1CacheControl(OpType op) {
285 return getL1CacheControl(*getCacheControl(op));
288template <
typename OpType>
289int32_t getL3CacheControl(OpType op) {
290 return getL3CacheControl(*getCacheControl(op));
293template <
typename OpType>
294static std::optional<ArrayAttr>
295getCacheControlMetadata(ConversionPatternRewriter &rewriter, OpType op) {
296 if (!getCacheControl(op))
299 constexpr int32_t decorationCacheControlArity{3};
300 constexpr int32_t loadCacheControlKey{6442};
301 constexpr int32_t storeCacheControlKey{6443};
302 constexpr bool isLoad = std::is_same_v<OpType, BlockLoad2dOp> ||
303 std::is_same_v<OpType, BlockPrefetch2dOp> ||
304 std::is_same_v<OpType, LLVM::LoadOp> ||
305 std::is_same_v<OpType, BlockLoadOp> ||
306 std::is_same_v<OpType, PrefetchOp>;
312 assert(((getL1CacheControl<OpType>(op) == -1) ==
313 (getL3CacheControl<OpType>(op) == -1)) &&
314 "If one of L1 or L3 cache control is USE_DEFAULT, both must be "
317 if (getL1CacheControl<OpType>(op) == -1 &&
318 getL3CacheControl<OpType>(op) == -1)
320 const int32_t controlKey{isLoad ? loadCacheControlKey : storeCacheControlKey};
322 controlKey, 0, getL1CacheControl<OpType>(op)};
324 controlKey, 1, getL3CacheControl<OpType>(op)};
325 auto arrayAttrL1 = rewriter.getI32ArrayAttr(decorationsL1);
326 auto arrayAttrL3 = rewriter.getI32ArrayAttr(decorationsL3);
329 return rewriter.getArrayAttr(combinedAttrs);
351 llvm::StringMap<bool> seen;
354 auto arr = dyn_cast<ArrayAttr>(a);
358 auto vals = arr.getValue();
359 assert(vals.size() == 3 &&
360 "Expected exactly 3 integer values (Token, CacheLevel, "
361 "ControlValue) in cache control attribute.");
363 auto tokenAttr = dyn_cast<IntegerAttr>(vals[0]);
364 auto secondAttr = dyn_cast<IntegerAttr>(vals[1]);
365 auto thirdAttr = dyn_cast<IntegerAttr>(vals[2]);
367 if (!tokenAttr || !secondAttr || !thirdAttr)
373 llvm::formatv(
"{{{0}:\"{1},{2}\"}", tokenAttr.getValue().getZExtValue(),
374 secondAttr.getValue().getZExtValue(),
375 thirdAttr.getValue().getZExtValue());
378 if (!seen.insert({entry, true}).second)
381 payloads.push_back(std::move(entry));
386static std::atomic<uint64_t> globalNameCounter{0};
391static Value createMetadataStringPtr(ConversionPatternRewriter &rewriter,
393 StringRef value, StringRef nameHint) {
395 std::string strWithNull = value.str();
396 strWithNull.push_back(
'\0');
397 StringRef strRef(strWithNull.data(), strWithNull.size());
399 auto as1PtrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), 1);
403 if (
auto existingGlobal = dyn_cast<LLVM::GlobalOp>(&op)) {
404 if (!existingGlobal.getSection() ||
405 *existingGlobal.getSection() !=
"llvm.metadata")
408 dyn_cast_or_null<StringAttr>(existingGlobal.getValueOrNull())) {
409 if (strAttr.getValue() == strRef) {
410 return LLVM::AddressOfOp::create(rewriter, loc, as1PtrTy,
411 existingGlobal.getSymName());
418 auto i8Type = rewriter.getI8Type();
419 auto arrayType = LLVM::LLVMArrayType::get(i8Type, strWithNull.size());
420 std::string globalName =
421 llvm::formatv(
"{0}.{1}", nameHint,
422 globalNameCounter.fetch_add(1, std::memory_order_relaxed))
427 rewriter.setInsertionPointToStart(&moduleOp->
getRegion(0).
front());
430 LLVM::GlobalOp::create(rewriter, loc, arrayType,
431 true, LLVM::Linkage::Private,
432 globalName, rewriter.getStringAttr(strRef));
433 globalOp.setSection(StringRef(
"llvm.metadata"));
434 globalOp.setUnnamedAddr(LLVM::UnnamedAddr::Global);
435 globalOp.setAlignment(1);
436 globalOp.setAddrSpace(1);
440 return LLVM::AddressOfOp::create(rewriter, loc, as1PtrTy, globalName);
463static Value annotatePtrWithCacheControl(ConversionPatternRewriter &rewriter,
468 buildCacheControlPayloads(cacheControls.getValue());
469 if (payloads.empty())
472 auto ptrType = cast<LLVM::LLVMPointerType>(
ptr.getType());
473 auto as1PtrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), 1);
474 auto i32Ty = rewriter.getI32Type();
478 createMetadataStringPtr(rewriter, moduleOp, loc,
"",
".str.file");
479 Value lineVal = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 0);
480 Value nullAS1 = LLVM::ZeroOp::create(rewriter, loc, as1PtrTy);
485 for (
const std::string &payload : payloads) {
486 Value annStr = createMetadataStringPtr(rewriter, moduleOp, loc, payload,
487 ".str.cachecontrol");
488 auto annOp = LLVM::PtrAnnotation::create(rewriter, loc, ptrType, curPtr,
489 annStr, fileStr, lineVal, nullAS1);
490 curPtr = annOp.getResult();
512template <
typename OpType>
514applyCacheControlAnnotation(ConversionPatternRewriter &rewriter,
Location loc,
516 Operation *moduleOp,
unsigned ptrIdx = 0) {
517 std::optional<ArrayAttr> optCacheControls =
518 getCacheControlMetadata(rewriter, op);
519 if (!optCacheControls)
522 Value annotatedPtr = annotatePtrWithCacheControl(rewriter, loc, args[ptrIdx],
523 *optCacheControls, moduleOp);
524 args[ptrIdx] = annotatedPtr;
531static LLVM::CallOp createDeviceFunctionCall(
532 ConversionPatternRewriter &rewriter, StringRef funcName,
Type retType,
535 LLVMFuncAttributeOptions funcAttributeOptions,
Operation *op) {
537 assert(moduleOp &&
"Expecting module");
542 assert(!
failed(funcOpRes));
543 LLVM::LLVMFuncOp funcOp = funcOpRes.value();
544 funcOp.setCConv(LLVM::cconv::CConv::SPIR_FUNC);
545 funcOp.setConvergent(funcAttributeOptions.isConvergent);
546 funcOp.setNoUnwind(funcAttributeOptions.isNoUnwind);
547 funcOp.setWillReturn(funcAttributeOptions.isWillReturn);
549 if (funcAttributeOptions.memEffectsAttr)
550 funcOp.setMemoryEffectsAttr(funcAttributeOptions.memEffectsAttr);
552 for (
auto [idx, attrName] : paramAttrs)
553 funcOp.setArgAttr(idx, attrName, rewriter.getUnitAttr());
555 auto callOp = LLVM::CallOp::create(rewriter, loc, funcOp, args);
557 auto copyAttr = [&](StringAttr name,
Attribute attr) {
558 if (callOp->getInherentAttr(name).has_value())
559 callOp->setInherentAttr(name, attr);
561 discardableAttrs.emplace_back(name, attr);
563 for (
NamedAttribute attr : funcOp->getDiscardableAttrDictionary())
564 copyAttr(attr.getName(), attr.getValue());
565 funcOp->getName().walkInherentAttrs(
566 funcOp, [&](StringRef name,
Attribute &attr) {
567 copyAttr(rewriter.getStringAttr(name), attr);
569 callOp->setDiscardableAttrs(discardableAttrs);
574static unsigned getNumOperandsPerDword(xevm::ElemType pTy) {
576 case xevm::ElemType::F32:
577 case xevm::ElemType::TF32:
579 case xevm::ElemType::BF16:
580 case xevm::ElemType::F16:
582 case xevm::ElemType::U8:
583 case xevm::ElemType::S8:
584 case xevm::ElemType::BF8:
585 case xevm::ElemType::F8:
587 case xevm::ElemType::E2M1:
588 case xevm::ElemType::U4:
589 case xevm::ElemType::S4:
592 llvm_unreachable(
"unsupported xevm::ElemType");
596class MMAToOCLPattern :
public OpConversionPattern<xevm::MMAOp> {
597 using OpConversionPattern::OpConversionPattern;
599 matchAndRewrite(xevm::MMAOp op, xevm::MMAOp::Adaptor adaptor,
600 ConversionPatternRewriter &rewriter)
const override {
602 return rewriter.notifyMatchFailure(op,
"OCL requires C operand");
604 auto precisionA = op.getTypes().getA();
605 auto precisionB = op.getTypes().getB();
606 auto precisionC = op.getTypes().getC();
607 auto precisionD = op.getTypes().getD();
608 if (precisionC != precisionD) {
609 return rewriter.notifyMatchFailure(op,
"type of C and D need to match");
611 if (precisionC != xevm::ElemType::S32 &&
612 precisionC != xevm::ElemType::F32 &&
613 precisionC != xevm::ElemType::F16 &&
614 precisionC != xevm::ElemType::BF16) {
615 return rewriter.notifyMatchFailure(
616 op,
"type of C and D must be S32, F32, F16 or BF16");
618 if (precisionA == xevm::ElemType::S32 ||
619 precisionA == xevm::ElemType::F32) {
620 return rewriter.notifyMatchFailure(op,
"type of A cannot be S32 or F32");
622 if (precisionB == xevm::ElemType::S32 ||
623 precisionB == xevm::ElemType::F32) {
624 return rewriter.notifyMatchFailure(op,
"type of B cannot be S32 or F32");
626 constexpr uint32_t bitWidthPackedA{16};
627 constexpr uint32_t bitWidthPackedB{32};
628 auto loc = op.getLoc();
630 auto castIfNeeded = [&](Value val, Type packedType) -> Value {
631 VectorType origTy = cast<VectorType>(val.
getType());
632 const uint32_t vecBitSize =
633 origTy.getNumElements() *
634 origTy.getElementType().getIntOrFloatBitWidth();
635 VectorType newTy = VectorType::get(
636 vecBitSize / packedType.getIntOrFloatBitWidth(), packedType);
638 val = LLVM::BitcastOp::create(rewriter, loc, newTy, val);
643 Type packedAType = (op.getTypes().getA() == xevm::ElemType::TF32)
644 ? cast<Type>(rewriter.getF32Type())
645 : rewriter.getIntegerType(bitWidthPackedA);
646 a = castIfNeeded(a, packedAType);
649 Type packedBType = (op.getTypes().getB() == xevm::ElemType::TF32)
650 ? cast<Type>(rewriter.getF32Type())
651 : rewriter.getIntegerType(bitWidthPackedB);
652 b = castIfNeeded(
b, packedBType);
655 VectorType cOrigTy = cast<VectorType>(c.
getType());
656 VectorType resOrigTy = cast<VectorType>(op->getResultTypes()[0]);
657 assert(cOrigTy == resOrigTy &&
"Accumulator and result type mismatch");
660 cOrigTy.getElementType().isBF16()
661 ? VectorType::get(cOrigTy.getShape(), rewriter.getIntegerType(16))
663 VectorType resTy = cTy;
665 c = LLVM::BitcastOp::create(rewriter, loc, cTy, c);
667 constexpr int32_t systolicDepth{8};
669 llvm::formatv(
"intel_sub_group_{0}_{1}_matrix_mad_k{2}",
670 stringifyElemType(op.getTypes().getA()).str(),
671 stringifyElemType(op.getTypes().getB()).str(),
673 getNumOperandsPerDword(op.getTypes().getA()))
675 SmallVector<Type> argTypes{a.
getType(),
b.getType(), cTy};
676 fnName = mangle(fnName, argTypes);
677 SmallVector<Value> args{a,
b, c};
679 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
680 LLVM::ModRefInfo::NoModRef,
681 LLVM::ModRefInfo::NoModRef,
682 LLVM::ModRefInfo::NoModRef,
683 LLVM::ModRefInfo::NoModRef,
684 LLVM::ModRefInfo::NoModRef,
685 LLVM::ModRefInfo::NoModRef);
686 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
687 funcAttrs.memEffectsAttr = memAttr;
689 createDeviceFunctionCall(rewriter, fnName, resTy, argTypes, args, {},
690 funcAttrs, op.getOperation())
693 if (resOrigTy != resTy)
694 result = LLVM::BitcastOp::create(rewriter, loc, resOrigTy,
result);
696 rewriter.replaceOp(op,
result);
701class PrefetchToOCLPattern :
public OpConversionPattern<PrefetchOp> {
702 using OpConversionPattern::OpConversionPattern;
704 matchAndRewrite(PrefetchOp op, PrefetchOp::Adaptor adaptor,
705 ConversionPatternRewriter &rewriter)
const override {
706 auto loc = op.getLoc();
709 const std::string fnName{
"_Z8prefetchPU3AS1Kcm"};
711 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), 1);
712 SmallVector<Value> args{op.getPtr(), one};
715 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
718 SmallVector<Type> argTypes;
719 for (
auto arg : args)
720 argTypes.push_back(arg.getType());
721 auto funcAttr = noUnwindAttrs;
722 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
723 LLVM::ModRefInfo::NoModRef,
724 LLVM::ModRefInfo::Ref,
725 LLVM::ModRefInfo::NoModRef,
726 LLVM::ModRefInfo::NoModRef,
727 LLVM::ModRefInfo::NoModRef,
728 LLVM::ModRefInfo::NoModRef);
729 funcAttr.memEffectsAttr = memAttr;
731 createDeviceFunctionCall(rewriter, fnName,
732 LLVM::LLVMVoidType::get(rewriter.getContext()),
733 argTypes, args, {}, funcAttr, op.getOperation());
734 rewriter.eraseOp(op);
739class MemfenceToOCLPattern :
public OpConversionPattern<MemfenceOp> {
740 using OpConversionPattern::OpConversionPattern;
742 matchAndRewrite(MemfenceOp op, MemfenceOp::Adaptor adaptor,
743 ConversionPatternRewriter &rewriter)
const override {
744 auto loc = op.getLoc();
745 const std::string fnName{
"atomic_work_item_fence"};
746 int memScope, addrSpace;
747 switch (op.getAddrspace()) {
748 case xevm::AddrSpace::SHARED:
751 case xevm::AddrSpace::GLOBAL:
756 return rewriter.notifyMatchFailure(
757 op,
"Fence only supports global and shared address spaces.");
759 switch (op.getScope()) {
760 case xevm::MemScope::WORKGROUP:
763 case xevm::MemScope::DEVICE:
768 return rewriter.notifyMatchFailure(
769 op,
"Fence only supports workgroup and device memory scopes.");
771 Type i32Type = rewriter.getI32Type();
772 Value acqRel = LLVM::ConstantOp::create(rewriter, loc, i32Type, 4);
773 Value memScopeConst =
774 LLVM::ConstantOp::create(rewriter, loc, i32Type, memScope);
775 Value addrSpaceConst =
776 LLVM::ConstantOp::create(rewriter, loc, i32Type, addrSpace);
777 SmallVector<Value> args{addrSpaceConst, acqRel, memScopeConst};
778 SmallVector<Type> argTypes{3, i32Type};
779 createDeviceFunctionCall(rewriter, mangle(fnName, argTypes),
780 LLVM::LLVMVoidType::get(rewriter.getContext()),
781 argTypes, args, {}, noUnwindAttrs,
783 rewriter.eraseOp(op);
787template <
typename OpType>
788class LoadStorePrefetchToOCLPattern :
public OpConversionPattern<OpType> {
789 using OpConversionPattern<OpType>::OpConversionPattern;
791 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
792 ConversionPatternRewriter &rewriter)
const override {
793 constexpr bool isLoad = std::is_same_v<OpType, BlockLoad2dOp>;
794 constexpr bool isPrefetch = std::is_same_v<OpType, BlockPrefetch2dOp>;
796 auto loc = op.getLoc();
797 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
799 bool packReg =
false;
800 bool transpose =
false;
801 if constexpr (isLoad) {
802 vecType = op.getRes().getType();
803 packReg = op.getPackRegister();
804 transpose = op.getTranspose();
805 }
else if constexpr (!isPrefetch) {
806 vecType = op.getStoredVal().getType();
809 auto i32Type = rewriter.getI32Type();
811 LLVM::UndefOp::create(rewriter, loc, VectorType::get(2, i32Type));
812 Value zero = LLVM::ConstantOp::create(rewriter, loc, i32Type, 0);
813 Value one = LLVM::ConstantOp::create(rewriter, loc, i32Type, 1);
814 byteCoord = LLVM::InsertElementOp::create(
815 rewriter, loc, VectorType::get(2, i32Type), byteCoord, op.getX(), zero);
816 byteCoord = LLVM::InsertElementOp::create(
817 rewriter, loc, VectorType::get(2, i32Type), byteCoord, op.getY(), one);
818 SmallVector<Value> args{op.getPtr(), op.getBaseWidth(), op.getBaseHeight(),
819 op.getBasePitch(), byteCoord};
822 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
825 SmallVector<Type> retTypes;
827 std::string funcName{
"intel_sub_group_2d_block_"};
828 std::string bitWidthId;
829 LLVMFuncAttributeOptions funcAttr{noUnwindWillReturnAttrs};
830 SmallVector<std::pair<unsigned, StringRef>, 4> paramAttrs;
831 if constexpr (isPrefetch) {
832 funcName +=
"prefetch";
833 paramAttrs = {std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName())};
834 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
835 LLVM::ModRefInfo::NoModRef,
836 LLVM::ModRefInfo::Ref,
837 LLVM::ModRefInfo::NoModRef,
838 LLVM::ModRefInfo::NoModRef,
839 LLVM::ModRefInfo::NoModRef,
840 LLVM::ModRefInfo::NoModRef);
841 funcAttr = noUnwindAttrs;
842 funcAttr.memEffectsAttr = memAttr;
844 auto vecElemType = vecType.getElementType();
845 auto vecElemBitWidth = vecElemType.getIntOrFloatBitWidth();
846 auto vecNumElems = vecType.getNumElements();
852 if (op.getElemSizeInBits() == 8 && op.getTileWidth() == 32) {
853 vecElemBitWidth = 16;
854 vecElemType = rewriter.getI16Type();
855 vecNumElems = vecNumElems / 2;
858 LLVM::ConstantOp::create(rewriter, loc, i32Type, vecNumElems);
859 auto dstOrSrcPtr = LLVM::AllocaOp::create(
860 rewriter, loc, LLVM::LLVMPointerType::get(rewriter.getContext()),
861 vecElemType, numElems);
862 args.push_back(dstOrSrcPtr);
863 if constexpr (isLoad) {
865 bitWidthId = getTypeMangling(vecElemType,
true);
867 funcName +=
"_transform";
869 funcName +=
"_transpose";
870 spvLoadDstPtr = dstOrSrcPtr;
871 retTypes.push_back(vecType);
873 std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName()),
874 std::make_pair(0, LLVM::LLVMDialect::getReadonlyAttrName()),
875 std::make_pair(5, LLVM::LLVMDialect::getNonNullAttrName()),
876 std::make_pair(5, LLVM::LLVMDialect::getWriteOnlyAttrName()),
880 bitWidthId = (vecElemBitWidth == 32)
882 : ((vecElemBitWidth == 16) ?
"t" :
"h");
883 LLVM::StoreOp::create(rewriter, loc, op.getStoredVal(), dstOrSrcPtr);
885 std::make_pair(0, LLVM::LLVMDialect::getNonNullAttrName()),
886 std::make_pair(0, LLVM::LLVMDialect::getWriteOnlyAttrName()),
887 std::make_pair(5, LLVM::LLVMDialect::getNonNullAttrName()),
888 std::make_pair(5, LLVM::LLVMDialect::getReadonlyAttrName()),
894 llvm::formatv(
"{0}_{1}b_{2}r{3}x{4}c", funcName, op.getElemSizeInBits(),
895 op.getTileHeight(), op.getTileWidth(), op.getVBlocks())
897 std::string prefetchCode(
"");
900 funcName = llvm::formatv(
"_Z{0}{1}PU3AS1viiiDv2_i{2}{3}", funcName.size(),
901 funcName, prefetchCode, bitWidthId)
903 SmallVector<Type> argTypes;
904 for (
auto arg : args) {
905 argTypes.push_back(arg.getType());
907 createDeviceFunctionCall(
908 rewriter, funcName, LLVM::LLVMVoidType::get(rewriter.getContext()),
909 argTypes, args, paramAttrs, funcAttr, op.getOperation());
911 if constexpr (isLoad)
913 op, LLVM::LoadOp::create(rewriter, loc, vecType, spvLoadDstPtr));
915 rewriter.eraseOp(op);
920template <
typename OpType>
921class BlockLoadStore1DToOCLPattern :
public OpConversionPattern<OpType> {
922 using OpConversionPattern<OpType>::OpConversionPattern;
924 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
925 ConversionPatternRewriter &rewriter)
const override {
926 constexpr bool isStore = std::is_same_v<OpType, xevm::BlockStoreOp>;
927 auto loc = op.getLoc();
928 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
933 std::string funcName{
"intel_sub_group_block_"};
936 if constexpr (isStore) {
937 funcName +=
"write_u";
938 valOrResTy = op.getVal().getType();
940 funcName +=
"read_u";
941 valOrResTy = op.getType();
944 VectorType vecTy = dyn_cast<VectorType>(valOrResTy);
945 Type elemType = vecTy ? vecTy.getElementType() : valOrResTy;
946 funcName += getTypeMangling(elemType);
948 funcName += std::to_string(vecTy.getNumElements());
949 SmallVector<Type, 2> argTypes{};
953 SmallVector<bool, 2> isUnsigned{};
957 SmallVector<Value, 2> args{};
958 args.push_back(op.getPtr());
959 argTypes.push_back(op.getPtr().getType());
960 isUnsigned.push_back(
true);
963 applyCacheControlAnnotation(rewriter, loc, op, args, moduleOp,
967 argTypes[0] = args[0].getType();
970 if constexpr (isStore) {
971 args.push_back(op.getVal());
972 argTypes.push_back(op.getVal().getType());
973 isUnsigned.push_back(
true);
974 retType = LLVM::LLVMVoidType::get(rewriter.getContext());
976 retType = valOrResTy;
978 funcName = std::string(
"_Z") + std::to_string(funcName.size()) + funcName +
980 std::to_string(op.getPtr().getType().getAddressSpace());
981 funcName += getTypeMangling(elemType,
true);
982 if constexpr (isStore)
983 funcName += getTypeMangling(valOrResTy,
true);
984 LLVMFuncAttributeOptions funcAttr{noUnwindWillReturnAttrs};
987 createDeviceFunctionCall(rewriter, funcName, retType, argTypes, args,
988 {}, funcAttr, op.getOperation());
990 if constexpr (isStore)
991 rewriter.eraseOp(op);
993 rewriter.replaceOp(op, call->getResult(0));
998template <
typename OpType>
999class LLVMLoadStoreToOCLPattern :
public OpConversionPattern<OpType> {
1000 using OpConversionPattern<OpType>::OpConversionPattern;
1002 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1003 ConversionPatternRewriter &rewriter)
const override {
1004 if (!op->hasDiscardableAttr(
"cache_control"))
1007 auto *moduleOp = op->template getParentWithTrait<OpTrait::SymbolTable>();
1008 std::optional<ArrayAttr> optCacheControls =
1009 getCacheControlMetadata(rewriter, op);
1010 if (!optCacheControls) {
1011 rewriter.modifyOpInPlace(
1012 op, [&]() { op->removeDiscardableAttr(
"cache_control"); });
1017 constexpr bool isStore = std::is_same_v<OpType, LLVM::StoreOp>;
1018 unsigned ptrIdx = isStore ? 1 : 0;
1019 Value ptr = op->getOperand(ptrIdx);
1022 Value annotatedPtr = annotatePtrWithCacheControl(
1023 rewriter, op->getLoc(), ptr, *optCacheControls, moduleOp);
1026 rewriter.modifyOpInPlace(op, [&]() {
1027 op->setOperand(ptrIdx, annotatedPtr);
1028 op->removeDiscardableAttr(
"cache_control");
1061static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdXOp) {
1062 return {
"get_local_id", 0};
1064static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdYOp) {
1065 return {
"get_local_id", 1};
1067static std::pair<StringRef, int64_t> getConfig(xevm::WorkitemIdZOp) {
1068 return {
"get_local_id", 2};
1070static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimXOp) {
1071 return {
"get_local_size", 0};
1073static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimYOp) {
1074 return {
"get_local_size", 1};
1076static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupDimZOp) {
1077 return {
"get_local_size", 2};
1079static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdXOp) {
1080 return {
"get_group_id", 0};
1082static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdYOp) {
1083 return {
"get_group_id", 1};
1085static std::pair<StringRef, int64_t> getConfig(xevm::WorkgroupIdZOp) {
1086 return {
"get_group_id", 2};
1088static std::pair<StringRef, int64_t> getConfig(xevm::GridDimXOp) {
1089 return {
"get_num_groups", 0};
1091static std::pair<StringRef, int64_t> getConfig(xevm::GridDimYOp) {
1092 return {
"get_num_groups", 1};
1094static std::pair<StringRef, int64_t> getConfig(xevm::GridDimZOp) {
1095 return {
"get_num_groups", 2};
1099template <
typename OpType>
1100class LaunchConfigOpToOCLPattern :
public OpConversionPattern<OpType> {
1101 using OpConversionPattern<OpType>::OpConversionPattern;
1103 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1104 ConversionPatternRewriter &rewriter)
const override {
1105 Location loc = op->getLoc();
1106 auto [baseName, dim] = getConfig(op);
1107 Type dimTy = rewriter.getI32Type();
1108 Value dimVal = LLVM::ConstantOp::create(rewriter, loc, dimTy,
1109 static_cast<int64_t
>(dim));
1110 std::string func = mangle(baseName, {dimTy}, {
true});
1111 Type resTy = op.getType();
1113 createDeviceFunctionCall(rewriter, func, resTy, {dimTy}, {dimVal}, {},
1114 noUnwindWillReturnAttrs, op.getOperation());
1115 constexpr auto noModRef = LLVM::ModRefInfo::NoModRef;
1116 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1122 call.setMemoryEffectsAttr(memAttr);
1123 rewriter.replaceOp(op, call);
1140static StringRef getConfig(xevm::LaneIdOp) {
return "get_sub_group_local_id"; }
1141static StringRef getConfig(xevm::SubgroupIdOp) {
return "get_sub_group_id"; }
1142static StringRef getConfig(xevm::SubgroupSizeOp) {
1143 return "get_sub_group_size";
1145template <
typename OpType>
1146class SubgroupOpWorkitemOpToOCLPattern :
public OpConversionPattern<OpType> {
1147 using OpConversionPattern<OpType>::OpConversionPattern;
1149 matchAndRewrite(OpType op,
typename OpType::Adaptor adaptor,
1150 ConversionPatternRewriter &rewriter)
const override {
1151 std::string func = mangle(getConfig(op).str(), {});
1152 Type resTy = op.getType();
1154 createDeviceFunctionCall(rewriter, func, resTy, {}, {}, {},
1155 noUnwindWillReturnAttrs, op.getOperation());
1156 constexpr auto noModRef = LLVM::ModRefInfo::NoModRef;
1157 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1163 call.setMemoryEffectsAttr(memAttr);
1164 rewriter.replaceOp(op, call);
1171static bool isSupportedSPIRVVectorLength(
int64_t numElements) {
1172 return llvm::is_contained({2, 3, 4, 8, 16}, numElements);
1176static Value castIfNeeded(ConversionPatternRewriter &rewriter,
Location loc,
1180 return LLVM::BitcastOp::create(rewriter, loc, ty, val);
1186static Value takeLeadingElements(ConversionPatternRewriter &rewriter,
1188 auto vecTy = cast<VectorType>(val.
getType());
1189 if (vecTy.getNumElements() == numElements)
1192 llvm::to_vector(llvm::seq<int32_t>(0,
static_cast<int32_t
>(numElements)));
1193 return LLVM::ShuffleVectorOp::create(rewriter, loc, val, val, mask);
1207class TruncfToOCLPattern :
public OpConversionPattern<TruncfOp> {
1208 using OpConversionPattern::OpConversionPattern;
1210 matchAndRewrite(TruncfOp op, TruncfOp::Adaptor adaptor,
1211 ConversionPatternRewriter &rewriter)
const override {
1213 auto srcEtype = op.getSrcEtype().getEtype();
1214 auto dstEtype = op.getDstEtype().getEtype();
1220 auto vecSrcTy = dyn_cast<VectorType>(op.getSrc().getType());
1222 return rewriter.notifyMatchFailure(op,
"Scalar src is not supported.");
1224 int64_t numElements = vecSrcTy.getNumElements();
1225 if (!isSupportedSPIRVVectorLength(numElements))
1226 return rewriter.notifyMatchFailure(
1227 op,
"src vector length must be 2, 3, 4, 8 or 16");
1230 Type dstTy = op.getDst().getType();
1231 Location loc = op.getLoc();
1232 Value src = op.getSrc();
1233 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1234 LLVM::ModRefInfo::NoModRef,
1235 LLVM::ModRefInfo::NoModRef,
1236 LLVM::ModRefInfo::NoModRef,
1237 LLVM::ModRefInfo::NoModRef,
1238 LLVM::ModRefInfo::NoModRef,
1239 LLVM::ModRefInfo::NoModRef);
1240 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1241 funcAttrs.memEffectsAttr = memAttr;
1244 if (dstEtype == TruncfDstElemTypes::E2M1) {
1254 constexpr int kDnsclConvertToE2M1 = 1;
1255 constexpr int kDnsclModeBytes02 = 0;
1256 constexpr int kDnsclModeBytes13 = 2;
1259 int64_t numLanes = llvm::divideCeil(numElements, 2);
1261 Type i32Ty = rewriter.getI32Type();
1262 Type i8Ty = rewriter.getI8Type();
1266 if (numElements != numLanes * 2) {
1267 SmallVector<int32_t> mask = llvm::to_vector(
1268 llvm::seq<int32_t>(0,
static_cast<int32_t
>(numElements)));
1270 mask.append(
static_cast<size_t>(numLanes * 2 - numElements), 0);
1271 padded = LLVM::ShuffleVectorOp::create(rewriter, loc, src, src, mask);
1277 laneVec = LLVM::BitcastOp::create(
1278 rewriter, loc, VectorType::get(numLanes, i32Ty), padded);
1280 laneVec = LLVM::BitcastOp::create(rewriter, loc, i32Ty, padded);
1281 auto getLane = [&](int64_t idx) -> Value {
1285 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), idx);
1286 return LLVM::ExtractElementOp::create(rewriter, loc, laneVec, pos)
1290 std::string fnName =
"__builtin_IB_dnscl_";
1291 fnName += (srcEtype == TruncfSrcElemTypes::F16) ?
"hf16" :
"bf16";
1293 LLVM::ConstantOp::create(rewriter, loc, i32Ty, kDnsclConvertToE2M1);
1294 auto genDnscl = [&](Value lo, Value hi,
int mode) -> Value {
1295 Value modeVal = LLVM::ConstantOp::create(rewriter, loc, i32Ty, mode);
1296 SmallVector<Type> argTypes{lo.
getType(), hi.getType(),
1298 SmallVector<Value> args{lo, hi, convertTo, modeVal};
1299 return createDeviceFunctionCall(rewriter, fnName, i32Ty, argTypes, args,
1300 {}, funcAttrs, op.getOperation())
1305 if (numLanes <= 2) {
1308 Value lo = getLane(0);
1312 : LLVM::UndefOp::create(rewriter, loc, i32Ty)->getResult(0);
1313 Value dword = genDnscl(lo, hi, kDnsclModeBytes02);
1314 if (numLanes == 1) {
1316 result = LLVM::TruncOp::create(rewriter, loc, i8Ty, dword);
1318 Value bytes = LLVM::BitcastOp::create(
1319 rewriter, loc, VectorType::get(4, i8Ty), dword);
1320 result = LLVM::ShuffleVectorOp::create(rewriter, loc, bytes, bytes,
1321 ArrayRef<int32_t>{0, 2});
1325 SmallVector<Value> dwords;
1326 for (int64_t base = 0; base < numLanes; base += 4) {
1331 Value lane0 = getLane(base);
1332 Value lane2 = getLane(base + 2);
1333 Value even = genDnscl(lane0, lane2, kDnsclModeBytes02);
1334 Value lane1 = getLane(base + 1);
1335 Value lane3 = getLane(base + 3);
1336 Value odd = genDnscl(lane1, lane3, kDnsclModeBytes13);
1337 dwords.push_back(LLVM::OrOp::create(rewriter, loc, even, odd));
1339 if (dwords.size() == 1) {
1342 Type packedTy = VectorType::get(dwords.size(), i32Ty);
1343 result = LLVM::UndefOp::create(rewriter, loc, packedTy);
1344 for (
auto [idx, dword] : llvm::enumerate(dwords)) {
1345 Value pos = LLVM::ConstantOp::create(rewriter, loc, i32Ty, idx);
1347 LLVM::InsertElementOp::create(rewriter, loc,
result, dword, pos)
1352 rewriter.replaceOp(op, castIfNeeded(rewriter, loc, dstTy,
result));
1359 std::string lenSuffix = std::to_string(numElements);
1362 if (srcEtype == TruncfSrcElemTypes::BF16) {
1365 src = LLVM::BitcastOp::create(
1366 rewriter, op.getLoc(),
1367 VectorType::get(vecSrcTy.getShape(), rewriter.getI16Type()), src);
1368 std::string fnName =
"__builtin_IB_bftof_" + lenSuffix;
1369 SmallVector<Type> argTypes{src.
getType()};
1370 SmallVector<Value> args{src};
1371 Type resTy = VectorType::get(vecSrcTy.getShape(), rewriter.getF32Type());
1372 src = createDeviceFunctionCall(rewriter, fnName, resTy, argTypes, args,
1373 {}, funcAttrs, op.getOperation())
1377 std::string truncFnName =
"convert_half" + lenSuffix;
1378 SmallVector<Type> truncArgTypes{src.
getType()};
1379 SmallVector<Value> truncArgs{src};
1380 truncFnName = mangle(truncFnName, truncArgTypes);
1381 resTy = VectorType::get(vecSrcTy.getShape(), rewriter.getF16Type());
1383 createDeviceFunctionCall(rewriter, truncFnName, resTy, truncArgTypes,
1384 truncArgs, {}, funcAttrs, op.getOperation())
1387 if (dstEtype == TruncfDstElemTypes::BF8) {
1389 std::string fnName =
"__builtin_IB_hftobf8_" + lenSuffix;
1390 SmallVector<Type> argTypes{src.
getType()};
1391 SmallVector<Value> args{src};
1393 createDeviceFunctionCall(rewriter, fnName, dstTy, argTypes, args, {},
1394 funcAttrs, op.getOperation())
1397 rewriter.replaceOp(op,
result);
1398 }
else if (dstEtype == TruncfDstElemTypes::F8) {
1400 std::string fnName =
"__builtin_IB_hftohf8_" + lenSuffix;
1401 SmallVector<Type> argTypes{src.
getType()};
1402 SmallVector<Value> args{src};
1404 createDeviceFunctionCall(rewriter, fnName, dstTy, argTypes, args, {},
1405 funcAttrs, op.getOperation())
1408 rewriter.replaceOp(op,
result);
1410 return rewriter.notifyMatchFailure(
1411 op,
"Unsupported src, dst element type pair.");
1417class ExtfToOCLPattern :
public OpConversionPattern<ExtfOp> {
1418 using OpConversionPattern::OpConversionPattern;
1420 matchAndRewrite(ExtfOp op, ExtfOp::Adaptor adaptor,
1421 ConversionPatternRewriter &rewriter)
const override {
1424 auto srcEtype = op.getSrcEtype().getEtype();
1425 auto dstEtype = op.getDstEtype().getEtype();
1428 Type srcTy = op.getSrc().getType();
1430 auto vecDstTy = dyn_cast<VectorType>(op.getDst().getType());
1432 return rewriter.notifyMatchFailure(op,
"Scalar dst is not supported.");
1434 int64_t numElements = vecDstTy.getNumElements();
1435 if (!isSupportedSPIRVVectorLength(numElements))
1436 return rewriter.notifyMatchFailure(
1437 op,
"dst vector length must be 2, 3, 4, 8 or 16");
1438 Location loc = op.getLoc();
1439 Value src = op.getSrc();
1440 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1441 LLVM::ModRefInfo::NoModRef,
1442 LLVM::ModRefInfo::NoModRef,
1443 LLVM::ModRefInfo::NoModRef,
1444 LLVM::ModRefInfo::NoModRef,
1445 LLVM::ModRefInfo::NoModRef,
1446 LLVM::ModRefInfo::NoModRef);
1447 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1448 funcAttrs.memEffectsAttr = memAttr;
1451 if (srcEtype == ExtfSrcElemTypes::E2M1) {
1464 int64_t numBytes = llvm::divideCeil(numElements, 2);
1465 constexpr int kLutE2M1ToF16 = 7;
1466 constexpr int kLutE2M1ToBF16 = 5;
1468 (dstEtype == ExtfDstElemTypes::F16) ? kLutE2M1ToF16 : kLutE2M1ToBF16;
1469 Value lutIdx = LLVM::ConstantOp::create(rewriter, loc,
1470 rewriter.getI32Type(), lutIndex);
1471 Type lutTy = VectorType::get(16, rewriter.getI32Type());
1473 createDeviceFunctionCall(rewriter,
"__builtin_IB_shfl_idx4_lut",
1474 lutTy, {lutIdx.
getType()}, {lutIdx}, {},
1475 funcAttrs, op.getOperation())
1479 Type i8Ty = rewriter.getI8Type();
1480 Type i32Ty = rewriter.getI32Type();
1481 std::string fnName =
"__builtin_IB_shfl_idx4_to_fp16_";
1482 Type argTy, packedResTy;
1483 if (numBytes == 1) {
1485 packedResTy = i32Ty;
1487 fnName += std::to_string(numBytes) +
"_";
1488 argTy = VectorType::get(numBytes, i8Ty);
1489 packedResTy = VectorType::get(numBytes, i32Ty);
1492 SmallVector<Type> convArgTypes{lut.
getType(), argTy};
1493 SmallVector<Value> convArgs{lut, castIfNeeded(rewriter, loc, argTy, src)};
1495 createDeviceFunctionCall(rewriter, fnName, packedResTy, convArgTypes,
1496 convArgs, {}, funcAttrs, op.getOperation())
1500 Type wideTy = VectorType::get(numBytes * 2, vecDstTy.getElementType());
1501 result = LLVM::BitcastOp::create(rewriter, loc, wideTy,
result);
1502 result = takeLeadingElements(rewriter, loc,
result, numElements);
1503 rewriter.replaceOp(op,
result);
1509 auto vecSrcTy = dyn_cast<VectorType>(srcTy);
1510 if (!vecSrcTy || vecSrcTy.getNumElements() != numElements)
1511 return rewriter.notifyMatchFailure(
1512 op,
"fp8 src and dst must have the same number of elements");
1513 std::string lenSuffix = std::to_string(numElements);
1518 std::string fnName = (srcEtype == ExtfSrcElemTypes::BF8)
1519 ?
"__builtin_IB_bf8tohf_"
1520 :
"__builtin_IB_hf8tohf_";
1521 fnName += lenSuffix;
1522 Type f16Ty = VectorType::get(vecSrcTy.getShape(), rewriter.getF16Type());
1523 SmallVector<Type> argTypes{src.
getType()};
1524 SmallVector<Value> args{src};
1526 createDeviceFunctionCall(rewriter, fnName, f16Ty, argTypes, args, {},
1527 funcAttrs, op.getOperation())
1531 if (dstEtype == ExtfDstElemTypes::F16) {
1532 rewriter.replaceOp(op,
result);
1540 std::string convFnName =
"convert_float" + lenSuffix;
1541 SmallVector<Type> convArgTypes{
result.getType()};
1542 SmallVector<Value> convArgs{
result};
1543 convFnName = mangle(convFnName, convArgTypes);
1544 Type f32Ty = VectorType::get(vecSrcTy.getShape(), rewriter.getF32Type());
1546 createDeviceFunctionCall(rewriter, convFnName, f32Ty, convArgTypes,
1547 convArgs, {}, funcAttrs, op.getOperation())
1551 std::string ftobfFnName =
"__builtin_IB_ftobf_" + lenSuffix;
1552 SmallVector<Type> ftobfArgTypes{
result.getType()};
1553 SmallVector<Value> ftobfArgs{
result};
1554 Type i16Ty = VectorType::get(vecSrcTy.getShape(), rewriter.getI16Type());
1556 createDeviceFunctionCall(rewriter, ftobfFnName, i16Ty, ftobfArgTypes,
1557 ftobfArgs, {}, funcAttrs, op.getOperation())
1560 result = LLVM::BitcastOp::create(rewriter, op.getLoc(), vecDstTy,
result);
1561 rewriter.replaceOp(op,
result);
1566class MMAMxToOCLPattern :
public OpConversionPattern<MMAMxOp> {
1567 using OpConversionPattern::OpConversionPattern;
1569 matchAndRewrite(MMAMxOp op, MMAMxOp::Adaptor adaptor,
1570 ConversionPatternRewriter &rewriter)
const override {
1572 return rewriter.notifyMatchFailure(op,
"OCL requires C operand");
1574 auto precisionC = op.getTypes().getC();
1575 auto precisionD = op.getTypes().getD();
1576 if (precisionC != precisionD) {
1577 return rewriter.notifyMatchFailure(op,
"type of C and D need to match");
1580 constexpr uint32_t bitWidthPackedA{16};
1581 constexpr uint32_t bitWidthPackedB{32};
1582 auto loc = op.getLoc();
1584 auto castIfNeeded = [&](Value val, Type packedType) -> Value {
1585 VectorType origTy = cast<VectorType>(val.
getType());
1586 const uint32_t vecBitSize =
1587 origTy.getNumElements() *
1588 origTy.getElementType().getIntOrFloatBitWidth();
1589 VectorType newTy = VectorType::get(
1590 vecBitSize / packedType.getIntOrFloatBitWidth(), packedType);
1591 if (origTy != newTy)
1592 val = LLVM::BitcastOp::create(rewriter, loc, newTy, val);
1596 Value a = op.getA();
1597 Type packedAType = (op.getTypes().getA() == xevm::ElemType::TF32)
1598 ? cast<Type>(rewriter.getF32Type())
1599 : rewriter.getIntegerType(bitWidthPackedA);
1600 a = castIfNeeded(a, packedAType);
1602 Value
b = op.getB();
1603 Type packedBType = (op.getTypes().getB() == xevm::ElemType::TF32)
1604 ? cast<Type>(rewriter.getF32Type())
1605 : rewriter.getIntegerType(bitWidthPackedB);
1606 b = castIfNeeded(
b, packedBType);
1608 Value c = op.getC();
1609 VectorType cOrigTy = cast<VectorType>(c.
getType());
1610 VectorType resOrigTy = cast<VectorType>(op->getResultTypes()[0]);
1611 assert(cOrigTy == resOrigTy &&
"Accumulator and result type mismatch");
1614 cOrigTy.getElementType().isBF16()
1615 ? VectorType::get(cOrigTy.getShape(), rewriter.getIntegerType(16))
1617 VectorType resTy = cTy;
1619 c = LLVM::BitcastOp::create(rewriter, loc, cTy, c);
1621 std::string fnName =
1622 llvm::formatv(
"__builtin_IB_sub_group16_bdpas_{0}_{1}_{2}_{3}_8_8",
1623 builtinElemType(op.getTypes().getD()),
1624 builtinElemType(op.getTypes().getC()),
1625 builtinElemType(op.getTypes().getA()),
1626 builtinElemType(op.getTypes().getB()))
1628 auto scaleA = op.getScaleA();
1629 auto scaleB = op.getScaleB();
1630 SmallVector<Type> argTypes{cTy, a.
getType(),
b.getType(), scaleA.getType(),
1632 SmallVector<Value> args{c, a,
b, scaleA, scaleB};
1634 auto memAttr = rewriter.getAttr<LLVM::MemoryEffectsAttr>(
1635 LLVM::ModRefInfo::NoModRef,
1636 LLVM::ModRefInfo::NoModRef,
1637 LLVM::ModRefInfo::NoModRef,
1638 LLVM::ModRefInfo::NoModRef,
1639 LLVM::ModRefInfo::NoModRef,
1640 LLVM::ModRefInfo::NoModRef);
1641 auto funcAttrs = convergentNoUnwindWillReturnAttrs;
1642 funcAttrs.memEffectsAttr = memAttr;
1644 createDeviceFunctionCall(rewriter, fnName, resTy, argTypes, args, {},
1645 funcAttrs, op.getOperation())
1648 if (resOrigTy != resTy)
1649 result = LLVM::BitcastOp::create(rewriter, loc, resOrigTy,
result);
1651 rewriter.replaceOp(op,
result);
1664class BitcastShuffleToGenISAPattern
1665 :
public OpConversionPattern<BitcastShuffleOp> {
1666 using OpConversionPattern::OpConversionPattern;
1668 matchAndRewrite(BitcastShuffleOp op, BitcastShuffleOp::Adaptor adaptor,
1669 ConversionPatternRewriter &rewriter)
const override {
1670 Type srcTy = op.getSrc().getType();
1671 Type resTy = op.getRes().getType();
1673 std::string fnName =
"llvm.genx.GenISA.SubgroupBitcastShuffle." +
1674 getGenISATypeMangling(resTy) +
"." +
1675 getGenISATypeMangling(srcTy);
1677 Value
result = createDeviceFunctionCall(
1678 rewriter, fnName, resTy, {srcTy}, {adaptor.getSrc()}, {},
1679 convergentNoUnwindWillReturnAttrs, op.getOperation())
1682 rewriter.replaceOp(op,
result);
1687class AllocaToGlobalPattern :
public OpConversionPattern<LLVM::AllocaOp> {
1688 using OpConversionPattern::OpConversionPattern;
1690 matchAndRewrite(LLVM::AllocaOp op, LLVM::AllocaOp::Adaptor adaptor,
1691 ConversionPatternRewriter &rewriter)
const override {
1692 auto ptrType = cast<LLVM::LLVMPointerType>(op.getType());
1693 auto addrSpace = ptrType.getAddressSpace();
1696 auto symTable = op->getParentWithTrait<OpTrait::SymbolTable>();
1700 if (ModuleOp mod = dyn_cast<ModuleOp>(*symTable)) {
1701 moduleBody = mod.getBody();
1702 }
else if (gpu::GPUModuleOp gpuMod =
1703 dyn_cast<gpu::GPUModuleOp>(*symTable)) {
1704 moduleBody = gpuMod.getBody();
1708 auto val = op.getArraySize();
1712 auto loc = op.getLoc();
1713 auto globalType = LLVM::LLVMArrayType::get(
1714 rewriter.getContext(), op.getElemType(), cst.getZExtValue());
1715 LLVM::GlobalOp globalVar;
1717 OpBuilder::InsertionGuard guard(rewriter);
1718 rewriter.setInsertionPointToStart(moduleBody);
1719 auto alignment = op.getAlignment();
1720 globalVar = LLVM::GlobalOp::create(
1721 rewriter, loc, globalType,
false,
1722 LLVM::Linkage::Internal,
1723 std::string(
"__global_alloca_") +
1724 std::to_string(getNextGlobalIdx()),
1726 alignment ? *alignment : 0, addrSpace);
1728 rewriter.replaceOpWithNewOp<LLVM::AddressOfOp>(op, globalVar);
1733 static unsigned getNextGlobalIdx() {
1734 static unsigned globalIdx = 0;
1747static bool isExtractingContiguousSlice(LLVM::ShuffleVectorOp op) {
1748 if (op.getV1() != op.getV2() &&
1749 !isa_and_present<LLVM::PoisonOp, LLVM::UndefOp>(
1750 op.getV2().getDefiningOp()))
1752 auto maskAttr = op.getMask();
1754 int64_t sourceSize = op.getV1().getType().getNumElements();
1755 if (maskSize > sourceSize)
1757 int64_t firstIndex = maskAttr[0];
1758 if (firstIndex < 0 || firstIndex >= sourceSize)
1760 for (
int64_t i = 1; i < maskSize; ++i) {
1762 if (
index != firstIndex + i)
1764 if (
index >= sourceSize)
1778class HandleVectorExtractPattern
1780 using OpRewritePattern<LLVM::ShuffleVectorOp>::OpRewritePattern;
1782 void initialize() { setHasBoundedRewriteRecursion(); }
1784 LogicalResult matchAndRewrite(LLVM::ShuffleVectorOp op,
1785 PatternRewriter &rewriter)
const override {
1787 if (!isExtractingContiguousSlice(op))
1790 auto mask = op.getMask();
1791 auto loc = op.getLoc();
1792 auto ty = op.getType();
1794 auto src = op.getV1();
1797 if (isa<LLVM::FPExtOp>(srcOp) || isa<LLVM::FPTruncOp>(srcOp)) {
1798 Value srcInput = srcOp->getOperand(0);
1800 auto srcVecTy = dyn_cast<VectorType>(srcInput.
getType());
1803 auto newShuffleVecTy =
1804 VectorType::get(mask.size(), srcVecTy.getElementType());
1805 auto newShuffle = LLVM::ShuffleVectorOp::create(
1806 rewriter, loc, newShuffleVecTy, srcInput, srcInput, mask);
1809 if (isa<LLVM::FPExtOp>(srcOp)) {
1810 newUnaryOp = LLVM::FPExtOp::create(rewriter, loc, ty, newShuffle);
1812 newUnaryOp = LLVM::FPTruncOp::create(rewriter, loc, ty, newShuffle);
1815 }
else if (isa<LLVM::BitcastOp>(srcOp)) {
1816 Value srcInput = srcOp->getOperand(0);
1819 auto srcInputVecTy = dyn_cast<VectorType>(srcInput.
getType());
1820 auto srcResVecTy = dyn_cast<VectorType>(srcOp->getResult(0).getType());
1821 if (!srcInputVecTy || !srcResVecTy)
1823 auto srcInputSize = srcInputVecTy.getNumElements();
1824 auto srcResSize = srcResVecTy.getNumElements();
1825 auto maskSize =
static_cast<int32_t
>(mask.size());
1826 if (srcInputSize > srcResSize) {
1829 if (srcResSize % srcInputSize != 0) {
1832 auto maskScale = srcResSize / srcInputSize;
1833 if (maskScale != 1) {
1836 if (mask[0] % maskScale != 0 || maskSize % maskScale != 0) {
1840 SmallVector<int32_t> newMask;
1841 int32_t newMaskSize = maskSize / maskScale;
1842 int32_t maskStart = mask[0] / maskScale;
1843 for (int32_t i = 0; i < newMaskSize; ++i) {
1844 newMask.push_back(maskStart + i);
1848 auto newShuffleVecTy = VectorType::get(
1849 static_cast<int64_t
>(mask.size()), srcInputVecTy.getElementType());
1850 auto newShuffle = LLVM::ShuffleVectorOp::create(
1851 rewriter, loc, newShuffleVecTy, srcInput, srcInput, mask);
1854 LLVM::BitcastOp::create(rewriter, loc, ty, newShuffle);
1856 }
else if (isa<LLVM::ShuffleVectorOp>(srcOp)) {
1861 auto srcShuffle = cast<LLVM::ShuffleVectorOp>(srcOp);
1862 if (!isExtractingContiguousSlice(srcShuffle))
1864 auto srcMask = srcShuffle.getMask();
1865 SmallVector<int32_t> combinedMask;
1866 for (
auto index : mask) {
1867 combinedMask.push_back(srcMask[index]);
1869 auto newShuffle = LLVM::ShuffleVectorOp::create(
1870 rewriter, loc, ty, srcShuffle.getV1(), srcShuffle.getV1(),
1873 }
else if (isa<LLVM::LoadOp>(srcOp)) {
1875 auto loadOp = cast<LLVM::LoadOp>(srcOp);
1876 auto loadPtr = loadOp.getAddr();
1877 auto loadAddrSpace = loadPtr.getType().getAddressSpace();
1878 if (loadAddrSpace != 0)
1880 auto loadTy = dyn_cast<VectorType>(loadOp.getType());
1883 auto elemTy = loadTy.getElementType();
1884 auto firstIndex = mask[0];
1885 auto newVecTy = VectorType::get(mask.size(), elemTy);
1888 auto newPtr = LLVM::GEPOp::create(
1890 LLVM::LLVMPointerType::get(rewriter.
getContext(), loadAddrSpace),
1891 elemTy, loadPtr, ArrayRef<LLVM::GEPArg>{firstIndex});
1892 auto newLoad = LLVM::LoadOp::create(rewriter, loc, newVecTy, newPtr);
1895 auto newLoad = LLVM::LoadOp::create(rewriter, loc, newVecTy, loadPtr);
1913struct ConvertXeVMToLLVMPass
1914 :
public impl::ConvertXeVMToLLVMPassBase<ConvertXeVMToLLVMPass> {
1917 void getDependentDialects(DialectRegistry ®istry)
const override {
1918 registry.
insert<LLVM::LLVMDialect, XeVMDialect>();
1921 void runOnOperation()
override {
1925 if (
failed(applyPartialConversion(getOperation(),
target,
1926 std::move(patterns))))
1927 signalPassFailure();
1931 RewritePatternSet vectorPatterns(&
getContext());
1932 vectorPatterns.add<HandleVectorExtractPattern>(&
getContext());
1933 GreedyRewriteConfig config{};
1938 config.enableFolding(
false);
1955 target.addDynamicallyLegalDialect<LLVM::LLVMDialect>([](
Operation *op) {
1959 if (isa<LLVM::AllocaOp>(op)) {
1960 LLVM::AllocaOp aOp = cast<LLVM::AllocaOp>(op);
1961 LLVM::LLVMPointerType pTy = cast<LLVM::LLVMPointerType>(aOp.getType());
1962 auto addrSpace = pTy.getAddressSpace();
1963 return addrSpace != 3;
1966 return !op->hasDiscardableAttr(
"cache_control");
1968 target.addIllegalDialect<XeVMDialect>();
1969 patterns.
add<LoadStorePrefetchToOCLPattern<BlockLoad2dOp>,
1970 LoadStorePrefetchToOCLPattern<BlockStore2dOp>,
1971 LoadStorePrefetchToOCLPattern<BlockPrefetch2dOp>,
1972 MMAToOCLPattern, MemfenceToOCLPattern, PrefetchToOCLPattern,
1973 LLVMLoadStoreToOCLPattern<LLVM::LoadOp>,
1974 LLVMLoadStoreToOCLPattern<LLVM::StoreOp>,
1975 BlockLoadStore1DToOCLPattern<BlockLoadOp>,
1976 BlockLoadStore1DToOCLPattern<BlockStoreOp>,
1977 LaunchConfigOpToOCLPattern<WorkitemIdXOp>,
1978 LaunchConfigOpToOCLPattern<WorkitemIdYOp>,
1979 LaunchConfigOpToOCLPattern<WorkitemIdZOp>,
1980 LaunchConfigOpToOCLPattern<WorkgroupDimXOp>,
1981 LaunchConfigOpToOCLPattern<WorkgroupDimYOp>,
1982 LaunchConfigOpToOCLPattern<WorkgroupDimZOp>,
1983 LaunchConfigOpToOCLPattern<WorkgroupIdXOp>,
1984 LaunchConfigOpToOCLPattern<WorkgroupIdYOp>,
1985 LaunchConfigOpToOCLPattern<WorkgroupIdZOp>,
1986 LaunchConfigOpToOCLPattern<GridDimXOp>,
1987 LaunchConfigOpToOCLPattern<GridDimYOp>,
1988 LaunchConfigOpToOCLPattern<GridDimZOp>,
1989 SubgroupOpWorkitemOpToOCLPattern<LaneIdOp>,
1990 SubgroupOpWorkitemOpToOCLPattern<SubgroupIdOp>,
1991 SubgroupOpWorkitemOpToOCLPattern<SubgroupSizeOp>,
1992 TruncfToOCLPattern, ExtfToOCLPattern, MMAMxToOCLPattern,
1993 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...
NamedAttribute represents a combination of a name and an Attribute value.
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...