diff --git a/libc/shared/math.h b/libc/shared/math.h index 4ddc29c7ae834..9db53b69041d0 100644 --- a/libc/shared/math.h +++ b/libc/shared/math.h @@ -12,5 +12,6 @@ #include "libc_common.h" #include "math/expf.h" +#include "math/expf16.h" #endif // LLVM_LIBC_SHARED_MATH_H diff --git a/libc/shared/math/expf16.h b/libc/shared/math/expf16.h new file mode 100644 index 0000000000000..f5b99158d59e2 --- /dev/null +++ b/libc/shared/math/expf16.h @@ -0,0 +1,28 @@ +//===-- Shared expf16 function ----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SHARED_MATH_EXPF16_H +#define LLVM_LIBC_SHARED_MATH_EXPF16_H + +#include "include/llvm-libc-macros/float16-macros.h" +#include "shared/libc_common.h" +#include "src/__support/math/expf16.h" + +#ifdef LIBC_TYPES_HAS_FLOAT16 + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using math::expf16; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_HAS_FLOAT16 + +#endif // LLVM_LIBC_SHARED_MATH_EXPF16_H diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt index 66c1d19a1cab0..a26782b6867c7 100644 --- a/libc/src/__support/math/CMakeLists.txt +++ b/libc/src/__support/math/CMakeLists.txt @@ -22,3 +22,23 @@ add_header_library( libc.src.__support.macros.config libc.src.__support.macros.optimization ) + +add_header_library( + expf16 + HDRS + expf16.h + DEPENDS + .expxf16 + libc.hdr.errno_macros + libc.hdr.fenv_macros + libc.src.__support.CPP.array + libc.src.__support.FPUtil.cast + libc.src.__support.FPUtil.except_value_utils + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.nearest_integer + libc.src.__support.FPUtil.polyeval + libc.src.__support.FPUtil.rounding_mode + libc.src.__support.macros.optimization +) diff --git a/libc/src/__support/math/expf16.h b/libc/src/__support/math/expf16.h new file mode 100644 index 0000000000000..e1fadae78cf16 --- /dev/null +++ b/libc/src/__support/math/expf16.h @@ -0,0 +1,140 @@ +//===-- Implementation header for expf16 ------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXPF16_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_EXPF16_H + +#include "include/llvm-libc-macros/float16-macros.h" + +#ifdef LIBC_TYPES_HAS_FLOAT16 + +#include "hdr/errno_macros.h" +#include "hdr/fenv_macros.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/PolyEval.h" +#include "src/__support/FPUtil/cast.h" +#include "src/__support/FPUtil/except_value_utils.h" +#include "src/__support/FPUtil/rounding_mode.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/optimization.h" +#include "src/math/generic/expxf16.h" + +namespace LIBC_NAMESPACE_DECL { + +namespace math { + +static constexpr float16 expf16(float16 x) { +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + constexpr fputil::ExceptValues EXPF16_EXCEPTS_LO = {{ + // (input, RZ output, RU offset, RD offset, RN offset) + // x = 0x1.de4p-8, expf16(x) = 0x1.01cp+0 (RZ) + {0x1f79U, 0x3c07U, 1U, 0U, 0U}, + // x = 0x1.73cp-6, expf16(x) = 0x1.05cp+0 (RZ) + {0x25cfU, 0x3c17U, 1U, 0U, 0U}, + }}; + + constexpr fputil::ExceptValues EXPF16_EXCEPTS_HI = {{ + // (input, RZ output, RU offset, RD offset, RN offset) + // x = 0x1.c34p+0, expf16(x) = 0x1.74cp+2 (RZ) + {0x3f0dU, 0x45d3U, 1U, 0U, 1U}, + // x = -0x1.488p-5, expf16(x) = 0x1.ebcp-1 (RZ) + {0xa922U, 0x3bafU, 1U, 0U, 0U}, + // x = -0x1.55p-5, expf16(x) = 0x1.ebp-1 (RZ) + {0xa954U, 0x3bacU, 1U, 0U, 0U}, + }}; +#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS + + using FPBits = fputil::FPBits; + FPBits x_bits(x); + + uint16_t x_u = x_bits.uintval(); + uint16_t x_abs = x_u & 0x7fffU; + + // When 0 < |x| <= 2^(-5), or |x| >= 12, or x is NaN. + if (LIBC_UNLIKELY(x_abs <= 0x2800U || x_abs >= 0x4a00U)) { + // exp(NaN) = NaN + if (x_bits.is_nan()) { + if (x_bits.is_signaling_nan()) { + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + + return x; + } + + // When x >= 12. + if (x_bits.is_pos() && x_abs >= 0x4a00U) { + // exp(+inf) = +inf + if (x_bits.is_inf()) + return FPBits::inf().get_val(); + + switch (fputil::quick_get_round()) { + case FE_TONEAREST: + case FE_UPWARD: + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_OVERFLOW); + return FPBits::inf().get_val(); + default: + return FPBits::max_normal().get_val(); + } + } + + // When x <= -18. + if (x_u >= 0xcc80U) { + // exp(-inf) = +0 + if (x_bits.is_inf()) + return FPBits::zero().get_val(); + + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT); + + switch (fputil::quick_get_round()) { + case FE_UPWARD: + return FPBits::min_subnormal().get_val(); + default: + return FPBits::zero().get_val(); + } + } + + // When 0 < |x| <= 2^(-5). + if (x_abs <= 0x2800U && !x_bits.is_zero()) { +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + if (auto r = EXPF16_EXCEPTS_LO.lookup(x_u); LIBC_UNLIKELY(r.has_value())) + return r.value(); +#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS + + float xf = x; + // Degree-3 minimax polynomial generated by Sollya with the following + // commands: + // > display = hexadecimal; + // > P = fpminimax(expm1(x)/x, 2, [|SG...|], [-2^-5, 2^-5]); + // > 1 + x * P; + return fputil::cast( + fputil::polyeval(xf, 0x1p+0f, 0x1p+0f, 0x1.0004p-1f, 0x1.555778p-3f)); + } + } + +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + if (auto r = EXPF16_EXCEPTS_HI.lookup(x_u); LIBC_UNLIKELY(r.has_value())) + return r.value(); +#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS + + // exp(x) = exp(hi + mid) * exp(lo) + auto [exp_hi_mid, exp_lo] = exp_range_reduction(x); + return fputil::cast(exp_hi_mid * exp_lo); +} + +} // namespace math + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_HAS_FLOAT16 + +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXPF16_H diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index fd1e6c0d648aa..a77db5262c51a 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1332,19 +1332,8 @@ add_entrypoint_object( HDRS ../expf16.h DEPENDS - .expxf16 - libc.hdr.errno_macros - libc.hdr.fenv_macros - libc.src.__support.CPP.array - libc.src.__support.FPUtil.cast - libc.src.__support.FPUtil.except_value_utils - libc.src.__support.FPUtil.fenv_impl - libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.multiply_add - libc.src.__support.FPUtil.nearest_integer - libc.src.__support.FPUtil.polyeval - libc.src.__support.FPUtil.rounding_mode - libc.src.__support.macros.optimization + libc.src.__support.math.expf16 + libc.src.errno.errno ) add_entrypoint_object( diff --git a/libc/src/math/generic/expf16.cpp b/libc/src/math/generic/expf16.cpp index 1af9b3ec9ad6e..ad213e237f021 100644 --- a/libc/src/math/generic/expf16.cpp +++ b/libc/src/math/generic/expf16.cpp @@ -7,120 +7,11 @@ //===----------------------------------------------------------------------===// #include "src/math/expf16.h" -#include "expxf16.h" -#include "hdr/errno_macros.h" -#include "hdr/fenv_macros.h" -#include "src/__support/FPUtil/FEnvImpl.h" -#include "src/__support/FPUtil/FPBits.h" -#include "src/__support/FPUtil/PolyEval.h" -#include "src/__support/FPUtil/cast.h" -#include "src/__support/FPUtil/except_value_utils.h" -#include "src/__support/FPUtil/rounding_mode.h" -#include "src/__support/common.h" -#include "src/__support/macros/config.h" -#include "src/__support/macros/optimization.h" -namespace LIBC_NAMESPACE_DECL { - -#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS -static constexpr fputil::ExceptValues EXPF16_EXCEPTS_LO = {{ - // (input, RZ output, RU offset, RD offset, RN offset) - // x = 0x1.de4p-8, expf16(x) = 0x1.01cp+0 (RZ) - {0x1f79U, 0x3c07U, 1U, 0U, 0U}, - // x = 0x1.73cp-6, expf16(x) = 0x1.05cp+0 (RZ) - {0x25cfU, 0x3c17U, 1U, 0U, 0U}, -}}; - -static constexpr fputil::ExceptValues EXPF16_EXCEPTS_HI = {{ - // (input, RZ output, RU offset, RD offset, RN offset) - // x = 0x1.c34p+0, expf16(x) = 0x1.74cp+2 (RZ) - {0x3f0dU, 0x45d3U, 1U, 0U, 1U}, - // x = -0x1.488p-5, expf16(x) = 0x1.ebcp-1 (RZ) - {0xa922U, 0x3bafU, 1U, 0U, 0U}, - // x = -0x1.55p-5, expf16(x) = 0x1.ebp-1 (RZ) - {0xa954U, 0x3bacU, 1U, 0U, 0U}, -}}; -#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS - -LLVM_LIBC_FUNCTION(float16, expf16, (float16 x)) { - using FPBits = fputil::FPBits; - FPBits x_bits(x); - - uint16_t x_u = x_bits.uintval(); - uint16_t x_abs = x_u & 0x7fffU; - - // When 0 < |x| <= 2^(-5), or |x| >= 12, or x is NaN. - if (LIBC_UNLIKELY(x_abs <= 0x2800U || x_abs >= 0x4a00U)) { - // exp(NaN) = NaN - if (x_bits.is_nan()) { - if (x_bits.is_signaling_nan()) { - fputil::raise_except_if_required(FE_INVALID); - return FPBits::quiet_nan().get_val(); - } - - return x; - } - - // When x >= 12. - if (x_bits.is_pos() && x_abs >= 0x4a00U) { - // exp(+inf) = +inf - if (x_bits.is_inf()) - return FPBits::inf().get_val(); +#include "src/__support/math/expf16.h" - switch (fputil::quick_get_round()) { - case FE_TONEAREST: - case FE_UPWARD: - fputil::set_errno_if_required(ERANGE); - fputil::raise_except_if_required(FE_OVERFLOW); - return FPBits::inf().get_val(); - default: - return FPBits::max_normal().get_val(); - } - } - - // When x <= -18. - if (x_u >= 0xcc80U) { - // exp(-inf) = +0 - if (x_bits.is_inf()) - return FPBits::zero().get_val(); - - fputil::set_errno_if_required(ERANGE); - fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT); - - switch (fputil::quick_get_round()) { - case FE_UPWARD: - return FPBits::min_subnormal().get_val(); - default: - return FPBits::zero().get_val(); - } - } - - // When 0 < |x| <= 2^(-5). - if (x_abs <= 0x2800U && !x_bits.is_zero()) { -#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS - if (auto r = EXPF16_EXCEPTS_LO.lookup(x_u); LIBC_UNLIKELY(r.has_value())) - return r.value(); -#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS - - float xf = x; - // Degree-3 minimax polynomial generated by Sollya with the following - // commands: - // > display = hexadecimal; - // > P = fpminimax(expm1(x)/x, 2, [|SG...|], [-2^-5, 2^-5]); - // > 1 + x * P; - return fputil::cast( - fputil::polyeval(xf, 0x1p+0f, 0x1p+0f, 0x1.0004p-1f, 0x1.555778p-3f)); - } - } - -#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS - if (auto r = EXPF16_EXCEPTS_HI.lookup(x_u); LIBC_UNLIKELY(r.has_value())) - return r.value(); -#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS +namespace LIBC_NAMESPACE_DECL { - // exp(x) = exp(hi + mid) * exp(lo) - auto [exp_hi_mid, exp_lo] = exp_range_reduction(x); - return fputil::cast(exp_hi_mid * exp_lo); -} +LLVM_LIBC_FUNCTION(float16, expf16, (float16 x)) { return math::expf16(x); } } // namespace LIBC_NAMESPACE_DECL diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 2484a2f1e2bd7..6e9ce06b6b1c1 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -2096,6 +2096,25 @@ libc_support_library( ], ) +libc_support_library( + name = "__support_math_expf16", + hdrs = ["src/__support/math/expf16.h"], + deps = [ + ":__support_common", + ":__support_libc_errno", + ":__support_cpp_array", + ":__support_fputil_cast", + ":__support_fputil_except_value_utils", + ":__support_fputil_fenv_impl", + ":__support_fputil_fp_bits", + ":__support_fputil_multiply_add", + ":__support_fputil_nearest_integer", + ":__support_fputil_polyeval", + ":__support_fputil_rounding_mode", + ":__support_macros_optimization", + ], +) + ############################### complex targets ################################ libc_function( @@ -2701,6 +2720,7 @@ libc_math_function( libc_math_function( name = "expf16", additional_deps = [ + ":__support_math_expf16", ":expxf16", ], )