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