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