MLIR 24.0.0git
OpToFuncCallLowering.h
Go to the documentation of this file.
1//===- OpToFuncCallLowering.h - GPU ops lowering to custom calls *- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#ifndef MLIR_CONVERSION_GPUCOMMON_OPTOFUNCCALLLOWERING_H_
9#define MLIR_CONVERSION_GPUCOMMON_OPTOFUNCCALLLOWERING_H_
10
15#include "mlir/IR/Builders.h"
16
17namespace mlir {
18
19namespace {
20/// Detection trait tor the `getFastmath` instance method.
21template <typename T>
22using has_get_fastmath_t = decltype(std::declval<T>().getFastmath());
23} // namespace
24
25/// Rewriting that replaces SourceOp with a CallOp to `f32Func` or `f64Func` or
26/// `f32ApproxFunc` or `f16Func` or `i32Type` depending on the element type and
27/// the fastMathFlag of that Op, if present. The function declaration is added
28/// in case it was not added before.
29///
30/// If the input values are of bf16 type (or f16 type if f16Func is empty), the
31/// value is first casted to f32, the function called and then the result casted
32/// back.
33///
34/// Example with NVVM:
35/// %exp_f32 = math.exp %arg_f32 : f32
36///
37/// will be transformed into
38/// llvm.call @__nv_expf(%arg_f32) : (f32) -> f32
39///
40/// If the fastMathFlag attribute of SourceOp is `afn` or `fast`, this Op lowers
41/// to the approximate calculation function.
42///
43/// Also example with NVVM:
44/// %exp_f32 = math.exp %arg_f32 fastmath<afn> : f32
45///
46/// will be transformed into
47/// llvm.call @__nv_fast_expf(%arg_f32) : (f32) -> f32
48///
49/// Final example with NVVM:
50/// %pow_f32 = math.fpowi %arg_f32, %arg_i32
51///
52/// will be transformed into
53/// llvm.call @__nv_powif(%arg_f32, %arg_i32) : (f32, i32) -> f32
54template <typename SourceOp>
56public:
58 const LLVMTypeConverter &lowering, StringRef f32Func, StringRef f64Func,
59 StringRef f32ApproxFunc, StringRef f16Func, StringRef i32Func = "",
60 PatternBenefit benefit = 1,
61 LLVM::cconv::CConv cconv = LLVM::cconv::CConv::C)
62 : ConvertOpToLLVMPattern<SourceOp>(lowering, benefit), f32Func(f32Func),
65
66 LogicalResult
67 matchAndRewrite(SourceOp op, typename SourceOp::Adaptor adaptor,
68 ConversionPatternRewriter &rewriter) const override {
69 using LLVM::LLVMFuncOp;
70
71 static_assert(
72 std::is_base_of<OpTrait::OneResult<SourceOp>, SourceOp>::value,
73 "expected single result op");
74
75 // This pattern only handles scalar ops. Ops with shaped (e.g. vector)
76 // result types, such as `math.isinf` on `vector<Nxf32>`, are expected to be
77 // scalarized first by `ScalarizeVectorOpLowering`, which is co-registered
78 // for these ops; bail out so that pattern can take over.
79 Type opResultType = op->getResultTypes().front();
80 if (!opResultType.isIntOrIndexOrFloat())
81 return rewriter.notifyMatchFailure(op, "expected scalar result type");
82
83 bool isResultBool = opResultType.isInteger(1);
84 if constexpr (!std::is_base_of<OpTrait::SameOperandsAndResultType<SourceOp>,
85 SourceOp>::value) {
86 assert(op->getNumOperands() > 0 &&
87 "expected op to take at least one operand");
88 assert((op->getResultTypes().front() == op->getOperand(0).getType() ||
89 isResultBool) &&
90 "expected op with same operand and result types");
91 }
92
93 if (!op->template getParentOfType<FunctionOpInterface>()) {
94 return rewriter.notifyMatchFailure(
95 op, "expected op to be within a function region");
96 }
97
98 SmallVector<Value, 1> castedOperands;
99 for (Value operand : adaptor.getOperands())
100 castedOperands.push_back(maybeCast(operand, rewriter));
101
102 Type castedOperandType = castedOperands.front().getType();
103
104 // At ABI level, booleans are treated as i32.
105 Type resultType =
106 isResultBool ? rewriter.getIntegerType(32) : castedOperandType;
107 Type funcType = getFunctionType(resultType, castedOperands);
108 StringRef funcName = getFunctionName(castedOperandType, op);
109 if (funcName.empty())
110 return failure();
111
112 LLVMFuncOp funcOp = appendOrGetFuncOp(funcName, funcType, op);
113 auto callOp =
114 LLVM::CallOp::create(rewriter, op->getLoc(), funcOp, castedOperands);
115 callOp.setCConv(cconv);
116
117 if (resultType == adaptor.getOperands().front().getType()) {
118 rewriter.replaceOp(op, {callOp.getResult()});
119 return success();
120 }
121
122 // Boolean result are mapping to i32 at the ABI level with zero values being
123 // interpreted as false and non-zero values being interpreted as true. Since
124 // there is no guarantee of a specific value being used to indicate true,
125 // compare for inequality with zero (rather than truncate or shift).
126 if (isResultBool) {
127 Value zero = LLVM::ConstantOp::create(rewriter, op->getLoc(),
128 rewriter.getIntegerType(32),
129 rewriter.getI32IntegerAttr(0));
130 Value truncated =
131 LLVM::ICmpOp::create(rewriter, op->getLoc(), LLVM::ICmpPredicate::ne,
132 callOp.getResult(), zero);
133 rewriter.replaceOp(op, {truncated});
134 return success();
135 }
136
137 assert(callOp.getResult().getType().isF32() &&
138 "only f32 types are supposed to be truncated back");
139 Value truncated = LLVM::FPTruncOp::create(
140 rewriter, op->getLoc(), adaptor.getOperands().front().getType(),
141 callOp.getResult());
142 rewriter.replaceOp(op, {truncated});
143 return success();
144 }
145
146 Value maybeCast(Value operand, PatternRewriter &rewriter) const {
147 Type type = operand.getType();
148 if (!isa<Float16Type, BFloat16Type>(type))
149 return operand;
150
151 // If there's an f16 function, no need to cast f16 values.
152 if (!f16Func.empty() && isa<Float16Type>(type))
153 return operand;
154
155 return LLVM::FPExtOp::create(rewriter, operand.getLoc(),
156 Float32Type::get(rewriter.getContext()),
157 operand);
158 }
159
160 Type getFunctionType(Type resultType, ValueRange operands) const {
161 SmallVector<Type> operandTypes(operands.getTypes());
162 return LLVM::LLVMFunctionType::get(resultType, operandTypes);
163 }
164
165 LLVM::LLVMFuncOp appendOrGetFuncOp(StringRef funcName, Type funcType,
166 Operation *op) const {
167 using LLVM::LLVMFuncOp;
168
169 auto funcAttr = StringAttr::get(op->getContext(), funcName);
170 auto funcOp =
172 if (funcOp)
173 return funcOp;
174
175 auto parentFunc = op->getParentOfType<FunctionOpInterface>();
176 assert(parentFunc && "expected there to be a parent function");
177 OpBuilder b(parentFunc);
178
179 // Create a valid global location removing any metadata attached to the
180 // location as debug info metadata inside of a function cannot be used
181 // outside of that function.
182 auto globalloc = op->getLoc()->findInstanceOfOrUnknown<FileLineColLoc>();
183 auto newFuncOp = LLVMFuncOp::create(b, globalloc, funcName, funcType);
184 newFuncOp.setCConv(cconv);
185 return newFuncOp;
186 }
187
188 StringRef getFunctionName(Type type, SourceOp op) const {
189 bool useApprox = false;
190 if constexpr (llvm::is_detected<has_get_fastmath_t, SourceOp>::value) {
191 arith::FastMathFlags flag = op.getFastmath();
192 useApprox = ((uint32_t)arith::FastMathFlags::afn & (uint32_t)flag) &&
193 !f32ApproxFunc.empty();
194 }
195
196 if (isa<Float16Type>(type))
197 return f16Func;
198 if (isa<Float32Type>(type)) {
199 if (useApprox)
200 return f32ApproxFunc;
201 return f32Func;
202 }
203 if (isa<Float64Type>(type))
204 return f64Func;
205
206 if (type.isInteger(32))
207 return i32Func;
208 return "";
209 }
210
211 const std::string f32Func;
212 const std::string f64Func;
213 const std::string f32ApproxFunc;
214 const std::string f16Func;
215 const std::string i32Func;
216 const LLVM::cconv::CConv cconv;
217};
218
219} // namespace mlir
220
221#endif // MLIR_CONVERSION_GPUCOMMON_OPTOFUNCCALLLOWERING_H_
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
MLIRContext * getContext() const
Definition Builders.h:56
ConvertOpToLLVMPattern(const LLVMTypeConverter &typeConverter, PatternBenefit benefit=1)
Definition Pattern.h:239
An instance of this location represents a tuple of file, line number, and column number.
Definition Location.h:174
Conversion from types to the LLVM IR dialect.
LocationAttr findInstanceOfOrUnknown()
Return an instance of the given location type if one is nested under the current location else return...
Definition Location.h:60
This class helps build Operations.
Definition Builders.h:210
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
Definition Types.cpp:122
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
type_range getTypes() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Include the generated interface declarations.
const LLVM::cconv::CConv cconv
OpToFuncCallLowering(const LLVMTypeConverter &lowering, StringRef f32Func, StringRef f64Func, StringRef f32ApproxFunc, StringRef f16Func, StringRef i32Func="", PatternBenefit benefit=1, LLVM::cconv::CConv cconv=LLVM::cconv::CConv::C)
StringRef getFunctionName(Type type, SourceOp op) const
LLVM::LLVMFuncOp appendOrGetFuncOp(StringRef funcName, Type funcType, Operation *op) const
LogicalResult matchAndRewrite(SourceOp op, typename SourceOp::Adaptor adaptor, ConversionPatternRewriter &rewriter) const override
Methods that operate on the SourceOp type.
Type getFunctionType(Type resultType, ValueRange operands) const
Value maybeCast(Value operand, PatternRewriter &rewriter) const