[libc] Replace type punning with bit_cast

Although type punning is defined for union in C, it is UB in C++.
This patch introduces a bit_cast function to convert between types in a safe way.

This is necessary to get llvm-libc compile with GCC.
This patch is extracted from D119002.

Differential Revision: https://reviews.llvm.org/D119145
This commit is contained in:
Guillaume Chatelet 2022-02-07 15:40:28 +00:00
parent 9be6e40d1a
commit 7e7ecef980
19 changed files with 129 additions and 60 deletions

View File

@ -0,0 +1,48 @@
//===-- Freestanding version of bit_cast -----------------------*- 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_SUPPORT_CPP_BIT_H
#define LLVM_LIBC_SUPPORT_CPP_BIT_H
namespace __llvm_libc {
#if defined __has_builtin
#if __has_builtin(__builtin_bit_cast)
#define LLVM_LIBC_HAS_BUILTIN_BIT_CAST
#endif
#endif
#if defined __has_builtin
#if __has_builtin(__builtin_memcpy_inline)
#define LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
#endif
#endif
// This function guarantees the bitcast to be optimized away by the compiler for
// GCC >= 8 and Clang >= 6.
template <class To, class From> constexpr To bit_cast(const From &from) {
static_assert(sizeof(To) == sizeof(From), "To and From must be of same size");
#if defined(LLVM_LIBC_HAS_BUILTIN_BIT_CAST)
return __builtin_bit_cast(To, from);
#else
To to;
char *dst = reinterpret_cast<char *>(&to);
const char *src = reinterpret_cast<const char *>(&from);
#if defined(LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE)
__builtin_memcpy_inline(dst, src, sizeof(To));
#else
for (unsigned i = 0; i < sizeof(To); ++i)
dst[i] = src[i];
#endif // defined(LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE)
return to;
#endif // defined(LLVM_LIBC_HAS_BUILTIN_BIT_CAST)
}
} // namespace __llvm_libc
#endif // LLVM_LIBC_SUPPORT_CPP_BIT_H

View File

@ -3,9 +3,10 @@ add_header_library(
HDRS HDRS
Array.h Array.h
ArrayRef.h ArrayRef.h
Bit.h
Bitset.h Bitset.h
Functional.h Functional.h
Limits.h
StringView.h StringView.h
TypeTraits.h TypeTraits.h
Limits.h
) )

View File

@ -11,6 +11,7 @@
#include "PlatformDefs.h" #include "PlatformDefs.h"
#include "src/__support/CPP/Bit.h"
#include "src/__support/CPP/TypeTraits.h" #include "src/__support/CPP/TypeTraits.h"
#include "FloatProperties.h" #include "FloatProperties.h"
@ -35,7 +36,7 @@ template <typename T> struct ExponentWidth {
// floating numbers. On x86 platforms however, the 'long double' type maps to // floating numbers. On x86 platforms however, the 'long double' type maps to
// an x87 floating point format. This format is an IEEE 754 extension format. // an x87 floating point format. This format is an IEEE 754 extension format.
// It is handled as an explicit specialization of this class. // It is handled as an explicit specialization of this class.
template <typename T> union FPBits { template <typename T> struct FPBits {
static_assert(cpp::IsFloatingPointType<T>::Value, static_assert(cpp::IsFloatingPointType<T>::Value,
"FPBits instantiated with invalid type."); "FPBits instantiated with invalid type.");
@ -76,7 +77,6 @@ template <typename T> union FPBits {
bool get_sign() const { bool get_sign() const {
return ((bits & FloatProp::SIGN_MASK) >> (FloatProp::BIT_WIDTH - 1)); return ((bits & FloatProp::SIGN_MASK) >> (FloatProp::BIT_WIDTH - 1));
} }
T val;
static_assert(sizeof(T) == sizeof(UIntType), static_assert(sizeof(T) == sizeof(UIntType),
"Data type and integral representation have different sizes."); "Data type and integral representation have different sizes.");
@ -96,15 +96,20 @@ template <typename T> union FPBits {
// type match. // type match.
template <typename XType, template <typename XType,
cpp::EnableIfType<cpp::IsSame<T, XType>::Value, int> = 0> cpp::EnableIfType<cpp::IsSame<T, XType>::Value, int> = 0>
explicit FPBits(XType x) : val(x) {} constexpr explicit FPBits(XType x)
: bits(__llvm_libc::bit_cast<UIntType>(x)) {}
template <typename XType, template <typename XType,
cpp::EnableIfType<cpp::IsSame<XType, UIntType>::Value, int> = 0> cpp::EnableIfType<cpp::IsSame<XType, UIntType>::Value, int> = 0>
explicit FPBits(XType x) : bits(x) {} constexpr explicit FPBits(XType x) : bits(x) {}
FPBits() : bits(0) {} FPBits() : bits(0) {}
explicit operator T() { return val; } T get_val() const { return __llvm_libc::bit_cast<T>(bits); }
void set_val(T value) { bits = __llvm_libc::bit_cast<UIntType>(value); }
explicit operator T() const { return get_val(); }
UIntType uintval() const { return bits; } UIntType uintval() const { return bits; }

View File

@ -12,6 +12,7 @@
#include "BasicOperations.h" #include "BasicOperations.h"
#include "FEnvImpl.h" #include "FEnvImpl.h"
#include "FPBits.h" #include "FPBits.h"
#include "src/__support/CPP/Bit.h"
#include "src/__support/CPP/TypeTraits.h" #include "src/__support/CPP/TypeTraits.h"
namespace __llvm_libc { namespace __llvm_libc {
@ -285,7 +286,7 @@ static inline T hypot(T x, T y) {
} }
y_new |= static_cast<UIntType>(out_exp) << MantissaWidth<T>::VALUE; y_new |= static_cast<UIntType>(out_exp) << MantissaWidth<T>::VALUE;
return *reinterpret_cast<T *>(&y_new); return __llvm_libc::bit_cast<T>(y_new);
} }
} // namespace fputil } // namespace fputil

View File

@ -14,6 +14,7 @@
#include "NormalFloat.h" #include "NormalFloat.h"
#include "PlatformDefs.h" #include "PlatformDefs.h"
#include "src/__support/CPP/Bit.h"
#include "src/__support/CPP/TypeTraits.h" #include "src/__support/CPP/TypeTraits.h"
#include <limits.h> #include <limits.h>
@ -171,7 +172,7 @@ static inline T nextafter(T from, T to) {
int_val = (to_bits.uintval() & sign_mask) + UIntType(1); int_val = (to_bits.uintval() & sign_mask) + UIntType(1);
} }
return *reinterpret_cast<T *>(&int_val); return __llvm_libc::bit_cast<T>(int_val);
// TODO: Raise floating point exceptions as required by the standard. // TODO: Raise floating point exceptions as required by the standard.
} }

View File

@ -10,6 +10,7 @@
#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_GENERIC_SQRT_H #define LLVM_LIBC_SRC_SUPPORT_FPUTIL_GENERIC_SQRT_H
#include "sqrt_80_bit_long_double.h" #include "sqrt_80_bit_long_double.h"
#include "src/__support/CPP/Bit.h"
#include "src/__support/CPP/TypeTraits.h" #include "src/__support/CPP/TypeTraits.h"
#include "src/__support/FPUtil/FEnvImpl.h" #include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/FPBits.h"
@ -203,7 +204,7 @@ sqrt(T x) {
break; break;
} }
return *reinterpret_cast<T *>(&y); return __llvm_libc::bit_cast<T>(y);
} }
} }
} }

View File

@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC_SUPPORT_FPUTIL_X86_64_LONG_DOUBLE_BITS_H #ifndef LLVM_LIBC_SRC_SUPPORT_FPUTIL_X86_64_LONG_DOUBLE_BITS_H
#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_X86_64_LONG_DOUBLE_BITS_H #define LLVM_LIBC_SRC_SUPPORT_FPUTIL_X86_64_LONG_DOUBLE_BITS_H
#include "src/__support/CPP/Bit.h"
#include "src/__support/architectures.h" #include "src/__support/architectures.h"
#if !defined(LLVM_LIBC_ARCH_X86) #if !defined(LLVM_LIBC_ARCH_X86)
@ -30,7 +31,7 @@ template <> struct Padding<4> { static constexpr unsigned VALUE = 16; };
// x86_64 padding. // x86_64 padding.
template <> struct Padding<8> { static constexpr unsigned VALUE = 48; }; template <> struct Padding<8> { static constexpr unsigned VALUE = 48; };
template <> union FPBits<long double> { template <> struct FPBits<long double> {
using UIntType = __uint128_t; using UIntType = __uint128_t;
static constexpr int EXPONENT_BIAS = 0x3FFF; static constexpr int EXPONENT_BIAS = 0x3FFF;
@ -91,13 +92,11 @@ template <> union FPBits<long double> {
return ((bits & FloatProp::SIGN_MASK) >> (FloatProp::BIT_WIDTH - 1)); return ((bits & FloatProp::SIGN_MASK) >> (FloatProp::BIT_WIDTH - 1));
} }
long double val;
FPBits() : bits(0) {} FPBits() : bits(0) {}
template <typename XType, template <typename XType,
cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0> cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0>
explicit FPBits(XType x) : val(x) { explicit FPBits(XType x) : bits(__llvm_libc::bit_cast<UIntType>(x)) {
// bits starts uninitialized, and setting it to a long double only // bits starts uninitialized, and setting it to a long double only
// overwrites the first 80 bits. This clears those upper bits. // overwrites the first 80 bits. This clears those upper bits.
bits = bits & ((UIntType(1) << 80) - 1); bits = bits & ((UIntType(1) << 80) - 1);
@ -107,7 +106,7 @@ template <> union FPBits<long double> {
cpp::EnableIfType<cpp::IsSame<XType, UIntType>::Value, int> = 0> cpp::EnableIfType<cpp::IsSame<XType, UIntType>::Value, int> = 0>
explicit FPBits(XType x) : bits(x) {} explicit FPBits(XType x) : bits(x) {}
operator long double() { return val; } operator long double() { return __llvm_libc::bit_cast<long double>(bits); }
UIntType uintval() { UIntType uintval() {
// We zero the padding bits as they can contain garbage. // We zero the padding bits as they can contain garbage.

View File

@ -15,6 +15,7 @@
#error "Invalid include" #error "Invalid include"
#endif #endif
#include "src/__support/CPP/Bit.h"
#include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/FPBits.h"
#include <stdint.h> #include <stdint.h>
@ -111,7 +112,7 @@ static inline long double nextafter(long double from, long double to) {
} }
} }
return *reinterpret_cast<long double *>(&int_val); return __llvm_libc::bit_cast<long double>(int_val);
// TODO: Raise floating point exceptions as required by the standard. // TODO: Raise floating point exceptions as required by the standard.
} }

View File

@ -33,9 +33,8 @@ template <> inline double sqrt<double>(double x) {
} }
template <> inline long double sqrt<long double>(long double x) { template <> inline long double sqrt<long double>(long double x) {
long double result; __asm__ __volatile__("fsqrt" : "+t"(x));
__asm__ __volatile__("fsqrt" : "=t"(result) : "t"(x)); return x;
return result;
} }
} // namespace fputil } // namespace fputil

View File

@ -155,7 +155,7 @@ LLVM_LIBC_FUNCTION(float, log10f, (float x)) {
return x; return x;
} }
// Normalize denormal inputs. // Normalize denormal inputs.
xbits.val *= 0x1.0p23f; xbits.set_val(xbits.get_val() * 0x1.0p23f);
m -= 23.0; m -= 23.0;
} }
@ -164,7 +164,7 @@ LLVM_LIBC_FUNCTION(float, log10f, (float x)) {
xbits.set_unbiased_exponent(0x7F); xbits.set_unbiased_exponent(0x7F);
int f_index = xbits.get_mantissa() >> 16; int f_index = xbits.get_mantissa() >> 16;
FPBits f(xbits.val); FPBits f = xbits;
f.bits &= ~0x0000'FFFF; f.bits &= ~0x0000'FFFF;
double d = static_cast<float>(xbits) - static_cast<float>(f); double d = static_cast<float>(xbits) - static_cast<float>(f);

View File

@ -59,7 +59,7 @@ INLINE_FMA static inline float log(double x) {
int f_index = int f_index =
xbits.get_mantissa() >> 45; // fputil::MantissaWidth<double>::VALUE - 7 xbits.get_mantissa() >> 45; // fputil::MantissaWidth<double>::VALUE - 7
FPBits f(xbits.val); FPBits f = xbits;
// Clear the lowest 45 bits. // Clear the lowest 45 bits.
f.bits &= ~0x0000'1FFF'FFFF'FFFFULL; f.bits &= ~0x0000'1FFF'FFFF'FFFFULL;

View File

@ -138,7 +138,7 @@ LLVM_LIBC_FUNCTION(float, log2f, (float x)) {
return x; return x;
} }
// Normalize denormal inputs. // Normalize denormal inputs.
xbits.val *= 0x1.0p23f; xbits.set_val(xbits.get_val() * 0x1.0p23f);
m = -23; m = -23;
} }
@ -149,7 +149,7 @@ LLVM_LIBC_FUNCTION(float, log2f, (float x)) {
// lookup tables. // lookup tables.
int f_index = xbits.get_mantissa() >> 16; int f_index = xbits.get_mantissa() >> 16;
FPBits f(xbits.val); FPBits f = xbits;
// Clear the lowest 16 bits. // Clear the lowest 16 bits.
f.bits &= ~0x0000'FFFF; f.bits &= ~0x0000'FFFF;

View File

@ -104,7 +104,7 @@ LLVM_LIBC_FUNCTION(float, logf, (float x)) {
return x; return x;
} }
// Normalize denormal inputs. // Normalize denormal inputs.
xbits.val *= 0x1.0p23f; xbits.set_val(xbits.get_val() * 0x1.0p23f);
m = -23; m = -23;
} }
@ -113,7 +113,7 @@ LLVM_LIBC_FUNCTION(float, logf, (float x)) {
xbits.set_unbiased_exponent(0x7F); xbits.set_unbiased_exponent(0x7F);
int f_index = xbits.get_mantissa() >> 16; int f_index = xbits.get_mantissa() >> 16;
FPBits f(xbits.val); FPBits f = xbits;
f.bits &= ~0x0000'FFFF; f.bits &= ~0x0000'FFFF;
double d = static_cast<float>(xbits) - static_cast<float>(f); double d = static_cast<float>(xbits) - static_cast<float>(f);

View File

@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC_MATH_MATH_UTILS_H #ifndef LLVM_LIBC_SRC_MATH_MATH_UTILS_H
#define LLVM_LIBC_SRC_MATH_MATH_UTILS_H #define LLVM_LIBC_SRC_MATH_MATH_UTILS_H
#include "src/__support/CPP/Bit.h"
#include "src/__support/CPP/TypeTraits.h" #include "src/__support/CPP/TypeTraits.h"
#include "src/__support/common.h" #include "src/__support/common.h"
#include <errno.h> #include <errno.h>
@ -19,19 +20,19 @@
namespace __llvm_libc { namespace __llvm_libc {
static inline uint32_t as_uint32_bits(float x) { static inline uint32_t as_uint32_bits(float x) {
return *reinterpret_cast<uint32_t *>(&x); return __llvm_libc::bit_cast<uint32_t>(x);
} }
static inline uint64_t as_uint64_bits(double x) { static inline uint64_t as_uint64_bits(double x) {
return *reinterpret_cast<uint64_t *>(&x); return __llvm_libc::bit_cast<uint64_t>(x);
} }
static inline float as_float(uint32_t x) { static inline float as_float(uint32_t x) {
return *reinterpret_cast<float *>(&x); return __llvm_libc::bit_cast<float>(x);
} }
static inline double as_double(uint64_t x) { static inline double as_double(uint64_t x) {
return *reinterpret_cast<double *>(&x); return __llvm_libc::bit_cast<double>(x);
} }
static inline uint32_t top12_bits(float x) { return as_uint32_bits(x) >> 20; } static inline uint32_t top12_bits(float x) { return as_uint32_bits(x) >> 20; }

View File

@ -7,6 +7,8 @@ add_header_library(
memcmp_implementations.h memcmp_implementations.h
memcpy_implementations.h memcpy_implementations.h
memset_implementations.h memset_implementations.h
DEPS
standalone_cpp
) )
add_header_library( add_header_library(

View File

@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_X86_H #ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_X86_H
#define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_X86_H #define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ELEMENTS_X86_H
#include "src/__support/CPP/Bit.h"
#include "src/__support/architectures.h" #include "src/__support/architectures.h"
#if defined(LLVM_LIBC_ARCH_X86) #if defined(LLVM_LIBC_ARCH_X86)
@ -66,16 +67,18 @@ struct M128 {
using T = char __attribute__((__vector_size__(SIZE))); using T = char __attribute__((__vector_size__(SIZE)));
static uint16_t mask(T value) { static uint16_t mask(T value) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm_movemask_epi8(value); return _mm_movemask_epi8(__llvm_libc::bit_cast<__m128i>(value));
} }
static uint16_t not_equal_mask(T a, T b) { return mask(a != b); } static uint16_t not_equal_mask(T a, T b) { return mask(a != b); }
static T load(const char *ptr) { static T load(const char *ptr) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm_loadu_si128(reinterpret_cast<__m128i_u const *>(ptr)); return __llvm_libc::bit_cast<T>(
_mm_loadu_si128(reinterpret_cast<__m128i_u const *>(ptr)));
} }
static void store(char *ptr, T value) { static void store(char *ptr, T value) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm_storeu_si128(reinterpret_cast<__m128i_u *>(ptr), value); return _mm_storeu_si128(reinterpret_cast<__m128i_u *>(ptr),
__llvm_libc::bit_cast<__m128i>(value));
} }
static T get_splatted_value(const char v) { static T get_splatted_value(const char v) {
const T splatted = {v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v}; const T splatted = {v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v};
@ -91,16 +94,18 @@ struct M256 {
using T = char __attribute__((__vector_size__(SIZE))); using T = char __attribute__((__vector_size__(SIZE)));
static uint32_t mask(T value) { static uint32_t mask(T value) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm256_movemask_epi8(value); return _mm256_movemask_epi8(__llvm_libc::bit_cast<__m256i>(value));
} }
static uint32_t not_equal_mask(T a, T b) { return mask(a != b); } static uint32_t not_equal_mask(T a, T b) { return mask(a != b); }
static T load(const char *ptr) { static T load(const char *ptr) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm256_loadu_si256(reinterpret_cast<__m256i const *>(ptr)); return __llvm_libc::bit_cast<T>(
_mm256_loadu_si256(reinterpret_cast<__m256i const *>(ptr)));
} }
static void store(char *ptr, T value) { static void store(char *ptr, T value) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), value); return _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr),
__llvm_libc::bit_cast<__m256i>(value));
} }
static T get_splatted_value(const char v) { static T get_splatted_value(const char v) {
const T splatted = {v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, const T splatted = {v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v,
@ -117,15 +122,16 @@ struct M512 {
using T = char __attribute__((__vector_size__(SIZE))); using T = char __attribute__((__vector_size__(SIZE)));
static uint64_t not_equal_mask(T a, T b) { static uint64_t not_equal_mask(T a, T b) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm512_cmpneq_epi8_mask(a, b); return _mm512_cmpneq_epi8_mask(__llvm_libc::bit_cast<__m512i>(a),
__llvm_libc::bit_cast<__m512i>(b));
} }
static T load(const char *ptr) { static T load(const char *ptr) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm512_loadu_epi8(ptr); return __llvm_libc::bit_cast<T>(_mm512_loadu_epi8(ptr));
} }
static void store(char *ptr, T value) { static void store(char *ptr, T value) {
// NOLINTNEXTLINE(llvmlibc-callee-namespace) // NOLINTNEXTLINE(llvmlibc-callee-namespace)
return _mm512_storeu_epi8(ptr, value); return _mm512_storeu_epi8(ptr, __llvm_libc::bit_cast<__m512i>(value));
} }
static T get_splatted_value(const char v) { static T get_splatted_value(const char v) {
const T splatted = {v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, const T splatted = {v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v,

View File

@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_TEST_SRC_MATH_NEXTAFTERTEST_H #ifndef LLVM_LIBC_TEST_SRC_MATH_NEXTAFTERTEST_H
#define LLVM_LIBC_TEST_SRC_MATH_NEXTAFTERTEST_H #define LLVM_LIBC_TEST_SRC_MATH_NEXTAFTERTEST_H
#include "src/__support/CPP/Bit.h"
#include "src/__support/CPP/TypeTraits.h" #include "src/__support/CPP/TypeTraits.h"
#include "src/__support/FPUtil/BasicOperations.h" #include "src/__support/FPUtil/BasicOperations.h"
#include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/FPBits.h"
@ -51,54 +52,54 @@ public:
T x = zero; T x = zero;
T result = func(x, T(1)); T result = func(x, T(1));
UIntType expected_bits = 1; UIntType expected_bits = 1;
T expected = *reinterpret_cast<T *>(&expected_bits); T expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
result = func(x, T(-1)); result = func(x, T(-1));
expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1; expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
x = neg_zero; x = neg_zero;
result = func(x, 1); result = func(x, 1);
expected_bits = 1; expected_bits = 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
result = func(x, -1); result = func(x, -1);
expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1; expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
// 'from' is max subnormal value. // 'from' is max subnormal value.
x = *reinterpret_cast<const T *>(&max_subnormal); x = __llvm_libc::bit_cast<T>(max_subnormal);
result = func(x, 1); result = func(x, 1);
expected = *reinterpret_cast<const T *>(&min_normal); expected = __llvm_libc::bit_cast<T>(min_normal);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
result = func(x, 0); result = func(x, 0);
expected_bits = max_subnormal - 1; expected_bits = max_subnormal - 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
x = -x; x = -x;
result = func(x, -1); result = func(x, -1);
expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_normal; expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_normal;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
result = func(x, 0); result = func(x, 0);
expected_bits = expected_bits =
(UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_subnormal - 1; (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_subnormal - 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
// 'from' is min subnormal value. // 'from' is min subnormal value.
x = *reinterpret_cast<const T *>(&min_subnormal); x = __llvm_libc::bit_cast<T>(min_subnormal);
result = func(x, 1); result = func(x, 1);
expected_bits = min_subnormal + 1; expected_bits = min_subnormal + 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
ASSERT_FP_EQ(func(x, 0), 0); ASSERT_FP_EQ(func(x, 0), 0);
@ -106,35 +107,35 @@ public:
result = func(x, -1); result = func(x, -1);
expected_bits = expected_bits =
(UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_subnormal + 1; (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_subnormal + 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
ASSERT_FP_EQ(func(x, 0), T(-0.0)); ASSERT_FP_EQ(func(x, 0), T(-0.0));
// 'from' is min normal. // 'from' is min normal.
x = *reinterpret_cast<const T *>(&min_normal); x = __llvm_libc::bit_cast<T>(min_normal);
result = func(x, 0); result = func(x, 0);
expected_bits = max_subnormal; expected_bits = max_subnormal;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
result = func(x, inf); result = func(x, inf);
expected_bits = min_normal + 1; expected_bits = min_normal + 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
x = -x; x = -x;
result = func(x, 0); result = func(x, 0);
expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_subnormal; expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_subnormal;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
result = func(x, -inf); result = func(x, -inf);
expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_normal + 1; expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_normal + 1;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
// 'from' is max normal and 'to' is infinity. // 'from' is max normal and 'to' is infinity.
x = *reinterpret_cast<const T *>(&max_normal); x = __llvm_libc::bit_cast<T>(max_normal);
result = func(x, inf); result = func(x, inf);
ASSERT_FP_EQ(result, inf); ASSERT_FP_EQ(result, inf);
@ -145,14 +146,14 @@ public:
x = inf; x = inf;
result = func(x, 0); result = func(x, 0);
expected_bits = max_normal; expected_bits = max_normal;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
ASSERT_FP_EQ(func(x, inf), inf); ASSERT_FP_EQ(func(x, inf), inf);
x = neg_inf; x = neg_inf;
result = func(x, 0); result = func(x, 0);
expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_normal; expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_normal;
expected = *reinterpret_cast<T *>(&expected_bits); expected = __llvm_libc::bit_cast<T>(expected_bits);
ASSERT_FP_EQ(result, expected); ASSERT_FP_EQ(result, expected);
ASSERT_FP_EQ(func(x, neg_inf), neg_inf); ASSERT_FP_EQ(func(x, neg_inf), neg_inf);

View File

@ -6,6 +6,7 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "src/__support/CPP/Bit.h"
#include "utils/MPFRWrapper/MPFRUtils.h" #include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/FPMatcher.h" #include "utils/UnitTest/FPMatcher.h"
#include "utils/UnitTest/Test.h" #include "utils/UnitTest/Test.h"
@ -47,7 +48,7 @@ public:
constexpr UIntType COUNT = 1'000'001; constexpr UIntType COUNT = 1'000'001;
constexpr UIntType STEP = HIDDEN_BIT / COUNT; constexpr UIntType STEP = HIDDEN_BIT / COUNT;
for (UIntType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { for (UIntType i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
T x = *reinterpret_cast<T *>(&v); T x = __llvm_libc::bit_cast<T>(v);
test_all_rounding_modes(func, x); test_all_rounding_modes(func, x);
} }
} }
@ -56,7 +57,7 @@ public:
constexpr UIntType COUNT = 10'000'001; constexpr UIntType COUNT = 10'000'001;
constexpr UIntType STEP = UIntType(-1) / COUNT; constexpr UIntType STEP = UIntType(-1) / COUNT;
for (UIntType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { for (UIntType i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
T x = *reinterpret_cast<T *>(&v); T x = __llvm_libc::bit_cast<T>(v);
if (isnan(x) || (x < 0)) { if (isnan(x) || (x < 0)) {
continue; continue;
} }

View File

@ -39,6 +39,7 @@ cc_library(
hdrs = [ hdrs = [
"src/__support/CPP/Array.h", "src/__support/CPP/Array.h",
"src/__support/CPP/ArrayRef.h", "src/__support/CPP/ArrayRef.h",
"src/__support/CPP/Bit.h",
"src/__support/CPP/Bitset.h", "src/__support/CPP/Bitset.h",
"src/__support/CPP/Functional.h", "src/__support/CPP/Functional.h",
"src/__support/CPP/Limits.h", "src/__support/CPP/Limits.h",
@ -661,6 +662,7 @@ cc_library(
], ],
deps = [ deps = [
":__support_common", ":__support_common",
":__support_standalone_cpp",
":libc_root", ":libc_root",
], ],
) )