Skip to content

Commit 31316b3

Browse files
authored
[reland][libc] Make BigInt bit_cast-able to compatible types (#74862)
Fix #74258 This is a reland of #74837, the error went unnoticed because it compiles fine on clang-16 but not on clang-12 which is the version used on the buildbots. The fix was to explicitly initialize `BigInt` variables in `constexpr` operations: `BigInt<Bits, Signed> result(0);` instead of `BigInt<Bits, Signed> result;`
1 parent 49b27b1 commit 31316b3

File tree

5 files changed

+80
-21
lines changed

5 files changed

+80
-21
lines changed

libc/src/__support/UInt.h

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525

2626
namespace LIBC_NAMESPACE::cpp {
2727

28+
// BigInt has the same semantics as the 'standard integer types' and can be
29+
// safely 'bit_cast'ed to compatible types.
2830
template <size_t Bits, bool Signed> struct BigInt {
29-
3031
static_assert(Bits > 0 && Bits % 64 == 0,
3132
"Number of bits in BigInt should be a multiple of 64.");
3233
LIBC_INLINE_VAR static constexpr size_t WORDCOUNT = Bits / 64;
33-
uint64_t val[WORDCOUNT]{};
34+
uint64_t val[WORDCOUNT];
3435

3536
LIBC_INLINE_VAR static constexpr uint64_t MASK32 = 0xFFFFFFFFu;
3637

@@ -158,7 +159,7 @@ template <size_t Bits, bool Signed> struct BigInt {
158159

159160
LIBC_INLINE constexpr BigInt<Bits, Signed>
160161
operator+(const BigInt<Bits, Signed> &other) const {
161-
BigInt<Bits, Signed> result;
162+
BigInt<Bits, Signed> result(0);
162163
SumCarry<uint64_t> s{0, 0};
163164
for (size_t i = 0; i < WORDCOUNT; ++i) {
164165
s = add_with_carry(val[i], other.val[i], s.carry);
@@ -171,7 +172,7 @@ template <size_t Bits, bool Signed> struct BigInt {
171172
// it will always use the constexpr version of add_with_carry.
172173
LIBC_INLINE constexpr BigInt<Bits, Signed>
173174
operator+(BigInt<Bits, Signed> &&other) const {
174-
BigInt<Bits, Signed> result;
175+
BigInt<Bits, Signed> result(0);
175176
SumCarry<uint64_t> s{0, 0};
176177
for (size_t i = 0; i < WORDCOUNT; ++i) {
177178
s = add_with_carry_const(val[i], other.val[i], s.carry);
@@ -199,7 +200,7 @@ template <size_t Bits, bool Signed> struct BigInt {
199200

200201
LIBC_INLINE BigInt<Bits, Signed>
201202
operator-(const BigInt<Bits, Signed> &other) const {
202-
BigInt<Bits, Signed> result;
203+
BigInt<Bits, Signed> result(0);
203204
DiffBorrow<uint64_t> d{0, 0};
204205
for (size_t i = 0; i < WORDCOUNT; ++i) {
205206
d = sub_with_borrow(val[i], other.val[i], d.borrow);
@@ -210,7 +211,7 @@ template <size_t Bits, bool Signed> struct BigInt {
210211

211212
LIBC_INLINE constexpr BigInt<Bits, Signed>
212213
operator-(BigInt<Bits, Signed> &&other) const {
213-
BigInt<Bits, Signed> result;
214+
BigInt<Bits, Signed> result(0);
214215
DiffBorrow<uint64_t> d{0, 0};
215216
for (size_t i = 0; i < WORDCOUNT; ++i) {
216217
d = sub_with_borrow_const(val[i], other.val[i], d.borrow);
@@ -690,7 +691,7 @@ template <size_t Bits, bool Signed> struct BigInt {
690691

691692
LIBC_INLINE constexpr BigInt<Bits, Signed>
692693
operator&(const BigInt<Bits, Signed> &other) const {
693-
BigInt<Bits, Signed> result;
694+
BigInt<Bits, Signed> result(0);
694695
for (size_t i = 0; i < WORDCOUNT; ++i)
695696
result.val[i] = val[i] & other.val[i];
696697
return result;
@@ -705,7 +706,7 @@ template <size_t Bits, bool Signed> struct BigInt {
705706

706707
LIBC_INLINE constexpr BigInt<Bits, Signed>
707708
operator|(const BigInt<Bits, Signed> &other) const {
708-
BigInt<Bits, Signed> result;
709+
BigInt<Bits, Signed> result(0);
709710
for (size_t i = 0; i < WORDCOUNT; ++i)
710711
result.val[i] = val[i] | other.val[i];
711712
return result;
@@ -720,7 +721,7 @@ template <size_t Bits, bool Signed> struct BigInt {
720721

721722
LIBC_INLINE constexpr BigInt<Bits, Signed>
722723
operator^(const BigInt<Bits, Signed> &other) const {
723-
BigInt<Bits, Signed> result;
724+
BigInt<Bits, Signed> result(0);
724725
for (size_t i = 0; i < WORDCOUNT; ++i)
725726
result.val[i] = val[i] ^ other.val[i];
726727
return result;
@@ -734,7 +735,7 @@ template <size_t Bits, bool Signed> struct BigInt {
734735
}
735736

736737
LIBC_INLINE constexpr BigInt<Bits, Signed> operator~() const {
737-
BigInt<Bits, Signed> result;
738+
BigInt<Bits, Signed> result(0);
738739
for (size_t i = 0; i < WORDCOUNT; ++i)
739740
result.val[i] = ~val[i];
740741
return result;

libc/src/__support/float_to_string.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ FloatToString<long double>::get_negative_block(int block_index) {
704704
if (exponent < 0) {
705705
const int32_t idx = -exponent / IDX_SIZE;
706706

707-
cpp::UInt<MID_INT_SIZE> val;
707+
cpp::UInt<MID_INT_SIZE> val(0);
708708
#ifdef LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
709709
// ------------------------------ TABLE MODE -------------------------------
710710
const int32_t SHIFT_CONST = TABLE_SHIFT_CONST;

libc/test/src/__support/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,11 @@ add_libc_test(
8989
SRCS
9090
uint_test.cpp
9191
DEPENDS
92-
libc.src.__support.uint
92+
libc.src.__support.CPP.bit
9393
libc.src.__support.CPP.optional
94+
libc.src.__support.CPP.type_traits
95+
libc.src.__support.macros.properties.float
96+
libc.src.__support.uint
9497
)
9598

9699
add_libc_test(

libc/test/src/__support/uint_test.cpp

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,73 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "src/__support/CPP/bit.h" // bit_cast
910
#include "src/__support/CPP/optional.h"
11+
#include "src/__support/CPP/type_traits.h" // is_trivially_constructible
1012
#include "src/__support/UInt.h"
13+
#include "src/__support/macros/properties/float.h" // LIBC_COMPILER_HAS_FLOAT128
1114

1215
#include "test/UnitTest/Test.h"
1316

14-
// We want to test LIBC_NAMESPACE::cpp::UInt<128> explicitly. So, for
17+
#include <math.h> // HUGE_VALF, HUGE_VALF
18+
19+
namespace LIBC_NAMESPACE {
20+
21+
using LL_UInt64 = cpp::UInt<64>;
22+
// We want to test cpp::UInt<128> explicitly. So, for
1523
// convenience, we use a sugar which does not conflict with the UInt128 type
1624
// 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>;
25+
using LL_UInt128 = cpp::UInt<128>;
26+
using LL_UInt192 = cpp::UInt<192>;
27+
using LL_UInt256 = cpp::UInt<256>;
28+
using LL_UInt320 = cpp::UInt<320>;
29+
using LL_UInt512 = cpp::UInt<512>;
30+
using LL_UInt1024 = cpp::UInt<1024>;
31+
32+
using LL_Int128 = cpp::Int<128>;
33+
using LL_Int192 = cpp::Int<192>;
34+
35+
TEST(LlvmLibcUIntClassTest, BitCastToFromDouble) {
36+
static_assert(cpp::is_trivially_constructible<LL_UInt64>::value);
37+
static_assert(cpp::is_trivially_copyable<LL_UInt64>::value);
38+
static_assert(sizeof(LL_UInt64) == sizeof(double));
39+
const double inf = HUGE_VAL;
40+
const double max = DBL_MAX;
41+
const double array[] = {0.0, 0.1, 1.0, max, inf};
42+
for (double value : array) {
43+
LL_UInt64 back = cpp::bit_cast<LL_UInt64>(value);
44+
double forth = cpp::bit_cast<double>(back);
45+
EXPECT_TRUE(value == forth);
46+
}
47+
}
2348

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

2777
TEST(LlvmLibcUIntClassTest, BasicInit) {
2878
LL_UInt128 half_val(12345);
@@ -634,3 +684,5 @@ TEST(LlvmLibcUIntClassTest, ConstructorFromUInt128Tests) {
634684
}
635685

636686
#endif // __SIZEOF_INT128__
687+
688+
} // namespace LIBC_NAMESPACE

utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ libc_test(
7474
name = "uint_test",
7575
srcs = ["uint_test.cpp"],
7676
deps = [
77+
"//libc:__support_cpp_bit",
7778
"//libc:__support_cpp_optional",
79+
"//libc:__support_cpp_type_traits",
80+
"//libc:__support_macros_properties_float",
7881
"//libc:__support_uint",
7982
],
8083
)

0 commit comments

Comments
 (0)