From: George Mitenkov Date: Tue, 23 Jun 2020 15:26:44 +0000 (-0400) Subject: [MLIR][SPIRVToLLVM] Implementation of spv.func conversion, and return ops X-Git-Tag: llvmorg-12-init~2219 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a4dc61344f08f7581b8794c5819ffdf9c708ecfe;p=platform%2Fupstream%2Fllvm.git [MLIR][SPIRVToLLVM] Implementation of spv.func conversion, and return ops This patch provides an implementation for `spv.func` conversion. The pattern is populated in a separate method added to the pass. At the moment, the type signature conversion only includes the supported types. The conversion pattern also matches SPIR-V function control attributes to LLVM function attributes. Those are modelled as `passthrough` attributes in LLVM dialect. The following mapping are used: - None: no attributes passed - Inline: `alwaysinline` seems to be the right equivalent (`inlinehint` is semantically weaker in my opinion) - DontInline: `noinline` - Pure and Const: I think those can be modelled as `readonly` and `readnone` attributes respectively. Also, 2 patterns added for return ops conversion (`spv.Return` for void return and `spv.ReturnValue` for a single value return). Differential Revision: https://reviews.llvm.org/D81931 --- diff --git a/mlir/include/mlir/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.h b/mlir/include/mlir/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.h index e82efac..178d27d 100644 --- a/mlir/include/mlir/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.h +++ b/mlir/include/mlir/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.h @@ -37,6 +37,12 @@ void populateSPIRVToLLVMConversionPatterns(MLIRContext *context, LLVMTypeConverter &typeConverter, OwningRewritePatternList &patterns); +/// Populates the given list with patterns for function conversion from SPIR-V +/// to LLVM. +void populateSPIRVToLLVMFunctionConversionPatterns( + MLIRContext *context, LLVMTypeConverter &typeConverter, + OwningRewritePatternList &patterns); + } // namespace mlir #endif // MLIR_CONVERSION_SPIRVTOLLVM_CONVERTSPIRVTOLLVM_H diff --git a/mlir/lib/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.cpp b/mlir/lib/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.cpp index 85f6a11..e32fdc5 100644 --- a/mlir/lib/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.cpp +++ b/mlir/lib/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVM.cpp @@ -21,6 +21,9 @@ #include "mlir/IR/PatternMatch.h" #include "mlir/Support/LogicalResult.h" #include "mlir/Transforms/DialectConversion.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "spirv-to-llvm-pattern" using namespace mlir; @@ -150,6 +153,32 @@ public: } }; +class ReturnPattern : public SPIRVToLLVMConversion { +public: + using SPIRVToLLVMConversion::SPIRVToLLVMConversion; + + LogicalResult + matchAndRewrite(spirv::ReturnOp returnOp, ArrayRef operands, + ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp(returnOp, ArrayRef(), + ArrayRef()); + return success(); + } +}; + +class ReturnValuePattern : public SPIRVToLLVMConversion { +public: + using SPIRVToLLVMConversion::SPIRVToLLVMConversion; + + LogicalResult + matchAndRewrite(spirv::ReturnValueOp returnValueOp, ArrayRef operands, + ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp(returnValueOp, ArrayRef(), + operands); + 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 @@ -191,6 +220,64 @@ public: return success(); } }; + +//===----------------------------------------------------------------------===// +// FuncOp conversion +//===----------------------------------------------------------------------===// + +class FuncConversionPattern : public SPIRVToLLVMConversion { +public: + using SPIRVToLLVMConversion::SPIRVToLLVMConversion; + + LogicalResult + matchAndRewrite(spirv::FuncOp funcOp, ArrayRef operands, + ConversionPatternRewriter &rewriter) const override { + + // Convert function signature. At the moment LLVMType converter is enough + // for currently supported types. + auto funcType = funcOp.getType(); + TypeConverter::SignatureConversion signatureConverter( + funcType.getNumInputs()); + auto llvmType = this->typeConverter.convertFunctionSignature( + funcOp.getType(), /*isVariadic=*/false, signatureConverter); + + // Create a new `LLVMFuncOp` + Location loc = funcOp.getLoc(); + StringRef name = funcOp.getName(); + auto newFuncOp = rewriter.create(loc, name, llvmType); + + // Convert SPIR-V Function Control to equivalent LLVM function attribute + MLIRContext *context = funcOp.getContext(); + switch (funcOp.function_control()) { +#define DISPATCH(functionControl, llvmAttr) \ + case functionControl: \ + newFuncOp.setAttr("passthrough", ArrayAttr::get({llvmAttr}, context)); \ + break; + + DISPATCH(spirv::FunctionControl::Inline, + StringAttr::get("alwaysinline", context)); + DISPATCH(spirv::FunctionControl::DontInline, + StringAttr::get("noinline", context)); + DISPATCH(spirv::FunctionControl::Pure, + StringAttr::get("readonly", context)); + DISPATCH(spirv::FunctionControl::Const, + StringAttr::get("readnone", context)); + +#undef DISPATCH + + // Default: if `spirv::FunctionControl::None`, then no attributes are + // needed. + default: + break; + } + + rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(), + newFuncOp.end()); + rewriter.applySignatureConversion(&newFuncOp.getBody(), signatureConverter); + rewriter.eraseOp(funcOp); + return success(); + } +}; } // namespace //===----------------------------------------------------------------------===// @@ -263,6 +350,14 @@ void mlir::populateSPIRVToLLVMConversionPatterns( // Shift ops ShiftPattern, ShiftPattern, - ShiftPattern>(context, - typeConverter); + ShiftPattern, + + // Return ops + ReturnPattern, ReturnValuePattern>(context, typeConverter); +} + +void mlir::populateSPIRVToLLVMFunctionConversionPatterns( + MLIRContext *context, LLVMTypeConverter &typeConverter, + OwningRewritePatternList &patterns) { + patterns.insert(context, typeConverter); } diff --git a/mlir/lib/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVMPass.cpp b/mlir/lib/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVMPass.cpp index 8f30054..81a3a71 100644 --- a/mlir/lib/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVMPass.cpp +++ b/mlir/lib/Conversion/SPIRVToLLVM/ConvertSPIRVToLLVMPass.cpp @@ -35,6 +35,7 @@ void ConvertSPIRVToLLVMPass::runOnOperation() { OwningRewritePatternList patterns; populateSPIRVToLLVMConversionPatterns(context, converter, patterns); + populateSPIRVToLLVMFunctionConversionPatterns(context, converter, patterns); // Currently pulls in Std to LLVM conversion patterns // that help with testing. This allows to convert diff --git a/mlir/test/Conversion/SPIRVToLLVM/func-to-llvm.mlir b/mlir/test/Conversion/SPIRVToLLVM/func-to-llvm.mlir new file mode 100644 index 0000000..5d2c491 --- /dev/null +++ b/mlir/test/Conversion/SPIRVToLLVM/func-to-llvm.mlir @@ -0,0 +1,62 @@ +// RUN: mlir-opt -convert-spirv-to-llvm %s | FileCheck %s + +//===----------------------------------------------------------------------===// +// spv.Return +//===----------------------------------------------------------------------===// + +func @return() { + // CHECK: llvm.return + spv.Return +} + +//===----------------------------------------------------------------------===// +// spv.ReturnValue +//===----------------------------------------------------------------------===// + +func @return_value(%arg: i32) { + // CHECK: llvm.return %{{.*}} : !llvm.i32 + spv.ReturnValue %arg : i32 +} + +//===----------------------------------------------------------------------===// +// spv.func +//===----------------------------------------------------------------------===// + +// CHECK-LABEL: llvm.func @none() +spv.func @none() -> () "None" { + spv.Return +} + +// CHECK-LABEL: llvm.func @inline() attributes {passthrough = ["alwaysinline"]} +spv.func @inline() -> () "Inline" { + spv.Return +} + +// CHECK-LABEL: llvm.func @dont_inline() attributes {passthrough = ["noinline"]} +spv.func @dont_inline() -> () "DontInline" { + spv.Return +} + +// CHECK-LABEL: llvm.func @pure() attributes {passthrough = ["readonly"]} +spv.func @pure() -> () "Pure" { + spv.Return +} + +// CHECK-LABEL: llvm.func @const() attributes {passthrough = ["readnone"]} +spv.func @const() -> () "Const" { + spv.Return +} + +// CHECK-LABEL: llvm.func @scalar_types(%arg0: !llvm.i32, %arg1: !llvm.i1, %arg2: !llvm.double, %arg3: !llvm.float) +spv.func @scalar_types(%arg0: i32, %arg1: i1, %arg2: f64, %arg3: f32) -> () "None" { + spv.Return +} + +// CHECK-LABEL: llvm.func @vector_types(%arg0: !llvm<"<2 x i64>">, %arg1: !llvm<"<2 x i64>">) -> !llvm<"<2 x i64>"> +spv.func @vector_types(%arg0: vector<2xi64>, %arg1: vector<2xi64>) -> vector<2xi64> "None" { + %0 = spv.IAdd %arg0, %arg1 : vector<2xi64> + spv.ReturnValue %0 : vector<2xi64> +} + + +