Project import generated by Copybara. am: 76a5866889 Original change: https://android-review.googlesource.com/c/platform/external/llvm-libc/+/3495325 Change-Id: Ib44c0295c3524baf4271d9586484e6f67fef6b77 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/include/__llvm-libc-common.h b/include/__llvm-libc-common.h index d3f8a4e..a0fa506 100644 --- a/include/__llvm-libc-common.h +++ b/include/__llvm-libc-common.h
@@ -9,6 +9,8 @@ #ifndef LLVM_LIBC_COMMON_H #define LLVM_LIBC_COMMON_H +#define __LLVM_LIBC__ 1 + #ifdef __cplusplus #undef __BEGIN_C_DECLS
diff --git a/include/llvm-libc-macros/endian-macros.h b/include/llvm-libc-macros/endian-macros.h index 94e1d60..e1e105d 100644 --- a/include/llvm-libc-macros/endian-macros.h +++ b/include/llvm-libc-macros/endian-macros.h
@@ -9,8 +9,42 @@ #ifndef LLVM_LIBC_MACROS_ENDIAN_MACROS_H #define LLVM_LIBC_MACROS_ENDIAN_MACROS_H +#include "stdint-macros.h" + #define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ #define BIG_ENDIAN __ORDER_BIG_ENDIAN__ #define BYTE_ORDER __BYTE_ORDER__ +#if BYTE_ORDER == LITTLE_ENDIAN + +#define htobe16(x) __builtin_bswap16((x)) +#define htobe32(x) __builtin_bswap32((x)) +#define htobe64(x) __builtin_bswap64((x)) +#define htole16(x) ((uint16_t)(x)) +#define htole32(x) ((uint32_t)(x)) +#define htole64(x) ((uint64_t)(x)) +#define be16toh(x) __builtin_bswap16((x)) +#define be32toh(x) __builtin_bswap32((x)) +#define be64toh(x) __builtin_bswap64((x)) +#define le16toh(x) ((uint16_t)(x)) +#define le32toh(x) ((uint32_t)(x)) +#define le64toh(x) ((uint64_t)(x)) + +#else + +#define htobe16(x) ((uint16_t)(x)) +#define htobe32(x) ((uint32_t)(x)) +#define htobe64(x) ((uint64_t)(x)) +#define htole16(x) __builtin_bswap16((x)) +#define htole32(x) __builtin_bswap32((x)) +#define htole64(x) __builtin_bswap64((x)) +#define be16toh(x) ((uint16_t)(x)) +#define be32toh(x) ((uint32_t)(x)) +#define be64toh(x) ((uint64_t)(x)) +#define le16toh(x) __builtin_bswap16((x)) +#define le32toh(x) __builtin_bswap32((x)) +#define le64toh(x) __builtin_bswap64((x)) + +#endif + #endif // LLVM_LIBC_MACROS_ENDIAN_MACROS_H
diff --git a/include/llvm-libc-macros/features-macros.h b/include/llvm-libc-macros/features-macros.h index 5bc87a6..f87ae4a 100644 --- a/include/llvm-libc-macros/features-macros.h +++ b/include/llvm-libc-macros/features-macros.h
@@ -9,6 +9,4 @@ #ifndef LLVM_LIBC_MACROS_FEATURES_MACROS_H #define LLVM_LIBC_MACROS_FEATURES_MACROS_H -#define __LLVM_LIBC__ 1 - #endif // LLVM_LIBC_MACROS_FEATURES_MACROS_H
diff --git a/include/llvm-libc-macros/pthread-macros.h b/include/llvm-libc-macros/pthread-macros.h index 8a144db..fcc6ef9 100644 --- a/include/llvm-libc-macros/pthread-macros.h +++ b/include/llvm-libc-macros/pthread-macros.h
@@ -9,6 +9,8 @@ #ifndef LLVM_LIBC_MACROS_PTHREAD_MACRO_H #define LLVM_LIBC_MACROS_PTHREAD_MACRO_H +#include "null-macro.h" + #define PTHREAD_CREATE_JOINABLE 0 #define PTHREAD_CREATE_DETACHED 1 @@ -25,8 +27,34 @@ #define PTHREAD_PROCESS_PRIVATE 0 #define PTHREAD_PROCESS_SHARED 1 -#define PTHREAD_MUTEX_INITIALIZER {0} -#define PTHREAD_RWLOCK_INITIALIZER {0} +#ifdef __linux__ +#define PTHREAD_MUTEX_INITIALIZER \ + { \ + /* .__timed = */ 0, /* .__recursive = */ 0, \ + /* .__robust = */ 0, /* .__owner = */ NULL, \ + /* .__lock_count = */ 0, /* .__futex_word = */ {0}, \ + } +#else +#define PTHREAD_MUTEX_INITIALIZER \ + { \ + /* .__timed = */ 0, /* .__recursive = */ 0, \ + /* .__robust = */ 0, /* .__owner = */ NULL, \ + /* .__lock_count = */ 0, \ + } +#endif + +#define PTHREAD_RWLOCK_INITIALIZER \ + { \ + /* .__is_pshared = */ 0, \ + /* .__preference = */ 0, \ + /* .__state = */ 0, \ + /* .__write_tid = */ 0, \ + /* .__wait_queue_mutex = */ {0}, \ + /* .__pending_readers = */ {0}, \ + /* .__pending_writers = */ {0}, \ + /* .__reader_serialization = */ {0}, \ + /* .__writer_serialization = */ {0}, \ + } // glibc extensions #define PTHREAD_STACK_MIN (1 << 14) // 16KB
diff --git a/include/llvm-libc-types/struct_tm.h b/include/llvm-libc-types/struct_tm.h index 9fef7c5..2ec74ec 100644 --- a/include/llvm-libc-types/struct_tm.h +++ b/include/llvm-libc-types/struct_tm.h
@@ -19,6 +19,7 @@ int tm_wday; // days since Sunday int tm_yday; // days since January int tm_isdst; // Daylight Saving Time flag + // TODO: add tm_gmtoff and tm_zone? (posix extensions) }; #endif // LLVM_LIBC_TYPES_STRUCT_TM_H
diff --git a/src/__support/CPP/bit.h b/src/__support/CPP/bit.h index adcd047..82b9eb5 100644 --- a/src/__support/CPP/bit.h +++ b/src/__support/CPP/bit.h
@@ -232,7 +232,7 @@ return value; if (rotate < 0) return cpp::rotr<T>(value, -rotate); - return (value << rotate) | (value >> (N - rotate)); + return static_cast<T>((value << rotate) | (value >> (N - rotate))); } template <typename T> @@ -244,7 +244,7 @@ return value; if (rotate < 0) return cpp::rotl<T>(value, -rotate); - return (value >> rotate) | (value << (N - rotate)); + return static_cast<T>((value >> rotate) | (value << (N - rotate))); } // TODO: Do we need this function at all? How is it different from
diff --git a/src/__support/FPUtil/FMA.h b/src/__support/FPUtil/FMA.h index 807d685..1e40d06 100644 --- a/src/__support/FPUtil/FMA.h +++ b/src/__support/FPUtil/FMA.h
@@ -25,11 +25,19 @@ #ifdef LIBC_TARGET_CPU_HAS_FMA template <> LIBC_INLINE float fma(float x, float y, float z) { +#if __has_builtin(__builtin_elementwise_fma) + return __builtin_elementwise_fma(x, y, z); +#else return __builtin_fmaf(x, y, z); +#endif } template <> LIBC_INLINE double fma(double x, double y, double z) { +#if __has_builtin(__builtin_elementwise_fma) + return __builtin_elementwise_fma(x, y, z); +#else return __builtin_fma(x, y, z); +#endif } #endif // LIBC_TARGET_CPU_HAS_FMA
diff --git a/src/__support/FPUtil/double_double.h b/src/__support/FPUtil/double_double.h index db3c2c8..b24ffd4 100644 --- a/src/__support/FPUtil/double_double.h +++ b/src/__support/FPUtil/double_double.h
@@ -18,43 +18,52 @@ namespace LIBC_NAMESPACE_DECL { namespace fputil { -#define DEFAULT_DOUBLE_SPLIT 27 +template <typename T> struct DefaultSplit; +template <> struct DefaultSplit<float> { + static constexpr size_t VALUE = 12; +}; +template <> struct DefaultSplit<double> { + static constexpr size_t VALUE = 27; +}; -using DoubleDouble = LIBC_NAMESPACE::NumberPair<double>; +using DoubleDouble = NumberPair<double>; +using FloatFloat = NumberPair<float>; // The output of Dekker's FastTwoSum algorithm is correct, i.e.: // r.hi + r.lo = a + b exactly // and |r.lo| < eps(r.lo) // Assumption: |a| >= |b|, or a = 0. -template <bool FAST2SUM = true> -LIBC_INLINE constexpr DoubleDouble exact_add(double a, double b) { - DoubleDouble r{0.0, 0.0}; +template <bool FAST2SUM = true, typename T = double> +LIBC_INLINE constexpr NumberPair<T> exact_add(T a, T b) { + NumberPair<T> r{0.0, 0.0}; if constexpr (FAST2SUM) { r.hi = a + b; - double t = r.hi - a; + T t = r.hi - a; r.lo = b - t; } else { r.hi = a + b; - double t1 = r.hi - a; - double t2 = r.hi - t1; - double t3 = b - t1; - double t4 = a - t2; + T t1 = r.hi - a; + T t2 = r.hi - t1; + T t3 = b - t1; + T t4 = a - t2; r.lo = t3 + t4; } return r; } // Assumption: |a.hi| >= |b.hi| -LIBC_INLINE constexpr DoubleDouble add(const DoubleDouble &a, - const DoubleDouble &b) { - DoubleDouble r = exact_add(a.hi, b.hi); - double lo = a.lo + b.lo; +template <typename T> +LIBC_INLINE constexpr NumberPair<T> add(const NumberPair<T> &a, + const NumberPair<T> &b) { + NumberPair<T> r = exact_add(a.hi, b.hi); + T lo = a.lo + b.lo; return exact_add(r.hi, r.lo + lo); } // Assumption: |a.hi| >= |b| -LIBC_INLINE constexpr DoubleDouble add(const DoubleDouble &a, double b) { - DoubleDouble r = exact_add<false>(a.hi, b); +template <typename T> +LIBC_INLINE constexpr NumberPair<T> add(const NumberPair<T> &a, T b) { + NumberPair<T> r = exact_add<false>(a.hi, b); return exact_add(r.hi, r.lo + a.lo); } @@ -63,12 +72,12 @@ // Zimmermann, P., "Note on the Veltkamp/Dekker Algorithms with Directed // Roundings," https://inria.hal.science/hal-04480440. // Default splitting constant = 2^ceil(prec(double)/2) + 1 = 2^27 + 1. -template <size_t N = DEFAULT_DOUBLE_SPLIT> -LIBC_INLINE constexpr DoubleDouble split(double a) { - DoubleDouble r{0.0, 0.0}; +template <typename T = double, size_t N = DefaultSplit<T>::VALUE> +LIBC_INLINE constexpr NumberPair<T> split(T a) { + NumberPair<T> r{0.0, 0.0}; // CN = 2^N. - constexpr double CN = static_cast<double>(1 << N); - constexpr double C = CN + 1.0; + constexpr T CN = static_cast<T>(1 << N); + constexpr T C = CN + 1.0; double t1 = C * a; double t2 = a - t1; r.hi = t1 + t2; @@ -77,16 +86,15 @@ } // Helper for non-fma exact mult where the first number is already split. -template <size_t SPLIT_B = DEFAULT_DOUBLE_SPLIT> -LIBC_INLINE DoubleDouble exact_mult(const DoubleDouble &as, double a, - double b) { - DoubleDouble bs = split<SPLIT_B>(b); - DoubleDouble r{0.0, 0.0}; +template <typename T = double, size_t SPLIT_B = DefaultSplit<T>::VALUE> +LIBC_INLINE NumberPair<T> exact_mult(const NumberPair<T> &as, T a, T b) { + NumberPair<T> bs = split<T, SPLIT_B>(b); + NumberPair<T> r{0.0, 0.0}; r.hi = a * b; - double t1 = as.hi * bs.hi - r.hi; - double t2 = as.hi * bs.lo + t1; - double t3 = as.lo * bs.hi + t2; + T t1 = as.hi * bs.hi - r.hi; + T t2 = as.hi * bs.lo + t1; + T t3 = as.lo * bs.hi + t2; r.lo = as.lo * bs.lo + t3; return r; @@ -99,18 +107,18 @@ // Using Theorem 1 in the paper above, without FMA instruction, if we restrict // the generated constants to precision <= 51, and splitting it by 2^28 + 1, // then a * b = r.hi + r.lo is exact for all rounding modes. -template <size_t SPLIT_B = 27> -LIBC_INLINE DoubleDouble exact_mult(double a, double b) { - DoubleDouble r{0.0, 0.0}; +template <typename T = double, size_t SPLIT_B = DefaultSplit<T>::VALUE> +LIBC_INLINE NumberPair<T> exact_mult(T a, T b) { + NumberPair<T> r{0.0, 0.0}; #ifdef LIBC_TARGET_CPU_HAS_FMA r.hi = a * b; r.lo = fputil::multiply_add(a, b, -r.hi); #else // Dekker's Product. - DoubleDouble as = split(a); + NumberPair<T> as = split(a); - r = exact_mult<SPLIT_B>(as, a, b); + r = exact_mult<T, SPLIT_B>(as, a, b); #endif // LIBC_TARGET_CPU_HAS_FMA return r; @@ -125,7 +133,7 @@ template <size_t SPLIT_B = 27> LIBC_INLINE DoubleDouble quick_mult(const DoubleDouble &a, const DoubleDouble &b) { - DoubleDouble r = exact_mult<SPLIT_B>(a.hi, b.hi); + DoubleDouble r = exact_mult<double, SPLIT_B>(a.hi, b.hi); double t1 = multiply_add(a.hi, b.lo, r.lo); double t2 = multiply_add(a.lo, b.hi, t1); r.lo = t2; @@ -157,19 +165,20 @@ // rl = q * (ah - bh * rh) + q * (al - bl * rh) // as accurate as possible, then the error is bounded by: // |(ah + al) / (bh + bl) - (rh + rl)| < O(bl/bh) * (2^-52 + al/ah + bl/bh) -LIBC_INLINE DoubleDouble div(const DoubleDouble &a, const DoubleDouble &b) { - DoubleDouble r; - double q = 1.0 / b.hi; +template <typename T> +LIBC_INLINE NumberPair<T> div(const NumberPair<T> &a, const NumberPair<T> &b) { + NumberPair<T> r; + T q = T(1) / b.hi; r.hi = a.hi * q; #ifdef LIBC_TARGET_CPU_HAS_FMA - double e_hi = fputil::multiply_add(b.hi, -r.hi, a.hi); - double e_lo = fputil::multiply_add(b.lo, -r.hi, a.lo); + T e_hi = fputil::multiply_add(b.hi, -r.hi, a.hi); + T e_lo = fputil::multiply_add(b.lo, -r.hi, a.lo); #else - DoubleDouble b_hi_r_hi = fputil::exact_mult(b.hi, -r.hi); - DoubleDouble b_lo_r_hi = fputil::exact_mult(b.lo, -r.hi); - double e_hi = (a.hi + b_hi_r_hi.hi) + b_hi_r_hi.lo; - double e_lo = (a.lo + b_lo_r_hi.hi) + b_lo_r_hi.lo; + NumberPair<T> b_hi_r_hi = fputil::exact_mult(b.hi, -r.hi); + NumberPair<T> b_lo_r_hi = fputil::exact_mult(b.lo, -r.hi); + T e_hi = (a.hi + b_hi_r_hi.hi) + b_hi_r_hi.lo; + T e_lo = (a.lo + b_lo_r_hi.hi) + b_lo_r_hi.lo; #endif // LIBC_TARGET_CPU_HAS_FMA r.lo = q * (e_hi + e_lo);
diff --git a/src/__support/FPUtil/multiply_add.h b/src/__support/FPUtil/multiply_add.h index a86067c..ae00e08 100644 --- a/src/__support/FPUtil/multiply_add.h +++ b/src/__support/FPUtil/multiply_add.h
@@ -47,11 +47,19 @@ namespace fputil { LIBC_INLINE float multiply_add(float x, float y, float z) { +#if __has_builtin(__builtin_elementwise_fma) + return __builtin_elementwise_fma(x, y, z); +#else return __builtin_fmaf(x, y, z); +#endif } LIBC_INLINE double multiply_add(double x, double y, double z) { +#if __has_builtin(__builtin_elementwise_fma) + return __builtin_elementwise_fma(x, y, z); +#else return __builtin_fma(x, y, z); +#endif } } // namespace fputil
diff --git a/src/__support/big_int.h b/src/__support/big_int.h index f591b41..fb5ad99 100644 --- a/src/__support/big_int.h +++ b/src/__support/big_int.h
@@ -241,7 +241,7 @@ } template <typename word, size_t N> -LIBC_INLINE constexpr bool is_negative(cpp::array<word, N> &array) { +LIBC_INLINE constexpr bool is_negative(const cpp::array<word, N> &array) { using signed_word = cpp::make_signed_t<word>; return cpp::bit_cast<signed_word>(array.back()) < 0; }
diff --git a/src/__support/fixed_point/fx_bits.h b/src/__support/fixed_point/fx_bits.h index 225ea41..7509419 100644 --- a/src/__support/fixed_point/fx_bits.h +++ b/src/__support/fixed_point/fx_bits.h
@@ -11,9 +11,10 @@ #include "include/llvm-libc-macros/stdfix-macros.h" #include "src/__support/CPP/bit.h" +#include "src/__support/CPP/limits.h" // numeric_limits #include "src/__support/CPP/type_traits.h" -#include "src/__support/macros/attributes.h" // LIBC_INLINE -#include "src/__support/macros/config.h" +#include "src/__support/macros/attributes.h" // LIBC_INLINE +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY #include "src/__support/math_extras.h" @@ -50,6 +51,12 @@ static constexpr StorageType SIGN_MASK = (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET); + // mask for <integral | fraction> + static constexpr StorageType VALUE_MASK = INTEGRAL_MASK | FRACTION_MASK; + + // mask for <sign | integral | fraction> + static constexpr StorageType TOTAL_MASK = SIGN_MASK | VALUE_MASK; + public: LIBC_INLINE constexpr FXBits() = default; @@ -74,6 +81,12 @@ return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET; } + // returns complete bitstring representation the fixed point number + // the bitstring is of the form: padding | sign | integral | fraction + LIBC_INLINE constexpr StorageType get_bits() { + return (value & TOTAL_MASK) >> FRACTION_OFFSET; + } + // TODO: replace bool with Sign LIBC_INLINE constexpr bool get_sign() { return static_cast<bool>((value & SIGN_MASK) >> SIGN_OFFSET); @@ -163,6 +176,24 @@ return bit_and((x + round_bit), rounding_mask); } +// count leading sign bits +// TODO: support fixed_point_padding +template <typename T> +LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, int> +countls(T f) { + using FXRep = FXRep<T>; + using BitType = typename FXRep::StorageType; + using FXBits = FXBits<T>; + + if constexpr (FXRep::SIGN_LEN > 0) { + if (f < 0) + f = bit_not(f); + } + + BitType value_bits = FXBits(f).get_bits(); + return cpp::countl_zero(value_bits) - FXRep::SIGN_LEN; +} + } // namespace fixed_point } // namespace LIBC_NAMESPACE_DECL
diff --git a/src/__support/fixed_point/fx_rep.h b/src/__support/fixed_point/fx_rep.h index 1869389..7227fff 100644 --- a/src/__support/fixed_point/fx_rep.h +++ b/src/__support/fixed_point/fx_rep.h
@@ -43,8 +43,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = SFRACT_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return SFRACT_MIN; } LIBC_INLINE static constexpr Type MAX() { return SFRACT_MAX; } @@ -63,8 +63,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = USFRACT_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return USFRACT_MIN; } LIBC_INLINE static constexpr Type MAX() { return USFRACT_MAX; } @@ -83,8 +83,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = FRACT_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return FRACT_MIN; } LIBC_INLINE static constexpr Type MAX() { return FRACT_MAX; } @@ -103,8 +103,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = UFRACT_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return UFRACT_MIN; } LIBC_INLINE static constexpr Type MAX() { return UFRACT_MAX; } @@ -123,8 +123,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = LFRACT_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return LFRACT_MIN; } LIBC_INLINE static constexpr Type MAX() { return LFRACT_MAX; } @@ -143,8 +143,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = 0; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = ULFRACT_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return ULFRACT_MIN; } LIBC_INLINE static constexpr Type MAX() { return ULFRACT_MAX; } @@ -163,8 +163,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = SACCUM_IBIT; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = SACCUM_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return SACCUM_MIN; } LIBC_INLINE static constexpr Type MAX() { return SACCUM_MAX; } @@ -183,8 +183,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = USACCUM_IBIT; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = USACCUM_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return USACCUM_MIN; } LIBC_INLINE static constexpr Type MAX() { return USACCUM_MAX; } @@ -203,8 +203,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = ACCUM_IBIT; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = ACCUM_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return ACCUM_MIN; } LIBC_INLINE static constexpr Type MAX() { return ACCUM_MAX; } @@ -223,8 +223,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = UACCUM_IBIT; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = UACCUM_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return UACCUM_MIN; } LIBC_INLINE static constexpr Type MAX() { return UACCUM_MAX; } @@ -243,8 +243,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = LACCUM_IBIT; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = LACCUM_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return LACCUM_MIN; } LIBC_INLINE static constexpr Type MAX() { return LACCUM_MAX; } @@ -263,8 +263,8 @@ LIBC_INLINE_VAR static constexpr int SIGN_LEN = 0; LIBC_INLINE_VAR static constexpr int INTEGRAL_LEN = ULACCUM_IBIT; LIBC_INLINE_VAR static constexpr int FRACTION_LEN = ULACCUM_FBIT; - LIBC_INLINE_VAR static constexpr int TOTAL_LEN = - SIGN_LEN + INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int VALUE_LEN = INTEGRAL_LEN + FRACTION_LEN; + LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + VALUE_LEN; LIBC_INLINE static constexpr Type MIN() { return ULACCUM_MIN; } LIBC_INLINE static constexpr Type MAX() { return ULACCUM_MAX; }
diff --git a/src/__support/macros/optimization.h b/src/__support/macros/optimization.h index a263495..253843e 100644 --- a/src/__support/macros/optimization.h +++ b/src/__support/macros/optimization.h
@@ -45,6 +45,7 @@ #define LIBC_MATH_FAST \ (LIBC_MATH_SKIP_ACCURATE_PASS | LIBC_MATH_SMALL_TABLES | \ LIBC_MATH_NO_ERRNO | LIBC_MATH_NO_EXCEPT) +#define LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT 0x10 #ifndef LIBC_MATH #define LIBC_MATH 0 @@ -58,4 +59,8 @@ #define LIBC_MATH_HAS_SMALL_TABLES #endif +#if (LIBC_MATH & LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT) +#define LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT +#endif + #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_OPTIMIZATION_H
diff --git a/src/math/asinf16.h b/src/math/asinf16.h new file mode 100644 index 0000000..f16647e --- /dev/null +++ b/src/math/asinf16.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for asinf16 -----------------------*- 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_MATH_ASINF16_H +#define LLVM_LIBC_SRC_MATH_ASINF16_H + +#include "src/__support/macros/config.h" +#include "src/__support/macros/properties/types.h" + +namespace LIBC_NAMESPACE_DECL { + +float16 asinf16(float16 x); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_MATH_ASINF16_H
diff --git a/src/math/generic/asinf16.cpp b/src/math/generic/asinf16.cpp new file mode 100644 index 0000000..518c384 --- /dev/null +++ b/src/math/generic/asinf16.cpp
@@ -0,0 +1,133 @@ +//===-- Half-precision asinf16(x) function --------------------------------===// +// +// 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 "src/math/asinf16.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/multiply_add.h" +#include "src/__support/FPUtil/sqrt.h" +#include "src/__support/macros/optimization.h" + +namespace LIBC_NAMESPACE_DECL { + +// Generated by Sollya using the following command: +// > round(pi/2, D, RN); +static constexpr float PI_2 = 0x1.921fb54442d18p0f; + +LLVM_LIBC_FUNCTION(float16, asinf16, (float16 x)) { + using FPBits = fputil::FPBits<float16>; + FPBits xbits(x); + + uint16_t x_u = xbits.uintval(); + uint16_t x_abs = x_u & 0x7fff; + float xf = x; + + // |x| > 0x1p0, |x| > 1, or x is NaN. + if (LIBC_UNLIKELY(x_abs > 0x3c00)) { + // asinf16(NaN) = NaN + if (xbits.is_nan()) { + if (xbits.is_signaling_nan()) { + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + + return x; + } + + // 1 < |x| <= +/-inf + fputil::raise_except_if_required(FE_INVALID); + fputil::set_errno_if_required(EDOM); + + return FPBits::quiet_nan().get_val(); + } + + float xsq = xf * xf; + + // |x| <= 0x1p-1, |x| <= 0.5 + if (x_abs <= 0x3800) { + // asinf16(+/-0) = +/-0 + if (LIBC_UNLIKELY(x_abs == 0)) + return x; + + // Exhaustive tests show that, + // for |x| <= 0x1.878p-9, when: + // x > 0, and rounding upward, or + // x < 0, and rounding downward, then, + // asin(x) = x * 2^-11 + x + // else, in other rounding modes, + // asin(x) = x + if (LIBC_UNLIKELY(x_abs <= 0x1a1e)) { + int rounding = fputil::quick_get_round(); + + if ((xbits.is_pos() && rounding == FE_UPWARD) || + (xbits.is_neg() && rounding == FE_DOWNWARD)) + return fputil::cast<float16>(fputil::multiply_add(xf, 0x1.0p-11f, xf)); + return x; + } + + // Degree-6 minimax odd polynomial of asin(x) generated by Sollya with: + // > P = fpminimax(asin(x)/x, [|0, 2, 4, 6, 8|], [|SG...|], [0, 0.5]); + float result = + fputil::polyeval(xsq, 0x1.000002p0f, 0x1.554c2ap-3f, 0x1.3541ccp-4f, + 0x1.43b2d6p-5f, 0x1.a0d73ep-5f); + return fputil::cast<float16>(xf * result); + } + + // When |x| > 0.5, assume that 0.5 < |x| <= 1, + // + // Step-by-step range-reduction proof: + // 1: Let y = asin(x), such that, x = sin(y) + // 2: From complimentary angle identity: + // x = sin(y) = cos(pi/2 - y) + // 3: Let z = pi/2 - y, such that x = cos(z) + // 4: From double angle formula; cos(2A) = 1 - sin^2(A): + // z = 2A, z/2 = A + // cos(z) = 1 - 2 * sin^2(z/2) + // 5: Make sin(z/2) subject of the formula: + // sin(z/2) = sqrt((1 - cos(z))/2) + // 6: Recall [3]; x = cos(z). Therefore: + // sin(z/2) = sqrt((1 - x)/2) + // 7: Let u = (1 - x)/2 + // 8: Therefore: + // asin(sqrt(u)) = z/2 + // 2 * asin(sqrt(u)) = z + // 9: Recall [3], z = pi/2 - y. Therefore: + // y = pi/2 - z + // y = pi/2 - 2 * asin(sqrt(u)) + // 10: Recall [1], y = asin(x). Therefore: + // asin(x) = pi/2 - 2 * asin(sqrt(u)) + // + // WHY? + // 11: Recall [7], u = (1 - x)/2 + // 12: Since 0.5 < x <= 1, therefore: + // 0 <= u <= 0.25 and 0 <= sqrt(u) <= 0.5 + // + // Hence, we can reuse the same [0, 0.5] domain polynomial approximation for + // Step [10] as `sqrt(u)` is in range. + + // 0x1p-1 < |x| <= 0x1p0, 0.5 < |x| <= 1.0 + float xf_abs = (xf < 0 ? -xf : xf); + float sign = (xbits.uintval() >> 15 == 1 ? -1.0 : 1.0); + float u = fputil::multiply_add(-0.5f, xf_abs, 0.5f); + float u_sqrt = fputil::sqrt<float>(u); + + // Degree-6 minimax odd polynomial of asin(x) generated by Sollya with: + // > P = fpminimax(asin(x)/x, [|0, 2, 4, 6, 8|], [|SG...|], [0, 0.5]); + float asin_sqrt_u = + u_sqrt * fputil::polyeval(u, 0x1.000002p0f, 0x1.554c2ap-3f, + 0x1.3541ccp-4f, 0x1.43b2d6p-5f, 0x1.a0d73ep-5f); + + return fputil::cast<float16>(sign * + fputil::multiply_add(-2.0f, asin_sqrt_u, PI_2)); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/math/generic/atan2f.cpp b/src/math/generic/atan2f.cpp index db76393..5ac2b29 100644 --- a/src/math/generic/atan2f.cpp +++ b/src/math/generic/atan2f.cpp
@@ -17,6 +17,14 @@ #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY +#if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) && \ + defined(LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT) + +// We use float-float implementation to reduce size. +#include "src/math/generic/atan2f_float.h" + +#else + namespace LIBC_NAMESPACE_DECL { namespace { @@ -324,3 +332,5 @@ } } // namespace LIBC_NAMESPACE_DECL + +#endif
diff --git a/src/math/generic/atan2f_float.h b/src/math/generic/atan2f_float.h new file mode 100644 index 0000000..1fd853d --- /dev/null +++ b/src/math/generic/atan2f_float.h
@@ -0,0 +1,237 @@ +//===-- Single-precision atan2f function ----------------------------------===// +// +// 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 "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/double_double.h" +#include "src/__support/FPUtil/multiply_add.h" +#include "src/__support/FPUtil/nearest_integer.h" +#include "src/__support/FPUtil/rounding_mode.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY +#include "src/math/atan2f.h" + +namespace LIBC_NAMESPACE_DECL { + +namespace { + +using FloatFloat = fputil::FloatFloat; + +// atan(i/64) with i = 0..16, generated by Sollya with: +// > for i from 0 to 16 do { +// a = round(atan(i/16), SG, RN); +// b = round(atan(i/16) - a, SG, RN); +// print("{", b, ",", a, "},"); +// }; +constexpr FloatFloat ATAN_I[17] = { + {0.0f, 0.0f}, + {-0x1.1a6042p-30f, 0x1.ff55bcp-5f}, + {-0x1.54f424p-30f, 0x1.fd5baap-4f}, + {0x1.79cb6p-28f, 0x1.7b97b4p-3f}, + {-0x1.b4dfc8p-29f, 0x1.f5b76p-3f}, + {-0x1.1f0286p-27f, 0x1.362774p-2f}, + {0x1.e4defp-30f, 0x1.6f6194p-2f}, + {0x1.e611fep-29f, 0x1.a64eecp-2f}, + {0x1.586ed4p-28f, 0x1.dac67p-2f}, + {-0x1.6499e6p-26f, 0x1.0657eap-1f}, + {0x1.7bdfd6p-26f, 0x1.1e00bap-1f}, + {-0x1.98e422p-28f, 0x1.345f02p-1f}, + {0x1.934f7p-28f, 0x1.4978fap-1f}, + {0x1.c5a6c6p-27f, 0x1.5d5898p-1f}, + {0x1.5e118cp-27f, 0x1.700a7cp-1f}, + {-0x1.1d4eb6p-26f, 0x1.819d0cp-1f}, + {-0x1.777a5cp-26f, 0x1.921fb6p-1f}, +}; + +// Approximate atan(x) for |x| <= 2^-5. +// Using degree-3 Taylor polynomial: +// P = x - x^3/3 +// Then the absolute error is bounded by: +// |atan(x) - P(x)| < |x|^5/5 < 2^(-5*5) / 5 < 2^-27. +// And the relative error is bounded by: +// |(atan(x) - P(x))/atan(x)| < |x|^4 / 4 < 2^-22. +// For x = x_hi + x_lo, fully expand the polynomial and drop any terms less than +// ulp(x_hi^3 / 3) gives us: +// P(x) ~ x_hi - x_hi^3/3 + x_lo * (1 - x_hi^2) +FloatFloat atan_eval(const FloatFloat &x) { + FloatFloat p; + p.hi = x.hi; + float x_hi_sq = x.hi * x.hi; + // c0 ~ - x_hi^2 / 3 + float c0 = -0x1.555556p-2f * x_hi_sq; + // c1 ~ x_lo * (1 - x_hi^2) + float c1 = fputil::multiply_add(x_hi_sq, -x.lo, x.lo); + // p.lo ~ - x_hi^3 / 3 + x_lo * (1 - x_hi*2) + p.lo = fputil::multiply_add(x.hi, c0, c1); + return p; +} + +} // anonymous namespace + +// There are several range reduction steps we can take for atan2(y, x) as +// follow: + +// * Range reduction 1: signness +// atan2(y, x) will return a number between -PI and PI representing the angle +// forming by the 0x axis and the vector (x, y) on the 0xy-plane. +// In particular, we have that: +// atan2(y, x) = atan( y/x ) if x >= 0 and y >= 0 (I-quadrant) +// = pi + atan( y/x ) if x < 0 and y >= 0 (II-quadrant) +// = -pi + atan( y/x ) if x < 0 and y < 0 (III-quadrant) +// = atan( y/x ) if x >= 0 and y < 0 (IV-quadrant) +// Since atan function is odd, we can use the formula: +// atan(-u) = -atan(u) +// to adjust the above conditions a bit further: +// atan2(y, x) = atan( |y|/|x| ) if x >= 0 and y >= 0 (I-quadrant) +// = pi - atan( |y|/|x| ) if x < 0 and y >= 0 (II-quadrant) +// = -pi + atan( |y|/|x| ) if x < 0 and y < 0 (III-quadrant) +// = -atan( |y|/|x| ) if x >= 0 and y < 0 (IV-quadrant) +// Which can be simplified to: +// atan2(y, x) = sign(y) * atan( |y|/|x| ) if x >= 0 +// = sign(y) * (pi - atan( |y|/|x| )) if x < 0 + +// * Range reduction 2: reciprocal +// Now that the argument inside atan is positive, we can use the formula: +// atan(1/x) = pi/2 - atan(x) +// to make the argument inside atan <= 1 as follow: +// atan2(y, x) = sign(y) * atan( |y|/|x|) if 0 <= |y| <= x +// = sign(y) * (pi/2 - atan( |x|/|y| ) if 0 <= x < |y| +// = sign(y) * (pi - atan( |y|/|x| )) if 0 <= |y| <= -x +// = sign(y) * (pi/2 + atan( |x|/|y| )) if 0 <= -x < |y| + +// * Range reduction 3: look up table. +// After the previous two range reduction steps, we reduce the problem to +// compute atan(u) with 0 <= u <= 1, or to be precise: +// atan( n / d ) where n = min(|x|, |y|) and d = max(|x|, |y|). +// An accurate polynomial approximation for the whole [0, 1] input range will +// require a very large degree. To make it more efficient, we reduce the input +// range further by finding an integer idx such that: +// | n/d - idx/16 | <= 1/32. +// In particular, +// idx := 2^-4 * round(2^4 * n/d) +// Then for the fast pass, we find a polynomial approximation for: +// atan( n/d ) ~ atan( idx/16 ) + (n/d - idx/16) * Q(n/d - idx/16) +// with Q(x) = x - x^3/3 be the cubic Taylor polynomial of atan(x). +// It's error in float-float precision is estimated in Sollya to be: +// > P = x - x^3/3; +// > dirtyinfnorm(atan(x) - P, [-2^-5, 2^-5]); +// 0x1.995...p-28. + +LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) { + using FPBits = typename fputil::FPBits<float>; + constexpr float IS_NEG[2] = {1.0f, -1.0f}; + constexpr FloatFloat ZERO = {0.0f, 0.0f}; + constexpr FloatFloat MZERO = {-0.0f, -0.0f}; + constexpr FloatFloat PI = {-0x1.777a5cp-24f, 0x1.921fb6p1f}; + constexpr FloatFloat MPI = {0x1.777a5cp-24f, -0x1.921fb6p1f}; + constexpr FloatFloat PI_OVER_4 = {-0x1.777a5cp-26f, 0x1.921fb6p-1f}; + constexpr FloatFloat PI_OVER_2 = {-0x1.777a5cp-25f, 0x1.921fb6p0f}; + constexpr FloatFloat MPI_OVER_2 = {-0x1.777a5cp-25f, 0x1.921fb6p0f}; + constexpr FloatFloat THREE_PI_OVER_4 = {-0x1.99bc5cp-28f, 0x1.2d97c8p1f}; + // Adjustment for constant term: + // CONST_ADJ[x_sign][y_sign][recip] + constexpr FloatFloat CONST_ADJ[2][2][2] = { + {{ZERO, MPI_OVER_2}, {MZERO, MPI_OVER_2}}, + {{MPI, PI_OVER_2}, {MPI, PI_OVER_2}}}; + + FPBits x_bits(x), y_bits(y); + bool x_sign = x_bits.sign().is_neg(); + bool y_sign = y_bits.sign().is_neg(); + x_bits = x_bits.abs(); + y_bits = y_bits.abs(); + uint32_t x_abs = x_bits.uintval(); + uint32_t y_abs = y_bits.uintval(); + bool recip = x_abs < y_abs; + uint32_t min_abs = recip ? x_abs : y_abs; + uint32_t max_abs = !recip ? x_abs : y_abs; + auto min_exp = static_cast<unsigned>(min_abs >> FPBits::FRACTION_LEN); + auto max_exp = static_cast<unsigned>(max_abs >> FPBits::FRACTION_LEN); + + float num = FPBits(min_abs).get_val(); + float den = FPBits(max_abs).get_val(); + + // Check for exceptional cases, whether inputs are 0, inf, nan, or close to + // overflow, or close to underflow. + if (LIBC_UNLIKELY(max_exp > 0xffU - 64U || min_exp < 64U)) { + if (x_bits.is_nan() || y_bits.is_nan()) + return FPBits::quiet_nan().get_val(); + unsigned x_except = x == 0.0f ? 0 : (FPBits(x_abs).is_inf() ? 2 : 1); + unsigned y_except = y == 0.0f ? 0 : (FPBits(y_abs).is_inf() ? 2 : 1); + + // Exceptional cases: + // EXCEPT[y_except][x_except][x_is_neg] + // with x_except & y_except: + // 0: zero + // 1: finite, non-zero + // 2: infinity + constexpr FloatFloat EXCEPTS[3][3][2] = { + {{ZERO, PI}, {ZERO, PI}, {ZERO, PI}}, + {{PI_OVER_2, PI_OVER_2}, {ZERO, ZERO}, {ZERO, PI}}, + {{PI_OVER_2, PI_OVER_2}, + {PI_OVER_2, PI_OVER_2}, + {PI_OVER_4, THREE_PI_OVER_4}}, + }; + + if ((x_except != 1) || (y_except != 1)) { + FloatFloat r = EXCEPTS[y_except][x_except][x_sign]; + return fputil::multiply_add(IS_NEG[y_sign], r.hi, IS_NEG[y_sign] * r.lo); + } + bool scale_up = min_exp < 64U; + bool scale_down = max_exp > 0xffU - 64U; + // At least one input is denormal, multiply both numerator and denominator + // by some large enough power of 2 to normalize denormal inputs. + if (scale_up) { + num *= 0x1.0p32f; + if (!scale_down) + den *= 0x1.0p32f; + } else if (scale_down) { + den *= 0x1.0p-32f; + num *= 0x1.0p-32f; + } + + min_abs = FPBits(num).uintval(); + max_abs = FPBits(den).uintval(); + min_exp = static_cast<unsigned>(min_abs >> FPBits::FRACTION_LEN); + max_exp = static_cast<unsigned>(max_abs >> FPBits::FRACTION_LEN); + } + + float final_sign = IS_NEG[(x_sign != y_sign) != recip]; + FloatFloat const_term = CONST_ADJ[x_sign][y_sign][recip]; + unsigned exp_diff = max_exp - min_exp; + // We have the following bound for normalized n and d: + // 2^(-exp_diff - 1) < n/d < 2^(-exp_diff + 1). + if (LIBC_UNLIKELY(exp_diff > 25)) + return fputil::multiply_add(final_sign, const_term.hi, + final_sign * (const_term.lo + num / den)); + + float k = fputil::nearest_integer(16.0f * num / den); + unsigned idx = static_cast<unsigned>(k); + // k = idx / 16 + k *= 0x1.0p-4f; + + // Range reduction: + // atan(n/d) - atan(k/64) = atan((n/d - k/16) / (1 + (n/d) * (k/16))) + // = atan((n - d * k/16)) / (d + n * k/16)) + FloatFloat num_k = fputil::exact_mult(num, k); + FloatFloat den_k = fputil::exact_mult(den, k); + + // num_dd = n - d * k + FloatFloat num_ff = fputil::exact_add(num - den_k.hi, -den_k.lo); + // den_dd = d + n * k + FloatFloat den_ff = fputil::exact_add(den, num_k.hi); + den_ff.lo += num_k.lo; + + // q = (n - d * k) / (d + n * k) + FloatFloat q = fputil::div(num_ff, den_ff); + // p ~ atan(q) + FloatFloat p = atan_eval(q); + + FloatFloat r = fputil::add(const_term, fputil::add(ATAN_I[idx], p)); + return final_sign * r.hi; +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/math/generic/pow.cpp b/src/math/generic/pow.cpp index 213dbd9..a2a0bb6 100644 --- a/src/math/generic/pow.cpp +++ b/src/math/generic/pow.cpp
@@ -400,7 +400,7 @@ #else double c = FPBits(m_x.uintval() & 0x3fff'e000'0000'0000).get_val(); dx = fputil::multiply_add(RD[idx_x], m_x.get_val() - c, CD[idx_x]); // Exact - dx_c0 = fputil::exact_mult<28>(dx, COEFFS[0]); // Exact + dx_c0 = fputil::exact_mult<double, 28>(dx, COEFFS[0]); // Exact #endif // LIBC_TARGET_CPU_HAS_FMA double dx2 = dx * dx;
diff --git a/src/math/generic/range_reduction_double_common.h b/src/math/generic/range_reduction_double_common.h index 06aeb49..711a122 100644 --- a/src/math/generic/range_reduction_double_common.h +++ b/src/math/generic/range_reduction_double_common.h
@@ -21,7 +21,7 @@ namespace LIBC_NAMESPACE_DECL { #ifdef LIBC_TARGET_CPU_HAS_FMA -static constexpr unsigned SPLIT = DEFAULT_DOUBLE_SPLIT; +static constexpr unsigned SPLIT = fputil::DefaultSplit<double>::VALUE; #else // When there is no-FMA instructions, in order to have exact product of 2 double // precision with directional roundings, we need to lower the precision of the
diff --git a/src/math/generic/range_reduction_double_fma.h b/src/math/generic/range_reduction_double_fma.h index cab031c..8e0bc3a 100644 --- a/src/math/generic/range_reduction_double_fma.h +++ b/src/math/generic/range_reduction_double_fma.h
@@ -33,14 +33,14 @@ // 2^62 <= |x_reduced| < 2^(62 + 16) = 2^78 x_reduced = xbits.get_val(); // x * c_hi = ph.hi + ph.lo exactly. - DoubleDouble ph = - fputil::exact_mult<SPLIT>(x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][0]); + DoubleDouble ph = fputil::exact_mult<double, SPLIT>( + x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][0]); // x * c_mid = pm.hi + pm.lo exactly. - DoubleDouble pm = - fputil::exact_mult<SPLIT>(x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][1]); + DoubleDouble pm = fputil::exact_mult<double, SPLIT>( + x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][1]); // x * c_lo = pl.hi + pl.lo exactly. - DoubleDouble pl = - fputil::exact_mult<SPLIT>(x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][2]); + DoubleDouble pl = fputil::exact_mult<double, SPLIT>( + x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][2]); // Extract integral parts and fractional parts of (ph.lo + pm.hi). double sum_hi = ph.lo + pm.hi; double kd = fputil::nearest_integer(sum_hi);
diff --git a/src/math/generic/range_reduction_double_nofma.h b/src/math/generic/range_reduction_double_nofma.h index 5640732..606c3f8 100644 --- a/src/math/generic/range_reduction_double_nofma.h +++ b/src/math/generic/range_reduction_double_nofma.h
@@ -34,14 +34,14 @@ x_reduced = xbits.get_val(); // x * c_hi = ph.hi + ph.lo exactly. DoubleDouble x_split = fputil::split(x_reduced); - DoubleDouble ph = fputil::exact_mult<SPLIT>(x_split, x_reduced, - ONE_TWENTY_EIGHT_OVER_PI[idx][0]); + DoubleDouble ph = fputil::exact_mult<double, SPLIT>( + x_split, x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][0]); // x * c_mid = pm.hi + pm.lo exactly. - DoubleDouble pm = fputil::exact_mult<SPLIT>(x_split, x_reduced, - ONE_TWENTY_EIGHT_OVER_PI[idx][1]); + DoubleDouble pm = fputil::exact_mult<double, SPLIT>( + x_split, x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][1]); // x * c_lo = pl.hi + pl.lo exactly. - DoubleDouble pl = fputil::exact_mult<SPLIT>(x_split, x_reduced, - ONE_TWENTY_EIGHT_OVER_PI[idx][2]); + DoubleDouble pl = fputil::exact_mult<double, SPLIT>( + x_split, x_reduced, ONE_TWENTY_EIGHT_OVER_PI[idx][2]); // Extract integral parts and fractional parts of (ph.lo + pm.hi). double sum_hi = ph.lo + pm.hi; double kd = fputil::nearest_integer(sum_hi);
diff --git a/src/math/generic/sqrtf128.cpp b/src/math/generic/sqrtf128.cpp index f87066b..c844d3a 100644 --- a/src/math/generic/sqrtf128.cpp +++ b/src/math/generic/sqrtf128.cpp
@@ -7,14 +7,431 @@ //===----------------------------------------------------------------------===// #include "src/math/sqrtf128.h" -#include "src/__support/FPUtil/sqrt.h" +#include "src/__support/CPP/bit.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.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/__support/uint128.h" + +// Compute sqrtf128 with correct rounding for all rounding modes using integer +// arithmetic by Alexei Sibidanov (sibid@uvic.ca): +// https://github.com/sibidanov/llvm-project/tree/as_sqrt_v2 +// https://github.com/sibidanov/llvm-project/tree/as_sqrt_v3 +// TODO: Update the reference once Alexei's implementation is in the CORE-MATH +// project. https://github.com/llvm/llvm-project/issues/126794 + +// Let the input be expressed as x = 2^e * m_x, +// - Step 1: Range reduction +// Let x_reduced = 2^(e % 2) * m_x, +// Then sqrt(x) = 2^(e / 2) * sqrt(x_reduced), with +// 1 <= x_reduced < 4. +// - Step 2: Polynomial approximation +// Approximate 1/sqrt(x_reduced) using polynomial approximation with the +// result errors bounded by: +// |r0 - 1/sqrt(x_reduced)| < 2^-32. +// The computations are done in uint64_t. +// - Step 3: First Newton iteration +// Let the scaled error defined by: +// h0 = r0^2 * x_reduced - 1. +// Then we compute the first Newton iteration: +// r1 = r0 - r0 * h0 / 2. +// The result is then bounded by: +// |r1 - 1 / sqrt(x_reduced)| < 2^-62. +// - Step 4: Second Newton iteration +// We calculate the scaled error from Step 3: +// h1 = r1^2 * x_reduced - 1. +// Then the second Newton iteration is computed by: +// r2 = x_reduced * (r1 - r1 * h0 / 2) +// ~ x_reduced * (1/sqrt(x_reduced)) = sqrt(x_reduced) +// - Step 5: Perform rounding test and correction if needed. +// Rounding correction is done by computing the exact rounding errors: +// x_reduced - r2^2. namespace LIBC_NAMESPACE_DECL { +using FPBits = fputil::FPBits<float128>; + +namespace { + +template <typename T, typename U = T> static inline constexpr T prod_hi(T, U); + +// Get high part of integer multiplications. +// Use template to prevent implicit conversion. +template <> +inline constexpr uint64_t prod_hi<uint64_t>(uint64_t x, uint64_t y) { + return static_cast<uint64_t>( + (static_cast<UInt128>(x) * static_cast<UInt128>(y)) >> 64); +} + +// Get high part of unsigned 128x64 bit multiplication. +template <> +inline constexpr UInt128 prod_hi<UInt128, uint64_t>(UInt128 x, uint64_t y) { + uint64_t x_lo = static_cast<uint64_t>(x); + uint64_t x_hi = static_cast<uint64_t>(x >> 64); + UInt128 xyl = static_cast<UInt128>(x_lo) * static_cast<UInt128>(y); + UInt128 xyh = static_cast<UInt128>(x_hi) * static_cast<UInt128>(y); + return xyh + (xyl >> 64); +} + +// Get high part of signed 64x64 bit multiplication. +template <> inline constexpr int64_t prod_hi<int64_t>(int64_t x, int64_t y) { + return static_cast<int64_t>( + (static_cast<Int128>(x) * static_cast<Int128>(y)) >> 64); +} + +// Get high 128-bit part of unsigned 128x128 bit multiplication. +template <> inline constexpr UInt128 prod_hi<UInt128>(UInt128 x, UInt128 y) { + uint64_t x_lo = static_cast<uint64_t>(x); + uint64_t x_hi = static_cast<uint64_t>(x >> 64); + uint64_t y_lo = static_cast<uint64_t>(y); + uint64_t y_hi = static_cast<uint64_t>(y >> 64); + + UInt128 xh_yh = static_cast<UInt128>(x_hi) * static_cast<UInt128>(y_hi); + UInt128 xh_yl = static_cast<UInt128>(x_hi) * static_cast<UInt128>(y_lo); + UInt128 xl_yh = static_cast<UInt128>(x_lo) * static_cast<UInt128>(y_hi); + + xh_yh += xh_yl >> 64; + + return xh_yh + (xl_yh >> 64); +} + +// Get high 128-bit part of mixed sign 128x128 bit multiplication. +template <> +inline constexpr Int128 prod_hi<Int128, UInt128>(Int128 x, UInt128 y) { + UInt128 mask = static_cast<UInt128>(x >> 127); + UInt128 negative_part = y & mask; + UInt128 prod = prod_hi(static_cast<UInt128>(x), y); + return static_cast<Int128>(prod - negative_part); +} + +// Newton-Raphson first order step to improve accuracy of the result. +// For the initial approximation r0 ~ 1/sqrt(x), let +// h = r0^2 * x - 1 +// be its scaled error. Then the first-order Newton-Raphson iteration is: +// r1 = r0 - r0 * h / 2 +// which has error bounded by: +// |r1 - 1/sqrt(x)| < h^2 / 2. +LIBC_INLINE uint64_t rsqrt_newton_raphson(uint64_t m, uint64_t r) { + uint64_t r2 = prod_hi(r, r); + // h = r0^2*x - 1. + int64_t h = static_cast<int64_t>(prod_hi(m, r2) + r2); + // hr = r * h / 2 + int64_t hr = prod_hi(h, static_cast<int64_t>(r >> 1)); + return r - hr; +} + +#ifdef LIBC_MATH_HAS_SMALL_TABLES +// Degree-12 minimax polynomials for 1/sqrt(x) on [1, 2]. +constexpr uint32_t RSQRT_COEFFS[12] = { + 0xb5947a4a, 0x2d651e32, 0x9ad50532, 0x2d28d093, 0x0d8be653, 0x04239014, + 0x01492449, 0x0066ff7d, 0x001e74a1, 0x000984cc, 0x00049abc, 0x00018340, +}; + +LIBC_INLINE uint64_t rsqrt_approx(uint64_t m) { + int64_t x = static_cast<uint64_t>(m) ^ (uint64_t(1) << 63); + int64_t x_26 = x >> 2; + int64_t z = x >> 31; + + if (LIBC_UNLIKELY(z <= -4294967296)) + return ~(m >> 1); + + uint64_t x2 = static_cast<uint64_t>(z) * static_cast<uint64_t>(z); + uint64_t x2_26 = x2 >> 5; + x2 >>= 32; + // Calculate the odd part of the polynomial using Horner's method. + uint64_t c0 = RSQRT_COEFFS[8] + ((x2 * RSQRT_COEFFS[10]) >> 32); + uint64_t c1 = RSQRT_COEFFS[6] + ((x2 * c0) >> 32); + uint64_t c2 = RSQRT_COEFFS[4] + ((x2 * c1) >> 32); + uint64_t c3 = RSQRT_COEFFS[2] + ((x2 * c2) >> 32); + uint64_t c4 = RSQRT_COEFFS[0] + ((x2 * c3) >> 32); + uint64_t odd = + static_cast<uint64_t>((x >> 34) * static_cast<int64_t>(c4 >> 3)) + x_26; + // Calculate the even part of the polynomial using Horner's method. + uint64_t d0 = RSQRT_COEFFS[9] + ((x2 * RSQRT_COEFFS[11]) >> 32); + uint64_t d1 = RSQRT_COEFFS[7] + ((x2 * d0) >> 32); + uint64_t d2 = RSQRT_COEFFS[5] + ((x2 * d1) >> 32); + uint64_t d3 = RSQRT_COEFFS[3] + ((x2 * d2) >> 32); + uint64_t d4 = RSQRT_COEFFS[1] + ((x2 * d3) >> 32); + uint64_t even = 0xd105eb806655d608ul + ((x2 * d4) >> 6) + x2_26; + + uint64_t r = even - odd; // error < 1.5e-10 + // Newton-Raphson first order step to improve accuracy of the result to almost + // 64 bits. + return rsqrt_newton_raphson(m, r); +} + +#else +// Cubic minimax polynomials for 1/sqrt(x) on [1 + k/64, 1 + (k + 1)/64] +// for k = 0..63. +constexpr uint32_t RSQRT_COEFFS[64][4] = { + {0xffffffff, 0xfffff780, 0xbff55815, 0x9bb5b6e7}, + {0xfc0bd889, 0xfa1d6e7d, 0xb8a95a89, 0x938bf8f0}, + {0xf82ec882, 0xf473bea9, 0xb1bf4705, 0x8bed0079}, + {0xf467f280, 0xeefff2a1, 0xab309d4a, 0x84cdb431}, + {0xf0b6848c, 0xe9bf46f4, 0xa4f76232, 0x7e24037b}, + {0xed19b75e, 0xe4af2628, 0x9f0e1340, 0x77e6ca62}, + {0xe990cdad, 0xdfcd2521, 0x996f9b96, 0x720db8df}, + {0xe61b138e, 0xdb16ffde, 0x94174a00, 0x6c913cff}, + {0xe2b7dddf, 0xd68a967b, 0x8f00c812, 0x676a6f92}, + {0xdf6689b7, 0xd225ea80, 0x8a281226, 0x62930308}, + {0xdc267bea, 0xcde71c63, 0x8589702c, 0x5e05343e}, + {0xd8f7208e, 0xc9cc6948, 0x81216f2e, 0x59bbbcf8}, + {0xd5d7ea91, 0xc5d428ee, 0x7cecdb76, 0x55b1c7d6}, + {0xd2c8534e, 0xc1fccbc9, 0x78e8bb45, 0x51e2e592}, + {0xcfc7da32, 0xbe44d94a, 0x75124a0a, 0x4e4b0369}, + {0xccd6045f, 0xbaaaee41, 0x7166f40f, 0x4ae66284}, + {0xc9f25c5c, 0xb72dbb69, 0x6de45288, 0x47b19045}, + {0xc71c71c7, 0xb3cc040f, 0x6a882804, 0x44a95f5f}, + {0xc453d90f, 0xb0849cd4, 0x67505d2a, 0x41cae1a0}, + {0xc1982b2e, 0xad566a85, 0x643afdc8, 0x3f13625c}, + {0xbee9056f, 0xaa406113, 0x6146361f, 0x3c806169}, + {0xbc46092e, 0xa7418293, 0x5e70506d, 0x3a0f8e8e}, + {0xb9aedba5, 0xa458de58, 0x5bb7b2b1, 0x37bec572}, + {0xb72325b7, 0xa1859022, 0x591adc9a, 0x358c09e2}, + {0xb4a293c2, 0x9ec6bf52, 0x569865a7, 0x33758476}, + {0xb22cd56d, 0x9c1b9e36, 0x542efb6a, 0x31797f8a}, + {0xafc19d86, 0x9983695c, 0x51dd5ffb, 0x2f96647a}, + {0xad60a1d1, 0x96fd66f7, 0x4fa2687c, 0x2dcab91f}, + {0xab099ae9, 0x9488e64b, 0x4d7cfbc9, 0x2c151d8a}, + {0xa8bc441a, 0x92253f20, 0x4b6c1139, 0x2a7449ef}, + {0xa6785b42, 0x8fd1d14a, 0x496eaf82, 0x28e70cc3}, + {0xa43da0ae, 0x8d8e042a, 0x4783eba7, 0x276c4900}, + {0xa20bd701, 0x8b594648, 0x45aae80a, 0x2602f493}, + {0x9fe2c315, 0x89330ce4, 0x43e2d382, 0x24aa16ec}, + {0x9dc22be4, 0x871ad399, 0x422ae88c, 0x2360c7af}, + {0x9ba9da6c, 0x85101c05, 0x40826c88, 0x22262d7b}, + {0x99999999, 0x83126d70, 0x3ee8af07, 0x20f97cd2}, + {0x97913630, 0x81215480, 0x3d5d0922, 0x1fd9f714}, + {0x95907eb8, 0x7f3c62ef, 0x3bdedce0, 0x1ec6e994}, + {0x93974369, 0x7d632f45, 0x3a6d94a9, 0x1dbfacbb}, + {0x91a55615, 0x7b955498, 0x3908a2be, 0x1cc3a33b}, + {0x8fba8a1c, 0x79d2724e, 0x37af80bf, 0x1bd23960}, + {0x8dd6b456, 0x781a2be4, 0x3661af39, 0x1aeae458}, + {0x8bf9ab07, 0x766c28ba, 0x351eb539, 0x1a0d21a2}, + {0x8a2345cc, 0x74c813dd, 0x33e61feb, 0x19387676}, + {0x88535d90, 0x732d9bdc, 0x32b7823a, 0x186c6f3e}, + {0x8689cc7e, 0x719c7297, 0x3192747d, 0x17a89f21}, + {0x84c66df1, 0x70144d19, 0x30769424, 0x16ec9f89}, + {0x83091e6a, 0x6e94e36c, 0x2f63836f, 0x16380fbf}, + {0x8151bb87, 0x6d1df079, 0x2e58e925, 0x158a9484}, + {0x7fa023f1, 0x6baf31de, 0x2d567053, 0x14e3d7ba}, + {0x7df43758, 0x6a4867d3, 0x2c5bc811, 0x1443880e}, + {0x7c4dd664, 0x68e95508, 0x2b68a346, 0x13a958ab}, + {0x7aace2b0, 0x6791be86, 0x2a7cb871, 0x131500ee}, + {0x79113ebc, 0x66416b95, 0x2997c17a, 0x12863c29}, + {0x777acde8, 0x64f825a1, 0x28b97b82, 0x11fcc95c}, + {0x75e9746a, 0x63b5b822, 0x27e1a6b4, 0x11786b03}, + {0x745d1746, 0x6279f081, 0x2710061d, 0x10f8e6da}, + {0x72d59c46, 0x61449e06, 0x26445f86, 0x107e05ac}, + {0x7152e9f4, 0x601591be, 0x257e7b4d, 0x10079327}, + {0x6fd4e793, 0x5eec9e6b, 0x24be2445, 0x0f955da9}, + {0x6e5b7d16, 0x5dc9986e, 0x24032795, 0x0f273620}, + {0x6ce6931d, 0x5cac55b7, 0x234d5496, 0x0ebcefdb}, + {0x6b7612ec, 0x5b94adb2, 0x229c7cbc, 0x0e56606e}, +}; + +// Approximate rsqrt with cubic polynomials. +// The range [1,2] is splitted into 64 equal sub-ranges and the reciprocal +// square root is approximated by a cubic polynomial by the minimax method in +// each subrange. The approximation accuracy fits into 32-33 bits and thus it is +// natural to round coefficients into 32 bit. The constant coefficient can be +// rounded to 33 bits since the most significant bit is always 1 and implicitly +// assumed in the table. +LIBC_INLINE uint64_t rsqrt_approx(uint64_t m) { + // ULP(m) = 2^-64. + // Use the top 6 bits as index for looking up polynomial coeffs. + uint64_t indx = m >> 58; + + uint64_t c0 = static_cast<uint64_t>(RSQRT_COEFFS[indx][0]); + c0 <<= 31; // to 64 bit with the space for the implicit bit + c0 |= 1ull << 63; // add implicit bit + + uint64_t c1 = static_cast<uint64_t>(RSQRT_COEFFS[indx][1]); + c1 <<= 25; // to 64 bit format + + uint64_t c2 = static_cast<uint64_t>(RSQRT_COEFFS[indx][2]); + uint64_t c3 = static_cast<uint64_t>(RSQRT_COEFFS[indx][3]); + + uint64_t d = (m << 6) >> 32; // local coordinate in the subrange [0, 2^32] + uint64_t d2 = (d * d) >> 32; // square of the local coordinate + uint64_t re = c0 + (d2 * c2 >> 13); // even part of the polynomial (positive) + uint64_t ro = d * ((c1 + ((d2 * c3) >> 19)) >> 26) >> + 6; // odd part of the polynomial (negative) + uint64_t r = re - ro; // maximal error < 1.55e-10 and it is less than 2^-32 + // Newton-Raphson first order step to improve accuracy of the result to almost + // 64 bits. + r = rsqrt_newton_raphson(m, r); + // Adjust in the unlucky case x~1; + if (LIBC_UNLIKELY(!r)) + --r; + return r; +} +#endif // LIBC_MATH_HAS_SMALL_TABLES + +} // anonymous namespace + LLVM_LIBC_FUNCTION(float128, sqrtf128, (float128 x)) { - return fputil::sqrt<float128>(x); + using FPBits = fputil::FPBits<float128>; + // Get rounding mode. + uint32_t rm = fputil::get_round(); + + FPBits xbits(x); + UInt128 x_u = xbits.uintval(); + // Bring leading bit of the mantissa to the highest bit. + // ulp(x_frac) = 2^-128. + UInt128 x_frac = xbits.get_mantissa() << (FPBits::EXP_LEN + 1); + + int sign_exp = static_cast<int>(x_u >> FPBits::FRACTION_LEN); + + if (LIBC_UNLIKELY(sign_exp == 0 || sign_exp >= 0x7fff)) { + // Special cases: NAN, inf, negative numbers + if (sign_exp >= 0x7fff) { + // x = -0 or x = inf + if (xbits.is_zero() || xbits == xbits.inf()) + return x; + // x is nan + if (xbits.is_nan()) { + // pass through quiet nan + if (xbits.is_quiet_nan()) + return x; + // transform signaling nan to quiet and return + return xbits.quiet_nan().get_val(); + } + // x < 0 or x = -inf + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_INVALID); + return xbits.quiet_nan().get_val(); + } + // Now x is subnormal or x = +0. + + // x is +0. + if (x_frac == 0) + return x; + + // Normalize subnormal inputs. + sign_exp = -cpp::countl_zero(x_frac); + int normal_shifts = 1 - sign_exp; + x_frac <<= normal_shifts; + } + + // For sign_exp = biased exponent of x = real_exponent + 16383, + // let f be the real exponent of the output: + // f = floor(real_exponent / 2) + // Then: + // floor((sign_exp + 1) / 2) = f + 8192 + // Hence, the biased exponent of the final result is: + // f + 16383 = floor((sign_exp + 1) / 2) + 8191. + // Since the output mantissa will include the hidden bit, we can define the + // output exponent part: + // e2 = floor((sign_exp + 1) / 2) + 8190 + unsigned i = static_cast<unsigned>(1 - (sign_exp & 1)); + uint32_t q2 = (sign_exp + 1) >> 1; + // Exponent of the final result + uint32_t e2 = q2 + 8190; + + constexpr uint64_t RSQRT_2[2] = {~0ull, + 0xb504f333f9de6484 /* 2^64/sqrt(2) */}; + + // Approximate 1/sqrt(1 + x_frac) + // Error: |r_1 - 1/sqrt(x)| < 2^-62. + uint64_t r1 = rsqrt_approx(static_cast<uint64_t>(x_frac >> 64)); + // Adjust for the even/odd exponent. + uint64_t r2 = prod_hi(r1, RSQRT_2[i]); + unsigned shift = 2 - i; + + // Normalized input: + // 1 <= x_reduced < 4 + UInt128 x_reduced = (x_frac >> shift) | (UInt128(1) << (126 + i)); + // With r2 ~ 1/sqrt(x) up to 2^-63, we perform another round of Newton-Raphson + // iteration: + // r3 = r2 - r2 * h / 2, + // for h = r2^2 * x - 1. + // Then: + // sqrt(x) = x * (1 / sqrt(x)) + // ~ x * r3 + // = x * (r2 - r2 * h / 2) + // = (x * r2) - (x * r2) * h / 2 + UInt128 sx = prod_hi(x_reduced, r2); + UInt128 h = prod_hi(sx, r2) << 2; + UInt128 ds = static_cast<UInt128>(prod_hi(static_cast<Int128>(h), sx)); + UInt128 v = (sx << 1) - ds; + + uint32_t nrst = rm == FE_TONEAREST; + // The result lies within (-2,5) of true square root so we now + // test that we can correctly round the result taking into account + // the rounding mode. + // Check the lowest 14 bits (by clearing and sign-extending the top + // 32 - 14 = 18 bits). + int dd = (static_cast<int>(v) << 18) >> 18; + + if (LIBC_UNLIKELY(dd < 4 && dd >= -8)) { // can round correctly? + // m is almost the final result it can be only 1 ulp off so we + // just need to test both possibilities. We square it and + // compare with the initial argument. + UInt128 m = v >> 15; + UInt128 m2 = m * m; + // The difference of the squared result and the argument + Int128 t0 = static_cast<Int128>(m2 - (x_reduced << 98)); + if (t0 == 0) { + // the square root is exact + v = m << 15; + } else { + // Add +-1 ulp to m depend on the sign of the difference. Here + // we do not need to square again since (m+1)^2 = m^2 + 2*m + + // 1 so just need to add shifted m and 1. + Int128 t1 = t0; + Int128 sgn = t0 >> 127; // sign of the difference + t1 -= (m << 1) ^ sgn; + t1 += 1 + sgn; + + Int128 sgn1 = t1 >> 127; + if (LIBC_UNLIKELY(sgn == sgn1)) { + t0 = t1; + v -= sgn << 15; + t1 -= (m << 1) ^ sgn; + t1 += 1 + sgn; + } + + if (t1 == 0) { + // 1 ulp offset brings again an exact root + v = (m - (2 * sgn + 1)) << 15; + } else { + t1 += t0; + Int128 side = t1 >> 127; // select what is closer m or m+-1 + v &= ~UInt128(0) << 15; // wipe the fractional bits + v -= ((sgn & side) | (~sgn & 1)) << (15 + side); + v |= 1; // add sticky bit since we cannot have an exact mid-point + // situation + } + } + } + + unsigned frac = static_cast<unsigned>(v) & 0x7fff; // fractional part + unsigned rnd; // round bit + if (LIBC_LIKELY(nrst != 0)) { + rnd = frac >> 14; // round to nearest tie to even + } else if (rm == FE_UPWARD) { + rnd = !!frac; // round up + } else { + rnd = 0; // round down or round to zero + } + + v >>= 15; // position mantissa + v += rnd; // round + + // Set inexact flag only if square root is inexact + // TODO: We will have to raise FE_INEXACT most of the time, but this + // operation is very costly, especially in x86-64, since technically, it + // needs to synchronize both SSE and x87 flags. Need to investigate + // further to see how we can make this performant. + // https://github.com/llvm/llvm-project/issues/126753 + + // if(frac) fputil::raise_except_if_required(FE_INEXACT); + + v += static_cast<UInt128>(e2) << FPBits::FRACTION_LEN; // place exponent + return cpp::bit_cast<float128>(v); } } // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlshk.cpp b/src/stdfix/countlshk.cpp new file mode 100644 index 0000000..f94728b --- /dev/null +++ b/src/stdfix/countlshk.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlshk function ----------------------------===// +// +// 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 "countlshk.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlshk, (short accum f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlshk.h b/src/stdfix/countlshk.h new file mode 100644 index 0000000..ab33424 --- /dev/null +++ b/src/stdfix/countlshk.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlshk 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_SRC_STDFIX_COUNTLSHK_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSHK_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlshk(short accum f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSHK_H
diff --git a/src/stdfix/countlshr.cpp b/src/stdfix/countlshr.cpp new file mode 100644 index 0000000..d77d3e9 --- /dev/null +++ b/src/stdfix/countlshr.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlshr function ----------------------------===// +// +// 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 "countlshr.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlshr, (short fract f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlshr.h b/src/stdfix/countlshr.h new file mode 100644 index 0000000..579b7b6 --- /dev/null +++ b/src/stdfix/countlshr.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlshr 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_SRC_STDFIX_COUNTLSHR_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSHR_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlshr(short fract f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSHR_H
diff --git a/src/stdfix/countlsk.cpp b/src/stdfix/countlsk.cpp new file mode 100644 index 0000000..b6f56ad --- /dev/null +++ b/src/stdfix/countlsk.cpp
@@ -0,0 +1,18 @@ +//===-- Implementation for countlsk function -----------------------------===// +// +// 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 "countlsk.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlsk, (accum f)) { return fixed_point::countls(f); } + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsk.h b/src/stdfix/countlsk.h new file mode 100644 index 0000000..d0c893b --- /dev/null +++ b/src/stdfix/countlsk.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlsk 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_SRC_STDFIX_COUNTLSK_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSK_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlsk(accum f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSK_H
diff --git a/src/stdfix/countlslk.cpp b/src/stdfix/countlslk.cpp new file mode 100644 index 0000000..9bf30ff --- /dev/null +++ b/src/stdfix/countlslk.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlslk function ----------------------------===// +// +// 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 "countlslk.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlslk, (long accum f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlslk.h b/src/stdfix/countlslk.h new file mode 100644 index 0000000..60fa469 --- /dev/null +++ b/src/stdfix/countlslk.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlslk 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_SRC_STDFIX_COUNTLSLK_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSLK_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlslk(long accum f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSLK_H
diff --git a/src/stdfix/countlslr.cpp b/src/stdfix/countlslr.cpp new file mode 100644 index 0000000..774023c --- /dev/null +++ b/src/stdfix/countlslr.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlslr function ----------------------------===// +// +// 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 "countlslr.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlslr, (long fract f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlslr.h b/src/stdfix/countlslr.h new file mode 100644 index 0000000..c909551 --- /dev/null +++ b/src/stdfix/countlslr.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlslr 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_SRC_STDFIX_COUNTLSLR_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSLR_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlslr(long fract f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSLR_H
diff --git a/src/stdfix/countlsr.cpp b/src/stdfix/countlsr.cpp new file mode 100644 index 0000000..1456312 --- /dev/null +++ b/src/stdfix/countlsr.cpp
@@ -0,0 +1,18 @@ +//===-- Implementation for countlsr function -----------------------------===// +// +// 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 "countlsr.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlsr, (fract f)) { return fixed_point::countls(f); } + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsr.h b/src/stdfix/countlsr.h new file mode 100644 index 0000000..75dcf4a --- /dev/null +++ b/src/stdfix/countlsr.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlsr 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_SRC_STDFIX_COUNTLSR_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSR_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlsr(fract f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSR_H
diff --git a/src/stdfix/countlsuhk.cpp b/src/stdfix/countlsuhk.cpp new file mode 100644 index 0000000..2cc266f --- /dev/null +++ b/src/stdfix/countlsuhk.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlsuhk function ---------------------------===// +// +// 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 "countlsuhk.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlsuhk, (unsigned short accum f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsuhk.h b/src/stdfix/countlsuhk.h new file mode 100644 index 0000000..fcb2fec --- /dev/null +++ b/src/stdfix/countlsuhk.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlsuhk 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_SRC_STDFIX_COUNTLSUHK_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSUHK_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlsuhk(unsigned short accum f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSUHK_H
diff --git a/src/stdfix/countlsuhr.cpp b/src/stdfix/countlsuhr.cpp new file mode 100644 index 0000000..f30b0dd --- /dev/null +++ b/src/stdfix/countlsuhr.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlsuhr function ---------------------------===// +// +// 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 "countlsuhr.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlsuhr, (unsigned short fract f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsuhr.h b/src/stdfix/countlsuhr.h new file mode 100644 index 0000000..b60132d --- /dev/null +++ b/src/stdfix/countlsuhr.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlsuhr 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_SRC_STDFIX_COUNTLSUHR_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSUHR_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlsuhr(unsigned short fract f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSUHR_H
diff --git a/src/stdfix/countlsuk.cpp b/src/stdfix/countlsuk.cpp new file mode 100644 index 0000000..90617cf --- /dev/null +++ b/src/stdfix/countlsuk.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlsuk function ----------------------------===// +// +// 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 "countlsuk.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlsuk, (unsigned accum f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsuk.h b/src/stdfix/countlsuk.h new file mode 100644 index 0000000..7ad0e70 --- /dev/null +++ b/src/stdfix/countlsuk.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlsuk 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_SRC_STDFIX_COUNTLSUK_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSUK_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlsuk(unsigned accum f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSUK_H
diff --git a/src/stdfix/countlsulk.cpp b/src/stdfix/countlsulk.cpp new file mode 100644 index 0000000..04090dd --- /dev/null +++ b/src/stdfix/countlsulk.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlsulk function ---------------------------===// +// +// 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 "countlsulk.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlsulk, (unsigned long accum f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsulk.h b/src/stdfix/countlsulk.h new file mode 100644 index 0000000..55ca9d2 --- /dev/null +++ b/src/stdfix/countlsulk.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlsulk 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_SRC_STDFIX_COUNTLSULK_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSULK_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlsulk(unsigned long accum f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSULK_H
diff --git a/src/stdfix/countlsulr.cpp b/src/stdfix/countlsulr.cpp new file mode 100644 index 0000000..d9d6ff4 --- /dev/null +++ b/src/stdfix/countlsulr.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlsulr function ---------------------------===// +// +// 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 "countlsulr.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlsulr, (unsigned long fract f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsulr.h b/src/stdfix/countlsulr.h new file mode 100644 index 0000000..59e7d72 --- /dev/null +++ b/src/stdfix/countlsulr.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlsulr 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_SRC_STDFIX_COUNTLSULR_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSULR_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlsulr(unsigned long fract f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSULR_H
diff --git a/src/stdfix/countlsur.cpp b/src/stdfix/countlsur.cpp new file mode 100644 index 0000000..777e5f3 --- /dev/null +++ b/src/stdfix/countlsur.cpp
@@ -0,0 +1,20 @@ +//===-- Implementation for countlsur function ----------------------------===// +// +// 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 "countlsur.h" +#include "src/__support/common.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, countlsur, (unsigned fract f)) { + return fixed_point::countls(f); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/stdfix/countlsur.h b/src/stdfix/countlsur.h new file mode 100644 index 0000000..1d34e97 --- /dev/null +++ b/src/stdfix/countlsur.h
@@ -0,0 +1,21 @@ +//===-- Implementation header for countlsur 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_SRC_STDFIX_COUNTLSUR_H +#define LLVM_LIBC_SRC_STDFIX_COUNTLSUR_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +int countlsur(unsigned fract f); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_COUNTLSUR_H
diff --git a/src/time/mktime.cpp b/src/time/mktime.cpp index 3874cad..fc05ff2 100644 --- a/src/time/mktime.cpp +++ b/src/time/mktime.cpp
@@ -14,100 +14,8 @@ namespace LIBC_NAMESPACE_DECL { -// Returns number of years from (1, year). -static constexpr int64_t get_num_of_leap_years_before(int64_t year) { - return (year / 4) - (year / 100) + (year / 400); -} - -// Returns True if year is a leap year. -static constexpr bool is_leap_year(const int64_t year) { - return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); -} - LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * tm_out)) { - // Unlike most C Library functions, mktime doesn't just die on bad input. - // TODO(rtenneti); Handle leap seconds. - int64_t tm_year_from_base = tm_out->tm_year + time_constants::TIME_YEAR_BASE; - - // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038. - if (sizeof(time_t) == 4 && - tm_year_from_base >= time_constants::END_OF32_BIT_EPOCH_YEAR) { - if (tm_year_from_base > time_constants::END_OF32_BIT_EPOCH_YEAR) - return time_utils::out_of_range(); - if (tm_out->tm_mon > 0) - return time_utils::out_of_range(); - if (tm_out->tm_mday > 19) - return time_utils::out_of_range(); - else if (tm_out->tm_mday == 19) { - if (tm_out->tm_hour > 3) - return time_utils::out_of_range(); - else if (tm_out->tm_hour == 3) { - if (tm_out->tm_min > 14) - return time_utils::out_of_range(); - else if (tm_out->tm_min == 14) { - if (tm_out->tm_sec > 7) - return time_utils::out_of_range(); - } - } - } - } - - // Years are ints. A 32-bit year will fit into a 64-bit time_t. - // A 64-bit year will not. - static_assert( - sizeof(int) == 4, - "ILP64 is unimplemented. This implementation requires 32-bit integers."); - - // Calculate number of months and years from tm_mon. - int64_t month = tm_out->tm_mon; - if (month < 0 || month >= time_constants::MONTHS_PER_YEAR - 1) { - int64_t years = month / 12; - month %= 12; - if (month < 0) { - years--; - month += 12; - } - tm_year_from_base += years; - } - bool tm_year_is_leap = is_leap_year(tm_year_from_base); - - // Calculate total number of days based on the month and the day (tm_mday). - int64_t total_days = tm_out->tm_mday - 1; - for (int64_t i = 0; i < month; ++i) - total_days += time_constants::NON_LEAP_YEAR_DAYS_IN_MONTH[i]; - // Add one day if it is a leap year and the month is after February. - if (tm_year_is_leap && month > 1) - total_days++; - - // Calculate total numbers of days based on the year. - total_days += (tm_year_from_base - time_constants::EPOCH_YEAR) * - time_constants::DAYS_PER_NON_LEAP_YEAR; - if (tm_year_from_base >= time_constants::EPOCH_YEAR) { - total_days += get_num_of_leap_years_before(tm_year_from_base - 1) - - get_num_of_leap_years_before(time_constants::EPOCH_YEAR); - } else if (tm_year_from_base >= 1) { - total_days -= get_num_of_leap_years_before(time_constants::EPOCH_YEAR) - - get_num_of_leap_years_before(tm_year_from_base - 1); - } else { - // Calculate number of leap years until 0th year. - total_days -= get_num_of_leap_years_before(time_constants::EPOCH_YEAR) - - get_num_of_leap_years_before(0); - if (tm_year_from_base <= 0) { - total_days -= 1; // Subtract 1 for 0th year. - // Calculate number of leap years until -1 year - if (tm_year_from_base < 0) { - total_days -= get_num_of_leap_years_before(-tm_year_from_base) - - get_num_of_leap_years_before(1); - } - } - } - - // TODO: https://github.com/llvm/llvm-project/issues/121962 - // Need to handle timezone and update of tm_isdst. - int64_t seconds = tm_out->tm_sec + - tm_out->tm_min * time_constants::SECONDS_PER_MIN + - tm_out->tm_hour * time_constants::SECONDS_PER_HOUR + - total_days * time_constants::SECONDS_PER_DAY; + int64_t seconds = time_utils::mktime_internal(tm_out); // Update the tm structure's year, month, day, etc. from seconds. if (time_utils::update_from_seconds(seconds, tm_out) < 0)
diff --git a/src/time/time_constants.h b/src/time/time_constants.h index 3e25f74..bcf19ff 100644 --- a/src/time/time_constants.h +++ b/src/time/time_constants.h
@@ -18,7 +18,7 @@ namespace time_constants { enum Month : int { - JANUARY, + JANUARY = 0, FEBRUARY, MARCH, APRIL, @@ -32,14 +32,28 @@ DECEMBER }; +enum WeekDay : int { + SUNDAY = 0, + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY +}; + constexpr int SECONDS_PER_MIN = 60; constexpr int MINUTES_PER_HOUR = 60; constexpr int HOURS_PER_DAY = 24; constexpr int DAYS_PER_WEEK = 7; +constexpr int WEEKS_PER_YEAR = 52; constexpr int MONTHS_PER_YEAR = 12; constexpr int DAYS_PER_NON_LEAP_YEAR = 365; constexpr int DAYS_PER_LEAP_YEAR = 366; +constexpr int LAST_DAY_OF_NON_LEAP_YEAR = DAYS_PER_NON_LEAP_YEAR - 1; +constexpr int LAST_DAY_OF_LEAP_YEAR = DAYS_PER_LEAP_YEAR - 1; + constexpr int SECONDS_PER_HOUR = SECONDS_PER_MIN * MINUTES_PER_HOUR; constexpr int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; constexpr int NUMBER_OF_SECONDS_IN_LEAP_YEAR = @@ -49,6 +63,8 @@ constexpr int EPOCH_YEAR = 1970; constexpr int EPOCH_WEEK_DAY = 4; +constexpr int ISO_FIRST_DAY_OF_YEAR = 3; // the 4th day of the year, 0-indexed. + // For asctime the behavior is undefined if struct tm's tm_wday or tm_mon are // not within the normal ranges as defined in <time.h>, or if struct tm's // tm_year exceeds {INT_MAX}-1990, or if the below asctime_internal algorithm
diff --git a/src/time/time_utils.cpp b/src/time/time_utils.cpp index abc93b8..3ccb2dd9 100644 --- a/src/time/time_utils.cpp +++ b/src/time/time_utils.cpp
@@ -12,9 +12,103 @@ #include "src/__support/macros/config.h" #include "src/time/time_constants.h" +#include <stdint.h> + namespace LIBC_NAMESPACE_DECL { namespace time_utils { +// TODO: clean this up in a followup patch +int64_t mktime_internal(const tm *tm_out) { + // Unlike most C Library functions, mktime doesn't just die on bad input. + // TODO(rtenneti); Handle leap seconds. + int64_t tm_year_from_base = tm_out->tm_year + time_constants::TIME_YEAR_BASE; + + // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038. + if (sizeof(time_t) == 4 && + tm_year_from_base >= time_constants::END_OF32_BIT_EPOCH_YEAR) { + if (tm_year_from_base > time_constants::END_OF32_BIT_EPOCH_YEAR) + return time_utils::out_of_range(); + if (tm_out->tm_mon > 0) + return time_utils::out_of_range(); + if (tm_out->tm_mday > 19) + return time_utils::out_of_range(); + else if (tm_out->tm_mday == 19) { + if (tm_out->tm_hour > 3) + return time_utils::out_of_range(); + else if (tm_out->tm_hour == 3) { + if (tm_out->tm_min > 14) + return time_utils::out_of_range(); + else if (tm_out->tm_min == 14) { + if (tm_out->tm_sec > 7) + return time_utils::out_of_range(); + } + } + } + } + + // Years are ints. A 32-bit year will fit into a 64-bit time_t. + // A 64-bit year will not. + static_assert( + sizeof(int) == 4, + "ILP64 is unimplemented. This implementation requires 32-bit integers."); + + // Calculate number of months and years from tm_mon. + int64_t month = tm_out->tm_mon; + if (month < 0 || month >= time_constants::MONTHS_PER_YEAR - 1) { + int64_t years = month / 12; + month %= 12; + if (month < 0) { + years--; + month += 12; + } + tm_year_from_base += years; + } + bool tm_year_is_leap = time_utils::is_leap_year(tm_year_from_base); + + // Calculate total number of days based on the month and the day (tm_mday). + int64_t total_days = tm_out->tm_mday - 1; + for (int64_t i = 0; i < month; ++i) + total_days += time_constants::NON_LEAP_YEAR_DAYS_IN_MONTH[i]; + // Add one day if it is a leap year and the month is after February. + if (tm_year_is_leap && month > 1) + total_days++; + + // Calculate total numbers of days based on the year. + total_days += (tm_year_from_base - time_constants::EPOCH_YEAR) * + time_constants::DAYS_PER_NON_LEAP_YEAR; + if (tm_year_from_base >= time_constants::EPOCH_YEAR) { + total_days += + time_utils::get_num_of_leap_years_before(tm_year_from_base - 1) - + time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR); + } else if (tm_year_from_base >= 1) { + total_days -= + time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR) - + time_utils::get_num_of_leap_years_before(tm_year_from_base - 1); + } else { + // Calculate number of leap years until 0th year. + total_days -= + time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR) - + time_utils::get_num_of_leap_years_before(0); + if (tm_year_from_base <= 0) { + total_days -= 1; // Subtract 1 for 0th year. + // Calculate number of leap years until -1 year + if (tm_year_from_base < 0) { + total_days -= + time_utils::get_num_of_leap_years_before(-tm_year_from_base) - + time_utils::get_num_of_leap_years_before(1); + } + } + } + + // TODO: https://github.com/llvm/llvm-project/issues/121962 + // Need to handle timezone and update of tm_isdst. + int64_t seconds = tm_out->tm_sec + + tm_out->tm_min * time_constants::SECONDS_PER_MIN + + tm_out->tm_hour * time_constants::SECONDS_PER_HOUR + + total_days * time_constants::SECONDS_PER_DAY; + return seconds; +} + static int64_t computeRemainingYears(int64_t daysPerYears, int64_t quotientYears, int64_t *remainingDays) { @@ -42,7 +136,7 @@ // // Compute the number of months from the remaining days. Finally, adjust years // to be 1900 and months to be from January. -int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) { +int64_t update_from_seconds(int64_t total_seconds, tm *tm) { // Days in month starting from March in the year 2000. static const char daysInMonth[] = {31 /* Mar */, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29};
diff --git a/src/time/time_utils.h b/src/time/time_utils.h index 5e0a692..68eaac8 100644 --- a/src/time/time_utils.h +++ b/src/time/time_utils.h
@@ -12,6 +12,8 @@ #include "hdr/types/size_t.h" #include "hdr/types/struct_tm.h" #include "hdr/types/time_t.h" +#include "src/__support/CPP/optional.h" +#include "src/__support/CPP/string_view.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" #include "src/errno/libc_errno.h" @@ -22,9 +24,13 @@ namespace LIBC_NAMESPACE_DECL { namespace time_utils { +// calculates the seconds from the epoch for tm_in. Does not update the struct, +// you must call update_from_seconds for that. +int64_t mktime_internal(const tm *tm_out); + // Update the "tm" structure's year, month, etc. members from seconds. // "total_seconds" is the number of seconds since January 1st, 1970. -extern int64_t update_from_seconds(int64_t total_seconds, struct tm *tm); +int64_t update_from_seconds(int64_t total_seconds, tm *tm); // TODO(michaelrj): move these functions to use ErrorOr instead of setting // errno. They always accompany a specific return value so we only need the one @@ -43,7 +49,7 @@ LIBC_INLINE void invalid_value() { libc_errno = EINVAL; } -LIBC_INLINE char *asctime(const struct tm *timeptr, char *buffer, +LIBC_INLINE char *asctime(const tm *timeptr, char *buffer, size_t bufferLength) { if (timeptr == nullptr || buffer == nullptr) { invalid_value(); @@ -61,6 +67,7 @@ } // TODO(michaelr): move this to use the strftime machinery + // equivalent to strftime(buffer, bufferLength, "%a %b %T %Y\n", timeptr) int written_size = __builtin_snprintf( buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", time_constants::WEEK_DAY_NAMES[timeptr->tm_wday].data(), @@ -76,7 +83,7 @@ return buffer; } -LIBC_INLINE struct tm *gmtime_internal(const time_t *timer, struct tm *result) { +LIBC_INLINE tm *gmtime_internal(const time_t *timer, tm *result) { int64_t seconds = *timer; // Update the tm structure's year, month, day, etc. from seconds. if (update_from_seconds(seconds, result) < 0) { @@ -89,11 +96,252 @@ // TODO: localtime is not yet implemented and a temporary solution is to // use gmtime, https://github.com/llvm/llvm-project/issues/107597 -LIBC_INLINE struct tm *localtime(const time_t *t_ptr) { - static struct tm result; +LIBC_INLINE tm *localtime(const time_t *t_ptr) { + static tm result; return time_utils::gmtime_internal(t_ptr, &result); } +// Returns number of years from (1, year). +LIBC_INLINE constexpr int64_t get_num_of_leap_years_before(int64_t year) { + return (year / 4) - (year / 100) + (year / 400); +} + +// Returns True if year is a leap year. +LIBC_INLINE constexpr bool is_leap_year(const int64_t year) { + return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); +} + +LIBC_INLINE constexpr int get_days_in_year(const int year) { + return is_leap_year(year) ? time_constants::DAYS_PER_LEAP_YEAR + : time_constants::DAYS_PER_NON_LEAP_YEAR; +} + +// This is a helper class that takes a struct tm and lets you inspect its +// values. Where relevant, results are bounds checked and returned as optionals. +// This class does not, however, do data normalization except where necessary. +// It will faithfully return a date of 9999-99-99, even though that makes no +// sense. +class TMReader final { + const tm *timeptr; + + template <size_t N> + LIBC_INLINE constexpr cpp::optional<cpp::string_view> + bounds_check(const cpp::array<cpp::string_view, N> &arr, int index) const { + if (index >= 0 && index < static_cast<int>(arr.size())) + return arr[index]; + return cpp::nullopt; + } + +public: + LIBC_INLINE constexpr explicit TMReader(const tm *tmptr) : timeptr(tmptr) {} + + // Strings + LIBC_INLINE constexpr cpp::optional<cpp::string_view> + get_weekday_short_name() const { + return bounds_check(time_constants::WEEK_DAY_NAMES, timeptr->tm_wday); + } + + LIBC_INLINE constexpr cpp::optional<cpp::string_view> + get_weekday_full_name() const { + return bounds_check(time_constants::WEEK_DAY_FULL_NAMES, timeptr->tm_wday); + } + + LIBC_INLINE constexpr cpp::optional<cpp::string_view> + get_month_short_name() const { + return bounds_check(time_constants::MONTH_NAMES, timeptr->tm_mon); + } + + LIBC_INLINE constexpr cpp::optional<cpp::string_view> + get_month_full_name() const { + return bounds_check(time_constants::MONTH_FULL_NAMES, timeptr->tm_mon); + } + + LIBC_INLINE constexpr cpp::string_view get_am_pm() const { + if (timeptr->tm_hour < 12) + return "AM"; + return "PM"; + } + + LIBC_INLINE constexpr cpp::string_view get_timezone_name() const { + // TODO: timezone support + return "UTC"; + } + + // Numbers + LIBC_INLINE constexpr int get_sec() const { return timeptr->tm_sec; } + LIBC_INLINE constexpr int get_min() const { return timeptr->tm_min; } + LIBC_INLINE constexpr int get_hour() const { return timeptr->tm_hour; } + LIBC_INLINE constexpr int get_mday() const { return timeptr->tm_mday; } + LIBC_INLINE constexpr int get_mon() const { return timeptr->tm_mon; } + LIBC_INLINE constexpr int get_yday() const { return timeptr->tm_yday; } + LIBC_INLINE constexpr int get_wday() const { return timeptr->tm_wday; } + LIBC_INLINE constexpr int get_isdst() const { return timeptr->tm_isdst; } + + // returns the year, counting from 1900 + LIBC_INLINE constexpr int get_year_raw() const { return timeptr->tm_year; } + // returns the year, counting from 0 + LIBC_INLINE constexpr int get_year() const { + return timeptr->tm_year + time_constants::TIME_YEAR_BASE; + } + + LIBC_INLINE constexpr int is_leap_year() const { + return time_utils::is_leap_year(get_year()); + } + + LIBC_INLINE constexpr int get_iso_wday() const { + using time_constants::DAYS_PER_WEEK; + using time_constants::MONDAY; + // ISO uses a week that starts on Monday, but struct tm starts its week on + // Sunday. This function normalizes the weekday so that it always returns a + // value 0-6 + const int NORMALIZED_WDAY = timeptr->tm_wday % DAYS_PER_WEEK; + return (NORMALIZED_WDAY + (DAYS_PER_WEEK - MONDAY)) % DAYS_PER_WEEK; + } + + // returns the week of the current year, with weeks starting on start_day. + LIBC_INLINE constexpr int get_week(time_constants::WeekDay start_day) const { + using time_constants::DAYS_PER_WEEK; + // The most recent start_day. The rest of the days into the current week + // don't count, so ignore them. + // Also add 7 to handle start_day > tm_wday + const int start_of_cur_week = + timeptr->tm_yday - + ((timeptr->tm_wday + DAYS_PER_WEEK - start_day) % DAYS_PER_WEEK); + + // The original formula is ceil((start_of_cur_week + 1) / DAYS_PER_WEEK) + // That becomes (start_of_cur_week + 1 + DAYS_PER_WEEK - 1) / DAYS_PER_WEEK) + // Which simplifies to (start_of_cur_week + DAYS_PER_WEEK) / DAYS_PER_WEEK + const int ceil_weeks_since_start = + (start_of_cur_week + DAYS_PER_WEEK) / DAYS_PER_WEEK; + + return ceil_weeks_since_start; + } + + LIBC_INLINE constexpr int get_iso_week() const { + using time_constants::DAYS_PER_WEEK; + using time_constants::ISO_FIRST_DAY_OF_YEAR; + using time_constants::MONDAY; + using time_constants::WeekDay; + using time_constants::WEEKS_PER_YEAR; + + constexpr WeekDay START_DAY = MONDAY; + + // The most recent start_day. The rest of the days into the current week + // don't count, so ignore them. + // Also add 7 to handle start_day > tm_wday + const int start_of_cur_week = + timeptr->tm_yday - + ((timeptr->tm_wday + DAYS_PER_WEEK - START_DAY) % DAYS_PER_WEEK); + + // if the week starts in the previous year, and also if the 4th of this year + // is not in this week. + if (start_of_cur_week < -3) { + const int days_into_prev_year = + get_days_in_year(get_year() - 1) + start_of_cur_week; + // Each year has at least 52 weeks, but a year's last week will be 53 if + // its first week starts in the previous year and its last week ends + // in the next year. We know get_year() - 1 must extend into get_year(), + // so here we check if it also extended into get_year() - 2 and add 1 week + // if it does. + return WEEKS_PER_YEAR + + ((days_into_prev_year % DAYS_PER_WEEK) > ISO_FIRST_DAY_OF_YEAR); + } + + // subtract 1 to account for yday being 0 indexed + const int days_until_end_of_year = + get_days_in_year(get_year()) - start_of_cur_week - 1; + + // if there are less than 3 days from the start of this week to the end of + // the year, then there must be 4 days in this week in the next year, which + // means that this week is the first week of that year. + if (days_until_end_of_year < 3) + return 1; + + // else just calculate the current week like normal. + const int ceil_weeks_since_start = + (start_of_cur_week + DAYS_PER_WEEK) / DAYS_PER_WEEK; + + // add 1 if this year's first week starts in the previous year. + const int WEEK_STARTS_IN_PREV_YEAR = + ((start_of_cur_week + time_constants::DAYS_PER_WEEK) % + time_constants::DAYS_PER_WEEK) > time_constants::ISO_FIRST_DAY_OF_YEAR; + return ceil_weeks_since_start + WEEK_STARTS_IN_PREV_YEAR; + } + + LIBC_INLINE constexpr int get_iso_year() const { + const int BASE_YEAR = get_year(); + // The ISO year is the same as a standard year for all dates after the start + // of the first week and before the last week. Since the first ISO week of a + // year starts on the 4th, anything after that is in this year. + if (timeptr->tm_yday >= time_constants::ISO_FIRST_DAY_OF_YEAR && + timeptr->tm_yday < time_constants::DAYS_PER_NON_LEAP_YEAR - + time_constants::DAYS_PER_WEEK) + return BASE_YEAR; + + const int ISO_WDAY = get_iso_wday(); + // The first week of the ISO year is defined as the week containing the + // 4th day of January. + + // first week + if (timeptr->tm_yday < time_constants::ISO_FIRST_DAY_OF_YEAR) { + /* + If jan 4 is in this week, then we're in BASE_YEAR, else we're in the + previous year. The formula's been rearranged so here's the derivation: + + +--------+-- days until jan 4 + | | + wday + (4 - yday) < 7 + | | + +---------------+-- weekday of jan 4 + + rearranged to get all the constants on one side: + + wday - yday < 7 - 4 + */ + const int IS_CUR_YEAR = (ISO_WDAY - timeptr->tm_yday < + time_constants::DAYS_PER_WEEK - + time_constants::ISO_FIRST_DAY_OF_YEAR); + return BASE_YEAR - !IS_CUR_YEAR; + } + + // last week + const int DAYS_LEFT_IN_YEAR = + get_days_in_year(get_year()) - timeptr->tm_yday; + /* + Similar to above, we're checking if jan 4 (of next year) is in this week. If + it is, this is in the next year. Note that this also handles the case of + yday > days in year gracefully. + + +------------------+-- days until jan 4 (of next year) + | | + wday + (4 + remaining days) < 7 + | | + +-------------------------+-- weekday of jan 4 + + rearranging we get: + + wday + remaining days < 7 - 4 + */ + const int IS_NEXT_YEAR = + (ISO_WDAY + DAYS_LEFT_IN_YEAR < + time_constants::DAYS_PER_WEEK - time_constants::ISO_FIRST_DAY_OF_YEAR); + return BASE_YEAR + IS_NEXT_YEAR; + } + + LIBC_INLINE time_t get_epoch() const { + return static_cast<time_t>(mktime_internal(timeptr)); + } + + // returns the timezone offset in microwave time: + // return (hours * 100) + minutes; + // This means that a shift of -4:30 is returned as -430, simplifying + // conversion. + LIBC_INLINE constexpr int get_timezone_offset() const { + // TODO: timezone support + return 0; + } +}; + } // namespace time_utils } // namespace LIBC_NAMESPACE_DECL
diff --git a/test/UnitTest/FPMatcher.h b/test/UnitTest/FPMatcher.h index 53e0c16..21b8a45 100644 --- a/test/UnitTest/FPMatcher.h +++ b/test/UnitTest/FPMatcher.h
@@ -330,27 +330,6 @@ EXPECT_FP_EXCEPTION(expected_except); \ } while (0) -#define EXPECT_FP_EQ_ALL_ROUNDING(expected, actual) \ - do { \ - using namespace LIBC_NAMESPACE::fputil::testing; \ - ForceRoundingMode __r1(RoundingMode::Nearest); \ - if (__r1.success) { \ - EXPECT_FP_EQ((expected), (actual)); \ - } \ - ForceRoundingMode __r2(RoundingMode::Upward); \ - if (__r2.success) { \ - EXPECT_FP_EQ((expected), (actual)); \ - } \ - ForceRoundingMode __r3(RoundingMode::Downward); \ - if (__r3.success) { \ - EXPECT_FP_EQ((expected), (actual)); \ - } \ - ForceRoundingMode __r4(RoundingMode::TowardZero); \ - if (__r4.success) { \ - EXPECT_FP_EQ((expected), (actual)); \ - } \ - } while (0) - #define EXPECT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \ do { \ using namespace LIBC_NAMESPACE::fputil::testing; \ @@ -372,6 +351,61 @@ #define EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \ EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero) +#define EXPECT_FP_EQ_ALL_ROUNDING_1(expected, actual) \ + do { \ + EXPECT_FP_EQ_ROUNDING_NEAREST((expected), (actual)); \ + EXPECT_FP_EQ_ROUNDING_UPWARD((expected), (actual)); \ + EXPECT_FP_EQ_ROUNDING_DOWNWARD((expected), (actual)); \ + EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO((expected), (actual)); \ + } while (0) + +#define EXPECT_FP_EQ_ALL_ROUNDING_4(expected_nearest, expected_upward, \ + expected_downward, expected_toward_zero, \ + actual) \ + do { \ + EXPECT_FP_EQ_ROUNDING_NEAREST((expected_nearest), (actual)); \ + EXPECT_FP_EQ_ROUNDING_UPWARD((expected_upward), (actual)); \ + EXPECT_FP_EQ_ROUNDING_DOWNWARD((expected_downward), (actual)); \ + EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO((expected_toward_zero), (actual)); \ + } while (0) + +#define EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED(...) \ + static_assert(false, "Unsupported number of arguments") + +#define EXPECT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG(ARG1, ARG2, ARG3, ARG4, ARG5, \ + ARG6, ...) \ + ARG6 + +#define EXPECT_FP_EQ_ALL_ROUNDING_SELECTION(...) \ + EXPECT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG( \ + __VA_ARGS__, EXPECT_FP_EQ_ALL_ROUNDING_4, \ + EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, \ + EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, EXPECT_FP_EQ_ALL_ROUNDING_1) + +#define EXPECT_FP_EQ_ALL_ROUNDING(...) \ + EXPECT_FP_EQ_ALL_ROUNDING_SELECTION(__VA_ARGS__)(__VA_ARGS__) + +#define ASSERT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \ + do { \ + using namespace LIBC_NAMESPACE::fputil::testing; \ + ForceRoundingMode __r((rounding_mode)); \ + if (__r.success) { \ + ASSERT_FP_EQ((expected), (actual)); \ + } \ + } while (0) + +#define ASSERT_FP_EQ_ROUNDING_NEAREST(expected, actual) \ + ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest) + +#define ASSERT_FP_EQ_ROUNDING_UPWARD(expected, actual) \ + ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward) + +#define ASSERT_FP_EQ_ROUNDING_DOWNWARD(expected, actual) \ + ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward) + +#define ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \ + ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero) + #define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \ expected, actual, expected_except, rounding_mode) \ do { \
diff --git a/test/UnitTest/LibcTest.h b/test/UnitTest/LibcTest.h index b4e3819..fbeafd0 100644 --- a/test/UnitTest/LibcTest.h +++ b/test/UnitTest/LibcTest.h
@@ -400,6 +400,14 @@ SuiteClass##_##TestName SuiteClass##_##TestName##_Instance; \ void SuiteClass##_##TestName::Run() +// Helper to trick the compiler into ignoring lack of braces on the else +// branch. We cannot introduce braces at this point, since it would prevent +// using `<< ...` after the test macro for additional failure output. +#define LIBC_TEST_DISABLE_DANGLING_ELSE \ + switch (0) \ + case 0: \ + default: // NOLINT + // If RET_OR_EMPTY is the 'return' keyword we perform an early return which // corresponds to an assert. If it is empty the execution continues, this // corresponds to an expect. @@ -411,6 +419,7 @@ // returning a boolean. This expression is responsible for logging the // diagnostic in case of failure. #define LIBC_TEST_SCAFFOLDING_(TEST, RET_OR_EMPTY) \ + LIBC_TEST_DISABLE_DANGLING_ELSE \ if (TEST) \ ; \ else \
diff --git a/test/integration/src/pthread/pthread_mutex_test.cpp b/test/integration/src/pthread/pthread_mutex_test.cpp index ce2a353..137daed 100644 --- a/test/integration/src/pthread/pthread_mutex_test.cpp +++ b/test/integration/src/pthread/pthread_mutex_test.cpp
@@ -186,6 +186,10 @@ LIBC_NAMESPACE::pthread_mutex_destroy(&counter_lock); } +// Test the initializer +[[maybe_unused]] +static pthread_mutex_t test_initializer = PTHREAD_MUTEX_INITIALIZER; + TEST_MAIN() { relay_counter(); wait_and_step();
diff --git a/test/src/__support/CPP/bit_test.cpp b/test/src/__support/CPP/bit_test.cpp index 9429b66..1f23152 100644 --- a/test/src/__support/CPP/bit_test.cpp +++ b/test/src/__support/CPP/bit_test.cpp
@@ -41,7 +41,8 @@ constexpr auto LSB = T(1); constexpr auto MSB = T(~(ALL_ONES >> 1)); for (T value = 1; value; value <<= 1) { - auto two_bits_value = value | ((value <= MIDPOINT) ? MSB : LSB); + T two_bits_value = + static_cast<T>(value | ((value <= MIDPOINT) ? MSB : LSB)); EXPECT_FALSE(has_single_bit<T>(two_bits_value)); } }
diff --git a/test/src/math/SqrtTest.h b/test/src/math/SqrtTest.h index 770cc94..fdfc4f9 100644 --- a/test/src/math/SqrtTest.h +++ b/test/src/math/SqrtTest.h
@@ -29,14 +29,14 @@ FPBits denormal(zero); denormal.set_mantissa(mant); InType x = denormal.get_val(); - EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5); + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5); } constexpr StorageType COUNT = 200'001; constexpr StorageType STEP = HIDDEN_BIT / COUNT; for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { InType x = FPBits(i).get_val(); - EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5); + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5); } } @@ -48,7 +48,7 @@ InType x = x_bits.get_val(); if (x_bits.is_nan() || (x < 0)) continue; - EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5); + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sqrt, x, func(x), 0.5); } } };
diff --git a/test/src/math/asinf16_test.cpp b/test/src/math/asinf16_test.cpp new file mode 100644 index 0000000..9593cad --- /dev/null +++ b/test/src/math/asinf16_test.cpp
@@ -0,0 +1,42 @@ +//===-- Exhaustive test for asinf16 ---------------------------------------===// +// +// 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 "src/math/asinf16.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +using LlvmLibcAsinf16Test = LIBC_NAMESPACE::testing::FPTest<float16>; + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +// Range: [0, Inf] +static constexpr uint16_t POS_START = 0x0000U; +static constexpr uint16_t POS_STOP = 0x7c00U; + +// Range: [-Inf, 0] +static constexpr uint16_t NEG_START = 0x8000U; +static constexpr uint16_t NEG_STOP = 0xfc00U; + +TEST_F(LlvmLibcAsinf16Test, PositiveRange) { + for (uint16_t v = POS_START; v <= POS_STOP; ++v) { + float16 x = FPBits(v).get_val(); + + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Asin, x, + LIBC_NAMESPACE::asinf16(x), 0.5); + } +} + +TEST_F(LlvmLibcAsinf16Test, NegativeRange) { + for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) { + float16 x = FPBits(v).get_val(); + + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Asin, x, + LIBC_NAMESPACE::asinf16(x), 0.5); + } +}
diff --git a/test/src/math/performance_testing/sqrtf128_perf.cpp b/test/src/math/performance_testing/sqrtf128_perf.cpp new file mode 100644 index 0000000..bc04e69 --- /dev/null +++ b/test/src/math/performance_testing/sqrtf128_perf.cpp
@@ -0,0 +1,20 @@ +//===-- Differential test for sqrtf128 +//----------------------------------------===// +// +// 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 "SingleInputSingleOutputPerf.h" + +#include "src/__support/FPUtil/sqrt.h" +#include "src/math/sqrtf128.h" + +float128 sqrtf128_placeholder(float128 x) { + return LIBC_NAMESPACE::fputil::sqrt<float128>(x); +} + +SINGLE_INPUT_SINGLE_OUTPUT_PERF(float128, LIBC_NAMESPACE::sqrtf128, + ::sqrtf128_placeholder, "sqrtf128_perf.log")
diff --git a/test/src/math/smoke/SqrtTest.h b/test/src/math/smoke/SqrtTest.h index b5eaee2..29666ad 100644 --- a/test/src/math/smoke/SqrtTest.h +++ b/test/src/math/smoke/SqrtTest.h
@@ -39,7 +39,8 @@ #define LIST_SQRT_TESTS(T, func) \ using LlvmLibcSqrtTest = SqrtTest<T, T>; \ - TEST_F(LlvmLibcSqrtTest, SpecialNumbers) { test_special_numbers(&func); } + TEST_F(LlvmLibcSqrtTest, SpecialNumbers) { test_special_numbers(&func); } \ + static_assert(true, "Require semicolon.") #define LIST_NARROWING_SQRT_TESTS(OutType, InType, func) \ using LlvmLibcSqrtTest = SqrtTest<OutType, InType>; \
diff --git a/test/src/math/smoke/asinf16_test.cpp b/test/src/math/smoke/asinf16_test.cpp new file mode 100644 index 0000000..9f675b0 --- /dev/null +++ b/test/src/math/smoke/asinf16_test.cpp
@@ -0,0 +1,42 @@ +//===-- Unittests for asinf16 ---------------------------------------------===// +// +// +// 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 "src/errno/libc_errno.h" +#include "src/math/asinf16.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcAsinf16Test = LIBC_NAMESPACE::testing::FPTest<float16>; + +TEST_F(LlvmLibcAsinf16Test, SpecialNumbers) { + LIBC_NAMESPACE::libc_errno = 0; + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(aNaN)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::asinf16(sNaN), FE_INVALID); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(zero, LIBC_NAMESPACE::asinf16(zero)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::asinf16(neg_zero)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(inf)); + EXPECT_MATH_ERRNO(EDOM); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(neg_inf)); + EXPECT_MATH_ERRNO(EDOM); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(2.0f)); + EXPECT_MATH_ERRNO(EDOM); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::asinf16(-2.0f)); + EXPECT_MATH_ERRNO(EDOM); +}
diff --git a/test/src/math/smoke/generic_sqrt_test.cpp b/test/src/math/smoke/generic_sqrt_test.cpp index d0ab31f..4451e5e 100644 --- a/test/src/math/smoke/generic_sqrt_test.cpp +++ b/test/src/math/smoke/generic_sqrt_test.cpp
@@ -10,4 +10,4 @@ #include "src/__support/FPUtil/generic/sqrt.h" -LIST_SQRT_TESTS(double, LIBC_NAMESPACE::fputil::sqrt<double>) +LIST_SQRT_TESTS(double, LIBC_NAMESPACE::fputil::sqrt<double>);
diff --git a/test/src/math/smoke/generic_sqrtf128_test.cpp b/test/src/math/smoke/generic_sqrtf128_test.cpp index edba114..790ff0a 100644 --- a/test/src/math/smoke/generic_sqrtf128_test.cpp +++ b/test/src/math/smoke/generic_sqrtf128_test.cpp
@@ -10,4 +10,4 @@ #include "src/__support/FPUtil/generic/sqrt.h" -LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::fputil::sqrt<float128>) +LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::fputil::sqrt<float128>);
diff --git a/test/src/math/smoke/generic_sqrtf_test.cpp b/test/src/math/smoke/generic_sqrtf_test.cpp index f22ac88..e04d4c4 100644 --- a/test/src/math/smoke/generic_sqrtf_test.cpp +++ b/test/src/math/smoke/generic_sqrtf_test.cpp
@@ -10,4 +10,4 @@ #include "src/__support/FPUtil/generic/sqrt.h" -LIST_SQRT_TESTS(float, LIBC_NAMESPACE::fputil::sqrt<float>) +LIST_SQRT_TESTS(float, LIBC_NAMESPACE::fputil::sqrt<float>);
diff --git a/test/src/math/smoke/generic_sqrtl_test.cpp b/test/src/math/smoke/generic_sqrtl_test.cpp index ddc6a23..ccb5054 100644 --- a/test/src/math/smoke/generic_sqrtl_test.cpp +++ b/test/src/math/smoke/generic_sqrtl_test.cpp
@@ -10,4 +10,4 @@ #include "src/__support/FPUtil/generic/sqrt.h" -LIST_SQRT_TESTS(long double, LIBC_NAMESPACE::fputil::sqrt<long double>) +LIST_SQRT_TESTS(long double, LIBC_NAMESPACE::fputil::sqrt<long double>);
diff --git a/test/src/math/smoke/sqrt_test.cpp b/test/src/math/smoke/sqrt_test.cpp index 1551b31..b41e06da 100644 --- a/test/src/math/smoke/sqrt_test.cpp +++ b/test/src/math/smoke/sqrt_test.cpp
@@ -10,4 +10,4 @@ #include "src/math/sqrt.h" -LIST_SQRT_TESTS(double, LIBC_NAMESPACE::sqrt) +LIST_SQRT_TESTS(double, LIBC_NAMESPACE::sqrt);
diff --git a/test/src/math/smoke/sqrtf128_test.cpp b/test/src/math/smoke/sqrtf128_test.cpp index 23397b0..3b9686c 100644 --- a/test/src/math/smoke/sqrtf128_test.cpp +++ b/test/src/math/smoke/sqrtf128_test.cpp
@@ -8,6 +8,130 @@ #include "SqrtTest.h" +#include "src/__support/uint128.h" #include "src/math/sqrtf128.h" -LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::sqrtf128) +LIST_SQRT_TESTS(float128, LIBC_NAMESPACE::sqrtf128); + +TEST_F(LlvmLibcSqrtTest, HardToRound) { + using LIBC_NAMESPACE::fputil::testing::RoundingMode; + using FPBits = LIBC_NAMESPACE::fputil::FPBits<float128>; + + // Since there is no exact half cases for square root I encode the + // round direction in the sign of the result. E.g. if the number is + // negative it means that the exact root is below the rounded value + // (the absolute value). Thus I can test not only hard to round + // cases for the round to nearest mode but also the directional + // modes. + float128 HARD_TO_ROUND[][2] = { + {0x0.000000dee2f5b6a26c8f07f05442p-16382q, + -0x1.ddbd8763a617cff753e2a31083p-8204q}, + {0x0.000000c86d174c5ad8ae54a548e7p-16382q, + 0x1.c507bb538940719890851ec1ca88p-8204q}, + {0x0.000020ab15cfe0b8e488e128f535p-16382q, + -0x1.6dccb402560213bc0d62d62e910bp-8201q}, + {0x0.0000219e97732a9970f2511989bap-16382q, + 0x1.73163d28be706f4b5052791e28a5p-8201q}, + {0x0.000026e477546ae99ef57066f9fdp-16382q, + -0x1.8f20dd0d0c570a23ea59bc2bf009p-8201q}, + {0x0.00002d0f88d27a496b3e533f5067p-16382q, + 0x1.ad9d4abe9f047225a7352bcc52c1p-8201q}, + {0x1.0000000000000000000000000001p+0q, 0x1p+0q}, + {0x1.0000000000000000000000000002p+0q, + -0x1.0000000000000000000000000001p+0q}, + {0x1.0000000000000000000000000003p+0q, + 0x1.0000000000000000000000000001p+0q}, + {0x1.0000000000000000000000000005p+0q, + 0x1.0000000000000000000000000002p+0q}, + {0x1.0000000000000000000000000006p+0q, + -0x1.0000000000000000000000000003p+0q}, + {0x1.1d4c381cbf3a0aa15b9aee344892p+0q, + 0x1.0e408c3fadc5e64b449c63673f4bp+0q}, + {0x1.2af17a4ae6f93d11310c49c11b59p+0q, + -0x1.14a3bdf0ea5231f12d421a5dbe33p+0q}, + {0x1.96f893bf29fb91e0fbe19a46d0c8p+0q, + 0x1.42c6bf6202e66f2295807dee44d9p+0q}, + {0x1.97fb3839925b66804c429289cce8p+0q, + -0x1.432d4049ac1c85a241f333d326e9p+0q}, + {0x1.be1d900eaeb1533f0f19cc15c7e6p+0q, + 0x1.51f1715154da44f3bf11f3d96c2dp+0q}, + {0x1.c4f5074269525063a26051a0ad27p+0q, + 0x1.54864e9b1daa4d9135ff00663366p+0q}, + {0x1.035cb5f298a801dc4be9b1f8cd97p+1q, + -0x1.6c688775bffcb3f507ba11d0abb9p+0q}, + {0x1.274be02380427e709beab4dedeb4p+1q, + -0x1.84d5763281f2318422392e506b1cp+0q}, + {0x1.64e797cfdbaa3f7e2f33279dbc6p+1q, + 0x1.ab79b164e255b26eca00ff99cc99p+0q}, + {0x1.693a741358c9dac44a570a7e9f6cp+1q, + 0x1.ae0e8eaeab25bb0c40ee0c2693d3p+0q}, + {0x1.8275db3fc4d822596047adcb71b9p+1q, + -0x1.bcd2bfb653e37a5dbe0ccc2cd917p+0q}, + {0x1.83280bb98c4a7b88bd6f535899d9p+1q, + 0x1.bd39409dfd1990dd6a7f8211bb27p+0q}, + {0x1.d78d8352b48608b510bfd5c75315p+1q, + -0x1.eb5c420f15adce0ed2bde5a241cep+0q}, + {0x1.e3e4774f564b526edff84ce46668p+1q, + 0x1.f1bf73c0523a19b4bb639c98c0b5p+0q}, + {0x1.fffffffffffffffffffffffffffap+1q, + -0x1.fffffffffffffffffffffffffffdp+0q}, + {0x1.fffffffffffffffffffffffffffbp+1q, + 0x1.fffffffffffffffffffffffffffdp+0q}, + {0x1.fffffffffffffffffffffffffffdp+1q, + 0x1.fffffffffffffffffffffffffffep+0q}, + {0x1.fffffffffffffffffffffffffffep+1q, + -0x1.ffffffffffffffffffffffffffffp+0q}, + {0x1.ffffffffffffffffffffffffffffp+1q, + 0x1.ffffffffffffffffffffffffffffp+0q}, + }; + + auto rnd = [](float128 x, RoundingMode rm) -> float128 { + bool is_neg = x < 0; + float128 y = is_neg ? -x : x; + FPBits ybits(y); + + if (is_neg && + (rm == RoundingMode::Downward || rm == RoundingMode::TowardZero)) + return FPBits(ybits.uintval() - 1).get_val(); + if (!is_neg && (rm == RoundingMode::Upward)) + return FPBits(ybits.uintval() + 1).get_val(); + + return y; + }; + + for (auto &t : HARD_TO_ROUND) { + EXPECT_FP_EQ_ALL_ROUNDING( + rnd(t[1], RoundingMode::Nearest), rnd(t[1], RoundingMode::Upward), + rnd(t[1], RoundingMode::Downward), rnd(t[1], RoundingMode::TowardZero), + LIBC_NAMESPACE::sqrtf128(t[0])); + } + + // Exact results for subnormal arguments + float128 EXACT_SUBNORMAL[][2] = { + {0x0.0000000000000000000000000001p-16382q, 0x1p-8247q}, + {0x0.0000000000000000000000000004p-16382q, 0x1p-8246q}, + {0x0.0000000000001000000000000000p-16382q, 0x1p-8217q}, + {0x0.0000000000010000000000000000p-16382q, 0x1p-8215q}, + {0x0.0000000000100000000000000000p-16382q, 0x1p-8213q}, + }; + + for (auto t : EXACT_SUBNORMAL) + EXPECT_FP_EQ_ALL_ROUNDING(t[1], LIBC_NAMESPACE::sqrtf128(t[0])); + + // Check exact cases starting from small numbers + for (unsigned k = 1; k < 100 * 100; ++k) { + unsigned k2 = k * k; + float128 x = static_cast<float128>(k2); + float128 y = static_cast<float128>(k); + EXPECT_FP_EQ_ALL_ROUNDING(y, LIBC_NAMESPACE::sqrtf128(x)); + }; + + // Then from the largest number. + uint64_t k0 = 101904826760412362ULL; + for (uint64_t k = k0; k > k0 - 10000; --k) { + UInt128 k2 = static_cast<UInt128>(k) * static_cast<UInt128>(k); + float128 x = static_cast<float128>(k2); + float128 y = static_cast<float128>(k); + EXPECT_FP_EQ_ALL_ROUNDING(y, LIBC_NAMESPACE::sqrtf128(x)); + } +}
diff --git a/test/src/math/smoke/sqrtf16_test.cpp b/test/src/math/smoke/sqrtf16_test.cpp index d620496..950abd2 100644 --- a/test/src/math/smoke/sqrtf16_test.cpp +++ b/test/src/math/smoke/sqrtf16_test.cpp
@@ -10,4 +10,4 @@ #include "src/math/sqrtf16.h" -LIST_SQRT_TESTS(float16, LIBC_NAMESPACE::sqrtf16) +LIST_SQRT_TESTS(float16, LIBC_NAMESPACE::sqrtf16);
diff --git a/test/src/math/smoke/sqrtf_test.cpp b/test/src/math/smoke/sqrtf_test.cpp index 3f2e973..888b6cb 100644 --- a/test/src/math/smoke/sqrtf_test.cpp +++ b/test/src/math/smoke/sqrtf_test.cpp
@@ -10,4 +10,4 @@ #include "src/math/sqrtf.h" -LIST_SQRT_TESTS(float, LIBC_NAMESPACE::sqrtf) +LIST_SQRT_TESTS(float, LIBC_NAMESPACE::sqrtf);
diff --git a/test/src/math/smoke/sqrtl_test.cpp b/test/src/math/smoke/sqrtl_test.cpp index f80bcfb..4f4a64f 100644 --- a/test/src/math/smoke/sqrtl_test.cpp +++ b/test/src/math/smoke/sqrtl_test.cpp
@@ -10,4 +10,4 @@ #include "src/math/sqrtl.h" -LIST_SQRT_TESTS(long double, LIBC_NAMESPACE::sqrtl) +LIST_SQRT_TESTS(long double, LIBC_NAMESPACE::sqrtl);
diff --git a/test/src/stdbit/stdc_bit_ceil_uc_test.cpp b/test/src/stdbit/stdc_bit_ceil_uc_test.cpp index 1ef87b0..6915859 100644 --- a/test/src/stdbit/stdc_bit_ceil_uc_test.cpp +++ b/test/src/stdbit/stdc_bit_ceil_uc_test.cpp
@@ -17,18 +17,21 @@ TEST(LlvmLibcStdcBitceilUcTest, Ones) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc(1U << i), - static_cast<unsigned char>(1U << i)); + EXPECT_EQ( + LIBC_NAMESPACE::stdc_bit_ceil_uc(static_cast<unsigned char>(1U << i)), + static_cast<unsigned char>(1U << i)); } TEST(LlvmLibcStdcBitceilUcTest, OneLessThanPowsTwo) { for (unsigned i = 2U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc((1U << i) - 1), + EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc( + static_cast<unsigned char>((1U << i) - 1)), static_cast<unsigned char>(1U << i)); } TEST(LlvmLibcStdcBitceilUcTest, OneMoreThanPowsTwo) { for (unsigned i = 1U; i != UCHAR_WIDTH - 1; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc((1U << i) + 1), + EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_uc( + static_cast<unsigned char>((1U << i) + 1)), static_cast<unsigned char>(1U << (i + 1))); }
diff --git a/test/src/stdbit/stdc_bit_ceil_us_test.cpp b/test/src/stdbit/stdc_bit_ceil_us_test.cpp index 56873c5..9a8b46f 100644 --- a/test/src/stdbit/stdc_bit_ceil_us_test.cpp +++ b/test/src/stdbit/stdc_bit_ceil_us_test.cpp
@@ -17,18 +17,21 @@ TEST(LlvmLibcStdcBitceilUsTest, Ones) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us(1U << i), - static_cast<unsigned short>(1U << i)); + EXPECT_EQ( + LIBC_NAMESPACE::stdc_bit_ceil_us(static_cast<unsigned short>(1U << i)), + static_cast<unsigned short>(1U << i)); } TEST(LlvmLibcStdcBitceilUsTest, OneLessThanPowsTwo) { for (unsigned i = 2U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us((1U << i) - 1), + EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us( + static_cast<unsigned short>((1U << i) - 1)), static_cast<unsigned short>(1U << i)); } TEST(LlvmLibcStdcBitceilUsTest, OneMoreThanPowsTwo) { for (unsigned i = 1U; i != USHRT_WIDTH - 1; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us((1U << i) + 1), + EXPECT_EQ(LIBC_NAMESPACE::stdc_bit_ceil_us( + static_cast<unsigned short>((1U << i) + 1)), static_cast<unsigned short>(1U << (i + 1))); }
diff --git a/test/src/stdbit/stdc_first_leading_one_uc_test.cpp b/test/src/stdbit/stdc_first_leading_one_uc_test.cpp index b8c8db5..2ab8397 100644 --- a/test/src/stdbit/stdc_first_leading_one_uc_test.cpp +++ b/test/src/stdbit/stdc_first_leading_one_uc_test.cpp
@@ -16,6 +16,7 @@ TEST(LlvmLibcStdcFirstLeadingOneUcTest, OneHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_one_uc(1U << i), + EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_one_uc( + static_cast<unsigned char>(1U << i)), UCHAR_WIDTH - i); }
diff --git a/test/src/stdbit/stdc_first_leading_one_us_test.cpp b/test/src/stdbit/stdc_first_leading_one_us_test.cpp index e948833..de81275 100644 --- a/test/src/stdbit/stdc_first_leading_one_us_test.cpp +++ b/test/src/stdbit/stdc_first_leading_one_us_test.cpp
@@ -16,6 +16,7 @@ TEST(LlvmLibcStdcFirstLeadingOneUsTest, OneHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_one_us(1U << i), + EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_one_us( + static_cast<unsigned short>(1U << i)), USHRT_WIDTH - i); }
diff --git a/test/src/stdbit/stdc_first_leading_zero_uc_test.cpp b/test/src/stdbit/stdc_first_leading_zero_uc_test.cpp index ac7e8c7..a19d0ab 100644 --- a/test/src/stdbit/stdc_first_leading_zero_uc_test.cpp +++ b/test/src/stdbit/stdc_first_leading_zero_uc_test.cpp
@@ -16,6 +16,7 @@ TEST(LlvmLibcStdcFirstLeadingZeroUcTest, ZeroHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_zero_uc(~(1U << i)), + EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_zero_uc( + static_cast<unsigned char>(~(1U << i))), UCHAR_WIDTH - i); }
diff --git a/test/src/stdbit/stdc_first_leading_zero_us_test.cpp b/test/src/stdbit/stdc_first_leading_zero_us_test.cpp index 37f8612..2971267 100644 --- a/test/src/stdbit/stdc_first_leading_zero_us_test.cpp +++ b/test/src/stdbit/stdc_first_leading_zero_us_test.cpp
@@ -16,6 +16,7 @@ TEST(LlvmLibcStdcFirstLeadingZeroUsTest, ZeroHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_zero_us(~(1U << i)), + EXPECT_EQ(LIBC_NAMESPACE::stdc_first_leading_zero_us( + static_cast<unsigned short>(~(1U << i))), USHRT_WIDTH - i); }
diff --git a/test/src/stdbit/stdc_first_trailing_one_uc_test.cpp b/test/src/stdbit/stdc_first_trailing_one_uc_test.cpp index ed2b492..5ca4cfc 100644 --- a/test/src/stdbit/stdc_first_trailing_one_uc_test.cpp +++ b/test/src/stdbit/stdc_first_trailing_one_uc_test.cpp
@@ -16,5 +16,7 @@ TEST(LlvmLibcStdcFirstTrailingOneUcTest, OneHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_one_uc(1U << i), i + 1); + EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_one_uc( + static_cast<unsigned char>(1U << i)), + i + 1); }
diff --git a/test/src/stdbit/stdc_first_trailing_one_us_test.cpp b/test/src/stdbit/stdc_first_trailing_one_us_test.cpp index 6002155..46c69ac 100644 --- a/test/src/stdbit/stdc_first_trailing_one_us_test.cpp +++ b/test/src/stdbit/stdc_first_trailing_one_us_test.cpp
@@ -16,5 +16,7 @@ TEST(LlvmLibcStdcFirstTrailingOneUsTest, OneHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_one_us(1U << i), i + 1); + EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_one_us( + static_cast<unsigned short>(1U << i)), + i + 1); }
diff --git a/test/src/stdbit/stdc_first_trailing_zero_uc_test.cpp b/test/src/stdbit/stdc_first_trailing_zero_uc_test.cpp index 2b17aa6..9535ad9 100644 --- a/test/src/stdbit/stdc_first_trailing_zero_uc_test.cpp +++ b/test/src/stdbit/stdc_first_trailing_zero_uc_test.cpp
@@ -16,5 +16,7 @@ TEST(LlvmLibcStdcFirstTrailingZeroUcTest, ZeroHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_zero_uc(~(1U << i)), i + 1); + EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_zero_uc( + static_cast<unsigned char>(~(1U << i))), + i + 1); }
diff --git a/test/src/stdbit/stdc_first_trailing_zero_us_test.cpp b/test/src/stdbit/stdc_first_trailing_zero_us_test.cpp index e370379..e0dc34f 100644 --- a/test/src/stdbit/stdc_first_trailing_zero_us_test.cpp +++ b/test/src/stdbit/stdc_first_trailing_zero_us_test.cpp
@@ -16,5 +16,7 @@ TEST(LlvmLibcStdcFirstTrailingZeroUsTest, ZeroHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_zero_us(~(1U << i)), i + 1); + EXPECT_EQ(LIBC_NAMESPACE::stdc_first_trailing_zero_us( + static_cast<unsigned short>(~(1U << i))), + i + 1); }
diff --git a/test/src/stdbit/stdc_has_single_bit_uc_test.cpp b/test/src/stdbit/stdc_has_single_bit_uc_test.cpp index 1bc189c..9dd2bdc 100644 --- a/test/src/stdbit/stdc_has_single_bit_uc_test.cpp +++ b/test/src/stdbit/stdc_has_single_bit_uc_test.cpp
@@ -16,5 +16,7 @@ TEST(LlvmLibcStdcHasSingleBitUcTest, OneHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_has_single_bit_uc(1U << i), true); + EXPECT_EQ(LIBC_NAMESPACE::stdc_has_single_bit_uc( + static_cast<unsigned char>(1U << i)), + true); }
diff --git a/test/src/stdbit/stdc_has_single_bit_us_test.cpp b/test/src/stdbit/stdc_has_single_bit_us_test.cpp index a038f6f..3ff0b83 100644 --- a/test/src/stdbit/stdc_has_single_bit_us_test.cpp +++ b/test/src/stdbit/stdc_has_single_bit_us_test.cpp
@@ -16,5 +16,7 @@ TEST(LlvmLibcStdcHasSingleBitUsTest, OneHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_has_single_bit_us(1U << i), true); + EXPECT_EQ(LIBC_NAMESPACE::stdc_has_single_bit_us( + static_cast<unsigned short>(1U << i)), + true); }
diff --git a/test/src/stdbit/stdc_leading_ones_uc_test.cpp b/test/src/stdbit/stdc_leading_ones_uc_test.cpp index 5d32d92..4ba240f 100644 --- a/test/src/stdbit/stdc_leading_ones_uc_test.cpp +++ b/test/src/stdbit/stdc_leading_ones_uc_test.cpp
@@ -17,6 +17,7 @@ TEST(LlvmLibcStdcLeadingOnesUcTest, ZeroHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_ones_uc(~(1U << i)), + EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_ones_uc( + static_cast<unsigned char>(~(1U << i))), UCHAR_WIDTH - i - 1U); }
diff --git a/test/src/stdbit/stdc_leading_ones_us_test.cpp b/test/src/stdbit/stdc_leading_ones_us_test.cpp index 91a1253..0f93eed 100644 --- a/test/src/stdbit/stdc_leading_ones_us_test.cpp +++ b/test/src/stdbit/stdc_leading_ones_us_test.cpp
@@ -17,6 +17,7 @@ TEST(LlvmLibcStdcLeadingOnesUsTest, ZeroHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_ones_us(~(1U << i)), + EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_ones_us( + static_cast<unsigned short>(~(1U << i))), USHRT_WIDTH - i - 1U); }
diff --git a/test/src/stdbit/stdc_leading_zeros_uc_test.cpp b/test/src/stdbit/stdc_leading_zeros_uc_test.cpp index 3d55507..42f78c2 100644 --- a/test/src/stdbit/stdc_leading_zeros_uc_test.cpp +++ b/test/src/stdbit/stdc_leading_zeros_uc_test.cpp
@@ -17,6 +17,7 @@ TEST(LlvmLibcStdcLeadingZerosUcTest, OneHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_zeros_uc(1U << i), + EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_zeros_uc( + static_cast<unsigned char>(1U << i)), UCHAR_WIDTH - i - 1U); }
diff --git a/test/src/stdbit/stdc_leading_zeros_us_test.cpp b/test/src/stdbit/stdc_leading_zeros_us_test.cpp index afb418a..967ceb1 100644 --- a/test/src/stdbit/stdc_leading_zeros_us_test.cpp +++ b/test/src/stdbit/stdc_leading_zeros_us_test.cpp
@@ -17,6 +17,7 @@ TEST(LlvmLibcStdcLeadingZerosUsTest, OneHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_zeros_us(1U << i), + EXPECT_EQ(LIBC_NAMESPACE::stdc_leading_zeros_us( + static_cast<unsigned short>(1U << i)), USHRT_WIDTH - i - 1U); }
diff --git a/test/src/stdbit/stdc_trailing_ones_uc_test.cpp b/test/src/stdbit/stdc_trailing_ones_uc_test.cpp index 79d4e5b..0036408 100644 --- a/test/src/stdbit/stdc_trailing_ones_uc_test.cpp +++ b/test/src/stdbit/stdc_trailing_ones_uc_test.cpp
@@ -17,5 +17,7 @@ TEST(LlvmLibcStdcTrailingOnesUcTest, ZeroHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_ones_uc(~(1U << i)), i); + EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_ones_uc( + static_cast<unsigned char>(~(1U << i))), + i); }
diff --git a/test/src/stdbit/stdc_trailing_ones_us_test.cpp b/test/src/stdbit/stdc_trailing_ones_us_test.cpp index 7ab1574..5ebacc8 100644 --- a/test/src/stdbit/stdc_trailing_ones_us_test.cpp +++ b/test/src/stdbit/stdc_trailing_ones_us_test.cpp
@@ -17,5 +17,7 @@ TEST(LlvmLibcStdcTrailingOnesUsTest, ZeroHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_ones_us(~(1U << i)), i); + EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_ones_us( + static_cast<unsigned short>(~(1U << i))), + i); }
diff --git a/test/src/stdbit/stdc_trailing_zeros_uc_test.cpp b/test/src/stdbit/stdc_trailing_zeros_uc_test.cpp index c02b518..129ab38 100644 --- a/test/src/stdbit/stdc_trailing_zeros_uc_test.cpp +++ b/test/src/stdbit/stdc_trailing_zeros_uc_test.cpp
@@ -17,5 +17,7 @@ TEST(LlvmLibcStdcTrailingZerosUcTest, OneHot) { for (unsigned i = 0U; i != UCHAR_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_zeros_uc(1U << i), i); + EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_zeros_uc( + static_cast<unsigned char>(1U << i)), + i); }
diff --git a/test/src/stdbit/stdc_trailing_zeros_us_test.cpp b/test/src/stdbit/stdc_trailing_zeros_us_test.cpp index a9f8327..e1171f2 100644 --- a/test/src/stdbit/stdc_trailing_zeros_us_test.cpp +++ b/test/src/stdbit/stdc_trailing_zeros_us_test.cpp
@@ -17,5 +17,7 @@ TEST(LlvmLibcStdcTrailingZerosUsTest, OneHot) { for (unsigned i = 0U; i != USHRT_WIDTH; ++i) - EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_zeros_us(1U << i), i); + EXPECT_EQ(LIBC_NAMESPACE::stdc_trailing_zeros_us( + static_cast<unsigned short>(1U << i)), + i); }
diff --git a/test/src/stdfix/CountlsTest.h b/test/src/stdfix/CountlsTest.h new file mode 100644 index 0000000..a8201ac --- /dev/null +++ b/test/src/stdfix/CountlsTest.h
@@ -0,0 +1,58 @@ +//===-- Utility class to test countls -------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "test/UnitTest/Test.h" + +#include "src/__support/fixed_point/fx_rep.h" + +template <typename T> class CountlsTest : public LIBC_NAMESPACE::testing::Test { + + using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<T>; + static constexpr T zero = FXRep::ZERO(); + static constexpr T max = FXRep::MAX(); + static constexpr T min = FXRep::MIN(); + static constexpr T one_half = FXRep::ONE_HALF(); + static constexpr T one_fourth = FXRep::ONE_FOURTH(); + static constexpr T eps = FXRep::EPS(); + +public: + typedef int (*CountlsFunc)(T); + + void testSpecialNumbers(CountlsFunc func) { + constexpr bool is_signed = (FXRep::SIGN_LEN > 0); + + EXPECT_EQ(FXRep::INTEGRAL_LEN, func(one_half)); + EXPECT_EQ(FXRep::INTEGRAL_LEN + 1, func(one_fourth)); + EXPECT_EQ(FXRep::VALUE_LEN, func(zero)); + EXPECT_EQ(FXRep::VALUE_LEN - 1, func(eps)); + EXPECT_EQ(0, func(max)); + // If signed, left shifting the minimum value will overflow, so countls = 0. + // If unsigned, the minimum value is zero, so countls is the number of value + // bits according to ISO/IEC TR 18037. + EXPECT_EQ(is_signed ? 0 : FXRep::VALUE_LEN, func(min)); + + if (10 <= static_cast<int>(max)) + EXPECT_EQ(FXRep::INTEGRAL_LEN - 4, func(10)); + + if (static_cast<int>(min) <= -10) + EXPECT_EQ(FXRep::INTEGRAL_LEN - 4, func(-10)); + + if constexpr (is_signed) { + EXPECT_EQ(FXRep::VALUE_LEN, func(-zero)); + EXPECT_EQ(FXRep::VALUE_LEN, func(-eps)); + EXPECT_EQ(FXRep::INTEGRAL_LEN + 1, func(-one_half)); + if (FXRep::FRACTION_LEN >= 2) + EXPECT_EQ(FXRep::INTEGRAL_LEN + 2, func(-one_fourth)); + } + } +}; + +#define LIST_COUNTLS_TESTS(T, func) \ + using LlvmLibcCountlsTest = CountlsTest<T>; \ + TEST_F(LlvmLibcCountlsTest, SpecialNumbers) { testSpecialNumbers(&func); } \ + static_assert(true, "Require semicolon.")
diff --git a/test/src/stdfix/countlshk_test.cpp b/test/src/stdfix/countlshk_test.cpp new file mode 100644 index 0000000..659f869 --- /dev/null +++ b/test/src/stdfix/countlshk_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlshk -------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlshk.h" + +LIST_COUNTLS_TESTS(short accum, LIBC_NAMESPACE::countlshk);
diff --git a/test/src/stdfix/countlshr_test.cpp b/test/src/stdfix/countlshr_test.cpp new file mode 100644 index 0000000..361d4ac --- /dev/null +++ b/test/src/stdfix/countlshr_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlshr -------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlshr.h" + +LIST_COUNTLS_TESTS(short fract, LIBC_NAMESPACE::countlshr);
diff --git a/test/src/stdfix/countlsk_test.cpp b/test/src/stdfix/countlsk_test.cpp new file mode 100644 index 0000000..74cb519 --- /dev/null +++ b/test/src/stdfix/countlsk_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlsk --------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlsk.h" + +LIST_COUNTLS_TESTS(accum, LIBC_NAMESPACE::countlsk);
diff --git a/test/src/stdfix/countlslk_test.cpp b/test/src/stdfix/countlslk_test.cpp new file mode 100644 index 0000000..006939d --- /dev/null +++ b/test/src/stdfix/countlslk_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlslk -------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlslk.h" + +LIST_COUNTLS_TESTS(long accum, LIBC_NAMESPACE::countlslk);
diff --git a/test/src/stdfix/countlslr_test.cpp b/test/src/stdfix/countlslr_test.cpp new file mode 100644 index 0000000..896cf92 --- /dev/null +++ b/test/src/stdfix/countlslr_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlslr -------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlslr.h" + +LIST_COUNTLS_TESTS(long fract, LIBC_NAMESPACE::countlslr);
diff --git a/test/src/stdfix/countlsr_test.cpp b/test/src/stdfix/countlsr_test.cpp new file mode 100644 index 0000000..d7ae91c --- /dev/null +++ b/test/src/stdfix/countlsr_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlsr --------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlsr.h" + +LIST_COUNTLS_TESTS(fract, LIBC_NAMESPACE::countlsr);
diff --git a/test/src/stdfix/countlsuhk_test.cpp b/test/src/stdfix/countlsuhk_test.cpp new file mode 100644 index 0000000..d8e68d6 --- /dev/null +++ b/test/src/stdfix/countlsuhk_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlsuhk ------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlsuhk.h" + +LIST_COUNTLS_TESTS(unsigned short accum, LIBC_NAMESPACE::countlsuhk);
diff --git a/test/src/stdfix/countlsuhr_test.cpp b/test/src/stdfix/countlsuhr_test.cpp new file mode 100644 index 0000000..7dbc590 --- /dev/null +++ b/test/src/stdfix/countlsuhr_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlsuhr ------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlsuhr.h" + +LIST_COUNTLS_TESTS(unsigned short fract, LIBC_NAMESPACE::countlsuhr);
diff --git a/test/src/stdfix/countlsuk_test.cpp b/test/src/stdfix/countlsuk_test.cpp new file mode 100644 index 0000000..20f78d8 --- /dev/null +++ b/test/src/stdfix/countlsuk_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlsuk -------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlsuk.h" + +LIST_COUNTLS_TESTS(unsigned accum, LIBC_NAMESPACE::countlsuk);
diff --git a/test/src/stdfix/countlsulk_test.cpp b/test/src/stdfix/countlsulk_test.cpp new file mode 100644 index 0000000..81ae208 --- /dev/null +++ b/test/src/stdfix/countlsulk_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlsulk ------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlsulk.h" + +LIST_COUNTLS_TESTS(unsigned long accum, LIBC_NAMESPACE::countlsulk);
diff --git a/test/src/stdfix/countlsulr_test.cpp b/test/src/stdfix/countlsulr_test.cpp new file mode 100644 index 0000000..5b9b047 --- /dev/null +++ b/test/src/stdfix/countlsulr_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlsulr ------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlsulr.h" + +LIST_COUNTLS_TESTS(unsigned long fract, LIBC_NAMESPACE::countlsulr);
diff --git a/test/src/stdfix/countlsur_test.cpp b/test/src/stdfix/countlsur_test.cpp new file mode 100644 index 0000000..67e32d7 --- /dev/null +++ b/test/src/stdfix/countlsur_test.cpp
@@ -0,0 +1,13 @@ +//===-- Unittests for countlsur -------------------------------------------===// +// +// 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 "CountlsTest.h" + +#include "src/stdfix/countlsur.h" + +LIST_COUNTLS_TESTS(unsigned fract, LIBC_NAMESPACE::countlsur);