1f99ccf65SEugene Zhulenev //===- PolynomialApproximation.cpp - Approximate math operations ----------===// 2f99ccf65SEugene Zhulenev // 3f99ccf65SEugene Zhulenev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4f99ccf65SEugene Zhulenev // See https://llvm.org/LICENSE.txt for license information. 5f99ccf65SEugene Zhulenev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6f99ccf65SEugene Zhulenev // 7f99ccf65SEugene Zhulenev //===----------------------------------------------------------------------===// 8f99ccf65SEugene Zhulenev // 9f99ccf65SEugene Zhulenev // This file implements expansion of math operations to fast approximations 10f99ccf65SEugene Zhulenev // that do not rely on any of the library functions. 11f99ccf65SEugene Zhulenev // 12f99ccf65SEugene Zhulenev //===----------------------------------------------------------------------===// 133a506b31SChris Lattner 14ec32d540SEugene Zhulenev #include <climits> 15ec32d540SEugene Zhulenev #include <cstddef> 16ec32d540SEugene Zhulenev 17a54f4eaeSMogball #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" 18f99ccf65SEugene Zhulenev #include "mlir/Dialect/Math/IR/Math.h" 19f1b92218SBoian Petkantchin #include "mlir/Dialect/Math/Transforms/Approximation.h" 20f99ccf65SEugene Zhulenev #include "mlir/Dialect/Math/Transforms/Passes.h" 21*99ef9eebSMatthias Springer #include "mlir/Dialect/Utils/IndexingUtils.h" 22*99ef9eebSMatthias Springer #include "mlir/Dialect/Vector/IR/VectorOps.h" 23*99ef9eebSMatthias Springer #include "mlir/Dialect/Vector/Utils/VectorUtils.h" 2435553d45SEmilio Cota #include "mlir/Dialect/X86Vector/X86VectorDialect.h" 25f99ccf65SEugene Zhulenev #include "mlir/IR/Builders.h" 26ce976d2dSEugene Zhulenev #include "mlir/IR/ImplicitLocOpBuilder.h" 27ec32d540SEugene Zhulenev #include "mlir/IR/TypeUtilities.h" 28f99ccf65SEugene Zhulenev #include "mlir/Transforms/DialectConversion.h" 29f99ccf65SEugene Zhulenev #include "mlir/Transforms/GreedyPatternRewriteDriver.h" 30f1b92218SBoian Petkantchin #include "llvm/ADT/ArrayRef.h" 31f99ccf65SEugene Zhulenev 32f99ccf65SEugene Zhulenev using namespace mlir; 33f1b92218SBoian Petkantchin using namespace mlir::math; 34f99ccf65SEugene Zhulenev using namespace mlir::vector; 35f99ccf65SEugene Zhulenev 36ec32d540SEugene Zhulenev // Returns vector shape if the type is a vector. Returns an empty shape if it is 37ec32d540SEugene Zhulenev // not a vector. 38ec32d540SEugene Zhulenev static ArrayRef<int64_t> vectorShape(Type type) { 39ce976d2dSEugene Zhulenev auto vectorType = type.dyn_cast<VectorType>(); 40ec32d540SEugene Zhulenev return vectorType ? vectorType.getShape() : ArrayRef<int64_t>(); 41ce976d2dSEugene Zhulenev } 42ce976d2dSEugene Zhulenev 43ec32d540SEugene Zhulenev static ArrayRef<int64_t> vectorShape(Value value) { 44ec32d540SEugene Zhulenev return vectorShape(value.getType()); 4539b2cd40SEugene Zhulenev } 4639b2cd40SEugene Zhulenev 47f99ccf65SEugene Zhulenev //----------------------------------------------------------------------------// 48ce976d2dSEugene Zhulenev // Broadcast scalar types and values into vector types and values. 49f99ccf65SEugene Zhulenev //----------------------------------------------------------------------------// 50f99ccf65SEugene Zhulenev 5196cee297SAlexander Belyaev // Broadcasts scalar type into vector type (iff shape is non-scalar). 5296cee297SAlexander Belyaev static Type broadcast(Type type, ArrayRef<int64_t> shape) { 5396cee297SAlexander Belyaev assert(!type.isa<VectorType>() && "must be scalar type"); 54ec32d540SEugene Zhulenev return !shape.empty() ? VectorType::get(shape, type) : type; 5596cee297SAlexander Belyaev } 5696cee297SAlexander Belyaev 5796cee297SAlexander Belyaev // Broadcasts scalar value into vector (iff shape is non-scalar). 5896cee297SAlexander Belyaev static Value broadcast(ImplicitLocOpBuilder &builder, Value value, 5996cee297SAlexander Belyaev ArrayRef<int64_t> shape) { 60ce976d2dSEugene Zhulenev assert(!value.getType().isa<VectorType>() && "must be scalar value"); 6196cee297SAlexander Belyaev auto type = broadcast(value.getType(), shape); 62ec32d540SEugene Zhulenev return !shape.empty() ? builder.create<BroadcastOp>(type, value) : value; 63ce976d2dSEugene Zhulenev } 64f99ccf65SEugene Zhulenev 65ce976d2dSEugene Zhulenev //----------------------------------------------------------------------------// 66627fa0b9SEugene Zhulenev // Helper function to handle n-D vectors with 1-D operations. 67627fa0b9SEugene Zhulenev //----------------------------------------------------------------------------// 68627fa0b9SEugene Zhulenev 69627fa0b9SEugene Zhulenev // Expands and unrolls n-D vector operands into multiple fixed size 1-D vectors 70627fa0b9SEugene Zhulenev // and calls the compute function with 1-D vector operands. Stitches back all 71627fa0b9SEugene Zhulenev // results into the original n-D vector result. 72627fa0b9SEugene Zhulenev // 73627fa0b9SEugene Zhulenev // Examples: vectorWidth = 8 74627fa0b9SEugene Zhulenev // - vector<4x8xf32> unrolled 4 times 75627fa0b9SEugene Zhulenev // - vector<16xf32> expanded to vector<2x8xf32> and unrolled 2 times 76627fa0b9SEugene Zhulenev // - vector<4x16xf32> expanded to vector<4x2x8xf32> and unrolled 4*2 times 77627fa0b9SEugene Zhulenev // 78627fa0b9SEugene Zhulenev // Some math approximations rely on ISA-specific operations that only accept 79627fa0b9SEugene Zhulenev // fixed size 1-D vectors (e.g. AVX expects vectors of width 8). 80627fa0b9SEugene Zhulenev // 81627fa0b9SEugene Zhulenev // It is the caller's responsibility to verify that the inner dimension is 82627fa0b9SEugene Zhulenev // divisible by the vectorWidth, and that all operands have the same vector 83627fa0b9SEugene Zhulenev // shape. 84627fa0b9SEugene Zhulenev static Value 85627fa0b9SEugene Zhulenev handleMultidimensionalVectors(ImplicitLocOpBuilder &builder, 86627fa0b9SEugene Zhulenev ValueRange operands, int64_t vectorWidth, 871fc096afSMehdi Amini llvm::function_ref<Value(ValueRange)> compute) { 88627fa0b9SEugene Zhulenev assert(!operands.empty() && "operands must be not empty"); 89627fa0b9SEugene Zhulenev assert(vectorWidth > 0 && "vector width must be larger than 0"); 90627fa0b9SEugene Zhulenev 91627fa0b9SEugene Zhulenev VectorType inputType = operands[0].getType().cast<VectorType>(); 92627fa0b9SEugene Zhulenev ArrayRef<int64_t> inputShape = inputType.getShape(); 93627fa0b9SEugene Zhulenev 94627fa0b9SEugene Zhulenev // If input shape matches target vector width, we can just call the 95627fa0b9SEugene Zhulenev // user-provided compute function with the operands. 96627fa0b9SEugene Zhulenev if (inputShape == llvm::makeArrayRef(vectorWidth)) 97627fa0b9SEugene Zhulenev return compute(operands); 98627fa0b9SEugene Zhulenev 99627fa0b9SEugene Zhulenev // Check if the inner dimension has to be expanded, or we can directly iterate 100627fa0b9SEugene Zhulenev // over the outer dimensions of the vector. 101627fa0b9SEugene Zhulenev int64_t innerDim = inputShape.back(); 102627fa0b9SEugene Zhulenev int64_t expansionDim = innerDim / vectorWidth; 103627fa0b9SEugene Zhulenev assert((innerDim % vectorWidth == 0) && "invalid inner dimension size"); 104627fa0b9SEugene Zhulenev 105627fa0b9SEugene Zhulenev // Maybe expand operands to the higher rank vector shape that we'll use to 106627fa0b9SEugene Zhulenev // iterate over and extract one dimensional vectors. 107627fa0b9SEugene Zhulenev SmallVector<int64_t> expandedShape(inputShape.begin(), inputShape.end()); 108627fa0b9SEugene Zhulenev SmallVector<Value> expandedOperands(operands); 109627fa0b9SEugene Zhulenev 110627fa0b9SEugene Zhulenev if (expansionDim > 1) { 111627fa0b9SEugene Zhulenev // Expand shape from [..., innerDim] to [..., expansionDim, vectorWidth]. 112627fa0b9SEugene Zhulenev expandedShape.insert(expandedShape.end() - 1, expansionDim); 113627fa0b9SEugene Zhulenev expandedShape.back() = vectorWidth; 114627fa0b9SEugene Zhulenev 115627fa0b9SEugene Zhulenev for (unsigned i = 0; i < operands.size(); ++i) { 116627fa0b9SEugene Zhulenev auto operand = operands[i]; 117627fa0b9SEugene Zhulenev auto eltType = operand.getType().cast<VectorType>().getElementType(); 118627fa0b9SEugene Zhulenev auto expandedType = VectorType::get(expandedShape, eltType); 119627fa0b9SEugene Zhulenev expandedOperands[i] = 120627fa0b9SEugene Zhulenev builder.create<vector::ShapeCastOp>(expandedType, operand); 121627fa0b9SEugene Zhulenev } 122627fa0b9SEugene Zhulenev } 123627fa0b9SEugene Zhulenev 124627fa0b9SEugene Zhulenev // Iterate over all outer dimensions of the compute shape vector type. 125627fa0b9SEugene Zhulenev auto iterationDims = ArrayRef<int64_t>(expandedShape).drop_back(); 126627fa0b9SEugene Zhulenev int64_t maxLinearIndex = computeMaxLinearIndex(iterationDims); 127627fa0b9SEugene Zhulenev 128627fa0b9SEugene Zhulenev SmallVector<int64_t> ones(iterationDims.size(), 1); 129627fa0b9SEugene Zhulenev auto strides = computeStrides(iterationDims, ones); 130627fa0b9SEugene Zhulenev 131627fa0b9SEugene Zhulenev // Compute results for each one dimensional vector. 132627fa0b9SEugene Zhulenev SmallVector<Value> results(maxLinearIndex); 133627fa0b9SEugene Zhulenev 134627fa0b9SEugene Zhulenev for (int64_t i = 0; i < maxLinearIndex; ++i) { 135627fa0b9SEugene Zhulenev auto offsets = delinearize(strides, i); 136627fa0b9SEugene Zhulenev 137627fa0b9SEugene Zhulenev SmallVector<Value> extracted(expandedOperands.size()); 13889de9cc8SMehdi Amini for (const auto &tuple : llvm::enumerate(expandedOperands)) 139627fa0b9SEugene Zhulenev extracted[tuple.index()] = 140627fa0b9SEugene Zhulenev builder.create<vector::ExtractOp>(tuple.value(), offsets); 141627fa0b9SEugene Zhulenev 142627fa0b9SEugene Zhulenev results[i] = compute(extracted); 143627fa0b9SEugene Zhulenev } 144627fa0b9SEugene Zhulenev 145627fa0b9SEugene Zhulenev // Stitch results together into one large vector. 146627fa0b9SEugene Zhulenev Type resultEltType = results[0].getType().cast<VectorType>().getElementType(); 147627fa0b9SEugene Zhulenev Type resultExpandedType = VectorType::get(expandedShape, resultEltType); 148627fa0b9SEugene Zhulenev Value result = builder.create<ConstantOp>( 149627fa0b9SEugene Zhulenev resultExpandedType, builder.getZeroAttr(resultExpandedType)); 150627fa0b9SEugene Zhulenev 151627fa0b9SEugene Zhulenev for (int64_t i = 0; i < maxLinearIndex; ++i) 152627fa0b9SEugene Zhulenev result = builder.create<vector::InsertOp>(results[i], result, 153627fa0b9SEugene Zhulenev delinearize(strides, i)); 154627fa0b9SEugene Zhulenev 155627fa0b9SEugene Zhulenev // Reshape back to the original vector shape. 156627fa0b9SEugene Zhulenev return builder.create<vector::ShapeCastOp>( 157627fa0b9SEugene Zhulenev VectorType::get(inputShape, resultEltType), result); 158627fa0b9SEugene Zhulenev } 159627fa0b9SEugene Zhulenev 160627fa0b9SEugene Zhulenev //----------------------------------------------------------------------------// 161ce976d2dSEugene Zhulenev // Helper functions to create constants. 162ce976d2dSEugene Zhulenev //----------------------------------------------------------------------------// 163f99ccf65SEugene Zhulenev 164ce976d2dSEugene Zhulenev static Value f32Cst(ImplicitLocOpBuilder &builder, float value) { 165a54f4eaeSMogball return builder.create<arith::ConstantOp>(builder.getF32FloatAttr(value)); 166ce976d2dSEugene Zhulenev } 167f99ccf65SEugene Zhulenev 168ce976d2dSEugene Zhulenev static Value i32Cst(ImplicitLocOpBuilder &builder, int32_t value) { 169a54f4eaeSMogball return builder.create<arith::ConstantOp>(builder.getI32IntegerAttr(value)); 170ce976d2dSEugene Zhulenev } 171f99ccf65SEugene Zhulenev 172ce976d2dSEugene Zhulenev static Value f32FromBits(ImplicitLocOpBuilder &builder, uint32_t bits) { 173ce976d2dSEugene Zhulenev Value i32Value = i32Cst(builder, static_cast<int32_t>(bits)); 174a54f4eaeSMogball return builder.create<arith::BitcastOp>(builder.getF32Type(), i32Value); 175ce976d2dSEugene Zhulenev } 176f99ccf65SEugene Zhulenev 177ce976d2dSEugene Zhulenev //----------------------------------------------------------------------------// 178ce976d2dSEugene Zhulenev // Helper functions to build math functions approximations. 179ce976d2dSEugene Zhulenev //----------------------------------------------------------------------------// 180ce976d2dSEugene Zhulenev 181ce976d2dSEugene Zhulenev static Value min(ImplicitLocOpBuilder &builder, Value a, Value b) { 182ce976d2dSEugene Zhulenev return builder.create<SelectOp>( 183a54f4eaeSMogball builder.create<arith::CmpFOp>(arith::CmpFPredicate::OLT, a, b), a, b); 184ce976d2dSEugene Zhulenev } 185ce976d2dSEugene Zhulenev 186ce976d2dSEugene Zhulenev static Value max(ImplicitLocOpBuilder &builder, Value a, Value b) { 187ce976d2dSEugene Zhulenev return builder.create<SelectOp>( 188a54f4eaeSMogball builder.create<arith::CmpFOp>(arith::CmpFPredicate::OGT, a, b), a, b); 189ce976d2dSEugene Zhulenev } 190ce976d2dSEugene Zhulenev 191ce976d2dSEugene Zhulenev static Value clamp(ImplicitLocOpBuilder &builder, Value value, Value lowerBound, 192ce976d2dSEugene Zhulenev Value upperBound) { 193ce976d2dSEugene Zhulenev return max(builder, min(builder, value, upperBound), lowerBound); 194ce976d2dSEugene Zhulenev } 195ce976d2dSEugene Zhulenev 196ce976d2dSEugene Zhulenev // Decomposes given floating point value `arg` into a normalized fraction and 197ce976d2dSEugene Zhulenev // an integral power of two (see std::frexp). Returned values have float type. 198ce976d2dSEugene Zhulenev static std::pair<Value, Value> frexp(ImplicitLocOpBuilder &builder, Value arg, 19902b6fb21SMehdi Amini bool isPositive = false) { 200ec32d540SEugene Zhulenev assert(getElementTypeOrSelf(arg).isF32() && "arg must be f32 type"); 201ec32d540SEugene Zhulenev ArrayRef<int64_t> shape = vectorShape(arg); 202ce976d2dSEugene Zhulenev 203ce976d2dSEugene Zhulenev auto bcast = [&](Value value) -> Value { 20496cee297SAlexander Belyaev return broadcast(builder, value, shape); 205f99ccf65SEugene Zhulenev }; 206f99ccf65SEugene Zhulenev 207ce976d2dSEugene Zhulenev auto i32 = builder.getIntegerType(32); 20896cee297SAlexander Belyaev auto i32Vec = broadcast(i32, shape); 20996cee297SAlexander Belyaev auto f32Vec = broadcast(builder.getF32Type(), shape); 210f99ccf65SEugene Zhulenev 211ce976d2dSEugene Zhulenev Value cst126f = f32Cst(builder, 126.0f); 212ce976d2dSEugene Zhulenev Value cstHalf = f32Cst(builder, 0.5f); 213ce976d2dSEugene Zhulenev Value cstInvMantMask = f32FromBits(builder, ~0x7f800000u); 214f99ccf65SEugene Zhulenev 215ce976d2dSEugene Zhulenev // Bitcast to i32 for bitwise operations. 216a54f4eaeSMogball Value i32Half = builder.create<arith::BitcastOp>(i32, cstHalf); 217a54f4eaeSMogball Value i32InvMantMask = builder.create<arith::BitcastOp>(i32, cstInvMantMask); 218a54f4eaeSMogball Value i32Arg = builder.create<arith::BitcastOp>(i32Vec, arg); 219f99ccf65SEugene Zhulenev 220ce976d2dSEugene Zhulenev // Compute normalized fraction. 221a54f4eaeSMogball Value tmp0 = builder.create<arith::AndIOp>(i32Arg, bcast(i32InvMantMask)); 222a54f4eaeSMogball Value tmp1 = builder.create<arith::OrIOp>(tmp0, bcast(i32Half)); 223a54f4eaeSMogball Value normalizedFraction = builder.create<arith::BitcastOp>(f32Vec, tmp1); 224f99ccf65SEugene Zhulenev 225ce976d2dSEugene Zhulenev // Compute exponent. 22602b6fb21SMehdi Amini Value arg0 = isPositive ? arg : builder.create<math::AbsOp>(arg); 227a54f4eaeSMogball Value biasedExponentBits = builder.create<arith::ShRUIOp>( 228a54f4eaeSMogball builder.create<arith::BitcastOp>(i32Vec, arg0), 229a54f4eaeSMogball bcast(i32Cst(builder, 23))); 230a54f4eaeSMogball Value biasedExponent = 231a54f4eaeSMogball builder.create<arith::SIToFPOp>(f32Vec, biasedExponentBits); 232a54f4eaeSMogball Value exponent = 233a54f4eaeSMogball builder.create<arith::SubFOp>(biasedExponent, bcast(cst126f)); 234f99ccf65SEugene Zhulenev 235ce976d2dSEugene Zhulenev return {normalizedFraction, exponent}; 236f99ccf65SEugene Zhulenev } 237f99ccf65SEugene Zhulenev 238ea7f211bSAhmed Taei // Computes exp2 for an i32 argument. 239ea7f211bSAhmed Taei static Value exp2I32(ImplicitLocOpBuilder &builder, Value arg) { 240ec32d540SEugene Zhulenev assert(getElementTypeOrSelf(arg).isInteger(32) && "arg must be i32 type"); 241ec32d540SEugene Zhulenev ArrayRef<int64_t> shape = vectorShape(arg); 242ea7f211bSAhmed Taei 243ea7f211bSAhmed Taei auto bcast = [&](Value value) -> Value { 24496cee297SAlexander Belyaev return broadcast(builder, value, shape); 245ea7f211bSAhmed Taei }; 246ea7f211bSAhmed Taei 24796cee297SAlexander Belyaev auto f32Vec = broadcast(builder.getF32Type(), shape); 248ea7f211bSAhmed Taei // The exponent of f32 located at 23-bit. 249ea7f211bSAhmed Taei auto exponetBitLocation = bcast(i32Cst(builder, 23)); 250ea7f211bSAhmed Taei // Set the exponent bias to zero. 251ea7f211bSAhmed Taei auto bias = bcast(i32Cst(builder, 127)); 252ea7f211bSAhmed Taei 253a54f4eaeSMogball Value biasedArg = builder.create<arith::AddIOp>(arg, bias); 254ea7f211bSAhmed Taei Value exp2ValueInt = 255a54f4eaeSMogball builder.create<arith::ShLIOp>(biasedArg, exponetBitLocation); 256a54f4eaeSMogball Value exp2ValueF32 = builder.create<arith::BitcastOp>(f32Vec, exp2ValueInt); 257ea7f211bSAhmed Taei 258ea7f211bSAhmed Taei return exp2ValueF32; 259ea7f211bSAhmed Taei } 260ea7f211bSAhmed Taei 261f1b92218SBoian Petkantchin namespace { 262f1b92218SBoian Petkantchin Value makePolynomialCalculation(ImplicitLocOpBuilder &builder, 263f1b92218SBoian Petkantchin llvm::ArrayRef<Value> coeffs, Value x) { 264ec32d540SEugene Zhulenev assert(getElementTypeOrSelf(x).isF32() && "x must be f32 type"); 265ec32d540SEugene Zhulenev ArrayRef<int64_t> shape = vectorShape(x); 266ec32d540SEugene Zhulenev 267ec32d540SEugene Zhulenev if (coeffs.empty()) 268ec32d540SEugene Zhulenev return broadcast(builder, f32Cst(builder, 0.0f), shape); 269ec32d540SEugene Zhulenev 270ec32d540SEugene Zhulenev if (coeffs.size() == 1) 271f1b92218SBoian Petkantchin return coeffs[0]; 272ec32d540SEugene Zhulenev 273f1b92218SBoian Petkantchin Value res = builder.create<math::FmaOp>(x, coeffs[coeffs.size() - 1], 274f1b92218SBoian Petkantchin coeffs[coeffs.size() - 2]); 275f1b92218SBoian Petkantchin for (auto i = ptrdiff_t(coeffs.size()) - 3; i >= 0; --i) { 276f1b92218SBoian Petkantchin res = builder.create<math::FmaOp>(x, res, coeffs[i]); 277f1b92218SBoian Petkantchin } 278f1b92218SBoian Petkantchin return res; 279f1b92218SBoian Petkantchin } 280f1b92218SBoian Petkantchin } // namespace 281f1b92218SBoian Petkantchin 282f99ccf65SEugene Zhulenev //----------------------------------------------------------------------------// 2832f9f9afaSRob Suderman // AtanOp approximation. 2842f9f9afaSRob Suderman //----------------------------------------------------------------------------// 2852f9f9afaSRob Suderman 2862f9f9afaSRob Suderman namespace { 2872f9f9afaSRob Suderman struct AtanApproximation : public OpRewritePattern<math::AtanOp> { 2882f9f9afaSRob Suderman public: 2892f9f9afaSRob Suderman using OpRewritePattern::OpRewritePattern; 2902f9f9afaSRob Suderman 2912f9f9afaSRob Suderman LogicalResult matchAndRewrite(math::AtanOp op, 2922f9f9afaSRob Suderman PatternRewriter &rewriter) const final; 2932f9f9afaSRob Suderman }; 2942f9f9afaSRob Suderman } // namespace 2952f9f9afaSRob Suderman 2962f9f9afaSRob Suderman LogicalResult 2972f9f9afaSRob Suderman AtanApproximation::matchAndRewrite(math::AtanOp op, 2982f9f9afaSRob Suderman PatternRewriter &rewriter) const { 2992f9f9afaSRob Suderman auto operand = op.getOperand(); 3002f9f9afaSRob Suderman if (!getElementTypeOrSelf(operand).isF32()) 3012f9f9afaSRob Suderman return rewriter.notifyMatchFailure(op, "unsupported operand type"); 3022f9f9afaSRob Suderman 3032f9f9afaSRob Suderman ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 3042f9f9afaSRob Suderman 3052f9f9afaSRob Suderman ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 3062f9f9afaSRob Suderman auto one = broadcast(builder, f32Cst(builder, 1.0f), shape); 3072f9f9afaSRob Suderman 3082f9f9afaSRob Suderman // Remap the problem over [0.0, 1.0] by looking at the absolute value and the 3092f9f9afaSRob Suderman // handling symmetry. 3102f9f9afaSRob Suderman Value abs = builder.create<math::AbsOp>(operand); 3112f9f9afaSRob Suderman Value reciprocal = builder.create<arith::DivFOp>(one, abs); 3122f9f9afaSRob Suderman Value compare = 3132f9f9afaSRob Suderman builder.create<arith::CmpFOp>(arith::CmpFPredicate::OLT, abs, reciprocal); 3142f9f9afaSRob Suderman Value x = builder.create<SelectOp>(compare, abs, reciprocal); 3152f9f9afaSRob Suderman 3162f9f9afaSRob Suderman // Perform the Taylor series approximation for atan over the range 3172f9f9afaSRob Suderman // [-1.0, 1.0]. 318dc3b9365SAlexandre Ganea auto n1 = broadcast(builder, f32Cst(builder, 0.14418283f), shape); 319dc3b9365SAlexandre Ganea auto n2 = broadcast(builder, f32Cst(builder, -0.34999234f), shape); 320dc3b9365SAlexandre Ganea auto n3 = broadcast(builder, f32Cst(builder, -0.01067831f), shape); 321dc3b9365SAlexandre Ganea auto n4 = broadcast(builder, f32Cst(builder, 1.00209986f), shape); 3222f9f9afaSRob Suderman 3232f9f9afaSRob Suderman Value p = builder.create<math::FmaOp>(x, n1, n2); 3242f9f9afaSRob Suderman p = builder.create<math::FmaOp>(x, p, n3); 3252f9f9afaSRob Suderman p = builder.create<math::FmaOp>(x, p, n4); 3262f9f9afaSRob Suderman p = builder.create<arith::MulFOp>(x, p); 3272f9f9afaSRob Suderman 3282f9f9afaSRob Suderman // Remap the solution for over [0.0, 1.0] to [0.0, inf] 32970ed93ecSMehdi Amini auto halfPi = broadcast(builder, f32Cst(builder, 1.57079632679f), shape); 33070ed93ecSMehdi Amini Value sub = builder.create<arith::SubFOp>(halfPi, p); 3312f9f9afaSRob Suderman Value select = builder.create<SelectOp>(compare, p, sub); 3322f9f9afaSRob Suderman 3332f9f9afaSRob Suderman // Correct for signing of the input. 3342f9f9afaSRob Suderman rewriter.replaceOpWithNewOp<math::CopySignOp>(op, select, operand); 3352f9f9afaSRob Suderman return success(); 3362f9f9afaSRob Suderman } 3372f9f9afaSRob Suderman 3382f9f9afaSRob Suderman //----------------------------------------------------------------------------// 3392f9f9afaSRob Suderman // AtanOp approximation. 3402f9f9afaSRob Suderman //----------------------------------------------------------------------------// 3412f9f9afaSRob Suderman 3422f9f9afaSRob Suderman namespace { 3432f9f9afaSRob Suderman struct Atan2Approximation : public OpRewritePattern<math::Atan2Op> { 3442f9f9afaSRob Suderman public: 3452f9f9afaSRob Suderman using OpRewritePattern::OpRewritePattern; 3462f9f9afaSRob Suderman 3472f9f9afaSRob Suderman LogicalResult matchAndRewrite(math::Atan2Op op, 3482f9f9afaSRob Suderman PatternRewriter &rewriter) const final; 3492f9f9afaSRob Suderman }; 3502f9f9afaSRob Suderman } // namespace 3512f9f9afaSRob Suderman 3522f9f9afaSRob Suderman LogicalResult 3532f9f9afaSRob Suderman Atan2Approximation::matchAndRewrite(math::Atan2Op op, 3542f9f9afaSRob Suderman PatternRewriter &rewriter) const { 3552f9f9afaSRob Suderman auto y = op.getOperand(0); 3562f9f9afaSRob Suderman auto x = op.getOperand(1); 3572f9f9afaSRob Suderman if (!getElementTypeOrSelf(x).isF32()) 3582f9f9afaSRob Suderman return rewriter.notifyMatchFailure(op, "unsupported operand type"); 3592f9f9afaSRob Suderman 3602f9f9afaSRob Suderman ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 3612f9f9afaSRob Suderman ArrayRef<int64_t> shape = vectorShape(op.getResult()); 3622f9f9afaSRob Suderman 3632f9f9afaSRob Suderman // Compute atan in the valid range. 3642f9f9afaSRob Suderman auto div = builder.create<arith::DivFOp>(y, x); 3652f9f9afaSRob Suderman auto atan = builder.create<math::AtanOp>(div); 3662f9f9afaSRob Suderman 3672f9f9afaSRob Suderman // Determine what the atan would be for a 180 degree rotation. 3682f9f9afaSRob Suderman auto zero = broadcast(builder, f32Cst(builder, 0.0f), shape); 3692f9f9afaSRob Suderman auto pi = broadcast(builder, f32Cst(builder, 3.14159265359f), shape); 37070ed93ecSMehdi Amini auto addPi = builder.create<arith::AddFOp>(atan, pi); 37170ed93ecSMehdi Amini auto subPi = builder.create<arith::SubFOp>(atan, pi); 37270ed93ecSMehdi Amini auto atanGt = 3732f9f9afaSRob Suderman builder.create<arith::CmpFOp>(arith::CmpFPredicate::OGT, atan, zero); 37470ed93ecSMehdi Amini auto flippedAtan = builder.create<SelectOp>(atanGt, subPi, addPi); 3752f9f9afaSRob Suderman 3762f9f9afaSRob Suderman // Determine whether to directly use atan or use the 180 degree flip 37770ed93ecSMehdi Amini auto xGt = builder.create<arith::CmpFOp>(arith::CmpFPredicate::OGT, x, zero); 37870ed93ecSMehdi Amini Value result = builder.create<SelectOp>(xGt, atan, flippedAtan); 3792f9f9afaSRob Suderman 3802f9f9afaSRob Suderman // Handle x = 0, y > 0 38170ed93ecSMehdi Amini Value xZero = 3822f9f9afaSRob Suderman builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, x, zero); 38370ed93ecSMehdi Amini Value yGt = builder.create<arith::CmpFOp>(arith::CmpFPredicate::OGT, y, zero); 38470ed93ecSMehdi Amini Value isHalfPi = builder.create<arith::AndIOp>(xZero, yGt); 38570ed93ecSMehdi Amini auto halfPi = broadcast(builder, f32Cst(builder, 1.57079632679f), shape); 38670ed93ecSMehdi Amini result = builder.create<SelectOp>(isHalfPi, halfPi, result); 3872f9f9afaSRob Suderman 3882f9f9afaSRob Suderman // Handle x = 0, y < 0 38970ed93ecSMehdi Amini Value yLt = builder.create<arith::CmpFOp>(arith::CmpFPredicate::OLT, y, zero); 39070ed93ecSMehdi Amini Value isNegativeHalfPiPi = builder.create<arith::AndIOp>(xZero, yLt); 39170ed93ecSMehdi Amini auto negativeHalfPiPi = 392dc3b9365SAlexandre Ganea broadcast(builder, f32Cst(builder, -1.57079632679f), shape); 39370ed93ecSMehdi Amini result = 39470ed93ecSMehdi Amini builder.create<SelectOp>(isNegativeHalfPiPi, negativeHalfPiPi, result); 3952f9f9afaSRob Suderman 3962f9f9afaSRob Suderman // Handle x = 0, y = 0; 39770ed93ecSMehdi Amini Value yZero = 3982f9f9afaSRob Suderman builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, y, zero); 39970ed93ecSMehdi Amini Value isNan = builder.create<arith::AndIOp>(xZero, yZero); 40070ed93ecSMehdi Amini Value cstNan = broadcast(builder, f32FromBits(builder, 0x7fc00000), shape); 40170ed93ecSMehdi Amini result = builder.create<SelectOp>(isNan, cstNan, result); 4022f9f9afaSRob Suderman 4032f9f9afaSRob Suderman rewriter.replaceOp(op, result); 4042f9f9afaSRob Suderman return success(); 4052f9f9afaSRob Suderman } 4062f9f9afaSRob Suderman 4072f9f9afaSRob Suderman //----------------------------------------------------------------------------// 408f99ccf65SEugene Zhulenev // TanhOp approximation. 409f99ccf65SEugene Zhulenev //----------------------------------------------------------------------------// 410f99ccf65SEugene Zhulenev 411f99ccf65SEugene Zhulenev namespace { 412f99ccf65SEugene Zhulenev struct TanhApproximation : public OpRewritePattern<math::TanhOp> { 413f99ccf65SEugene Zhulenev public: 414f99ccf65SEugene Zhulenev using OpRewritePattern::OpRewritePattern; 415f99ccf65SEugene Zhulenev 416f99ccf65SEugene Zhulenev LogicalResult matchAndRewrite(math::TanhOp op, 417f99ccf65SEugene Zhulenev PatternRewriter &rewriter) const final; 418f99ccf65SEugene Zhulenev }; 419f99ccf65SEugene Zhulenev } // namespace 420f99ccf65SEugene Zhulenev 421f99ccf65SEugene Zhulenev LogicalResult 422f99ccf65SEugene Zhulenev TanhApproximation::matchAndRewrite(math::TanhOp op, 423f99ccf65SEugene Zhulenev PatternRewriter &rewriter) const { 42462fea88bSJacques Pienaar if (!getElementTypeOrSelf(op.getOperand()).isF32()) 425f99ccf65SEugene Zhulenev return rewriter.notifyMatchFailure(op, "unsupported operand type"); 426f99ccf65SEugene Zhulenev 42762fea88bSJacques Pienaar ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 428ec32d540SEugene Zhulenev 429ce976d2dSEugene Zhulenev ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 430ce976d2dSEugene Zhulenev auto bcast = [&](Value value) -> Value { 431ec32d540SEugene Zhulenev return broadcast(builder, value, shape); 432ce976d2dSEugene Zhulenev }; 433f99ccf65SEugene Zhulenev 434f99ccf65SEugene Zhulenev // Clamp operand into [plusClamp, minusClamp] range. 435bf32bb7eSEugene Zhulenev Value minusClamp = bcast(f32Cst(builder, -7.99881172180175781f)); 436bf32bb7eSEugene Zhulenev Value plusClamp = bcast(f32Cst(builder, 7.99881172180175781f)); 43762fea88bSJacques Pienaar Value x = clamp(builder, op.getOperand(), minusClamp, plusClamp); 438f99ccf65SEugene Zhulenev 439f99ccf65SEugene Zhulenev // Mask for tiny values that are approximated with `operand`. 440ce976d2dSEugene Zhulenev Value tiny = bcast(f32Cst(builder, 0.0004f)); 441a54f4eaeSMogball Value tinyMask = builder.create<arith::CmpFOp>( 44262fea88bSJacques Pienaar arith::CmpFPredicate::OLT, builder.create<math::AbsOp>(op.getOperand()), 443a54f4eaeSMogball tiny); 444f99ccf65SEugene Zhulenev 445f99ccf65SEugene Zhulenev // The monomial coefficients of the numerator polynomial (odd). 446ce976d2dSEugene Zhulenev Value alpha1 = bcast(f32Cst(builder, 4.89352455891786e-03f)); 447ce976d2dSEugene Zhulenev Value alpha3 = bcast(f32Cst(builder, 6.37261928875436e-04f)); 448ce976d2dSEugene Zhulenev Value alpha5 = bcast(f32Cst(builder, 1.48572235717979e-05f)); 449ce976d2dSEugene Zhulenev Value alpha7 = bcast(f32Cst(builder, 5.12229709037114e-08f)); 450ce976d2dSEugene Zhulenev Value alpha9 = bcast(f32Cst(builder, -8.60467152213735e-11f)); 451ce976d2dSEugene Zhulenev Value alpha11 = bcast(f32Cst(builder, 2.00018790482477e-13f)); 452ce976d2dSEugene Zhulenev Value alpha13 = bcast(f32Cst(builder, -2.76076847742355e-16f)); 453f99ccf65SEugene Zhulenev 454f99ccf65SEugene Zhulenev // The monomial coefficients of the denominator polynomial (even). 455ce976d2dSEugene Zhulenev Value beta0 = bcast(f32Cst(builder, 4.89352518554385e-03f)); 456ce976d2dSEugene Zhulenev Value beta2 = bcast(f32Cst(builder, 2.26843463243900e-03f)); 457ce976d2dSEugene Zhulenev Value beta4 = bcast(f32Cst(builder, 1.18534705686654e-04f)); 458ce976d2dSEugene Zhulenev Value beta6 = bcast(f32Cst(builder, 1.19825839466702e-06f)); 459f99ccf65SEugene Zhulenev 460f99ccf65SEugene Zhulenev // Since the polynomials are odd/even, we need x^2. 461a54f4eaeSMogball Value x2 = builder.create<arith::MulFOp>(x, x); 462f99ccf65SEugene Zhulenev 463f99ccf65SEugene Zhulenev // Evaluate the numerator polynomial p. 464a54f4eaeSMogball Value p = builder.create<math::FmaOp>(x2, alpha13, alpha11); 465a54f4eaeSMogball p = builder.create<math::FmaOp>(x2, p, alpha9); 466a54f4eaeSMogball p = builder.create<math::FmaOp>(x2, p, alpha7); 467a54f4eaeSMogball p = builder.create<math::FmaOp>(x2, p, alpha5); 468a54f4eaeSMogball p = builder.create<math::FmaOp>(x2, p, alpha3); 469a54f4eaeSMogball p = builder.create<math::FmaOp>(x2, p, alpha1); 470a54f4eaeSMogball p = builder.create<arith::MulFOp>(x, p); 471f99ccf65SEugene Zhulenev 472f99ccf65SEugene Zhulenev // Evaluate the denominator polynomial q. 473a54f4eaeSMogball Value q = builder.create<math::FmaOp>(x2, beta6, beta4); 474a54f4eaeSMogball q = builder.create<math::FmaOp>(x2, q, beta2); 475a54f4eaeSMogball q = builder.create<math::FmaOp>(x2, q, beta0); 476f99ccf65SEugene Zhulenev 477f99ccf65SEugene Zhulenev // Divide the numerator by the denominator. 478a54f4eaeSMogball Value res = builder.create<SelectOp>(tinyMask, x, 479a54f4eaeSMogball builder.create<arith::DivFOp>(p, q)); 480f99ccf65SEugene Zhulenev 481f99ccf65SEugene Zhulenev rewriter.replaceOp(op, res); 482f99ccf65SEugene Zhulenev 483f99ccf65SEugene Zhulenev return success(); 484f99ccf65SEugene Zhulenev } 485f99ccf65SEugene Zhulenev 486ea7f211bSAhmed Taei #define LN2_VALUE \ 487ea7f211bSAhmed Taei 0.693147180559945309417232121458176568075500134360255254120680009493393621L 488c0891706SEmilio Cota #define LOG2E_VALUE \ 489ea7f211bSAhmed Taei 1.442695040888963407359924681001892137426645954152985934135449406931109219L 490ea7f211bSAhmed Taei 491f99ccf65SEugene Zhulenev //----------------------------------------------------------------------------// 492c0891706SEmilio Cota // LogOp and Log2Op approximation. 493ce976d2dSEugene Zhulenev //----------------------------------------------------------------------------// 494ce976d2dSEugene Zhulenev 495ce976d2dSEugene Zhulenev namespace { 496c0891706SEmilio Cota template <typename Op> 497c0891706SEmilio Cota struct LogApproximationBase : public OpRewritePattern<Op> { 498c0891706SEmilio Cota using OpRewritePattern<Op>::OpRewritePattern; 499ce976d2dSEugene Zhulenev 500c0891706SEmilio Cota /// Base 2 if 'base2' is set; natural logarithm (base e) otherwise. 501c0891706SEmilio Cota LogicalResult logMatchAndRewrite(Op op, PatternRewriter &rewriter, 502c0891706SEmilio Cota bool base2) const; 503ce976d2dSEugene Zhulenev }; 504ce976d2dSEugene Zhulenev } // namespace 505ce976d2dSEugene Zhulenev 506c0891706SEmilio Cota // This approximation comes from Julien Pommier's SSE math library. 507c0891706SEmilio Cota // Link: http://gruntthepeon.free.fr/ssemath 508c0891706SEmilio Cota template <typename Op> 509ce976d2dSEugene Zhulenev LogicalResult 510c0891706SEmilio Cota LogApproximationBase<Op>::logMatchAndRewrite(Op op, PatternRewriter &rewriter, 511c0891706SEmilio Cota bool base2) const { 51262fea88bSJacques Pienaar if (!getElementTypeOrSelf(op.getOperand()).isF32()) 513ce976d2dSEugene Zhulenev return rewriter.notifyMatchFailure(op, "unsupported operand type"); 514ce976d2dSEugene Zhulenev 51562fea88bSJacques Pienaar ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 516ec32d540SEugene Zhulenev 517ce976d2dSEugene Zhulenev ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 518ce976d2dSEugene Zhulenev auto bcast = [&](Value value) -> Value { 519ec32d540SEugene Zhulenev return broadcast(builder, value, shape); 520ce976d2dSEugene Zhulenev }; 521ce976d2dSEugene Zhulenev 522ce976d2dSEugene Zhulenev Value cstZero = bcast(f32Cst(builder, 0.0f)); 523ce976d2dSEugene Zhulenev Value cstOne = bcast(f32Cst(builder, 1.0f)); 524ce976d2dSEugene Zhulenev Value cstNegHalf = bcast(f32Cst(builder, -0.5f)); 525ce976d2dSEugene Zhulenev 526ce976d2dSEugene Zhulenev // The smallest non denormalized float number. 527ce976d2dSEugene Zhulenev Value cstMinNormPos = bcast(f32FromBits(builder, 0x00800000u)); 528ce976d2dSEugene Zhulenev Value cstMinusInf = bcast(f32FromBits(builder, 0xff800000u)); 529ce976d2dSEugene Zhulenev Value cstPosInf = bcast(f32FromBits(builder, 0x7f800000u)); 530ce976d2dSEugene Zhulenev Value cstNan = bcast(f32FromBits(builder, 0x7fc00000)); 531ce976d2dSEugene Zhulenev 532ce976d2dSEugene Zhulenev // Polynomial coefficients. 533ce976d2dSEugene Zhulenev Value cstCephesSQRTHF = bcast(f32Cst(builder, 0.707106781186547524f)); 534ce976d2dSEugene Zhulenev Value cstCephesLogP0 = bcast(f32Cst(builder, 7.0376836292E-2f)); 535ce976d2dSEugene Zhulenev Value cstCephesLogP1 = bcast(f32Cst(builder, -1.1514610310E-1f)); 536ce976d2dSEugene Zhulenev Value cstCephesLogP2 = bcast(f32Cst(builder, 1.1676998740E-1f)); 537ce976d2dSEugene Zhulenev Value cstCephesLogP3 = bcast(f32Cst(builder, -1.2420140846E-1f)); 538ce976d2dSEugene Zhulenev Value cstCephesLogP4 = bcast(f32Cst(builder, +1.4249322787E-1f)); 539ce976d2dSEugene Zhulenev Value cstCephesLogP5 = bcast(f32Cst(builder, -1.6668057665E-1f)); 540ce976d2dSEugene Zhulenev Value cstCephesLogP6 = bcast(f32Cst(builder, +2.0000714765E-1f)); 541ce976d2dSEugene Zhulenev Value cstCephesLogP7 = bcast(f32Cst(builder, -2.4999993993E-1f)); 542ce976d2dSEugene Zhulenev Value cstCephesLogP8 = bcast(f32Cst(builder, +3.3333331174E-1f)); 543ce976d2dSEugene Zhulenev 54462fea88bSJacques Pienaar Value x = op.getOperand(); 545ce976d2dSEugene Zhulenev 546ce976d2dSEugene Zhulenev // Truncate input values to the minimum positive normal. 547ce976d2dSEugene Zhulenev x = max(builder, x, cstMinNormPos); 548ce976d2dSEugene Zhulenev 549ce976d2dSEugene Zhulenev // Extract significant in the range [0.5,1) and exponent. 550ced8690dSMehdi Amini std::pair<Value, Value> pair = frexp(builder, x, /*isPositive=*/true); 551ce976d2dSEugene Zhulenev x = pair.first; 552ce976d2dSEugene Zhulenev Value e = pair.second; 553ce976d2dSEugene Zhulenev 554ce976d2dSEugene Zhulenev // Shift the inputs from the range [0.5,1) to [sqrt(1/2), sqrt(2)) and shift 555ce976d2dSEugene Zhulenev // by -1.0. The values are then centered around 0, which improves the 556ce976d2dSEugene Zhulenev // stability of the polynomial evaluation: 557ce976d2dSEugene Zhulenev // 558ce976d2dSEugene Zhulenev // if( x < SQRTHF ) { 559ce976d2dSEugene Zhulenev // e -= 1; 560ce976d2dSEugene Zhulenev // x = x + x - 1.0; 561ce976d2dSEugene Zhulenev // } else { x = x - 1.0; } 562a54f4eaeSMogball Value mask = builder.create<arith::CmpFOp>(arith::CmpFPredicate::OLT, x, 563a54f4eaeSMogball cstCephesSQRTHF); 564ce976d2dSEugene Zhulenev Value tmp = builder.create<SelectOp>(mask, x, cstZero); 565ce976d2dSEugene Zhulenev 566a54f4eaeSMogball x = builder.create<arith::SubFOp>(x, cstOne); 567a54f4eaeSMogball e = builder.create<arith::SubFOp>( 568a54f4eaeSMogball e, builder.create<SelectOp>(mask, cstOne, cstZero)); 569a54f4eaeSMogball x = builder.create<arith::AddFOp>(x, tmp); 570ce976d2dSEugene Zhulenev 571a54f4eaeSMogball Value x2 = builder.create<arith::MulFOp>(x, x); 572a54f4eaeSMogball Value x3 = builder.create<arith::MulFOp>(x2, x); 573ce976d2dSEugene Zhulenev 574ce976d2dSEugene Zhulenev // Evaluate the polynomial approximant of degree 8 in three parts. 575ce976d2dSEugene Zhulenev Value y0, y1, y2; 576a54f4eaeSMogball y0 = builder.create<math::FmaOp>(cstCephesLogP0, x, cstCephesLogP1); 577a54f4eaeSMogball y1 = builder.create<math::FmaOp>(cstCephesLogP3, x, cstCephesLogP4); 578a54f4eaeSMogball y2 = builder.create<math::FmaOp>(cstCephesLogP6, x, cstCephesLogP7); 579a54f4eaeSMogball y0 = builder.create<math::FmaOp>(y0, x, cstCephesLogP2); 580a54f4eaeSMogball y1 = builder.create<math::FmaOp>(y1, x, cstCephesLogP5); 581a54f4eaeSMogball y2 = builder.create<math::FmaOp>(y2, x, cstCephesLogP8); 582a54f4eaeSMogball y0 = builder.create<math::FmaOp>(y0, x3, y1); 583a54f4eaeSMogball y0 = builder.create<math::FmaOp>(y0, x3, y2); 584a54f4eaeSMogball y0 = builder.create<arith::MulFOp>(y0, x3); 585ce976d2dSEugene Zhulenev 586a54f4eaeSMogball y0 = builder.create<math::FmaOp>(cstNegHalf, x2, y0); 587a54f4eaeSMogball x = builder.create<arith::AddFOp>(x, y0); 588ce976d2dSEugene Zhulenev 589c0891706SEmilio Cota if (base2) { 590c0891706SEmilio Cota Value cstLog2e = bcast(f32Cst(builder, static_cast<float>(LOG2E_VALUE))); 591a54f4eaeSMogball x = builder.create<math::FmaOp>(x, cstLog2e, e); 592c0891706SEmilio Cota } else { 593ce976d2dSEugene Zhulenev Value cstLn2 = bcast(f32Cst(builder, static_cast<float>(LN2_VALUE))); 594a54f4eaeSMogball x = builder.create<math::FmaOp>(e, cstLn2, x); 595c0891706SEmilio Cota } 596ce976d2dSEugene Zhulenev 597a54f4eaeSMogball Value invalidMask = builder.create<arith::CmpFOp>(arith::CmpFPredicate::ULT, 59862fea88bSJacques Pienaar op.getOperand(), cstZero); 599a54f4eaeSMogball Value zeroMask = builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, 60062fea88bSJacques Pienaar op.getOperand(), cstZero); 601a54f4eaeSMogball Value posInfMask = builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, 60262fea88bSJacques Pienaar op.getOperand(), cstPosInf); 603ce976d2dSEugene Zhulenev 604ce976d2dSEugene Zhulenev // Filter out invalid values: 605ce976d2dSEugene Zhulenev // • x == 0 -> -INF 606ce976d2dSEugene Zhulenev // • x < 0 -> NAN 607ce976d2dSEugene Zhulenev // • x == +INF -> +INF 608ce976d2dSEugene Zhulenev Value aproximation = builder.create<SelectOp>( 609ce976d2dSEugene Zhulenev zeroMask, cstMinusInf, 610ce976d2dSEugene Zhulenev builder.create<SelectOp>( 611ce976d2dSEugene Zhulenev invalidMask, cstNan, 612ce976d2dSEugene Zhulenev builder.create<SelectOp>(posInfMask, cstPosInf, x))); 613ce976d2dSEugene Zhulenev 614ce976d2dSEugene Zhulenev rewriter.replaceOp(op, aproximation); 615ce976d2dSEugene Zhulenev 616ce976d2dSEugene Zhulenev return success(); 617ce976d2dSEugene Zhulenev } 618ce976d2dSEugene Zhulenev 619c0891706SEmilio Cota namespace { 620c0891706SEmilio Cota struct LogApproximation : public LogApproximationBase<math::LogOp> { 621c0891706SEmilio Cota using LogApproximationBase::LogApproximationBase; 622c0891706SEmilio Cota 623c0891706SEmilio Cota LogicalResult matchAndRewrite(math::LogOp op, 624c0891706SEmilio Cota PatternRewriter &rewriter) const final { 625c0891706SEmilio Cota return logMatchAndRewrite(op, rewriter, /*base2=*/false); 626c0891706SEmilio Cota } 627c0891706SEmilio Cota }; 628c0891706SEmilio Cota } // namespace 629c0891706SEmilio Cota 630c0891706SEmilio Cota namespace { 631c0891706SEmilio Cota struct Log2Approximation : public LogApproximationBase<math::Log2Op> { 632c0891706SEmilio Cota using LogApproximationBase::LogApproximationBase; 633c0891706SEmilio Cota 634c0891706SEmilio Cota LogicalResult matchAndRewrite(math::Log2Op op, 635c0891706SEmilio Cota PatternRewriter &rewriter) const final { 636c0891706SEmilio Cota return logMatchAndRewrite(op, rewriter, /*base2=*/true); 637c0891706SEmilio Cota } 638c0891706SEmilio Cota }; 639c0891706SEmilio Cota } // namespace 640c0891706SEmilio Cota 641ce976d2dSEugene Zhulenev //----------------------------------------------------------------------------// 6421c0374e7SEmilio Cota // Log1p approximation. 6431c0374e7SEmilio Cota //----------------------------------------------------------------------------// 6441c0374e7SEmilio Cota 6451c0374e7SEmilio Cota namespace { 6461c0374e7SEmilio Cota struct Log1pApproximation : public OpRewritePattern<math::Log1pOp> { 6471c0374e7SEmilio Cota public: 6481c0374e7SEmilio Cota using OpRewritePattern::OpRewritePattern; 6491c0374e7SEmilio Cota 6501c0374e7SEmilio Cota LogicalResult matchAndRewrite(math::Log1pOp op, 6511c0374e7SEmilio Cota PatternRewriter &rewriter) const final; 6521c0374e7SEmilio Cota }; 6531c0374e7SEmilio Cota } // namespace 6541c0374e7SEmilio Cota 6551c0374e7SEmilio Cota // Approximate log(1+x). 6561c0374e7SEmilio Cota LogicalResult 6571c0374e7SEmilio Cota Log1pApproximation::matchAndRewrite(math::Log1pOp op, 6581c0374e7SEmilio Cota PatternRewriter &rewriter) const { 65962fea88bSJacques Pienaar if (!getElementTypeOrSelf(op.getOperand()).isF32()) 6601c0374e7SEmilio Cota return rewriter.notifyMatchFailure(op, "unsupported operand type"); 6611c0374e7SEmilio Cota 66262fea88bSJacques Pienaar ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 663ec32d540SEugene Zhulenev 6641c0374e7SEmilio Cota ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 6651c0374e7SEmilio Cota auto bcast = [&](Value value) -> Value { 666ec32d540SEugene Zhulenev return broadcast(builder, value, shape); 6671c0374e7SEmilio Cota }; 6681c0374e7SEmilio Cota 6691c0374e7SEmilio Cota // Approximate log(1+x) using the following, due to W. Kahan: 6701c0374e7SEmilio Cota // u = x + 1.0; 6711c0374e7SEmilio Cota // if (u == 1.0 || u == inf) return x; 6721c0374e7SEmilio Cota // return x * log(u) / (u - 1.0); 6731c0374e7SEmilio Cota // ^^^^^^^^^^^^^^^^^^^^^^ 6741c0374e7SEmilio Cota // "logLarge" below. 6751c0374e7SEmilio Cota Value cstOne = bcast(f32Cst(builder, 1.0f)); 67662fea88bSJacques Pienaar Value x = op.getOperand(); 677a54f4eaeSMogball Value u = builder.create<arith::AddFOp>(x, cstOne); 678a54f4eaeSMogball Value uSmall = 679a54f4eaeSMogball builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, u, cstOne); 6801c0374e7SEmilio Cota Value logU = builder.create<math::LogOp>(u); 681a54f4eaeSMogball Value uInf = 682a54f4eaeSMogball builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, u, logU); 683a54f4eaeSMogball Value logLarge = builder.create<arith::MulFOp>( 684a54f4eaeSMogball x, builder.create<arith::DivFOp>( 685a54f4eaeSMogball logU, builder.create<arith::SubFOp>(u, cstOne))); 686a54f4eaeSMogball Value approximation = builder.create<SelectOp>( 687a54f4eaeSMogball builder.create<arith::OrIOp>(uSmall, uInf), x, logLarge); 6881c0374e7SEmilio Cota rewriter.replaceOp(op, approximation); 6891c0374e7SEmilio Cota return success(); 6901c0374e7SEmilio Cota } 6911c0374e7SEmilio Cota 6921c0374e7SEmilio Cota //----------------------------------------------------------------------------// 693f1b92218SBoian Petkantchin // Erf approximation. 694f1b92218SBoian Petkantchin //----------------------------------------------------------------------------// 695f1b92218SBoian Petkantchin 696f1b92218SBoian Petkantchin // Approximates erf(x) with 697f1b92218SBoian Petkantchin // a - P(x)/Q(x) 698f1b92218SBoian Petkantchin // where P and Q are polynomials of degree 4. 699f1b92218SBoian Petkantchin // Different coefficients are chosen based on the value of x. 700f1b92218SBoian Petkantchin // The approximation error is ~2.5e-07. 701f1b92218SBoian Petkantchin // Boost's minimax tool that utilizes the Remez method was used to find the 702f1b92218SBoian Petkantchin // coefficients. 703f1b92218SBoian Petkantchin LogicalResult 704f1b92218SBoian Petkantchin ErfPolynomialApproximation::matchAndRewrite(math::ErfOp op, 705f1b92218SBoian Petkantchin PatternRewriter &rewriter) const { 70662fea88bSJacques Pienaar if (!getElementTypeOrSelf(op.getOperand()).isF32()) 707f1b92218SBoian Petkantchin return rewriter.notifyMatchFailure(op, "unsupported operand type"); 708f1b92218SBoian Petkantchin 70962fea88bSJacques Pienaar ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 710ec32d540SEugene Zhulenev 711f1b92218SBoian Petkantchin ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 712f1b92218SBoian Petkantchin auto bcast = [&](Value value) -> Value { 713ec32d540SEugene Zhulenev return broadcast(builder, value, shape); 714f1b92218SBoian Petkantchin }; 715f1b92218SBoian Petkantchin 716f1b92218SBoian Petkantchin const int intervalsCount = 3; 717f1b92218SBoian Petkantchin const int polyDegree = 4; 718f1b92218SBoian Petkantchin 719f1b92218SBoian Petkantchin Value zero = bcast(f32Cst(builder, 0)); 720f1b92218SBoian Petkantchin Value one = bcast(f32Cst(builder, 1)); 721f1b92218SBoian Petkantchin Value pp[intervalsCount][polyDegree + 1]; 7227d79a257SAlexander Belyaev pp[0][0] = bcast(f32Cst(builder, +0.00000000000000000e+00f)); 7237d79a257SAlexander Belyaev pp[0][1] = bcast(f32Cst(builder, +1.12837916222975858e+00f)); 7247d79a257SAlexander Belyaev pp[0][2] = bcast(f32Cst(builder, -5.23018562988006470e-01f)); 7257d79a257SAlexander Belyaev pp[0][3] = bcast(f32Cst(builder, +2.09741709609267072e-01f)); 7267d79a257SAlexander Belyaev pp[0][4] = bcast(f32Cst(builder, +2.58146801602987875e-02f)); 7277d79a257SAlexander Belyaev pp[1][0] = bcast(f32Cst(builder, +0.00000000000000000e+00f)); 7287d79a257SAlexander Belyaev pp[1][1] = bcast(f32Cst(builder, +1.12750687816789140e+00f)); 7297d79a257SAlexander Belyaev pp[1][2] = bcast(f32Cst(builder, -3.64721408487825775e-01f)); 7307d79a257SAlexander Belyaev pp[1][3] = bcast(f32Cst(builder, +1.18407396425136952e-01f)); 7317d79a257SAlexander Belyaev pp[1][4] = bcast(f32Cst(builder, +3.70645533056476558e-02f)); 7327d79a257SAlexander Belyaev pp[2][0] = bcast(f32Cst(builder, -3.30093071049483172e-03f)); 7337d79a257SAlexander Belyaev pp[2][1] = bcast(f32Cst(builder, +3.51961938357697011e-03f)); 7347d79a257SAlexander Belyaev pp[2][2] = bcast(f32Cst(builder, -1.41373622814988039e-03f)); 7357d79a257SAlexander Belyaev pp[2][3] = bcast(f32Cst(builder, +2.53447094961941348e-04f)); 7367d79a257SAlexander Belyaev pp[2][4] = bcast(f32Cst(builder, -1.71048029455037401e-05f)); 737f1b92218SBoian Petkantchin 738f1b92218SBoian Petkantchin Value qq[intervalsCount][polyDegree + 1]; 7397d79a257SAlexander Belyaev qq[0][0] = bcast(f32Cst(builder, +1.000000000000000000e+00f)); 7407d79a257SAlexander Belyaev qq[0][1] = bcast(f32Cst(builder, -4.635138185962547255e-01f)); 7417d79a257SAlexander Belyaev qq[0][2] = bcast(f32Cst(builder, +5.192301327279782447e-01f)); 7427d79a257SAlexander Belyaev qq[0][3] = bcast(f32Cst(builder, -1.318089722204810087e-01f)); 7437d79a257SAlexander Belyaev qq[0][4] = bcast(f32Cst(builder, +7.397964654672315005e-02f)); 7447d79a257SAlexander Belyaev qq[1][0] = bcast(f32Cst(builder, +1.00000000000000000e+00f)); 7457d79a257SAlexander Belyaev qq[1][1] = bcast(f32Cst(builder, -3.27607011824493086e-01f)); 7467d79a257SAlexander Belyaev qq[1][2] = bcast(f32Cst(builder, +4.48369090658821977e-01f)); 7477d79a257SAlexander Belyaev qq[1][3] = bcast(f32Cst(builder, -8.83462621207857930e-02f)); 7487d79a257SAlexander Belyaev qq[1][4] = bcast(f32Cst(builder, +5.72442770283176093e-02f)); 7497d79a257SAlexander Belyaev qq[2][0] = bcast(f32Cst(builder, +1.00000000000000000e+00f)); 7507d79a257SAlexander Belyaev qq[2][1] = bcast(f32Cst(builder, -2.06069165953913769e+00f)); 7517d79a257SAlexander Belyaev qq[2][2] = bcast(f32Cst(builder, +1.62705939945477759e+00f)); 7527d79a257SAlexander Belyaev qq[2][3] = bcast(f32Cst(builder, -5.83389859211130017e-01f)); 7537d79a257SAlexander Belyaev qq[2][4] = bcast(f32Cst(builder, +8.21908939856640930e-02f)); 754f1b92218SBoian Petkantchin 755f1b92218SBoian Petkantchin Value offsets[intervalsCount]; 7567d79a257SAlexander Belyaev offsets[0] = bcast(f32Cst(builder, 0.0f)); 7577d79a257SAlexander Belyaev offsets[1] = bcast(f32Cst(builder, 0.0f)); 7587d79a257SAlexander Belyaev offsets[2] = bcast(f32Cst(builder, 1.0f)); 759f1b92218SBoian Petkantchin 760f1b92218SBoian Petkantchin Value bounds[intervalsCount]; 7617d79a257SAlexander Belyaev bounds[0] = bcast(f32Cst(builder, 0.8f)); 7627d79a257SAlexander Belyaev bounds[1] = bcast(f32Cst(builder, 2.0f)); 7637d79a257SAlexander Belyaev bounds[2] = bcast(f32Cst(builder, 3.75f)); 764f1b92218SBoian Petkantchin 765f1b92218SBoian Petkantchin Value isNegativeArg = builder.create<arith::CmpFOp>(arith::CmpFPredicate::OLT, 76662fea88bSJacques Pienaar op.getOperand(), zero); 76762fea88bSJacques Pienaar Value negArg = builder.create<arith::NegFOp>(op.getOperand()); 76862fea88bSJacques Pienaar Value x = builder.create<SelectOp>(isNegativeArg, negArg, op.getOperand()); 769f1b92218SBoian Petkantchin 770f1b92218SBoian Petkantchin Value offset = offsets[0]; 771f1b92218SBoian Petkantchin Value p[polyDegree + 1]; 772f1b92218SBoian Petkantchin Value q[polyDegree + 1]; 773f1b92218SBoian Petkantchin for (int i = 0; i <= polyDegree; ++i) { 774f1b92218SBoian Petkantchin p[i] = pp[0][i]; 775f1b92218SBoian Petkantchin q[i] = qq[0][i]; 776f1b92218SBoian Petkantchin } 777f1b92218SBoian Petkantchin 778f1b92218SBoian Petkantchin // TODO: maybe use vector stacking to reduce the number of selects. 779f1b92218SBoian Petkantchin Value isLessThanBound[intervalsCount]; 780f1b92218SBoian Petkantchin for (int j = 0; j < intervalsCount - 1; ++j) { 781f1b92218SBoian Petkantchin isLessThanBound[j] = 782f1b92218SBoian Petkantchin builder.create<arith::CmpFOp>(arith::CmpFPredicate::OLT, x, bounds[j]); 783f1b92218SBoian Petkantchin for (int i = 0; i <= polyDegree; ++i) { 784f1b92218SBoian Petkantchin p[i] = builder.create<SelectOp>(isLessThanBound[j], p[i], pp[j + 1][i]); 785f1b92218SBoian Petkantchin q[i] = builder.create<SelectOp>(isLessThanBound[j], q[i], qq[j + 1][i]); 786f1b92218SBoian Petkantchin } 787f1b92218SBoian Petkantchin offset = 788f1b92218SBoian Petkantchin builder.create<SelectOp>(isLessThanBound[j], offset, offsets[j + 1]); 789f1b92218SBoian Petkantchin } 790f1b92218SBoian Petkantchin isLessThanBound[intervalsCount - 1] = builder.create<arith::CmpFOp>( 791f1b92218SBoian Petkantchin arith::CmpFPredicate::ULT, x, bounds[intervalsCount - 1]); 792f1b92218SBoian Petkantchin 793f1b92218SBoian Petkantchin Value pPoly = makePolynomialCalculation(builder, p, x); 794f1b92218SBoian Petkantchin Value qPoly = makePolynomialCalculation(builder, q, x); 795f1b92218SBoian Petkantchin Value rationalPoly = builder.create<arith::DivFOp>(pPoly, qPoly); 796f1b92218SBoian Petkantchin Value formula = builder.create<arith::AddFOp>(offset, rationalPoly); 797f1b92218SBoian Petkantchin formula = builder.create<SelectOp>(isLessThanBound[intervalsCount - 1], 798f1b92218SBoian Petkantchin formula, one); 799f1b92218SBoian Petkantchin 800f1b92218SBoian Petkantchin // erf is odd function: erf(x) = -erf(-x). 801f1b92218SBoian Petkantchin Value negFormula = builder.create<arith::NegFOp>(formula); 802f1b92218SBoian Petkantchin Value res = builder.create<SelectOp>(isNegativeArg, negFormula, formula); 803f1b92218SBoian Petkantchin 804f1b92218SBoian Petkantchin rewriter.replaceOp(op, res); 805f1b92218SBoian Petkantchin 806f1b92218SBoian Petkantchin return success(); 807f1b92218SBoian Petkantchin } 808f1b92218SBoian Petkantchin 809f1b92218SBoian Petkantchin //----------------------------------------------------------------------------// 810ea7f211bSAhmed Taei // Exp approximation. 811ea7f211bSAhmed Taei //----------------------------------------------------------------------------// 812ea7f211bSAhmed Taei 813ea7f211bSAhmed Taei namespace { 814ea7f211bSAhmed Taei 815ea7f211bSAhmed Taei struct ExpApproximation : public OpRewritePattern<math::ExpOp> { 816ea7f211bSAhmed Taei public: 817ea7f211bSAhmed Taei using OpRewritePattern::OpRewritePattern; 818ea7f211bSAhmed Taei 819ea7f211bSAhmed Taei LogicalResult matchAndRewrite(math::ExpOp op, 820ea7f211bSAhmed Taei PatternRewriter &rewriter) const final; 821ea7f211bSAhmed Taei }; 822ea7f211bSAhmed Taei } // namespace 823ea7f211bSAhmed Taei 824ea7f211bSAhmed Taei // Approximate exp(x) using its reduced range exp(y) where y is in the range 825ea7f211bSAhmed Taei // [0, ln(2)], let y = x - floor(x / ln(2)) * ln(2) = x - k * ln(2), exp(x) 826ea7f211bSAhmed Taei // = exp(y) * 2^k. exp(y). 827ea7f211bSAhmed Taei LogicalResult 828ea7f211bSAhmed Taei ExpApproximation::matchAndRewrite(math::ExpOp op, 829ea7f211bSAhmed Taei PatternRewriter &rewriter) const { 83062fea88bSJacques Pienaar if (!getElementTypeOrSelf(op.getOperand()).isF32()) 831ea7f211bSAhmed Taei return rewriter.notifyMatchFailure(op, "unsupported operand type"); 832ec32d540SEugene Zhulenev 83362fea88bSJacques Pienaar ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 834ec32d540SEugene Zhulenev 835ea7f211bSAhmed Taei ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 836ea7f211bSAhmed Taei 837ea7f211bSAhmed Taei // TODO: Consider a common pattern rewriter with all methods below to 838ea7f211bSAhmed Taei // write the approximations. 839ea7f211bSAhmed Taei auto bcast = [&](Value value) -> Value { 840ec32d540SEugene Zhulenev return broadcast(builder, value, shape); 841ea7f211bSAhmed Taei }; 842ea7f211bSAhmed Taei auto fmla = [&](Value a, Value b, Value c) { 843a54f4eaeSMogball return builder.create<math::FmaOp>(a, b, c); 844ea7f211bSAhmed Taei }; 845ea7f211bSAhmed Taei auto mul = [&](Value a, Value b) -> Value { 846a54f4eaeSMogball return builder.create<arith::MulFOp>(a, b); 847ea7f211bSAhmed Taei }; 848ea7f211bSAhmed Taei auto sub = [&](Value a, Value b) -> Value { 849a54f4eaeSMogball return builder.create<arith::SubFOp>(a, b); 850ea7f211bSAhmed Taei }; 851a54f4eaeSMogball auto floor = [&](Value a) { return builder.create<math::FloorOp>(a); }; 852ea7f211bSAhmed Taei 853ea7f211bSAhmed Taei Value cstLn2 = bcast(f32Cst(builder, static_cast<float>(LN2_VALUE))); 854c0891706SEmilio Cota Value cstLog2E = bcast(f32Cst(builder, static_cast<float>(LOG2E_VALUE))); 855ea7f211bSAhmed Taei 856ea7f211bSAhmed Taei // Polynomial coefficients. 857ea7f211bSAhmed Taei Value cstCephesExpP0 = bcast(f32Cst(builder, 1.0)); 858ea7f211bSAhmed Taei Value cstCephesExpP1 = bcast(f32Cst(builder, 1.0)); 859ea7f211bSAhmed Taei Value cstCephesExpP2 = bcast(f32Cst(builder, 0.49970514590562437052f)); 860ea7f211bSAhmed Taei Value cstCephesExpP3 = bcast(f32Cst(builder, 0.16873890085469545053f)); 861ea7f211bSAhmed Taei Value cstCephesExpP4 = bcast(f32Cst(builder, 0.03668965196652099192f)); 862ea7f211bSAhmed Taei Value cstCephesExpP5 = bcast(f32Cst(builder, 0.01314350012789660196f)); 863ea7f211bSAhmed Taei 86462fea88bSJacques Pienaar Value x = op.getOperand(); 865ea7f211bSAhmed Taei 866ea7f211bSAhmed Taei // Reduced y = x - floor(x / ln(2)) * ln(2) = x - k * ln(2) 867c0891706SEmilio Cota Value xL2Inv = mul(x, cstLog2E); 868ea7f211bSAhmed Taei Value kF32 = floor(xL2Inv); 869ea7f211bSAhmed Taei Value kLn2 = mul(kF32, cstLn2); 870ea7f211bSAhmed Taei Value y = sub(x, kLn2); 871ea7f211bSAhmed Taei 872ea7f211bSAhmed Taei // Use Estrin's evaluation scheme with 3 independent parts: 873ea7f211bSAhmed Taei // P(y)^y : (c0 + c1 y) + (c2 + c3 y) y^2 + (c4 + c5 y) y^4 874ea7f211bSAhmed Taei Value y2 = mul(y, y); 875ea7f211bSAhmed Taei Value y4 = mul(y2, y2); 876ea7f211bSAhmed Taei 877ea7f211bSAhmed Taei Value q0 = fmla(cstCephesExpP1, y, cstCephesExpP0); 878ea7f211bSAhmed Taei Value q1 = fmla(cstCephesExpP3, y, cstCephesExpP2); 879ea7f211bSAhmed Taei Value q2 = fmla(cstCephesExpP5, y, cstCephesExpP4); 880ea7f211bSAhmed Taei Value expY = fmla(q1, y2, q0); 881ea7f211bSAhmed Taei expY = fmla(q2, y4, expY); 882ea7f211bSAhmed Taei 883ec32d540SEugene Zhulenev auto i32Vec = broadcast(builder.getI32Type(), shape); 884ea7f211bSAhmed Taei 885ea7f211bSAhmed Taei // exp2(k) 886a54f4eaeSMogball Value k = builder.create<arith::FPToSIOp>(kF32, i32Vec); 887ea7f211bSAhmed Taei Value exp2KValue = exp2I32(builder, k); 888ea7f211bSAhmed Taei 889ea7f211bSAhmed Taei // exp(x) = exp(y) * exp2(k) 890ea7f211bSAhmed Taei expY = mul(expY, exp2KValue); 891ea7f211bSAhmed Taei 892ea7f211bSAhmed Taei // Handle overflow, inf and underflow of exp(x). exp(x) range is [0, inf], its 893ea7f211bSAhmed Taei // partitioned as the following: 894ea7f211bSAhmed Taei // exp(x) = 0, x <= -inf 895ea7f211bSAhmed Taei // exp(x) = underflow (min_float), x <= -88 896ea7f211bSAhmed Taei // exp(x) = inf (min_float), x >= 88 897ea7f211bSAhmed Taei // Note: |k| = 127 is the value where the 8-bits exponent saturates. 898ea7f211bSAhmed Taei Value zerof32Const = bcast(f32Cst(builder, 0)); 899ea7f211bSAhmed Taei auto constPosInfinity = 900ea7f211bSAhmed Taei bcast(f32Cst(builder, std::numeric_limits<float>::infinity())); 901ea7f211bSAhmed Taei auto constNegIfinity = 902ea7f211bSAhmed Taei bcast(f32Cst(builder, -std::numeric_limits<float>::infinity())); 903ea7f211bSAhmed Taei auto underflow = bcast(f32Cst(builder, std::numeric_limits<float>::min())); 904ea7f211bSAhmed Taei 905ea7f211bSAhmed Taei Value kMaxConst = bcast(i32Cst(builder, 127)); 906ea7f211bSAhmed Taei Value kMaxNegConst = bcast(i32Cst(builder, -127)); 907a54f4eaeSMogball Value rightBound = 908a54f4eaeSMogball builder.create<arith::CmpIOp>(arith::CmpIPredicate::sle, k, kMaxConst); 909a54f4eaeSMogball Value leftBound = 910a54f4eaeSMogball builder.create<arith::CmpIOp>(arith::CmpIPredicate::sge, k, kMaxNegConst); 911ea7f211bSAhmed Taei 912a54f4eaeSMogball Value isNegInfinityX = builder.create<arith::CmpFOp>( 913a54f4eaeSMogball arith::CmpFPredicate::OEQ, x, constNegIfinity); 91421f9e4a1SAhmed Taei Value isPosInfinityX = builder.create<arith::CmpFOp>( 91521f9e4a1SAhmed Taei arith::CmpFPredicate::OEQ, x, constPosInfinity); 916ea7f211bSAhmed Taei Value isPostiveX = 917a54f4eaeSMogball builder.create<arith::CmpFOp>(arith::CmpFPredicate::OGT, x, zerof32Const); 918a54f4eaeSMogball Value isComputable = builder.create<arith::AndIOp>(rightBound, leftBound); 919ea7f211bSAhmed Taei 920ea7f211bSAhmed Taei expY = builder.create<SelectOp>( 92121f9e4a1SAhmed Taei isNegInfinityX, zerof32Const, 922ea7f211bSAhmed Taei builder.create<SelectOp>( 92321f9e4a1SAhmed Taei isPosInfinityX, constPosInfinity, 92421f9e4a1SAhmed Taei builder.create<SelectOp>(isComputable, expY, 92521f9e4a1SAhmed Taei builder.create<SelectOp>(isPostiveX, 92621f9e4a1SAhmed Taei constPosInfinity, 92721f9e4a1SAhmed Taei underflow)))); 928ea7f211bSAhmed Taei 929ea7f211bSAhmed Taei rewriter.replaceOp(op, expY); 930ea7f211bSAhmed Taei 931ea7f211bSAhmed Taei return success(); 932ea7f211bSAhmed Taei } 933ea7f211bSAhmed Taei 934ea7f211bSAhmed Taei //----------------------------------------------------------------------------// 9350edc4bc8SEmilio Cota // ExpM1 approximation. 9360edc4bc8SEmilio Cota //----------------------------------------------------------------------------// 9370edc4bc8SEmilio Cota 9380edc4bc8SEmilio Cota namespace { 9390edc4bc8SEmilio Cota 9400edc4bc8SEmilio Cota struct ExpM1Approximation : public OpRewritePattern<math::ExpM1Op> { 9410edc4bc8SEmilio Cota public: 9420edc4bc8SEmilio Cota using OpRewritePattern::OpRewritePattern; 9430edc4bc8SEmilio Cota 9440edc4bc8SEmilio Cota LogicalResult matchAndRewrite(math::ExpM1Op op, 9450edc4bc8SEmilio Cota PatternRewriter &rewriter) const final; 9460edc4bc8SEmilio Cota }; 9470edc4bc8SEmilio Cota } // namespace 9480edc4bc8SEmilio Cota 9490edc4bc8SEmilio Cota LogicalResult 9500edc4bc8SEmilio Cota ExpM1Approximation::matchAndRewrite(math::ExpM1Op op, 9510edc4bc8SEmilio Cota PatternRewriter &rewriter) const { 95262fea88bSJacques Pienaar if (!getElementTypeOrSelf(op.getOperand()).isF32()) 9530edc4bc8SEmilio Cota return rewriter.notifyMatchFailure(op, "unsupported operand type"); 9540edc4bc8SEmilio Cota 95562fea88bSJacques Pienaar ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 956ec32d540SEugene Zhulenev 9570edc4bc8SEmilio Cota ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 9580edc4bc8SEmilio Cota auto bcast = [&](Value value) -> Value { 959ec32d540SEugene Zhulenev return broadcast(builder, value, shape); 9600edc4bc8SEmilio Cota }; 9610edc4bc8SEmilio Cota 9620edc4bc8SEmilio Cota // expm1(x) = exp(x) - 1 = u - 1. 9630edc4bc8SEmilio Cota // We have to handle it carefully when x is near 0, i.e. u ~= 1, 9640edc4bc8SEmilio Cota // and when the input is ~= -inf, i.e. u - 1 ~= -1. 9650edc4bc8SEmilio Cota Value cstOne = bcast(f32Cst(builder, 1.0f)); 9660edc4bc8SEmilio Cota Value cstNegOne = bcast(f32Cst(builder, -1.0f)); 96762fea88bSJacques Pienaar Value x = op.getOperand(); 9680edc4bc8SEmilio Cota Value u = builder.create<math::ExpOp>(x); 969a54f4eaeSMogball Value uEqOne = 970a54f4eaeSMogball builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, u, cstOne); 971a54f4eaeSMogball Value uMinusOne = builder.create<arith::SubFOp>(u, cstOne); 972a54f4eaeSMogball Value uMinusOneEqNegOne = builder.create<arith::CmpFOp>( 973a54f4eaeSMogball arith::CmpFPredicate::OEQ, uMinusOne, cstNegOne); 9740edc4bc8SEmilio Cota // logU = log(u) ~= x 9750edc4bc8SEmilio Cota Value logU = builder.create<math::LogOp>(u); 9760edc4bc8SEmilio Cota 9770edc4bc8SEmilio Cota // Detect exp(x) = +inf; written this way to avoid having to form +inf. 978a54f4eaeSMogball Value isInf = 979a54f4eaeSMogball builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, logU, u); 9800edc4bc8SEmilio Cota 9810edc4bc8SEmilio Cota // (u - 1) * (x / ~x) 982a54f4eaeSMogball Value expm1 = builder.create<arith::MulFOp>( 983a54f4eaeSMogball uMinusOne, builder.create<arith::DivFOp>(x, logU)); 9840edc4bc8SEmilio Cota expm1 = builder.create<SelectOp>(isInf, u, expm1); 9850edc4bc8SEmilio Cota Value approximation = builder.create<SelectOp>( 9860edc4bc8SEmilio Cota uEqOne, x, builder.create<SelectOp>(uMinusOneEqNegOne, cstNegOne, expm1)); 9870edc4bc8SEmilio Cota rewriter.replaceOp(op, approximation); 9880edc4bc8SEmilio Cota return success(); 9890edc4bc8SEmilio Cota } 9900edc4bc8SEmilio Cota 9910edc4bc8SEmilio Cota //----------------------------------------------------------------------------// 9927e2d672aSAhmed S. Taei // Sin and Cos approximation. 9937e2d672aSAhmed S. Taei //----------------------------------------------------------------------------// 9947e2d672aSAhmed S. Taei 9957e2d672aSAhmed S. Taei namespace { 9967e2d672aSAhmed S. Taei 9977e2d672aSAhmed S. Taei template <bool isSine, typename OpTy> 9987e2d672aSAhmed S. Taei struct SinAndCosApproximation : public OpRewritePattern<OpTy> { 9997e2d672aSAhmed S. Taei public: 10007e2d672aSAhmed S. Taei using OpRewritePattern<OpTy>::OpRewritePattern; 10017e2d672aSAhmed S. Taei 10027e2d672aSAhmed S. Taei LogicalResult matchAndRewrite(OpTy op, PatternRewriter &rewriter) const final; 10037e2d672aSAhmed S. Taei }; 10047e2d672aSAhmed S. Taei } // namespace 10057e2d672aSAhmed S. Taei 10067e2d672aSAhmed S. Taei #define TWO_OVER_PI \ 10077e2d672aSAhmed S. Taei 0.6366197723675813430755350534900574481378385829618257949906693762L 10087e2d672aSAhmed S. Taei #define PI_OVER_2 \ 10097e2d672aSAhmed S. Taei 1.5707963267948966192313216916397514420985846996875529104874722961L 10107e2d672aSAhmed S. Taei 10117e2d672aSAhmed S. Taei // Approximates sin(x) or cos(x) by finding the best approximation polynomial in 10127e2d672aSAhmed S. Taei // the reduced range [0, pi/2] for both sin(x) and cos(x). Then given y in the 10137e2d672aSAhmed S. Taei // reduced range sin(x) will be computed as sin(y), -sin(y), cos(y) or -cos(y). 10147e2d672aSAhmed S. Taei template <bool isSine, typename OpTy> 10157e2d672aSAhmed S. Taei LogicalResult SinAndCosApproximation<isSine, OpTy>::matchAndRewrite( 10167e2d672aSAhmed S. Taei OpTy op, PatternRewriter &rewriter) const { 10177e2d672aSAhmed S. Taei static_assert( 10187e2d672aSAhmed S. Taei llvm::is_one_of<OpTy, math::SinOp, math::CosOp>::value, 10197e2d672aSAhmed S. Taei "SinAndCosApproximation pattern expects math::SinOp or math::CosOp"); 1020ec32d540SEugene Zhulenev 102162fea88bSJacques Pienaar if (!getElementTypeOrSelf(op.getOperand()).isF32()) 10227e2d672aSAhmed S. Taei return rewriter.notifyMatchFailure(op, "unsupported operand type"); 10237e2d672aSAhmed S. Taei 102462fea88bSJacques Pienaar ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 1025ec32d540SEugene Zhulenev 10267e2d672aSAhmed S. Taei ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 10277e2d672aSAhmed S. Taei auto bcast = [&](Value value) -> Value { 1028ec32d540SEugene Zhulenev return broadcast(builder, value, shape); 10297e2d672aSAhmed S. Taei }; 10307e2d672aSAhmed S. Taei auto mul = [&](Value a, Value b) -> Value { 1031a54f4eaeSMogball return builder.create<arith::MulFOp>(a, b); 10327e2d672aSAhmed S. Taei }; 10337e2d672aSAhmed S. Taei auto sub = [&](Value a, Value b) -> Value { 1034a54f4eaeSMogball return builder.create<arith::SubFOp>(a, b); 10357e2d672aSAhmed S. Taei }; 1036a54f4eaeSMogball auto floor = [&](Value a) { return builder.create<math::FloorOp>(a); }; 10377e2d672aSAhmed S. Taei 1038ec32d540SEugene Zhulenev auto i32Vec = broadcast(builder.getI32Type(), shape); 10397e2d672aSAhmed S. Taei auto fPToSingedInteger = [&](Value a) -> Value { 1040a54f4eaeSMogball return builder.create<arith::FPToSIOp>(a, i32Vec); 10417e2d672aSAhmed S. Taei }; 10427e2d672aSAhmed S. Taei 10437e2d672aSAhmed S. Taei auto modulo4 = [&](Value a) -> Value { 1044a54f4eaeSMogball return builder.create<arith::AndIOp>(a, bcast(i32Cst(builder, 3))); 10457e2d672aSAhmed S. Taei }; 10467e2d672aSAhmed S. Taei 10477e2d672aSAhmed S. Taei auto isEqualTo = [&](Value a, Value b) -> Value { 1048a54f4eaeSMogball return builder.create<arith::CmpIOp>(arith::CmpIPredicate::eq, a, b); 10497e2d672aSAhmed S. Taei }; 10507e2d672aSAhmed S. Taei 10517e2d672aSAhmed S. Taei auto isGreaterThan = [&](Value a, Value b) -> Value { 1052a54f4eaeSMogball return builder.create<arith::CmpIOp>(arith::CmpIPredicate::sgt, a, b); 10537e2d672aSAhmed S. Taei }; 10547e2d672aSAhmed S. Taei 10557e2d672aSAhmed S. Taei auto select = [&](Value cond, Value t, Value f) -> Value { 10567e2d672aSAhmed S. Taei return builder.create<SelectOp>(cond, t, f); 10577e2d672aSAhmed S. Taei }; 10587e2d672aSAhmed S. Taei 10597e2d672aSAhmed S. Taei auto fmla = [&](Value a, Value b, Value c) { 1060a54f4eaeSMogball return builder.create<math::FmaOp>(a, b, c); 10617e2d672aSAhmed S. Taei }; 10627e2d672aSAhmed S. Taei 1063a54f4eaeSMogball auto bitwiseOr = [&](Value a, Value b) { 1064a54f4eaeSMogball return builder.create<arith::OrIOp>(a, b); 1065a54f4eaeSMogball }; 10667e2d672aSAhmed S. Taei 1067dc3b9365SAlexandre Ganea Value twoOverPi = bcast(f32Cst(builder, (float)TWO_OVER_PI)); 1068dc3b9365SAlexandre Ganea Value piOverTwo = bcast(f32Cst(builder, (float)PI_OVER_2)); 10697e2d672aSAhmed S. Taei 107062fea88bSJacques Pienaar Value x = op.getOperand(); 10717e2d672aSAhmed S. Taei 10727e2d672aSAhmed S. Taei Value k = floor(mul(x, twoOverPi)); 10737e2d672aSAhmed S. Taei 10747e2d672aSAhmed S. Taei Value y = sub(x, mul(k, piOverTwo)); 10757e2d672aSAhmed S. Taei 10767e2d672aSAhmed S. Taei Value cstOne = bcast(f32Cst(builder, 1.0)); 10777e2d672aSAhmed S. Taei Value cstNegativeOne = bcast(f32Cst(builder, -1.0)); 10787e2d672aSAhmed S. Taei 10797e2d672aSAhmed S. Taei Value cstSC2 = bcast(f32Cst(builder, -0.16666667163372039794921875f)); 10807e2d672aSAhmed S. Taei Value cstSC4 = bcast(f32Cst(builder, 8.333347737789154052734375e-3f)); 10817e2d672aSAhmed S. Taei Value cstSC6 = bcast(f32Cst(builder, -1.9842604524455964565277099609375e-4f)); 10827e2d672aSAhmed S. Taei Value cstSC8 = 10837e2d672aSAhmed S. Taei bcast(f32Cst(builder, 2.760012648650445044040679931640625e-6f)); 10847e2d672aSAhmed S. Taei Value cstSC10 = 10857e2d672aSAhmed S. Taei bcast(f32Cst(builder, -2.50293279435709337121807038784027099609375e-8f)); 10867e2d672aSAhmed S. Taei 10877e2d672aSAhmed S. Taei Value cstCC2 = bcast(f32Cst(builder, -0.5f)); 10887e2d672aSAhmed S. Taei Value cstCC4 = bcast(f32Cst(builder, 4.166664183139801025390625e-2f)); 10897e2d672aSAhmed S. Taei Value cstCC6 = bcast(f32Cst(builder, -1.388833043165504932403564453125e-3f)); 10907e2d672aSAhmed S. Taei Value cstCC8 = bcast(f32Cst(builder, 2.47562347794882953166961669921875e-5f)); 10917e2d672aSAhmed S. Taei Value cstCC10 = 10927e2d672aSAhmed S. Taei bcast(f32Cst(builder, -2.59630184018533327616751194000244140625e-7f)); 10937e2d672aSAhmed S. Taei 10947e2d672aSAhmed S. Taei Value kMod4 = modulo4(fPToSingedInteger(k)); 10957e2d672aSAhmed S. Taei 10967e2d672aSAhmed S. Taei Value kR0 = isEqualTo(kMod4, bcast(i32Cst(builder, 0))); 10977e2d672aSAhmed S. Taei Value kR1 = isEqualTo(kMod4, bcast(i32Cst(builder, 1))); 10987e2d672aSAhmed S. Taei Value kR2 = isEqualTo(kMod4, bcast(i32Cst(builder, 2))); 10997e2d672aSAhmed S. Taei Value kR3 = isEqualTo(kMod4, bcast(i32Cst(builder, 3))); 11007e2d672aSAhmed S. Taei 11017e2d672aSAhmed S. Taei Value sinuseCos = isSine ? bitwiseOr(kR1, kR3) : bitwiseOr(kR0, kR2); 11027e2d672aSAhmed S. Taei Value negativeRange = isSine ? isGreaterThan(kMod4, bcast(i32Cst(builder, 1))) 11037e2d672aSAhmed S. Taei : bitwiseOr(kR1, kR2); 11047e2d672aSAhmed S. Taei 11057e2d672aSAhmed S. Taei Value y2 = mul(y, y); 11067e2d672aSAhmed S. Taei 11077e2d672aSAhmed S. Taei Value base = select(sinuseCos, cstOne, y); 11087e2d672aSAhmed S. Taei Value cstC2 = select(sinuseCos, cstCC2, cstSC2); 11097e2d672aSAhmed S. Taei Value cstC4 = select(sinuseCos, cstCC4, cstSC4); 11107e2d672aSAhmed S. Taei Value cstC6 = select(sinuseCos, cstCC6, cstSC6); 11117e2d672aSAhmed S. Taei Value cstC8 = select(sinuseCos, cstCC8, cstSC8); 11127e2d672aSAhmed S. Taei Value cstC10 = select(sinuseCos, cstCC10, cstSC10); 11137e2d672aSAhmed S. Taei 11147e2d672aSAhmed S. Taei Value v1 = fmla(y2, cstC10, cstC8); 11157e2d672aSAhmed S. Taei Value v2 = fmla(y2, v1, cstC6); 11167e2d672aSAhmed S. Taei Value v3 = fmla(y2, v2, cstC4); 11177e2d672aSAhmed S. Taei Value v4 = fmla(y2, v3, cstC2); 11187e2d672aSAhmed S. Taei Value v5 = fmla(y2, v4, cstOne); 11197e2d672aSAhmed S. Taei Value v6 = mul(base, v5); 11207e2d672aSAhmed S. Taei 11217e2d672aSAhmed S. Taei Value approximation = select(negativeRange, mul(cstNegativeOne, v6), v6); 11227e2d672aSAhmed S. Taei 11237e2d672aSAhmed S. Taei rewriter.replaceOp(op, approximation); 11247e2d672aSAhmed S. Taei 11257e2d672aSAhmed S. Taei return success(); 11267e2d672aSAhmed S. Taei } 11277e2d672aSAhmed S. Taei 11287e2d672aSAhmed S. Taei //----------------------------------------------------------------------------// 112935553d45SEmilio Cota // Rsqrt approximation. 113035553d45SEmilio Cota //----------------------------------------------------------------------------// 113135553d45SEmilio Cota 113235553d45SEmilio Cota namespace { 113335553d45SEmilio Cota struct RsqrtApproximation : public OpRewritePattern<math::RsqrtOp> { 113435553d45SEmilio Cota using OpRewritePattern::OpRewritePattern; 113535553d45SEmilio Cota 113635553d45SEmilio Cota LogicalResult matchAndRewrite(math::RsqrtOp op, 113735553d45SEmilio Cota PatternRewriter &rewriter) const final; 113835553d45SEmilio Cota }; 113935553d45SEmilio Cota } // namespace 114035553d45SEmilio Cota 114135553d45SEmilio Cota LogicalResult 114235553d45SEmilio Cota RsqrtApproximation::matchAndRewrite(math::RsqrtOp op, 114335553d45SEmilio Cota PatternRewriter &rewriter) const { 114462fea88bSJacques Pienaar if (!getElementTypeOrSelf(op.getOperand()).isF32()) 1145ec32d540SEugene Zhulenev return rewriter.notifyMatchFailure(op, "unsupported operand type"); 1146ec32d540SEugene Zhulenev 114762fea88bSJacques Pienaar ArrayRef<int64_t> shape = vectorShape(op.getOperand()); 1148ec32d540SEugene Zhulenev 114935553d45SEmilio Cota // Only support already-vectorized rsqrt's. 1150ec32d540SEugene Zhulenev if (shape.empty() || shape.back() % 8 != 0) 115135553d45SEmilio Cota return rewriter.notifyMatchFailure(op, "unsupported operand type"); 115235553d45SEmilio Cota 115335553d45SEmilio Cota ImplicitLocOpBuilder builder(op->getLoc(), rewriter); 115435553d45SEmilio Cota auto bcast = [&](Value value) -> Value { 1155ec32d540SEugene Zhulenev return broadcast(builder, value, shape); 115635553d45SEmilio Cota }; 115735553d45SEmilio Cota 115835553d45SEmilio Cota Value cstPosInf = bcast(f32FromBits(builder, 0x7f800000u)); 115935553d45SEmilio Cota Value cstOnePointFive = bcast(f32Cst(builder, 1.5f)); 116035553d45SEmilio Cota Value cstNegHalf = bcast(f32Cst(builder, -0.5f)); 116135553d45SEmilio Cota Value cstMinNormPos = bcast(f32FromBits(builder, 0x00800000u)); 116235553d45SEmilio Cota 116362fea88bSJacques Pienaar Value negHalf = builder.create<arith::MulFOp>(op.getOperand(), cstNegHalf); 116435553d45SEmilio Cota 116535553d45SEmilio Cota // Select only the inverse sqrt of positive normals (denormals are 116635553d45SEmilio Cota // flushed to zero). 116762fea88bSJacques Pienaar Value ltMinMask = builder.create<arith::CmpFOp>( 116862fea88bSJacques Pienaar arith::CmpFPredicate::OLT, op.getOperand(), cstMinNormPos); 116935553d45SEmilio Cota Value infMask = builder.create<arith::CmpFOp>(arith::CmpFPredicate::OEQ, 117062fea88bSJacques Pienaar op.getOperand(), cstPosInf); 117135553d45SEmilio Cota Value notNormalFiniteMask = builder.create<arith::OrIOp>(ltMinMask, infMask); 117235553d45SEmilio Cota 117335553d45SEmilio Cota // Compute an approximate result. 1174627fa0b9SEugene Zhulenev Value yApprox = handleMultidimensionalVectors( 1175627fa0b9SEugene Zhulenev builder, op->getOperands(), 8, [&builder](ValueRange operands) -> Value { 1176627fa0b9SEugene Zhulenev return builder.create<x86vector::RsqrtOp>(operands); 1177627fa0b9SEugene Zhulenev }); 117835553d45SEmilio Cota 117935553d45SEmilio Cota // Do a single step of Newton-Raphson iteration to improve the approximation. 118035553d45SEmilio Cota // This uses the formula y_{n+1} = y_n * (1.5 - y_n * (0.5 * x) * y_n). 118135553d45SEmilio Cota // It is essential to evaluate the inner term like this because forming 118235553d45SEmilio Cota // y_n^2 may over- or underflow. 118335553d45SEmilio Cota Value inner = builder.create<arith::MulFOp>(negHalf, yApprox); 118435553d45SEmilio Cota Value fma = builder.create<math::FmaOp>(yApprox, inner, cstOnePointFive); 118535553d45SEmilio Cota Value yNewton = builder.create<arith::MulFOp>(yApprox, fma); 118635553d45SEmilio Cota 118735553d45SEmilio Cota // Select the result of the Newton-Raphson step for positive normal arguments. 118835553d45SEmilio Cota // For other arguments, choose the output of the intrinsic. This will 118935553d45SEmilio Cota // return rsqrt(+inf) = 0, rsqrt(x) = NaN if x < 0, and rsqrt(x) = +inf if 119035553d45SEmilio Cota // x is zero or a positive denormalized float (equivalent to flushing positive 119135553d45SEmilio Cota // denormalized inputs to zero). 119235553d45SEmilio Cota Value res = builder.create<SelectOp>(notNormalFiniteMask, yApprox, yNewton); 119335553d45SEmilio Cota rewriter.replaceOp(op, res); 119435553d45SEmilio Cota 119535553d45SEmilio Cota return success(); 119635553d45SEmilio Cota } 119735553d45SEmilio Cota 119835553d45SEmilio Cota //----------------------------------------------------------------------------// 1199f99ccf65SEugene Zhulenev 1200f99ccf65SEugene Zhulenev void mlir::populateMathPolynomialApproximationPatterns( 120135553d45SEmilio Cota RewritePatternSet &patterns, 120235553d45SEmilio Cota const MathPolynomialApproximationOptions &options) { 12032f9f9afaSRob Suderman patterns.add<AtanApproximation, Atan2Approximation, TanhApproximation, 12042f9f9afaSRob Suderman LogApproximation, Log2Approximation, Log1pApproximation, 12052f9f9afaSRob Suderman ErfPolynomialApproximation, ExpApproximation, ExpM1Approximation, 12062f9f9afaSRob Suderman SinAndCosApproximation<true, math::SinOp>, 12077e2d672aSAhmed S. Taei SinAndCosApproximation<false, math::CosOp>>( 12080edc4bc8SEmilio Cota patterns.getContext()); 120935553d45SEmilio Cota if (options.enableAvx2) 121035553d45SEmilio Cota patterns.add<RsqrtApproximation>(patterns.getContext()); 1211f99ccf65SEugene Zhulenev } 1212