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 
12226be7fe2SLei Zhang //===----------------------------------------------------------------------===//
12326be7fe2SLei Zhang // Operation conversion
12426be7fe2SLei Zhang //===----------------------------------------------------------------------===//
12526be7fe2SLei Zhang 
12626be7fe2SLei Zhang // Note that DRR cannot be used for the patterns in this file: we may need to
12726be7fe2SLei Zhang // convert type along the way, which requires ConversionPattern. DRR generates
12826be7fe2SLei Zhang // normal RewritePattern.
12926be7fe2SLei Zhang 
13026be7fe2SLei Zhang namespace {
13126be7fe2SLei Zhang 
13226be7fe2SLei Zhang /// Converts an allocation operation to SPIR-V. Currently only supports lowering
13326be7fe2SLei Zhang /// to Workgroup memory when the size is constant.  Note that this pattern needs
13426be7fe2SLei Zhang /// to be applied in a pass that runs at least at spv.module scope since it wil
13526be7fe2SLei Zhang /// ladd global variables into the spv.module.
13626be7fe2SLei Zhang class AllocOpPattern final : public OpConversionPattern<memref::AllocOp> {
13726be7fe2SLei Zhang public:
13826be7fe2SLei Zhang   using OpConversionPattern<memref::AllocOp>::OpConversionPattern;
13926be7fe2SLei Zhang 
14026be7fe2SLei Zhang   LogicalResult
14126be7fe2SLei Zhang   matchAndRewrite(memref::AllocOp operation, ArrayRef<Value> operands,
14226be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
14326be7fe2SLei Zhang };
14426be7fe2SLei Zhang 
14526be7fe2SLei Zhang /// Removed a deallocation if it is a supported allocation. Currently only
14626be7fe2SLei Zhang /// removes deallocation if the memory space is workgroup memory.
14726be7fe2SLei Zhang class DeallocOpPattern final : public OpConversionPattern<memref::DeallocOp> {
14826be7fe2SLei Zhang public:
14926be7fe2SLei Zhang   using OpConversionPattern<memref::DeallocOp>::OpConversionPattern;
15026be7fe2SLei Zhang 
15126be7fe2SLei Zhang   LogicalResult
15226be7fe2SLei Zhang   matchAndRewrite(memref::DeallocOp operation, ArrayRef<Value> operands,
15326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
15426be7fe2SLei Zhang };
15526be7fe2SLei Zhang 
15626be7fe2SLei Zhang /// Converts memref.load to spv.Load.
15726be7fe2SLei Zhang class IntLoadOpPattern final : public OpConversionPattern<memref::LoadOp> {
15826be7fe2SLei Zhang public:
15926be7fe2SLei Zhang   using OpConversionPattern<memref::LoadOp>::OpConversionPattern;
16026be7fe2SLei Zhang 
16126be7fe2SLei Zhang   LogicalResult
16226be7fe2SLei Zhang   matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
16326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
16426be7fe2SLei Zhang };
16526be7fe2SLei Zhang 
16626be7fe2SLei Zhang /// Converts memref.load to spv.Load.
16726be7fe2SLei Zhang class LoadOpPattern final : public OpConversionPattern<memref::LoadOp> {
16826be7fe2SLei Zhang public:
16926be7fe2SLei Zhang   using OpConversionPattern<memref::LoadOp>::OpConversionPattern;
17026be7fe2SLei Zhang 
17126be7fe2SLei Zhang   LogicalResult
17226be7fe2SLei Zhang   matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
17326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
17426be7fe2SLei Zhang };
17526be7fe2SLei Zhang 
17626be7fe2SLei Zhang /// Converts memref.store to spv.Store on integers.
17726be7fe2SLei Zhang class IntStoreOpPattern final : public OpConversionPattern<memref::StoreOp> {
17826be7fe2SLei Zhang public:
17926be7fe2SLei Zhang   using OpConversionPattern<memref::StoreOp>::OpConversionPattern;
18026be7fe2SLei Zhang 
18126be7fe2SLei Zhang   LogicalResult
18226be7fe2SLei Zhang   matchAndRewrite(memref::StoreOp storeOp, ArrayRef<Value> operands,
18326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
18426be7fe2SLei Zhang };
18526be7fe2SLei Zhang 
18626be7fe2SLei Zhang /// Converts memref.store to spv.Store.
18726be7fe2SLei Zhang class StoreOpPattern final : public OpConversionPattern<memref::StoreOp> {
18826be7fe2SLei Zhang public:
18926be7fe2SLei Zhang   using OpConversionPattern<memref::StoreOp>::OpConversionPattern;
19026be7fe2SLei Zhang 
19126be7fe2SLei Zhang   LogicalResult
19226be7fe2SLei Zhang   matchAndRewrite(memref::StoreOp storeOp, ArrayRef<Value> operands,
19326be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
19426be7fe2SLei Zhang };
19526be7fe2SLei Zhang 
19626be7fe2SLei Zhang } // namespace
19726be7fe2SLei Zhang 
19826be7fe2SLei Zhang //===----------------------------------------------------------------------===//
19926be7fe2SLei Zhang // AllocOp
20026be7fe2SLei Zhang //===----------------------------------------------------------------------===//
20126be7fe2SLei Zhang 
20226be7fe2SLei Zhang LogicalResult
20326be7fe2SLei Zhang AllocOpPattern::matchAndRewrite(memref::AllocOp operation,
20426be7fe2SLei Zhang                                 ArrayRef<Value> operands,
20526be7fe2SLei Zhang                                 ConversionPatternRewriter &rewriter) const {
20626be7fe2SLei Zhang   MemRefType allocType = operation.getType();
20726be7fe2SLei Zhang   if (!isAllocationSupported(allocType))
20826be7fe2SLei Zhang     return operation.emitError("unhandled allocation type");
20926be7fe2SLei Zhang 
21026be7fe2SLei Zhang   // Get the SPIR-V type for the allocation.
21126be7fe2SLei Zhang   Type spirvType = getTypeConverter()->convertType(allocType);
21226be7fe2SLei Zhang 
21326be7fe2SLei Zhang   // Insert spv.GlobalVariable for this allocation.
21426be7fe2SLei Zhang   Operation *parent =
21526be7fe2SLei Zhang       SymbolTable::getNearestSymbolTable(operation->getParentOp());
21626be7fe2SLei Zhang   if (!parent)
21726be7fe2SLei Zhang     return failure();
21826be7fe2SLei Zhang   Location loc = operation.getLoc();
21926be7fe2SLei Zhang   spirv::GlobalVariableOp varOp;
22026be7fe2SLei Zhang   {
22126be7fe2SLei Zhang     OpBuilder::InsertionGuard guard(rewriter);
22226be7fe2SLei Zhang     Block &entryBlock = *parent->getRegion(0).begin();
22326be7fe2SLei Zhang     rewriter.setInsertionPointToStart(&entryBlock);
22426be7fe2SLei Zhang     auto varOps = entryBlock.getOps<spirv::GlobalVariableOp>();
22526be7fe2SLei Zhang     std::string varName =
22626be7fe2SLei Zhang         std::string("__workgroup_mem__") +
22726be7fe2SLei Zhang         std::to_string(std::distance(varOps.begin(), varOps.end()));
22826be7fe2SLei Zhang     varOp = rewriter.create<spirv::GlobalVariableOp>(loc, spirvType, varName,
22926be7fe2SLei Zhang                                                      /*initializer=*/nullptr);
23026be7fe2SLei Zhang   }
23126be7fe2SLei Zhang 
23226be7fe2SLei Zhang   // Get pointer to global variable at the current scope.
23326be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::AddressOfOp>(operation, varOp);
23426be7fe2SLei Zhang   return success();
23526be7fe2SLei Zhang }
23626be7fe2SLei Zhang 
23726be7fe2SLei Zhang //===----------------------------------------------------------------------===//
23826be7fe2SLei Zhang // DeallocOp
23926be7fe2SLei Zhang //===----------------------------------------------------------------------===//
24026be7fe2SLei Zhang 
24126be7fe2SLei Zhang LogicalResult
24226be7fe2SLei Zhang DeallocOpPattern::matchAndRewrite(memref::DeallocOp operation,
24326be7fe2SLei Zhang                                   ArrayRef<Value> operands,
24426be7fe2SLei Zhang                                   ConversionPatternRewriter &rewriter) const {
24526be7fe2SLei Zhang   MemRefType deallocType = operation.memref().getType().cast<MemRefType>();
24626be7fe2SLei Zhang   if (!isAllocationSupported(deallocType))
24726be7fe2SLei Zhang     return operation.emitError("unhandled deallocation type");
24826be7fe2SLei Zhang   rewriter.eraseOp(operation);
24926be7fe2SLei Zhang   return success();
25026be7fe2SLei Zhang }
25126be7fe2SLei Zhang 
25226be7fe2SLei Zhang //===----------------------------------------------------------------------===//
25326be7fe2SLei Zhang // LoadOp
25426be7fe2SLei Zhang //===----------------------------------------------------------------------===//
25526be7fe2SLei Zhang 
25626be7fe2SLei Zhang LogicalResult
25726be7fe2SLei Zhang IntLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp,
25826be7fe2SLei Zhang                                   ArrayRef<Value> operands,
25926be7fe2SLei Zhang                                   ConversionPatternRewriter &rewriter) const {
26026be7fe2SLei Zhang   memref::LoadOpAdaptor loadOperands(operands);
26126be7fe2SLei Zhang   auto loc = loadOp.getLoc();
26226be7fe2SLei Zhang   auto memrefType = loadOp.memref().getType().cast<MemRefType>();
26326be7fe2SLei Zhang   if (!memrefType.getElementType().isSignlessInteger())
26426be7fe2SLei Zhang     return failure();
26526be7fe2SLei Zhang 
26626be7fe2SLei Zhang   auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
26726be7fe2SLei Zhang   spirv::AccessChainOp accessChainOp =
26826be7fe2SLei Zhang       spirv::getElementPtr(typeConverter, memrefType, loadOperands.memref(),
26926be7fe2SLei Zhang                            loadOperands.indices(), loc, rewriter);
27026be7fe2SLei Zhang 
271*1e9799e2SButygin   if (!accessChainOp)
272*1e9799e2SButygin     return failure();
273*1e9799e2SButygin 
27426be7fe2SLei Zhang   int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
27526be7fe2SLei Zhang   bool isBool = srcBits == 1;
27626be7fe2SLei Zhang   if (isBool)
27726be7fe2SLei Zhang     srcBits = typeConverter.getOptions().boolNumBits;
27826be7fe2SLei Zhang   Type pointeeType = typeConverter.convertType(memrefType)
27926be7fe2SLei Zhang                          .cast<spirv::PointerType>()
28026be7fe2SLei Zhang                          .getPointeeType();
28126be7fe2SLei Zhang   Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0);
28226be7fe2SLei Zhang   Type dstType;
28326be7fe2SLei Zhang   if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>())
28426be7fe2SLei Zhang     dstType = arrayType.getElementType();
28526be7fe2SLei Zhang   else
28626be7fe2SLei Zhang     dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType();
28726be7fe2SLei Zhang 
28826be7fe2SLei Zhang   int dstBits = dstType.getIntOrFloatBitWidth();
28926be7fe2SLei Zhang   assert(dstBits % srcBits == 0);
29026be7fe2SLei Zhang 
29126be7fe2SLei Zhang   // If the rewrited load op has the same bit width, use the loading value
29226be7fe2SLei Zhang   // directly.
29326be7fe2SLei Zhang   if (srcBits == dstBits) {
29426be7fe2SLei Zhang     rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp,
29526be7fe2SLei Zhang                                                accessChainOp.getResult());
29626be7fe2SLei Zhang     return success();
29726be7fe2SLei Zhang   }
29826be7fe2SLei Zhang 
29926be7fe2SLei Zhang   // Assume that getElementPtr() works linearizely. If it's a scalar, the method
30026be7fe2SLei Zhang   // still returns a linearized accessing. If the accessing is not linearized,
30126be7fe2SLei Zhang   // there will be offset issues.
30226be7fe2SLei Zhang   assert(accessChainOp.indices().size() == 2);
30326be7fe2SLei Zhang   Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp,
30426be7fe2SLei Zhang                                                    srcBits, dstBits, rewriter);
30526be7fe2SLei Zhang   Value spvLoadOp = rewriter.create<spirv::LoadOp>(
30626be7fe2SLei Zhang       loc, dstType, adjustedPtr,
30726be7fe2SLei Zhang       loadOp->getAttrOfType<spirv::MemoryAccessAttr>(
30826be7fe2SLei Zhang           spirv::attributeName<spirv::MemoryAccess>()),
30926be7fe2SLei Zhang       loadOp->getAttrOfType<IntegerAttr>("alignment"));
31026be7fe2SLei Zhang 
31126be7fe2SLei Zhang   // Shift the bits to the rightmost.
31226be7fe2SLei Zhang   // ____XXXX________ -> ____________XXXX
31326be7fe2SLei Zhang   Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
31426be7fe2SLei Zhang   Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter);
31526be7fe2SLei Zhang   Value result = rewriter.create<spirv::ShiftRightArithmeticOp>(
31626be7fe2SLei Zhang       loc, spvLoadOp.getType(), spvLoadOp, offset);
31726be7fe2SLei Zhang 
31826be7fe2SLei Zhang   // Apply the mask to extract corresponding bits.
31926be7fe2SLei Zhang   Value mask = rewriter.create<spirv::ConstantOp>(
32026be7fe2SLei Zhang       loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
32126be7fe2SLei Zhang   result = rewriter.create<spirv::BitwiseAndOp>(loc, dstType, result, mask);
32226be7fe2SLei Zhang 
32326be7fe2SLei Zhang   // Apply sign extension on the loading value unconditionally. The signedness
32426be7fe2SLei Zhang   // semantic is carried in the operator itself, we relies other pattern to
32526be7fe2SLei Zhang   // handle the casting.
32626be7fe2SLei Zhang   IntegerAttr shiftValueAttr =
32726be7fe2SLei Zhang       rewriter.getIntegerAttr(dstType, dstBits - srcBits);
32826be7fe2SLei Zhang   Value shiftValue =
32926be7fe2SLei Zhang       rewriter.create<spirv::ConstantOp>(loc, dstType, shiftValueAttr);
33026be7fe2SLei Zhang   result = rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, result,
33126be7fe2SLei Zhang                                                       shiftValue);
33226be7fe2SLei Zhang   result = rewriter.create<spirv::ShiftRightArithmeticOp>(loc, dstType, result,
33326be7fe2SLei Zhang                                                           shiftValue);
33426be7fe2SLei Zhang 
33526be7fe2SLei Zhang   if (isBool) {
33626be7fe2SLei Zhang     dstType = typeConverter.convertType(loadOp.getType());
33726be7fe2SLei Zhang     mask = spirv::ConstantOp::getOne(result.getType(), loc, rewriter);
33826be7fe2SLei Zhang     Value isOne = rewriter.create<spirv::IEqualOp>(loc, result, mask);
33926be7fe2SLei Zhang     Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
34026be7fe2SLei Zhang     Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
34126be7fe2SLei Zhang     result = rewriter.create<spirv::SelectOp>(loc, dstType, isOne, one, zero);
34226be7fe2SLei Zhang   } else if (result.getType().getIntOrFloatBitWidth() !=
34326be7fe2SLei Zhang              static_cast<unsigned>(dstBits)) {
34426be7fe2SLei Zhang     result = rewriter.create<spirv::SConvertOp>(loc, dstType, result);
34526be7fe2SLei Zhang   }
34626be7fe2SLei Zhang   rewriter.replaceOp(loadOp, result);
34726be7fe2SLei Zhang 
34826be7fe2SLei Zhang   assert(accessChainOp.use_empty());
34926be7fe2SLei Zhang   rewriter.eraseOp(accessChainOp);
35026be7fe2SLei Zhang 
35126be7fe2SLei Zhang   return success();
35226be7fe2SLei Zhang }
35326be7fe2SLei Zhang 
35426be7fe2SLei Zhang LogicalResult
35526be7fe2SLei Zhang LoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
35626be7fe2SLei Zhang                                ConversionPatternRewriter &rewriter) const {
35726be7fe2SLei Zhang   memref::LoadOpAdaptor loadOperands(operands);
35826be7fe2SLei Zhang   auto memrefType = loadOp.memref().getType().cast<MemRefType>();
35926be7fe2SLei Zhang   if (memrefType.getElementType().isSignlessInteger())
36026be7fe2SLei Zhang     return failure();
36126be7fe2SLei Zhang   auto loadPtr = spirv::getElementPtr(
36226be7fe2SLei Zhang       *getTypeConverter<SPIRVTypeConverter>(), memrefType,
36326be7fe2SLei Zhang       loadOperands.memref(), loadOperands.indices(), loadOp.getLoc(), rewriter);
364*1e9799e2SButygin 
365*1e9799e2SButygin   if (!loadPtr)
366*1e9799e2SButygin     return failure();
367*1e9799e2SButygin 
36826be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, loadPtr);
36926be7fe2SLei Zhang   return success();
37026be7fe2SLei Zhang }
37126be7fe2SLei Zhang 
37226be7fe2SLei Zhang LogicalResult
37326be7fe2SLei Zhang IntStoreOpPattern::matchAndRewrite(memref::StoreOp storeOp,
37426be7fe2SLei Zhang                                    ArrayRef<Value> operands,
37526be7fe2SLei Zhang                                    ConversionPatternRewriter &rewriter) const {
37626be7fe2SLei Zhang   memref::StoreOpAdaptor storeOperands(operands);
37726be7fe2SLei Zhang   auto memrefType = storeOp.memref().getType().cast<MemRefType>();
37826be7fe2SLei Zhang   if (!memrefType.getElementType().isSignlessInteger())
37926be7fe2SLei Zhang     return failure();
38026be7fe2SLei Zhang 
38126be7fe2SLei Zhang   auto loc = storeOp.getLoc();
38226be7fe2SLei Zhang   auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
38326be7fe2SLei Zhang   spirv::AccessChainOp accessChainOp =
38426be7fe2SLei Zhang       spirv::getElementPtr(typeConverter, memrefType, storeOperands.memref(),
38526be7fe2SLei Zhang                            storeOperands.indices(), loc, rewriter);
386*1e9799e2SButygin 
387*1e9799e2SButygin   if (!accessChainOp)
388*1e9799e2SButygin     return failure();
389*1e9799e2SButygin 
39026be7fe2SLei Zhang   int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
39126be7fe2SLei Zhang 
39226be7fe2SLei Zhang   bool isBool = srcBits == 1;
39326be7fe2SLei Zhang   if (isBool)
39426be7fe2SLei Zhang     srcBits = typeConverter.getOptions().boolNumBits;
39526be7fe2SLei Zhang   Type pointeeType = typeConverter.convertType(memrefType)
39626be7fe2SLei Zhang                          .cast<spirv::PointerType>()
39726be7fe2SLei Zhang                          .getPointeeType();
39826be7fe2SLei Zhang   Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0);
39926be7fe2SLei Zhang   Type dstType;
40026be7fe2SLei Zhang   if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>())
40126be7fe2SLei Zhang     dstType = arrayType.getElementType();
40226be7fe2SLei Zhang   else
40326be7fe2SLei Zhang     dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType();
40426be7fe2SLei Zhang 
40526be7fe2SLei Zhang   int dstBits = dstType.getIntOrFloatBitWidth();
40626be7fe2SLei Zhang   assert(dstBits % srcBits == 0);
40726be7fe2SLei Zhang 
40826be7fe2SLei Zhang   if (srcBits == dstBits) {
40926be7fe2SLei Zhang     rewriter.replaceOpWithNewOp<spirv::StoreOp>(
41026be7fe2SLei Zhang         storeOp, accessChainOp.getResult(), storeOperands.value());
41126be7fe2SLei Zhang     return success();
41226be7fe2SLei Zhang   }
41326be7fe2SLei Zhang 
41426be7fe2SLei Zhang   // Since there are multi threads in the processing, the emulation will be done
41526be7fe2SLei Zhang   // with atomic operations. E.g., if the storing value is i8, rewrite the
41626be7fe2SLei Zhang   // StoreOp to
41726be7fe2SLei Zhang   // 1) load a 32-bit integer
41826be7fe2SLei Zhang   // 2) clear 8 bits in the loading value
41926be7fe2SLei Zhang   // 3) store 32-bit value back
42026be7fe2SLei Zhang   // 4) load a 32-bit integer
42126be7fe2SLei Zhang   // 5) modify 8 bits in the loading value
42226be7fe2SLei Zhang   // 6) store 32-bit value back
42326be7fe2SLei Zhang   // The step 1 to step 3 are done by AtomicAnd as one atomic step, and the step
42426be7fe2SLei Zhang   // 4 to step 6 are done by AtomicOr as another atomic step.
42526be7fe2SLei Zhang   assert(accessChainOp.indices().size() == 2);
42626be7fe2SLei Zhang   Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
42726be7fe2SLei Zhang   Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter);
42826be7fe2SLei Zhang 
42926be7fe2SLei Zhang   // Create a mask to clear the destination. E.g., if it is the second i8 in
43026be7fe2SLei Zhang   // i32, 0xFFFF00FF is created.
43126be7fe2SLei Zhang   Value mask = rewriter.create<spirv::ConstantOp>(
43226be7fe2SLei Zhang       loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
43326be7fe2SLei Zhang   Value clearBitsMask =
43426be7fe2SLei Zhang       rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, mask, offset);
43526be7fe2SLei Zhang   clearBitsMask = rewriter.create<spirv::NotOp>(loc, dstType, clearBitsMask);
43626be7fe2SLei Zhang 
43726be7fe2SLei Zhang   Value storeVal = storeOperands.value();
43826be7fe2SLei Zhang   if (isBool) {
43926be7fe2SLei Zhang     Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
44026be7fe2SLei Zhang     Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
44126be7fe2SLei Zhang     storeVal =
44226be7fe2SLei Zhang         rewriter.create<spirv::SelectOp>(loc, dstType, storeVal, one, zero);
44326be7fe2SLei Zhang   }
44426be7fe2SLei Zhang   storeVal = shiftValue(loc, storeVal, offset, mask, dstBits, rewriter);
44526be7fe2SLei Zhang   Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp,
44626be7fe2SLei Zhang                                                    srcBits, dstBits, rewriter);
44726be7fe2SLei Zhang   Optional<spirv::Scope> scope = getAtomicOpScope(memrefType);
44826be7fe2SLei Zhang   if (!scope)
44926be7fe2SLei Zhang     return failure();
45026be7fe2SLei Zhang   Value result = rewriter.create<spirv::AtomicAndOp>(
45126be7fe2SLei Zhang       loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease,
45226be7fe2SLei Zhang       clearBitsMask);
45326be7fe2SLei Zhang   result = rewriter.create<spirv::AtomicOrOp>(
45426be7fe2SLei Zhang       loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease,
45526be7fe2SLei Zhang       storeVal);
45626be7fe2SLei Zhang 
45726be7fe2SLei Zhang   // The AtomicOrOp has no side effect. Since it is already inserted, we can
45826be7fe2SLei Zhang   // just remove the original StoreOp. Note that rewriter.replaceOp()
45926be7fe2SLei Zhang   // doesn't work because it only accepts that the numbers of result are the
46026be7fe2SLei Zhang   // same.
46126be7fe2SLei Zhang   rewriter.eraseOp(storeOp);
46226be7fe2SLei Zhang 
46326be7fe2SLei Zhang   assert(accessChainOp.use_empty());
46426be7fe2SLei Zhang   rewriter.eraseOp(accessChainOp);
46526be7fe2SLei Zhang 
46626be7fe2SLei Zhang   return success();
46726be7fe2SLei Zhang }
46826be7fe2SLei Zhang 
46926be7fe2SLei Zhang LogicalResult
47026be7fe2SLei Zhang StoreOpPattern::matchAndRewrite(memref::StoreOp storeOp,
47126be7fe2SLei Zhang                                 ArrayRef<Value> operands,
47226be7fe2SLei Zhang                                 ConversionPatternRewriter &rewriter) const {
47326be7fe2SLei Zhang   memref::StoreOpAdaptor storeOperands(operands);
47426be7fe2SLei Zhang   auto memrefType = storeOp.memref().getType().cast<MemRefType>();
47526be7fe2SLei Zhang   if (memrefType.getElementType().isSignlessInteger())
47626be7fe2SLei Zhang     return failure();
47726be7fe2SLei Zhang   auto storePtr =
47826be7fe2SLei Zhang       spirv::getElementPtr(*getTypeConverter<SPIRVTypeConverter>(), memrefType,
47926be7fe2SLei Zhang                            storeOperands.memref(), storeOperands.indices(),
48026be7fe2SLei Zhang                            storeOp.getLoc(), rewriter);
481*1e9799e2SButygin 
482*1e9799e2SButygin   if (!storePtr)
483*1e9799e2SButygin     return failure();
484*1e9799e2SButygin 
48526be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, storePtr,
48626be7fe2SLei Zhang                                               storeOperands.value());
48726be7fe2SLei Zhang   return success();
48826be7fe2SLei Zhang }
48926be7fe2SLei Zhang 
49026be7fe2SLei Zhang //===----------------------------------------------------------------------===//
49126be7fe2SLei Zhang // Pattern population
49226be7fe2SLei Zhang //===----------------------------------------------------------------------===//
49326be7fe2SLei Zhang 
49426be7fe2SLei Zhang namespace mlir {
49526be7fe2SLei Zhang void populateMemRefToSPIRVPatterns(SPIRVTypeConverter &typeConverter,
49626be7fe2SLei Zhang                                    RewritePatternSet &patterns) {
49726be7fe2SLei Zhang   patterns.add<AllocOpPattern, DeallocOpPattern, IntLoadOpPattern,
49826be7fe2SLei Zhang                IntStoreOpPattern, LoadOpPattern, StoreOpPattern>(
49926be7fe2SLei Zhang       typeConverter, patterns.getContext());
50026be7fe2SLei Zhang }
50126be7fe2SLei Zhang } // namespace mlir
502