| //===- MathOps.cpp - MLIR operations for math implementation --------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "mlir/Dialect/Arith/IR/Arith.h" | |
| #include "mlir/Dialect/CommonFolders.h" | |
| #include "mlir/Dialect/Math/IR/Math.h" | |
| #include "mlir/Dialect/UB/IR/UBOps.h" | |
| #include "mlir/IR/Builders.h" | |
| #include <optional> | |
| using namespace mlir; | |
| using namespace mlir::math; | |
| //===----------------------------------------------------------------------===// | |
| // Common helpers | |
| //===----------------------------------------------------------------------===// | |
| /// Return the type of the same shape (scalar, vector or tensor) containing i1. | |
| static Type getI1SameShape(Type type) { | |
| auto i1Type = IntegerType::get(type.getContext(), 1); | |
| if (auto shapedType = llvm::dyn_cast<ShapedType>(type)) | |
| return shapedType.cloneWith(std::nullopt, i1Type); | |
| if (llvm::isa<UnrankedTensorType>(type)) | |
| return UnrankedTensorType::get(i1Type); | |
| return i1Type; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // TableGen'd op method definitions | |
| //===----------------------------------------------------------------------===// | |
| #define GET_OP_CLASSES | |
| #include "mlir/Dialect/Math/IR/MathOps.cpp.inc" | |
| //===----------------------------------------------------------------------===// | |
| // AbsFOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::AbsFOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOp<FloatAttr>(adaptor.getOperands(), | |
| [](const APFloat &a) { return abs(a); }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AbsIOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::AbsIOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOp<IntegerAttr>(adaptor.getOperands(), | |
| [](const APInt &a) { return a.abs(); }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AcosOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::AcosOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(acos(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(acosf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AcoshOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::AcoshOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(acosh(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(acoshf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AsinOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::AsinOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(asin(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(asinf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AsinhOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::AsinhOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(asinh(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(asinhf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AtanOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::AtanOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(atan(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(atanf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AtanhOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::AtanhOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(atanh(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(atanhf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Atan2Op folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::Atan2Op::fold(FoldAdaptor adaptor) { | |
| return constFoldBinaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), | |
| [](const APFloat &a, const APFloat &b) -> std::optional<APFloat> { | |
| if (a.isZero() && b.isZero()) | |
| return llvm::APFloat::getNaN(a.getSemantics()); | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(atan2(a.convertToDouble(), b.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(atan2f(a.convertToFloat(), b.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CbrtOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::CbrtOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(cbrt(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(cbrtf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CeilOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::CeilOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOp<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) { | |
| APFloat result(a); | |
| result.roundToIntegral(llvm::RoundingMode::TowardPositive); | |
| return result; | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CopySignOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::CopySignOp::fold(FoldAdaptor adaptor) { | |
| return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), | |
| [](const APFloat &a, const APFloat &b) { | |
| APFloat result(a); | |
| result.copySign(b); | |
| return result; | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CosOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::CosOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(cos(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(cosf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CoshOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::CoshOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(cosh(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(coshf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // SinOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::SinOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(sin(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(sinf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // SinhOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::SinhOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(sinh(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(sinhf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // SinCosOp | |
| //===----------------------------------------------------------------------===// | |
| std::optional<SmallVector<int64_t, 4>> math::SincosOp::getShapeForUnroll() { | |
| if (auto vt = mlir::dyn_cast<VectorType>(getOperand().getType())) | |
| return llvm::to_vector<4>(vt.getShape()); | |
| return std::nullopt; | |
| } | |
| LogicalResult math::SincosOp::fold(FoldAdaptor adaptor, | |
| SmallVectorImpl<OpFoldResult> &result) { | |
| auto foldSincos = [](const APFloat &a, double (*fnDouble)(double), | |
| float (*fnFloat)(float)) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(fnDouble(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(fnFloat(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }; | |
| Attribute sinRes = constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), | |
| [&](const APFloat &a) { return foldSincos(a, sin, sinf); }); | |
| Attribute cosRes = constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), | |
| [&](const APFloat &a) { return foldSincos(a, cos, cosf); }); | |
| if (sinRes && cosRes) { | |
| result.push_back(sinRes); | |
| result.push_back(cosRes); | |
| return success(); | |
| } | |
| return failure(); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CountLeadingZerosOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::CountLeadingZerosOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOp<IntegerAttr>( | |
| adaptor.getOperands(), | |
| [](const APInt &a) { return APInt(a.getBitWidth(), a.countl_zero()); }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CountTrailingZerosOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::CountTrailingZerosOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOp<IntegerAttr>( | |
| adaptor.getOperands(), | |
| [](const APInt &a) { return APInt(a.getBitWidth(), a.countr_zero()); }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // CtPopOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::CtPopOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOp<IntegerAttr>( | |
| adaptor.getOperands(), | |
| [](const APInt &a) { return APInt(a.getBitWidth(), a.popcount()); }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // ErfOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::ErfOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(erf(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(erff(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // ErfcOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::ErfcOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(erfc(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(erfcf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IPowIOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::IPowIOp::fold(FoldAdaptor adaptor) { | |
| return constFoldBinaryOpConditional<IntegerAttr>( | |
| adaptor.getOperands(), | |
| [](const APInt &base, const APInt &power) -> std::optional<APInt> { | |
| unsigned width = base.getBitWidth(); | |
| auto zeroValue = APInt::getZero(width); | |
| // i1 folding is ambiguous with signed semantics, don't fold. | |
| if (width == 1) | |
| return {}; | |
| APInt oneValue{width, 1ULL, /*isSigned=*/true}; | |
| APInt minusOneValue{width, -1ULL, /*isSigned=*/true}; | |
| if (power.isZero()) | |
| return oneValue; | |
| if (power.isNegative()) { | |
| // Leave 0 raised to negative power not folded. | |
| if (base.isZero()) | |
| return {}; | |
| if (base.isOne()) | |
| return oneValue; | |
| // If abs(base) > 1, then the result is zero. | |
| if (base.ne(minusOneValue)) | |
| return zeroValue; | |
| // base == -1: | |
| // -1: power is odd | |
| // 1: power is even | |
| if (power[0] == 1) | |
| return minusOneValue; | |
| return oneValue; | |
| } | |
| // power is positive. | |
| APInt result = oneValue; | |
| APInt curBase = base; | |
| APInt curPower = power; | |
| while (true) { | |
| if (curPower[0] == 1) | |
| result *= curBase; | |
| curPower.lshrInPlace(1); | |
| if (curPower.isZero()) | |
| return result; | |
| curBase *= curBase; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // LogOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::LogOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| if (a.isNegative()) | |
| return {}; | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(log(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(logf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Log2Op folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::Log2Op::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| if (a.isNegative()) | |
| return {}; | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(log2(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(log2f(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Log10Op folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::Log10Op::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| if (a.isNegative()) | |
| return {}; | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(log10(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(log10f(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Log1pOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::Log1pOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| if ((a + APFloat(1.0)).isNegative()) | |
| return {}; | |
| return APFloat(log1p(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| if ((a + APFloat(1.0f)).isNegative()) | |
| return {}; | |
| return APFloat(log1pf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // PowFOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::PowFOp::fold(FoldAdaptor adaptor) { | |
| return constFoldBinaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), | |
| [](const APFloat &a, const APFloat &b) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(pow(a.convertToDouble(), b.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(powf(a.convertToFloat(), b.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // RsqrtOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::RsqrtOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| if (a.isNegative()) | |
| return {}; | |
| APFloat one(a.getSemantics(), 1); | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return one / APFloat(sqrt(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return one / APFloat(sqrtf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // SqrtOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::SqrtOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| if (a.isNegative()) | |
| return {}; | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(sqrt(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(sqrtf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // ExpOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::ExpOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(exp(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(expf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Exp2Op folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::Exp2Op::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(exp2(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(exp2f(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // ExpM1Op folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::ExpM1Op::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(expm1(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(expm1f(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IsFiniteOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::IsFiniteOp::fold(FoldAdaptor adaptor) { | |
| if (auto val = dyn_cast_or_null<FloatAttr>(adaptor.getOperand())) { | |
| return BoolAttr::get(val.getContext(), val.getValue().isFinite()); | |
| } | |
| if (auto splat = dyn_cast_or_null<SplatElementsAttr>(adaptor.getOperand())) { | |
| return DenseElementsAttr::get( | |
| cast<ShapedType>(getType()), | |
| APInt(1, splat.getSplatValue<APFloat>().isFinite())); | |
| } | |
| return {}; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IsInfOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::IsInfOp::fold(FoldAdaptor adaptor) { | |
| if (auto val = dyn_cast_or_null<FloatAttr>(adaptor.getOperand())) { | |
| return BoolAttr::get(val.getContext(), val.getValue().isInfinity()); | |
| } | |
| if (auto splat = dyn_cast_or_null<SplatElementsAttr>(adaptor.getOperand())) { | |
| return DenseElementsAttr::get( | |
| cast<ShapedType>(getType()), | |
| APInt(1, splat.getSplatValue<APFloat>().isInfinity())); | |
| } | |
| return {}; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IsNaNOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::IsNaNOp::fold(FoldAdaptor adaptor) { | |
| if (auto val = dyn_cast_or_null<FloatAttr>(adaptor.getOperand())) { | |
| return BoolAttr::get(val.getContext(), val.getValue().isNaN()); | |
| } | |
| if (auto splat = dyn_cast_or_null<SplatElementsAttr>(adaptor.getOperand())) { | |
| return DenseElementsAttr::get( | |
| cast<ShapedType>(getType()), | |
| APInt(1, splat.getSplatValue<APFloat>().isNaN())); | |
| } | |
| return {}; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IsNormalOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::IsNormalOp::fold(FoldAdaptor adaptor) { | |
| if (auto val = dyn_cast_or_null<FloatAttr>(adaptor.getOperand())) { | |
| return BoolAttr::get(val.getContext(), val.getValue().isNormal()); | |
| } | |
| if (auto splat = dyn_cast_or_null<SplatElementsAttr>(adaptor.getOperand())) { | |
| return DenseElementsAttr::get( | |
| cast<ShapedType>(getType()), | |
| APInt(1, splat.getSplatValue<APFloat>().isNormal())); | |
| } | |
| return {}; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // TanOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::TanOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(tan(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(tanf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // TanhOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::TanhOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(tanh(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(tanhf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // RoundEvenOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::RoundEvenOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOp<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) { | |
| APFloat result(a); | |
| result.roundToIntegral(llvm::RoundingMode::NearestTiesToEven); | |
| return result; | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // FloorOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::FloorOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOp<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) { | |
| APFloat result(a); | |
| result.roundToIntegral(llvm::RoundingMode::TowardNegative); | |
| return result; | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // RoundOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::RoundOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(round(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(roundf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // TruncOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::TruncOp::fold(FoldAdaptor adaptor) { | |
| return constFoldUnaryOpConditional<FloatAttr>( | |
| adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(a.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| return APFloat(trunc(a.convertToDouble())); | |
| case APFloat::Semantics::S_IEEEsingle: | |
| return APFloat(truncf(a.convertToFloat())); | |
| default: | |
| return {}; | |
| } | |
| }); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // FPowIOp folder | |
| //===----------------------------------------------------------------------===// | |
| OpFoldResult math::FPowIOp::fold(FoldAdaptor adaptor) { | |
| return constFoldBinaryOpConditional<FloatAttr, IntegerAttr>( | |
| adaptor.getOperands(), | |
| [](const APFloat &base, const APInt &exp) -> std::optional<APFloat> { | |
| switch (APFloat::SemanticsToEnum(base.getSemantics())) { | |
| case APFloat::Semantics::S_IEEEdouble: | |
| case APFloat::Semantics::S_IEEEsingle: | |
| break; | |
| default: | |
| return {}; | |
| } | |
| // Square-and-multiply using the base's own semantics, matching the | |
| // multiply sequence ExpandPowI builds in SelectionDAGBuilder.cpp. | |
| const llvm::fltSemantics &sem = base.getSemantics(); | |
| APInt magnitude = exp.abs(); | |
| APFloat res = APFloat::getOne(sem); | |
| APFloat curSquare = base; | |
| while (!magnitude.isZero()) { | |
| if (magnitude[0]) | |
| res = res * curSquare; | |
| curSquare = curSquare * curSquare; | |
| magnitude.lshrInPlace(1); | |
| } | |
| if (exp.isNegative()) | |
| res = APFloat::getOne(sem) / res; | |
| return res; | |
| }); | |
| } | |
| /// Materialize an integer or floating point constant. | |
| Operation *math::MathDialect::materializeConstant(OpBuilder &builder, | |
| Attribute value, Type type, | |
| Location loc) { | |
| if (auto poison = dyn_cast<ub::PoisonAttr>(value)) | |
| return ub::PoisonOp::create(builder, loc, type, poison); | |
| return arith::ConstantOp::materialize(builder, value, type, loc); | |
| } |