126be7fe2SLei Zhang //===- MemRefToSPIRV.cpp - MemRef to SPIR-V Patterns ----------------------===//
226be7fe2SLei Zhang //
326be7fe2SLei Zhang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
426be7fe2SLei Zhang // See https://llvm.org/LICENSE.txt for license information.
526be7fe2SLei Zhang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
626be7fe2SLei Zhang //
726be7fe2SLei Zhang //===----------------------------------------------------------------------===//
826be7fe2SLei Zhang //
926be7fe2SLei Zhang // This file implements patterns to convert MemRef dialect to SPIR-V dialect.
1026be7fe2SLei Zhang //
1126be7fe2SLei Zhang //===----------------------------------------------------------------------===//
1226be7fe2SLei Zhang
1326be7fe2SLei Zhang #include "mlir/Dialect/MemRef/IR/MemRef.h"
1426be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
158854b736SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVEnums.h"
1626be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
1726be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h"
1826be7fe2SLei Zhang #include "llvm/Support/Debug.h"
1926be7fe2SLei Zhang
2026be7fe2SLei Zhang #define DEBUG_TYPE "memref-to-spirv-pattern"
2126be7fe2SLei Zhang
2226be7fe2SLei Zhang using namespace mlir;
2326be7fe2SLei Zhang
2426be7fe2SLei Zhang //===----------------------------------------------------------------------===//
2526be7fe2SLei Zhang // Utility functions
2626be7fe2SLei Zhang //===----------------------------------------------------------------------===//
2726be7fe2SLei Zhang
2826be7fe2SLei Zhang /// Returns the offset of the value in `targetBits` representation.
2926be7fe2SLei Zhang ///
3026be7fe2SLei Zhang /// `srcIdx` is an index into a 1-D array with each element having `sourceBits`.
3126be7fe2SLei Zhang /// It's assumed to be non-negative.
3226be7fe2SLei Zhang ///
3326be7fe2SLei Zhang /// When accessing an element in the array treating as having elements of
3426be7fe2SLei Zhang /// `targetBits`, multiple values are loaded in the same time. The method
3526be7fe2SLei Zhang /// returns the offset where the `srcIdx` locates in the value. For example, if
3626be7fe2SLei Zhang /// `sourceBits` equals to 8 and `targetBits` equals to 32, the x-th element is
3726be7fe2SLei Zhang /// located at (x % 4) * 8. Because there are four elements in one i32, and one
3826be7fe2SLei Zhang /// element has 8 bits.
getOffsetForBitwidth(Location loc,Value srcIdx,int sourceBits,int targetBits,OpBuilder & builder)3926be7fe2SLei Zhang static Value getOffsetForBitwidth(Location loc, Value srcIdx, int sourceBits,
4026be7fe2SLei Zhang int targetBits, OpBuilder &builder) {
4126be7fe2SLei Zhang assert(targetBits % sourceBits == 0);
4226be7fe2SLei Zhang IntegerType targetType = builder.getIntegerType(targetBits);
4326be7fe2SLei Zhang IntegerAttr idxAttr =
4426be7fe2SLei Zhang builder.getIntegerAttr(targetType, targetBits / sourceBits);
4526be7fe2SLei Zhang auto idx = builder.create<spirv::ConstantOp>(loc, targetType, idxAttr);
4626be7fe2SLei Zhang IntegerAttr srcBitsAttr = builder.getIntegerAttr(targetType, sourceBits);
4726be7fe2SLei Zhang auto srcBitsValue =
4826be7fe2SLei Zhang builder.create<spirv::ConstantOp>(loc, targetType, srcBitsAttr);
4926be7fe2SLei Zhang auto m = builder.create<spirv::UModOp>(loc, srcIdx, idx);
5026be7fe2SLei Zhang return builder.create<spirv::IMulOp>(loc, targetType, m, srcBitsValue);
5126be7fe2SLei Zhang }
5226be7fe2SLei Zhang
5326be7fe2SLei Zhang /// Returns an adjusted spirv::AccessChainOp. Based on the
5426be7fe2SLei Zhang /// extension/capabilities, certain integer bitwidths `sourceBits` might not be
5526be7fe2SLei Zhang /// supported. During conversion if a memref of an unsupported type is used,
5626be7fe2SLei Zhang /// load/stores to this memref need to be modified to use a supported higher
5726be7fe2SLei Zhang /// bitwidth `targetBits` and extracting the required bits. For an accessing a
5826be7fe2SLei Zhang /// 1D array (spv.array or spv.rt_array), the last index is modified to load the
5926be7fe2SLei Zhang /// bits needed. The extraction of the actual bits needed are handled
6026be7fe2SLei Zhang /// separately. Note that this only works for a 1-D tensor.
adjustAccessChainForBitwidth(SPIRVTypeConverter & typeConverter,spirv::AccessChainOp op,int sourceBits,int targetBits,OpBuilder & builder)6126be7fe2SLei Zhang static Value adjustAccessChainForBitwidth(SPIRVTypeConverter &typeConverter,
6226be7fe2SLei Zhang spirv::AccessChainOp op,
6326be7fe2SLei Zhang int sourceBits, int targetBits,
6426be7fe2SLei Zhang OpBuilder &builder) {
6526be7fe2SLei Zhang assert(targetBits % sourceBits == 0);
6626be7fe2SLei Zhang const auto loc = op.getLoc();
6726be7fe2SLei Zhang IntegerType targetType = builder.getIntegerType(targetBits);
6826be7fe2SLei Zhang IntegerAttr attr =
6926be7fe2SLei Zhang builder.getIntegerAttr(targetType, targetBits / sourceBits);
7026be7fe2SLei Zhang auto idx = builder.create<spirv::ConstantOp>(loc, targetType, attr);
7126be7fe2SLei Zhang auto lastDim = op->getOperand(op.getNumOperands() - 1);
7226be7fe2SLei Zhang auto indices = llvm::to_vector<4>(op.indices());
7326be7fe2SLei Zhang // There are two elements if this is a 1-D tensor.
7426be7fe2SLei Zhang assert(indices.size() == 2);
7526be7fe2SLei Zhang indices.back() = builder.create<spirv::SDivOp>(loc, lastDim, idx);
7626be7fe2SLei Zhang Type t = typeConverter.convertType(op.component_ptr().getType());
7726be7fe2SLei Zhang return builder.create<spirv::AccessChainOp>(loc, t, op.base_ptr(), indices);
7826be7fe2SLei Zhang }
7926be7fe2SLei Zhang
8026be7fe2SLei Zhang /// Returns the shifted `targetBits`-bit value with the given offset.
shiftValue(Location loc,Value value,Value offset,Value mask,int targetBits,OpBuilder & builder)8126be7fe2SLei Zhang static Value shiftValue(Location loc, Value value, Value offset, Value mask,
8226be7fe2SLei Zhang int targetBits, OpBuilder &builder) {
8326be7fe2SLei Zhang Type targetType = builder.getIntegerType(targetBits);
8426be7fe2SLei Zhang Value result = builder.create<spirv::BitwiseAndOp>(loc, value, mask);
8526be7fe2SLei Zhang return builder.create<spirv::ShiftLeftLogicalOp>(loc, targetType, result,
8626be7fe2SLei Zhang offset);
8726be7fe2SLei Zhang }
8826be7fe2SLei Zhang
898854b736SLei Zhang /// Returns true if the allocations of memref `type` generated from `allocOp`
908854b736SLei Zhang /// can be lowered to SPIR-V.
isAllocationSupported(Operation * allocOp,MemRefType type)918854b736SLei Zhang static bool isAllocationSupported(Operation *allocOp, MemRefType type) {
928854b736SLei Zhang if (isa<memref::AllocOp, memref::DeallocOp>(allocOp)) {
938854b736SLei Zhang if (SPIRVTypeConverter::getMemorySpaceForStorageClass(
948854b736SLei Zhang spirv::StorageClass::Workgroup) != type.getMemorySpaceAsInt())
9526be7fe2SLei Zhang return false;
968854b736SLei Zhang } else if (isa<memref::AllocaOp>(allocOp)) {
978854b736SLei Zhang if (SPIRVTypeConverter::getMemorySpaceForStorageClass(
988854b736SLei Zhang spirv::StorageClass::Function) != type.getMemorySpaceAsInt())
998854b736SLei Zhang return false;
1008854b736SLei Zhang } else {
1018854b736SLei Zhang return false;
1028854b736SLei Zhang }
1038854b736SLei Zhang
1048854b736SLei Zhang // Currently only support static shape and int or float or vector of int or
1058854b736SLei Zhang // float element type.
1068854b736SLei Zhang if (!type.hasStaticShape())
1078854b736SLei Zhang return false;
1088854b736SLei Zhang
1098854b736SLei Zhang Type elementType = type.getElementType();
11026be7fe2SLei Zhang if (auto vecType = elementType.dyn_cast<VectorType>())
11126be7fe2SLei Zhang elementType = vecType.getElementType();
11226be7fe2SLei Zhang return elementType.isIntOrFloat();
11326be7fe2SLei Zhang }
11426be7fe2SLei Zhang
11526be7fe2SLei Zhang /// Returns the scope to use for atomic operations use for emulating store
11626be7fe2SLei Zhang /// operations of unsupported integer bitwidths, based on the memref
11726be7fe2SLei Zhang /// type. Returns None on failure.
getAtomicOpScope(MemRefType type)1188854b736SLei Zhang static Optional<spirv::Scope> getAtomicOpScope(MemRefType type) {
11926be7fe2SLei Zhang Optional<spirv::StorageClass> storageClass =
12026be7fe2SLei Zhang SPIRVTypeConverter::getStorageClassForMemorySpace(
1218854b736SLei Zhang type.getMemorySpaceAsInt());
12226be7fe2SLei Zhang if (!storageClass)
12326be7fe2SLei Zhang return {};
12426be7fe2SLei Zhang switch (*storageClass) {
12526be7fe2SLei Zhang case spirv::StorageClass::StorageBuffer:
12626be7fe2SLei Zhang return spirv::Scope::Device;
12726be7fe2SLei Zhang case spirv::StorageClass::Workgroup:
12826be7fe2SLei Zhang return spirv::Scope::Workgroup;
12926be7fe2SLei Zhang default: {
13026be7fe2SLei Zhang }
13126be7fe2SLei Zhang }
13226be7fe2SLei Zhang return {};
13326be7fe2SLei Zhang }
13426be7fe2SLei Zhang
1350065bd2aSLei Zhang /// Casts the given `srcInt` into a boolean value.
castIntNToBool(Location loc,Value srcInt,OpBuilder & builder)1360065bd2aSLei Zhang static Value castIntNToBool(Location loc, Value srcInt, OpBuilder &builder) {
1370065bd2aSLei Zhang if (srcInt.getType().isInteger(1))
1380065bd2aSLei Zhang return srcInt;
1390065bd2aSLei Zhang
1400065bd2aSLei Zhang auto one = spirv::ConstantOp::getOne(srcInt.getType(), loc, builder);
1410065bd2aSLei Zhang return builder.create<spirv::IEqualOp>(loc, srcInt, one);
1420065bd2aSLei Zhang }
1430065bd2aSLei Zhang
1449f5300c8SLei Zhang /// Casts the given `srcBool` into an integer of `dstType`.
castBoolToIntN(Location loc,Value srcBool,Type dstType,OpBuilder & builder)1459f5300c8SLei Zhang static Value castBoolToIntN(Location loc, Value srcBool, Type dstType,
1469f5300c8SLei Zhang OpBuilder &builder) {
1479f5300c8SLei Zhang assert(srcBool.getType().isInteger(1));
1489f5300c8SLei Zhang if (dstType.isInteger(1))
1499f5300c8SLei Zhang return srcBool;
1509f5300c8SLei Zhang Value zero = spirv::ConstantOp::getZero(dstType, loc, builder);
1519f5300c8SLei Zhang Value one = spirv::ConstantOp::getOne(dstType, loc, builder);
1529f5300c8SLei Zhang return builder.create<spirv::SelectOp>(loc, dstType, srcBool, one, zero);
1539f5300c8SLei Zhang }
1549f5300c8SLei Zhang
15526be7fe2SLei Zhang //===----------------------------------------------------------------------===//
15626be7fe2SLei Zhang // Operation conversion
15726be7fe2SLei Zhang //===----------------------------------------------------------------------===//
15826be7fe2SLei Zhang
15926be7fe2SLei Zhang // Note that DRR cannot be used for the patterns in this file: we may need to
16026be7fe2SLei Zhang // convert type along the way, which requires ConversionPattern. DRR generates
16126be7fe2SLei Zhang // normal RewritePattern.
16226be7fe2SLei Zhang
16326be7fe2SLei Zhang namespace {
16426be7fe2SLei Zhang
1658854b736SLei Zhang /// Converts memref.alloca to SPIR-V Function variables.
1668854b736SLei Zhang class AllocaOpPattern final : public OpConversionPattern<memref::AllocaOp> {
1678854b736SLei Zhang public:
1688854b736SLei Zhang using OpConversionPattern<memref::AllocaOp>::OpConversionPattern;
1698854b736SLei Zhang
1708854b736SLei Zhang LogicalResult
1718854b736SLei Zhang matchAndRewrite(memref::AllocaOp allocaOp, OpAdaptor adaptor,
1728854b736SLei Zhang ConversionPatternRewriter &rewriter) const override;
1738854b736SLei Zhang };
1748854b736SLei Zhang
17526be7fe2SLei Zhang /// Converts an allocation operation to SPIR-V. Currently only supports lowering
17626be7fe2SLei Zhang /// to Workgroup memory when the size is constant. Note that this pattern needs
17726be7fe2SLei Zhang /// to be applied in a pass that runs at least at spv.module scope since it wil
17826be7fe2SLei Zhang /// ladd global variables into the spv.module.
17926be7fe2SLei Zhang class AllocOpPattern final : public OpConversionPattern<memref::AllocOp> {
18026be7fe2SLei Zhang public:
18126be7fe2SLei Zhang using OpConversionPattern<memref::AllocOp>::OpConversionPattern;
18226be7fe2SLei Zhang
18326be7fe2SLei Zhang LogicalResult
184b54c724bSRiver Riddle matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor,
18526be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override;
18626be7fe2SLei Zhang };
18726be7fe2SLei Zhang
18826be7fe2SLei Zhang /// Removed a deallocation if it is a supported allocation. Currently only
18926be7fe2SLei Zhang /// removes deallocation if the memory space is workgroup memory.
19026be7fe2SLei Zhang class DeallocOpPattern final : public OpConversionPattern<memref::DeallocOp> {
19126be7fe2SLei Zhang public:
19226be7fe2SLei Zhang using OpConversionPattern<memref::DeallocOp>::OpConversionPattern;
19326be7fe2SLei Zhang
19426be7fe2SLei Zhang LogicalResult
195b54c724bSRiver Riddle matchAndRewrite(memref::DeallocOp operation, OpAdaptor adaptor,
19626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override;
19726be7fe2SLei Zhang };
19826be7fe2SLei Zhang
19926be7fe2SLei Zhang /// Converts memref.load to spv.Load.
20026be7fe2SLei Zhang class IntLoadOpPattern final : public OpConversionPattern<memref::LoadOp> {
20126be7fe2SLei Zhang public:
20226be7fe2SLei Zhang using OpConversionPattern<memref::LoadOp>::OpConversionPattern;
20326be7fe2SLei Zhang
20426be7fe2SLei Zhang LogicalResult
205b54c724bSRiver Riddle matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
20626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override;
20726be7fe2SLei Zhang };
20826be7fe2SLei Zhang
20926be7fe2SLei Zhang /// Converts memref.load to spv.Load.
21026be7fe2SLei Zhang class LoadOpPattern final : public OpConversionPattern<memref::LoadOp> {
21126be7fe2SLei Zhang public:
21226be7fe2SLei Zhang using OpConversionPattern<memref::LoadOp>::OpConversionPattern;
21326be7fe2SLei Zhang
21426be7fe2SLei Zhang LogicalResult
215b54c724bSRiver Riddle matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
21626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override;
21726be7fe2SLei Zhang };
21826be7fe2SLei Zhang
21926be7fe2SLei Zhang /// Converts memref.store to spv.Store on integers.
22026be7fe2SLei Zhang class IntStoreOpPattern final : public OpConversionPattern<memref::StoreOp> {
22126be7fe2SLei Zhang public:
22226be7fe2SLei Zhang using OpConversionPattern<memref::StoreOp>::OpConversionPattern;
22326be7fe2SLei Zhang
22426be7fe2SLei Zhang LogicalResult
225b54c724bSRiver Riddle matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
22626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override;
22726be7fe2SLei Zhang };
22826be7fe2SLei Zhang
22926be7fe2SLei Zhang /// Converts memref.store to spv.Store.
23026be7fe2SLei Zhang class StoreOpPattern final : public OpConversionPattern<memref::StoreOp> {
23126be7fe2SLei Zhang public:
23226be7fe2SLei Zhang using OpConversionPattern<memref::StoreOp>::OpConversionPattern;
23326be7fe2SLei Zhang
23426be7fe2SLei Zhang LogicalResult
235b54c724bSRiver Riddle matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
23626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override;
23726be7fe2SLei Zhang };
23826be7fe2SLei Zhang
23926be7fe2SLei Zhang } // namespace
24026be7fe2SLei Zhang
24126be7fe2SLei Zhang //===----------------------------------------------------------------------===//
2428854b736SLei Zhang // AllocaOp
2438854b736SLei Zhang //===----------------------------------------------------------------------===//
2448854b736SLei Zhang
2458854b736SLei Zhang LogicalResult
matchAndRewrite(memref::AllocaOp allocaOp,OpAdaptor adaptor,ConversionPatternRewriter & rewriter) const2468854b736SLei Zhang AllocaOpPattern::matchAndRewrite(memref::AllocaOp allocaOp, OpAdaptor adaptor,
2478854b736SLei Zhang ConversionPatternRewriter &rewriter) const {
2488854b736SLei Zhang MemRefType allocType = allocaOp.getType();
2498854b736SLei Zhang if (!isAllocationSupported(allocaOp, allocType))
2508854b736SLei Zhang return rewriter.notifyMatchFailure(allocaOp, "unhandled allocation type");
2518854b736SLei Zhang
2528854b736SLei Zhang // Get the SPIR-V type for the allocation.
2538854b736SLei Zhang Type spirvType = getTypeConverter()->convertType(allocType);
2548854b736SLei Zhang rewriter.replaceOpWithNewOp<spirv::VariableOp>(allocaOp, spirvType,
2558854b736SLei Zhang spirv::StorageClass::Function,
2568854b736SLei Zhang /*initializer=*/nullptr);
2578854b736SLei Zhang return success();
2588854b736SLei Zhang }
2598854b736SLei Zhang
2608854b736SLei Zhang //===----------------------------------------------------------------------===//
26126be7fe2SLei Zhang // AllocOp
26226be7fe2SLei Zhang //===----------------------------------------------------------------------===//
26326be7fe2SLei Zhang
26426be7fe2SLei Zhang LogicalResult
matchAndRewrite(memref::AllocOp operation,OpAdaptor adaptor,ConversionPatternRewriter & rewriter) const265b54c724bSRiver Riddle AllocOpPattern::matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor,
26626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const {
26726be7fe2SLei Zhang MemRefType allocType = operation.getType();
2688854b736SLei Zhang if (!isAllocationSupported(operation, allocType))
2698854b736SLei Zhang return rewriter.notifyMatchFailure(operation, "unhandled allocation type");
27026be7fe2SLei Zhang
27126be7fe2SLei Zhang // Get the SPIR-V type for the allocation.
27226be7fe2SLei Zhang Type spirvType = getTypeConverter()->convertType(allocType);
27326be7fe2SLei Zhang
27426be7fe2SLei Zhang // Insert spv.GlobalVariable for this allocation.
27526be7fe2SLei Zhang Operation *parent =
27626be7fe2SLei Zhang SymbolTable::getNearestSymbolTable(operation->getParentOp());
27726be7fe2SLei Zhang if (!parent)
27826be7fe2SLei Zhang return failure();
27926be7fe2SLei Zhang Location loc = operation.getLoc();
28026be7fe2SLei Zhang spirv::GlobalVariableOp varOp;
28126be7fe2SLei Zhang {
28226be7fe2SLei Zhang OpBuilder::InsertionGuard guard(rewriter);
28326be7fe2SLei Zhang Block &entryBlock = *parent->getRegion(0).begin();
28426be7fe2SLei Zhang rewriter.setInsertionPointToStart(&entryBlock);
28526be7fe2SLei Zhang auto varOps = entryBlock.getOps<spirv::GlobalVariableOp>();
28626be7fe2SLei Zhang std::string varName =
28726be7fe2SLei Zhang std::string("__workgroup_mem__") +
28826be7fe2SLei Zhang std::to_string(std::distance(varOps.begin(), varOps.end()));
28926be7fe2SLei Zhang varOp = rewriter.create<spirv::GlobalVariableOp>(loc, spirvType, varName,
29026be7fe2SLei Zhang /*initializer=*/nullptr);
29126be7fe2SLei Zhang }
29226be7fe2SLei Zhang
29326be7fe2SLei Zhang // Get pointer to global variable at the current scope.
29426be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::AddressOfOp>(operation, varOp);
29526be7fe2SLei Zhang return success();
29626be7fe2SLei Zhang }
29726be7fe2SLei Zhang
29826be7fe2SLei Zhang //===----------------------------------------------------------------------===//
29926be7fe2SLei Zhang // DeallocOp
30026be7fe2SLei Zhang //===----------------------------------------------------------------------===//
30126be7fe2SLei Zhang
30226be7fe2SLei Zhang LogicalResult
matchAndRewrite(memref::DeallocOp operation,OpAdaptor adaptor,ConversionPatternRewriter & rewriter) const30326be7fe2SLei Zhang DeallocOpPattern::matchAndRewrite(memref::DeallocOp operation,
304b54c724bSRiver Riddle OpAdaptor adaptor,
30526be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const {
306*136d746eSJacques Pienaar MemRefType deallocType = operation.getMemref().getType().cast<MemRefType>();
3078854b736SLei Zhang if (!isAllocationSupported(operation, deallocType))
3088854b736SLei Zhang return rewriter.notifyMatchFailure(operation, "unhandled allocation type");
30926be7fe2SLei Zhang rewriter.eraseOp(operation);
31026be7fe2SLei Zhang return success();
31126be7fe2SLei Zhang }
31226be7fe2SLei Zhang
31326be7fe2SLei Zhang //===----------------------------------------------------------------------===//
31426be7fe2SLei Zhang // LoadOp
31526be7fe2SLei Zhang //===----------------------------------------------------------------------===//
31626be7fe2SLei Zhang
31726be7fe2SLei Zhang LogicalResult
matchAndRewrite(memref::LoadOp loadOp,OpAdaptor adaptor,ConversionPatternRewriter & rewriter) const318b54c724bSRiver Riddle IntLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
31926be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const {
32026be7fe2SLei Zhang auto loc = loadOp.getLoc();
321*136d746eSJacques Pienaar auto memrefType = loadOp.getMemref().getType().cast<MemRefType>();
32226be7fe2SLei Zhang if (!memrefType.getElementType().isSignlessInteger())
32326be7fe2SLei Zhang return failure();
32426be7fe2SLei Zhang
32526be7fe2SLei Zhang auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
32626be7fe2SLei Zhang spirv::AccessChainOp accessChainOp =
327*136d746eSJacques Pienaar spirv::getElementPtr(typeConverter, memrefType, adaptor.getMemref(),
328*136d746eSJacques Pienaar adaptor.getIndices(), loc, rewriter);
32926be7fe2SLei Zhang
3301e9799e2SButygin if (!accessChainOp)
3311e9799e2SButygin return failure();
3321e9799e2SButygin
33326be7fe2SLei Zhang int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
33426be7fe2SLei Zhang bool isBool = srcBits == 1;
33526be7fe2SLei Zhang if (isBool)
33626be7fe2SLei Zhang srcBits = typeConverter.getOptions().boolNumBits;
33726be7fe2SLei Zhang Type pointeeType = typeConverter.convertType(memrefType)
33826be7fe2SLei Zhang .cast<spirv::PointerType>()
33926be7fe2SLei Zhang .getPointeeType();
34026be7fe2SLei Zhang Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0);
34126be7fe2SLei Zhang Type dstType;
34226be7fe2SLei Zhang if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>())
34326be7fe2SLei Zhang dstType = arrayType.getElementType();
34426be7fe2SLei Zhang else
34526be7fe2SLei Zhang dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType();
34626be7fe2SLei Zhang
34726be7fe2SLei Zhang int dstBits = dstType.getIntOrFloatBitWidth();
34826be7fe2SLei Zhang assert(dstBits % srcBits == 0);
34926be7fe2SLei Zhang
35026be7fe2SLei Zhang // If the rewrited load op has the same bit width, use the loading value
35126be7fe2SLei Zhang // directly.
35226be7fe2SLei Zhang if (srcBits == dstBits) {
3530065bd2aSLei Zhang Value loadVal =
3540065bd2aSLei Zhang rewriter.create<spirv::LoadOp>(loc, accessChainOp.getResult());
3550065bd2aSLei Zhang if (isBool)
3560065bd2aSLei Zhang loadVal = castIntNToBool(loc, loadVal, rewriter);
3570065bd2aSLei Zhang rewriter.replaceOp(loadOp, loadVal);
35826be7fe2SLei Zhang return success();
35926be7fe2SLei Zhang }
36026be7fe2SLei Zhang
36126be7fe2SLei Zhang // Assume that getElementPtr() works linearizely. If it's a scalar, the method
36226be7fe2SLei Zhang // still returns a linearized accessing. If the accessing is not linearized,
36326be7fe2SLei Zhang // there will be offset issues.
36426be7fe2SLei Zhang assert(accessChainOp.indices().size() == 2);
36526be7fe2SLei Zhang Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp,
36626be7fe2SLei Zhang srcBits, dstBits, rewriter);
36726be7fe2SLei Zhang Value spvLoadOp = rewriter.create<spirv::LoadOp>(
36826be7fe2SLei Zhang loc, dstType, adjustedPtr,
36926be7fe2SLei Zhang loadOp->getAttrOfType<spirv::MemoryAccessAttr>(
37026be7fe2SLei Zhang spirv::attributeName<spirv::MemoryAccess>()),
37126be7fe2SLei Zhang loadOp->getAttrOfType<IntegerAttr>("alignment"));
37226be7fe2SLei Zhang
37326be7fe2SLei Zhang // Shift the bits to the rightmost.
37426be7fe2SLei Zhang // ____XXXX________ -> ____________XXXX
37526be7fe2SLei Zhang Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
37626be7fe2SLei Zhang Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter);
37726be7fe2SLei Zhang Value result = rewriter.create<spirv::ShiftRightArithmeticOp>(
37826be7fe2SLei Zhang loc, spvLoadOp.getType(), spvLoadOp, offset);
37926be7fe2SLei Zhang
38026be7fe2SLei Zhang // Apply the mask to extract corresponding bits.
38126be7fe2SLei Zhang Value mask = rewriter.create<spirv::ConstantOp>(
38226be7fe2SLei Zhang loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
38326be7fe2SLei Zhang result = rewriter.create<spirv::BitwiseAndOp>(loc, dstType, result, mask);
38426be7fe2SLei Zhang
38526be7fe2SLei Zhang // Apply sign extension on the loading value unconditionally. The signedness
38626be7fe2SLei Zhang // semantic is carried in the operator itself, we relies other pattern to
38726be7fe2SLei Zhang // handle the casting.
38826be7fe2SLei Zhang IntegerAttr shiftValueAttr =
38926be7fe2SLei Zhang rewriter.getIntegerAttr(dstType, dstBits - srcBits);
39026be7fe2SLei Zhang Value shiftValue =
39126be7fe2SLei Zhang rewriter.create<spirv::ConstantOp>(loc, dstType, shiftValueAttr);
39226be7fe2SLei Zhang result = rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, result,
39326be7fe2SLei Zhang shiftValue);
39426be7fe2SLei Zhang result = rewriter.create<spirv::ShiftRightArithmeticOp>(loc, dstType, result,
39526be7fe2SLei Zhang shiftValue);
39626be7fe2SLei Zhang
39726be7fe2SLei Zhang if (isBool) {
39826be7fe2SLei Zhang dstType = typeConverter.convertType(loadOp.getType());
39926be7fe2SLei Zhang mask = spirv::ConstantOp::getOne(result.getType(), loc, rewriter);
4000065bd2aSLei Zhang result = rewriter.create<spirv::IEqualOp>(loc, result, mask);
40126be7fe2SLei Zhang } else if (result.getType().getIntOrFloatBitWidth() !=
40226be7fe2SLei Zhang static_cast<unsigned>(dstBits)) {
40326be7fe2SLei Zhang result = rewriter.create<spirv::SConvertOp>(loc, dstType, result);
40426be7fe2SLei Zhang }
40526be7fe2SLei Zhang rewriter.replaceOp(loadOp, result);
40626be7fe2SLei Zhang
40726be7fe2SLei Zhang assert(accessChainOp.use_empty());
40826be7fe2SLei Zhang rewriter.eraseOp(accessChainOp);
40926be7fe2SLei Zhang
41026be7fe2SLei Zhang return success();
41126be7fe2SLei Zhang }
41226be7fe2SLei Zhang
41326be7fe2SLei Zhang LogicalResult
matchAndRewrite(memref::LoadOp loadOp,OpAdaptor adaptor,ConversionPatternRewriter & rewriter) const414b54c724bSRiver Riddle LoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor,
41526be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const {
416*136d746eSJacques Pienaar auto memrefType = loadOp.getMemref().getType().cast<MemRefType>();
41726be7fe2SLei Zhang if (memrefType.getElementType().isSignlessInteger())
41826be7fe2SLei Zhang return failure();
41926be7fe2SLei Zhang auto loadPtr = spirv::getElementPtr(
420*136d746eSJacques Pienaar *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.getMemref(),
421*136d746eSJacques Pienaar adaptor.getIndices(), loadOp.getLoc(), rewriter);
4221e9799e2SButygin
4231e9799e2SButygin if (!loadPtr)
4241e9799e2SButygin return failure();
4251e9799e2SButygin
42626be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, loadPtr);
42726be7fe2SLei Zhang return success();
42826be7fe2SLei Zhang }
42926be7fe2SLei Zhang
43026be7fe2SLei Zhang LogicalResult
matchAndRewrite(memref::StoreOp storeOp,OpAdaptor adaptor,ConversionPatternRewriter & rewriter) const431b54c724bSRiver Riddle IntStoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
43226be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const {
433*136d746eSJacques Pienaar auto memrefType = storeOp.getMemref().getType().cast<MemRefType>();
43426be7fe2SLei Zhang if (!memrefType.getElementType().isSignlessInteger())
43526be7fe2SLei Zhang return failure();
43626be7fe2SLei Zhang
43726be7fe2SLei Zhang auto loc = storeOp.getLoc();
43826be7fe2SLei Zhang auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
43926be7fe2SLei Zhang spirv::AccessChainOp accessChainOp =
440*136d746eSJacques Pienaar spirv::getElementPtr(typeConverter, memrefType, adaptor.getMemref(),
441*136d746eSJacques Pienaar adaptor.getIndices(), loc, rewriter);
4421e9799e2SButygin
4431e9799e2SButygin if (!accessChainOp)
4441e9799e2SButygin return failure();
4451e9799e2SButygin
44626be7fe2SLei Zhang int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
44726be7fe2SLei Zhang
44826be7fe2SLei Zhang bool isBool = srcBits == 1;
44926be7fe2SLei Zhang if (isBool)
45026be7fe2SLei Zhang srcBits = typeConverter.getOptions().boolNumBits;
4519f5300c8SLei Zhang
45226be7fe2SLei Zhang Type pointeeType = typeConverter.convertType(memrefType)
45326be7fe2SLei Zhang .cast<spirv::PointerType>()
45426be7fe2SLei Zhang .getPointeeType();
45526be7fe2SLei Zhang Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0);
45626be7fe2SLei Zhang Type dstType;
45726be7fe2SLei Zhang if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>())
45826be7fe2SLei Zhang dstType = arrayType.getElementType();
45926be7fe2SLei Zhang else
46026be7fe2SLei Zhang dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType();
46126be7fe2SLei Zhang
46226be7fe2SLei Zhang int dstBits = dstType.getIntOrFloatBitWidth();
46326be7fe2SLei Zhang assert(dstBits % srcBits == 0);
46426be7fe2SLei Zhang
46526be7fe2SLei Zhang if (srcBits == dstBits) {
466*136d746eSJacques Pienaar Value storeVal = adaptor.getValue();
4679f5300c8SLei Zhang if (isBool)
4689f5300c8SLei Zhang storeVal = castBoolToIntN(loc, storeVal, dstType, rewriter);
46926be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::StoreOp>(
4709f5300c8SLei Zhang storeOp, accessChainOp.getResult(), storeVal);
47126be7fe2SLei Zhang return success();
47226be7fe2SLei Zhang }
47326be7fe2SLei Zhang
47426be7fe2SLei Zhang // Since there are multi threads in the processing, the emulation will be done
47526be7fe2SLei Zhang // with atomic operations. E.g., if the storing value is i8, rewrite the
47626be7fe2SLei Zhang // StoreOp to
47726be7fe2SLei Zhang // 1) load a 32-bit integer
47826be7fe2SLei Zhang // 2) clear 8 bits in the loading value
47926be7fe2SLei Zhang // 3) store 32-bit value back
48026be7fe2SLei Zhang // 4) load a 32-bit integer
48126be7fe2SLei Zhang // 5) modify 8 bits in the loading value
48226be7fe2SLei Zhang // 6) store 32-bit value back
48326be7fe2SLei Zhang // The step 1 to step 3 are done by AtomicAnd as one atomic step, and the step
48426be7fe2SLei Zhang // 4 to step 6 are done by AtomicOr as another atomic step.
48526be7fe2SLei Zhang assert(accessChainOp.indices().size() == 2);
48626be7fe2SLei Zhang Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
48726be7fe2SLei Zhang Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter);
48826be7fe2SLei Zhang
48926be7fe2SLei Zhang // Create a mask to clear the destination. E.g., if it is the second i8 in
49026be7fe2SLei Zhang // i32, 0xFFFF00FF is created.
49126be7fe2SLei Zhang Value mask = rewriter.create<spirv::ConstantOp>(
49226be7fe2SLei Zhang loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
49326be7fe2SLei Zhang Value clearBitsMask =
49426be7fe2SLei Zhang rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, mask, offset);
49526be7fe2SLei Zhang clearBitsMask = rewriter.create<spirv::NotOp>(loc, dstType, clearBitsMask);
49626be7fe2SLei Zhang
497*136d746eSJacques Pienaar Value storeVal = adaptor.getValue();
4989f5300c8SLei Zhang if (isBool)
4999f5300c8SLei Zhang storeVal = castBoolToIntN(loc, storeVal, dstType, rewriter);
50026be7fe2SLei Zhang storeVal = shiftValue(loc, storeVal, offset, mask, dstBits, rewriter);
50126be7fe2SLei Zhang Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp,
50226be7fe2SLei Zhang srcBits, dstBits, rewriter);
50326be7fe2SLei Zhang Optional<spirv::Scope> scope = getAtomicOpScope(memrefType);
50426be7fe2SLei Zhang if (!scope)
50526be7fe2SLei Zhang return failure();
50626be7fe2SLei Zhang Value result = rewriter.create<spirv::AtomicAndOp>(
50726be7fe2SLei Zhang loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease,
50826be7fe2SLei Zhang clearBitsMask);
50926be7fe2SLei Zhang result = rewriter.create<spirv::AtomicOrOp>(
51026be7fe2SLei Zhang loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease,
51126be7fe2SLei Zhang storeVal);
51226be7fe2SLei Zhang
51326be7fe2SLei Zhang // The AtomicOrOp has no side effect. Since it is already inserted, we can
51426be7fe2SLei Zhang // just remove the original StoreOp. Note that rewriter.replaceOp()
51526be7fe2SLei Zhang // doesn't work because it only accepts that the numbers of result are the
51626be7fe2SLei Zhang // same.
51726be7fe2SLei Zhang rewriter.eraseOp(storeOp);
51826be7fe2SLei Zhang
51926be7fe2SLei Zhang assert(accessChainOp.use_empty());
52026be7fe2SLei Zhang rewriter.eraseOp(accessChainOp);
52126be7fe2SLei Zhang
52226be7fe2SLei Zhang return success();
52326be7fe2SLei Zhang }
52426be7fe2SLei Zhang
52526be7fe2SLei Zhang LogicalResult
matchAndRewrite(memref::StoreOp storeOp,OpAdaptor adaptor,ConversionPatternRewriter & rewriter) const526b54c724bSRiver Riddle StoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor,
52726be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const {
528*136d746eSJacques Pienaar auto memrefType = storeOp.getMemref().getType().cast<MemRefType>();
52926be7fe2SLei Zhang if (memrefType.getElementType().isSignlessInteger())
53026be7fe2SLei Zhang return failure();
531b54c724bSRiver Riddle auto storePtr = spirv::getElementPtr(
532*136d746eSJacques Pienaar *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.getMemref(),
533*136d746eSJacques Pienaar adaptor.getIndices(), storeOp.getLoc(), rewriter);
5341e9799e2SButygin
5351e9799e2SButygin if (!storePtr)
5361e9799e2SButygin return failure();
5371e9799e2SButygin
53826be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, storePtr,
539*136d746eSJacques Pienaar adaptor.getValue());
54026be7fe2SLei Zhang return success();
54126be7fe2SLei Zhang }
54226be7fe2SLei Zhang
54326be7fe2SLei Zhang //===----------------------------------------------------------------------===//
54426be7fe2SLei Zhang // Pattern population
54526be7fe2SLei Zhang //===----------------------------------------------------------------------===//
54626be7fe2SLei Zhang
54726be7fe2SLei Zhang namespace mlir {
populateMemRefToSPIRVPatterns(SPIRVTypeConverter & typeConverter,RewritePatternSet & patterns)54826be7fe2SLei Zhang void populateMemRefToSPIRVPatterns(SPIRVTypeConverter &typeConverter,
54926be7fe2SLei Zhang RewritePatternSet &patterns) {
5508854b736SLei Zhang patterns
5518854b736SLei Zhang .add<AllocaOpPattern, AllocOpPattern, DeallocOpPattern, IntLoadOpPattern,
55226be7fe2SLei Zhang IntStoreOpPattern, LoadOpPattern, StoreOpPattern>(
55326be7fe2SLei Zhang typeConverter, patterns.getContext());
55426be7fe2SLei Zhang }
55526be7fe2SLei Zhang } // namespace mlir
556