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 1220065bd2aSLei Zhang /// Casts the given `srcInt` into a boolean value. 1230065bd2aSLei Zhang static Value castIntNToBool(Location loc, Value srcInt, OpBuilder &builder) { 1240065bd2aSLei Zhang if (srcInt.getType().isInteger(1)) 1250065bd2aSLei Zhang return srcInt; 1260065bd2aSLei Zhang 1270065bd2aSLei Zhang auto one = spirv::ConstantOp::getOne(srcInt.getType(), loc, builder); 1280065bd2aSLei Zhang return builder.create<spirv::IEqualOp>(loc, srcInt, one); 1290065bd2aSLei Zhang } 1300065bd2aSLei 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 161*b54c724bSRiver Riddle matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor, 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 172*b54c724bSRiver Riddle matchAndRewrite(memref::DeallocOp operation, OpAdaptor adaptor, 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 182*b54c724bSRiver Riddle matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor, 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 192*b54c724bSRiver Riddle matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor, 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 202*b54c724bSRiver Riddle matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor, 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 212*b54c724bSRiver Riddle matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor, 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 223*b54c724bSRiver Riddle AllocOpPattern::matchAndRewrite(memref::AllocOp operation, OpAdaptor adaptor, 22426be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 22526be7fe2SLei Zhang MemRefType allocType = operation.getType(); 22626be7fe2SLei Zhang if (!isAllocationSupported(allocType)) 22726be7fe2SLei Zhang return operation.emitError("unhandled allocation type"); 22826be7fe2SLei Zhang 22926be7fe2SLei Zhang // Get the SPIR-V type for the allocation. 23026be7fe2SLei Zhang Type spirvType = getTypeConverter()->convertType(allocType); 23126be7fe2SLei Zhang 23226be7fe2SLei Zhang // Insert spv.GlobalVariable for this allocation. 23326be7fe2SLei Zhang Operation *parent = 23426be7fe2SLei Zhang SymbolTable::getNearestSymbolTable(operation->getParentOp()); 23526be7fe2SLei Zhang if (!parent) 23626be7fe2SLei Zhang return failure(); 23726be7fe2SLei Zhang Location loc = operation.getLoc(); 23826be7fe2SLei Zhang spirv::GlobalVariableOp varOp; 23926be7fe2SLei Zhang { 24026be7fe2SLei Zhang OpBuilder::InsertionGuard guard(rewriter); 24126be7fe2SLei Zhang Block &entryBlock = *parent->getRegion(0).begin(); 24226be7fe2SLei Zhang rewriter.setInsertionPointToStart(&entryBlock); 24326be7fe2SLei Zhang auto varOps = entryBlock.getOps<spirv::GlobalVariableOp>(); 24426be7fe2SLei Zhang std::string varName = 24526be7fe2SLei Zhang std::string("__workgroup_mem__") + 24626be7fe2SLei Zhang std::to_string(std::distance(varOps.begin(), varOps.end())); 24726be7fe2SLei Zhang varOp = rewriter.create<spirv::GlobalVariableOp>(loc, spirvType, varName, 24826be7fe2SLei Zhang /*initializer=*/nullptr); 24926be7fe2SLei Zhang } 25026be7fe2SLei Zhang 25126be7fe2SLei Zhang // Get pointer to global variable at the current scope. 25226be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::AddressOfOp>(operation, varOp); 25326be7fe2SLei Zhang return success(); 25426be7fe2SLei Zhang } 25526be7fe2SLei Zhang 25626be7fe2SLei Zhang //===----------------------------------------------------------------------===// 25726be7fe2SLei Zhang // DeallocOp 25826be7fe2SLei Zhang //===----------------------------------------------------------------------===// 25926be7fe2SLei Zhang 26026be7fe2SLei Zhang LogicalResult 26126be7fe2SLei Zhang DeallocOpPattern::matchAndRewrite(memref::DeallocOp operation, 262*b54c724bSRiver Riddle OpAdaptor adaptor, 26326be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 26426be7fe2SLei Zhang MemRefType deallocType = operation.memref().getType().cast<MemRefType>(); 26526be7fe2SLei Zhang if (!isAllocationSupported(deallocType)) 26626be7fe2SLei Zhang return operation.emitError("unhandled deallocation type"); 26726be7fe2SLei Zhang rewriter.eraseOp(operation); 26826be7fe2SLei Zhang return success(); 26926be7fe2SLei Zhang } 27026be7fe2SLei Zhang 27126be7fe2SLei Zhang //===----------------------------------------------------------------------===// 27226be7fe2SLei Zhang // LoadOp 27326be7fe2SLei Zhang //===----------------------------------------------------------------------===// 27426be7fe2SLei Zhang 27526be7fe2SLei Zhang LogicalResult 276*b54c724bSRiver Riddle IntLoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor, 27726be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 27826be7fe2SLei Zhang auto loc = loadOp.getLoc(); 27926be7fe2SLei Zhang auto memrefType = loadOp.memref().getType().cast<MemRefType>(); 28026be7fe2SLei Zhang if (!memrefType.getElementType().isSignlessInteger()) 28126be7fe2SLei Zhang return failure(); 28226be7fe2SLei Zhang 28326be7fe2SLei Zhang auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>(); 28426be7fe2SLei Zhang spirv::AccessChainOp accessChainOp = 285*b54c724bSRiver Riddle spirv::getElementPtr(typeConverter, memrefType, adaptor.memref(), 286*b54c724bSRiver Riddle adaptor.indices(), loc, rewriter); 28726be7fe2SLei Zhang 2881e9799e2SButygin if (!accessChainOp) 2891e9799e2SButygin return failure(); 2901e9799e2SButygin 29126be7fe2SLei Zhang int srcBits = memrefType.getElementType().getIntOrFloatBitWidth(); 29226be7fe2SLei Zhang bool isBool = srcBits == 1; 29326be7fe2SLei Zhang if (isBool) 29426be7fe2SLei Zhang srcBits = typeConverter.getOptions().boolNumBits; 29526be7fe2SLei Zhang Type pointeeType = typeConverter.convertType(memrefType) 29626be7fe2SLei Zhang .cast<spirv::PointerType>() 29726be7fe2SLei Zhang .getPointeeType(); 29826be7fe2SLei Zhang Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0); 29926be7fe2SLei Zhang Type dstType; 30026be7fe2SLei Zhang if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>()) 30126be7fe2SLei Zhang dstType = arrayType.getElementType(); 30226be7fe2SLei Zhang else 30326be7fe2SLei Zhang dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType(); 30426be7fe2SLei Zhang 30526be7fe2SLei Zhang int dstBits = dstType.getIntOrFloatBitWidth(); 30626be7fe2SLei Zhang assert(dstBits % srcBits == 0); 30726be7fe2SLei Zhang 30826be7fe2SLei Zhang // If the rewrited load op has the same bit width, use the loading value 30926be7fe2SLei Zhang // directly. 31026be7fe2SLei Zhang if (srcBits == dstBits) { 3110065bd2aSLei Zhang Value loadVal = 3120065bd2aSLei Zhang rewriter.create<spirv::LoadOp>(loc, accessChainOp.getResult()); 3130065bd2aSLei Zhang if (isBool) 3140065bd2aSLei Zhang loadVal = castIntNToBool(loc, loadVal, rewriter); 3150065bd2aSLei Zhang rewriter.replaceOp(loadOp, loadVal); 31626be7fe2SLei Zhang return success(); 31726be7fe2SLei Zhang } 31826be7fe2SLei Zhang 31926be7fe2SLei Zhang // Assume that getElementPtr() works linearizely. If it's a scalar, the method 32026be7fe2SLei Zhang // still returns a linearized accessing. If the accessing is not linearized, 32126be7fe2SLei Zhang // there will be offset issues. 32226be7fe2SLei Zhang assert(accessChainOp.indices().size() == 2); 32326be7fe2SLei Zhang Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp, 32426be7fe2SLei Zhang srcBits, dstBits, rewriter); 32526be7fe2SLei Zhang Value spvLoadOp = rewriter.create<spirv::LoadOp>( 32626be7fe2SLei Zhang loc, dstType, adjustedPtr, 32726be7fe2SLei Zhang loadOp->getAttrOfType<spirv::MemoryAccessAttr>( 32826be7fe2SLei Zhang spirv::attributeName<spirv::MemoryAccess>()), 32926be7fe2SLei Zhang loadOp->getAttrOfType<IntegerAttr>("alignment")); 33026be7fe2SLei Zhang 33126be7fe2SLei Zhang // Shift the bits to the rightmost. 33226be7fe2SLei Zhang // ____XXXX________ -> ____________XXXX 33326be7fe2SLei Zhang Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1); 33426be7fe2SLei Zhang Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter); 33526be7fe2SLei Zhang Value result = rewriter.create<spirv::ShiftRightArithmeticOp>( 33626be7fe2SLei Zhang loc, spvLoadOp.getType(), spvLoadOp, offset); 33726be7fe2SLei Zhang 33826be7fe2SLei Zhang // Apply the mask to extract corresponding bits. 33926be7fe2SLei Zhang Value mask = rewriter.create<spirv::ConstantOp>( 34026be7fe2SLei Zhang loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1)); 34126be7fe2SLei Zhang result = rewriter.create<spirv::BitwiseAndOp>(loc, dstType, result, mask); 34226be7fe2SLei Zhang 34326be7fe2SLei Zhang // Apply sign extension on the loading value unconditionally. The signedness 34426be7fe2SLei Zhang // semantic is carried in the operator itself, we relies other pattern to 34526be7fe2SLei Zhang // handle the casting. 34626be7fe2SLei Zhang IntegerAttr shiftValueAttr = 34726be7fe2SLei Zhang rewriter.getIntegerAttr(dstType, dstBits - srcBits); 34826be7fe2SLei Zhang Value shiftValue = 34926be7fe2SLei Zhang rewriter.create<spirv::ConstantOp>(loc, dstType, shiftValueAttr); 35026be7fe2SLei Zhang result = rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, result, 35126be7fe2SLei Zhang shiftValue); 35226be7fe2SLei Zhang result = rewriter.create<spirv::ShiftRightArithmeticOp>(loc, dstType, result, 35326be7fe2SLei Zhang shiftValue); 35426be7fe2SLei Zhang 35526be7fe2SLei Zhang if (isBool) { 35626be7fe2SLei Zhang dstType = typeConverter.convertType(loadOp.getType()); 35726be7fe2SLei Zhang mask = spirv::ConstantOp::getOne(result.getType(), loc, rewriter); 3580065bd2aSLei Zhang result = rewriter.create<spirv::IEqualOp>(loc, result, mask); 35926be7fe2SLei Zhang } else if (result.getType().getIntOrFloatBitWidth() != 36026be7fe2SLei Zhang static_cast<unsigned>(dstBits)) { 36126be7fe2SLei Zhang result = rewriter.create<spirv::SConvertOp>(loc, dstType, result); 36226be7fe2SLei Zhang } 36326be7fe2SLei Zhang rewriter.replaceOp(loadOp, result); 36426be7fe2SLei Zhang 36526be7fe2SLei Zhang assert(accessChainOp.use_empty()); 36626be7fe2SLei Zhang rewriter.eraseOp(accessChainOp); 36726be7fe2SLei Zhang 36826be7fe2SLei Zhang return success(); 36926be7fe2SLei Zhang } 37026be7fe2SLei Zhang 37126be7fe2SLei Zhang LogicalResult 372*b54c724bSRiver Riddle LoadOpPattern::matchAndRewrite(memref::LoadOp loadOp, OpAdaptor adaptor, 37326be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 37426be7fe2SLei Zhang auto memrefType = loadOp.memref().getType().cast<MemRefType>(); 37526be7fe2SLei Zhang if (memrefType.getElementType().isSignlessInteger()) 37626be7fe2SLei Zhang return failure(); 37726be7fe2SLei Zhang auto loadPtr = spirv::getElementPtr( 378*b54c724bSRiver Riddle *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.memref(), 379*b54c724bSRiver Riddle adaptor.indices(), loadOp.getLoc(), rewriter); 3801e9799e2SButygin 3811e9799e2SButygin if (!loadPtr) 3821e9799e2SButygin return failure(); 3831e9799e2SButygin 38426be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, loadPtr); 38526be7fe2SLei Zhang return success(); 38626be7fe2SLei Zhang } 38726be7fe2SLei Zhang 38826be7fe2SLei Zhang LogicalResult 389*b54c724bSRiver Riddle IntStoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor, 39026be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 39126be7fe2SLei Zhang auto memrefType = storeOp.memref().getType().cast<MemRefType>(); 39226be7fe2SLei Zhang if (!memrefType.getElementType().isSignlessInteger()) 39326be7fe2SLei Zhang return failure(); 39426be7fe2SLei Zhang 39526be7fe2SLei Zhang auto loc = storeOp.getLoc(); 39626be7fe2SLei Zhang auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>(); 39726be7fe2SLei Zhang spirv::AccessChainOp accessChainOp = 398*b54c724bSRiver Riddle spirv::getElementPtr(typeConverter, memrefType, adaptor.memref(), 399*b54c724bSRiver Riddle adaptor.indices(), loc, rewriter); 4001e9799e2SButygin 4011e9799e2SButygin if (!accessChainOp) 4021e9799e2SButygin return failure(); 4031e9799e2SButygin 40426be7fe2SLei Zhang int srcBits = memrefType.getElementType().getIntOrFloatBitWidth(); 40526be7fe2SLei Zhang 40626be7fe2SLei Zhang bool isBool = srcBits == 1; 40726be7fe2SLei Zhang if (isBool) 40826be7fe2SLei Zhang srcBits = typeConverter.getOptions().boolNumBits; 4099f5300c8SLei Zhang 41026be7fe2SLei Zhang Type pointeeType = typeConverter.convertType(memrefType) 41126be7fe2SLei Zhang .cast<spirv::PointerType>() 41226be7fe2SLei Zhang .getPointeeType(); 41326be7fe2SLei Zhang Type structElemType = pointeeType.cast<spirv::StructType>().getElementType(0); 41426be7fe2SLei Zhang Type dstType; 41526be7fe2SLei Zhang if (auto arrayType = structElemType.dyn_cast<spirv::ArrayType>()) 41626be7fe2SLei Zhang dstType = arrayType.getElementType(); 41726be7fe2SLei Zhang else 41826be7fe2SLei Zhang dstType = structElemType.cast<spirv::RuntimeArrayType>().getElementType(); 41926be7fe2SLei Zhang 42026be7fe2SLei Zhang int dstBits = dstType.getIntOrFloatBitWidth(); 42126be7fe2SLei Zhang assert(dstBits % srcBits == 0); 42226be7fe2SLei Zhang 42326be7fe2SLei Zhang if (srcBits == dstBits) { 424*b54c724bSRiver Riddle Value storeVal = adaptor.value(); 4259f5300c8SLei Zhang if (isBool) 4269f5300c8SLei Zhang storeVal = castBoolToIntN(loc, storeVal, dstType, rewriter); 42726be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::StoreOp>( 4289f5300c8SLei Zhang storeOp, accessChainOp.getResult(), storeVal); 42926be7fe2SLei Zhang return success(); 43026be7fe2SLei Zhang } 43126be7fe2SLei Zhang 43226be7fe2SLei Zhang // Since there are multi threads in the processing, the emulation will be done 43326be7fe2SLei Zhang // with atomic operations. E.g., if the storing value is i8, rewrite the 43426be7fe2SLei Zhang // StoreOp to 43526be7fe2SLei Zhang // 1) load a 32-bit integer 43626be7fe2SLei Zhang // 2) clear 8 bits in the loading value 43726be7fe2SLei Zhang // 3) store 32-bit value back 43826be7fe2SLei Zhang // 4) load a 32-bit integer 43926be7fe2SLei Zhang // 5) modify 8 bits in the loading value 44026be7fe2SLei Zhang // 6) store 32-bit value back 44126be7fe2SLei Zhang // The step 1 to step 3 are done by AtomicAnd as one atomic step, and the step 44226be7fe2SLei Zhang // 4 to step 6 are done by AtomicOr as another atomic step. 44326be7fe2SLei Zhang assert(accessChainOp.indices().size() == 2); 44426be7fe2SLei Zhang Value lastDim = accessChainOp->getOperand(accessChainOp.getNumOperands() - 1); 44526be7fe2SLei Zhang Value offset = getOffsetForBitwidth(loc, lastDim, srcBits, dstBits, rewriter); 44626be7fe2SLei Zhang 44726be7fe2SLei Zhang // Create a mask to clear the destination. E.g., if it is the second i8 in 44826be7fe2SLei Zhang // i32, 0xFFFF00FF is created. 44926be7fe2SLei Zhang Value mask = rewriter.create<spirv::ConstantOp>( 45026be7fe2SLei Zhang loc, dstType, rewriter.getIntegerAttr(dstType, (1 << srcBits) - 1)); 45126be7fe2SLei Zhang Value clearBitsMask = 45226be7fe2SLei Zhang rewriter.create<spirv::ShiftLeftLogicalOp>(loc, dstType, mask, offset); 45326be7fe2SLei Zhang clearBitsMask = rewriter.create<spirv::NotOp>(loc, dstType, clearBitsMask); 45426be7fe2SLei Zhang 455*b54c724bSRiver Riddle Value storeVal = adaptor.value(); 4569f5300c8SLei Zhang if (isBool) 4579f5300c8SLei Zhang storeVal = castBoolToIntN(loc, storeVal, dstType, rewriter); 45826be7fe2SLei Zhang storeVal = shiftValue(loc, storeVal, offset, mask, dstBits, rewriter); 45926be7fe2SLei Zhang Value adjustedPtr = adjustAccessChainForBitwidth(typeConverter, accessChainOp, 46026be7fe2SLei Zhang srcBits, dstBits, rewriter); 46126be7fe2SLei Zhang Optional<spirv::Scope> scope = getAtomicOpScope(memrefType); 46226be7fe2SLei Zhang if (!scope) 46326be7fe2SLei Zhang return failure(); 46426be7fe2SLei Zhang Value result = rewriter.create<spirv::AtomicAndOp>( 46526be7fe2SLei Zhang loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease, 46626be7fe2SLei Zhang clearBitsMask); 46726be7fe2SLei Zhang result = rewriter.create<spirv::AtomicOrOp>( 46826be7fe2SLei Zhang loc, dstType, adjustedPtr, *scope, spirv::MemorySemantics::AcquireRelease, 46926be7fe2SLei Zhang storeVal); 47026be7fe2SLei Zhang 47126be7fe2SLei Zhang // The AtomicOrOp has no side effect. Since it is already inserted, we can 47226be7fe2SLei Zhang // just remove the original StoreOp. Note that rewriter.replaceOp() 47326be7fe2SLei Zhang // doesn't work because it only accepts that the numbers of result are the 47426be7fe2SLei Zhang // same. 47526be7fe2SLei Zhang rewriter.eraseOp(storeOp); 47626be7fe2SLei Zhang 47726be7fe2SLei Zhang assert(accessChainOp.use_empty()); 47826be7fe2SLei Zhang rewriter.eraseOp(accessChainOp); 47926be7fe2SLei Zhang 48026be7fe2SLei Zhang return success(); 48126be7fe2SLei Zhang } 48226be7fe2SLei Zhang 48326be7fe2SLei Zhang LogicalResult 484*b54c724bSRiver Riddle StoreOpPattern::matchAndRewrite(memref::StoreOp storeOp, OpAdaptor adaptor, 48526be7fe2SLei Zhang ConversionPatternRewriter &rewriter) const { 48626be7fe2SLei Zhang auto memrefType = storeOp.memref().getType().cast<MemRefType>(); 48726be7fe2SLei Zhang if (memrefType.getElementType().isSignlessInteger()) 48826be7fe2SLei Zhang return failure(); 489*b54c724bSRiver Riddle auto storePtr = spirv::getElementPtr( 490*b54c724bSRiver Riddle *getTypeConverter<SPIRVTypeConverter>(), memrefType, adaptor.memref(), 491*b54c724bSRiver Riddle adaptor.indices(), storeOp.getLoc(), rewriter); 4921e9799e2SButygin 4931e9799e2SButygin if (!storePtr) 4941e9799e2SButygin return failure(); 4951e9799e2SButygin 49626be7fe2SLei Zhang rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, storePtr, 497*b54c724bSRiver Riddle adaptor.value()); 49826be7fe2SLei Zhang return success(); 49926be7fe2SLei Zhang } 50026be7fe2SLei Zhang 50126be7fe2SLei Zhang //===----------------------------------------------------------------------===// 50226be7fe2SLei Zhang // Pattern population 50326be7fe2SLei Zhang //===----------------------------------------------------------------------===// 50426be7fe2SLei Zhang 50526be7fe2SLei Zhang namespace mlir { 50626be7fe2SLei Zhang void populateMemRefToSPIRVPatterns(SPIRVTypeConverter &typeConverter, 50726be7fe2SLei Zhang RewritePatternSet &patterns) { 50826be7fe2SLei Zhang patterns.add<AllocOpPattern, DeallocOpPattern, IntLoadOpPattern, 50926be7fe2SLei Zhang IntStoreOpPattern, LoadOpPattern, StoreOpPattern>( 51026be7fe2SLei Zhang typeConverter, patterns.getContext()); 51126be7fe2SLei Zhang } 51226be7fe2SLei Zhang } // namespace mlir 513