Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 1 | //===- OpToFuncCallLowering.h - GPU ops lowering to custom calls *- C++ -*-===// |
| 2 | // |
Mehdi Amini | 3085710 | 2020-01-26 03:58:30 | [diff] [blame] | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
Mehdi Amini | 56222a0 | 2019-12-23 17:35:36 | [diff] [blame] | 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 6 | // |
Mehdi Amini | 56222a0 | 2019-12-23 17:35:36 | [diff] [blame] | 7 | //===----------------------------------------------------------------------===// |
Alexander Belyaev | 780a108 | 2019-10-26 15:20:59 | [diff] [blame] | 8 | #ifndef MLIR_CONVERSION_GPUCOMMON_OPTOFUNCCALLLOWERING_H_ |
| 9 | #define MLIR_CONVERSION_GPUCOMMON_OPTOFUNCCALLLOWERING_H_ |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 10 | |
Alex Zinenko | 75e5f0a | 2021-07-08 16:35:18 | [diff] [blame] | 11 | #include "mlir/Conversion/LLVMCommon/Pattern.h" |
River Riddle | 23aa5a7 | 2022-02-26 22:49:54 | [diff] [blame^] | 12 | #include "mlir/Dialect/Func/IR/FuncOps.h" |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 13 | #include "mlir/Dialect/GPU/GPUDialect.h" |
| 14 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 15 | #include "mlir/IR/Builders.h" |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 16 | |
| 17 | namespace mlir { |
| 18 | |
Alexander Belyaev | 780a108 | 2019-10-26 15:20:59 | [diff] [blame] | 19 | /// Rewriting that replace SourceOp with a CallOp to `f32Func` or `f64Func` |
| 20 | /// depending on the element type that Op operates upon. The function |
| 21 | /// declaration is added in case it was not added before. |
| 22 | /// |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 23 | /// If the input values are of f16 type, the value is first casted to f32, the |
| 24 | /// function called and then the result casted back. |
| 25 | /// |
Alexander Belyaev | 780a108 | 2019-10-26 15:20:59 | [diff] [blame] | 26 | /// Example with NVVM: |
River Riddle | 23aa5a7 | 2022-02-26 22:49:54 | [diff] [blame^] | 27 | /// %exp_f32 = math.exp %arg_f32 : f32 |
Alexander Belyaev | 780a108 | 2019-10-26 15:20:59 | [diff] [blame] | 28 | /// |
| 29 | /// will be transformed into |
Alex Zinenko | dd5165a | 2021-01-06 15:21:08 | [diff] [blame] | 30 | /// llvm.call @__nv_expf(%arg_f32) : (f32) -> f32 |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 31 | template <typename SourceOp> |
Rahul Joshi | 563879b | 2020-12-10 02:18:35 | [diff] [blame] | 32 | struct OpToFuncCallLowering : public ConvertOpToLLVMPattern<SourceOp> { |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 33 | public: |
Mehdi Amini | cac7aab | 2022-01-14 01:36:04 | [diff] [blame] | 34 | explicit OpToFuncCallLowering(LLVMTypeConverter &lowering, StringRef f32Func, |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 35 | StringRef f64Func) |
Mehdi Amini | cac7aab | 2022-01-14 01:36:04 | [diff] [blame] | 36 | : ConvertOpToLLVMPattern<SourceOp>(lowering), f32Func(f32Func), |
Rahul Joshi | 563879b | 2020-12-10 02:18:35 | [diff] [blame] | 37 | f64Func(f64Func) {} |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 38 | |
River Riddle | 3145427 | 2020-03-18 03:07:55 | [diff] [blame] | 39 | LogicalResult |
River Riddle | ef97633 | 2021-09-24 17:51:20 | [diff] [blame] | 40 | matchAndRewrite(SourceOp op, typename SourceOp::Adaptor adaptor, |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 41 | ConversionPatternRewriter &rewriter) const override { |
| 42 | using LLVM::LLVMFuncOp; |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 43 | |
| 44 | static_assert( |
| 45 | std::is_base_of<OpTrait::OneResult<SourceOp>, SourceOp>::value, |
| 46 | "expected single result op"); |
| 47 | |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 48 | static_assert(std::is_base_of<OpTrait::SameOperandsAndResultType<SourceOp>, |
| 49 | SourceOp>::value, |
| 50 | "expected op with same operand and result types"); |
| 51 | |
| 52 | SmallVector<Value, 1> castedOperands; |
River Riddle | ef97633 | 2021-09-24 17:51:20 | [diff] [blame] | 53 | for (Value operand : adaptor.getOperands()) |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 54 | castedOperands.push_back(maybeCast(operand, rewriter)); |
| 55 | |
Alex Zinenko | c69c9e0 | 2021-01-05 15:22:53 | [diff] [blame] | 56 | Type resultType = castedOperands.front().getType(); |
| 57 | Type funcType = getFunctionType(resultType, castedOperands); |
Alex Zinenko | 8de43b9 | 2020-12-22 10:22:21 | [diff] [blame] | 58 | StringRef funcName = getFunctionName( |
| 59 | funcType.cast<LLVM::LLVMFunctionType>().getReturnType()); |
Alexander Belyaev | 780a108 | 2019-10-26 15:20:59 | [diff] [blame] | 60 | if (funcName.empty()) |
River Riddle | 3145427 | 2020-03-18 03:07:55 | [diff] [blame] | 61 | return failure(); |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 62 | |
| 63 | LLVMFuncOp funcOp = appendOrGetFuncOp(funcName, funcType, op); |
| 64 | auto callOp = rewriter.create<LLVM::CallOp>( |
Chris Lattner | faf1c22 | 2021-08-30 16:31:48 | [diff] [blame] | 65 | op->getLoc(), resultType, SymbolRefAttr::get(funcOp), castedOperands); |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 66 | |
River Riddle | ef97633 | 2021-09-24 17:51:20 | [diff] [blame] | 67 | if (resultType == adaptor.getOperands().front().getType()) { |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 68 | rewriter.replaceOp(op, {callOp.getResult(0)}); |
| 69 | return success(); |
| 70 | } |
| 71 | |
| 72 | Value truncated = rewriter.create<LLVM::FPTruncOp>( |
River Riddle | ef97633 | 2021-09-24 17:51:20 | [diff] [blame] | 73 | op->getLoc(), adaptor.getOperands().front().getType(), |
| 74 | callOp.getResult(0)); |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 75 | rewriter.replaceOp(op, {truncated}); |
River Riddle | 3145427 | 2020-03-18 03:07:55 | [diff] [blame] | 76 | return success(); |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 77 | } |
| 78 | |
| 79 | private: |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 80 | Value maybeCast(Value operand, PatternRewriter &rewriter) const { |
Alex Zinenko | c69c9e0 | 2021-01-05 15:22:53 | [diff] [blame] | 81 | Type type = operand.getType(); |
Alex Zinenko | dd5165a | 2021-01-06 15:21:08 | [diff] [blame] | 82 | if (!type.isa<Float16Type>()) |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 83 | return operand; |
| 84 | |
| 85 | return rewriter.create<LLVM::FPExtOp>( |
Alex Zinenko | dd5165a | 2021-01-06 15:21:08 | [diff] [blame] | 86 | operand.getLoc(), Float32Type::get(rewriter.getContext()), operand); |
Stephan Herhut | 2c8afe1 | 2020-06-09 15:20:53 | [diff] [blame] | 87 | } |
| 88 | |
River Riddle | ef97633 | 2021-09-24 17:51:20 | [diff] [blame] | 89 | Type getFunctionType(Type resultType, ValueRange operands) const { |
| 90 | SmallVector<Type> operandTypes(operands.getTypes()); |
Alex Zinenko | 7ed9cfc | 2020-12-22 10:22:56 | [diff] [blame] | 91 | return LLVM::LLVMFunctionType::get(resultType, operandTypes); |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 92 | } |
| 93 | |
Alex Zinenko | c69c9e0 | 2021-01-05 15:22:53 | [diff] [blame] | 94 | StringRef getFunctionName(Type type) const { |
Alex Zinenko | dd5165a | 2021-01-06 15:21:08 | [diff] [blame] | 95 | if (type.isa<Float32Type>()) |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 96 | return f32Func; |
Alex Zinenko | dd5165a | 2021-01-06 15:21:08 | [diff] [blame] | 97 | if (type.isa<Float64Type>()) |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 98 | return f64Func; |
| 99 | return ""; |
| 100 | } |
| 101 | |
Alex Zinenko | c69c9e0 | 2021-01-05 15:22:53 | [diff] [blame] | 102 | LLVM::LLVMFuncOp appendOrGetFuncOp(StringRef funcName, Type funcType, |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 103 | Operation *op) const { |
| 104 | using LLVM::LLVMFuncOp; |
| 105 | |
Chris Lattner | 41d4aa7 | 2021-08-29 21:22:24 | [diff] [blame] | 106 | auto funcAttr = StringAttr::get(op->getContext(), funcName); |
| 107 | Operation *funcOp = SymbolTable::lookupNearestSymbolFrom(op, funcAttr); |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 108 | if (funcOp) |
River Riddle | 4562e38 | 2019-12-18 17:28:48 | [diff] [blame] | 109 | return cast<LLVMFuncOp>(*funcOp); |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 110 | |
| 111 | mlir::OpBuilder b(op->getParentOfType<LLVMFuncOp>()); |
Alex Zinenko | fdbb99c | 2019-12-03 08:26:13 | [diff] [blame] | 112 | return b.create<LLVMFuncOp>(op->getLoc(), funcName, funcType); |
Alexander Belyaev | d2ce435 | 2019-10-24 08:41:25 | [diff] [blame] | 113 | } |
| 114 | |
| 115 | const std::string f32Func; |
| 116 | const std::string f64Func; |
| 117 | }; |
| 118 | |
| 119 | } // namespace mlir |
| 120 | |
Alexander Belyaev | 780a108 | 2019-10-26 15:20:59 | [diff] [blame] | 121 | #endif // MLIR_CONVERSION_GPUCOMMON_OPTOFUNCCALLLOWERING_H_ |