MLIR 24.0.0git
MathToNVVM.cpp
Go to the documentation of this file.
1//===-- MathToNVVM.cpp - conversion from Math to CUDA libdevice calls ----===//
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
17#include "mlir/Pass/Pass.h"
18
21
22namespace mlir {
23#define GEN_PASS_DEF_CONVERTMATHTONVVM
24#include "mlir/Conversion/Passes.h.inc"
25} // namespace mlir
26
27using namespace mlir;
28
29#define DEBUG_TYPE "math-to-nvvm"
30
31template <typename OpTy>
32static void populateOpPatterns(const LLVMTypeConverter &converter,
33 RewritePatternSet &patterns,
34 PatternBenefit benefit, StringRef f32Func,
35 StringRef f64Func, StringRef f32ApproxFunc = "",
36 StringRef f16Func = "") {
37 patterns.add<ScalarizeVectorOpLowering<OpTy>>(converter, benefit);
38 patterns.add<OpToFuncCallLowering<OpTy>>(converter, f32Func, f64Func,
39 f32ApproxFunc, f16Func,
40 /*i32Func=*/"", benefit);
41}
42
43template <typename OpTy>
44static void populateIntOpPatterns(const LLVMTypeConverter &converter,
45 RewritePatternSet &patterns,
46 PatternBenefit benefit, StringRef i32Func) {
47 patterns.add<ScalarizeVectorOpLowering<OpTy>>(converter, benefit);
48 patterns.add<OpToFuncCallLowering<OpTy>>(converter, "", "", "", "", i32Func,
49 benefit);
50}
51
52/// libdevice's __nv_powi and __nv_powif take an i32 exponent, so math.fpowi
53/// with a wider or index exponent is not lowered.
54static bool exponentFitsInI32(math::FPowIOp op) {
55 auto type =
56 dyn_cast<IntegerType>(getElementTypeOrSelf(op.getRhs().getType()));
57 return type && type.getWidth() <= 32;
58}
59
60namespace {
61struct FPowIScalarizeLowering
62 : public ScalarizeVectorOpLowering<math::FPowIOp> {
63 using ScalarizeVectorOpLowering::ScalarizeVectorOpLowering;
64
65 LogicalResult
66 matchAndRewrite(math::FPowIOp op, OpAdaptor adaptor,
67 ConversionPatternRewriter &rewriter) const override {
68 if (!exponentFitsInI32(op))
69 return rewriter.notifyMatchFailure(op, "exponent does not fit in i32");
70 return ScalarizeVectorOpLowering::matchAndRewrite(op, adaptor, rewriter);
71 }
72};
73
74struct FPowIOpLowering : public OpToFuncCallLowering<math::FPowIOp> {
76
77 LogicalResult
78 matchAndRewrite(math::FPowIOp op, OpAdaptor adaptor,
79 ConversionPatternRewriter &rewriter) const override {
80 if (!exponentFitsInI32(op))
81 return rewriter.notifyMatchFailure(op, "exponent does not fit in i32");
82 // Return before creating anything in the cases OpToFuncCallLowering
83 // does not handle.
84 if (!op->getParentOfType<FunctionOpInterface>() ||
85 !isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
86 adaptor.getLhs().getType()))
87 return rewriter.notifyMatchFailure(op, "cannot be lowered to libdevice");
88 // Sign-extend a narrower exponent.
89 Value exponent = adaptor.getRhs();
90 if (!exponent.getType().isInteger(32))
91 exponent = LLVM::SExtOp::create(rewriter, op.getLoc(),
92 rewriter.getI32Type(), exponent);
93 Value operands[] = {adaptor.getLhs(), exponent};
95 op, OpAdaptor(ValueRange(operands), op), rewriter);
96 }
97};
98} // namespace
99
100// Custom pattern for sincos since it returns two values
101struct SincosOpLowering : public ConvertOpToLLVMPattern<math::SincosOp> {
103
104 LogicalResult
105 matchAndRewrite(math::SincosOp op, OpAdaptor adaptor,
106 ConversionPatternRewriter &rewriter) const override {
107 Location loc = op.getLoc();
108 Value input = adaptor.getOperand();
109 Type inputType = input.getType();
110 auto convertedInput = maybeExt(input, rewriter);
111 auto computeType = convertedInput.getType();
112
113 StringRef sincosFunc;
114 if (isa<Float32Type>(computeType)) {
115 const arith::FastMathFlags flag = op.getFastmath();
116 const bool useApprox =
117 mlir::arith::bitEnumContainsAny(flag, arith::FastMathFlags::afn);
118 sincosFunc = useApprox ? "__nv_fast_sincosf" : "__nv_sincosf";
119 } else if (isa<Float64Type>(computeType)) {
120 sincosFunc = "__nv_sincos";
121 } else {
122 return rewriter.notifyMatchFailure(op,
123 "unsupported operand type for sincos");
124 }
125
126 auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext());
127
128 Value sinPtr, cosPtr;
129 {
130 OpBuilder::InsertionGuard guard(rewriter);
131 auto *scope =
132 op->getParentWithTrait<mlir::OpTrait::AutomaticAllocationScope>();
133 assert(scope && "Expected op to be inside automatic allocation scope");
134 rewriter.setInsertionPointToStart(&scope->getRegion(0).front());
135 auto one = LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(),
136 rewriter.getI32IntegerAttr(1));
137 sinPtr =
138 LLVM::AllocaOp::create(rewriter, loc, ptrType, computeType, one, 0);
139 cosPtr =
140 LLVM::AllocaOp::create(rewriter, loc, ptrType, computeType, one, 0);
141 }
142
143 createSincosCall(rewriter, loc, sincosFunc, convertedInput, sinPtr, cosPtr,
144 op);
145
146 auto sinResult = LLVM::LoadOp::create(rewriter, loc, computeType, sinPtr);
147 auto cosResult = LLVM::LoadOp::create(rewriter, loc, computeType, cosPtr);
148
149 rewriter.replaceOp(op, {maybeTrunc(sinResult, inputType, rewriter),
150 maybeTrunc(cosResult, inputType, rewriter)});
151 return success();
152 }
153
154private:
155 Value maybeExt(Value operand, PatternRewriter &rewriter) const {
156 if (isa<Float16Type, BFloat16Type>(operand.getType()))
157 return LLVM::FPExtOp::create(rewriter, operand.getLoc(),
158 Float32Type::get(rewriter.getContext()),
159 operand);
160 return operand;
161 }
162
163 Value maybeTrunc(Value operand, Type type, PatternRewriter &rewriter) const {
164 if (operand.getType() != type)
165 return LLVM::FPTruncOp::create(rewriter, operand.getLoc(), type, operand);
166 return operand;
167 }
168
169 void createSincosCall(ConversionPatternRewriter &rewriter, Location loc,
170 StringRef funcName, Value input, Value sinPtr,
171 Value cosPtr, Operation *op) const {
172 auto voidType = LLVM::LLVMVoidType::get(rewriter.getContext());
173 auto ptrType = sinPtr.getType();
174
175 SmallVector<Type> operandTypes = {input.getType(), ptrType, ptrType};
176 auto funcType = LLVM::LLVMFunctionType::get(voidType, operandTypes);
177
178 auto funcAttr = StringAttr::get(op->getContext(), funcName);
179 auto funcOp =
181
182 if (!funcOp) {
183 auto parentFunc = op->getParentOfType<FunctionOpInterface>();
184 assert(parentFunc && "expected there to be a parent function");
185 OpBuilder b(parentFunc);
186
187 auto globalloc = loc->findInstanceOfOrUnknown<FileLineColLoc>();
188 funcOp = LLVM::LLVMFuncOp::create(b, globalloc, funcName, funcType);
189 }
190
191 SmallVector<Value> callOperands = {input, sinPtr, cosPtr};
192 LLVM::CallOp::create(rewriter, loc, funcOp, callOperands);
193 }
194};
195
197 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
198 PatternBenefit benefit) {
199 populateOpPatterns<arith::RemFOp>(converter, patterns, benefit, "__nv_fmodf",
200 "__nv_fmod");
201 populateOpPatterns<arith::MaxNumFOp>(converter, patterns, benefit,
202 "__nv_fmaxf", "__nv_fmax");
203 populateOpPatterns<arith::MinNumFOp>(converter, patterns, benefit,
204 "__nv_fminf", "__nv_fmin");
205
206 populateIntOpPatterns<math::AbsIOp>(converter, patterns, benefit, "__nv_abs");
207 populateOpPatterns<math::AbsFOp>(converter, patterns, benefit, "__nv_fabsf",
208 "__nv_fabs");
209 populateOpPatterns<math::AcosOp>(converter, patterns, benefit, "__nv_acosf",
210 "__nv_acos");
211 populateOpPatterns<math::AcoshOp>(converter, patterns, benefit, "__nv_acoshf",
212 "__nv_acosh");
213 populateOpPatterns<math::AsinOp>(converter, patterns, benefit, "__nv_asinf",
214 "__nv_asin");
215 populateOpPatterns<math::AsinhOp>(converter, patterns, benefit, "__nv_asinhf",
216 "__nv_asinh");
217 populateOpPatterns<math::AtanOp>(converter, patterns, benefit, "__nv_atanf",
218 "__nv_atan");
219 populateOpPatterns<math::Atan2Op>(converter, patterns, benefit, "__nv_atan2f",
220 "__nv_atan2");
221 populateOpPatterns<math::AtanhOp>(converter, patterns, benefit, "__nv_atanhf",
222 "__nv_atanh");
223 populateOpPatterns<math::CbrtOp>(converter, patterns, benefit, "__nv_cbrtf",
224 "__nv_cbrt");
225 populateOpPatterns<math::CeilOp>(converter, patterns, benefit, "__nv_ceilf",
226 "__nv_ceil");
227 populateOpPatterns<math::CopySignOp>(converter, patterns, benefit,
228 "__nv_copysignf", "__nv_copysign");
229 populateOpPatterns<math::CosOp>(converter, patterns, benefit, "__nv_cosf",
230 "__nv_cos", "__nv_fast_cosf");
231 populateOpPatterns<math::CoshOp>(converter, patterns, benefit, "__nv_coshf",
232 "__nv_cosh");
233 populateOpPatterns<math::ErfOp>(converter, patterns, benefit, "__nv_erff",
234 "__nv_erf");
235 populateOpPatterns<math::ErfcOp>(converter, patterns, benefit, "__nv_erfcf",
236 "__nv_erfc");
237 populateOpPatterns<math::ExpOp>(converter, patterns, benefit, "__nv_expf",
238 "__nv_exp", "__nv_fast_expf");
239 populateOpPatterns<math::Exp2Op>(converter, patterns, benefit, "__nv_exp2f",
240 "__nv_exp2");
241 populateOpPatterns<math::ExpM1Op>(converter, patterns, benefit, "__nv_expm1f",
242 "__nv_expm1");
243 populateOpPatterns<math::FloorOp>(converter, patterns, benefit, "__nv_floorf",
244 "__nv_floor");
245 populateOpPatterns<math::FmaOp>(converter, patterns, benefit, "__nv_fmaf",
246 "__nv_fma");
247 // Note: libdevice uses a different name for 32-bit finite checking
248 populateOpPatterns<math::IsFiniteOp>(converter, patterns, benefit,
249 "__nv_finitef", "__nv_isfinited");
250 populateOpPatterns<math::IsInfOp>(converter, patterns, benefit, "__nv_isinff",
251 "__nv_isinfd");
252 populateOpPatterns<math::IsNaNOp>(converter, patterns, benefit, "__nv_isnanf",
253 "__nv_isnand");
254 populateOpPatterns<math::LogOp>(converter, patterns, benefit, "__nv_logf",
255 "__nv_log", "__nv_fast_logf");
256 populateOpPatterns<math::Log10Op>(converter, patterns, benefit, "__nv_log10f",
257 "__nv_log10", "__nv_fast_log10f");
258 populateOpPatterns<math::Log1pOp>(converter, patterns, benefit, "__nv_log1pf",
259 "__nv_log1p");
260 populateOpPatterns<math::Log2Op>(converter, patterns, benefit, "__nv_log2f",
261 "__nv_log2", "__nv_fast_log2f");
262 populateOpPatterns<math::PowFOp>(converter, patterns, benefit, "__nv_powf",
263 "__nv_pow", "__nv_fast_powf");
264 patterns.add<FPowIScalarizeLowering>(converter, benefit);
265 patterns.add<FPowIOpLowering>(converter, "__nv_powif", "__nv_powi", "", "",
266 /*i32Func=*/"", benefit);
267 populateOpPatterns<math::RoundOp>(converter, patterns, benefit, "__nv_roundf",
268 "__nv_round");
269 populateOpPatterns<math::RoundEvenOp>(converter, patterns, benefit,
270 "__nv_rintf", "__nv_rint");
271 populateOpPatterns<math::RsqrtOp>(converter, patterns, benefit, "__nv_rsqrtf",
272 "__nv_rsqrt");
273 populateOpPatterns<math::SinOp>(converter, patterns, benefit, "__nv_sinf",
274 "__nv_sin", "__nv_fast_sinf");
275 populateOpPatterns<math::SinhOp>(converter, patterns, benefit, "__nv_sinhf",
276 "__nv_sinh");
277 populateOpPatterns<math::SqrtOp>(converter, patterns, benefit, "__nv_sqrtf",
278 "__nv_sqrt");
279 populateOpPatterns<math::TanOp>(converter, patterns, benefit, "__nv_tanf",
280 "__nv_tan", "__nv_fast_tanf");
281 populateOpPatterns<math::TanhOp>(converter, patterns, benefit, "__nv_tanhf",
282 "__nv_tanh");
283
284 // Custom pattern for sincos since it returns two values
285 patterns.add<SincosOpLowering>(converter, benefit);
286}
287
288namespace {
289struct ConvertMathToNVVMPass final
290 : impl::ConvertMathToNVVMBase<ConvertMathToNVVMPass> {
292 ConvertMathToNVVMPass>::ConvertMathToNVVMBase;
293
294 void runOnOperation() override;
295};
296} // namespace
297
298void ConvertMathToNVVMPass::runOnOperation() {
299 auto m = getOperation();
300 MLIRContext *ctx = m.getContext();
301
302 RewritePatternSet patterns(&getContext());
303 LowerToLLVMOptions options(ctx, DataLayout(m));
304 LLVMTypeConverter converter(ctx, options);
305
306 populateLibDeviceConversionPatterns(converter, patterns, /*benefit=*/1);
307
308 ConversionTarget target(getContext());
309 target
310 .addLegalDialect<BuiltinDialect, func::FuncDialect, vector::VectorDialect,
311 LLVM::LLVMDialect, NVVM::NVVMDialect>();
312 target.addIllegalOp<LLVM::CosOp, LLVM::ExpOp, LLVM::Exp2Op, LLVM::FAbsOp,
313 LLVM::FCeilOp, LLVM::FFloorOp, LLVM::FRemOp, LLVM::LogOp,
314 LLVM::Log10Op, LLVM::Log2Op, LLVM::PowOp, LLVM::SinOp,
315 LLVM::SqrtOp>();
316 if (failed(applyPartialConversion(m, target, std::move(patterns))))
317 signalPassFailure();
318}
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
static bool exponentFitsInI32(math::FPowIOp op)
libdevice's __nv_powi and __nv_powif take an i32 exponent, so math.fpowi with a wider or index expone...
static void populateOpPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit, StringRef f32Func, StringRef f64Func, StringRef f32ApproxFunc="", StringRef f16Func="")
static void populateIntOpPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit, StringRef i32Func)
static llvm::ManagedStatic< PassManagerOptions > options
MLIRContext * getContext() const
Definition Builders.h:56
ConvertOpToLLVMPattern(const LLVMTypeConverter &typeConverter, PatternBenefit benefit=1)
Definition Pattern.h:239
typename math::SincosOp::Adaptor OpAdaptor
Definition Pattern.h:235
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 defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
A trait of region holding operations that define a new scope for automatic allocations,...
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...
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
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 isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
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
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Include the generated interface declarations.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
void populateLibDeviceConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit=1)
Populate the given list with patterns that convert from Math to NVVM libdevice calls.
LogicalResult matchAndRewrite(math::SincosOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
Rewriting that replaces SourceOp with a CallOp to f32Func or f64Func or f32ApproxFunc or f16Func or i...
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)
LogicalResult matchAndRewrite(SourceOp op, typename SourceOp::Adaptor adaptor, ConversionPatternRewriter &rewriter) const override
Methods that operate on the SourceOp type.
Unrolls SourceOp to array/vector elements.
LogicalResult matchAndRewrite(SourceOp op, typename SourceOp::Adaptor adaptor, ConversionPatternRewriter &rewriter) const override
Methods that operate on the SourceOp type.