Skip to content

Commit 8120cab

Browse files
committed
[libc] Make BigInt bit_cast-able to compatible types
This is a second take on llvm#74837 to fix llvm#74258
1 parent 75193b1 commit 8120cab

File tree

5 files changed

+108
-42
lines changed

5 files changed

+108
-42
lines changed

libc/src/__support/CPP/bit.h

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,27 @@ namespace LIBC_NAMESPACE::cpp {
2525
#define LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
2626
#endif
2727

28+
// Performs a copy from 'src' to 'dst' of 'size' bytes.
29+
// The semantics is valid if 'src' and 'dst' are equal but undefined if the
30+
// regions defined by [src, src + size] and [dst, dst + size] overlap.
31+
template <size_t size, typename DstT, typename SrcT>
32+
LIBC_INLINE constexpr void memcpy_inline(DstT *__restrict dst,
33+
const SrcT *__restrict src) {
34+
#ifdef LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
35+
__builtin_memcpy(dst, src, size);
36+
#else
37+
// `memcpy_inline` is instantiated several times with different value of the
38+
// size parameter. This doesn't play well with GCC's Value Range Analysis that
39+
// wrongly detects out of bounds accesses. We disable the 'array-bounds'
40+
// warning for the purpose of this function.
41+
#pragma GCC diagnostic push
42+
#pragma GCC diagnostic ignored "-Warray-bounds"
43+
for (size_t i = 0; i < size; ++i)
44+
static_cast<char *>(dst)[i] = static_cast<const char *>(src)[i];
45+
#pragma GCC diagnostic pop
46+
#endif
47+
}
48+
2849
// This implementation of bit_cast requires trivially-constructible To, to avoid
2950
// UB in the implementation.
3051
template <
@@ -39,14 +60,7 @@ LIBC_INLINE constexpr To bit_cast(const From &from) {
3960
return __builtin_bit_cast(To, from);
4061
#else
4162
To to;
42-
char *dst = reinterpret_cast<char *>(&to);
43-
const char *src = reinterpret_cast<const char *>(&from);
44-
#if LIBC_HAS_BUILTIN(__builtin_memcpy_inline)
45-
__builtin_memcpy_inline(dst, src, sizeof(To));
46-
#else
47-
for (unsigned i = 0; i < sizeof(To); ++i)
48-
dst[i] = src[i];
49-
#endif // LIBC_HAS_BUILTIN(__builtin_memcpy_inline)
63+
memcpy_inline<sizeof(To)>(&to, &from);
5064
return to;
5165
#endif // LIBC_HAS_BUILTIN(__builtin_bit_cast)
5266
}

libc/src/__support/FPUtil/FPBits.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "src/__support/CPP/bit.h"
1313
#include "src/__support/CPP/type_traits.h"
14+
#include "src/__support/UInt128.h"
1415
#include "src/__support/common.h"
1516
#include "src/__support/macros/attributes.h" // LIBC_INLINE
1617

libc/src/__support/UInt.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,35 @@ struct make_signed<UInt<Bits>> : type_identity<Int<Bits>> {
952952
"Number of bits in Int should be a multiple of 64.");
953953
};
954954

955+
namespace internal {
956+
template <typename T> struct is_custom_uint : cpp::false_type {};
957+
template <size_t Bits> struct is_custom_uint<UInt<Bits>> : cpp::true_type {};
958+
} // namespace internal
959+
960+
// bit_cast to UInt
961+
template <typename To, typename From,
962+
typename = cpp::enable_if_t<internal::is_custom_uint<To>::value>,
963+
typename = cpp::enable_if_t<sizeof(To) == sizeof(From)>,
964+
typename = cpp::enable_if_t<cpp::is_trivially_copyable<From>::value>>
965+
LIBC_INLINE constexpr To bit_cast(const From &from) {
966+
To out;
967+
cpp::memcpy_inline<sizeof(out)>(out.val, &from);
968+
return out;
969+
}
970+
971+
// bit_cast from UInt
972+
template <
973+
typename To, size_t Bits,
974+
typename = cpp::enable_if_t<sizeof(To) == sizeof(UInt<Bits>)>,
975+
typename = cpp::enable_if_t<cpp::is_trivially_constructible<To>::value>,
976+
typename = cpp::enable_if_t<cpp::is_trivially_copyable<To>::value>,
977+
typename = cpp::enable_if_t<cpp::is_trivially_copyable<UInt<Bits>>::value>>
978+
LIBC_INLINE constexpr To bit_cast(const UInt<Bits> &from) {
979+
To out;
980+
cpp::memcpy_inline<sizeof(out)>(&out, from.val);
981+
return out;
982+
}
983+
955984
} // namespace LIBC_NAMESPACE::cpp
956985

957986
#endif // LLVM_LIBC_SRC___SUPPORT_UINT_H

libc/src/string/memory_utils/utils.h

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -71,33 +71,10 @@ LIBC_INLINE bool is_disjoint(const void *p1, const void *p2, size_t size) {
7171
return sdiff >= 0 ? size <= udiff : size <= neg_udiff;
7272
}
7373

74-
#if LIBC_HAS_BUILTIN(__builtin_memcpy_inline)
75-
#define LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
76-
#endif
77-
7874
#if LIBC_HAS_BUILTIN(__builtin_memset_inline)
7975
#define LLVM_LIBC_HAS_BUILTIN_MEMSET_INLINE
8076
#endif
8177

82-
// Performs a constant count copy.
83-
template <size_t Size>
84-
LIBC_INLINE void memcpy_inline(void *__restrict dst,
85-
const void *__restrict src) {
86-
#ifdef LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
87-
__builtin_memcpy_inline(dst, src, Size);
88-
#else
89-
// In memory functions `memcpy_inline` is instantiated several times with
90-
// different value of the Size parameter. This doesn't play well with GCC's
91-
// Value Range Analysis that wrongly detects out of bounds accesses. We
92-
// disable the 'array-bounds' warning for the purpose of this function.
93-
#pragma GCC diagnostic push
94-
#pragma GCC diagnostic ignored "-Warray-bounds"
95-
for (size_t i = 0; i < Size; ++i)
96-
static_cast<char *>(dst)[i] = static_cast<const char *>(src)[i];
97-
#pragma GCC diagnostic pop
98-
#endif
99-
}
100-
10178
using Ptr = cpp::byte *; // Pointer to raw data.
10279
using CPtr = const cpp::byte *; // Const pointer to raw data.
10380

@@ -204,13 +181,13 @@ LIBC_INLINE MemcmpReturnType cmp_neq_uint64_t(uint64_t a, uint64_t b) {
204181
// type.
205182
template <typename T> LIBC_INLINE T load(CPtr ptr) {
206183
T Out;
207-
memcpy_inline<sizeof(T)>(&Out, ptr);
184+
cpp::memcpy_inline<sizeof(T)>(&Out, ptr);
208185
return Out;
209186
}
210187

211188
// Stores a value of type T in memory (possibly unaligned).
212189
template <typename T> LIBC_INLINE void store(Ptr ptr, T value) {
213-
memcpy_inline<sizeof(T)>(ptr, &value);
190+
cpp::memcpy_inline<sizeof(T)>(ptr, &value);
214191
}
215192

216193
// On architectures that do not allow for unaligned access we perform several

libc/test/src/__support/uint_test.cpp

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,62 @@
1010
#include "src/__support/UInt.h"
1111

1212
#include "test/UnitTest/Test.h"
13+
#include <math.h> // HUGE_VALF, HUGE_VALF
1314

14-
// We want to test LIBC_NAMESPACE::cpp::UInt<128> explicitly. So, for
15+
namespace LIBC_NAMESPACE {
16+
17+
using LL_UInt64 = cpp::UInt<64>;
18+
// We want to test cpp::UInt<128> explicitly. So, for
1519
// convenience, we use a sugar which does not conflict with the UInt128 type
1620
// which can resolve to __uint128_t if the platform has it.
17-
using LL_UInt128 = LIBC_NAMESPACE::cpp::UInt<128>;
18-
using LL_UInt192 = LIBC_NAMESPACE::cpp::UInt<192>;
19-
using LL_UInt256 = LIBC_NAMESPACE::cpp::UInt<256>;
20-
using LL_UInt320 = LIBC_NAMESPACE::cpp::UInt<320>;
21-
using LL_UInt512 = LIBC_NAMESPACE::cpp::UInt<512>;
22-
using LL_UInt1024 = LIBC_NAMESPACE::cpp::UInt<1024>;
21+
using LL_UInt128 = cpp::UInt<128>;
22+
using LL_UInt192 = cpp::UInt<192>;
23+
using LL_UInt256 = cpp::UInt<256>;
24+
using LL_UInt320 = cpp::UInt<320>;
25+
using LL_UInt512 = cpp::UInt<512>;
26+
using LL_UInt1024 = cpp::UInt<1024>;
27+
28+
using LL_Int128 = cpp::Int<128>;
29+
using LL_Int192 = cpp::Int<192>;
30+
31+
TEST(LlvmLibcUIntClassTest, BitCastToFromDouble) {
32+
static_assert(cpp::is_trivially_copyable<LL_UInt64>::value);
33+
static_assert(sizeof(LL_UInt64) == sizeof(double));
34+
const double inf = HUGE_VAL;
35+
const double max = DBL_MAX;
36+
const double array[] = {0.0, 0.1, 1.0, max, inf};
37+
for (double value : array) {
38+
LL_UInt64 back = cpp::bit_cast<LL_UInt64>(value);
39+
double forth = cpp::bit_cast<double>(back);
40+
EXPECT_TRUE(value == forth);
41+
}
42+
}
2343

24-
using LL_Int128 = LIBC_NAMESPACE::cpp::Int<128>;
25-
using LL_Int192 = LIBC_NAMESPACE::cpp::Int<192>;
44+
#ifdef __SIZEOF_INT128__
45+
TEST(LlvmLibcUIntClassTest, BitCastToFromNativeUint128) {
46+
static_assert(cpp::is_trivially_copyable<LL_UInt128>::value);
47+
static_assert(sizeof(LL_UInt128) == sizeof(__uint128_t));
48+
const __uint128_t array[] = {0, 1, ~__uint128_t(0)};
49+
for (__uint128_t value : array) {
50+
LL_UInt128 back = cpp::bit_cast<LL_UInt128>(value);
51+
__uint128_t forth = cpp::bit_cast<__uint128_t>(back);
52+
EXPECT_TRUE(value == forth);
53+
}
54+
}
55+
#endif
56+
57+
#ifdef LIBC_COMPILER_HAS_FLOAT128
58+
TEST(LlvmLibcUIntClassTest, BitCastToFromNativeFloat128) {
59+
static_assert(cpp::is_trivially_copyable<LL_UInt128>::value);
60+
static_assert(sizeof(LL_UInt128) == sizeof(float128));
61+
const float128 array[] = {0, 0.1, 1};
62+
for (float128 value : array) {
63+
LL_UInt128 back = cpp::bit_cast<LL_UInt128>(value);
64+
float128 forth = cpp::bit_cast<float128>(back);
65+
EXPECT_TRUE(value == forth);
66+
}
67+
}
68+
#endif
2669

2770
TEST(LlvmLibcUIntClassTest, BasicInit) {
2871
LL_UInt128 half_val(12345);
@@ -634,3 +677,5 @@ TEST(LlvmLibcUIntClassTest, ConstructorFromUInt128Tests) {
634677
}
635678

636679
#endif // __SIZEOF_INT128__
680+
681+
} // namespace LIBC_NAMESPACE

0 commit comments

Comments
 (0)