[libc] [llvm] Revert "[libc] Remove UB specializations of type traits for `BigInt`" (PR #84297)

Guillaume Chatelet via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 7 02:06:48 PST 2024


https://github.com/gchatelet created https://github.com/llvm/llvm-project/pull/84297

Reverts llvm/llvm-project#84035

Several bots are failing:
 - https://lab.llvm.org/buildbot/#/builders/223/builds/37522
 - https://lab.llvm.org/buildbot/#/builders/162/builds/51978
 - https://lab.llvm.org/buildbot/#/builders/163/builds/52560
 - https://lab.llvm.org/buildbot/#/builders/250/builds/19619

>From f3ca6565d507bdcd9db5a363d627f6b69cb6f277 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <chatelet.guillaume at gmail.com>
Date: Thu, 7 Mar 2024 11:05:26 +0100
Subject: [PATCH] Revert "[libc] Remove UB specializations of type traits for
 `BigInt` (#84035)"

This reverts commit 84f483dbeeba5ecadbf3e4a75bfb71525a3fa332.
---
 libc/src/__support/CMakeLists.txt             |   1 -
 libc/src/__support/CPP/bit.h                  | 112 ++++------
 libc/src/__support/UInt.h                     | 192 ++++++------------
 libc/src/__support/float_to_string.h          |   2 +-
 libc/src/__support/integer_to_string.h        |  19 +-
 libc/test/UnitTest/CMakeLists.txt             |   1 -
 libc/test/UnitTest/LibcTest.cpp               |  10 +-
 libc/test/UnitTest/LibcTest.h                 |   1 -
 libc/test/UnitTest/TestLogger.cpp             |   8 +-
 libc/test/src/__support/CPP/bit_test.cpp      |  49 ++---
 .../llvm-project-overlay/libc/BUILD.bazel     |   1 -
 .../libc/test/UnitTest/BUILD.bazel            |   1 -
 12 files changed, 125 insertions(+), 272 deletions(-)

diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 17c04aa57e6fd6..1a4b3e9a2145c0 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -95,7 +95,6 @@ add_header_library(
   HDRS
     integer_to_string.h
   DEPENDS
-    .uint
     libc.src.__support.common
     libc.src.__support.CPP.algorithm
     libc.src.__support.CPP.limits
diff --git a/libc/src/__support/CPP/bit.h b/libc/src/__support/CPP/bit.h
index bc2f595845a95f..7d11e7d5c497e0 100644
--- a/libc/src/__support/CPP/bit.h
+++ b/libc/src/__support/CPP/bit.h
@@ -27,14 +27,13 @@ namespace LIBC_NAMESPACE::cpp {
 
 // This implementation of bit_cast requires trivially-constructible To, to avoid
 // UB in the implementation.
-template <typename To, typename From>
-LIBC_INLINE constexpr cpp::enable_if_t<
-    (sizeof(To) == sizeof(From)) &&
-        cpp::is_trivially_constructible<To>::value &&
-        cpp::is_trivially_copyable<To>::value &&
-        cpp::is_trivially_copyable<From>::value,
-    To>
-bit_cast(const From &from) {
+template <
+    typename To, typename From,
+    typename = cpp::enable_if_t<sizeof(To) == sizeof(From) &&
+                                cpp::is_trivially_constructible<To>::value &&
+                                cpp::is_trivially_copyable<To>::value &&
+                                cpp::is_trivially_copyable<From>::value>>
+LIBC_INLINE constexpr To bit_cast(const From &from) {
   MSAN_UNPOISON(&from, sizeof(From));
 #if LIBC_HAS_BUILTIN(__builtin_bit_cast)
   return __builtin_bit_cast(To, from);
@@ -52,10 +51,8 @@ bit_cast(const From &from) {
 #endif // LIBC_HAS_BUILTIN(__builtin_bit_cast)
 }
 
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>,
-                                                     bool>
-has_single_bit(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr bool has_single_bit(T value) {
   return (value != 0) && ((value & (value - 1)) == 0);
 }
 
@@ -73,9 +70,8 @@ has_single_bit(T value) {
 /// Only unsigned integral types are allowed.
 ///
 /// Returns cpp::numeric_limits<T>::digits on an input of 0.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-countr_zero(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int countr_zero(T value) {
   if (!value)
     return cpp::numeric_limits<T>::digits;
   if (value & 0x1)
@@ -107,9 +103,8 @@ ADD_SPECIALIZATION(countr_zero, unsigned long long, __builtin_ctzll)
 /// Only unsigned integral types are allowed.
 ///
 /// Returns cpp::numeric_limits<T>::digits on an input of 0.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-countl_zero(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int countl_zero(T value) {
   if (!value)
     return cpp::numeric_limits<T>::digits;
   // Bisection method.
@@ -140,9 +135,8 @@ ADD_SPECIALIZATION(countl_zero, unsigned long long, __builtin_clzll)
 /// Only unsigned integral types are allowed.
 ///
 /// Returns cpp::numeric_limits<T>::digits on an input of all ones.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-countl_one(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int countl_one(T value) {
   return cpp::countl_zero<T>(~value);
 }
 
@@ -153,9 +147,8 @@ countl_one(T value) {
 /// Only unsigned integral types are allowed.
 ///
 /// Returns cpp::numeric_limits<T>::digits on an input of all ones.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-countr_one(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int countr_one(T value) {
   return cpp::countr_zero<T>(~value);
 }
 
@@ -163,9 +156,8 @@ countr_one(T value) {
 /// Returns 0 otherwise.
 ///
 /// Ex. bit_width(5) == 3.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-bit_width(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int bit_width(T value) {
   return cpp::numeric_limits<T>::digits - cpp::countl_zero(value);
 }
 
@@ -173,9 +165,8 @@ bit_width(T value) {
 /// nonzero.  Returns 0 otherwise.
 ///
 /// Ex. bit_floor(5) == 4.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-bit_floor(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr T bit_floor(T value) {
   if (!value)
     return 0;
   return T(1) << (cpp::bit_width(value) - 1);
@@ -188,9 +179,8 @@ bit_floor(T value) {
 ///
 /// The return value is undefined if the input is larger than the largest power
 /// of two representable in T.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-bit_ceil(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr T bit_ceil(T value) {
   if (value < 2)
     return 1;
   return T(1) << cpp::bit_width<T>(value - 1u);
@@ -200,31 +190,28 @@ bit_ceil(T value) {
 // from https://blog.regehr.org/archives/1063.
 
 // Forward-declare rotr so that rotl can use it.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-rotr(T value, int rotate);
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate);
 
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-rotl(T value, int rotate) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr T rotl(T value, int rotate) {
   constexpr unsigned N = cpp::numeric_limits<T>::digits;
   rotate = rotate % N;
   if (!rotate)
     return value;
   if (rotate < 0)
-    return cpp::rotr<T>(value, -rotate);
+    return cpp::rotr(value, -rotate);
   return (value << rotate) | (value >> (N - rotate));
 }
 
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-rotr(T value, int rotate) {
+template <typename T, typename>
+[[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate) {
   constexpr unsigned N = cpp::numeric_limits<T>::digits;
   rotate = rotate % N;
   if (!rotate)
     return value;
   if (rotate < 0)
-    return cpp::rotl<T>(value, -rotate);
+    return cpp::rotl(value, -rotate);
   return (value >> rotate) | (value << (N - rotate));
 }
 
@@ -239,44 +226,33 @@ LIBC_INLINE constexpr To bit_or_static_cast(const From &from) {
   }
 }
 
-// TODO: remove from 'bit.h' as it is not a standard function.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-first_leading_zero(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int first_leading_zero(T value) {
   return value == cpp::numeric_limits<T>::max() ? 0 : countl_one(value) + 1;
 }
 
-// TODO: remove from 'bit.h' as it is not a standard function.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-first_leading_one(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int first_leading_one(T value) {
   return first_leading_zero(static_cast<T>(~value));
 }
 
-// TODO: remove from 'bit.h' as it is not a standard function.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-first_trailing_zero(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int first_trailing_zero(T value) {
   return value == cpp::numeric_limits<T>::max()
              ? 0
              : countr_zero(static_cast<T>(~value)) + 1;
 }
 
-// TODO: remove from 'bit.h' as it is not a standard function.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-first_trailing_one(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int first_trailing_one(T value) {
   return value == cpp::numeric_limits<T>::max() ? 0 : countr_zero(value) + 1;
 }
 
 /// Count number of 1's aka population count or hamming weight.
 ///
 /// Only unsigned integral types are allowed.
-// TODO: rename as 'popcount' to follow the standard
-// https://en.cppreference.com/w/cpp/numeric/popcount
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-count_ones(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int count_ones(T value) {
   int count = 0;
   for (int i = 0; i != cpp::numeric_limits<T>::digits; ++i)
     if ((value >> i) & 0x1)
@@ -296,10 +272,8 @@ ADD_SPECIALIZATION(unsigned long long, __builtin_popcountll)
 // TODO: 128b specializations?
 #undef ADD_SPECIALIZATION
 
-// TODO: remove from 'bit.h' as it is not a standard function.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
-count_zeros(T value) {
+template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
+[[nodiscard]] LIBC_INLINE constexpr int count_zeros(T value) {
   return count_ones<T>(static_cast<T>(~value));
 }
 
diff --git a/libc/src/__support/UInt.h b/libc/src/__support/UInt.h
index b3d8f00b9a01a5..5973e6fab1d7d5 100644
--- a/libc/src/__support/UInt.h
+++ b/libc/src/__support/UInt.h
@@ -43,9 +43,6 @@ struct BigInt {
   static_assert(is_integral_v<WordType> && is_unsigned_v<WordType>,
                 "WordType must be unsigned integer.");
 
-  using word_type = WordType;
-  LIBC_INLINE_VAR static constexpr bool SIGNED = Signed;
-  LIBC_INLINE_VAR static constexpr size_t BITS = Bits;
   LIBC_INLINE_VAR
   static constexpr size_t WORD_SIZE = sizeof(WordType) * CHAR_BIT;
 
@@ -53,10 +50,6 @@ struct BigInt {
                 "Number of bits in BigInt should be a multiple of WORD_SIZE.");
 
   LIBC_INLINE_VAR static constexpr size_t WORD_COUNT = Bits / WORD_SIZE;
-
-  using unsigned_type = BigInt<BITS, false, word_type>;
-  using signed_type = BigInt<BITS, true, word_type>;
-
   cpp::array<WordType, WORD_COUNT> val{};
 
   LIBC_INLINE constexpr BigInt() = default;
@@ -586,33 +579,19 @@ struct BigInt {
     return *this;
   }
 
-  // TODO: remove and use cpp::countl_zero below.
-  [[nodiscard]] LIBC_INLINE constexpr int clz() const {
-    constexpr int word_digits = cpp::numeric_limits<word_type>::digits;
-    int leading_zeroes = 0;
-    for (auto i = val.size(); i > 0;) {
-      --i;
-      const int zeroes = countl_zero(val[i]);
-      leading_zeroes += zeroes;
-      if (zeroes != word_digits)
+  LIBC_INLINE constexpr uint64_t clz() {
+    uint64_t leading_zeroes = 0;
+    for (size_t i = WORD_COUNT; i > 0; --i) {
+      if (val[i - 1] == 0) {
+        leading_zeroes += WORD_SIZE;
+      } else {
+        leading_zeroes += countl_zero(val[i - 1]);
         break;
+      }
     }
     return leading_zeroes;
   }
 
-  // TODO: remove and use cpp::countr_zero below.
-  [[nodiscard]] LIBC_INLINE constexpr int ctz() const {
-    constexpr int word_digits = cpp::numeric_limits<word_type>::digits;
-    int trailing_zeroes = 0;
-    for (auto word : val) {
-      const int zeroes = countr_zero(word);
-      trailing_zeroes += zeroes;
-      if (zeroes != word_digits)
-        break;
-    }
-    return trailing_zeroes;
-  }
-
   LIBC_INLINE constexpr void shift_left(size_t s) {
     if constexpr (Bits == WORD_SIZE) {
       // Use native types if possible.
@@ -937,121 +916,64 @@ template <> class numeric_limits<Int<128>> {
   LIBC_INLINE_VAR static constexpr int digits = 128;
 };
 
-// type traits to determine whether a T is a cpp::BigInt.
-template <typename T> struct is_big_int : cpp::false_type {};
-
+// Provides is_integral of U/Int<128>, U/Int<192>, U/Int<256>.
 template <size_t Bits, bool Signed, typename T>
-struct is_big_int<BigInt<Bits, Signed, T>> : cpp::true_type {};
-
-template <class T>
-LIBC_INLINE_VAR constexpr bool is_big_int_v = is_big_int<T>::value;
-
-// Specialization of cpp::bit_cast ('bit.h') from T to BigInt.
-template <typename To, typename From>
-LIBC_INLINE constexpr cpp::enable_if_t<
-    (sizeof(To) == sizeof(From)) && cpp::is_trivially_copyable<To>::value &&
-        cpp::is_trivially_copyable<From>::value && is_big_int<To>::value,
-    To>
-bit_cast(const From &from) {
-  To out;
-  using Storage = decltype(out.val);
-  out.val = cpp::bit_cast<Storage>(from);
-  return out;
-}
+struct is_integral<BigInt<Bits, Signed, T>> : cpp::true_type {};
 
-// Specialization of cpp::bit_cast ('bit.h') from BigInt to T.
-template <typename To, size_t Bits>
-LIBC_INLINE constexpr cpp::enable_if_t<
-    sizeof(To) == sizeof(UInt<Bits>) &&
-        cpp::is_trivially_constructible<To>::value &&
-        cpp::is_trivially_copyable<To>::value &&
-        cpp::is_trivially_copyable<UInt<Bits>>::value,
-    To>
-bit_cast(const UInt<Bits> &from) {
-  return cpp::bit_cast<To>(from.val);
-}
-
-// Specialization of cpp::has_single_bit ('bit.h') for BigInt.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, bool>
-has_single_bit(T value) {
-  int bits = 0;
-  for (auto word : value.val) {
-    if (word == 0)
-      continue;
-    bits += count_ones(word);
-    if (bits > 1)
-      return false;
-  }
-  return bits == 1;
-}
-
-// Specialization of cpp::countr_zero ('bit.h') for BigInt.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
-countr_zero(const T &value) {
-  return value.ctz();
-}
+// Provides is_unsigned of UInt<128>, UInt<192>, UInt<256>.
+template <size_t Bits, bool Signed, typename T>
+struct is_unsigned<BigInt<Bits, Signed, T>> : cpp::bool_constant<!Signed> {};
 
-// Specialization of cpp::countl_zero ('bit.h') for BigInt.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
-countl_zero(const T &value) {
-  return value.clz();
-}
+template <size_t Bits, bool Signed, typename T>
+struct make_unsigned<BigInt<Bits, Signed, T>>
+    : type_identity<BigInt<Bits, false, T>> {};
 
-// Specialization of cpp::countl_one ('bit.h') for BigInt.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
-countl_one(T value) {
-  // TODO : Implement a faster version not involving operator~.
-  return cpp::countl_zero<T>(~value);
-}
+template <size_t Bits, bool Signed, typename T>
+struct make_signed<BigInt<Bits, Signed, T>>
+    : type_identity<BigInt<Bits, true, T>> {};
 
-// Specialization of cpp::countr_one ('bit.h') for BigInt.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
-countr_one(T value) {
-  // TODO : Implement a faster version not involving operator~.
-  return cpp::countr_zero<T>(~value);
-}
+namespace internal {
+template <typename T> struct is_custom_uint : cpp::false_type {};
 
-// Specialization of cpp::bit_width ('bit.h') for BigInt.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
-bit_width(T value) {
-  return cpp::numeric_limits<T>::digits - cpp::countl_zero(value);
-}
+template <size_t Bits, bool Signed, typename T>
+struct is_custom_uint<BigInt<Bits, Signed, T>> : cpp::true_type {};
+} // namespace internal
 
-// Forward-declare rotr so that rotl can use it.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, T>
-rotr(T value, int rotate);
-
-// Specialization of cpp::rotl ('bit.h') for BigInt.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, T>
-rotl(T value, int rotate) {
-  constexpr unsigned N = cpp::numeric_limits<T>::digits;
-  rotate = rotate % N;
-  if (!rotate)
-    return value;
-  if (rotate < 0)
-    return cpp::rotr<T>(value, -rotate);
-  return (value << rotate) | (value >> (N - rotate));
+// 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<typename = cpp::enable_if_t< is_custom_uint<To>::value == true> ...
+// template<typename = cpp::enable_if_t< is_custom_uint<To>::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 <typename To, typename From,
+          typename = cpp::enable_if_t<sizeof(To) == sizeof(From) &&
+                                      cpp::is_trivially_copyable<To>::value &&
+                                      cpp::is_trivially_copyable<From>::value>,
+          typename = cpp::enable_if_t<internal::is_custom_uint<To>::value>>
+LIBC_INLINE constexpr To bit_cast(const From &from) {
+  To out;
+  using Storage = decltype(out.val);
+  out.val = cpp::bit_cast<Storage>(from);
+  return out;
 }
 
-// Specialization of cpp::rotr ('bit.h') for BigInt.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, T>
-rotr(T value, int rotate) {
-  constexpr unsigned N = cpp::numeric_limits<T>::digits;
-  rotate = rotate % N;
-  if (!rotate)
-    return value;
-  if (rotate < 0)
-    return cpp::rotl<T>(value, -rotate);
-  return (value >> rotate) | (value << (N - rotate));
+// bit_cast from UInt
+template <
+    typename To, size_t Bits,
+    typename = cpp::enable_if_t<sizeof(To) == sizeof(UInt<Bits>) &&
+                                cpp::is_trivially_constructible<To>::value &&
+                                cpp::is_trivially_copyable<To>::value &&
+                                cpp::is_trivially_copyable<UInt<Bits>>::value>>
+LIBC_INLINE constexpr To bit_cast(const UInt<Bits> &from) {
+  return cpp::bit_cast<To>(from.val);
 }
 
 } // namespace LIBC_NAMESPACE::cpp
diff --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h
index 27476433a94575..744842ced8d772 100644
--- a/libc/src/__support/float_to_string.h
+++ b/libc/src/__support/float_to_string.h
@@ -713,7 +713,7 @@ template <> class FloatToString<long double> {
       float_as_fixed.shift_left(SHIFT_AMOUNT);
 
       // If there are still digits above the decimal point, handle those.
-      if (float_as_fixed.clz() < static_cast<int>(EXTRA_INT_WIDTH)) {
+      if (float_as_fixed.clz() < EXTRA_INT_WIDTH) {
         cpp::UInt<EXTRA_INT_WIDTH> above_decimal_point =
             float_as_fixed >> FLOAT_AS_INT_WIDTH;
 
diff --git a/libc/src/__support/integer_to_string.h b/libc/src/__support/integer_to_string.h
index a5872dce652036..81ed21ccfca166 100644
--- a/libc/src/__support/integer_to_string.h
+++ b/libc/src/__support/integer_to_string.h
@@ -67,7 +67,6 @@
 #include "src/__support/CPP/span.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/type_traits.h"
-#include "src/__support/UInt.h" // is_big_int
 #include "src/__support/common.h"
 
 namespace LIBC_NAMESPACE {
@@ -150,18 +149,6 @@ template <bool forward> class StringBufferWriterImpl {
 using StringBufferWriter = StringBufferWriterImpl<true>;
 using BackwardStringBufferWriter = StringBufferWriterImpl<false>;
 
-template <typename T, class = void> struct IntegerWriterUnsigned {};
-
-template <typename T>
-struct IntegerWriterUnsigned<T, cpp::enable_if_t<cpp::is_integral_v<T>>> {
-  using type = cpp::make_unsigned_t<T>;
-};
-
-template <typename T>
-struct IntegerWriterUnsigned<T, cpp::enable_if_t<cpp::is_big_int_v<T>>> {
-  using type = typename T::unsigned_type;
-};
-
 } // namespace details
 
 namespace radix {
@@ -176,7 +163,7 @@ template <size_t radix> using Custom = details::Fmt<radix>;
 
 // See file header for documentation.
 template <typename T, typename Fmt = radix::Dec> class IntegerToString {
-  static_assert(cpp::is_integral_v<T> || cpp::is_big_int_v<T>);
+  static_assert(cpp::is_integral_v<T>);
 
   LIBC_INLINE static constexpr size_t compute_buffer_size() {
     constexpr auto MAX_DIGITS = []() -> size_t {
@@ -221,8 +208,8 @@ template <typename T, typename Fmt = radix::Dec> class IntegerToString {
 
   // An internal stateless structure that handles the number formatting logic.
   struct IntegerWriter {
-    static_assert(cpp::is_integral_v<T> || cpp::is_big_int_v<T>);
-    using UNSIGNED_T = typename details::IntegerWriterUnsigned<T>::type;
+    static_assert(cpp::is_integral_v<T>);
+    using UNSIGNED_T = cpp::make_unsigned_t<T>;
 
     LIBC_INLINE static char digit_char(uint8_t digit) {
       if (digit < 10)
diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt
index 36837c553efce1..4668f0061975f8 100644
--- a/libc/test/UnitTest/CMakeLists.txt
+++ b/libc/test/UnitTest/CMakeLists.txt
@@ -74,7 +74,6 @@ add_unittest_framework_library(
     libc.src.__support.CPP.type_traits
     libc.src.__support.fixed_point.fx_rep
     libc.src.__support.OSUtil.osutil
-    libc.src.__support.uint
     libc.src.__support.uint128
 )
 
diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp
index 0340f7ed37100e..7b0e4fca83683b 100644
--- a/libc/test/UnitTest/LibcTest.cpp
+++ b/libc/test/UnitTest/LibcTest.cpp
@@ -38,8 +38,7 @@ TestLogger &operator<<(TestLogger &logger, Location Loc) {
 // When the value is UInt128, __uint128_t or wider, show its hexadecimal
 // digits.
 template <typename T>
-cpp::enable_if_t<(cpp::is_integral_v<T> && (sizeof(T) > sizeof(uint64_t))) ||
-                     cpp::is_big_int_v<T>,
+cpp::enable_if_t<cpp::is_integral_v<T> && (sizeof(T) > sizeof(uint64_t)),
                  cpp::string>
 describeValue(T Value) {
   static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
@@ -48,10 +47,11 @@ describeValue(T Value) {
 }
 
 // When the value is of a standard integral type, just display it as normal.
-template <typename T>
-cpp::enable_if_t<cpp::is_integral_v<T> && (sizeof(T) <= sizeof(uint64_t)),
+template <typename ValType>
+cpp::enable_if_t<cpp::is_integral_v<ValType> &&
+                     sizeof(ValType) <= sizeof(uint64_t),
                  cpp::string>
-describeValue(T Value) {
+describeValue(ValType Value) {
   return cpp::to_string(Value);
 }
 
diff --git a/libc/test/UnitTest/LibcTest.h b/libc/test/UnitTest/LibcTest.h
index d26d6490bcb572..639f6005832576 100644
--- a/libc/test/UnitTest/LibcTest.h
+++ b/libc/test/UnitTest/LibcTest.h
@@ -127,7 +127,6 @@ class Test {
   // of type promotion.
   template <typename ValType,
             cpp::enable_if_t<cpp::is_integral_v<ValType> ||
-                                 cpp::is_big_int_v<ValType> ||
                                  cpp::is_fixed_point_v<ValType>,
                              int> = 0>
   bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
diff --git a/libc/test/UnitTest/TestLogger.cpp b/libc/test/UnitTest/TestLogger.cpp
index 469b3a11d57d9b..6bb0e17dc3888e 100644
--- a/libc/test/UnitTest/TestLogger.cpp
+++ b/libc/test/UnitTest/TestLogger.cpp
@@ -2,7 +2,6 @@
 #include "src/__support/CPP/string.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/OSUtil/io.h" // write_to_stderr
-#include "src/__support/UInt.h"      // is_big_int
 #include "src/__support/UInt128.h"
 
 #include <stdint.h>
@@ -48,9 +47,8 @@ template <> TestLogger &TestLogger::operator<<(void *addr) {
 }
 
 template <typename T> TestLogger &TestLogger::operator<<(T t) {
-  if constexpr (cpp::is_big_int_v<T> ||
-                (cpp::is_integral_v<T> && cpp::is_unsigned_v<T> &&
-                 (sizeof(T) > sizeof(uint64_t)))) {
+  if constexpr (cpp::is_integral_v<T> && cpp::is_unsigned_v<T> &&
+                sizeof(T) > sizeof(uint64_t)) {
     static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
     const IntegerToString<T, radix::Hex::WithPrefix> buffer(t);
     return *this << buffer.view();
@@ -70,7 +68,7 @@ template TestLogger &TestLogger::operator<< <unsigned short>(unsigned short);
 template TestLogger &TestLogger::operator<< <unsigned int>(unsigned int);
 template TestLogger &TestLogger::operator<< <unsigned long>(unsigned long);
 template TestLogger &
-    TestLogger::operator<< <unsigned long long>(unsigned long long);
+TestLogger::operator<< <unsigned long long>(unsigned long long);
 
 #ifdef __SIZEOF_INT128__
 template TestLogger &TestLogger::operator<< <__uint128_t>(__uint128_t);
diff --git a/libc/test/src/__support/CPP/bit_test.cpp b/libc/test/src/__support/CPP/bit_test.cpp
index 1e3d895e6453a7..115a5d505c4b7a 100644
--- a/libc/test/src/__support/CPP/bit_test.cpp
+++ b/libc/test/src/__support/CPP/bit_test.cpp
@@ -12,44 +12,21 @@
 
 #include <stdint.h>
 
-#include <iostream>
-
 namespace LIBC_NAMESPACE::cpp {
 
-using UnsignedTypesNoBigInt = testing::TypeList<
-#if defined(__SIZEOF_INT128__)
-    __uint128_t,
-#endif
-    unsigned char, unsigned short, unsigned int, unsigned long,
-    unsigned long long>;
-
-using UnsignedTypes = testing::TypeList<
+using UnsignedTypes =
+    testing::TypeList<unsigned char, unsigned short, unsigned int,
+                      unsigned long, unsigned long long,
 #if defined(__SIZEOF_INT128__)
-    __uint128_t,
+                      __uint128_t,
 #endif
-    unsigned char, unsigned short, unsigned int, unsigned long,
-    unsigned long long, cpp::UInt<128>>;
+                      cpp::UInt<128>>;
 
 TYPED_TEST(LlvmLibcBitTest, HasSingleBit, UnsignedTypes) {
-  constexpr auto ZERO = T(0);
-  constexpr auto ALL_ONES = T(~ZERO);
-  EXPECT_FALSE(has_single_bit<T>(ZERO));
-  EXPECT_FALSE(has_single_bit<T>(ALL_ONES));
-
+  EXPECT_FALSE(has_single_bit<T>(T(0)));
+  EXPECT_FALSE(has_single_bit<T>(~T(0)));
   for (T value = 1; value; value <<= 1)
     EXPECT_TRUE(has_single_bit<T>(value));
-
-  // We test that if two bits are set has_single_bit returns false.
-  // We do this by setting the highest or lowest bit depending or where the
-  // current bit is. This is a bit convoluted but it helps catch a bug on BigInt
-  // where we have to work on an element-by-element basis.
-  constexpr auto MIDPOINT = T(ALL_ONES / 2);
-  constexpr auto LSB = T(1);
-  constexpr auto MSB = T(~(ALL_ONES >> 1));
-  for (T value = 1; value; value <<= 1) {
-    auto two_bits_value = value | ((value <= MIDPOINT) ? MSB : LSB);
-    EXPECT_FALSE(has_single_bit<T>(two_bits_value));
-  }
 }
 
 TYPED_TEST(LlvmLibcBitTest, CountLZero, UnsignedTypes) {
@@ -229,39 +206,39 @@ TEST(LlvmLibcBitTest, Rotr) {
             rotr<uint64_t>(0x12345678deadbeefULL, -19));
 }
 
-TYPED_TEST(LlvmLibcBitTest, FirstLeadingZero, UnsignedTypesNoBigInt) {
+TYPED_TEST(LlvmLibcBitTest, FirstLeadingZero, UnsignedTypes) {
   EXPECT_EQ(first_leading_zero<T>(cpp::numeric_limits<T>::max()), 0);
   for (int i = 0U; i != cpp::numeric_limits<T>::digits; ++i)
     EXPECT_EQ(first_leading_zero<T>(~(T(1) << i)),
               cpp::numeric_limits<T>::digits - i);
 }
 
-TYPED_TEST(LlvmLibcBitTest, FirstLeadingOne, UnsignedTypesNoBigInt) {
+TYPED_TEST(LlvmLibcBitTest, FirstLeadingOne, UnsignedTypes) {
   EXPECT_EQ(first_leading_one<T>(static_cast<T>(0)), 0);
   for (int i = 0U; i != cpp::numeric_limits<T>::digits; ++i)
     EXPECT_EQ(first_leading_one<T>(T(1) << i),
               cpp::numeric_limits<T>::digits - i);
 }
 
-TYPED_TEST(LlvmLibcBitTest, FirstTrailingZero, UnsignedTypesNoBigInt) {
+TYPED_TEST(LlvmLibcBitTest, FirstTrailingZero, UnsignedTypes) {
   EXPECT_EQ(first_trailing_zero<T>(cpp::numeric_limits<T>::max()), 0);
   for (int i = 0U; i != cpp::numeric_limits<T>::digits; ++i)
     EXPECT_EQ(first_trailing_zero<T>(~(T(1) << i)), i + 1);
 }
 
-TYPED_TEST(LlvmLibcBitTest, FirstTrailingOne, UnsignedTypesNoBigInt) {
+TYPED_TEST(LlvmLibcBitTest, FirstTrailingOne, UnsignedTypes) {
   EXPECT_EQ(first_trailing_one<T>(cpp::numeric_limits<T>::max()), 0);
   for (int i = 0U; i != cpp::numeric_limits<T>::digits; ++i)
     EXPECT_EQ(first_trailing_one<T>(T(1) << i), i + 1);
 }
 
-TYPED_TEST(LlvmLibcBitTest, CountZeros, UnsignedTypesNoBigInt) {
+TYPED_TEST(LlvmLibcBitTest, CountZeros, UnsignedTypes) {
   EXPECT_EQ(count_zeros(T(0)), cpp::numeric_limits<T>::digits);
   for (int i = 0; i != cpp::numeric_limits<T>::digits; ++i)
     EXPECT_EQ(count_zeros<T>(cpp::numeric_limits<T>::max() >> i), i);
 }
 
-TYPED_TEST(LlvmLibcBitTest, CountOnes, UnsignedTypesNoBigInt) {
+TYPED_TEST(LlvmLibcBitTest, CountOnes, UnsignedTypes) {
   EXPECT_EQ(count_ones(T(0)), 0);
   for (int i = 0; i != cpp::numeric_limits<T>::digits; ++i)
     EXPECT_EQ(count_ones<T>(cpp::numeric_limits<T>::max() >> i),
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 5c6cf761ebe7de..49a454379e1c7a 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -507,7 +507,6 @@ libc_support_library(
         ":__support_cpp_span",
         ":__support_cpp_string_view",
         ":__support_cpp_type_traits",
-        ":__support_uint",
     ],
 )
 
diff --git a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
index 44692947af7c08..a5c18fbb68b398 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
@@ -18,7 +18,6 @@ libc_support_library(
         "//libc:__support_cpp_string",
         "//libc:__support_cpp_string_view",
         "//libc:__support_osutil_io",
-        "//libc:__support_uint",
         "//libc:__support_uint128",
     ],
 )



More information about the llvm-commits mailing list