[libc][NFC] Use cpp::optional for checking exceptional values of math functions.

Update the utility functions for checking exceptional values of math
functions to use cpp::optional return values.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D133134
This commit is contained in:
Tue Ly 2022-09-01 14:30:51 -04:00
parent 0caa4a9559
commit a4d48e3b0b
4 changed files with 100 additions and 85 deletions

View File

@ -102,6 +102,7 @@ add_header_library(
DEPENDS
.fp_bits
.fenv_impl
libc.src.__support.CPP.optional
)

View File

@ -11,55 +11,88 @@
#include "FEnvImpl.h"
#include "FPBits.h"
#include "src/__support/CPP/optional.h"
namespace __llvm_libc {
namespace fputil {
template <typename T, int N> struct ExceptionalValues {
using UIntType = typename FPBits<T>::UIntType;
static constexpr int SIZE = N;
// Input bits.
UIntType inputs[SIZE];
// Output bits contains 4 values:
// output[i][0]: output bits corresponding to FE_TOWARDZERO
// output[i][1]: offset for FE_UPWARD
// output[i][2]: offset for FE_DOWNWARD
// output[i][3]: offset for FE_TONEAREST
UIntType outputs[SIZE][4];
};
// This file contains utility functions and classes to manage exceptional values
// when there are many of them.
//
// Example usage:
//
// Define list of exceptional inputs and outputs:
// static constexpr int N = ...; // Number of exceptional values.
// static constexpr fputil::ExceptValues<UIntType, N> Excepts {
// <list of input bits, output bits and offsets>
// };
//
// Check for exceptional inputs:
// if (auto r = Excepts.lookup(x_bits); unlikely(r.has_value()))
// return r.value();
template <typename T, int N> struct ExceptionChecker {
using UIntType = typename FPBits<T>::UIntType;
using FPBits = FPBits<T>;
using ExceptionalValues = ExceptionalValues<T, N>;
template <typename T, size_t N> struct ExceptValues {
static_assert(cpp::is_floating_point_v<T>, "Must be a floating point type.");
static bool check_odd_func(const ExceptionalValues &ExceptVals,
UIntType x_abs, bool sign, T &result) {
for (int i = 0; i < N; ++i) {
if (unlikely(x_abs == ExceptVals.inputs[i])) {
UIntType out_bits = ExceptVals.outputs[i][0]; // FE_TOWARDZERO
using UIntType = typename FPBits<T>::UIntType;
struct Mapping {
UIntType input;
UIntType rnd_towardzero_result;
UIntType rnd_upward_offset;
UIntType rnd_downward_offset;
UIntType rnd_tonearest_offset;
};
Mapping values[N];
constexpr cpp::optional<T> lookup(UIntType x_bits) const {
for (size_t i = 0; i < N; ++i) {
if (unlikely(x_bits == values[i].input)) {
UIntType out_bits = values[i].rnd_towardzero_result;
switch (fputil::get_round()) {
case FE_UPWARD:
out_bits +=
sign ? ExceptVals.outputs[i][2] : ExceptVals.outputs[i][1];
out_bits += values[i].rnd_upward_offset;
break;
case FE_DOWNWARD:
out_bits +=
sign ? ExceptVals.outputs[i][1] : ExceptVals.outputs[i][2];
out_bits += values[i].rnd_downward_offset;
break;
case FE_TONEAREST:
out_bits += ExceptVals.outputs[i][3];
out_bits += values[i].rnd_tonearest_offset;
break;
}
result = FPBits(out_bits).get_val();
return FPBits<T>(out_bits).get_val();
}
}
return cpp::nullopt;
}
constexpr cpp::optional<T> lookup_odd(UIntType x_abs, bool sign) const {
for (size_t i = 0; i < N; ++i) {
if (unlikely(x_abs == values[i].input)) {
UIntType out_bits = values[i].rnd_towardzero_result;
switch (fputil::get_round()) {
case FE_UPWARD:
out_bits += sign ? values[i].rnd_downward_offset
: values[i].rnd_upward_offset;
break;
case FE_DOWNWARD:
out_bits += sign ? values[i].rnd_upward_offset
: values[i].rnd_downward_offset;
break;
case FE_TONEAREST:
out_bits += values[i].rnd_tonearest_offset;
break;
}
T result = FPBits<T>(out_bits).get_val();
if (sign)
result = -result;
return true;
return result;
}
}
return false;
return cpp::nullopt;
}
};

View File

@ -20,27 +20,23 @@
namespace __llvm_libc {
// Exceptional cases for cosf.
static constexpr int COSF_EXCEPTS = 6;
static constexpr size_t N_EXCEPTS = 6;
static constexpr fputil::ExceptionalValues<float, COSF_EXCEPTS> CosfExcepts{
/* inputs */ {
0x55325019, // x = 0x1.64a032p43
0x5922aa80, // x = 0x1.4555p51
0x5aa4542c, // x = 0x1.48a858p54
0x5f18b878, // x = 0x1.3170fp63
0x6115cb11, // x = 0x1.2b9622p67
0x7beef5ef, // x = 0x1.ddebdep120
},
/* outputs (RZ, RU offset, RD offset, RN offset) */
{
{0x3f4ea5d2, 1, 0, 0}, // x = 0x1.64a032p43, cos(x) = 0x1.9d4ba4p-1 (RZ)
{0x3f08aebe, 1, 0, 1}, // x = 0x1.4555p51, cos(x) = 0x1.115d7cp-1 (RZ)
{0x3efa40a4, 1, 0, 0}, // x = 0x1.48a858p54, cos(x) = 0x1.f48148p-2 (RZ)
{0x3f7f14bb, 1, 0, 0}, // x = 0x1.3170fp63, cos(x) = 0x1.fe2976p-1 (RZ)
{0x3f78142e, 1, 0, 1}, // x = 0x1.2b9622p67, cos(x) = 0x1.f0285cp-1 (RZ)
{0x3f08a21c, 1, 0,
0}, // x = 0x1.ddebdep120, cos(x) = 0x1.114438p-1 (RZ)
}};
static constexpr fputil::ExceptValues<float, N_EXCEPTS> COSF_EXCEPTS{{
// (inputs, RZ output, RU offset, RD offset, RN offset)
// x = 0x1.64a032p43, cos(x) = 0x1.9d4ba4p-1 (RZ)
{0x55325019, 0x3f4ea5d2, 1, 0, 0},
// x = 0x1.4555p51, cos(x) = 0x1.115d7cp-1 (RZ)
{0x5922aa80, 0x3f08aebe, 1, 0, 1},
// x = 0x1.48a858p54, cos(x) = 0x1.f48148p-2 (RZ)
{0x5aa4542c, 0x3efa40a4, 1, 0, 0},
// x = 0x1.3170fp63, cos(x) = 0x1.fe2976p-1 (RZ)
{0x5f18b878, 0x3f7f14bb, 1, 0, 0},
// x = 0x1.2b9622p67, cos(x) = 0x1.f0285cp-1 (RZ)
{0x6115cb11, 0x3f78142e, 1, 0, 1},
// x = 0x1.ddebdep120, cos(x) = 0x1.114438p-1 (RZ)
{0x7beef5ef, 0x3f08a21c, 1, 0, 0},
}};
LLVM_LIBC_FUNCTION(float, cosf, (float x)) {
using FPBits = typename fputil::FPBits<float>;
@ -110,12 +106,8 @@ LLVM_LIBC_FUNCTION(float, cosf, (float x)) {
#endif // LIBC_TARGET_HAS_FMA
}
using ExceptChecker = typename fputil::ExceptionChecker<float, COSF_EXCEPTS>;
{
float result;
if (ExceptChecker::check_odd_func(CosfExcepts, x_abs, false, result))
return result;
}
if (auto r = COSF_EXCEPTS.lookup(x_abs); unlikely(r.has_value()))
return r.value();
// x is inf or nan.
if (unlikely(x_abs >= 0x7f80'0000U)) {

View File

@ -56,29 +56,23 @@ static constexpr double TAN_K_PI_OVER_32[32] = {
};
// Exceptional cases for tanf.
static constexpr int TANF_EXCEPTS = 6;
static constexpr size_t N_EXCEPTS = 6;
static constexpr fputil::ExceptionalValues<float, TANF_EXCEPTS> TanfExcepts{
/* inputs */ {
0x531d744c, // x = 0x1.3ae898p39
0x57d7b0ed, // x = 0x1.af61dap48
0x65ee8695, // x = 0x1.dd0d2ap76
0x6798fe4f, // x = 0x1.31fc9ep80
0x6ad36709, // x = 0x1.a6ce12p86
0x72b505bb, // x = 0x1.6a0b76p102
},
/* outputs (RZ, RU offset, RD offset, RN offset) */
{
{0x4591ea1e, 1, 0, 1}, // x = 0x1.3ae898p39, tan(x) = 0x1.23d43cp12 (RZ)
{0x3eb068e3, 1, 0, 1}, // x = 0x1.af61dap48, tan(x) = 0x1.60d1c6p-2 (RZ)
{0xcaa32f8e, 0, 1,
0}, // x = 0x1.dd0d2ap76, tan(x) = -0x1.465f1cp22 (RZ)
{0x461e09f7, 1, 0, 0}, // x = 0x1.31fc9ep80, tan(x) = 0x1.3c13eep13 (RZ)
{0xbf62b097, 0, 1,
0}, // x = 0x1.a6ce12p86, tan(x) = -0x1.c5612ep-1 (RZ)
{0xbff2150f, 0, 1,
0}, // x = 0x1.6a0b76p102, tan(x) = -0x1.e42a1ep0 (RZ)
}};
static constexpr fputil::ExceptValues<float, N_EXCEPTS> TANF_EXCEPTS{{
// (inputs, RZ output, RU offset, RD offset, RN offset)
// x = 0x1.3ae898p39, tan(x) = 0x1.23d43cp12 (RZ)
{0x531d744c, 0x4591ea1e, 1, 0, 1},
// x = 0x1.af61dap48, tan(x) = 0x1.60d1c6p-2 (RZ)
{0x57d7b0ed, 0x3eb068e3, 1, 0, 1},
// x = 0x1.dd0d2ap76, tan(x) = -0x1.465f1cp22 (RZ)
{0x65ee8695, 0xcaa32f8e, 0, 1, 0},
// x = 0x1.31fc9ep80, tan(x) = 0x1.3c13eep13 (RZ)
{0x6798fe4f, 0x461e09f7, 1, 0, 0},
// x = 0x1.a6ce12p86, tan(x) = -0x1.c5612ep-1 (RZ)
{0x6ad36709, 0xbf62b097, 0, 1, 0},
// x = 0x1.6a0b76p102, tan(x) = -0x1.e42a1ep0 (RZ)
{0x72b505bb, 0xbff2150f, 0, 1, 0},
}};
LLVM_LIBC_FUNCTION(float, tanf, (float x)) {
using FPBits = typename fputil::FPBits<float>;
@ -200,14 +194,9 @@ LLVM_LIBC_FUNCTION(float, tanf, (float x)) {
k = small_range_reduction(xd, y);
} else {
using ExceptChecker =
typename fputil::ExceptionChecker<float, TANF_EXCEPTS>;
{
float result;
if (ExceptChecker::check_odd_func(TanfExcepts, x_abs, x_sign <= 0.0,
result))
return result;
}
if (auto r = TANF_EXCEPTS.lookup_odd(x_abs, x_sign <= 0.0);
unlikely(r.has_value()))
return r.value();
fputil::FPBits<float> x_bits(x_abs);
k = large_range_reduction(xd, x_bits.get_exponent(), y);