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