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" 15*8854b736SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVEnums.h" 1626be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/IR/SPIRVOps.h" 1726be7fe2SLei Zhang #include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h" 1826be7fe2SLei Zhang #include "llvm/Support/Debug.h" 1926be7fe2SLei Zhang 2026be7fe2SLei Zhang #define DEBUG_TYPE "memref-to-spirv-pattern" 2126be7fe2SLei Zhang 2226be7fe2SLei Zhang using namespace mlir; 2326be7fe2SLei Zhang 2426be7fe2SLei Zhang //===----------------------------------------------------------------------===// 2526be7fe2SLei Zhang // Utility functions 2626be7fe2SLei Zhang //===----------------------------------------------------------------------===// 2726be7fe2SLei Zhang 2826be7fe2SLei Zhang /// Returns the offset of the value in `targetBits` representation. 2926be7fe2SLei Zhang /// 3026be7fe2SLei Zhang /// `srcIdx` is an index into a 1-D array with each element having `sourceBits`. 3126be7fe2SLei Zhang /// It's assumed to be non-negative. 3226be7fe2SLei Zhang /// 3326be7fe2SLei Zhang /// When accessing an element in the array treating as having elements of 3426be7fe2SLei Zhang /// `targetBits`, multiple values are loaded in the same time. The method 3526be7fe2SLei Zhang /// returns the offset where the `srcIdx` locates in the value. For example, if 3626be7fe2SLei Zhang /// `sourceBits` equals to 8 and `targetBits` equals to 32, the x-th element is 3726be7fe2SLei Zhang /// located at (x % 4) * 8. Because there are four elements in one i32, and one 3826be7fe2SLei Zhang /// element has 8 bits. 3926be7fe2SLei Zhang static Value getOffsetForBitwidth(Location loc, Value srcIdx, int sourceBits, 4026be7fe2SLei Zhang int targetBits, OpBuilder &builder) { 4126be7fe2SLei Zhang assert(targetBits % sourceBits == 0); 4226be7fe2SLei Zhang IntegerType targetType = builder.getIntegerType(targetBits); 4326be7fe2SLei Zhang IntegerAttr idxAttr = 4426be7fe2SLei Zhang builder.getIntegerAttr(targetType, targetBits / sourceBits); 4526be7fe2SLei Zhang auto idx = builder.create<spirv::ConstantOp>(loc, targetType, idxAttr); 4626be7fe2SLei Zhang IntegerAttr srcBitsAttr = builder.getIntegerAttr(targetType, sourceBits); 4726be7fe2SLei Zhang auto srcBitsValue = 4826be7fe2SLei Zhang builder.create<spirv::ConstantOp>(loc, targetType, srcBitsAttr); 4926be7fe2SLei Zhang auto m = builder.create<spirv::UModOp>(loc, srcIdx, idx); 5026be7fe2SLei Zhang return builder.create<spirv::IMulOp>(loc, targetType, m, srcBitsValue); 5126be7fe2SLei Zhang } 5226be7fe2SLei Zhang 5326be7fe2SLei Zhang /// Returns an adjusted spirv::AccessChainOp. Based on the 5426be7fe2SLei Zhang /// extension/capabilities, certain integer bitwidths `sourceBits` might not be 5526be7fe2SLei Zhang /// supported. During conversion if a memref of an unsupported type is used, 5626be7fe2SLei Zhang /// load/stores to this memref need to be modified to use a supported higher 5726be7fe2SLei Zhang /// bitwidth `targetBits` and extracting the required bits. For an accessing a 5826be7fe2SLei Zhang /// 1D array (spv.array or spv.rt_array), the last index is modified to load the 5926be7fe2SLei Zhang /// bits needed. The extraction of the actual bits needed are handled 6026be7fe2SLei Zhang /// separately. Note that this only works for a 1-D tensor. 6126be7fe2SLei Zhang static Value adjustAccessChainForBitwidth(SPIRVTypeConverter &typeConverter, 6226be7fe2SLei Zhang spirv::AccessChainOp op, 6326be7fe2SLei Zhang int sourceBits, int targetBits, 6426be7fe2SLei Zhang OpBuilder &builder) { 6526be7fe2SLei Zhang assert(targetBits % sourceBits == 0); 6626be7fe2SLei Zhang const auto loc = op.getLoc(); 6726be7fe2SLei Zhang IntegerType targetType = builder.getIntegerType(targetBits); 6826be7fe2SLei Zhang IntegerAttr attr = 6926be7fe2SLei Zhang builder.getIntegerAttr(targetType, targetBits / sourceBits); 7026be7fe2SLei Zhang auto idx = builder.create<spirv::ConstantOp>(loc, targetType, attr); 7126be7fe2SLei Zhang auto lastDim = op->getOperand(op.getNumOperands() - 1); 7226be7fe2SLei Zhang auto indices = llvm::to_vector<4>(op.indices()); 7326be7fe2SLei Zhang // There are two elements if this is a 1-D tensor. 7426be7fe2SLei Zhang assert(indices.size() == 2); 7526be7fe2SLei Zhang indices.back() = builder.create<spirv::SDivOp>(loc, lastDim, idx); 7626be7fe2SLei Zhang Type t = typeConverter.convertType(op.component_ptr().getType()); 7726be7fe2SLei Zhang return builder.create<spirv::AccessChainOp>(loc, t, op.base_ptr(), indices); 7826be7fe2SLei Zhang } 7926be7fe2SLei Zhang 8026be7fe2SLei Zhang /// Returns the shifted `targetBits`-bit value with the given offset. 8126be7fe2SLei Zhang static Value shiftValue(Location loc, Value value, Value offset, Value mask, 8226be7fe2SLei Zhang int targetBits, OpBuilder &builder) { 8326be7fe2SLei Zhang Type targetType = builder.getIntegerType(targetBits); 8426be7fe2SLei Zhang Value result = builder.create<spirv::BitwiseAndOp>(loc, value, mask); 8526be7fe2SLei Zhang return builder.create<spirv::ShiftLeftLogicalOp>(loc, targetType, result, 8626be7fe2SLei Zhang offset); 8726be7fe2SLei Zhang } 8826be7fe2SLei Zhang 89*8854b736SLei Zhang /// Returns true if the allocations of memref `type` generated from `allocOp` 90*8854b736SLei Zhang /// can be lowered to SPIR-V. 91*8854b736SLei Zhang static bool isAllocationSupported(Operation *allocOp, MemRefType type) { 92*8854b736SLei Zhang if (isa<memref::AllocOp, memref::DeallocOp>(allocOp)) { 93*8854b736SLei Zhang if (SPIRVTypeConverter::getMemorySpaceForStorageClass( 94*8854b736SLei Zhang spirv::StorageClass::Workgroup) != type.getMemorySpaceAsInt()) 9526be7fe2SLei Zhang return false; 96*8854b736SLei Zhang } else if (isa<memref::AllocaOp>(allocOp)) { 97*8854b736SLei Zhang if (SPIRVTypeConverter::getMemorySpaceForStorageClass( 98*8854b736SLei Zhang spirv::StorageClass::Function) != type.getMemorySpaceAsInt()) 99*8854b736SLei Zhang return false; 100*8854b736SLei Zhang } else { 101*8854b736SLei Zhang return false; 102*8854b736SLei Zhang } 103*8854b736SLei Zhang 104*8854b736SLei Zhang // Currently only support static shape and int or float or vector of int or 105*8854b736SLei Zhang // float element type. 106*8854b736SLei Zhang if (!type.hasStaticShape()) 107*8854b736SLei Zhang return false; 108*8854b736SLei Zhang 109*8854b736SLei Zhang Type elementType = type.getElementType(); 11026be7fe2SLei Zhang if (auto vecType = elementType.dyn_cast<VectorType>()) 11126be7fe2SLei Zhang elementType = vecType.getElementType(); 11226be7fe2SLei Zhang return elementType.isIntOrFloat(); 11326be7fe2SLei Zhang } 11426be7fe2SLei Zhang 11526be7fe2SLei Zhang /// Returns the scope to use for atomic operations use for emulating store 11626be7fe2SLei Zhang /// operations of unsupported integer bitwidths, based on the memref 11726be7fe2SLei Zhang /// type. Returns None on failure. 118*8854b736SLei Zhang static Optional<spirv::Scope> getAtomicOpScope(MemRefType type) { 11926be7fe2SLei Zhang Optional<spirv::StorageClass> storageClass = 12026be7fe2SLei Zhang SPIRVTypeConverter::getStorageClassForMemorySpace( 121*8854b736SLei Zhang type.getMemorySpaceAsInt()); 12226be7fe2SLei Zhang if (!storageClass) 12326be7fe2SLei Zhang return {}; 12426be7fe2SLei Zhang switch (*storageClass) { 12526be7fe2SLei Zhang case spirv::StorageClass::StorageBuffer: 12626be7fe2SLei Zhang return spirv::Scope::Device; 12726be7fe2SLei Zhang case spirv::StorageClass::Workgroup: 12826be7fe2SLei Zhang return spirv::Scope::Workgroup; 12926be7fe2SLei Zhang default: { 13026be7fe2SLei Zhang } 13126be7fe2SLei Zhang } 13226be7fe2SLei Zhang return {}; 13326be7fe2SLei Zhang } 13426be7fe2SLei Zhang 1350065bd2aSLei Zhang /// Casts the given `srcInt` into a boolean value. 1360065bd2aSLei Zhang static Value castIntNToBool(Location loc, Value srcInt, OpBuilder &builder) { 1370065bd2aSLei Zhang if (srcInt.getType().isInteger(1)) 1380065bd2aSLei Zhang return srcInt; 1390065bd2aSLei Zhang 1400065bd2aSLei Zhang auto one = spirv::ConstantOp::getOne(srcInt.getType(), loc, builder); 1410065bd2aSLei Zhang return builder.create<spirv::IEqualOp>(loc, srcInt, one); 1420065bd2aSLei Zhang } 1430065bd2aSLei Zhang 1449f5300c8SLei Zhang /// Casts the given `srcBool` into an integer of `dstType`. 1459f5300c8SLei Zhang static Value castBoolToIntN(Location loc, Value srcBool, Type dstType, 1469f5300c8SLei Zhang OpBuilder &builder) { 1479f5300c8SLei Zhang assert(srcBool.getType().isInteger(1)); 1489f5300c8SLei Zhang if (dstType.isInteger(1)) 1499f5300c8SLei Zhang return srcBool; 1509f5300c8SLei Zhang Value zero = spirv::ConstantOp::getZero(dstType, loc, builder); 1519f5300c8SLei Zhang Value one = spirv::ConstantOp::getOne(dstType, loc, builder); 1529f5300c8SLei Zhang return builder.create<spirv::SelectOp>(loc, dstType, srcBool, one, zero); 1539f5300c8SLei Zhang } 1549f5300c8SLei Zhang 15526be7fe2SLei Zhang //===----------------------------------------------------------------------===// 15626be7fe2SLei Zhang // Operation conversion 15726be7fe2SLei Zhang //===----------------------------------------------------------------------===// 15826be7fe2SLei Zhang 15926be7fe2SLei Zhang // Note that DRR cannot be used for the patterns in this file: we may need to 16026be7fe2SLei Zhang // convert type along the way, which requires ConversionPattern. DRR generates 16126be7fe2SLei Zhang // normal RewritePattern. 16226be7fe2SLei Zhang 16326be7fe2SLei Zhang namespace { 16426be7fe2SLei Zhang 165*8854b736SLei Zhang /// Converts memref.alloca to SPIR-V Function variables. 166*8854b736SLei Zhang class AllocaOpPattern final : public OpConversionPattern<memref::AllocaOp> { 167*8854b736SLei Zhang public: 168*8854b736SLei Zhang using OpConversionPattern<memref::AllocaOp>::OpConversionPattern; 169*8854b736SLei Zhang 170*8854b736SLei Zhang LogicalResult 171*8854b736SLei Zhang matchAndRewrite(memref::AllocaOp allocaOp, OpAdaptor adaptor, 172*8854b736SLei Zhang ConversionPatternRewriter &rewriter) const override; 173*8854b736SLei Zhang }; 174*8854b736SLei Zhang 17526be7fe2SLei Zhang /// Converts an allocation operation to SPIR-V. Currently only supports lowering 17626be7fe2SLei Zhang /// to Workgroup memory when the size is constant. Note that this pattern needs 17726be7fe2SLei Zhang /// to be applied in a pass that runs at least at spv.module scope since it wil 17826be7fe2SLei Zhang /// ladd global variables into the spv.module. 17926be7fe2SLei Zhang class AllocOpPattern final : public OpConversionPattern<memref::AllocOp> { 18026be7fe2SLei Zhang public: 18126be7fe2SLei Zhang using OpConversionPattern<memref::AllocOp>::OpConversionPattern; 18226be7fe2SLei Zhang 18326be7fe2SLei Zhang LogicalResult 184b54c724bSRiver Riddle matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor, 18526be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override; 18626be7fe2SLei Zhang }; 18726be7fe2SLei Zhang 18826be7fe2SLei Zhang /// Removed a deallocation if it is a supported allocation. Currently only 18926be7fe2SLei Zhang /// removes deallocation if the memory space is workgroup memory. 19026be7fe2SLei Zhang class DeallocOpPattern final : public OpConversionPattern<memref::DeallocOp> { 19126be7fe2SLei Zhang public: 19226be7fe2SLei Zhang using OpConversionPattern<memref::DeallocOp>::OpConversionPattern; 19326be7fe2SLei Zhang 19426be7fe2SLei Zhang LogicalResult 195b54c724bSRiver Riddle matchAndRewrite(memref::DeallocOp operation, OpAdaptor adaptor, 19626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override; 19726be7fe2SLei Zhang }; 19826be7fe2SLei Zhang 19926be7fe2SLei Zhang /// Converts memref.load to spv.Load. 20026be7fe2SLei Zhang class IntLoadOpPattern final : public OpConversionPattern<memref::LoadOp> { 20126be7fe2SLei Zhang public: 20226be7fe2SLei Zhang using OpConversionPattern<memref::LoadOp>::OpConversionPattern; 20326be7fe2SLei Zhang 20426be7fe2SLei Zhang LogicalResult 205b54c724bSRiver Riddle matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor, 20626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override; 20726be7fe2SLei Zhang }; 20826be7fe2SLei Zhang 20926be7fe2SLei Zhang /// Converts memref.load to spv.Load. 21026be7fe2SLei Zhang class LoadOpPattern final : public OpConversionPattern<memref::LoadOp> { 21126be7fe2SLei Zhang public: 21226be7fe2SLei Zhang using OpConversionPattern<memref::LoadOp>::OpConversionPattern; 21326be7fe2SLei Zhang 21426be7fe2SLei Zhang LogicalResult 215b54c724bSRiver Riddle matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor, 21626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override; 21726be7fe2SLei Zhang }; 21826be7fe2SLei Zhang 21926be7fe2SLei Zhang /// Converts memref.store to spv.Store on integers. 22026be7fe2SLei Zhang class IntStoreOpPattern final : public OpConversionPattern<memref::StoreOp> { 22126be7fe2SLei Zhang public: 22226be7fe2SLei Zhang using OpConversionPattern<memref::StoreOp>::OpConversionPattern; 22326be7fe2SLei Zhang 22426be7fe2SLei Zhang LogicalResult 225b54c724bSRiver Riddle matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor, 22626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override; 22726be7fe2SLei Zhang }; 22826be7fe2SLei Zhang 22926be7fe2SLei Zhang /// Converts memref.store to spv.Store. 23026be7fe2SLei Zhang class StoreOpPattern final : public OpConversionPattern<memref::StoreOp> { 23126be7fe2SLei Zhang public: 23226be7fe2SLei Zhang using OpConversionPattern<memref::StoreOp>::OpConversionPattern; 23326be7fe2SLei Zhang 23426be7fe2SLei Zhang LogicalResult 235b54c724bSRiver Riddle matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor, 23626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const override; 23726be7fe2SLei Zhang }; 23826be7fe2SLei Zhang 23926be7fe2SLei Zhang } // namespace 24026be7fe2SLei Zhang 24126be7fe2SLei Zhang //===----------------------------------------------------------------------===// 242*8854b736SLei Zhang // AllocaOp 243*8854b736SLei Zhang //===----------------------------------------------------------------------===// 244*8854b736SLei Zhang 245*8854b736SLei Zhang LogicalResult 246*8854b736SLei Zhang AllocaOpPattern::matchAndRewrite(memref::AllocaOp allocaOp, OpAdaptor adaptor, 247*8854b736SLei Zhang ConversionPatternRewriter &rewriter) const { 248*8854b736SLei Zhang MemRefType allocType = allocaOp.getType(); 249*8854b736SLei Zhang if (!isAllocationSupported(allocaOp, allocType)) 250*8854b736SLei Zhang return rewriter.notifyMatchFailure(allocaOp, "unhandled allocation type"); 251*8854b736SLei Zhang 252*8854b736SLei Zhang // Get the SPIR-V type for the allocation. 253*8854b736SLei Zhang Type spirvType = getTypeConverter()->convertType(allocType); 254*8854b736SLei Zhang rewriter.replaceOpWithNewOp<spirv::VariableOp>(allocaOp, spirvType, 255*8854b736SLei Zhang spirv::StorageClass::Function, 256*8854b736SLei Zhang /*initializer=*/nullptr); 257*8854b736SLei Zhang return success(); 258*8854b736SLei Zhang } 259*8854b736SLei Zhang 260*8854b736SLei Zhang //===----------------------------------------------------------------------===// 26126be7fe2SLei Zhang // AllocOp 26226be7fe2SLei Zhang //===----------------------------------------------------------------------===// 26326be7fe2SLei Zhang 26426be7fe2SLei Zhang LogicalResult 265b54c724bSRiver Riddle AllocOpPattern::matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor, 26626be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 26726be7fe2SLei Zhang MemRefType allocType = operation.getType(); 268*8854b736SLei Zhang if (!isAllocationSupported(operation, allocType)) 269*8854b736SLei Zhang return rewriter.notifyMatchFailure(operation, "unhandled allocation type"); 27026be7fe2SLei Zhang 27126be7fe2SLei Zhang // Get the SPIR-V type for the allocation. 27226be7fe2SLei Zhang Type spirvType = getTypeConverter()->convertType(allocType); 27326be7fe2SLei Zhang 27426be7fe2SLei Zhang // Insert spv.GlobalVariable for this allocation. 27526be7fe2SLei Zhang Operation *parent = 27626be7fe2SLei Zhang SymbolTable::getNearestSymbolTable(operation->getParentOp()); 27726be7fe2SLei Zhang if (!parent) 27826be7fe2SLei Zhang return failure(); 27926be7fe2SLei Zhang Location loc = operation.getLoc(); 28026be7fe2SLei Zhang spirv::GlobalVariableOp varOp; 28126be7fe2SLei Zhang { 28226be7fe2SLei Zhang OpBuilder::InsertionGuard guard(rewriter); 28326be7fe2SLei Zhang Block &entryBlock = *parent->getRegion(0).begin(); 28426be7fe2SLei Zhang rewriter.setInsertionPointToStart(&entryBlock); 28526be7fe2SLei Zhang auto varOps = entryBlock.getOps<spirv::GlobalVariableOp>(); 28626be7fe2SLei Zhang std::string varName = 28726be7fe2SLei Zhang std::string("__workgroup_mem__") + 28826be7fe2SLei Zhang std::to_string(std::distance(varOps.begin(), varOps.end())); 28926be7fe2SLei Zhang varOp = rewriter.create<spirv::GlobalVariableOp>(loc, spirvType, varName, 29026be7fe2SLei Zhang /*initializer=*/nullptr); 29126be7fe2SLei Zhang } 29226be7fe2SLei Zhang 29326be7fe2SLei Zhang // Get pointer to global variable at the current scope. 29426be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::AddressOfOp>(operation, varOp); 29526be7fe2SLei Zhang return success(); 29626be7fe2SLei Zhang } 29726be7fe2SLei Zhang 29826be7fe2SLei Zhang //===----------------------------------------------------------------------===// 29926be7fe2SLei Zhang // DeallocOp 30026be7fe2SLei Zhang //===----------------------------------------------------------------------===// 30126be7fe2SLei Zhang 30226be7fe2SLei Zhang LogicalResult 30326be7fe2SLei Zhang DeallocOpPattern::matchAndRewrite(memref::DeallocOp operation, 304b54c724bSRiver Riddle OpAdaptor adaptor, 30526be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 30626be7fe2SLei Zhang MemRefType deallocType = operation.memref().getType().cast<MemRefType>(); 307*8854b736SLei Zhang if (!isAllocationSupported(operation, deallocType)) 308*8854b736SLei Zhang return rewriter.notifyMatchFailure(operation, "unhandled allocation type"); 30926be7fe2SLei Zhang rewriter.eraseOp(operation); 31026be7fe2SLei Zhang return success(); 31126be7fe2SLei Zhang } 31226be7fe2SLei Zhang 31326be7fe2SLei Zhang //===----------------------------------------------------------------------===// 31426be7fe2SLei Zhang // LoadOp 31526be7fe2SLei Zhang //===----------------------------------------------------------------------===// 31626be7fe2SLei Zhang 31726be7fe2SLei Zhang LogicalResult 318b54c724bSRiver Riddle IntLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor, 31926be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 32026be7fe2SLei Zhang auto loc = loadOp.getLoc(); 32126be7fe2SLei Zhang auto memrefType = loadOp.memref().getType().cast<MemRefType>(); 32226be7fe2SLei Zhang if (!memrefType.getElementType().isSignlessInteger()) 32326be7fe2SLei Zhang return failure(); 32426be7fe2SLei Zhang 32526be7fe2SLei Zhang auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>(); 32626be7fe2SLei Zhang spirv::AccessChainOp accessChainOp = 327b54c724bSRiver Riddle spirv::getElementPtr(typeConverter, memrefType, adaptor.memref(), 328b54c724bSRiver Riddle adaptor.indices(), loc, rewriter); 32926be7fe2SLei Zhang 3301e9799e2SButygin if (!accessChainOp) 3311e9799e2SButygin return failure(); 3321e9799e2SButygin 33326be7fe2SLei Zhang int srcBits = memrefType.getElementType().getIntOrFloatBitWidth(); 33426be7fe2SLei Zhang bool isBool = srcBits == 1; 33526be7fe2SLei Zhang if (isBool) 33626be7fe2SLei Zhang srcBits = typeConverter.getOptions().boolNumBits; 33726be7fe2SLei Zhang Type pointeeType = typeConverter.convertType(memrefType) 33826be7fe2SLei Zhang .cast<spirv::PointerType>() 33926be7fe2SLei Zhang .getPointeeType(); 34026be7fe2SLei Zhang Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0); 34126be7fe2SLei Zhang Type dstType; 34226be7fe2SLei Zhang if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>()) 34326be7fe2SLei Zhang dstType = arrayType.getElementType(); 34426be7fe2SLei Zhang else 34526be7fe2SLei Zhang dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType(); 34626be7fe2SLei Zhang 34726be7fe2SLei Zhang int dstBits = dstType.getIntOrFloatBitWidth(); 34826be7fe2SLei Zhang assert(dstBits % srcBits == 0); 34926be7fe2SLei Zhang 35026be7fe2SLei Zhang // If the rewrited load op has the same bit width, use the loading value 35126be7fe2SLei Zhang // directly. 35226be7fe2SLei Zhang if (srcBits == dstBits) { 3530065bd2aSLei Zhang Value loadVal = 3540065bd2aSLei Zhang rewriter.create<spirv::LoadOp>(loc, accessChainOp.getResult()); 3550065bd2aSLei Zhang if (isBool) 3560065bd2aSLei Zhang loadVal = castIntNToBool(loc, loadVal, rewriter); 3570065bd2aSLei Zhang rewriter.replaceOp(loadOp, loadVal); 35826be7fe2SLei Zhang return success(); 35926be7fe2SLei Zhang } 36026be7fe2SLei Zhang 36126be7fe2SLei Zhang // Assume that getElementPtr() works linearizely. If it's a scalar, the method 36226be7fe2SLei Zhang // still returns a linearized accessing. If the accessing is not linearized, 36326be7fe2SLei Zhang // there will be offset issues. 36426be7fe2SLei Zhang assert(accessChainOp.indices().size() == 2); 36526be7fe2SLei Zhang Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp, 36626be7fe2SLei Zhang srcBits, dstBits, rewriter); 36726be7fe2SLei Zhang Value spvLoadOp = rewriter.create<spirv::LoadOp>( 36826be7fe2SLei Zhang loc, dstType, adjustedPtr, 36926be7fe2SLei Zhang loadOp->getAttrOfType<spirv::MemoryAccessAttr>( 37026be7fe2SLei Zhang spirv::attributeName<spirv::MemoryAccess>()), 37126be7fe2SLei Zhang loadOp->getAttrOfType<IntegerAttr>("alignment")); 37226be7fe2SLei Zhang 37326be7fe2SLei Zhang // Shift the bits to the rightmost. 37426be7fe2SLei Zhang // ____XXXX________ -> ____________XXXX 37526be7fe2SLei Zhang Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1); 37626be7fe2SLei Zhang Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter); 37726be7fe2SLei Zhang Value result = rewriter.create<spirv::ShiftRightArithmeticOp>( 37826be7fe2SLei Zhang loc, spvLoadOp.getType(), spvLoadOp, offset); 37926be7fe2SLei Zhang 38026be7fe2SLei Zhang // Apply the mask to extract corresponding bits. 38126be7fe2SLei Zhang Value mask = rewriter.create<spirv::ConstantOp>( 38226be7fe2SLei Zhang loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1)); 38326be7fe2SLei Zhang result = rewriter.create<spirv::BitwiseAndOp>(loc, dstType, result, mask); 38426be7fe2SLei Zhang 38526be7fe2SLei Zhang // Apply sign extension on the loading value unconditionally. The signedness 38626be7fe2SLei Zhang // semantic is carried in the operator itself, we relies other pattern to 38726be7fe2SLei Zhang // handle the casting. 38826be7fe2SLei Zhang IntegerAttr shiftValueAttr = 38926be7fe2SLei Zhang rewriter.getIntegerAttr(dstType, dstBits - srcBits); 39026be7fe2SLei Zhang Value shiftValue = 39126be7fe2SLei Zhang rewriter.create<spirv::ConstantOp>(loc, dstType, shiftValueAttr); 39226be7fe2SLei Zhang result = rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, result, 39326be7fe2SLei Zhang shiftValue); 39426be7fe2SLei Zhang result = rewriter.create<spirv::ShiftRightArithmeticOp>(loc, dstType, result, 39526be7fe2SLei Zhang shiftValue); 39626be7fe2SLei Zhang 39726be7fe2SLei Zhang if (isBool) { 39826be7fe2SLei Zhang dstType = typeConverter.convertType(loadOp.getType()); 39926be7fe2SLei Zhang mask = spirv::ConstantOp::getOne(result.getType(), loc, rewriter); 4000065bd2aSLei Zhang result = rewriter.create<spirv::IEqualOp>(loc, result, mask); 40126be7fe2SLei Zhang } else if (result.getType().getIntOrFloatBitWidth() != 40226be7fe2SLei Zhang static_cast<unsigned>(dstBits)) { 40326be7fe2SLei Zhang result = rewriter.create<spirv::SConvertOp>(loc, dstType, result); 40426be7fe2SLei Zhang } 40526be7fe2SLei Zhang rewriter.replaceOp(loadOp, result); 40626be7fe2SLei Zhang 40726be7fe2SLei Zhang assert(accessChainOp.use_empty()); 40826be7fe2SLei Zhang rewriter.eraseOp(accessChainOp); 40926be7fe2SLei Zhang 41026be7fe2SLei Zhang return success(); 41126be7fe2SLei Zhang } 41226be7fe2SLei Zhang 41326be7fe2SLei Zhang LogicalResult 414b54c724bSRiver Riddle LoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor, 41526be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 41626be7fe2SLei Zhang auto memrefType = loadOp.memref().getType().cast<MemRefType>(); 41726be7fe2SLei Zhang if (memrefType.getElementType().isSignlessInteger()) 41826be7fe2SLei Zhang return failure(); 41926be7fe2SLei Zhang auto loadPtr = spirv::getElementPtr( 420b54c724bSRiver Riddle *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.memref(), 421b54c724bSRiver Riddle adaptor.indices(), loadOp.getLoc(), rewriter); 4221e9799e2SButygin 4231e9799e2SButygin if (!loadPtr) 4241e9799e2SButygin return failure(); 4251e9799e2SButygin 42626be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, loadPtr); 42726be7fe2SLei Zhang return success(); 42826be7fe2SLei Zhang } 42926be7fe2SLei Zhang 43026be7fe2SLei Zhang LogicalResult 431b54c724bSRiver Riddle IntStoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor, 43226be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 43326be7fe2SLei Zhang auto memrefType = storeOp.memref().getType().cast<MemRefType>(); 43426be7fe2SLei Zhang if (!memrefType.getElementType().isSignlessInteger()) 43526be7fe2SLei Zhang return failure(); 43626be7fe2SLei Zhang 43726be7fe2SLei Zhang auto loc = storeOp.getLoc(); 43826be7fe2SLei Zhang auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>(); 43926be7fe2SLei Zhang spirv::AccessChainOp accessChainOp = 440b54c724bSRiver Riddle spirv::getElementPtr(typeConverter, memrefType, adaptor.memref(), 441b54c724bSRiver Riddle adaptor.indices(), loc, rewriter); 4421e9799e2SButygin 4431e9799e2SButygin if (!accessChainOp) 4441e9799e2SButygin return failure(); 4451e9799e2SButygin 44626be7fe2SLei Zhang int srcBits = memrefType.getElementType().getIntOrFloatBitWidth(); 44726be7fe2SLei Zhang 44826be7fe2SLei Zhang bool isBool = srcBits == 1; 44926be7fe2SLei Zhang if (isBool) 45026be7fe2SLei Zhang srcBits = typeConverter.getOptions().boolNumBits; 4519f5300c8SLei Zhang 45226be7fe2SLei Zhang Type pointeeType = typeConverter.convertType(memrefType) 45326be7fe2SLei Zhang .cast<spirv::PointerType>() 45426be7fe2SLei Zhang .getPointeeType(); 45526be7fe2SLei Zhang Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0); 45626be7fe2SLei Zhang Type dstType; 45726be7fe2SLei Zhang if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>()) 45826be7fe2SLei Zhang dstType = arrayType.getElementType(); 45926be7fe2SLei Zhang else 46026be7fe2SLei Zhang dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType(); 46126be7fe2SLei Zhang 46226be7fe2SLei Zhang int dstBits = dstType.getIntOrFloatBitWidth(); 46326be7fe2SLei Zhang assert(dstBits % srcBits == 0); 46426be7fe2SLei Zhang 46526be7fe2SLei Zhang if (srcBits == dstBits) { 466b54c724bSRiver Riddle Value storeVal = adaptor.value(); 4679f5300c8SLei Zhang if (isBool) 4689f5300c8SLei Zhang storeVal = castBoolToIntN(loc, storeVal, dstType, rewriter); 46926be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::StoreOp>( 4709f5300c8SLei Zhang storeOp, accessChainOp.getResult(), storeVal); 47126be7fe2SLei Zhang return success(); 47226be7fe2SLei Zhang } 47326be7fe2SLei Zhang 47426be7fe2SLei Zhang // Since there are multi threads in the processing, the emulation will be done 47526be7fe2SLei Zhang // with atomic operations. E.g., if the storing value is i8, rewrite the 47626be7fe2SLei Zhang // StoreOp to 47726be7fe2SLei Zhang // 1) load a 32-bit integer 47826be7fe2SLei Zhang // 2) clear 8 bits in the loading value 47926be7fe2SLei Zhang // 3) store 32-bit value back 48026be7fe2SLei Zhang // 4) load a 32-bit integer 48126be7fe2SLei Zhang // 5) modify 8 bits in the loading value 48226be7fe2SLei Zhang // 6) store 32-bit value back 48326be7fe2SLei Zhang // The step 1 to step 3 are done by AtomicAnd as one atomic step, and the step 48426be7fe2SLei Zhang // 4 to step 6 are done by AtomicOr as another atomic step. 48526be7fe2SLei Zhang assert(accessChainOp.indices().size() == 2); 48626be7fe2SLei Zhang Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1); 48726be7fe2SLei Zhang Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter); 48826be7fe2SLei Zhang 48926be7fe2SLei Zhang // Create a mask to clear the destination. E.g., if it is the second i8 in 49026be7fe2SLei Zhang // i32, 0xFFFF00FF is created. 49126be7fe2SLei Zhang Value mask = rewriter.create<spirv::ConstantOp>( 49226be7fe2SLei Zhang loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1)); 49326be7fe2SLei Zhang Value clearBitsMask = 49426be7fe2SLei Zhang rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, mask, offset); 49526be7fe2SLei Zhang clearBitsMask = rewriter.create<spirv::NotOp>(loc, dstType, clearBitsMask); 49626be7fe2SLei Zhang 497b54c724bSRiver Riddle Value storeVal = adaptor.value(); 4989f5300c8SLei Zhang if (isBool) 4999f5300c8SLei Zhang storeVal = castBoolToIntN(loc, storeVal, dstType, rewriter); 50026be7fe2SLei Zhang storeVal = shiftValue(loc, storeVal, offset, mask, dstBits, rewriter); 50126be7fe2SLei Zhang Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp, 50226be7fe2SLei Zhang srcBits, dstBits, rewriter); 50326be7fe2SLei Zhang Optional<spirv::Scope> scope = getAtomicOpScope(memrefType); 50426be7fe2SLei Zhang if (!scope) 50526be7fe2SLei Zhang return failure(); 50626be7fe2SLei Zhang Value result = rewriter.create<spirv::AtomicAndOp>( 50726be7fe2SLei Zhang loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease, 50826be7fe2SLei Zhang clearBitsMask); 50926be7fe2SLei Zhang result = rewriter.create<spirv::AtomicOrOp>( 51026be7fe2SLei Zhang loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease, 51126be7fe2SLei Zhang storeVal); 51226be7fe2SLei Zhang 51326be7fe2SLei Zhang // The AtomicOrOp has no side effect. Since it is already inserted, we can 51426be7fe2SLei Zhang // just remove the original StoreOp. Note that rewriter.replaceOp() 51526be7fe2SLei Zhang // doesn't work because it only accepts that the numbers of result are the 51626be7fe2SLei Zhang // same. 51726be7fe2SLei Zhang rewriter.eraseOp(storeOp); 51826be7fe2SLei Zhang 51926be7fe2SLei Zhang assert(accessChainOp.use_empty()); 52026be7fe2SLei Zhang rewriter.eraseOp(accessChainOp); 52126be7fe2SLei Zhang 52226be7fe2SLei Zhang return success(); 52326be7fe2SLei Zhang } 52426be7fe2SLei Zhang 52526be7fe2SLei Zhang LogicalResult 526b54c724bSRiver Riddle StoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor, 52726be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 52826be7fe2SLei Zhang auto memrefType = storeOp.memref().getType().cast<MemRefType>(); 52926be7fe2SLei Zhang if (memrefType.getElementType().isSignlessInteger()) 53026be7fe2SLei Zhang return failure(); 531b54c724bSRiver Riddle auto storePtr = spirv::getElementPtr( 532b54c724bSRiver Riddle *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.memref(), 533b54c724bSRiver Riddle adaptor.indices(), storeOp.getLoc(), rewriter); 5341e9799e2SButygin 5351e9799e2SButygin if (!storePtr) 5361e9799e2SButygin return failure(); 5371e9799e2SButygin 53826be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, storePtr, 539b54c724bSRiver Riddle adaptor.value()); 54026be7fe2SLei Zhang return success(); 54126be7fe2SLei Zhang } 54226be7fe2SLei Zhang 54326be7fe2SLei Zhang //===----------------------------------------------------------------------===// 54426be7fe2SLei Zhang // Pattern population 54526be7fe2SLei Zhang //===----------------------------------------------------------------------===// 54626be7fe2SLei Zhang 54726be7fe2SLei Zhang namespace mlir { 54826be7fe2SLei Zhang void populateMemRefToSPIRVPatterns(SPIRVTypeConverter &typeConverter, 54926be7fe2SLei Zhang RewritePatternSet &patterns) { 550*8854b736SLei Zhang patterns 551*8854b736SLei Zhang .add<AllocaOpPattern, AllocOpPattern, DeallocOpPattern, IntLoadOpPattern, 55226be7fe2SLei Zhang IntStoreOpPattern, LoadOpPattern, StoreOpPattern>( 55326be7fe2SLei Zhang typeConverter, patterns.getContext()); 55426be7fe2SLei Zhang } 55526be7fe2SLei Zhang } // namespace mlir 556