1*26be7fe2SLei Zhang //===- MemRefToSPIRV.cpp - MemRef to SPIR-V Patterns ----------------------===//
2*26be7fe2SLei Zhang //
3*26be7fe2SLei Zhang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*26be7fe2SLei Zhang // See https://llvm.org/LICENSE.txt for license information.
5*26be7fe2SLei Zhang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*26be7fe2SLei Zhang //
7*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
8*26be7fe2SLei Zhang //
9*26be7fe2SLei Zhang // This file implements patterns to convert MemRef dialect to SPIR-V dialect.
10*26be7fe2SLei Zhang //
11*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
12*26be7fe2SLei Zhang 
13*26be7fe2SLei Zhang #include "mlir/Dialect/MemRef/IR/MemRef.h"
14*26be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
15*26be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
16*26be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h"
17*26be7fe2SLei Zhang #include "llvm/Support/Debug.h"
18*26be7fe2SLei Zhang 
19*26be7fe2SLei Zhang #define DEBUG_TYPE "memref-to-spirv-pattern"
20*26be7fe2SLei Zhang 
21*26be7fe2SLei Zhang using namespace mlir;
22*26be7fe2SLei Zhang 
23*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
24*26be7fe2SLei Zhang // Utility functions
25*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
26*26be7fe2SLei Zhang 
27*26be7fe2SLei Zhang /// Returns the offset of the value in `targetBits` representation.
28*26be7fe2SLei Zhang ///
29*26be7fe2SLei Zhang /// `srcIdx` is an index into a 1-D array with each element having `sourceBits`.
30*26be7fe2SLei Zhang /// It's assumed to be non-negative.
31*26be7fe2SLei Zhang ///
32*26be7fe2SLei Zhang /// When accessing an element in the array treating as having elements of
33*26be7fe2SLei Zhang /// `targetBits`, multiple values are loaded in the same time. The method
34*26be7fe2SLei Zhang /// returns the offset where the `srcIdx` locates in the value. For example, if
35*26be7fe2SLei Zhang /// `sourceBits` equals to 8 and `targetBits` equals to 32, the x-th element is
36*26be7fe2SLei Zhang /// located at (x % 4) * 8. Because there are four elements in one i32, and one
37*26be7fe2SLei Zhang /// element has 8 bits.
38*26be7fe2SLei Zhang static Value getOffsetForBitwidth(Location loc, Value srcIdx, int sourceBits,
39*26be7fe2SLei Zhang                                   int targetBits, OpBuilder &builder) {
40*26be7fe2SLei Zhang   assert(targetBits % sourceBits == 0);
41*26be7fe2SLei Zhang   IntegerType targetType = builder.getIntegerType(targetBits);
42*26be7fe2SLei Zhang   IntegerAttr idxAttr =
43*26be7fe2SLei Zhang       builder.getIntegerAttr(targetType, targetBits / sourceBits);
44*26be7fe2SLei Zhang   auto idx = builder.create<spirv::ConstantOp>(loc, targetType, idxAttr);
45*26be7fe2SLei Zhang   IntegerAttr srcBitsAttr = builder.getIntegerAttr(targetType, sourceBits);
46*26be7fe2SLei Zhang   auto srcBitsValue =
47*26be7fe2SLei Zhang       builder.create<spirv::ConstantOp>(loc, targetType, srcBitsAttr);
48*26be7fe2SLei Zhang   auto m = builder.create<spirv::UModOp>(loc, srcIdx, idx);
49*26be7fe2SLei Zhang   return builder.create<spirv::IMulOp>(loc, targetType, m, srcBitsValue);
50*26be7fe2SLei Zhang }
51*26be7fe2SLei Zhang 
52*26be7fe2SLei Zhang /// Returns an adjusted spirv::AccessChainOp. Based on the
53*26be7fe2SLei Zhang /// extension/capabilities, certain integer bitwidths `sourceBits` might not be
54*26be7fe2SLei Zhang /// supported. During conversion if a memref of an unsupported type is used,
55*26be7fe2SLei Zhang /// load/stores to this memref need to be modified to use a supported higher
56*26be7fe2SLei Zhang /// bitwidth `targetBits` and extracting the required bits. For an accessing a
57*26be7fe2SLei Zhang /// 1D array (spv.array or spv.rt_array), the last index is modified to load the
58*26be7fe2SLei Zhang /// bits needed. The extraction of the actual bits needed are handled
59*26be7fe2SLei Zhang /// separately. Note that this only works for a 1-D tensor.
60*26be7fe2SLei Zhang static Value adjustAccessChainForBitwidth(SPIRVTypeConverter &typeConverter,
61*26be7fe2SLei Zhang                                           spirv::AccessChainOp op,
62*26be7fe2SLei Zhang                                           int sourceBits, int targetBits,
63*26be7fe2SLei Zhang                                           OpBuilder &builder) {
64*26be7fe2SLei Zhang   assert(targetBits % sourceBits == 0);
65*26be7fe2SLei Zhang   const auto loc = op.getLoc();
66*26be7fe2SLei Zhang   IntegerType targetType = builder.getIntegerType(targetBits);
67*26be7fe2SLei Zhang   IntegerAttr attr =
68*26be7fe2SLei Zhang       builder.getIntegerAttr(targetType, targetBits / sourceBits);
69*26be7fe2SLei Zhang   auto idx = builder.create<spirv::ConstantOp>(loc, targetType, attr);
70*26be7fe2SLei Zhang   auto lastDim = op->getOperand(op.getNumOperands() - 1);
71*26be7fe2SLei Zhang   auto indices = llvm::to_vector<4>(op.indices());
72*26be7fe2SLei Zhang   // There are two elements if this is a 1-D tensor.
73*26be7fe2SLei Zhang   assert(indices.size() == 2);
74*26be7fe2SLei Zhang   indices.back() = builder.create<spirv::SDivOp>(loc, lastDim, idx);
75*26be7fe2SLei Zhang   Type t = typeConverter.convertType(op.component_ptr().getType());
76*26be7fe2SLei Zhang   return builder.create<spirv::AccessChainOp>(loc, t, op.base_ptr(), indices);
77*26be7fe2SLei Zhang }
78*26be7fe2SLei Zhang 
79*26be7fe2SLei Zhang /// Returns the shifted `targetBits`-bit value with the given offset.
80*26be7fe2SLei Zhang static Value shiftValue(Location loc, Value value, Value offset, Value mask,
81*26be7fe2SLei Zhang                         int targetBits, OpBuilder &builder) {
82*26be7fe2SLei Zhang   Type targetType = builder.getIntegerType(targetBits);
83*26be7fe2SLei Zhang   Value result = builder.create<spirv::BitwiseAndOp>(loc, value, mask);
84*26be7fe2SLei Zhang   return builder.create<spirv::ShiftLeftLogicalOp>(loc, targetType, result,
85*26be7fe2SLei Zhang                                                    offset);
86*26be7fe2SLei Zhang }
87*26be7fe2SLei Zhang 
88*26be7fe2SLei Zhang /// Returns true if the allocations of type `t` can be lowered to SPIR-V.
89*26be7fe2SLei Zhang static bool isAllocationSupported(MemRefType t) {
90*26be7fe2SLei Zhang   // Currently only support workgroup local memory allocations with static
91*26be7fe2SLei Zhang   // shape and int or float or vector of int or float element type.
92*26be7fe2SLei Zhang   if (!(t.hasStaticShape() &&
93*26be7fe2SLei Zhang         SPIRVTypeConverter::getMemorySpaceForStorageClass(
94*26be7fe2SLei Zhang             spirv::StorageClass::Workgroup) == t.getMemorySpaceAsInt()))
95*26be7fe2SLei Zhang     return false;
96*26be7fe2SLei Zhang   Type elementType = t.getElementType();
97*26be7fe2SLei Zhang   if (auto vecType = elementType.dyn_cast<VectorType>())
98*26be7fe2SLei Zhang     elementType = vecType.getElementType();
99*26be7fe2SLei Zhang   return elementType.isIntOrFloat();
100*26be7fe2SLei Zhang }
101*26be7fe2SLei Zhang 
102*26be7fe2SLei Zhang /// Returns the scope to use for atomic operations use for emulating store
103*26be7fe2SLei Zhang /// operations of unsupported integer bitwidths, based on the memref
104*26be7fe2SLei Zhang /// type. Returns None on failure.
105*26be7fe2SLei Zhang static Optional<spirv::Scope> getAtomicOpScope(MemRefType t) {
106*26be7fe2SLei Zhang   Optional<spirv::StorageClass> storageClass =
107*26be7fe2SLei Zhang       SPIRVTypeConverter::getStorageClassForMemorySpace(
108*26be7fe2SLei Zhang           t.getMemorySpaceAsInt());
109*26be7fe2SLei Zhang   if (!storageClass)
110*26be7fe2SLei Zhang     return {};
111*26be7fe2SLei Zhang   switch (*storageClass) {
112*26be7fe2SLei Zhang   case spirv::StorageClass::StorageBuffer:
113*26be7fe2SLei Zhang     return spirv::Scope::Device;
114*26be7fe2SLei Zhang   case spirv::StorageClass::Workgroup:
115*26be7fe2SLei Zhang     return spirv::Scope::Workgroup;
116*26be7fe2SLei Zhang   default: {
117*26be7fe2SLei Zhang   }
118*26be7fe2SLei Zhang   }
119*26be7fe2SLei Zhang   return {};
120*26be7fe2SLei Zhang }
121*26be7fe2SLei Zhang 
122*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
123*26be7fe2SLei Zhang // Operation conversion
124*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
125*26be7fe2SLei Zhang 
126*26be7fe2SLei Zhang // Note that DRR cannot be used for the patterns in this file: we may need to
127*26be7fe2SLei Zhang // convert type along the way, which requires ConversionPattern. DRR generates
128*26be7fe2SLei Zhang // normal RewritePattern.
129*26be7fe2SLei Zhang 
130*26be7fe2SLei Zhang namespace {
131*26be7fe2SLei Zhang 
132*26be7fe2SLei Zhang /// Converts an allocation operation to SPIR-V. Currently only supports lowering
133*26be7fe2SLei Zhang /// to Workgroup memory when the size is constant.  Note that this pattern needs
134*26be7fe2SLei Zhang /// to be applied in a pass that runs at least at spv.module scope since it wil
135*26be7fe2SLei Zhang /// ladd global variables into the spv.module.
136*26be7fe2SLei Zhang class AllocOpPattern final : public OpConversionPattern<memref::AllocOp> {
137*26be7fe2SLei Zhang public:
138*26be7fe2SLei Zhang   using OpConversionPattern<memref::AllocOp>::OpConversionPattern;
139*26be7fe2SLei Zhang 
140*26be7fe2SLei Zhang   LogicalResult
141*26be7fe2SLei Zhang   matchAndRewrite(memref::AllocOp operation, ArrayRef<Value> operands,
142*26be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
143*26be7fe2SLei Zhang };
144*26be7fe2SLei Zhang 
145*26be7fe2SLei Zhang /// Removed a deallocation if it is a supported allocation. Currently only
146*26be7fe2SLei Zhang /// removes deallocation if the memory space is workgroup memory.
147*26be7fe2SLei Zhang class DeallocOpPattern final : public OpConversionPattern<memref::DeallocOp> {
148*26be7fe2SLei Zhang public:
149*26be7fe2SLei Zhang   using OpConversionPattern<memref::DeallocOp>::OpConversionPattern;
150*26be7fe2SLei Zhang 
151*26be7fe2SLei Zhang   LogicalResult
152*26be7fe2SLei Zhang   matchAndRewrite(memref::DeallocOp operation, ArrayRef<Value> operands,
153*26be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
154*26be7fe2SLei Zhang };
155*26be7fe2SLei Zhang 
156*26be7fe2SLei Zhang /// Converts memref.load to spv.Load.
157*26be7fe2SLei Zhang class IntLoadOpPattern final : public OpConversionPattern<memref::LoadOp> {
158*26be7fe2SLei Zhang public:
159*26be7fe2SLei Zhang   using OpConversionPattern<memref::LoadOp>::OpConversionPattern;
160*26be7fe2SLei Zhang 
161*26be7fe2SLei Zhang   LogicalResult
162*26be7fe2SLei Zhang   matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
163*26be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
164*26be7fe2SLei Zhang };
165*26be7fe2SLei Zhang 
166*26be7fe2SLei Zhang /// Converts memref.load to spv.Load.
167*26be7fe2SLei Zhang class LoadOpPattern final : public OpConversionPattern<memref::LoadOp> {
168*26be7fe2SLei Zhang public:
169*26be7fe2SLei Zhang   using OpConversionPattern<memref::LoadOp>::OpConversionPattern;
170*26be7fe2SLei Zhang 
171*26be7fe2SLei Zhang   LogicalResult
172*26be7fe2SLei Zhang   matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
173*26be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
174*26be7fe2SLei Zhang };
175*26be7fe2SLei Zhang 
176*26be7fe2SLei Zhang /// Converts memref.store to spv.Store on integers.
177*26be7fe2SLei Zhang class IntStoreOpPattern final : public OpConversionPattern<memref::StoreOp> {
178*26be7fe2SLei Zhang public:
179*26be7fe2SLei Zhang   using OpConversionPattern<memref::StoreOp>::OpConversionPattern;
180*26be7fe2SLei Zhang 
181*26be7fe2SLei Zhang   LogicalResult
182*26be7fe2SLei Zhang   matchAndRewrite(memref::StoreOp storeOp, ArrayRef<Value> operands,
183*26be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
184*26be7fe2SLei Zhang };
185*26be7fe2SLei Zhang 
186*26be7fe2SLei Zhang /// Converts memref.store to spv.Store.
187*26be7fe2SLei Zhang class StoreOpPattern final : public OpConversionPattern<memref::StoreOp> {
188*26be7fe2SLei Zhang public:
189*26be7fe2SLei Zhang   using OpConversionPattern<memref::StoreOp>::OpConversionPattern;
190*26be7fe2SLei Zhang 
191*26be7fe2SLei Zhang   LogicalResult
192*26be7fe2SLei Zhang   matchAndRewrite(memref::StoreOp storeOp, ArrayRef<Value> operands,
193*26be7fe2SLei Zhang                   ConversionPatternRewriter &rewriter) const override;
194*26be7fe2SLei Zhang };
195*26be7fe2SLei Zhang 
196*26be7fe2SLei Zhang } // namespace
197*26be7fe2SLei Zhang 
198*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
199*26be7fe2SLei Zhang // AllocOp
200*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
201*26be7fe2SLei Zhang 
202*26be7fe2SLei Zhang LogicalResult
203*26be7fe2SLei Zhang AllocOpPattern::matchAndRewrite(memref::AllocOp operation,
204*26be7fe2SLei Zhang                                 ArrayRef<Value> operands,
205*26be7fe2SLei Zhang                                 ConversionPatternRewriter &rewriter) const {
206*26be7fe2SLei Zhang   MemRefType allocType = operation.getType();
207*26be7fe2SLei Zhang   if (!isAllocationSupported(allocType))
208*26be7fe2SLei Zhang     return operation.emitError("unhandled allocation type");
209*26be7fe2SLei Zhang 
210*26be7fe2SLei Zhang   // Get the SPIR-V type for the allocation.
211*26be7fe2SLei Zhang   Type spirvType = getTypeConverter()->convertType(allocType);
212*26be7fe2SLei Zhang 
213*26be7fe2SLei Zhang   // Insert spv.GlobalVariable for this allocation.
214*26be7fe2SLei Zhang   Operation *parent =
215*26be7fe2SLei Zhang       SymbolTable::getNearestSymbolTable(operation->getParentOp());
216*26be7fe2SLei Zhang   if (!parent)
217*26be7fe2SLei Zhang     return failure();
218*26be7fe2SLei Zhang   Location loc = operation.getLoc();
219*26be7fe2SLei Zhang   spirv::GlobalVariableOp varOp;
220*26be7fe2SLei Zhang   {
221*26be7fe2SLei Zhang     OpBuilder::InsertionGuard guard(rewriter);
222*26be7fe2SLei Zhang     Block &entryBlock = *parent->getRegion(0).begin();
223*26be7fe2SLei Zhang     rewriter.setInsertionPointToStart(&entryBlock);
224*26be7fe2SLei Zhang     auto varOps = entryBlock.getOps<spirv::GlobalVariableOp>();
225*26be7fe2SLei Zhang     std::string varName =
226*26be7fe2SLei Zhang         std::string("__workgroup_mem__") +
227*26be7fe2SLei Zhang         std::to_string(std::distance(varOps.begin(), varOps.end()));
228*26be7fe2SLei Zhang     varOp = rewriter.create<spirv::GlobalVariableOp>(loc, spirvType, varName,
229*26be7fe2SLei Zhang                                                      /*initializer=*/nullptr);
230*26be7fe2SLei Zhang   }
231*26be7fe2SLei Zhang 
232*26be7fe2SLei Zhang   // Get pointer to global variable at the current scope.
233*26be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::AddressOfOp>(operation, varOp);
234*26be7fe2SLei Zhang   return success();
235*26be7fe2SLei Zhang }
236*26be7fe2SLei Zhang 
237*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
238*26be7fe2SLei Zhang // DeallocOp
239*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
240*26be7fe2SLei Zhang 
241*26be7fe2SLei Zhang LogicalResult
242*26be7fe2SLei Zhang DeallocOpPattern::matchAndRewrite(memref::DeallocOp operation,
243*26be7fe2SLei Zhang                                   ArrayRef<Value> operands,
244*26be7fe2SLei Zhang                                   ConversionPatternRewriter &rewriter) const {
245*26be7fe2SLei Zhang   MemRefType deallocType = operation.memref().getType().cast<MemRefType>();
246*26be7fe2SLei Zhang   if (!isAllocationSupported(deallocType))
247*26be7fe2SLei Zhang     return operation.emitError("unhandled deallocation type");
248*26be7fe2SLei Zhang   rewriter.eraseOp(operation);
249*26be7fe2SLei Zhang   return success();
250*26be7fe2SLei Zhang }
251*26be7fe2SLei Zhang 
252*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
253*26be7fe2SLei Zhang // LoadOp
254*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
255*26be7fe2SLei Zhang 
256*26be7fe2SLei Zhang LogicalResult
257*26be7fe2SLei Zhang IntLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp,
258*26be7fe2SLei Zhang                                   ArrayRef<Value> operands,
259*26be7fe2SLei Zhang                                   ConversionPatternRewriter &rewriter) const {
260*26be7fe2SLei Zhang   memref::LoadOpAdaptor loadOperands(operands);
261*26be7fe2SLei Zhang   auto loc = loadOp.getLoc();
262*26be7fe2SLei Zhang   auto memrefType = loadOp.memref().getType().cast<MemRefType>();
263*26be7fe2SLei Zhang   if (!memrefType.getElementType().isSignlessInteger())
264*26be7fe2SLei Zhang     return failure();
265*26be7fe2SLei Zhang 
266*26be7fe2SLei Zhang   auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
267*26be7fe2SLei Zhang   spirv::AccessChainOp accessChainOp =
268*26be7fe2SLei Zhang       spirv::getElementPtr(typeConverter, memrefType, loadOperands.memref(),
269*26be7fe2SLei Zhang                            loadOperands.indices(), loc, rewriter);
270*26be7fe2SLei Zhang 
271*26be7fe2SLei Zhang   int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
272*26be7fe2SLei Zhang   bool isBool = srcBits == 1;
273*26be7fe2SLei Zhang   if (isBool)
274*26be7fe2SLei Zhang     srcBits = typeConverter.getOptions().boolNumBits;
275*26be7fe2SLei Zhang   Type pointeeType = typeConverter.convertType(memrefType)
276*26be7fe2SLei Zhang                          .cast<spirv::PointerType>()
277*26be7fe2SLei Zhang                          .getPointeeType();
278*26be7fe2SLei Zhang   Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0);
279*26be7fe2SLei Zhang   Type dstType;
280*26be7fe2SLei Zhang   if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>())
281*26be7fe2SLei Zhang     dstType = arrayType.getElementType();
282*26be7fe2SLei Zhang   else
283*26be7fe2SLei Zhang     dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType();
284*26be7fe2SLei Zhang 
285*26be7fe2SLei Zhang   int dstBits = dstType.getIntOrFloatBitWidth();
286*26be7fe2SLei Zhang   assert(dstBits % srcBits == 0);
287*26be7fe2SLei Zhang 
288*26be7fe2SLei Zhang   // If the rewrited load op has the same bit width, use the loading value
289*26be7fe2SLei Zhang   // directly.
290*26be7fe2SLei Zhang   if (srcBits == dstBits) {
291*26be7fe2SLei Zhang     rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp,
292*26be7fe2SLei Zhang                                                accessChainOp.getResult());
293*26be7fe2SLei Zhang     return success();
294*26be7fe2SLei Zhang   }
295*26be7fe2SLei Zhang 
296*26be7fe2SLei Zhang   // Assume that getElementPtr() works linearizely. If it's a scalar, the method
297*26be7fe2SLei Zhang   // still returns a linearized accessing. If the accessing is not linearized,
298*26be7fe2SLei Zhang   // there will be offset issues.
299*26be7fe2SLei Zhang   assert(accessChainOp.indices().size() == 2);
300*26be7fe2SLei Zhang   Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp,
301*26be7fe2SLei Zhang                                                    srcBits, dstBits, rewriter);
302*26be7fe2SLei Zhang   Value spvLoadOp = rewriter.create<spirv::LoadOp>(
303*26be7fe2SLei Zhang       loc, dstType, adjustedPtr,
304*26be7fe2SLei Zhang       loadOp->getAttrOfType<spirv::MemoryAccessAttr>(
305*26be7fe2SLei Zhang           spirv::attributeName<spirv::MemoryAccess>()),
306*26be7fe2SLei Zhang       loadOp->getAttrOfType<IntegerAttr>("alignment"));
307*26be7fe2SLei Zhang 
308*26be7fe2SLei Zhang   // Shift the bits to the rightmost.
309*26be7fe2SLei Zhang   // ____XXXX________ -> ____________XXXX
310*26be7fe2SLei Zhang   Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
311*26be7fe2SLei Zhang   Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter);
312*26be7fe2SLei Zhang   Value result = rewriter.create<spirv::ShiftRightArithmeticOp>(
313*26be7fe2SLei Zhang       loc, spvLoadOp.getType(), spvLoadOp, offset);
314*26be7fe2SLei Zhang 
315*26be7fe2SLei Zhang   // Apply the mask to extract corresponding bits.
316*26be7fe2SLei Zhang   Value mask = rewriter.create<spirv::ConstantOp>(
317*26be7fe2SLei Zhang       loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
318*26be7fe2SLei Zhang   result = rewriter.create<spirv::BitwiseAndOp>(loc, dstType, result, mask);
319*26be7fe2SLei Zhang 
320*26be7fe2SLei Zhang   // Apply sign extension on the loading value unconditionally. The signedness
321*26be7fe2SLei Zhang   // semantic is carried in the operator itself, we relies other pattern to
322*26be7fe2SLei Zhang   // handle the casting.
323*26be7fe2SLei Zhang   IntegerAttr shiftValueAttr =
324*26be7fe2SLei Zhang       rewriter.getIntegerAttr(dstType, dstBits - srcBits);
325*26be7fe2SLei Zhang   Value shiftValue =
326*26be7fe2SLei Zhang       rewriter.create<spirv::ConstantOp>(loc, dstType, shiftValueAttr);
327*26be7fe2SLei Zhang   result = rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, result,
328*26be7fe2SLei Zhang                                                       shiftValue);
329*26be7fe2SLei Zhang   result = rewriter.create<spirv::ShiftRightArithmeticOp>(loc, dstType, result,
330*26be7fe2SLei Zhang                                                           shiftValue);
331*26be7fe2SLei Zhang 
332*26be7fe2SLei Zhang   if (isBool) {
333*26be7fe2SLei Zhang     dstType = typeConverter.convertType(loadOp.getType());
334*26be7fe2SLei Zhang     mask = spirv::ConstantOp::getOne(result.getType(), loc, rewriter);
335*26be7fe2SLei Zhang     Value isOne = rewriter.create<spirv::IEqualOp>(loc, result, mask);
336*26be7fe2SLei Zhang     Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
337*26be7fe2SLei Zhang     Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
338*26be7fe2SLei Zhang     result = rewriter.create<spirv::SelectOp>(loc, dstType, isOne, one, zero);
339*26be7fe2SLei Zhang   } else if (result.getType().getIntOrFloatBitWidth() !=
340*26be7fe2SLei Zhang              static_cast<unsigned>(dstBits)) {
341*26be7fe2SLei Zhang     result = rewriter.create<spirv::SConvertOp>(loc, dstType, result);
342*26be7fe2SLei Zhang   }
343*26be7fe2SLei Zhang   rewriter.replaceOp(loadOp, result);
344*26be7fe2SLei Zhang 
345*26be7fe2SLei Zhang   assert(accessChainOp.use_empty());
346*26be7fe2SLei Zhang   rewriter.eraseOp(accessChainOp);
347*26be7fe2SLei Zhang 
348*26be7fe2SLei Zhang   return success();
349*26be7fe2SLei Zhang }
350*26be7fe2SLei Zhang 
351*26be7fe2SLei Zhang LogicalResult
352*26be7fe2SLei Zhang LoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, ArrayRef<Value> operands,
353*26be7fe2SLei Zhang                                ConversionPatternRewriter &rewriter) const {
354*26be7fe2SLei Zhang   memref::LoadOpAdaptor loadOperands(operands);
355*26be7fe2SLei Zhang   auto memrefType = loadOp.memref().getType().cast<MemRefType>();
356*26be7fe2SLei Zhang   if (memrefType.getElementType().isSignlessInteger())
357*26be7fe2SLei Zhang     return failure();
358*26be7fe2SLei Zhang   auto loadPtr = spirv::getElementPtr(
359*26be7fe2SLei Zhang       *getTypeConverter<SPIRVTypeConverter>(), memrefType,
360*26be7fe2SLei Zhang       loadOperands.memref(), loadOperands.indices(), loadOp.getLoc(), rewriter);
361*26be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, loadPtr);
362*26be7fe2SLei Zhang   return success();
363*26be7fe2SLei Zhang }
364*26be7fe2SLei Zhang 
365*26be7fe2SLei Zhang LogicalResult
366*26be7fe2SLei Zhang IntStoreOpPattern::matchAndRewrite(memref::StoreOp storeOp,
367*26be7fe2SLei Zhang                                    ArrayRef<Value> operands,
368*26be7fe2SLei Zhang                                    ConversionPatternRewriter &rewriter) const {
369*26be7fe2SLei Zhang   memref::StoreOpAdaptor storeOperands(operands);
370*26be7fe2SLei Zhang   auto memrefType = storeOp.memref().getType().cast<MemRefType>();
371*26be7fe2SLei Zhang   if (!memrefType.getElementType().isSignlessInteger())
372*26be7fe2SLei Zhang     return failure();
373*26be7fe2SLei Zhang 
374*26be7fe2SLei Zhang   auto loc = storeOp.getLoc();
375*26be7fe2SLei Zhang   auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
376*26be7fe2SLei Zhang   spirv::AccessChainOp accessChainOp =
377*26be7fe2SLei Zhang       spirv::getElementPtr(typeConverter, memrefType, storeOperands.memref(),
378*26be7fe2SLei Zhang                            storeOperands.indices(), loc, rewriter);
379*26be7fe2SLei Zhang   int srcBits = memrefType.getElementType().getIntOrFloatBitWidth();
380*26be7fe2SLei Zhang 
381*26be7fe2SLei Zhang   bool isBool = srcBits == 1;
382*26be7fe2SLei Zhang   if (isBool)
383*26be7fe2SLei Zhang     srcBits = typeConverter.getOptions().boolNumBits;
384*26be7fe2SLei Zhang   Type pointeeType = typeConverter.convertType(memrefType)
385*26be7fe2SLei Zhang                          .cast<spirv::PointerType>()
386*26be7fe2SLei Zhang                          .getPointeeType();
387*26be7fe2SLei Zhang   Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0);
388*26be7fe2SLei Zhang   Type dstType;
389*26be7fe2SLei Zhang   if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>())
390*26be7fe2SLei Zhang     dstType = arrayType.getElementType();
391*26be7fe2SLei Zhang   else
392*26be7fe2SLei Zhang     dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType();
393*26be7fe2SLei Zhang 
394*26be7fe2SLei Zhang   int dstBits = dstType.getIntOrFloatBitWidth();
395*26be7fe2SLei Zhang   assert(dstBits % srcBits == 0);
396*26be7fe2SLei Zhang 
397*26be7fe2SLei Zhang   if (srcBits == dstBits) {
398*26be7fe2SLei Zhang     rewriter.replaceOpWithNewOp<spirv::StoreOp>(
399*26be7fe2SLei Zhang         storeOp, accessChainOp.getResult(), storeOperands.value());
400*26be7fe2SLei Zhang     return success();
401*26be7fe2SLei Zhang   }
402*26be7fe2SLei Zhang 
403*26be7fe2SLei Zhang   // Since there are multi threads in the processing, the emulation will be done
404*26be7fe2SLei Zhang   // with atomic operations. E.g., if the storing value is i8, rewrite the
405*26be7fe2SLei Zhang   // StoreOp to
406*26be7fe2SLei Zhang   // 1) load a 32-bit integer
407*26be7fe2SLei Zhang   // 2) clear 8 bits in the loading value
408*26be7fe2SLei Zhang   // 3) store 32-bit value back
409*26be7fe2SLei Zhang   // 4) load a 32-bit integer
410*26be7fe2SLei Zhang   // 5) modify 8 bits in the loading value
411*26be7fe2SLei Zhang   // 6) store 32-bit value back
412*26be7fe2SLei Zhang   // The step 1 to step 3 are done by AtomicAnd as one atomic step, and the step
413*26be7fe2SLei Zhang   // 4 to step 6 are done by AtomicOr as another atomic step.
414*26be7fe2SLei Zhang   assert(accessChainOp.indices().size() == 2);
415*26be7fe2SLei Zhang   Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1);
416*26be7fe2SLei Zhang   Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter);
417*26be7fe2SLei Zhang 
418*26be7fe2SLei Zhang   // Create a mask to clear the destination. E.g., if it is the second i8 in
419*26be7fe2SLei Zhang   // i32, 0xFFFF00FF is created.
420*26be7fe2SLei Zhang   Value mask = rewriter.create<spirv::ConstantOp>(
421*26be7fe2SLei Zhang       loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1));
422*26be7fe2SLei Zhang   Value clearBitsMask =
423*26be7fe2SLei Zhang       rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, mask, offset);
424*26be7fe2SLei Zhang   clearBitsMask = rewriter.create<spirv::NotOp>(loc, dstType, clearBitsMask);
425*26be7fe2SLei Zhang 
426*26be7fe2SLei Zhang   Value storeVal = storeOperands.value();
427*26be7fe2SLei Zhang   if (isBool) {
428*26be7fe2SLei Zhang     Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
429*26be7fe2SLei Zhang     Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
430*26be7fe2SLei Zhang     storeVal =
431*26be7fe2SLei Zhang         rewriter.create<spirv::SelectOp>(loc, dstType, storeVal, one, zero);
432*26be7fe2SLei Zhang   }
433*26be7fe2SLei Zhang   storeVal = shiftValue(loc, storeVal, offset, mask, dstBits, rewriter);
434*26be7fe2SLei Zhang   Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp,
435*26be7fe2SLei Zhang                                                    srcBits, dstBits, rewriter);
436*26be7fe2SLei Zhang   Optional<spirv::Scope> scope = getAtomicOpScope(memrefType);
437*26be7fe2SLei Zhang   if (!scope)
438*26be7fe2SLei Zhang     return failure();
439*26be7fe2SLei Zhang   Value result = rewriter.create<spirv::AtomicAndOp>(
440*26be7fe2SLei Zhang       loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease,
441*26be7fe2SLei Zhang       clearBitsMask);
442*26be7fe2SLei Zhang   result = rewriter.create<spirv::AtomicOrOp>(
443*26be7fe2SLei Zhang       loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease,
444*26be7fe2SLei Zhang       storeVal);
445*26be7fe2SLei Zhang 
446*26be7fe2SLei Zhang   // The AtomicOrOp has no side effect. Since it is already inserted, we can
447*26be7fe2SLei Zhang   // just remove the original StoreOp. Note that rewriter.replaceOp()
448*26be7fe2SLei Zhang   // doesn't work because it only accepts that the numbers of result are the
449*26be7fe2SLei Zhang   // same.
450*26be7fe2SLei Zhang   rewriter.eraseOp(storeOp);
451*26be7fe2SLei Zhang 
452*26be7fe2SLei Zhang   assert(accessChainOp.use_empty());
453*26be7fe2SLei Zhang   rewriter.eraseOp(accessChainOp);
454*26be7fe2SLei Zhang 
455*26be7fe2SLei Zhang   return success();
456*26be7fe2SLei Zhang }
457*26be7fe2SLei Zhang 
458*26be7fe2SLei Zhang LogicalResult
459*26be7fe2SLei Zhang StoreOpPattern::matchAndRewrite(memref::StoreOp storeOp,
460*26be7fe2SLei Zhang                                 ArrayRef<Value> operands,
461*26be7fe2SLei Zhang                                 ConversionPatternRewriter &rewriter) const {
462*26be7fe2SLei Zhang   memref::StoreOpAdaptor storeOperands(operands);
463*26be7fe2SLei Zhang   auto memrefType = storeOp.memref().getType().cast<MemRefType>();
464*26be7fe2SLei Zhang   if (memrefType.getElementType().isSignlessInteger())
465*26be7fe2SLei Zhang     return failure();
466*26be7fe2SLei Zhang   auto storePtr =
467*26be7fe2SLei Zhang       spirv::getElementPtr(*getTypeConverter<SPIRVTypeConverter>(), memrefType,
468*26be7fe2SLei Zhang                            storeOperands.memref(), storeOperands.indices(),
469*26be7fe2SLei Zhang                            storeOp.getLoc(), rewriter);
470*26be7fe2SLei Zhang   rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, storePtr,
471*26be7fe2SLei Zhang                                               storeOperands.value());
472*26be7fe2SLei Zhang   return success();
473*26be7fe2SLei Zhang }
474*26be7fe2SLei Zhang 
475*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
476*26be7fe2SLei Zhang // Pattern population
477*26be7fe2SLei Zhang //===----------------------------------------------------------------------===//
478*26be7fe2SLei Zhang 
479*26be7fe2SLei Zhang namespace mlir {
480*26be7fe2SLei Zhang void populateMemRefToSPIRVPatterns(SPIRVTypeConverter &typeConverter,
481*26be7fe2SLei Zhang                                    RewritePatternSet &patterns) {
482*26be7fe2SLei Zhang   patterns.add<AllocOpPattern, DeallocOpPattern, IntLoadOpPattern,
483*26be7fe2SLei Zhang                IntStoreOpPattern, LoadOpPattern, StoreOpPattern>(
484*26be7fe2SLei Zhang       typeConverter, patterns.getContext());
485*26be7fe2SLei Zhang }
486*26be7fe2SLei Zhang } // namespace mlir
487