//===- ConvertSPIRVToLLVM.cpp - SPIR-V dialect to LLVM dialect conversion -===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements patterns to convert SPIR-V dialect to LLVM dialect. // //===----------------------------------------------------------------------===// #include "mlir/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/SPIRV/SPIRVDialect.h" #include "mlir/Dialect/SPIRV/SPIRVOps.h" #include "mlir/Dialect/StandardOps/IR/Ops.h" #include "mlir/IR/Module.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Support/LogicalResult.h" #include "mlir/Transforms/DialectConversion.h" using namespace mlir; //===----------------------------------------------------------------------===// // Utility functions //===----------------------------------------------------------------------===// /// Returns true if the given type is an unsigned integer or vector type static bool isUnsignedIntegerOrVector(Type type) { if (type.isUnsignedInteger()) return true; if (auto vecType = type.dyn_cast()) return vecType.getElementType().isUnsignedInteger(); return false; } /// Returns the bit width of integer, float or vector of float or integer values static unsigned getBitWidth(Type type) { assert((type.isIntOrFloat() || type.isa()) && "bitwidth is not supported for this type"); if (type.isIntOrFloat()) return type.getIntOrFloatBitWidth(); auto vecType = type.dyn_cast(); auto elementType = vecType.getElementType(); assert(elementType.isIntOrFloat() && "only integers and floats have a bitwidth"); return elementType.getIntOrFloatBitWidth(); } //===----------------------------------------------------------------------===// // Operation conversion //===----------------------------------------------------------------------===// namespace { /// Converts SPIR-V operations that have straightforward LLVM equivalent /// into LLVM dialect operations. template class DirectConversionPattern : public SPIRVToLLVMConversion { public: using SPIRVToLLVMConversion::SPIRVToLLVMConversion; LogicalResult matchAndRewrite(SPIRVOp operation, ArrayRef operands, ConversionPatternRewriter &rewriter) const override { auto dstType = this->typeConverter.convertType(operation.getType()); if (!dstType) return failure(); rewriter.template replaceOpWithNewOp(operation, dstType, operands); return success(); } }; /// Converts SPIR-V cast ops that do not have straightforward LLVM /// equivalent in LLVM dialect. template class IndirectCastPattern : public SPIRVToLLVMConversion { public: using SPIRVToLLVMConversion::SPIRVToLLVMConversion; LogicalResult matchAndRewrite(SPIRVOp operation, ArrayRef operands, ConversionPatternRewriter &rewriter) const override { Type fromType = operation.operand().getType(); Type toType = operation.getType(); auto dstType = this->typeConverter.convertType(toType); if (!dstType) return failure(); if (getBitWidth(fromType) < getBitWidth(toType)) { rewriter.template replaceOpWithNewOp(operation, dstType, operands); return success(); } if (getBitWidth(fromType) > getBitWidth(toType)) { rewriter.template replaceOpWithNewOp(operation, dstType, operands); return success(); } return failure(); } }; /// Converts SPIR-V floating-point comparisons to llvm.fcmp "predicate" template class FComparePattern : public SPIRVToLLVMConversion { public: using SPIRVToLLVMConversion::SPIRVToLLVMConversion; LogicalResult matchAndRewrite(SPIRVOp operation, ArrayRef operands, ConversionPatternRewriter &rewriter) const override { auto dstType = this->typeConverter.convertType(operation.getType()); if (!dstType) return failure(); rewriter.template replaceOpWithNewOp( operation, dstType, rewriter.getI64IntegerAttr(static_cast(predicate)), operation.operand1(), operation.operand2()); return success(); } }; /// Converts SPIR-V integer comparisons to llvm.icmp "predicate" template class IComparePattern : public SPIRVToLLVMConversion { public: using SPIRVToLLVMConversion::SPIRVToLLVMConversion; LogicalResult matchAndRewrite(SPIRVOp operation, ArrayRef operands, ConversionPatternRewriter &rewriter) const override { auto dstType = this->typeConverter.convertType(operation.getType()); if (!dstType) return failure(); rewriter.template replaceOpWithNewOp( operation, dstType, rewriter.getI64IntegerAttr(static_cast(predicate)), operation.operand1(), operation.operand2()); return success(); } }; /// Converts SPIR-V shift ops to LLVM shift ops. Since LLVM dialect /// puts a restriction on `Shift` and `Base` to have the same bit width, /// `Shift` is zero or sign extended to match this specification. Cases when /// `Shift` bit width > `Base` bit width are considered to be illegal. template class ShiftPattern : public SPIRVToLLVMConversion { public: using SPIRVToLLVMConversion::SPIRVToLLVMConversion; LogicalResult matchAndRewrite(SPIRVOp operation, ArrayRef operands, ConversionPatternRewriter &rewriter) const override { auto dstType = this->typeConverter.convertType(operation.getType()); if (!dstType) return failure(); Type op1Type = operation.operand1().getType(); Type op2Type = operation.operand2().getType(); if (op1Type == op2Type) { rewriter.template replaceOpWithNewOp(operation, dstType, operands); return success(); } Location loc = operation.getLoc(); Value extended; if (isUnsignedIntegerOrVector(op2Type)) { extended = rewriter.template create(loc, dstType, operation.operand2()); } else { extended = rewriter.template create(loc, dstType, operation.operand2()); } Value result = rewriter.template create( loc, dstType, operation.operand1(), extended); rewriter.replaceOp(operation, result); return success(); } }; } // namespace //===----------------------------------------------------------------------===// // Pattern population //===----------------------------------------------------------------------===// void mlir::populateSPIRVToLLVMConversionPatterns( MLIRContext *context, LLVMTypeConverter &typeConverter, OwningRewritePatternList &patterns) { patterns.insert< // Arithmetic ops DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, // Bitwise ops DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, // Cast ops DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, DirectConversionPattern, IndirectCastPattern, IndirectCastPattern, IndirectCastPattern, // Comparison ops IComparePattern, IComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, FComparePattern, IComparePattern, IComparePattern, IComparePattern, IComparePattern, IComparePattern, IComparePattern, IComparePattern, IComparePattern, // Logical ops DirectConversionPattern, DirectConversionPattern, IComparePattern, IComparePattern, // Shift ops ShiftPattern, ShiftPattern, ShiftPattern>(context, typeConverter); }