1*34810e1bSTres Popp //===-- MathToLibm.cpp - conversion from Math to libm calls ---------------===//
2*34810e1bSTres Popp //
3*34810e1bSTres Popp // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*34810e1bSTres Popp // See https://llvm.org/LICENSE.txt for license information.
5*34810e1bSTres Popp // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*34810e1bSTres Popp //
7*34810e1bSTres Popp //===----------------------------------------------------------------------===//
8*34810e1bSTres Popp 
9*34810e1bSTres Popp #include "mlir/Conversion/MathToLibm/MathToLibm.h"
10*34810e1bSTres Popp 
11*34810e1bSTres Popp #include "../PassDetail.h"
12*34810e1bSTres Popp #include "mlir/Dialect/Math/IR/Math.h"
13*34810e1bSTres Popp #include "mlir/Dialect/StandardOps/IR/Ops.h"
14*34810e1bSTres Popp #include "mlir/Dialect/Vector/VectorOps.h"
15*34810e1bSTres Popp #include "mlir/IR/BuiltinDialect.h"
16*34810e1bSTres Popp #include "mlir/IR/PatternMatch.h"
17*34810e1bSTres Popp 
18*34810e1bSTres Popp using namespace mlir;
19*34810e1bSTres Popp 
20*34810e1bSTres Popp namespace {
21*34810e1bSTres Popp // Pattern to convert vector operations to scalar operations. This is needed as
22*34810e1bSTres Popp // libm calls require scalars.
23*34810e1bSTres Popp template <typename Op>
24*34810e1bSTres Popp struct VecOpToScalarOp : public OpRewritePattern<Op> {
25*34810e1bSTres Popp public:
26*34810e1bSTres Popp   using OpRewritePattern<Op>::OpRewritePattern;
27*34810e1bSTres Popp 
28*34810e1bSTres Popp   LogicalResult matchAndRewrite(Op op, PatternRewriter &rewriter) const final;
29*34810e1bSTres Popp };
30*34810e1bSTres Popp // Pattern to convert scalar math operations to calls to libm functions.
31*34810e1bSTres Popp // Additionally the libm function signatures are declared.
32*34810e1bSTres Popp template <typename Op>
33*34810e1bSTres Popp struct ScalarOpToLibmCall : public OpRewritePattern<Op> {
34*34810e1bSTres Popp public:
35*34810e1bSTres Popp   using OpRewritePattern<Op>::OpRewritePattern;
36*34810e1bSTres Popp   ScalarOpToLibmCall<Op>(MLIRContext *context, StringRef floatFunc,
37*34810e1bSTres Popp                          StringRef doubleFunc, PatternBenefit benefit)
38*34810e1bSTres Popp       : OpRewritePattern<Op>(context, benefit), floatFunc(floatFunc),
39*34810e1bSTres Popp         doubleFunc(doubleFunc){};
40*34810e1bSTres Popp 
41*34810e1bSTres Popp   LogicalResult matchAndRewrite(Op op, PatternRewriter &rewriter) const final;
42*34810e1bSTres Popp 
43*34810e1bSTres Popp private:
44*34810e1bSTres Popp   std::string floatFunc, doubleFunc;
45*34810e1bSTres Popp };
46*34810e1bSTres Popp } // namespace
47*34810e1bSTres Popp 
48*34810e1bSTres Popp template <typename Op>
49*34810e1bSTres Popp LogicalResult
50*34810e1bSTres Popp VecOpToScalarOp<Op>::matchAndRewrite(Op op, PatternRewriter &rewriter) const {
51*34810e1bSTres Popp   auto opType = op.getType();
52*34810e1bSTres Popp   auto loc = op.getLoc();
53*34810e1bSTres Popp   auto vecType = opType.template dyn_cast<VectorType>();
54*34810e1bSTres Popp 
55*34810e1bSTres Popp   if (!vecType)
56*34810e1bSTres Popp     return failure();
57*34810e1bSTres Popp   if (!vecType.hasRank())
58*34810e1bSTres Popp     return failure();
59*34810e1bSTres Popp   auto shape = vecType.getShape();
60*34810e1bSTres Popp   // TODO: support multidimensional vectors
61*34810e1bSTres Popp   if (shape.size() != 1)
62*34810e1bSTres Popp     return failure();
63*34810e1bSTres Popp 
64*34810e1bSTres Popp   Value result = rewriter.create<ConstantOp>(
65*34810e1bSTres Popp       loc, DenseElementsAttr::get(
66*34810e1bSTres Popp                vecType, FloatAttr::get(vecType.getElementType(), 0.0)));
67*34810e1bSTres Popp   for (auto i = 0; i < shape.front(); ++i) {
68*34810e1bSTres Popp     SmallVector<Value> operands;
69*34810e1bSTres Popp     for (auto input : op->getOperands())
70*34810e1bSTres Popp       operands.push_back(
71*34810e1bSTres Popp           rewriter.create<vector::ExtractElementOp>(loc, input, i));
72*34810e1bSTres Popp     Value scalarOp =
73*34810e1bSTres Popp         rewriter.create<Op>(loc, vecType.getElementType(), operands);
74*34810e1bSTres Popp     result = rewriter.create<vector::InsertElementOp>(loc, scalarOp, result, i);
75*34810e1bSTres Popp   }
76*34810e1bSTres Popp   rewriter.replaceOp(op, {result});
77*34810e1bSTres Popp   return success();
78*34810e1bSTres Popp }
79*34810e1bSTres Popp 
80*34810e1bSTres Popp template <typename Op>
81*34810e1bSTres Popp LogicalResult
82*34810e1bSTres Popp ScalarOpToLibmCall<Op>::matchAndRewrite(Op op,
83*34810e1bSTres Popp                                         PatternRewriter &rewriter) const {
84*34810e1bSTres Popp   auto module = op->template getParentOfType<ModuleOp>();
85*34810e1bSTres Popp   auto type = op.getType();
86*34810e1bSTres Popp   // TODO: Support Float16 by upcasting to Float32
87*34810e1bSTres Popp   if (!type.template isa<Float32Type, Float64Type>())
88*34810e1bSTres Popp     return failure();
89*34810e1bSTres Popp 
90*34810e1bSTres Popp   auto name = type.getIntOrFloatBitWidth() == 64 ? doubleFunc : floatFunc;
91*34810e1bSTres Popp   auto opFunc = module.template lookupSymbol<FuncOp>(name);
92*34810e1bSTres Popp   // Forward declare function if it hasn't already been
93*34810e1bSTres Popp   if (!opFunc) {
94*34810e1bSTres Popp     OpBuilder::InsertionGuard guard(rewriter);
95*34810e1bSTres Popp     rewriter.setInsertionPointToStart(module.getBody());
96*34810e1bSTres Popp     auto opFunctionTy = FunctionType::get(
97*34810e1bSTres Popp         rewriter.getContext(), op->getOperandTypes(), op->getResultTypes());
98*34810e1bSTres Popp     opFunc =
99*34810e1bSTres Popp         rewriter.create<FuncOp>(rewriter.getUnknownLoc(), name, opFunctionTy);
100*34810e1bSTres Popp     opFunc.setPrivate();
101*34810e1bSTres Popp   }
102*34810e1bSTres Popp   assert(opFunc.getType().template cast<FunctionType>().getResults() ==
103*34810e1bSTres Popp          op->getResultTypes());
104*34810e1bSTres Popp   assert(opFunc.getType().template cast<FunctionType>().getInputs() ==
105*34810e1bSTres Popp          op->getOperandTypes());
106*34810e1bSTres Popp 
107*34810e1bSTres Popp   rewriter.replaceOpWithNewOp<CallOp>(op, opFunc, op->getOperands());
108*34810e1bSTres Popp 
109*34810e1bSTres Popp   return success();
110*34810e1bSTres Popp }
111*34810e1bSTres Popp 
112*34810e1bSTres Popp void mlir::populateMathToLibmConversionPatterns(RewritePatternSet &patterns,
113*34810e1bSTres Popp                                                 PatternBenefit benefit) {
114*34810e1bSTres Popp   patterns.add<VecOpToScalarOp<math::Atan2Op>, VecOpToScalarOp<math::ExpM1Op>,
115*34810e1bSTres Popp                VecOpToScalarOp<math::TanhOp>>(patterns.getContext(), benefit);
116*34810e1bSTres Popp   patterns.add<ScalarOpToLibmCall<math::Atan2Op>>(patterns.getContext(),
117*34810e1bSTres Popp                                                   "atan2f", "atan2", benefit);
118*34810e1bSTres Popp   patterns.add<ScalarOpToLibmCall<math::ExpM1Op>>(patterns.getContext(),
119*34810e1bSTres Popp                                                   "expm1f", "expm1", benefit);
120*34810e1bSTres Popp   patterns.add<ScalarOpToLibmCall<math::TanhOp>>(patterns.getContext(), "tanhf",
121*34810e1bSTres Popp                                                  "tanh", benefit);
122*34810e1bSTres Popp }
123*34810e1bSTres Popp 
124*34810e1bSTres Popp namespace {
125*34810e1bSTres Popp struct ConvertMathToLibmPass
126*34810e1bSTres Popp     : public ConvertMathToLibmBase<ConvertMathToLibmPass> {
127*34810e1bSTres Popp   void runOnOperation() override;
128*34810e1bSTres Popp };
129*34810e1bSTres Popp } // namespace
130*34810e1bSTres Popp 
131*34810e1bSTres Popp void ConvertMathToLibmPass::runOnOperation() {
132*34810e1bSTres Popp   auto module = getOperation();
133*34810e1bSTres Popp 
134*34810e1bSTres Popp   RewritePatternSet patterns(&getContext());
135*34810e1bSTres Popp   populateMathToLibmConversionPatterns(patterns, /*benefit=*/1);
136*34810e1bSTres Popp 
137*34810e1bSTres Popp   ConversionTarget target(getContext());
138*34810e1bSTres Popp   target.addLegalDialect<BuiltinDialect, StandardOpsDialect,
139*34810e1bSTres Popp                          vector::VectorDialect>();
140*34810e1bSTres Popp   target.addIllegalDialect<math::MathDialect>();
141*34810e1bSTres Popp   if (failed(applyPartialConversion(module, target, std::move(patterns))))
142*34810e1bSTres Popp     signalPassFailure();
143*34810e1bSTres Popp }
144*34810e1bSTres Popp 
145*34810e1bSTres Popp std::unique_ptr<OperationPass<ModuleOp>> mlir::createConvertMathToLibmPass() {
146*34810e1bSTres Popp   return std::make_unique<ConvertMathToLibmPass>();
147*34810e1bSTres Popp }
148