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"
1526be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
1626be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h"
1726be7fe2SLei Zhang #include "llvm/Support/Debug.h"
1826be7fe2SLei Zhang 
1926be7fe2SLei Zhang #define DEBUG_TYPE "memref-to-spirv-pattern"
2026be7fe2SLei Zhang 
2126be7fe2SLei Zhang using namespace mlir;
2226be7fe2SLei Zhang 
2326be7fe2SLei Zhang //===----------------------------------------------------------------------===//
2426be7fe2SLei Zhang // Utility functions
2526be7fe2SLei Zhang //===----------------------------------------------------------------------===//
2626be7fe2SLei Zhang 
2726be7fe2SLei Zhang /// Returns the offset of the value in `targetBits` representation.
2826be7fe2SLei Zhang ///
2926be7fe2SLei Zhang /// `srcIdx` is an index into a 1-D array with each element having `sourceBits`.
3026be7fe2SLei Zhang /// It's assumed to be non-negative.
3126be7fe2SLei Zhang ///
3226be7fe2SLei Zhang /// When accessing an element in the array treating as having elements of
3326be7fe2SLei Zhang /// `targetBits`, multiple values are loaded in the same time. The method
3426be7fe2SLei Zhang /// returns the offset where the `srcIdx` locates in the value. For example, if
3526be7fe2SLei Zhang /// `sourceBits` equals to 8 and `targetBits` equals to 32, the x-th element is
3626be7fe2SLei Zhang /// located at (x % 4) * 8. Because there are four elements in one i32, and one
3726be7fe2SLei Zhang /// element has 8 bits.
3826be7fe2SLei Zhang static Value getOffsetForBitwidth(Location loc, Value srcIdx, int sourceBits,
3926be7fe2SLei Zhang                                   int targetBits, OpBuilder &builder) {
4026be7fe2SLei Zhang   assert(targetBits % sourceBits == 0);
4126be7fe2SLei Zhang   IntegerType targetType = builder.getIntegerType(targetBits);
4226be7fe2SLei Zhang   IntegerAttr idxAttr =
4326be7fe2SLei Zhang       builder.getIntegerAttr(targetType, targetBits / sourceBits);
4426be7fe2SLei Zhang   auto idx = builder.create<spirv::ConstantOp>(loc, targetType, idxAttr);
4526be7fe2SLei Zhang   IntegerAttr srcBitsAttr = builder.getIntegerAttr(targetType, sourceBits);
4626be7fe2SLei Zhang   auto srcBitsValue =
4726be7fe2SLei Zhang       builder.create<spirv::ConstantOp>(loc, targetType, srcBitsAttr);
4826be7fe2SLei Zhang   auto m = builder.create<spirv::UModOp>(loc, srcIdx, idx);
4926be7fe2SLei Zhang   return builder.create<spirv::IMulOp>(loc, targetType, m, srcBitsValue);
5026be7fe2SLei Zhang }
5126be7fe2SLei Zhang 
5226be7fe2SLei Zhang /// Returns an adjusted spirv::AccessChainOp. Based on the
5326be7fe2SLei Zhang /// extension/capabilities, certain integer bitwidths `sourceBits` might not be
5426be7fe2SLei Zhang /// supported. During conversion if a memref of an unsupported type is used,
5526be7fe2SLei Zhang /// load/stores to this memref need to be modified to use a supported higher
5626be7fe2SLei Zhang /// bitwidth `targetBits` and extracting the required bits. For an accessing a
5726be7fe2SLei Zhang /// 1D array (spv.array or spv.rt_array), the last index is modified to load the
5826be7fe2SLei Zhang /// bits needed. The extraction of the actual bits needed are handled
5926be7fe2SLei Zhang /// separately. Note that this only works for a 1-D tensor.
6026be7fe2SLei Zhang static Value adjustAccessChainForBitwidth(SPIRVTypeConverter &typeConverter,
6126be7fe2SLei Zhang                                           spirv::AccessChainOp op,
6226be7fe2SLei Zhang                                           int sourceBits, int targetBits,
6326be7fe2SLei Zhang                                           OpBuilder &builder) {
6426be7fe2SLei Zhang   assert(targetBits % sourceBits == 0);
6526be7fe2SLei Zhang   const auto loc = op.getLoc();
6626be7fe2SLei Zhang   IntegerType targetType = builder.getIntegerType(targetBits);
6726be7fe2SLei Zhang   IntegerAttr attr =
6826be7fe2SLei Zhang       builder.getIntegerAttr(targetType, targetBits / sourceBits);
6926be7fe2SLei Zhang   auto idx = builder.create<spirv::ConstantOp>(loc, targetType, attr);
7026be7fe2SLei Zhang   auto lastDim = op->getOperand(op.getNumOperands() - 1);
7126be7fe2SLei Zhang   auto indices = llvm::to_vector<4>(op.indices());
7226be7fe2SLei Zhang   // There are two elements if this is a 1-D tensor.
7326be7fe2SLei Zhang   assert(indices.size() == 2);
7426be7fe2SLei Zhang   indices.back() = builder.create<spirv::SDivOp>(loc, lastDim, idx);
7526be7fe2SLei Zhang   Type t = typeConverter.convertType(op.component_ptr().getType());
7626be7fe2SLei Zhang   return builder.create<spirv::AccessChainOp>(loc, t, op.base_ptr(), indices);
7726be7fe2SLei Zhang }
7826be7fe2SLei Zhang 
7926be7fe2SLei Zhang /// Returns the shifted `targetBits`-bit value with the given offset.
8026be7fe2SLei Zhang static Value shiftValue(Location loc, Value value, Value offset, Value mask,
8126be7fe2SLei Zhang                         int targetBits, OpBuilder &builder) {
8226be7fe2SLei Zhang   Type targetType = builder.getIntegerType(targetBits);
8326be7fe2SLei Zhang   Value result = builder.create<spirv::BitwiseAndOp>(loc, value, mask);
8426be7fe2SLei Zhang   return builder.create<spirv::ShiftLeftLogicalOp>(loc, targetType, result,
8526be7fe2SLei Zhang                                                    offset);
8626be7fe2SLei Zhang }
8726be7fe2SLei Zhang 
8826be7fe2SLei Zhang /// Returns true if the allocations of type `t` can be lowered to SPIR-V.
8926be7fe2SLei Zhang static bool isAllocationSupported(MemRefType t) {
9026be7fe2SLei Zhang   // Currently only support workgroup local memory allocations with static
9126be7fe2SLei Zhang   // shape and int or float or vector of int or float element type.
9226be7fe2SLei Zhang   if (!(t.hasStaticShape() &&
9326be7fe2SLei Zhang         SPIRVTypeConverter::getMemorySpaceForStorageClass(
9426be7fe2SLei Zhang             spirv::StorageClass::Workgroup) == t.getMemorySpaceAsInt()))
9526be7fe2SLei Zhang     return false;
9626be7fe2SLei Zhang   Type elementType = t.getElementType();
9726be7fe2SLei Zhang   if (auto vecType = elementType.dyn_cast<VectorType>())
9826be7fe2SLei Zhang     elementType = vecType.getElementType();
9926be7fe2SLei Zhang   return elementType.isIntOrFloat();
10026be7fe2SLei Zhang }
10126be7fe2SLei Zhang 
10226be7fe2SLei Zhang /// Returns the scope to use for atomic operations use for emulating store
10326be7fe2SLei Zhang /// operations of unsupported integer bitwidths, based on the memref
10426be7fe2SLei Zhang /// type. Returns None on failure.
10526be7fe2SLei Zhang static Optional<spirv::Scope> getAtomicOpScope(MemRefType t) {
10626be7fe2SLei Zhang   Optional<spirv::StorageClass> storageClass =
10726be7fe2SLei Zhang       SPIRVTypeConverter::getStorageClassForMemorySpace(
10826be7fe2SLei Zhang           t.getMemorySpaceAsInt());
10926be7fe2SLei Zhang   if (!storageClass)
11026be7fe2SLei Zhang     return {};
11126be7fe2SLei Zhang   switch (*storageClass) {
11226be7fe2SLei Zhang   case spirv::StorageClass::StorageBuffer:
11326be7fe2SLei Zhang     return spirv::Scope::Device;
11426be7fe2SLei Zhang   case spirv::StorageClass::Workgroup:
11526be7fe2SLei Zhang     return spirv::Scope::Workgroup;
11626be7fe2SLei Zhang   default: {
11726be7fe2SLei Zhang   }
11826be7fe2SLei Zhang   }
11926be7fe2SLei Zhang   return {};
12026be7fe2SLei Zhang }
12126be7fe2SLei Zhang 
122*0065bd2aSLei Zhang /// Casts the given `srcInt` into a boolean value.
123*0065bd2aSLei Zhang static Value castIntNToBool(Location loc, Value srcInt, OpBuilder &builder) {
124*0065bd2aSLei Zhang   if (srcInt.getType().isInteger(1))
125*0065bd2aSLei Zhang     return srcInt;
126*0065bd2aSLei Zhang 
127*0065bd2aSLei Zhang   auto one = spirv::ConstantOp::getOne(srcInt.getType(), loc, builder);
128*0065bd2aSLei Zhang   return builder.create<spirv::IEqualOp>(loc, srcInt, one);
129*0065bd2aSLei Zhang }
130*0065bd2aSLei Zhang 
1319f5300c8SLei Zhang /// Casts the given `srcBool` into an integer of `dstType`.
1329f5300c8SLei Zhang static Value castBoolToIntN(Location loc, Value srcBool, Type dstType,
1339f5300c8SLei Zhang                             OpBuilder &builder) {
1349f5300c8SLei Zhang   assert(srcBool.getType().isInteger(1));
1359f5300c8SLei Zhang   if (dstType.isInteger(1))
1369f5300c8SLei Zhang     return srcBool;
1379f5300c8SLei Zhang   Value zero = spirv::ConstantOp::getZero(dstType, loc, builder);
1389f5300c8SLei Zhang   Value one = spirv::ConstantOp::getOne(dstType, loc, builder);
1399f5300c8SLei Zhang   return builder.create<spirv::SelectOp>(loc, dstType, srcBool, one, zero);
1409f5300c8SLei Zhang }
1419f5300c8SLei Zhang 
14226be7fe2SLei Zhang //===----------------------------------------------------------------------===//
14326be7fe2SLei Zhang // Operation conversion
14426be7fe2SLei Zhang //===----------------------------------------------------------------------===//
14526be7fe2SLei Zhang 
14626be7fe2SLei Zhang // Note that DRR cannot be used for the patterns in this file: we may need to
14726be7fe2SLei Zhang // convert type along the way, which requires ConversionPattern. DRR generates
14826be7fe2SLei Zhang // normal RewritePattern.
14926be7fe2SLei Zhang 
15026be7fe2SLei Zhang namespace {
15126be7fe2SLei Zhang 
15226be7fe2SLei Zhang /// Converts an allocation operation to SPIR-V. Currently only supports lowering
15326be7fe2SLei Zhang /// to Workgroup memory when the size is constant.  Note that this pattern needs
15426be7fe2SLei Zhang /// to be applied in a pass that runs at least at spv.module scope since it wil
15526be7fe2SLei Zhang /// ladd global variables into the spv.module.
15626be7fe2SLei Zhang class AllocOpPattern final : public OpConversionPattern<memref::AllocOp> {
15726be7fe2SLei Zhang public:
15826be7fe2SLei Zhang   using OpConversionPattern<memref::AllocOp>::OpConversionPattern;
15926be7fe2SLei Zhang 
16026be7fe2SLei Zhang   LogicalResult
16126be7fe2SLei Zhang   matchAndRewrite(memref::AllocOp operation, ArrayRef<Value> operands,
16226be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
16326be7fe2SLei Zhang };
16426be7fe2SLei Zhang 
16526be7fe2SLei Zhang /// Removed a deallocation if it is a supported allocation. Currently only
16626be7fe2SLei Zhang /// removes deallocation if the memory space is workgroup memory.
16726be7fe2SLei Zhang class DeallocOpPattern final : public OpConversionPattern<memref::DeallocOp> {
16826be7fe2SLei Zhang public:
16926be7fe2SLei Zhang   using OpConversionPattern<memref::DeallocOp>::OpConversionPattern;
17026be7fe2SLei Zhang 
17126be7fe2SLei Zhang   LogicalResult
17226be7fe2SLei Zhang   matchAndRewrite(memref::DeallocOp operation, ArrayRef<Value> operands,
17326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
17426be7fe2SLei Zhang };
17526be7fe2SLei Zhang 
17626be7fe2SLei Zhang /// Converts memref.load to spv.Load.
17726be7fe2SLei Zhang class IntLoadOpPattern final : public OpConversionPattern<memref::LoadOp> {
17826be7fe2SLei Zhang public:
17926be7fe2SLei Zhang   using OpConversionPattern<memref::LoadOp>::OpConversionPattern;
18026be7fe2SLei Zhang 
18126be7fe2SLei Zhang   LogicalResult
18226be7fe2SLei Zhang   matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
18326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
18426be7fe2SLei Zhang };
18526be7fe2SLei Zhang 
18626be7fe2SLei Zhang /// Converts memref.load to spv.Load.
18726be7fe2SLei Zhang class LoadOpPattern final : public OpConversionPattern<memref::LoadOp> {
18826be7fe2SLei Zhang public:
18926be7fe2SLei Zhang   using OpConversionPattern<memref::LoadOp>::OpConversionPattern;
19026be7fe2SLei Zhang 
19126be7fe2SLei Zhang   LogicalResult
19226be7fe2SLei Zhang   matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
19326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
19426be7fe2SLei Zhang };
19526be7fe2SLei Zhang 
19626be7fe2SLei Zhang /// Converts memref.store to spv.Store on integers.
19726be7fe2SLei Zhang class IntStoreOpPattern final : public OpConversionPattern<memref::StoreOp> {
19826be7fe2SLei Zhang public:
19926be7fe2SLei Zhang   using OpConversionPattern<memref::StoreOp>::OpConversionPattern;
20026be7fe2SLei Zhang 
20126be7fe2SLei Zhang   LogicalResult
20226be7fe2SLei Zhang   matchAndRewrite(memref::StoreOp storeOp, ArrayRef<Value> operands,
20326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
20426be7fe2SLei Zhang };
20526be7fe2SLei Zhang 
20626be7fe2SLei Zhang /// Converts memref.store to spv.Store.
20726be7fe2SLei Zhang class StoreOpPattern final : public OpConversionPattern<memref::StoreOp> {
20826be7fe2SLei Zhang public:
20926be7fe2SLei Zhang   using OpConversionPattern<memref::StoreOp>::OpConversionPattern;
21026be7fe2SLei Zhang 
21126be7fe2SLei Zhang   LogicalResult
21226be7fe2SLei Zhang   matchAndRewrite(memref::StoreOp storeOp, ArrayRef<Value> operands,
21326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
21426be7fe2SLei Zhang };
21526be7fe2SLei Zhang 
21626be7fe2SLei Zhang } // namespace
21726be7fe2SLei Zhang 
21826be7fe2SLei Zhang //===----------------------------------------------------------------------===//
21926be7fe2SLei Zhang // AllocOp
22026be7fe2SLei Zhang //===----------------------------------------------------------------------===//
22126be7fe2SLei Zhang 
22226be7fe2SLei Zhang LogicalResult
22326be7fe2SLei Zhang AllocOpPattern::matchAndRewrite(memref::AllocOp operation,
22426be7fe2SLei Zhang                                 ArrayRef<Value> operands,
22526be7fe2SLei Zhang                                 ConversionPatternRewriter &rewriter) const {
22626be7fe2SLei Zhang   MemRefType allocType = operation.getType();
22726be7fe2SLei Zhang   if (!isAllocationSupported(allocType))
22826be7fe2SLei Zhang     return operation.emitError("unhandled allocation type");
22926be7fe2SLei Zhang 
23026be7fe2SLei Zhang   // Get the SPIR-V type for the allocation.
23126be7fe2SLei Zhang   Type spirvType = getTypeConverter()->convertType(allocType);
23226be7fe2SLei Zhang 
23326be7fe2SLei Zhang   // Insert spv.GlobalVariable for this allocation.
23426be7fe2SLei Zhang   Operation *parent =
23526be7fe2SLei Zhang       SymbolTable::getNearestSymbolTable(operation->getParentOp());
23626be7fe2SLei Zhang   if (!parent)
23726be7fe2SLei Zhang     return failure();
23826be7fe2SLei Zhang   Location loc = operation.getLoc();
23926be7fe2SLei Zhang   spirv::GlobalVariableOp varOp;
24026be7fe2SLei Zhang   {
24126be7fe2SLei Zhang     OpBuilder::InsertionGuard guard(rewriter);
24226be7fe2SLei Zhang     Block &entryBlock = *parent->getRegion(0).begin();
24326be7fe2SLei Zhang     rewriter.setInsertionPointToStart(&entryBlock);
24426be7fe2SLei Zhang     auto varOps = entryBlock.getOps<spirv::GlobalVariableOp>();
24526be7fe2SLei Zhang     std::string varName =
24626be7fe2SLei Zhang         std::string("__workgroup_mem__") +
24726be7fe2SLei Zhang         std::to_string(std::distance(varOps.begin(), varOps.end()));
24826be7fe2SLei Zhang     varOp = rewriter.create<spirv::GlobalVariableOp>(loc, spirvType, varName,
24926be7fe2SLei Zhang                                                      /*initializer=*/nullptr);
25026be7fe2SLei Zhang   }
25126be7fe2SLei Zhang 
25226be7fe2SLei Zhang   // Get pointer to global variable at the current scope.
25326be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::AddressOfOp>(operation, varOp);
25426be7fe2SLei Zhang   return success();
25526be7fe2SLei Zhang }
25626be7fe2SLei Zhang 
25726be7fe2SLei Zhang //===----------------------------------------------------------------------===//
25826be7fe2SLei Zhang // DeallocOp
25926be7fe2SLei Zhang //===----------------------------------------------------------------------===//
26026be7fe2SLei Zhang 
26126be7fe2SLei Zhang LogicalResult
26226be7fe2SLei Zhang DeallocOpPattern::matchAndRewrite(memref::DeallocOp operation,
26326be7fe2SLei Zhang                                   ArrayRef<Value> operands,
26426be7fe2SLei Zhang                                   ConversionPatternRewriter &rewriter) const {
26526be7fe2SLei Zhang   MemRefType deallocType = operation.memref().getType().cast<MemRefType>();
26626be7fe2SLei Zhang   if (!isAllocationSupported(deallocType))
26726be7fe2SLei Zhang     return operation.emitError("unhandled deallocation type");
26826be7fe2SLei Zhang   rewriter.eraseOp(operation);
26926be7fe2SLei Zhang   return success();
27026be7fe2SLei Zhang }
27126be7fe2SLei Zhang 
27226be7fe2SLei Zhang //===----------------------------------------------------------------------===//
27326be7fe2SLei Zhang // LoadOp
27426be7fe2SLei Zhang //===----------------------------------------------------------------------===//
27526be7fe2SLei Zhang 
27626be7fe2SLei Zhang LogicalResult
27726be7fe2SLei Zhang IntLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp,
27826be7fe2SLei Zhang                                   ArrayRef<Value> operands,
27926be7fe2SLei Zhang                                   ConversionPatternRewriter &rewriter) const {
28026be7fe2SLei Zhang   memref::LoadOpAdaptor loadOperands(operands);
28126be7fe2SLei Zhang   auto loc = loadOp.getLoc();
28226be7fe2SLei Zhang   auto memrefType = loadOp.memref().getType().cast<MemRefType>();
28326be7fe2SLei Zhang   if (!memrefType.getElementType().isSignlessInteger())
28426be7fe2SLei Zhang     return failure();
28526be7fe2SLei Zhang 
28626be7fe2SLei Zhang   auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
28726be7fe2SLei Zhang   spirv::AccessChainOp accessChainOp =
28826be7fe2SLei Zhang       spirv::getElementPtr(typeConverter, memrefType, loadOperands.memref(),
28926be7fe2SLei Zhang                            loadOperands.indices(), loc, rewriter);
29026be7fe2SLei Zhang 
2911e9799e2SButygin   if (!accessChainOp)
2921e9799e2SButygin     return failure();
2931e9799e2SButygin 
29426be7fe2SLei Zhang   int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
29526be7fe2SLei Zhang   bool isBool = srcBits == 1;
29626be7fe2SLei Zhang   if (isBool)
29726be7fe2SLei Zhang     srcBits = typeConverter.getOptions().boolNumBits;
29826be7fe2SLei Zhang   Type pointeeType = typeConverter.convertType(memrefType)
29926be7fe2SLei Zhang                          .cast<spirv::PointerType>()
30026be7fe2SLei Zhang                          .getPointeeType();
30126be7fe2SLei Zhang   Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0);
30226be7fe2SLei Zhang   Type dstType;
30326be7fe2SLei Zhang   if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>())
30426be7fe2SLei Zhang     dstType = arrayType.getElementType();
30526be7fe2SLei Zhang   else
30626be7fe2SLei Zhang     dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType();
30726be7fe2SLei Zhang 
30826be7fe2SLei Zhang   int dstBits = dstType.getIntOrFloatBitWidth();
30926be7fe2SLei Zhang   assert(dstBits % srcBits == 0);
31026be7fe2SLei Zhang 
31126be7fe2SLei Zhang   // If the rewrited load op has the same bit width, use the loading value
31226be7fe2SLei Zhang   // directly.
31326be7fe2SLei Zhang   if (srcBits == dstBits) {
314*0065bd2aSLei Zhang     Value loadVal =
315*0065bd2aSLei Zhang         rewriter.create<spirv::LoadOp>(loc, accessChainOp.getResult());
316*0065bd2aSLei Zhang     if (isBool)
317*0065bd2aSLei Zhang       loadVal = castIntNToBool(loc, loadVal, rewriter);
318*0065bd2aSLei Zhang     rewriter.replaceOp(loadOp, loadVal);
31926be7fe2SLei Zhang     return success();
32026be7fe2SLei Zhang   }
32126be7fe2SLei Zhang 
32226be7fe2SLei Zhang   // Assume that getElementPtr() works linearizely. If it's a scalar, the method
32326be7fe2SLei Zhang   // still returns a linearized accessing. If the accessing is not linearized,
32426be7fe2SLei Zhang   // there will be offset issues.
32526be7fe2SLei Zhang   assert(accessChainOp.indices().size() == 2);
32626be7fe2SLei Zhang   Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp,
32726be7fe2SLei Zhang                                                    srcBits, dstBits, rewriter);
32826be7fe2SLei Zhang   Value spvLoadOp = rewriter.create<spirv::LoadOp>(
32926be7fe2SLei Zhang       loc, dstType, adjustedPtr,
33026be7fe2SLei Zhang       loadOp->getAttrOfType<spirv::MemoryAccessAttr>(
33126be7fe2SLei Zhang           spirv::attributeName<spirv::MemoryAccess>()),
33226be7fe2SLei Zhang       loadOp->getAttrOfType<IntegerAttr>("alignment"));
33326be7fe2SLei Zhang 
33426be7fe2SLei Zhang   // Shift the bits to the rightmost.
33526be7fe2SLei Zhang   // ____XXXX________ -> ____________XXXX
33626be7fe2SLei Zhang   Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
33726be7fe2SLei Zhang   Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter);
33826be7fe2SLei Zhang   Value result = rewriter.create<spirv::ShiftRightArithmeticOp>(
33926be7fe2SLei Zhang       loc, spvLoadOp.getType(), spvLoadOp, offset);
34026be7fe2SLei Zhang 
34126be7fe2SLei Zhang   // Apply the mask to extract corresponding bits.
34226be7fe2SLei Zhang   Value mask = rewriter.create<spirv::ConstantOp>(
34326be7fe2SLei Zhang       loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
34426be7fe2SLei Zhang   result = rewriter.create<spirv::BitwiseAndOp>(loc, dstType, result, mask);
34526be7fe2SLei Zhang 
34626be7fe2SLei Zhang   // Apply sign extension on the loading value unconditionally. The signedness
34726be7fe2SLei Zhang   // semantic is carried in the operator itself, we relies other pattern to
34826be7fe2SLei Zhang   // handle the casting.
34926be7fe2SLei Zhang   IntegerAttr shiftValueAttr =
35026be7fe2SLei Zhang       rewriter.getIntegerAttr(dstType, dstBits - srcBits);
35126be7fe2SLei Zhang   Value shiftValue =
35226be7fe2SLei Zhang       rewriter.create<spirv::ConstantOp>(loc, dstType, shiftValueAttr);
35326be7fe2SLei Zhang   result = rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, result,
35426be7fe2SLei Zhang                                                       shiftValue);
35526be7fe2SLei Zhang   result = rewriter.create<spirv::ShiftRightArithmeticOp>(loc, dstType, result,
35626be7fe2SLei Zhang                                                           shiftValue);
35726be7fe2SLei Zhang 
35826be7fe2SLei Zhang   if (isBool) {
35926be7fe2SLei Zhang     dstType = typeConverter.convertType(loadOp.getType());
36026be7fe2SLei Zhang     mask = spirv::ConstantOp::getOne(result.getType(), loc, rewriter);
361*0065bd2aSLei Zhang     result = rewriter.create<spirv::IEqualOp>(loc, result, mask);
36226be7fe2SLei Zhang   } else if (result.getType().getIntOrFloatBitWidth() !=
36326be7fe2SLei Zhang              static_cast<unsigned>(dstBits)) {
36426be7fe2SLei Zhang     result = rewriter.create<spirv::SConvertOp>(loc, dstType, result);
36526be7fe2SLei Zhang   }
36626be7fe2SLei Zhang   rewriter.replaceOp(loadOp, result);
36726be7fe2SLei Zhang 
36826be7fe2SLei Zhang   assert(accessChainOp.use_empty());
36926be7fe2SLei Zhang   rewriter.eraseOp(accessChainOp);
37026be7fe2SLei Zhang 
37126be7fe2SLei Zhang   return success();
37226be7fe2SLei Zhang }
37326be7fe2SLei Zhang 
37426be7fe2SLei Zhang LogicalResult
37526be7fe2SLei Zhang LoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
37626be7fe2SLei Zhang                                ConversionPatternRewriter &rewriter) const {
37726be7fe2SLei Zhang   memref::LoadOpAdaptor loadOperands(operands);
37826be7fe2SLei Zhang   auto memrefType = loadOp.memref().getType().cast<MemRefType>();
37926be7fe2SLei Zhang   if (memrefType.getElementType().isSignlessInteger())
38026be7fe2SLei Zhang     return failure();
38126be7fe2SLei Zhang   auto loadPtr = spirv::getElementPtr(
38226be7fe2SLei Zhang       *getTypeConverter<SPIRVTypeConverter>(), memrefType,
38326be7fe2SLei Zhang       loadOperands.memref(), loadOperands.indices(), loadOp.getLoc(), rewriter);
3841e9799e2SButygin 
3851e9799e2SButygin   if (!loadPtr)
3861e9799e2SButygin     return failure();
3871e9799e2SButygin 
38826be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, loadPtr);
38926be7fe2SLei Zhang   return success();
39026be7fe2SLei Zhang }
39126be7fe2SLei Zhang 
39226be7fe2SLei Zhang LogicalResult
39326be7fe2SLei Zhang IntStoreOpPattern::matchAndRewrite(memref::StoreOp storeOp,
39426be7fe2SLei Zhang                                    ArrayRef<Value> operands,
39526be7fe2SLei Zhang                                    ConversionPatternRewriter &rewriter) const {
39626be7fe2SLei Zhang   memref::StoreOpAdaptor storeOperands(operands);
39726be7fe2SLei Zhang   auto memrefType = storeOp.memref().getType().cast<MemRefType>();
39826be7fe2SLei Zhang   if (!memrefType.getElementType().isSignlessInteger())
39926be7fe2SLei Zhang     return failure();
40026be7fe2SLei Zhang 
40126be7fe2SLei Zhang   auto loc = storeOp.getLoc();
40226be7fe2SLei Zhang   auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
40326be7fe2SLei Zhang   spirv::AccessChainOp accessChainOp =
40426be7fe2SLei Zhang       spirv::getElementPtr(typeConverter, memrefType, storeOperands.memref(),
40526be7fe2SLei Zhang                            storeOperands.indices(), loc, rewriter);
4061e9799e2SButygin 
4071e9799e2SButygin   if (!accessChainOp)
4081e9799e2SButygin     return failure();
4091e9799e2SButygin 
41026be7fe2SLei Zhang   int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
41126be7fe2SLei Zhang 
41226be7fe2SLei Zhang   bool isBool = srcBits == 1;
41326be7fe2SLei Zhang   if (isBool)
41426be7fe2SLei Zhang     srcBits = typeConverter.getOptions().boolNumBits;
4159f5300c8SLei Zhang 
41626be7fe2SLei Zhang   Type pointeeType = typeConverter.convertType(memrefType)
41726be7fe2SLei Zhang                          .cast<spirv::PointerType>()
41826be7fe2SLei Zhang                          .getPointeeType();
41926be7fe2SLei Zhang   Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0);
42026be7fe2SLei Zhang   Type dstType;
42126be7fe2SLei Zhang   if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>())
42226be7fe2SLei Zhang     dstType = arrayType.getElementType();
42326be7fe2SLei Zhang   else
42426be7fe2SLei Zhang     dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType();
42526be7fe2SLei Zhang 
42626be7fe2SLei Zhang   int dstBits = dstType.getIntOrFloatBitWidth();
42726be7fe2SLei Zhang   assert(dstBits % srcBits == 0);
42826be7fe2SLei Zhang 
42926be7fe2SLei Zhang   if (srcBits == dstBits) {
4309f5300c8SLei Zhang     Value storeVal = storeOperands.value();
4319f5300c8SLei Zhang     if (isBool)
4329f5300c8SLei Zhang       storeVal = castBoolToIntN(loc, storeVal, dstType, rewriter);
43326be7fe2SLei Zhang     rewriter.replaceOpWithNewOp<spirv::StoreOp>(
4349f5300c8SLei Zhang         storeOp, accessChainOp.getResult(), storeVal);
43526be7fe2SLei Zhang     return success();
43626be7fe2SLei Zhang   }
43726be7fe2SLei Zhang 
43826be7fe2SLei Zhang   // Since there are multi threads in the processing, the emulation will be done
43926be7fe2SLei Zhang   // with atomic operations. E.g., if the storing value is i8, rewrite the
44026be7fe2SLei Zhang   // StoreOp to
44126be7fe2SLei Zhang   // 1) load a 32-bit integer
44226be7fe2SLei Zhang   // 2) clear 8 bits in the loading value
44326be7fe2SLei Zhang   // 3) store 32-bit value back
44426be7fe2SLei Zhang   // 4) load a 32-bit integer
44526be7fe2SLei Zhang   // 5) modify 8 bits in the loading value
44626be7fe2SLei Zhang   // 6) store 32-bit value back
44726be7fe2SLei Zhang   // The step 1 to step 3 are done by AtomicAnd as one atomic step, and the step
44826be7fe2SLei Zhang   // 4 to step 6 are done by AtomicOr as another atomic step.
44926be7fe2SLei Zhang   assert(accessChainOp.indices().size() == 2);
45026be7fe2SLei Zhang   Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
45126be7fe2SLei Zhang   Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter);
45226be7fe2SLei Zhang 
45326be7fe2SLei Zhang   // Create a mask to clear the destination. E.g., if it is the second i8 in
45426be7fe2SLei Zhang   // i32, 0xFFFF00FF is created.
45526be7fe2SLei Zhang   Value mask = rewriter.create<spirv::ConstantOp>(
45626be7fe2SLei Zhang       loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
45726be7fe2SLei Zhang   Value clearBitsMask =
45826be7fe2SLei Zhang       rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, mask, offset);
45926be7fe2SLei Zhang   clearBitsMask = rewriter.create<spirv::NotOp>(loc, dstType, clearBitsMask);
46026be7fe2SLei Zhang 
46126be7fe2SLei Zhang   Value storeVal = storeOperands.value();
4629f5300c8SLei Zhang   if (isBool)
4639f5300c8SLei Zhang     storeVal = castBoolToIntN(loc, storeVal, dstType, rewriter);
46426be7fe2SLei Zhang   storeVal = shiftValue(loc, storeVal, offset, mask, dstBits, rewriter);
46526be7fe2SLei Zhang   Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp,
46626be7fe2SLei Zhang                                                    srcBits, dstBits, rewriter);
46726be7fe2SLei Zhang   Optional<spirv::Scope> scope = getAtomicOpScope(memrefType);
46826be7fe2SLei Zhang   if (!scope)
46926be7fe2SLei Zhang     return failure();
47026be7fe2SLei Zhang   Value result = rewriter.create<spirv::AtomicAndOp>(
47126be7fe2SLei Zhang       loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease,
47226be7fe2SLei Zhang       clearBitsMask);
47326be7fe2SLei Zhang   result = rewriter.create<spirv::AtomicOrOp>(
47426be7fe2SLei Zhang       loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease,
47526be7fe2SLei Zhang       storeVal);
47626be7fe2SLei Zhang 
47726be7fe2SLei Zhang   // The AtomicOrOp has no side effect. Since it is already inserted, we can
47826be7fe2SLei Zhang   // just remove the original StoreOp. Note that rewriter.replaceOp()
47926be7fe2SLei Zhang   // doesn't work because it only accepts that the numbers of result are the
48026be7fe2SLei Zhang   // same.
48126be7fe2SLei Zhang   rewriter.eraseOp(storeOp);
48226be7fe2SLei Zhang 
48326be7fe2SLei Zhang   assert(accessChainOp.use_empty());
48426be7fe2SLei Zhang   rewriter.eraseOp(accessChainOp);
48526be7fe2SLei Zhang 
48626be7fe2SLei Zhang   return success();
48726be7fe2SLei Zhang }
48826be7fe2SLei Zhang 
48926be7fe2SLei Zhang LogicalResult
49026be7fe2SLei Zhang StoreOpPattern::matchAndRewrite(memref::StoreOp storeOp,
49126be7fe2SLei Zhang                                 ArrayRef<Value> operands,
49226be7fe2SLei Zhang                                 ConversionPatternRewriter &rewriter) const {
49326be7fe2SLei Zhang   memref::StoreOpAdaptor storeOperands(operands);
49426be7fe2SLei Zhang   auto memrefType = storeOp.memref().getType().cast<MemRefType>();
49526be7fe2SLei Zhang   if (memrefType.getElementType().isSignlessInteger())
49626be7fe2SLei Zhang     return failure();
49726be7fe2SLei Zhang   auto storePtr =
49826be7fe2SLei Zhang       spirv::getElementPtr(*getTypeConverter<SPIRVTypeConverter>(), memrefType,
49926be7fe2SLei Zhang                            storeOperands.memref(), storeOperands.indices(),
50026be7fe2SLei Zhang                            storeOp.getLoc(), rewriter);
5011e9799e2SButygin 
5021e9799e2SButygin   if (!storePtr)
5031e9799e2SButygin     return failure();
5041e9799e2SButygin 
50526be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, storePtr,
50626be7fe2SLei Zhang                                               storeOperands.value());
50726be7fe2SLei Zhang   return success();
50826be7fe2SLei Zhang }
50926be7fe2SLei Zhang 
51026be7fe2SLei Zhang //===----------------------------------------------------------------------===//
51126be7fe2SLei Zhang // Pattern population
51226be7fe2SLei Zhang //===----------------------------------------------------------------------===//
51326be7fe2SLei Zhang 
51426be7fe2SLei Zhang namespace mlir {
51526be7fe2SLei Zhang void populateMemRefToSPIRVPatterns(SPIRVTypeConverter &typeConverter,
51626be7fe2SLei Zhang                                    RewritePatternSet &patterns) {
51726be7fe2SLei Zhang   patterns.add<AllocOpPattern, DeallocOpPattern, IntLoadOpPattern,
51826be7fe2SLei Zhang                IntStoreOpPattern, LoadOpPattern, StoreOpPattern>(
51926be7fe2SLei Zhang       typeConverter, patterns.getContext());
52026be7fe2SLei Zhang }
52126be7fe2SLei Zhang } // namespace mlir
522