diff --git a/libc/src/__support/CPP/bit.h b/libc/src/__support/CPP/bit.h index 4de142b56165b..122f6b8c33281 100644 --- a/libc/src/__support/CPP/bit.h +++ b/libc/src/__support/CPP/bit.h @@ -29,10 +29,10 @@ namespace LIBC_NAMESPACE::cpp { // UB in the implementation. template < typename To, typename From, - typename = cpp::enable_if_t, - typename = cpp::enable_if_t::value>, - typename = cpp::enable_if_t::value>, - typename = cpp::enable_if_t::value>> + typename = cpp::enable_if_t::value && + cpp::is_trivially_copyable::value && + cpp::is_trivially_copyable::value>> LIBC_INLINE constexpr To bit_cast(const From &from) { MSAN_UNPOISON(&from, sizeof(From)); #if LIBC_HAS_BUILTIN(__builtin_bit_cast) diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h index 37e2820eab855..b13ce80f94f6e 100644 --- a/libc/src/__support/FPUtil/FPBits.h +++ b/libc/src/__support/FPUtil/FPBits.h @@ -11,6 +11,7 @@ #include "src/__support/CPP/bit.h" #include "src/__support/CPP/type_traits.h" +#include "src/__support/UInt128.h" #include "src/__support/common.h" #include "src/__support/macros/attributes.h" // LIBC_INLINE diff --git a/libc/src/__support/UInt.h b/libc/src/__support/UInt.h index cfd495c586185..79e05940f0277 100644 --- a/libc/src/__support/UInt.h +++ b/libc/src/__support/UInt.h @@ -30,7 +30,7 @@ template struct BigInt { static_assert(Bits > 0 && Bits % 64 == 0, "Number of bits in BigInt should be a multiple of 64."); LIBC_INLINE_VAR static constexpr size_t WORDCOUNT = Bits / 64; - uint64_t val[WORDCOUNT]{}; + cpp::array val{}; LIBC_INLINE_VAR static constexpr uint64_t MASK32 = 0xFFFFFFFFu; @@ -954,6 +954,48 @@ struct make_signed> : type_identity> { "Number of bits in Int should be a multiple of 64."); }; +namespace internal { +template struct is_custom_uint : cpp::false_type {}; +template struct is_custom_uint> : cpp::true_type {}; +} // namespace internal + +// bit_cast to UInt +// Note: The standard scheme for SFINAE selection is to have exactly one +// function instanciation valid at a time. This is usually done by having a +// predicate in one function and the negated predicate in the other one. +// e.g. +// template::value == true> ... +// template::value == false> ... +// +// Unfortunately this would make the default 'cpp::bit_cast' aware of +// 'is_custom_uint' (or any other customization). To prevent exposing all +// customizations in the original function, we create a different function with +// four 'typename's instead of three - otherwise it would be considered as a +// redeclaration of the same function leading to "error: template parameter +// redefines default argument". +template ::value && + cpp::is_trivially_copyable::value>, + typename = cpp::enable_if_t::value>> +LIBC_INLINE constexpr To bit_cast(const From &from) { + To out; + using Storage = decltype(out.val); + out.val = cpp::bit_cast(from); + return out; +} + +// bit_cast from UInt +template < + typename To, size_t Bits, + typename = cpp::enable_if_t) && + cpp::is_trivially_constructible::value && + cpp::is_trivially_copyable::value && + cpp::is_trivially_copyable>::value>> +LIBC_INLINE constexpr To bit_cast(const UInt &from) { + return cpp::bit_cast(from.val); +} + } // namespace LIBC_NAMESPACE::cpp #endif // LLVM_LIBC_SRC___SUPPORT_UINT_H diff --git a/libc/test/src/__support/uint_test.cpp b/libc/test/src/__support/uint_test.cpp index 971bac55bd9d3..0ad72c35645c4 100644 --- a/libc/test/src/__support/uint_test.cpp +++ b/libc/test/src/__support/uint_test.cpp @@ -10,19 +10,62 @@ #include "src/__support/UInt.h" #include "test/UnitTest/Test.h" +#include // HUGE_VALF, HUGE_VALF -// We want to test LIBC_NAMESPACE::cpp::UInt<128> explicitly. So, for +namespace LIBC_NAMESPACE { + +using LL_UInt64 = cpp::UInt<64>; +// We want to test cpp::UInt<128> explicitly. So, for // convenience, we use a sugar which does not conflict with the UInt128 type // which can resolve to __uint128_t if the platform has it. -using LL_UInt128 = LIBC_NAMESPACE::cpp::UInt<128>; -using LL_UInt192 = LIBC_NAMESPACE::cpp::UInt<192>; -using LL_UInt256 = LIBC_NAMESPACE::cpp::UInt<256>; -using LL_UInt320 = LIBC_NAMESPACE::cpp::UInt<320>; -using LL_UInt512 = LIBC_NAMESPACE::cpp::UInt<512>; -using LL_UInt1024 = LIBC_NAMESPACE::cpp::UInt<1024>; +using LL_UInt128 = cpp::UInt<128>; +using LL_UInt192 = cpp::UInt<192>; +using LL_UInt256 = cpp::UInt<256>; +using LL_UInt320 = cpp::UInt<320>; +using LL_UInt512 = cpp::UInt<512>; +using LL_UInt1024 = cpp::UInt<1024>; + +using LL_Int128 = cpp::Int<128>; +using LL_Int192 = cpp::Int<192>; + +TEST(LlvmLibcUIntClassTest, BitCastToFromDouble) { + static_assert(cpp::is_trivially_copyable::value); + static_assert(sizeof(LL_UInt64) == sizeof(double)); + const double inf = HUGE_VAL; + const double max = DBL_MAX; + const double array[] = {0.0, 0.1, 1.0, max, inf}; + for (double value : array) { + LL_UInt64 back = cpp::bit_cast(value); + double forth = cpp::bit_cast(back); + EXPECT_TRUE(value == forth); + } +} -using LL_Int128 = LIBC_NAMESPACE::cpp::Int<128>; -using LL_Int192 = LIBC_NAMESPACE::cpp::Int<192>; +#ifdef __SIZEOF_INT128__ +TEST(LlvmLibcUIntClassTest, BitCastToFromNativeUint128) { + static_assert(cpp::is_trivially_copyable::value); + static_assert(sizeof(LL_UInt128) == sizeof(__uint128_t)); + const __uint128_t array[] = {0, 1, ~__uint128_t(0)}; + for (__uint128_t value : array) { + LL_UInt128 back = cpp::bit_cast(value); + __uint128_t forth = cpp::bit_cast<__uint128_t>(back); + EXPECT_TRUE(value == forth); + } +} +#endif + +#ifdef LIBC_COMPILER_HAS_FLOAT128 +TEST(LlvmLibcUIntClassTest, BitCastToFromNativeFloat128) { + static_assert(cpp::is_trivially_copyable::value); + static_assert(sizeof(LL_UInt128) == sizeof(float128)); + const float128 array[] = {0, 0.1, 1}; + for (float128 value : array) { + LL_UInt128 back = cpp::bit_cast(value); + float128 forth = cpp::bit_cast(back); + EXPECT_TRUE(value == forth); + } +} +#endif TEST(LlvmLibcUIntClassTest, BasicInit) { LL_UInt128 half_val(12345); @@ -634,3 +677,5 @@ TEST(LlvmLibcUIntClassTest, ConstructorFromUInt128Tests) { } #endif // __SIZEOF_INT128__ + +} // namespace LIBC_NAMESPACE