29#define DEBUG_TYPE "memref-to-spirv-pattern"
50 assert(targetBits % sourceBits == 0);
52 IntegerAttr idxAttr = builder.
getIntegerAttr(type, targetBits / sourceBits);
53 auto idx = builder.
createOrFold<spirv::ConstantOp>(loc, type, idxAttr);
54 IntegerAttr srcBitsAttr = builder.
getIntegerAttr(type, sourceBits);
56 builder.
createOrFold<spirv::ConstantOp>(loc, type, srcBitsAttr);
57 auto m = builder.
createOrFold<spirv::UModOp>(loc, srcIdx, idx);
58 return builder.
createOrFold<spirv::IMulOp>(loc, type, m, srcBitsValue);
71 spirv::AccessChainOp op,
int sourceBits,
73 assert(targetBits % sourceBits == 0);
74 const auto loc = op.getLoc();
75 Value lastDim = op->getOperand(op.getNumOperands() - 1);
77 IntegerAttr attr = builder.
getIntegerAttr(type, targetBits / sourceBits);
78 auto idx = builder.
createOrFold<spirv::ConstantOp>(loc, type, attr);
79 auto indices = llvm::to_vector<4>(op.getIndices());
83 Type t = typeConverter.convertType(op.getComponentPtr().getType());
84 return spirv::AccessChainOp::create(builder, loc, t, op.getBasePtr(),
94 Value zero = spirv::ConstantOp::getZero(dstType, loc, builder);
95 Value one = spirv::ConstantOp::getOne(dstType, loc, builder);
96 return builder.
createOrFold<spirv::SelectOp>(loc, dstType, srcBool, one,
104 IntegerType dstType = cast<IntegerType>(mask.
getType());
105 int targetBits =
static_cast<int>(dstType.getWidth());
107 assert(valueBits <= targetBits);
109 if (valueBits == 1) {
112 if (valueBits < targetBits) {
113 value = spirv::UConvertOp::create(
117 value = builder.
createOrFold<spirv::BitwiseAndOp>(loc, value, mask);
127 if (!type.hasStaticShape())
130 if (isa<memref::AllocOp, memref::DeallocOp>(allocOp)) {
131 auto sc = dyn_cast_or_null<spirv::StorageClassAttr>(type.getMemorySpace());
132 if (!sc || sc.getValue() != spirv::StorageClass::Workgroup)
134 }
else if (isa<memref::AllocaOp>(allocOp)) {
135 auto sc = dyn_cast_or_null<spirv::StorageClassAttr>(type.getMemorySpace());
136 if (!sc || sc.getValue() != spirv::StorageClass::Function)
140 if (isa<MemRefElementTypeInterface>(type.getElementType()))
147 Type elementType = type.getElementType();
148 if (
auto vecType = dyn_cast<VectorType>(elementType))
149 elementType = vecType.getElementType();
150 if (
auto compType = dyn_cast<ComplexType>(elementType))
151 elementType = compType.getElementType();
159 auto sc = dyn_cast_or_null<spirv::StorageClassAttr>(type.getMemorySpace());
160 switch (sc.getValue()) {
161 case spirv::StorageClass::StorageBuffer:
162 return spirv::Scope::Device;
163 case spirv::StorageClass::Workgroup:
164 return spirv::Scope::Workgroup;
174static spirv::MemorySemantics
177 case spirv::StorageClass::StorageBuffer:
178 case spirv::StorageClass::Uniform:
179 return spirv::MemorySemantics::UniformMemory;
180 case spirv::StorageClass::Workgroup:
181 return spirv::MemorySemantics::WorkgroupMemory;
182 case spirv::StorageClass::CrossWorkgroup:
183 return spirv::MemorySemantics::CrossWorkgroupMemory;
184 case spirv::StorageClass::AtomicCounter:
185 return spirv::MemorySemantics::AtomicCounterMemory;
186 case spirv::StorageClass::Image:
187 return spirv::MemorySemantics::ImageMemory;
189 return spirv::MemorySemantics::None;
196 auto sc = cast<spirv::StorageClassAttr>(type.getMemorySpace()).getValue();
197 return spirv::MemorySemantics::AcquireRelease |
210 if (typeConverter.
allows(spirv::Capability::Kernel)) {
211 if (
auto arrayType = dyn_cast<spirv::ArrayType>(pointeeType))
212 return arrayType.getElementType();
216 Type structElemType = cast<spirv::StructType>(pointeeType).getElementType(0);
217 if (
auto arrayType = dyn_cast<spirv::ArrayType>(structElemType))
218 return arrayType.getElementType();
219 return cast<spirv::RuntimeArrayType>(structElemType).getElementType();
227 auto one = spirv::ConstantOp::getZero(srcInt.
getType(), loc, builder);
228 return builder.
createOrFold<spirv::INotEqualOp>(loc, srcInt, one);
242class AllocaOpPattern final :
public OpConversionPattern<memref::AllocaOp> {
247 matchAndRewrite(memref::AllocaOp allocaOp, OpAdaptor adaptor,
248 ConversionPatternRewriter &rewriter)
const override;
255class AllocOpPattern final :
public OpConversionPattern<memref::AllocOp> {
260 matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor,
261 ConversionPatternRewriter &rewriter)
const override;
265class AtomicRMWOpPattern final
266 :
public OpConversionPattern<memref::AtomicRMWOp> {
271 matchAndRewrite(memref::AtomicRMWOp atomicOp, OpAdaptor adaptor,
272 ConversionPatternRewriter &rewriter)
const override;
277class DeallocOpPattern final :
public OpConversionPattern<memref::DeallocOp> {
282 matchAndRewrite(memref::DeallocOp operation, OpAdaptor adaptor,
283 ConversionPatternRewriter &rewriter)
const override;
287class IntLoadOpPattern final :
public OpConversionPattern<memref::LoadOp> {
292 matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
293 ConversionPatternRewriter &rewriter)
const override;
297class LoadOpPattern final :
public OpConversionPattern<memref::LoadOp> {
302 matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
303 ConversionPatternRewriter &rewriter)
const override;
307class ImageLoadOpPattern final :
public OpConversionPattern<memref::LoadOp> {
312 matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
313 ConversionPatternRewriter &rewriter)
const override;
317class IntStoreOpPattern final :
public OpConversionPattern<memref::StoreOp> {
322 matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
323 ConversionPatternRewriter &rewriter)
const override;
327class MemorySpaceCastOpPattern final
328 :
public OpConversionPattern<memref::MemorySpaceCastOp> {
333 matchAndRewrite(memref::MemorySpaceCastOp addrCastOp, OpAdaptor adaptor,
334 ConversionPatternRewriter &rewriter)
const override;
338class StoreOpPattern final :
public OpConversionPattern<memref::StoreOp> {
343 matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
344 ConversionPatternRewriter &rewriter)
const override;
348class CopyOpPattern final :
public OpConversionPattern<memref::CopyOp> {
353 matchAndRewrite(memref::CopyOp copyOp, OpAdaptor adaptor,
354 ConversionPatternRewriter &rewriter)
const override;
357class ReinterpretCastPattern final
358 :
public OpConversionPattern<memref::ReinterpretCastOp> {
363 matchAndRewrite(memref::ReinterpretCastOp op, OpAdaptor adaptor,
364 ConversionPatternRewriter &rewriter)
const override;
367class CastPattern final :
public OpConversionPattern<memref::CastOp> {
372 matchAndRewrite(memref::CastOp op, OpAdaptor adaptor,
373 ConversionPatternRewriter &rewriter)
const override {
374 Value src = adaptor.getSource();
377 const TypeConverter *converter = getTypeConverter();
378 Type dstType = converter->convertType(op.getType());
379 if (srcType != dstType)
380 return rewriter.notifyMatchFailure(op, [&](Diagnostic &
diag) {
381 diag <<
"types doesn't match: " << srcType <<
" and " << dstType;
384 rewriter.replaceOp(op, src);
390class ExtractAlignedPointerAsIndexOpPattern final
391 :
public OpConversionPattern<memref::ExtractAlignedPointerAsIndexOp> {
396 matchAndRewrite(memref::ExtractAlignedPointerAsIndexOp extractOp,
398 ConversionPatternRewriter &rewriter)
const override;
407AllocaOpPattern::matchAndRewrite(memref::AllocaOp allocaOp, OpAdaptor adaptor,
408 ConversionPatternRewriter &rewriter)
const {
409 MemRefType allocType = allocaOp.getType();
411 return rewriter.notifyMatchFailure(allocaOp,
"unhandled allocation type");
414 Type spirvType = getTypeConverter()->convertType(allocType);
416 return rewriter.notifyMatchFailure(allocaOp,
"type conversion failed");
418 auto function = allocaOp->getParentOfType<FunctionOpInterface>();
420 return rewriter.notifyMatchFailure(allocaOp,
421 "requires a containing function");
424 OpBuilder::InsertionGuard guard(rewriter);
425 Block &entryBlock = function->getRegion(0).front();
428 while (insertionPoint != entryBlock.
end() &&
429 isa<spirv::VariableOp>(*insertionPoint))
431 rewriter.setInsertionPoint(&entryBlock, insertionPoint);
432 Value variable = spirv::VariableOp::create(
433 rewriter, allocaOp.getLoc(), spirvType, spirv::StorageClass::Function,
435 rewriter.replaceOp(allocaOp, variable);
444AllocOpPattern::matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor,
445 ConversionPatternRewriter &rewriter)
const {
446 MemRefType allocType = operation.getType();
448 return rewriter.notifyMatchFailure(operation,
"unhandled allocation type");
451 Type spirvType = getTypeConverter()->convertType(allocType);
453 return rewriter.notifyMatchFailure(operation,
"type conversion failed");
460 Location loc = operation.getLoc();
461 spirv::GlobalVariableOp varOp;
463 OpBuilder::InsertionGuard guard(rewriter);
465 rewriter.setInsertionPointToStart(&entryBlock);
466 auto varOps = entryBlock.
getOps<spirv::GlobalVariableOp>();
467 std::string varName =
468 std::string(
"__workgroup_mem__") +
469 std::to_string(std::distance(varOps.begin(), varOps.end()));
470 varOp = spirv::GlobalVariableOp::create(rewriter, loc, spirvType, varName,
475 rewriter.replaceOpWithNewOp<spirv::AddressOfOp>(operation, varOp);
484AtomicRMWOpPattern::matchAndRewrite(memref::AtomicRMWOp atomicOp,
486 ConversionPatternRewriter &rewriter)
const {
487 auto memrefType = cast<MemRefType>(atomicOp.getMemref().getType());
490 return rewriter.notifyMatchFailure(atomicOp,
491 "unsupported memref memory space");
493 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
494 Type resultType = typeConverter.convertType(atomicOp.getType());
496 return rewriter.notifyMatchFailure(atomicOp,
497 "failed to convert result type");
499 auto loc = atomicOp.getLoc();
502 adaptor.getIndices(), loc, rewriter);
510 int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
511 auto pointerType = typeConverter.convertType<spirv::PointerType>(memrefType);
513 return rewriter.notifyMatchFailure(atomicOp,
514 "failed to convert memref type");
516 Type pointeeType = pointerType.getPointeeType();
517 Type storageElemType =
519 if (!storageElemType || !storageElemType.
isIntOrFloat())
520 return rewriter.notifyMatchFailure(
521 atomicOp,
"failed to determine destination element type");
524 assert(dstBits % srcBits == 0);
530 if (srcBits == dstBits) {
531#define ATOMIC_CASE(kind, spirvOp) \
532 case arith::AtomicRMWKind::kind: \
533 rewriter.replaceOpWithNewOp<spirv::spirvOp>( \
534 atomicOp, resultType, ptr, *scope, memSem, adaptor.getValue()); \
537 switch (atomicOp.getKind()) {
547 return rewriter.notifyMatchFailure(atomicOp,
"unimplemented atomic kind");
562 if (atomicOp.getKind() != arith::AtomicRMWKind::ori &&
563 atomicOp.getKind() != arith::AtomicRMWKind::andi) {
564 return rewriter.notifyMatchFailure(
566 "atomic op on sub-element-width types is only supported for ori/andi");
571 if (typeConverter.allows(spirv::Capability::Kernel))
572 return rewriter.notifyMatchFailure(
574 "sub-element-width atomic ops unsupported with Kernel capability");
576 auto dstType = cast<IntegerType>(storageElemType);
578 auto accessChainOp = ptr.
getDefiningOp<spirv::AccessChainOp>();
584 assert(accessChainOp.getIndices().size() == 2);
585 Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
588 srcBits, dstBits, rewriter);
590 switch (atomicOp.getKind()) {
591 case arith::AtomicRMWKind::ori: {
594 Value elemMask = rewriter.createOrFold<spirv::ConstantOp>(
595 loc, dstType, rewriter.getIntegerAttr(dstType, (1uLL << srcBits) - 1));
597 shiftValue(loc, adaptor.getValue(), offset, elemMask, rewriter);
598 result = spirv::AtomicOrOp::create(rewriter, loc, dstType, adjustedPtr,
599 *scope, memSem, storeVal);
602 case arith::AtomicRMWKind::andi: {
606 Value elemMask = rewriter.createOrFold<spirv::ConstantOp>(
607 loc, dstType, rewriter.getIntegerAttr(dstType, (1uLL << srcBits) - 1));
609 shiftValue(loc, adaptor.getValue(), offset, elemMask, rewriter);
610 Value shiftedElemMask = rewriter.createOrFold<spirv::ShiftLeftLogicalOp>(
611 loc, dstType, elemMask, offset);
612 Value invertedElemMask =
613 rewriter.createOrFold<spirv::NotOp>(loc, dstType, shiftedElemMask);
614 Value mask = rewriter.createOrFold<spirv::BitwiseOrOp>(loc, storeVal,
616 result = spirv::AtomicAndOp::create(rewriter, loc, dstType, adjustedPtr,
617 *scope, memSem, mask);
621 return rewriter.notifyMatchFailure(atomicOp,
"unimplemented atomic kind");
626 result = rewriter.createOrFold<spirv::ShiftRightLogicalOp>(loc, dstType,
628 Value mask = rewriter.createOrFold<spirv::ConstantOp>(
629 loc, dstType, rewriter.getIntegerAttr(dstType, (1uLL << srcBits) - 1));
631 rewriter.createOrFold<spirv::BitwiseAndOp>(loc, dstType,
result, mask);
632 rewriter.replaceOp(atomicOp,
result);
642DeallocOpPattern::matchAndRewrite(memref::DeallocOp operation,
644 ConversionPatternRewriter &rewriter)
const {
645 MemRefType deallocType = cast<MemRefType>(operation.getMemref().getType());
647 return rewriter.notifyMatchFailure(operation,
"unhandled allocation type");
648 rewriter.eraseOp(operation);
663static FailureOr<MemoryRequirements>
665 uint64_t preferredAlignment) {
666 if (preferredAlignment >= std::numeric_limits<uint32_t>::max()) {
672 auto memoryAccess = spirv::MemoryAccess::None;
674 memoryAccess = spirv::MemoryAccess::Nontemporal;
677 auto ptrType = cast<spirv::PointerType>(accessedPtr.
getType());
678 bool mayOmitAlignment =
679 !preferredAlignment &&
680 ptrType.getStorageClass() != spirv::StorageClass::PhysicalStorageBuffer;
681 if (mayOmitAlignment) {
682 if (memoryAccess == spirv::MemoryAccess::None) {
691 std::optional<int64_t> sizeInBytes;
692 Type rawPointeeType = ptrType.getPointeeType();
693 if (
auto scalarType = dyn_cast<spirv::ScalarType>(rawPointeeType)) {
695 sizeInBytes = scalarType.getSizeInBytes();
696 }
else if (
auto vecType = dyn_cast<VectorType>(rawPointeeType)) {
699 if (
auto scalarElem =
700 dyn_cast<spirv::ScalarType>(vecType.getElementType())) {
701 if (
auto elemSize = scalarElem.getSizeInBytes())
702 sizeInBytes = *elemSize * vecType.getNumElements();
706 if (!sizeInBytes.has_value())
709 memoryAccess |= spirv::MemoryAccess::Aligned;
710 auto memAccessAttr = spirv::MemoryAccessAttr::get(ctx, memoryAccess);
711 auto alignmentValue = preferredAlignment ? preferredAlignment : *sizeInBytes;
712 auto alignment = IntegerAttr::get(IntegerType::get(ctx, 32), alignmentValue);
719template <
class LoadOrStoreOp>
720static FailureOr<MemoryRequirements>
723 llvm::is_one_of<LoadOrStoreOp, memref::LoadOp, memref::StoreOp>::value,
724 "Must be called on either memref::LoadOp or memref::StoreOp");
727 loadOrStoreOp.getNontemporal(),
728 loadOrStoreOp.getAlignment().value_or(0));
732IntLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
733 ConversionPatternRewriter &rewriter)
const {
734 auto loc = loadOp.getLoc();
735 auto memrefType = cast<MemRefType>(loadOp.getMemref().getType());
736 if (!memrefType.getElementType().isSignlessInteger())
739 auto memorySpaceAttr =
740 dyn_cast_if_present<spirv::StorageClassAttr>(memrefType.getMemorySpace());
741 if (!memorySpaceAttr)
742 return rewriter.notifyMatchFailure(
743 loadOp,
"missing memory space SPIR-V storage class attribute");
745 if (memorySpaceAttr.getValue() == spirv::StorageClass::Image)
746 return rewriter.notifyMatchFailure(
748 "failed to lower memref in image storage class to storage buffer");
750 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
753 adaptor.getIndices(), loc, rewriter);
758 int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
759 bool isBool = srcBits == 1;
761 srcBits = typeConverter.getOptions().boolNumBits;
763 auto pointerType = typeConverter.convertType<spirv::PointerType>(memrefType);
765 return rewriter.notifyMatchFailure(loadOp,
"failed to convert memref type");
767 Type pointeeType = pointerType.getPointeeType();
770 assert(dstBits % srcBits == 0);
774 if (srcBits == dstBits) {
776 if (
failed(memoryRequirements))
777 return rewriter.notifyMatchFailure(
778 loadOp,
"failed to determine memory requirements");
780 auto [memoryAccess, alignment] = *memoryRequirements;
781 Value loadVal = spirv::LoadOp::create(rewriter, loc, accessChain,
782 memoryAccess, alignment);
785 rewriter.replaceOp(loadOp, loadVal);
791 if (typeConverter.allows(spirv::Capability::Kernel))
794 auto accessChainOp = accessChain.
getDefiningOp<spirv::AccessChainOp>();
801 assert(accessChainOp.getIndices().size() == 2);
803 srcBits, dstBits, rewriter);
805 if (
failed(memoryRequirements))
806 return rewriter.notifyMatchFailure(
807 loadOp,
"failed to determine memory requirements");
809 auto [memoryAccess, alignment] = *memoryRequirements;
810 Value spvLoadOp = spirv::LoadOp::create(rewriter, loc, dstType, adjustedPtr,
811 memoryAccess, alignment);
815 Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
817 Value
result = rewriter.createOrFold<spirv::ShiftRightArithmeticOp>(
818 loc, spvLoadOp.
getType(), spvLoadOp, offset);
821 Value mask = rewriter.createOrFold<spirv::ConstantOp>(
822 loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
824 rewriter.createOrFold<spirv::BitwiseAndOp>(loc, dstType,
result, mask);
829 IntegerAttr shiftValueAttr =
830 rewriter.getIntegerAttr(dstType, dstBits - srcBits);
832 rewriter.createOrFold<spirv::ConstantOp>(loc, dstType, shiftValueAttr);
833 result = rewriter.createOrFold<spirv::ShiftLeftLogicalOp>(loc, dstType,
835 result = rewriter.createOrFold<spirv::ShiftRightArithmeticOp>(
838 rewriter.replaceOp(loadOp,
result);
840 assert(accessChainOp.use_empty());
841 rewriter.eraseOp(accessChainOp);
847LoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
848 ConversionPatternRewriter &rewriter)
const {
849 auto memrefType = cast<MemRefType>(loadOp.getMemref().getType());
850 if (memrefType.getElementType().isSignlessInteger())
853 auto memorySpaceAttr =
854 dyn_cast_if_present<spirv::StorageClassAttr>(memrefType.getMemorySpace());
855 if (!memorySpaceAttr)
856 return rewriter.notifyMatchFailure(
857 loadOp,
"missing memory space SPIR-V storage class attribute");
859 if (memorySpaceAttr.getValue() == spirv::StorageClass::Image)
860 return rewriter.notifyMatchFailure(
862 "failed to lower memref in image storage class to storage buffer");
865 *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.getMemref(),
866 adaptor.getIndices(), loadOp.getLoc(), rewriter);
872 if (
failed(memoryRequirements))
873 return rewriter.notifyMatchFailure(
874 loadOp,
"failed to determine memory requirements");
876 auto [memoryAccess, alignment] = *memoryRequirements;
877 rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, loadPtr, memoryAccess,
882template <
typename OpAdaptor>
883static FailureOr<SmallVector<Value>>
885 ConversionPatternRewriter &rewriter) {
892 AffineMap map = loadOp.getMemRefType().getLayout().getAffineMap();
894 return rewriter.notifyMatchFailure(
896 "Cannot lower memrefs with memory layout which is not a permutation");
902 for (
unsigned dim = 0; dim < dimCount; ++dim)
908 return llvm::to_vector(llvm::reverse(coords));
912ImageLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
913 ConversionPatternRewriter &rewriter)
const {
914 auto memrefType = cast<MemRefType>(loadOp.getMemref().getType());
916 auto memorySpaceAttr =
917 dyn_cast_if_present<spirv::StorageClassAttr>(memrefType.getMemorySpace());
918 if (!memorySpaceAttr)
919 return rewriter.notifyMatchFailure(
920 loadOp,
"missing memory space SPIR-V storage class attribute");
922 if (memorySpaceAttr.getValue() != spirv::StorageClass::Image)
923 return rewriter.notifyMatchFailure(
924 loadOp,
"failed to lower memref in non-image storage class to image");
926 Value loadPtr = adaptor.getMemref();
928 if (
failed(memoryRequirements))
929 return rewriter.notifyMatchFailure(
930 loadOp,
"failed to determine memory requirements");
932 const auto [memoryAccess, alignment] = *memoryRequirements;
934 if (!loadOp.getMemRefType().hasRank())
935 return rewriter.notifyMatchFailure(
936 loadOp,
"cannot lower unranked memrefs to SPIR-V images");
941 if (!isa<spirv::ScalarType>(loadOp.getMemRefType().getElementType()))
942 return rewriter.notifyMatchFailure(
944 "cannot lower memrefs who's element type is not a SPIR-V scalar type"
951 auto convertedPointeeType = cast<spirv::PointerType>(
952 getTypeConverter()->convertType(loadOp.getMemRefType()));
953 if (!isa<spirv::SampledImageType>(convertedPointeeType.getPointeeType()))
954 return rewriter.notifyMatchFailure(loadOp,
955 "cannot lower memrefs which do not "
956 "convert to SPIR-V sampled images");
959 Location loc = loadOp->getLoc();
961 spirv::LoadOp::create(rewriter, loc, loadPtr, memoryAccess, alignment);
963 auto imageOp = spirv::ImageOp::create(rewriter, loc, imageLoadOp);
967 if (memrefType.getRank() == 1) {
968 coords = adaptor.getIndices()[0];
970 FailureOr<SmallVector<Value>> maybeCoords =
974 auto coordVectorType = VectorType::get({loadOp.getMemRefType().getRank()},
975 adaptor.getIndices().
getType()[0]);
976 coords = spirv::CompositeConstructOp::create(rewriter, loc, coordVectorType,
977 maybeCoords.value());
981 auto resultVectorType = VectorType::get({4}, loadOp.getType());
982 auto fetchOp = spirv::ImageFetchOp::create(
983 rewriter, loc, resultVectorType, imageOp, coords,
984 mlir::spirv::ImageOperandsAttr{},
ValueRange{});
989 auto compositeExtractOp =
990 spirv::CompositeExtractOp::create(rewriter, loc, fetchOp, 0);
992 rewriter.replaceOp(loadOp, compositeExtractOp);
997IntStoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
998 ConversionPatternRewriter &rewriter)
const {
999 auto memrefType = cast<MemRefType>(storeOp.getMemref().getType());
1000 if (!memrefType.getElementType().isSignlessInteger())
1001 return rewriter.notifyMatchFailure(storeOp,
1002 "element type is not a signless int");
1004 auto loc = storeOp.getLoc();
1005 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
1008 adaptor.getIndices(), loc, rewriter);
1011 return rewriter.notifyMatchFailure(
1012 storeOp,
"failed to convert element pointer type");
1014 int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
1016 bool isBool = srcBits == 1;
1018 srcBits = typeConverter.getOptions().boolNumBits;
1020 auto pointerType = typeConverter.convertType<spirv::PointerType>(memrefType);
1022 return rewriter.notifyMatchFailure(storeOp,
1023 "failed to convert memref type");
1025 Type pointeeType = pointerType.getPointeeType();
1026 auto dstType = dyn_cast<IntegerType>(
1029 return rewriter.notifyMatchFailure(
1030 storeOp,
"failed to determine destination element type");
1032 int dstBits =
static_cast<int>(dstType.getWidth());
1033 assert(dstBits % srcBits == 0);
1035 if (srcBits == dstBits) {
1037 if (
failed(memoryRequirements))
1038 return rewriter.notifyMatchFailure(
1039 storeOp,
"failed to determine memory requirements");
1041 auto [memoryAccess, alignment] = *memoryRequirements;
1042 Value storeVal = adaptor.getValue();
1045 rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, accessChain, storeVal,
1046 memoryAccess, alignment);
1052 if (typeConverter.allows(spirv::Capability::Kernel))
1055 auto accessChainOp = accessChain.
getDefiningOp<spirv::AccessChainOp>();
1070 assert(accessChainOp.getIndices().size() == 2);
1071 Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
1076 Value mask = rewriter.createOrFold<spirv::ConstantOp>(
1077 loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
1078 Value clearBitsMask = rewriter.createOrFold<spirv::ShiftLeftLogicalOp>(
1079 loc, dstType, mask, offset);
1081 rewriter.createOrFold<spirv::NotOp>(loc, dstType, clearBitsMask);
1083 Value storeVal =
shiftValue(loc, adaptor.getValue(), offset, mask, rewriter);
1085 srcBits, dstBits, rewriter);
1088 return rewriter.notifyMatchFailure(storeOp,
"atomic scope not available");
1091 Value
result = spirv::AtomicAndOp::create(rewriter, loc, dstType, adjustedPtr,
1092 *scope, memSem, clearBitsMask);
1093 result = spirv::AtomicOrOp::create(rewriter, loc, dstType, adjustedPtr,
1094 *scope, memSem, storeVal);
1100 rewriter.eraseOp(storeOp);
1102 assert(accessChainOp.use_empty());
1103 rewriter.eraseOp(accessChainOp);
1112LogicalResult MemorySpaceCastOpPattern::matchAndRewrite(
1113 memref::MemorySpaceCastOp addrCastOp, OpAdaptor adaptor,
1114 ConversionPatternRewriter &rewriter)
const {
1115 Location loc = addrCastOp.getLoc();
1116 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
1117 if (!typeConverter.allows(spirv::Capability::Kernel))
1118 return rewriter.notifyMatchFailure(
1119 loc,
"address space casts require kernel capability");
1121 auto sourceType = dyn_cast<MemRefType>(addrCastOp.getSource().getType());
1123 return rewriter.notifyMatchFailure(
1124 loc,
"SPIR-V lowering requires ranked memref types");
1125 auto resultType = cast<MemRefType>(addrCastOp.getResult().getType());
1127 auto sourceStorageClassAttr =
1128 dyn_cast_or_null<spirv::StorageClassAttr>(sourceType.getMemorySpace());
1129 if (!sourceStorageClassAttr)
1130 return rewriter.notifyMatchFailure(loc, [sourceType](Diagnostic &
diag) {
1131 diag <<
"source address space " << sourceType.getMemorySpace()
1132 <<
" must be a SPIR-V storage class";
1134 auto resultStorageClassAttr =
1135 dyn_cast_or_null<spirv::StorageClassAttr>(resultType.getMemorySpace());
1136 if (!resultStorageClassAttr)
1137 return rewriter.notifyMatchFailure(loc, [resultType](Diagnostic &
diag) {
1138 diag <<
"result address space " << resultType.getMemorySpace()
1139 <<
" must be a SPIR-V storage class";
1142 spirv::StorageClass sourceSc = sourceStorageClassAttr.getValue();
1143 spirv::StorageClass resultSc = resultStorageClassAttr.getValue();
1145 Value
result = adaptor.getSource();
1146 Type resultPtrType = typeConverter.convertType(resultType);
1148 return rewriter.notifyMatchFailure(addrCastOp,
1149 "failed to convert memref type");
1151 Type genericPtrType = resultPtrType;
1159 if (sourceSc != spirv::StorageClass::Generic &&
1160 resultSc != spirv::StorageClass::Generic) {
1161 Type intermediateType =
1162 MemRefType::get(sourceType.getShape(), sourceType.getElementType(),
1163 sourceType.getLayout(),
1164 rewriter.getAttr<spirv::StorageClassAttr>(
1165 spirv::StorageClass::Generic));
1166 genericPtrType = typeConverter.convertType(intermediateType);
1168 if (sourceSc != spirv::StorageClass::Generic) {
1169 result = spirv::PtrCastToGenericOp::create(rewriter, loc, genericPtrType,
1172 if (resultSc != spirv::StorageClass::Generic) {
1174 spirv::GenericCastToPtrOp::create(rewriter, loc, resultPtrType,
result);
1176 rewriter.replaceOp(addrCastOp,
result);
1181StoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
1182 ConversionPatternRewriter &rewriter)
const {
1183 auto memrefType = cast<MemRefType>(storeOp.getMemref().getType());
1184 if (memrefType.getElementType().isSignlessInteger())
1185 return rewriter.notifyMatchFailure(storeOp,
"signless int");
1187 *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.getMemref(),
1188 adaptor.getIndices(), storeOp.getLoc(), rewriter);
1191 return rewriter.notifyMatchFailure(storeOp,
"type conversion failed");
1194 if (
failed(memoryRequirements))
1195 return rewriter.notifyMatchFailure(
1196 storeOp,
"failed to determine memory requirements");
1198 auto [memoryAccess, alignment] = *memoryRequirements;
1199 rewriter.replaceOpWithNewOp<spirv::StoreOp>(
1200 storeOp, storePtr, adaptor.getValue(), memoryAccess, alignment);
1209CopyOpPattern::matchAndRewrite(memref::CopyOp copyOp, OpAdaptor adaptor,
1210 ConversionPatternRewriter &rewriter)
const {
1211 auto memrefType = cast<MemRefType>(copyOp.getSource().getType());
1212 if (!memrefType.hasStaticShape())
1213 return rewriter.notifyMatchFailure(copyOp,
"unsupported dynamic shape");
1215 for (MemRefType type :
1216 {memrefType, cast<MemRefType>(copyOp.getTarget().getType())}) {
1217 auto memorySpaceAttr =
1218 dyn_cast_if_present<spirv::StorageClassAttr>(type.getMemorySpace());
1219 if (memorySpaceAttr &&
1220 memorySpaceAttr.getValue() == spirv::StorageClass::Image)
1221 return rewriter.notifyMatchFailure(
1222 copyOp,
"cannot lower memref.copy in image storage class");
1228 Value source = adaptor.getSource();
1229 Value
target = adaptor.getTarget();
1230 auto sourcePtrType = dyn_cast<spirv::PointerType>(source.
getType());
1231 auto targetPtrType = dyn_cast<spirv::PointerType>(
target.getType());
1232 if (!sourcePtrType || !targetPtrType)
1233 return rewriter.notifyMatchFailure(copyOp,
"failed to convert memref type");
1235 if (sourcePtrType.getPointeeType() != targetPtrType.getPointeeType())
1236 return rewriter.notifyMatchFailure(
1237 copyOp,
"source and target pointee types do not match");
1239 rewriter.replaceOpWithNewOp<spirv::CopyMemoryOp>(
1240 copyOp,
target, source, spirv::MemoryAccessAttr{},
1242 spirv::MemoryAccessAttr{}, IntegerAttr{});
1246LogicalResult ReinterpretCastPattern::matchAndRewrite(
1247 memref::ReinterpretCastOp op, OpAdaptor adaptor,
1248 ConversionPatternRewriter &rewriter)
const {
1249 Value src = adaptor.getSource();
1250 auto srcType = dyn_cast<spirv::PointerType>(src.
getType());
1253 return rewriter.notifyMatchFailure(op, [&](Diagnostic &
diag) {
1257 const TypeConverter *converter = getTypeConverter();
1259 auto dstType = converter->convertType<spirv::PointerType>(op.getType());
1260 if (dstType != srcType)
1261 return rewriter.notifyMatchFailure(op, [&](Diagnostic &
diag) {
1262 diag <<
"invalid dst type " << op.getType();
1265 OpFoldResult offset =
1266 getMixedValues(adaptor.getStaticOffsets(), adaptor.getOffsets(), rewriter)
1269 rewriter.replaceOp(op, src);
1273 Type intType = converter->convertType(rewriter.getIndexType());
1275 return rewriter.notifyMatchFailure(op,
"failed to convert index type");
1277 Location loc = op.getLoc();
1278 auto offsetValue = [&]() -> Value {
1279 if (
auto val = dyn_cast<Value>(offset))
1282 int64_t attrVal = cast<IntegerAttr>(cast<Attribute>(offset)).getInt();
1283 Attribute attr = rewriter.getIntegerAttr(intType, attrVal);
1284 return rewriter.createOrFold<spirv::ConstantOp>(loc, intType, attr);
1287 rewriter.replaceOpWithNewOp<spirv::InBoundsPtrAccessChainOp>(
1296LogicalResult ExtractAlignedPointerAsIndexOpPattern::matchAndRewrite(
1297 memref::ExtractAlignedPointerAsIndexOp extractOp, OpAdaptor adaptor,
1298 ConversionPatternRewriter &rewriter)
const {
1299 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
1300 Type indexType = typeConverter.getIndexType();
1301 rewriter.replaceOpWithNewOp<spirv::ConvertPtrToUOp>(extractOp, indexType,
1302 adaptor.getSource());
1313 patterns.
add<AllocaOpPattern, AllocOpPattern, AtomicRMWOpPattern,
1314 CopyOpPattern, DeallocOpPattern, IntLoadOpPattern,
1315 ImageLoadOpPattern, IntStoreOpPattern, LoadOpPattern,
1316 MemorySpaceCastOpPattern, StoreOpPattern, ReinterpretCastPattern,
1317 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.
OpListType::iterator iterator
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