From 96ab34020ae36d085ab18cc9ce3db5bf3b1b77ef Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Tue, 21 Nov 2023 13:06:56 +0000 Subject: [PATCH] simplify fp headers --- libc/src/__support/CPP/limits.h | 53 +++ libc/src/__support/FPUtil/CMakeLists.txt | 8 + .../__support/FPUtil/FloatRepresentation.h | 340 ++++++++++++++++++ libc/test/src/__support/FPUtil/CMakeLists.txt | 10 + .../FPUtil/float_representation_test.cpp | 247 +++++++++++++ .../llvm-project-overlay/libc/BUILD.bazel | 8 + .../test/src/__support/FPUtil/BUILD.bazel | 21 ++ 7 files changed, 687 insertions(+) create mode 100644 libc/src/__support/FPUtil/FloatRepresentation.h create mode 100644 libc/test/src/__support/FPUtil/float_representation_test.cpp create mode 100644 utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel diff --git a/libc/src/__support/CPP/limits.h b/libc/src/__support/CPP/limits.h index e163f00c73209..676b03da190b2 100644 --- a/libc/src/__support/CPP/limits.h +++ b/libc/src/__support/CPP/limits.h @@ -9,7 +9,10 @@ #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_LIMITS_H #define LLVM_LIBC_SRC___SUPPORT_CPP_LIMITS_H +#include "src/__support/macros/attributes.h" +#include #include +#include namespace LIBC_NAMESPACE { namespace cpp { @@ -95,6 +98,56 @@ template <> class numeric_limits<__uint128_t> { }; #endif +// floating point types + +template <> struct numeric_limits { + static constexpr float denorm_min() { return FLT_TRUE_MIN; } + static constexpr float min() { return FLT_MIN; } + static constexpr float lowest() { return -FLT_MAX; } + static constexpr float max() { return FLT_MAX; } + static constexpr float epsilon() { return FLT_EPSILON; } + // static constexpr float infinity() { return HUGE_VALF; } + LIBC_INLINE_VAR static constexpr int digits = FLT_MANT_DIG; + LIBC_INLINE_VAR static constexpr int digits10 = FLT_DIG; + LIBC_INLINE_VAR static constexpr int min_exponent = FLT_MIN_EXP; + LIBC_INLINE_VAR static constexpr int min_exponent10 = FLT_MIN_10_EXP; + LIBC_INLINE_VAR static constexpr int max_exponent = FLT_MAX_EXP; + LIBC_INLINE_VAR static constexpr int max_exponent10 = FLT_MAX_10_EXP; + LIBC_INLINE_VAR static constexpr int radix = FLT_RADIX; +}; + +template <> struct numeric_limits { + static constexpr double denorm_min() { return DBL_TRUE_MIN; } + static constexpr double min() { return DBL_MIN; } + static constexpr double lowest() { return -DBL_MAX; } + static constexpr double max() { return DBL_MAX; } + static constexpr double epsilon() { return DBL_EPSILON; } + // static constexpr double infinity() { return HUGE_VAL; } + LIBC_INLINE_VAR static constexpr int digits = DBL_MANT_DIG; + LIBC_INLINE_VAR static constexpr int digits10 = DBL_DIG; + LIBC_INLINE_VAR static constexpr int min_exponent = DBL_MIN_EXP; + LIBC_INLINE_VAR static constexpr int min_exponent10 = DBL_MIN_10_EXP; + LIBC_INLINE_VAR static constexpr int max_exponent = DBL_MAX_EXP; + LIBC_INLINE_VAR static constexpr int max_exponent10 = DBL_MAX_10_EXP; + LIBC_INLINE_VAR static constexpr int radix = FLT_RADIX; +}; + +template <> struct numeric_limits { + static constexpr long double denorm_min() { return LDBL_TRUE_MIN; } + static constexpr long double min() { return LDBL_MIN; } + static constexpr long double lowest() { return -LDBL_MAX; } + static constexpr long double max() { return LDBL_MAX; } + static constexpr long double epsilon() { return LDBL_EPSILON; } + // static constexpr long double infinity() { return HUGE_VALL; } + LIBC_INLINE_VAR static constexpr int digits = LDBL_MANT_DIG; + LIBC_INLINE_VAR static constexpr int digits10 = LDBL_DIG; + LIBC_INLINE_VAR static constexpr int min_exponent = LDBL_MIN_EXP; + LIBC_INLINE_VAR static constexpr int min_exponent10 = LDBL_MIN_10_EXP; + LIBC_INLINE_VAR static constexpr int max_exponent = LDBL_MAX_EXP; + LIBC_INLINE_VAR static constexpr int max_exponent10 = LDBL_MAX_10_EXP; + LIBC_INLINE_VAR static constexpr int radix = FLT_RADIX; +}; + } // namespace cpp } // namespace LIBC_NAMESPACE diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt index e19d8fc4aa9a0..583aff1d75ff0 100644 --- a/libc/src/__support/FPUtil/CMakeLists.txt +++ b/libc/src/__support/FPUtil/CMakeLists.txt @@ -23,6 +23,14 @@ add_header_library( libc.src.errno.errno ) +add_header_library( + float_representation + HDRS + FloatRepresentation.h + DEPENDS + libc.src.__support.uint128 +) + add_header_library( platform_defs HDRS diff --git a/libc/src/__support/FPUtil/FloatRepresentation.h b/libc/src/__support/FPUtil/FloatRepresentation.h new file mode 100644 index 0000000000000..e6e443807b927 --- /dev/null +++ b/libc/src/__support/FPUtil/FloatRepresentation.h @@ -0,0 +1,340 @@ +//===-- Properties of floating point numbers --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOATREPRESENTATION_H +#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOATREPRESENTATION_H + +#include "src/__support/CPP/expected.h" +#include "src/__support/CPP/type_traits.h" +#include "src/__support/UInt128.h" +#include + +namespace LIBC_NAMESPACE { +namespace fputil { + +#ifndef P +#define P(var) +#endif +#ifndef D +#define D(var) +#endif + +enum class FPType { + IEEE754_Binary16, + IEEE754_Binary32, + IEEE754_Binary64, + IEEE754_Binary128, + X86_Binary80, +}; + +enum class FPEncoding { + IEEE754, + X86_ExtendedPrecision, +}; + +enum class fp_error { overflow, underflow }; + +// template static constexpr FPType fp_type() { +// static_assert(cpp::is_floating_point_v); // floating point +// static_assert(cpp::numeric_limits::radix == 2); // binary +// switch (cpp::numeric_limits::digits) { +// default: +// __builtin_unreachable(); +// case 11: +// return FPType::IEEE754_Binary16; +// case 24: +// return FPType::IEEE754_Binary32; +// case 53: +// return FPType::IEEE754_Binary64; +// case 64: +// return FPType::X86_Binary80; +// case 113: +// return FPType::IEEE754_Binary128; +// } +// } + +// TODO: move these utilities elsewhere +template static constexpr T mask_trailing_ones() { + static_assert(cpp::is_unsigned_v); + constexpr unsigned t_bits = CHAR_BIT * sizeof(T); + static_assert(count <= t_bits && "Invalid bit index"); + return count == 0 ? 0 : (T(-1) >> (t_bits - count)); +} + +template static constexpr T mask_leading_ones() { + constexpr unsigned t_bits = CHAR_BIT * sizeof(T); + return ~mask_trailing_ones(); +} + +template static constexpr T mask_trailing_zeros() { + constexpr unsigned t_bits = CHAR_BIT * sizeof(T); + return mask_leading_ones(); +} + +template static constexpr T mask_leading_zeros() { + constexpr unsigned t_bits = CHAR_BIT * sizeof(T); + return mask_trailing_ones(); +} +template int leading_zeroes(T value); +template <> LIBC_INLINE int leading_zeroes(uint16_t value) { + return value == 0 ? sizeof(uint16_t) * 8 : __builtin_clzs(value); +} +template <> LIBC_INLINE int leading_zeroes(uint32_t value) { + return value == 0 ? sizeof(uint32_t) * 8 : __builtin_clz(value); +} +template <> LIBC_INLINE int leading_zeroes(uint64_t value) { + return value == 0 ? sizeof(uint64_t) * 8 : __builtin_clzl(value); +} +template <> LIBC_INLINE int leading_zeroes(UInt128 value) { + return value == 0 ? sizeof(UInt128) * 8 + : (value > UInt128(UINT64_MAX) + ? __builtin_clzl(value >> 64) + : __builtin_clzl(value & UINT64_MAX) + 64); +} + +template struct fp_type_traits_base {}; + +template <> struct fp_type_traits_base { + LIBC_INLINE_VAR static constexpr int FP_BITS = 16; + LIBC_INLINE_VAR static constexpr int SIG_BITS = 10; + LIBC_INLINE_VAR static constexpr int EXP_BITS = 5; + LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754; + using UIntType = uint16_t; +}; + +template <> struct fp_type_traits_base { + LIBC_INLINE_VAR static constexpr int FP_BITS = 32; + LIBC_INLINE_VAR static constexpr int SIG_BITS = 23; + LIBC_INLINE_VAR static constexpr int EXP_BITS = 8; + LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754; + using UIntType = uint32_t; +}; + +template <> struct fp_type_traits_base { + LIBC_INLINE_VAR static constexpr int FP_BITS = 64; + LIBC_INLINE_VAR static constexpr int SIG_BITS = 52; + LIBC_INLINE_VAR static constexpr int EXP_BITS = 11; + LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754; + using UIntType = uint64_t; +}; + +template <> struct fp_type_traits_base { + LIBC_INLINE_VAR static constexpr int FP_BITS = 128; + LIBC_INLINE_VAR static constexpr int SIG_BITS = 112; + LIBC_INLINE_VAR static constexpr int EXP_BITS = 15; + LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754; + using UIntType = UInt128; +}; + +template <> struct fp_type_traits_base { + LIBC_INLINE_VAR static constexpr int FP_BITS = 80; + LIBC_INLINE_VAR static constexpr int SIG_BITS = 64; + LIBC_INLINE_VAR static constexpr int EXP_BITS = 15; + LIBC_INLINE_VAR static constexpr auto ENCODING = + FPEncoding::X86_ExtendedPrecision; + using UIntType = UInt128; +}; + +template +struct fp_repesentation : public fp_type_traits_base { + using base = fp_type_traits_base; + using base::ENCODING; + using base::EXP_BITS; + using base::FP_BITS; + using base::SIG_BITS; + using typename base::UIntType; + static_assert(cpp::is_unsigned_v); + + LIBC_INLINE_VAR static constexpr bool HAS_IMPLICIT_SIG_BIT = + (ENCODING == FPEncoding::IEEE754); + + LIBC_INLINE_VAR static constexpr int STORAGE_BITS = + sizeof(UIntType) * CHAR_BIT; + static_assert(STORAGE_BITS >= FP_BITS); + + // The number of bits to represent sign. + // For documentation purpose, always 1. + LIBC_INLINE_VAR static constexpr int SIGN_BITS = 1; + static_assert(SIGN_BITS + EXP_BITS + SIG_BITS == FP_BITS); + + // The exponent bias. Always positive. + LIBC_INLINE_VAR static constexpr int32_t EXP_BIAS = + (1U << (EXP_BITS - 1U)) - 1U; + static_assert(EXP_BIAS > 0); + + // The exponent value for denormal numbers. + LIBC_INLINE_VAR static constexpr int32_t EXP_DENORM = -EXP_BIAS; + // The minimum exponent value for normal numbers. + LIBC_INLINE_VAR static constexpr int32_t EXP_MIN = EXP_DENORM + 1; + // The maximum exponent value for normal numbers. + LIBC_INLINE_VAR static constexpr int32_t EXP_MAX = EXP_BIAS; + + // Shifts + LIBC_INLINE_VAR static constexpr int SIG_MASK_SHIFT = 0; + LIBC_INLINE_VAR static constexpr int EXP_MASK_SHIFT = SIG_BITS; + LIBC_INLINE_VAR static constexpr int SIGN_MASK_SHIFT = SIG_BITS + EXP_BITS; + LIBC_INLINE_VAR static constexpr int SIG_HIGH_BIT_SHIFT = SIG_BITS - 1; + + // Masks + LIBC_INLINE_VAR static constexpr UIntType SIG_MASK = + mask_trailing_ones() << SIG_MASK_SHIFT; + LIBC_INLINE_VAR static constexpr UIntType EXP_MASK = + mask_trailing_ones() << EXP_MASK_SHIFT; + LIBC_INLINE_VAR static constexpr UIntType SIGN_MASK = + mask_trailing_ones() << SIGN_MASK_SHIFT; + LIBC_INLINE_VAR static constexpr UIntType FP_MASK = + mask_trailing_ones(); + // Masks are disjoint + static_assert((SIG_MASK & EXP_MASK & SIGN_MASK) == 0); + // Masks cover all the bits + static_assert((SIG_MASK | EXP_MASK | SIGN_MASK) == FP_MASK); + + // Representation observer + static UIntType sign_bits(UIntType rep) { return rep & SIGN_MASK; } + static UIntType exp_bits(UIntType rep) { return rep & EXP_MASK; } + static UIntType sig_bits(UIntType rep) { return rep & SIG_MASK; } + + // Default NaN Payload is the top most significant bit + LIBC_INLINE_VAR static constexpr UIntType DEFAULT_NAN_PAYLOAD = + mask_trailing_ones() << SIG_HIGH_BIT_SHIFT; + + static bool exp_all_zeroes(UIntType rep) { return exp_bits(rep) == 0; } + static bool exp_all_ones(UIntType rep) { return exp_bits(rep) == EXP_MASK; } + static bool sig_all_zeroes(UIntType rep) { return sig_bits(rep) == 0; } + +public: + static bool sign(UIntType rep) { return sign_bits(rep); } + static bool is_nan(UIntType rep) { + return exp_all_ones(rep) && !sig_all_zeroes(rep); + } + static bool is_inf(UIntType rep) { + if constexpr (ENCODING == FPEncoding::X86_ExtendedPrecision) { + constexpr UIntType BITS_62_0 = DEFAULT_NAN_PAYLOAD - 1; + return exp_all_ones(rep) && ((rep & BITS_62_0) == 0); + } + return exp_all_ones(rep) && sig_all_zeroes(rep); + } + static bool is_zero(UIntType rep) { + return exp_all_zeroes(rep) && sig_all_zeroes(rep); + } + static bool is_normal(UIntType rep) { + return !exp_all_ones(rep) && !exp_all_zeroes(rep); + } + static bool is_denorm(UIntType rep) { + return exp_all_zeroes(rep) && !sig_all_zeroes(rep); + } + static int32_t get_exponent(UIntType rep) { + return int32_t((rep & EXP_MASK) >> SIG_BITS) - EXP_BIAS - SIG_BITS; + } + static UIntType get_significand(UIntType rep) { + if (ENCODING == FPEncoding::IEEE754 && !is_denorm(rep)) + return (UIntType(1) << SIG_BITS) | sig_bits(rep); + return sig_bits(rep); + } + static UIntType zero(bool sign) { return sign ? SIGN_MASK : 0; } + static UIntType inf(bool sign) { + P("inf()"); + return zero(sign) | EXP_MASK; + } + static UIntType nan(bool sign, UIntType payload = 0) { + P("nan()"); + if (payload == 0) + payload = DEFAULT_NAN_PAYLOAD; + else + payload &= SIG_MASK; + return inf(sign) | payload; + } + // Simply pack provided values without performing any checks. This may result + // in invalid representation. + static UIntType assemble(bool sign, int32_t exponent, UIntType significand) { + // assert(exponent >= EXP_DENORM && exponent <= EXP_MAX); + P("assemble()"); + D(sign); + D(exponent); + D(significand); + D(significand & SIG_MASK); + const UIntType biased_exponent = exponent + EXP_BIAS; + D(biased_exponent); + return zero(sign) | (biased_exponent << SIG_BITS) | + (significand & SIG_MASK); + } + // This normalizes exponent and significand for the underlying representation. + static cpp::expected number(bool sign, int32_t exponent, + UIntType significand) { + if (significand == 0) + return zero(sign); + static_assert(sizeof(UIntType) * CHAR_BIT > SIG_BITS); + constexpr int HIDDEN_SIG_BITS = HAS_IMPLICIT_SIG_BIT ? 1 : 0; + constexpr int WORKING_SIG_EXTRA_BITS = + STORAGE_BITS - SIG_BITS - HIDDEN_SIG_BITS; + constexpr UIntType MIN_WORKING_SIG = + mask_trailing_ones(); + constexpr UIntType MAX_WORKING_SIG = + mask_trailing_zeros(); + P("constants"); + D(STORAGE_BITS); + D(HIDDEN_SIG_BITS); + D(FP_BITS); + D(SIG_BITS); + D(EXP_BITS); + D(WORKING_SIG_EXTRA_BITS); + D(SIG_MASK); + D(EXP_MASK); + D(SIGN_MASK); + D(FP_MASK); + D(MIN_WORKING_SIG); + D(MAX_WORKING_SIG); + D(EXP_BIAS); + D(EXP_DENORM); + D(EXP_MIN); + D(EXP_MAX); + + P("arguments"); + D(sign); + D(exponent); + D(significand); + P("start"); + // The working significand format is a normalized form where the leading bit + // is moved to the 'UIntType' highest bit. It is stored in 'UIntType' which + // size is larger than 'SIG_BITS', this provides extra bits to detect + // overflow / underflow. For IEEE754 Binary format we remove one extra bit + // to account for the hidden leading bit. + // e.g., for IEEE754_Binary16 where SIG_BITS == 10 and UIntType is uint16_t. + const int sig_shl = leading_zeroes(significand); + const UIntType working_sig = significand << sig_shl; + const int32_t working_exp = + exponent - sig_shl + STORAGE_BITS - HIDDEN_SIG_BITS; + D(sig_shl); + D(working_sig); + D(working_exp); + if (working_exp < EXP_MIN) { + P("working_exp < EXP_MIN"); + const bool sig_underflow = working_sig <= MIN_WORKING_SIG; + const int sig_shr = EXP_MIN - working_exp - 1; + D(sig_underflow); + D(sig_shr); + if (sig_shr > SIG_BITS || (sig_shr == SIG_BITS && sig_underflow)) + return cpp::unexpected(fp_error::underflow); + // denormal + return assemble(sign, EXP_DENORM, + working_sig >> (sig_shr + WORKING_SIG_EXTRA_BITS)); + } + P("working_exp >= EXP_MIN"); + const bool sig_overflow = working_sig > MAX_WORKING_SIG; + D(sig_overflow); + if (working_exp > EXP_MAX || (working_exp == EXP_MAX && sig_overflow)) + return cpp::unexpected(fp_error::overflow); + // normal + return assemble(sign, working_exp, working_sig >> WORKING_SIG_EXTRA_BITS); + } +}; + +} // namespace fputil +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOATREPRESENTATION_H diff --git a/libc/test/src/__support/FPUtil/CMakeLists.txt b/libc/test/src/__support/FPUtil/CMakeLists.txt index 411b8281470cf..59d20b755cbf5 100644 --- a/libc/test/src/__support/FPUtil/CMakeLists.txt +++ b/libc/test/src/__support/FPUtil/CMakeLists.txt @@ -22,6 +22,16 @@ add_libc_test( libc.src.__support.FPUtil.fpbits_str ) +add_libc_test( + float_representation_test + SUITE + libc-fputil-tests + SRCS + float_representation_test.cpp + DEPENDS + libc.src.__support.FPUtil.float_representation +) + add_fp_unittest( rounding_mode_test SUITE diff --git a/libc/test/src/__support/FPUtil/float_representation_test.cpp b/libc/test/src/__support/FPUtil/float_representation_test.cpp new file mode 100644 index 0000000000000..318c13429cb75 --- /dev/null +++ b/libc/test/src/__support/FPUtil/float_representation_test.cpp @@ -0,0 +1,247 @@ +//===-- Unittests for FloatRepresentation ---------------------------------===// +// +// 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/CPP/bit.h" +#include "src/__support/CPP/type_traits.h" +#include "src/__support/FPUtil/FloatRepresentation.h" +#include "test/UnitTest/Test.h" + +namespace LIBC_NAMESPACE { +namespace fputil { +namespace { + +constexpr __uint128_t operator""_u128(const char *x) { + __uint128_t y = 0; + for (int i = 2; x[i] != '\0'; ++i) { + y *= 16ull; + if ('0' <= x[i] && x[i] <= '9') + y += x[i] - '0'; + else if ('A' <= x[i] && x[i] <= 'F') + y += x[i] - 'A' + 10; + else if ('a' <= x[i] && x[i] <= 'f') + y += x[i] - 'a' + 10; + } + return y; +} + +enum FPValue : uint8_t { + PosInf, + PosLargestNormal, + Pos1, + Pos0_1, + PosSmallestNormal, + PosLargestDenormal, + PosSmallestDenormal, + Pos0, + Neg0, + NegSmallestDenormal, + NegLargestDenormal, + NegSmallestNormal, + Neg0_1, + Neg1, + NegLargestNormal, + NegInf, + QuietNaN, + SignalingNaN, +}; + +enum class FPCategory { Inf, NaN, Normal, Denormal, Zero }; + +FPCategory getFPCategory(FPValue value) { + switch (value) { + case PosInf: + case NegInf: + return FPCategory::Inf; + case PosLargestNormal: + case Pos1: + case Pos0_1: + case PosSmallestNormal: + case NegSmallestNormal: + case Neg0_1: + case Neg1: + case NegLargestNormal: + return FPCategory::Normal; + case PosLargestDenormal: + case PosSmallestDenormal: + case NegSmallestDenormal: + case NegLargestDenormal: + return FPCategory::Denormal; + case Pos0: + case Neg0: + return FPCategory::Zero; + case QuietNaN: + case SignalingNaN: + return FPCategory::NaN; + } +} + +struct IEEE754_Binary16_TestCase { + FPValue semantic; + uint16_t representation; +} kIEEE754_Binary16TestCases[] = { + {PosInf, /* */ 0x7C00}, + {PosLargestNormal, /* */ 0x7BFF}, + {Pos1, /* */ 0x3C00}, + {Pos0_1, /* */ 0x2E66}, + {PosSmallestNormal, /* */ 0x0400}, + {PosLargestDenormal, /* */ 0x03FF}, + {PosSmallestDenormal, /* */ 0x0001}, + {Pos0, /* */ 0x0000}, + {Neg0, /* */ 0x8000}, + {NegSmallestDenormal, /* */ 0x8001}, + {NegLargestDenormal, /* */ 0x83FF}, + {NegSmallestNormal, /* */ 0x8400}, + {Neg0_1, /* */ 0xAE66}, + {Neg1, /* */ 0xBC00}, + {NegLargestNormal, /* */ 0xFBFF}, + {NegInf, /* */ 0xFC00}, + {QuietNaN, /* */ 0x7E00}, + {SignalingNaN, /* */ 0x7D00}, +}; + +struct IEEE754_Binary32_TestCase { + FPValue semantic; + uint32_t representation; +} kIEEE754_Binary32TestCases[] = { + {PosInf, /* */ 0x7F800000}, + {PosLargestNormal, /* */ 0x7F7FFFFF}, + {Pos1, /* */ 0x3F800000}, + {Pos0_1, /* */ 0x3DCCCCCD}, + {PosSmallestNormal, /* */ 0x00800000}, + {PosLargestDenormal, /* */ 0x007FFFFF}, + {PosSmallestDenormal, /* */ 0x00000001}, + {Pos0, /* */ 0x00000000}, + {Neg0, /* */ 0x80000000}, + {NegSmallestDenormal, /* */ 0x80000001}, + {NegLargestDenormal, /* */ 0x807FFFFF}, + {NegSmallestNormal, /* */ 0x80800000}, + {Neg0_1, /* */ 0xBDCCCCCD}, + {Neg1, /* */ 0xBF800000}, + {NegLargestNormal, /* */ 0xFF7FFFFF}, + {NegInf, /* */ 0xFF800000}, + {QuietNaN, /* */ 0x7FC00000}, + {SignalingNaN, /* */ 0x7FA00000}, +}; + +struct IEEE754_Binary64_TestCase { + FPValue semantic; + uint64_t representation; +} kIEEE754_Binary64TestCases[] = { + {PosInf, /* */ 0x7FF0000000000000}, + {PosLargestNormal, /* */ 0x7FEFFFFFFFFFFFFF}, + {Pos1, /* */ 0x3FF0000000000000}, + {Pos0_1, /* */ 0x3FB999999999999A}, + {PosSmallestNormal, /* */ 0x0010000000000000}, + {PosLargestDenormal, /* */ 0x000FFFFFFFFFFFFF}, + {PosSmallestDenormal, /* */ 0x0000000000000001}, + {Pos0, /* */ 0x0000000000000000}, + {Neg0, /* */ 0x8000000000000000}, + {NegSmallestDenormal, /* */ 0x8000000000000001}, + {NegLargestDenormal, /* */ 0x800FFFFFFFFFFFFF}, + {NegSmallestNormal, /* */ 0x8010000000000000}, + {Neg0_1, /* */ 0xBFB999999999999A}, + {Neg1, /* */ 0xBFF0000000000000}, + {NegLargestNormal, /* */ 0xFFEFFFFFFFFFFFFF}, + {NegInf, /* */ 0xFFF0000000000000}, + {QuietNaN, /* */ 0x7FF8000000000000}, + {SignalingNaN, /* */ 0x7FF4000000000000}, +}; + +struct IEEE754_Binary128_TestCase { + FPValue semantic; + UInt128 representation; +} kIEEE754_Binary128TestCases[] = { + {PosInf, /* */ 0x7FFF0000000000000000000000000000_u128}, + {PosLargestNormal, /* */ 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128}, + {Pos1, /* */ 0x3FFF0000000000000000000000000000_u128}, + {Pos0_1, /* */ 0x3FFB999999999999A000000000000000_u128}, + {PosSmallestNormal, /* */ 0x00010000000000000000000000000000_u128}, + {PosLargestDenormal, /* */ 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128}, + {PosSmallestDenormal, /* */ 0x00000000000000000000000000000001_u128}, + {Pos0, /* */ 0x00000000000000000000000000000000_u128}, + {Neg0, /* */ 0x80000000000000000000000000000000_u128}, + {NegSmallestDenormal, /* */ 0x80000000000000000000000000000001_u128}, + {NegLargestDenormal, /* */ 0x8000FFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128}, + {NegSmallestNormal, /* */ 0x80010000000000000000000000000000_u128}, + {Neg0_1, /* */ 0xBFFB999999999999A000000000000000_u128}, + {Neg1, /* */ 0xBFFF0000000000000000000000000000_u128}, + {NegLargestNormal, /* */ 0xFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128}, + {NegInf, /* */ 0xFFFF0000000000000000000000000000_u128}, + {QuietNaN, /* */ 0x7FFF8000000000000000000000000000_u128}, + {SignalingNaN, /* */ 0x7FFF4000000000000000000000000000_u128}, +}; + +struct X86_Binary80_TestCase { + FPValue semantic; + UInt128 representation; +} kX86_Binary80TestCases[] = { + {PosInf, /* */ 0x0000000000007FFF8000000000000000_u128}, + {PosLargestNormal, /* */ 0x0000000000007FFEFFFFFFFFFFFFFFFF_u128}, + {Pos1, /* */ 0x0000000000003FFF8000000000000000_u128}, + {Pos0_1, /* */ 0x0000000000003FFBCCCCCCCCCCCCD000_u128}, + {PosSmallestNormal, /* */ 0x00000000000000018000000000000000_u128}, + {PosLargestDenormal, /* */ 0x00000000000000007FFFFFFFFFFFFFFF_u128}, + {PosSmallestDenormal, /* */ 0x00000000000000000000000000000001_u128}, + {Pos0, /* */ 0x00000000000000000000000000000000_u128}, + {Neg0, /* */ 0x00000000000080000000000000000000_u128}, + {NegSmallestDenormal, /* */ 0x00000000000080000000000000000001_u128}, + {NegLargestDenormal, /* */ 0x00000000000080007FFFFFFFFFFFFFFF_u128}, + {NegSmallestNormal, /* */ 0x00000000000080018000000000000000_u128}, + {Neg0_1, /* */ 0x000000000000BFFBCCCCCCCCCCCCD000_u128}, + {Neg1, /* */ 0x000000000000BFFF8000000000000000_u128}, + {NegLargestNormal, /* */ 0x000000000000FFFEFFFFFFFFFFFFFFFF_u128}, + {NegInf, /* */ 0x000000000000FFFF8000000000000000_u128}, + {QuietNaN, /* */ 0x0000000000007FFFC000000000000000_u128}, + {SignalingNaN, /* */ 0x0000000000007FFFA000000000000000_u128}, +}; + +#define FP_TEST(FP_TYPE) \ + TEST(LlvmLibcFloatRepresentationTest, FP_TYPE) { \ + constexpr auto type = FPType::FP_TYPE; \ + using FP = fp_repesentation; \ + using UIntType = typename FP::UIntType; \ + for (auto tc : k##FP_TYPE##TestCases) { \ + const UIntType in_rep = tc.representation; \ + const auto category = getFPCategory(tc.semantic); \ + switch (category) { \ + case FPCategory::Inf: \ + EXPECT_TRUE(FP::is_inf(in_rep)); \ + break; \ + case FPCategory::NaN: \ + EXPECT_TRUE(FP::is_nan(in_rep)); \ + break; \ + case FPCategory::Normal: \ + EXPECT_TRUE(FP::is_normal(in_rep)); \ + break; \ + case FPCategory::Denormal: \ + EXPECT_TRUE(FP::is_denorm(in_rep)); \ + break; \ + case FPCategory::Zero: \ + EXPECT_TRUE(FP::is_zero(in_rep)); \ + break; \ + } \ + if (category == FPCategory::Normal || \ + category == FPCategory::Denormal || category == FPCategory::Zero) { \ + const bool sign = FP::sign(in_rep); \ + const UIntType sig = FP::get_significand(in_rep); \ + const int32_t exponent = FP::get_exponent(in_rep); \ + const UIntType out_rep = FP::number(sign, exponent, sig); \ + ASSERT_EQ(in_rep, out_rep); \ + } \ + } \ + } + +FP_TEST(IEEE754_Binary16) +FP_TEST(IEEE754_Binary32) +FP_TEST(IEEE754_Binary64) +FP_TEST(IEEE754_Binary128) +FP_TEST(X86_Binary80) + +} // namespace +} // namespace fputil +} // namespace LIBC_NAMESPACE diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 486146187d3a6..55343e62e1f9f 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -861,6 +861,14 @@ libc_support_library( ], ) +libc_support_library( + name = "__support_fputil_float_representation", + hdrs = ["src/__support/FPUtil/FloatRepresentation.h"], + deps = [ + ":__support_cpp_expected", + ], +) + libc_support_library( name = "__support_osutil_syscall", hdrs = ["src/__support/OSUtil/syscall.h"], diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel new file mode 100644 index 0000000000000..bc430464d8e46 --- /dev/null +++ b/utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel @@ -0,0 +1,21 @@ +# This file is licensed 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 + +# Tests for LLVM libc FPUtil functions. + +load("//libc/test:libc_test_rules.bzl", "libc_test") + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +libc_test( + name = "float_representation_test", + srcs = ["float_representation_test.cpp"], + deps = [ + "//libc:__support_cpp_bit", + "//libc:__support_cpp_type_traits", + "//libc:__support_fputil_float_representation", + ], +)