28#define DEBUG_TYPE "memref-to-spirv-pattern"
49 assert(targetBits % sourceBits == 0);
51 IntegerAttr idxAttr = builder.
getIntegerAttr(type, targetBits / sourceBits);
52 auto idx = builder.
createOrFold<spirv::ConstantOp>(loc, type, idxAttr);
53 IntegerAttr srcBitsAttr = builder.
getIntegerAttr(type, sourceBits);
55 builder.
createOrFold<spirv::ConstantOp>(loc, type, srcBitsAttr);
56 auto m = builder.
createOrFold<spirv::UModOp>(loc, srcIdx, idx);
57 return builder.
createOrFold<spirv::IMulOp>(loc, type, m, srcBitsValue);
70 spirv::AccessChainOp op,
int sourceBits,
72 assert(targetBits % sourceBits == 0);
73 const auto loc = op.getLoc();
74 Value lastDim = op->getOperand(op.getNumOperands() - 1);
76 IntegerAttr attr = builder.
getIntegerAttr(type, targetBits / sourceBits);
77 auto idx = builder.
createOrFold<spirv::ConstantOp>(loc, type, attr);
78 auto indices = llvm::to_vector<4>(op.getIndices());
82 Type t = typeConverter.convertType(op.getComponentPtr().getType());
83 return spirv::AccessChainOp::create(builder, loc, t, op.getBasePtr(),
93 Value zero = spirv::ConstantOp::getZero(dstType, loc, builder);
94 Value one = spirv::ConstantOp::getOne(dstType, loc, builder);
95 return builder.
createOrFold<spirv::SelectOp>(loc, dstType, srcBool, one,
103 IntegerType dstType = cast<IntegerType>(mask.
getType());
104 int targetBits =
static_cast<int>(dstType.getWidth());
106 assert(valueBits <= targetBits);
108 if (valueBits == 1) {
111 if (valueBits < targetBits) {
112 value = spirv::UConvertOp::create(
116 value = builder.
createOrFold<spirv::BitwiseAndOp>(loc, value, mask);
125 if (isa<memref::AllocOp, memref::DeallocOp>(allocOp)) {
126 auto sc = dyn_cast_or_null<spirv::StorageClassAttr>(type.getMemorySpace());
127 if (!sc || sc.getValue() != spirv::StorageClass::Workgroup)
129 }
else if (isa<memref::AllocaOp>(allocOp)) {
130 auto sc = dyn_cast_or_null<spirv::StorageClassAttr>(type.getMemorySpace());
131 if (!sc || sc.getValue() != spirv::StorageClass::Function)
139 if (!type.hasStaticShape())
142 Type elementType = type.getElementType();
143 if (
auto vecType = dyn_cast<VectorType>(elementType))
144 elementType = vecType.getElementType();
145 if (
auto compType = dyn_cast<ComplexType>(elementType))
146 elementType = compType.getElementType();
154 auto sc = dyn_cast_or_null<spirv::StorageClassAttr>(type.getMemorySpace());
155 switch (sc.getValue()) {
156 case spirv::StorageClass::StorageBuffer:
157 return spirv::Scope::Device;
158 case spirv::StorageClass::Workgroup:
159 return spirv::Scope::Workgroup;
169static spirv::MemorySemantics
172 case spirv::StorageClass::StorageBuffer:
173 case spirv::StorageClass::Uniform:
174 return spirv::MemorySemantics::UniformMemory;
175 case spirv::StorageClass::Workgroup:
176 return spirv::MemorySemantics::WorkgroupMemory;
177 case spirv::StorageClass::CrossWorkgroup:
178 return spirv::MemorySemantics::CrossWorkgroupMemory;
179 case spirv::StorageClass::AtomicCounter:
180 return spirv::MemorySemantics::AtomicCounterMemory;
181 case spirv::StorageClass::Image:
182 return spirv::MemorySemantics::ImageMemory;
184 return spirv::MemorySemantics::None;
191 auto sc = cast<spirv::StorageClassAttr>(type.getMemorySpace()).getValue();
192 return spirv::MemorySemantics::AcquireRelease |
205 if (typeConverter.
allows(spirv::Capability::Kernel)) {
206 if (
auto arrayType = dyn_cast<spirv::ArrayType>(pointeeType))
207 return arrayType.getElementType();
211 Type structElemType = cast<spirv::StructType>(pointeeType).getElementType(0);
212 if (
auto arrayType = dyn_cast<spirv::ArrayType>(structElemType))
213 return arrayType.getElementType();
214 return cast<spirv::RuntimeArrayType>(structElemType).getElementType();
222 auto one = spirv::ConstantOp::getZero(srcInt.
getType(), loc, builder);
223 return builder.
createOrFold<spirv::INotEqualOp>(loc, srcInt, one);
237class AllocaOpPattern final :
public OpConversionPattern<memref::AllocaOp> {
242 matchAndRewrite(memref::AllocaOp allocaOp, OpAdaptor adaptor,
243 ConversionPatternRewriter &rewriter)
const override;
250class AllocOpPattern final :
public OpConversionPattern<memref::AllocOp> {
255 matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor,
256 ConversionPatternRewriter &rewriter)
const override;
260class AtomicRMWOpPattern final
261 :
public OpConversionPattern<memref::AtomicRMWOp> {
266 matchAndRewrite(memref::AtomicRMWOp atomicOp, OpAdaptor adaptor,
267 ConversionPatternRewriter &rewriter)
const override;
272class DeallocOpPattern final :
public OpConversionPattern<memref::DeallocOp> {
277 matchAndRewrite(memref::DeallocOp operation, OpAdaptor adaptor,
278 ConversionPatternRewriter &rewriter)
const override;
282class IntLoadOpPattern final :
public OpConversionPattern<memref::LoadOp> {
287 matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
288 ConversionPatternRewriter &rewriter)
const override;
292class LoadOpPattern final :
public OpConversionPattern<memref::LoadOp> {
297 matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
298 ConversionPatternRewriter &rewriter)
const override;
302class ImageLoadOpPattern final :
public OpConversionPattern<memref::LoadOp> {
307 matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
308 ConversionPatternRewriter &rewriter)
const override;
312class IntStoreOpPattern final :
public OpConversionPattern<memref::StoreOp> {
317 matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
318 ConversionPatternRewriter &rewriter)
const override;
322class MemorySpaceCastOpPattern final
323 :
public OpConversionPattern<memref::MemorySpaceCastOp> {
328 matchAndRewrite(memref::MemorySpaceCastOp addrCastOp, OpAdaptor adaptor,
329 ConversionPatternRewriter &rewriter)
const override;
333class StoreOpPattern final :
public OpConversionPattern<memref::StoreOp> {
338 matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
339 ConversionPatternRewriter &rewriter)
const override;
343class CopyOpPattern final :
public OpConversionPattern<memref::CopyOp> {
348 matchAndRewrite(memref::CopyOp copyOp, OpAdaptor adaptor,
349 ConversionPatternRewriter &rewriter)
const override;
352class ReinterpretCastPattern final
353 :
public OpConversionPattern<memref::ReinterpretCastOp> {
358 matchAndRewrite(memref::ReinterpretCastOp op, OpAdaptor adaptor,
359 ConversionPatternRewriter &rewriter)
const override;
362class CastPattern final :
public OpConversionPattern<memref::CastOp> {
367 matchAndRewrite(memref::CastOp op, OpAdaptor adaptor,
368 ConversionPatternRewriter &rewriter)
const override {
369 Value src = adaptor.getSource();
372 const TypeConverter *converter = getTypeConverter();
373 Type dstType = converter->convertType(op.getType());
374 if (srcType != dstType)
375 return rewriter.notifyMatchFailure(op, [&](Diagnostic &
diag) {
376 diag <<
"types doesn't match: " << srcType <<
" and " << dstType;
379 rewriter.replaceOp(op, src);
385class ExtractAlignedPointerAsIndexOpPattern final
386 :
public OpConversionPattern<memref::ExtractAlignedPointerAsIndexOp> {
391 matchAndRewrite(memref::ExtractAlignedPointerAsIndexOp extractOp,
393 ConversionPatternRewriter &rewriter)
const override;
402AllocaOpPattern::matchAndRewrite(memref::AllocaOp allocaOp, OpAdaptor adaptor,
403 ConversionPatternRewriter &rewriter)
const {
404 MemRefType allocType = allocaOp.getType();
406 return rewriter.notifyMatchFailure(allocaOp,
"unhandled allocation type");
409 Type spirvType = getTypeConverter()->convertType(allocType);
411 return rewriter.notifyMatchFailure(allocaOp,
"type conversion failed");
413 rewriter.replaceOpWithNewOp<spirv::VariableOp>(allocaOp, spirvType,
414 spirv::StorageClass::Function,
424AllocOpPattern::matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor,
425 ConversionPatternRewriter &rewriter)
const {
426 MemRefType allocType = operation.getType();
428 return rewriter.notifyMatchFailure(operation,
"unhandled allocation type");
431 Type spirvType = getTypeConverter()->convertType(allocType);
433 return rewriter.notifyMatchFailure(operation,
"type conversion failed");
440 Location loc = operation.getLoc();
441 spirv::GlobalVariableOp varOp;
443 OpBuilder::InsertionGuard guard(rewriter);
445 rewriter.setInsertionPointToStart(&entryBlock);
446 auto varOps = entryBlock.
getOps<spirv::GlobalVariableOp>();
447 std::string varName =
448 std::string(
"__workgroup_mem__") +
449 std::to_string(std::distance(varOps.begin(), varOps.end()));
450 varOp = spirv::GlobalVariableOp::create(rewriter, loc, spirvType, varName,
455 rewriter.replaceOpWithNewOp<spirv::AddressOfOp>(operation, varOp);
464AtomicRMWOpPattern::matchAndRewrite(memref::AtomicRMWOp atomicOp,
466 ConversionPatternRewriter &rewriter)
const {
467 auto memrefType = cast<MemRefType>(atomicOp.getMemref().getType());
470 return rewriter.notifyMatchFailure(atomicOp,
471 "unsupported memref memory space");
473 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
474 Type resultType = typeConverter.convertType(atomicOp.getType());
476 return rewriter.notifyMatchFailure(atomicOp,
477 "failed to convert result type");
479 auto loc = atomicOp.getLoc();
482 adaptor.getIndices(), loc, rewriter);
490 int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
491 auto pointerType = typeConverter.convertType<spirv::PointerType>(memrefType);
493 return rewriter.notifyMatchFailure(atomicOp,
494 "failed to convert memref type");
496 Type pointeeType = pointerType.getPointeeType();
497 Type storageElemType =
499 if (!storageElemType || !storageElemType.
isIntOrFloat())
500 return rewriter.notifyMatchFailure(
501 atomicOp,
"failed to determine destination element type");
504 assert(dstBits % srcBits == 0);
510 if (srcBits == dstBits) {
511#define ATOMIC_CASE(kind, spirvOp) \
512 case arith::AtomicRMWKind::kind: \
513 rewriter.replaceOpWithNewOp<spirv::spirvOp>( \
514 atomicOp, resultType, ptr, *scope, memSem, adaptor.getValue()); \
517 switch (atomicOp.getKind()) {
527 return rewriter.notifyMatchFailure(atomicOp,
"unimplemented atomic kind");
542 if (atomicOp.getKind() != arith::AtomicRMWKind::ori &&
543 atomicOp.getKind() != arith::AtomicRMWKind::andi) {
544 return rewriter.notifyMatchFailure(
546 "atomic op on sub-element-width types is only supported for ori/andi");
551 if (typeConverter.allows(spirv::Capability::Kernel))
552 return rewriter.notifyMatchFailure(
554 "sub-element-width atomic ops unsupported with Kernel capability");
556 auto dstType = cast<IntegerType>(storageElemType);
558 auto accessChainOp = ptr.
getDefiningOp<spirv::AccessChainOp>();
564 assert(accessChainOp.getIndices().size() == 2);
565 Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
568 srcBits, dstBits, rewriter);
570 switch (atomicOp.getKind()) {
571 case arith::AtomicRMWKind::ori: {
574 Value elemMask = rewriter.createOrFold<spirv::ConstantOp>(
575 loc, dstType, rewriter.getIntegerAttr(dstType, (1uLL << srcBits) - 1));
577 shiftValue(loc, adaptor.getValue(), offset, elemMask, rewriter);
578 result = spirv::AtomicOrOp::create(rewriter, loc, dstType, adjustedPtr,
579 *scope, memSem, storeVal);
582 case arith::AtomicRMWKind::andi: {
586 Value elemMask = rewriter.createOrFold<spirv::ConstantOp>(
587 loc, dstType, rewriter.getIntegerAttr(dstType, (1uLL << srcBits) - 1));
589 shiftValue(loc, adaptor.getValue(), offset, elemMask, rewriter);
590 Value shiftedElemMask = rewriter.createOrFold<spirv::ShiftLeftLogicalOp>(
591 loc, dstType, elemMask, offset);
592 Value invertedElemMask =
593 rewriter.createOrFold<spirv::NotOp>(loc, dstType, shiftedElemMask);
594 Value mask = rewriter.createOrFold<spirv::BitwiseOrOp>(loc, storeVal,
596 result = spirv::AtomicAndOp::create(rewriter, loc, dstType, adjustedPtr,
597 *scope, memSem, mask);
601 return rewriter.notifyMatchFailure(atomicOp,
"unimplemented atomic kind");
606 result = rewriter.createOrFold<spirv::ShiftRightLogicalOp>(loc, dstType,
608 Value mask = rewriter.createOrFold<spirv::ConstantOp>(
609 loc, dstType, rewriter.getIntegerAttr(dstType, (1uLL << srcBits) - 1));
611 rewriter.createOrFold<spirv::BitwiseAndOp>(loc, dstType,
result, mask);
612 rewriter.replaceOp(atomicOp,
result);
622DeallocOpPattern::matchAndRewrite(memref::DeallocOp operation,
624 ConversionPatternRewriter &rewriter)
const {
625 MemRefType deallocType = cast<MemRefType>(operation.getMemref().getType());
627 return rewriter.notifyMatchFailure(operation,
"unhandled allocation type");
628 rewriter.eraseOp(operation);
643static FailureOr<MemoryRequirements>
645 uint64_t preferredAlignment) {
646 if (preferredAlignment >= std::numeric_limits<uint32_t>::max()) {
652 auto memoryAccess = spirv::MemoryAccess::None;
654 memoryAccess = spirv::MemoryAccess::Nontemporal;
657 auto ptrType = cast<spirv::PointerType>(accessedPtr.
getType());
658 bool mayOmitAlignment =
659 !preferredAlignment &&
660 ptrType.getStorageClass() != spirv::StorageClass::PhysicalStorageBuffer;
661 if (mayOmitAlignment) {
662 if (memoryAccess == spirv::MemoryAccess::None) {
671 std::optional<int64_t> sizeInBytes;
672 Type rawPointeeType = ptrType.getPointeeType();
673 if (
auto scalarType = dyn_cast<spirv::ScalarType>(rawPointeeType)) {
675 sizeInBytes = scalarType.getSizeInBytes();
676 }
else if (
auto vecType = dyn_cast<VectorType>(rawPointeeType)) {
679 if (
auto scalarElem =
680 dyn_cast<spirv::ScalarType>(vecType.getElementType())) {
681 if (
auto elemSize = scalarElem.getSizeInBytes())
682 sizeInBytes = *elemSize * vecType.getNumElements();
686 if (!sizeInBytes.has_value())
689 memoryAccess |= spirv::MemoryAccess::Aligned;
690 auto memAccessAttr = spirv::MemoryAccessAttr::get(ctx, memoryAccess);
691 auto alignmentValue = preferredAlignment ? preferredAlignment : *sizeInBytes;
692 auto alignment = IntegerAttr::get(IntegerType::get(ctx, 32), alignmentValue);
699template <
class LoadOrStoreOp>
700static FailureOr<MemoryRequirements>
703 llvm::is_one_of<LoadOrStoreOp, memref::LoadOp, memref::StoreOp>::value,
704 "Must be called on either memref::LoadOp or memref::StoreOp");
707 loadOrStoreOp.getNontemporal(),
708 loadOrStoreOp.getAlignment().value_or(0));
712IntLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
713 ConversionPatternRewriter &rewriter)
const {
714 auto loc = loadOp.getLoc();
715 auto memrefType = cast<MemRefType>(loadOp.getMemref().getType());
716 if (!memrefType.getElementType().isSignlessInteger())
719 auto memorySpaceAttr =
720 dyn_cast_if_present<spirv::StorageClassAttr>(memrefType.getMemorySpace());
721 if (!memorySpaceAttr)
722 return rewriter.notifyMatchFailure(
723 loadOp,
"missing memory space SPIR-V storage class attribute");
725 if (memorySpaceAttr.getValue() == spirv::StorageClass::Image)
726 return rewriter.notifyMatchFailure(
728 "failed to lower memref in image storage class to storage buffer");
730 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
733 adaptor.getIndices(), loc, rewriter);
738 int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
739 bool isBool = srcBits == 1;
741 srcBits = typeConverter.getOptions().boolNumBits;
743 auto pointerType = typeConverter.convertType<spirv::PointerType>(memrefType);
745 return rewriter.notifyMatchFailure(loadOp,
"failed to convert memref type");
747 Type pointeeType = pointerType.getPointeeType();
750 assert(dstBits % srcBits == 0);
754 if (srcBits == dstBits) {
756 if (
failed(memoryRequirements))
757 return rewriter.notifyMatchFailure(
758 loadOp,
"failed to determine memory requirements");
760 auto [memoryAccess, alignment] = *memoryRequirements;
761 Value loadVal = spirv::LoadOp::create(rewriter, loc, accessChain,
762 memoryAccess, alignment);
765 rewriter.replaceOp(loadOp, loadVal);
771 if (typeConverter.allows(spirv::Capability::Kernel))
774 auto accessChainOp = accessChain.
getDefiningOp<spirv::AccessChainOp>();
781 assert(accessChainOp.getIndices().size() == 2);
783 srcBits, dstBits, rewriter);
785 if (
failed(memoryRequirements))
786 return rewriter.notifyMatchFailure(
787 loadOp,
"failed to determine memory requirements");
789 auto [memoryAccess, alignment] = *memoryRequirements;
790 Value spvLoadOp = spirv::LoadOp::create(rewriter, loc, dstType, adjustedPtr,
791 memoryAccess, alignment);
795 Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
797 Value
result = rewriter.createOrFold<spirv::ShiftRightArithmeticOp>(
798 loc, spvLoadOp.
getType(), spvLoadOp, offset);
801 Value mask = rewriter.createOrFold<spirv::ConstantOp>(
802 loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
804 rewriter.createOrFold<spirv::BitwiseAndOp>(loc, dstType,
result, mask);
809 IntegerAttr shiftValueAttr =
810 rewriter.getIntegerAttr(dstType, dstBits - srcBits);
812 rewriter.createOrFold<spirv::ConstantOp>(loc, dstType, shiftValueAttr);
813 result = rewriter.createOrFold<spirv::ShiftLeftLogicalOp>(loc, dstType,
815 result = rewriter.createOrFold<spirv::ShiftRightArithmeticOp>(
818 rewriter.replaceOp(loadOp,
result);
820 assert(accessChainOp.use_empty());
821 rewriter.eraseOp(accessChainOp);
827LoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
828 ConversionPatternRewriter &rewriter)
const {
829 auto memrefType = cast<MemRefType>(loadOp.getMemref().getType());
830 if (memrefType.getElementType().isSignlessInteger())
833 auto memorySpaceAttr =
834 dyn_cast_if_present<spirv::StorageClassAttr>(memrefType.getMemorySpace());
835 if (!memorySpaceAttr)
836 return rewriter.notifyMatchFailure(
837 loadOp,
"missing memory space SPIR-V storage class attribute");
839 if (memorySpaceAttr.getValue() == spirv::StorageClass::Image)
840 return rewriter.notifyMatchFailure(
842 "failed to lower memref in image storage class to storage buffer");
845 *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.getMemref(),
846 adaptor.getIndices(), loadOp.getLoc(), rewriter);
852 if (
failed(memoryRequirements))
853 return rewriter.notifyMatchFailure(
854 loadOp,
"failed to determine memory requirements");
856 auto [memoryAccess, alignment] = *memoryRequirements;
857 rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, loadPtr, memoryAccess,
862template <
typename OpAdaptor>
863static FailureOr<SmallVector<Value>>
865 ConversionPatternRewriter &rewriter) {
872 AffineMap map = loadOp.getMemRefType().getLayout().getAffineMap();
874 return rewriter.notifyMatchFailure(
876 "Cannot lower memrefs with memory layout which is not a permutation");
882 for (
unsigned dim = 0; dim < dimCount; ++dim)
888 return llvm::to_vector(llvm::reverse(coords));
892ImageLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
893 ConversionPatternRewriter &rewriter)
const {
894 auto memrefType = cast<MemRefType>(loadOp.getMemref().getType());
896 auto memorySpaceAttr =
897 dyn_cast_if_present<spirv::StorageClassAttr>(memrefType.getMemorySpace());
898 if (!memorySpaceAttr)
899 return rewriter.notifyMatchFailure(
900 loadOp,
"missing memory space SPIR-V storage class attribute");
902 if (memorySpaceAttr.getValue() != spirv::StorageClass::Image)
903 return rewriter.notifyMatchFailure(
904 loadOp,
"failed to lower memref in non-image storage class to image");
906 Value loadPtr = adaptor.getMemref();
908 if (
failed(memoryRequirements))
909 return rewriter.notifyMatchFailure(
910 loadOp,
"failed to determine memory requirements");
912 const auto [memoryAccess, alignment] = *memoryRequirements;
914 if (!loadOp.getMemRefType().hasRank())
915 return rewriter.notifyMatchFailure(
916 loadOp,
"cannot lower unranked memrefs to SPIR-V images");
921 if (!isa<spirv::ScalarType>(loadOp.getMemRefType().getElementType()))
922 return rewriter.notifyMatchFailure(
924 "cannot lower memrefs who's element type is not a SPIR-V scalar type"
931 auto convertedPointeeType = cast<spirv::PointerType>(
932 getTypeConverter()->convertType(loadOp.getMemRefType()));
933 if (!isa<spirv::SampledImageType>(convertedPointeeType.getPointeeType()))
934 return rewriter.notifyMatchFailure(loadOp,
935 "cannot lower memrefs which do not "
936 "convert to SPIR-V sampled images");
939 Location loc = loadOp->getLoc();
941 spirv::LoadOp::create(rewriter, loc, loadPtr, memoryAccess, alignment);
943 auto imageOp = spirv::ImageOp::create(rewriter, loc, imageLoadOp);
947 if (memrefType.getRank() == 1) {
948 coords = adaptor.getIndices()[0];
950 FailureOr<SmallVector<Value>> maybeCoords =
954 auto coordVectorType = VectorType::get({loadOp.getMemRefType().getRank()},
955 adaptor.getIndices().
getType()[0]);
956 coords = spirv::CompositeConstructOp::create(rewriter, loc, coordVectorType,
957 maybeCoords.value());
961 auto resultVectorType = VectorType::get({4}, loadOp.getType());
962 auto fetchOp = spirv::ImageFetchOp::create(
963 rewriter, loc, resultVectorType, imageOp, coords,
964 mlir::spirv::ImageOperandsAttr{},
ValueRange{});
969 auto compositeExtractOp =
970 spirv::CompositeExtractOp::create(rewriter, loc, fetchOp, 0);
972 rewriter.replaceOp(loadOp, compositeExtractOp);
977IntStoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
978 ConversionPatternRewriter &rewriter)
const {
979 auto memrefType = cast<MemRefType>(storeOp.getMemref().getType());
980 if (!memrefType.getElementType().isSignlessInteger())
981 return rewriter.notifyMatchFailure(storeOp,
982 "element type is not a signless int");
984 auto loc = storeOp.getLoc();
985 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
988 adaptor.getIndices(), loc, rewriter);
991 return rewriter.notifyMatchFailure(
992 storeOp,
"failed to convert element pointer type");
994 int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
996 bool isBool = srcBits == 1;
998 srcBits = typeConverter.getOptions().boolNumBits;
1000 auto pointerType = typeConverter.convertType<spirv::PointerType>(memrefType);
1002 return rewriter.notifyMatchFailure(storeOp,
1003 "failed to convert memref type");
1005 Type pointeeType = pointerType.getPointeeType();
1006 auto dstType = dyn_cast<IntegerType>(
1009 return rewriter.notifyMatchFailure(
1010 storeOp,
"failed to determine destination element type");
1012 int dstBits =
static_cast<int>(dstType.getWidth());
1013 assert(dstBits % srcBits == 0);
1015 if (srcBits == dstBits) {
1017 if (
failed(memoryRequirements))
1018 return rewriter.notifyMatchFailure(
1019 storeOp,
"failed to determine memory requirements");
1021 auto [memoryAccess, alignment] = *memoryRequirements;
1022 Value storeVal = adaptor.getValue();
1025 rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, accessChain, storeVal,
1026 memoryAccess, alignment);
1032 if (typeConverter.allows(spirv::Capability::Kernel))
1035 auto accessChainOp = accessChain.
getDefiningOp<spirv::AccessChainOp>();
1050 assert(accessChainOp.getIndices().size() == 2);
1051 Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
1056 Value mask = rewriter.createOrFold<spirv::ConstantOp>(
1057 loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
1058 Value clearBitsMask = rewriter.createOrFold<spirv::ShiftLeftLogicalOp>(
1059 loc, dstType, mask, offset);
1061 rewriter.createOrFold<spirv::NotOp>(loc, dstType, clearBitsMask);
1063 Value storeVal =
shiftValue(loc, adaptor.getValue(), offset, mask, rewriter);
1065 srcBits, dstBits, rewriter);
1068 return rewriter.notifyMatchFailure(storeOp,
"atomic scope not available");
1071 Value
result = spirv::AtomicAndOp::create(rewriter, loc, dstType, adjustedPtr,
1072 *scope, memSem, clearBitsMask);
1073 result = spirv::AtomicOrOp::create(rewriter, loc, dstType, adjustedPtr,
1074 *scope, memSem, storeVal);
1080 rewriter.eraseOp(storeOp);
1082 assert(accessChainOp.use_empty());
1083 rewriter.eraseOp(accessChainOp);
1092LogicalResult MemorySpaceCastOpPattern::matchAndRewrite(
1093 memref::MemorySpaceCastOp addrCastOp, OpAdaptor adaptor,
1094 ConversionPatternRewriter &rewriter)
const {
1095 Location loc = addrCastOp.getLoc();
1096 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
1097 if (!typeConverter.allows(spirv::Capability::Kernel))
1098 return rewriter.notifyMatchFailure(
1099 loc,
"address space casts require kernel capability");
1101 auto sourceType = dyn_cast<MemRefType>(addrCastOp.getSource().getType());
1103 return rewriter.notifyMatchFailure(
1104 loc,
"SPIR-V lowering requires ranked memref types");
1105 auto resultType = cast<MemRefType>(addrCastOp.getResult().getType());
1107 auto sourceStorageClassAttr =
1108 dyn_cast_or_null<spirv::StorageClassAttr>(sourceType.getMemorySpace());
1109 if (!sourceStorageClassAttr)
1110 return rewriter.notifyMatchFailure(loc, [sourceType](Diagnostic &
diag) {
1111 diag <<
"source address space " << sourceType.getMemorySpace()
1112 <<
" must be a SPIR-V storage class";
1114 auto resultStorageClassAttr =
1115 dyn_cast_or_null<spirv::StorageClassAttr>(resultType.getMemorySpace());
1116 if (!resultStorageClassAttr)
1117 return rewriter.notifyMatchFailure(loc, [resultType](Diagnostic &
diag) {
1118 diag <<
"result address space " << resultType.getMemorySpace()
1119 <<
" must be a SPIR-V storage class";
1122 spirv::StorageClass sourceSc = sourceStorageClassAttr.getValue();
1123 spirv::StorageClass resultSc = resultStorageClassAttr.getValue();
1125 Value
result = adaptor.getSource();
1126 Type resultPtrType = typeConverter.convertType(resultType);
1128 return rewriter.notifyMatchFailure(addrCastOp,
1129 "failed to convert memref type");
1131 Type genericPtrType = resultPtrType;
1139 if (sourceSc != spirv::StorageClass::Generic &&
1140 resultSc != spirv::StorageClass::Generic) {
1141 Type intermediateType =
1142 MemRefType::get(sourceType.getShape(), sourceType.getElementType(),
1143 sourceType.getLayout(),
1144 rewriter.getAttr<spirv::StorageClassAttr>(
1145 spirv::StorageClass::Generic));
1146 genericPtrType = typeConverter.convertType(intermediateType);
1148 if (sourceSc != spirv::StorageClass::Generic) {
1149 result = spirv::PtrCastToGenericOp::create(rewriter, loc, genericPtrType,
1152 if (resultSc != spirv::StorageClass::Generic) {
1154 spirv::GenericCastToPtrOp::create(rewriter, loc, resultPtrType,
result);
1156 rewriter.replaceOp(addrCastOp,
result);
1161StoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
1162 ConversionPatternRewriter &rewriter)
const {
1163 auto memrefType = cast<MemRefType>(storeOp.getMemref().getType());
1164 if (memrefType.getElementType().isSignlessInteger())
1165 return rewriter.notifyMatchFailure(storeOp,
"signless int");
1167 *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.getMemref(),
1168 adaptor.getIndices(), storeOp.getLoc(), rewriter);
1171 return rewriter.notifyMatchFailure(storeOp,
"type conversion failed");
1174 if (
failed(memoryRequirements))
1175 return rewriter.notifyMatchFailure(
1176 storeOp,
"failed to determine memory requirements");
1178 auto [memoryAccess, alignment] = *memoryRequirements;
1179 rewriter.replaceOpWithNewOp<spirv::StoreOp>(
1180 storeOp, storePtr, adaptor.getValue(), memoryAccess, alignment);
1189CopyOpPattern::matchAndRewrite(memref::CopyOp copyOp, OpAdaptor adaptor,
1190 ConversionPatternRewriter &rewriter)
const {
1191 auto memrefType = cast<MemRefType>(copyOp.getSource().getType());
1192 if (!memrefType.hasStaticShape())
1193 return rewriter.notifyMatchFailure(copyOp,
"unsupported dynamic shape");
1195 for (MemRefType type :
1196 {memrefType, cast<MemRefType>(copyOp.getTarget().getType())}) {
1197 auto memorySpaceAttr =
1198 dyn_cast_if_present<spirv::StorageClassAttr>(type.getMemorySpace());
1199 if (memorySpaceAttr &&
1200 memorySpaceAttr.getValue() == spirv::StorageClass::Image)
1201 return rewriter.notifyMatchFailure(
1202 copyOp,
"cannot lower memref.copy in image storage class");
1208 Value source = adaptor.getSource();
1209 Value
target = adaptor.getTarget();
1210 auto sourcePtrType = dyn_cast<spirv::PointerType>(source.
getType());
1211 auto targetPtrType = dyn_cast<spirv::PointerType>(
target.getType());
1212 if (!sourcePtrType || !targetPtrType)
1213 return rewriter.notifyMatchFailure(copyOp,
"failed to convert memref type");
1215 if (sourcePtrType.getPointeeType() != targetPtrType.getPointeeType())
1216 return rewriter.notifyMatchFailure(
1217 copyOp,
"source and target pointee types do not match");
1219 rewriter.replaceOpWithNewOp<spirv::CopyMemoryOp>(
1220 copyOp,
target, source, spirv::MemoryAccessAttr{},
1222 spirv::MemoryAccessAttr{}, IntegerAttr{});
1226LogicalResult ReinterpretCastPattern::matchAndRewrite(
1227 memref::ReinterpretCastOp op, OpAdaptor adaptor,
1228 ConversionPatternRewriter &rewriter)
const {
1229 Value src = adaptor.getSource();
1230 auto srcType = dyn_cast<spirv::PointerType>(src.
getType());
1233 return rewriter.notifyMatchFailure(op, [&](Diagnostic &
diag) {
1237 const TypeConverter *converter = getTypeConverter();
1239 auto dstType = converter->convertType<spirv::PointerType>(op.getType());
1240 if (dstType != srcType)
1241 return rewriter.notifyMatchFailure(op, [&](Diagnostic &
diag) {
1242 diag <<
"invalid dst type " << op.getType();
1245 OpFoldResult offset =
1246 getMixedValues(adaptor.getStaticOffsets(), adaptor.getOffsets(), rewriter)
1249 rewriter.replaceOp(op, src);
1253 Type intType = converter->convertType(rewriter.getIndexType());
1255 return rewriter.notifyMatchFailure(op,
"failed to convert index type");
1257 Location loc = op.getLoc();
1258 auto offsetValue = [&]() -> Value {
1259 if (
auto val = dyn_cast<Value>(offset))
1262 int64_t attrVal = cast<IntegerAttr>(cast<Attribute>(offset)).getInt();
1263 Attribute attr = rewriter.getIntegerAttr(intType, attrVal);
1264 return rewriter.createOrFold<spirv::ConstantOp>(loc, intType, attr);
1267 rewriter.replaceOpWithNewOp<spirv::InBoundsPtrAccessChainOp>(
1276LogicalResult ExtractAlignedPointerAsIndexOpPattern::matchAndRewrite(
1277 memref::ExtractAlignedPointerAsIndexOp extractOp, OpAdaptor adaptor,
1278 ConversionPatternRewriter &rewriter)
const {
1279 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
1280 Type indexType = typeConverter.getIndexType();
1281 rewriter.replaceOpWithNewOp<spirv::ConvertPtrToUOp>(extractOp, indexType,
1282 adaptor.getSource());
1293 patterns.
add<AllocaOpPattern, AllocOpPattern, AtomicRMWOpPattern,
1294 CopyOpPattern, DeallocOpPattern, IntLoadOpPattern,
1295 ImageLoadOpPattern, IntStoreOpPattern, LoadOpPattern,
1296 MemorySpaceCastOpPattern, StoreOpPattern, ReinterpretCastPattern,
1297 CastPattern, ExtractAlignedPointerAsIndexOpPattern>(
static spirv::MemorySemantics getMemorySemanticsForStorageClass(spirv::StorageClass sc)
Returns the MemorySemantics storage-class bit corresponding to sc.
static Value castIntNToBool(Location loc, Value srcInt, OpBuilder &builder)
Casts the given srcInt into a boolean value.
static Type getElementTypeForStoragePointer(Type pointeeType, const SPIRVTypeConverter &typeConverter)
Extracts the element type from a SPIR-V pointer type pointing to storage.
static std::optional< spirv::Scope > getAtomicOpScope(MemRefType type)
Returns the scope to use for atomic operations use for emulating store operations of unsupported inte...
static Value shiftValue(Location loc, Value value, Value offset, Value mask, OpBuilder &builder)
Returns the targetBits-bit value shifted by the given offset, and cast to the type destination type,...
static FailureOr< SmallVector< Value > > extractLoadCoordsForComposite(memref::LoadOp loadOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter)
static Value adjustAccessChainForBitwidth(const SPIRVTypeConverter &typeConverter, spirv::AccessChainOp op, int sourceBits, int targetBits, OpBuilder &builder)
Returns an adjusted spirv::AccessChainOp.
static bool isAllocationSupported(Operation *allocOp, MemRefType type)
Returns true if the allocations of memref type generated from allocOp can be lowered to SPIR-V.
static Value getOffsetForBitwidth(Location loc, Value srcIdx, int sourceBits, int targetBits, OpBuilder &builder)
Returns the offset of the value in targetBits representation.
static spirv::MemorySemantics getAtomicAcqRelMemorySemantics(MemRefType type)
Returns the AcquireRelease memory semantics OR'd with the storage-class bit derived from the memory s...
#define ATOMIC_CASE(kind, spirvOp)
static FailureOr< MemoryRequirements > calculateMemoryRequirements(Value accessedPtr, bool isNontemporal, uint64_t preferredAlignment)
Given an accessed SPIR-V pointer, calculates its alignment requirements, if any.
static Value castBoolToIntN(Location loc, Value srcBool, Type dstType, OpBuilder &builder)
Casts the given srcBool into an integer of dstType.
static std::string diag(const llvm::Value &value)
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
unsigned getDimPosition(unsigned idx) const
Extracts the position of the dimensional expression at the given result, when the caller knows it is ...
unsigned getNumDims() const
bool isPermutation() const
Returns true if the AffineMap represents a symbol-less permutation map.
iterator_range< op_iterator< OpT > > getOps()
Return an iterator range over the operations within this block that are of 'OpT'.
IntegerAttr getIntegerAttr(Type type, int64_t value)
IntegerType getIntegerType(unsigned width)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This class helps build Operations.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Operation is the basic unit of execution within MLIR.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
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.
Type conversion from builtin types to SPIR-V types for shader interface.
bool allows(spirv::Capability capability) const
Checks if the SPIR-V capability inquired is supported.
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isInteger() const
Return true if this is an integer type (with the specified width).
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
MLIRContext * getContext() const
Utility to get the associated MLIRContext that this value is defined in.
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.
Value getElementPtr(const SPIRVTypeConverter &typeConverter, MemRefType baseType, Value basePtr, ValueRange indices, Location loc, OpBuilder &builder)
Performs the index computation to get to the element at indices of the memory pointed to by basePtr,...
Include the generated interface declarations.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void populateMemRefToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
Appends to a pattern list additional patterns for translating MemRef ops to SPIR-V ops.
spirv::MemoryAccessAttr memoryAccess