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