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