[llvm-branch-commits] [libc] c874f0d - Revert "[libc] Refactor `BigInt` (#86137)"
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Apr 4 02:07:58 PDT 2024
Author: Guillaume Chatelet
Date: 2024-04-04T11:07:55+02:00
New Revision: c874f0dbfc76f0c5b83e70eb6b8f810fcefb6592
URL: https://github.com/llvm/llvm-project/commit/c874f0dbfc76f0c5b83e70eb6b8f810fcefb6592
DIFF: https://github.com/llvm/llvm-project/commit/c874f0dbfc76f0c5b83e70eb6b8f810fcefb6592.diff
LOG: Revert "[libc] Refactor `BigInt` (#86137)"
This reverts commit a2306b65d223212dcfafe12c7299262d8d4fdcb4.
Added:
Modified:
libc/fuzzing/CMakeLists.txt
libc/src/__support/FPUtil/dyadic_float.h
libc/src/__support/UInt.h
libc/src/__support/float_to_string.h
libc/src/__support/integer_literals.h
libc/src/__support/math_extras.h
libc/src/__support/number_pair.h
libc/test/src/__support/integer_literals_test.cpp
libc/test/src/__support/math_extras_test.cpp
libc/test/src/__support/uint_test.cpp
utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel
Removed:
libc/fuzzing/__support/CMakeLists.txt
libc/fuzzing/__support/uint_fuzz.cpp
################################################################################
diff --git a/libc/fuzzing/CMakeLists.txt b/libc/fuzzing/CMakeLists.txt
index 816691b4bd4403..82487688af1162 100644
--- a/libc/fuzzing/CMakeLists.txt
+++ b/libc/fuzzing/CMakeLists.txt
@@ -1,7 +1,6 @@
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer")
add_custom_target(libc-fuzzer)
-add_subdirectory(__support)
# TODO(#85680): Re-enable math fuzzing after headers are sorted out
# add_subdirectory(math)
add_subdirectory(stdlib)
diff --git a/libc/fuzzing/__support/CMakeLists.txt b/libc/fuzzing/__support/CMakeLists.txt
deleted file mode 100644
index 278e914e3fbe95..00000000000000
--- a/libc/fuzzing/__support/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-add_libc_fuzzer(
- uint_fuzz
- SRCS
- uint_fuzz.cpp
- DEPENDS
- libc.src.__support.uint
-)
diff --git a/libc/fuzzing/__support/uint_fuzz.cpp b/libc/fuzzing/__support/uint_fuzz.cpp
deleted file mode 100644
index f48f00d3b4ba11..00000000000000
--- a/libc/fuzzing/__support/uint_fuzz.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "src/__support/CPP/bit.h"
-#include "src/__support/UInt.h"
-#include "src/string/memory_utils/inline_memcpy.h"
-
-using namespace LIBC_NAMESPACE;
-
-// Helper function when using gdb / lldb to set a breakpoint and inspect values.
-template <typename T> void debug_and_trap(const char *msg, T a, T b) {
- __builtin_trap();
-}
-
-#define DEBUG_AND_TRAP()
-
-#define TEST_BINOP(OP) \
- if ((a OP b) != (static_cast<T>(BigInt(a) OP BigInt(b)))) \
- debug_and_trap(#OP, a, b);
-
-#define TEST_SHIFTOP(OP) \
- if ((a OP b) != (static_cast<T>(BigInt(a) OP b))) \
- debug_and_trap(#OP, a, b);
-
-#define TEST_FUNCTION(FUN) \
- if (FUN(a) != FUN(BigInt(a))) \
- debug_and_trap(#FUN, a, b);
-
-// Test that basic arithmetic operations of BigInt behave like their scalar
-// counterparts.
-template <typename T, typename BigInt> void run_tests(T a, T b) {
- TEST_BINOP(+)
- TEST_BINOP(-)
- TEST_BINOP(*)
- if (b != 0)
- TEST_BINOP(/)
- if (b >= 0 && b < cpp::numeric_limits<T>::digits) {
- TEST_SHIFTOP(<<)
- TEST_SHIFTOP(>>)
- }
- if constexpr (!BigInt::SIGNED) {
- TEST_FUNCTION(cpp::has_single_bit)
- TEST_FUNCTION(cpp::countr_zero)
- TEST_FUNCTION(cpp::countl_zero)
- TEST_FUNCTION(cpp::countl_one)
- TEST_FUNCTION(cpp::countr_one)
- }
-}
-
-// Reads a T from libfuzzer data.
-template <typename T> T read(const uint8_t *data, size_t &remainder) {
- T out = 0;
- constexpr size_t T_SIZE = sizeof(T);
- const size_t copy_size = remainder < T_SIZE ? remainder : T_SIZE;
- inline_memcpy(&out, data, copy_size);
- remainder -= copy_size;
- return out;
-}
-
-template <typename T, typename BigInt>
-void run_tests(const uint8_t *data, size_t size) {
- const auto a = read<T>(data, size);
- const auto b = read<T>(data, size);
- run_tests<T, BigInt>(a, b);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
- // unsigned
- run_tests<uint64_t, BigInt<64, false, uint16_t>>(data, size);
- // signed
- run_tests<int64_t, BigInt<64, true, uint16_t>>(data, size);
- return 0;
-}
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index e0c205f52383ba..73fd7381c3c838 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -58,9 +58,9 @@ template <size_t Bits> struct DyadicFloat {
// significant bit.
LIBC_INLINE constexpr DyadicFloat &normalize() {
if (!mantissa.is_zero()) {
- int shift_length = cpp::countl_zero(mantissa);
+ int shift_length = static_cast<int>(mantissa.clz());
exponent -= shift_length;
- mantissa <<= static_cast<size_t>(shift_length);
+ mantissa.shift_left(static_cast<size_t>(shift_length));
}
return *this;
}
@@ -233,7 +233,7 @@ LIBC_INLINE constexpr DyadicFloat<Bits> quick_add(DyadicFloat<Bits> a,
result.sign = a.sign;
result.exponent = a.exponent;
result.mantissa = a.mantissa;
- if (result.mantissa.add_overflow(b.mantissa)) {
+ if (result.mantissa.add(b.mantissa)) {
// Mantissa addition overflow.
result.shift_right(1);
result.mantissa.val[DyadicFloat<Bits>::MantissaType::WORD_COUNT - 1] |=
diff --git a/libc/src/__support/UInt.h b/libc/src/__support/UInt.h
index c524de38d98654..282efdba1c5f2b 100644
--- a/libc/src/__support/UInt.h
+++ b/libc/src/__support/UInt.h
@@ -14,11 +14,10 @@
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/type_traits.h"
-#include "src/__support/macros/attributes.h" // LIBC_INLINE
-#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
-#include "src/__support/macros/properties/compiler.h" // LIBC_COMPILER_IS_CLANG
+#include "src/__support/macros/attributes.h" // LIBC_INLINE
+#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
#include "src/__support/macros/properties/types.h" // LIBC_TYPES_HAS_INT128, LIBC_TYPES_HAS_INT64
-#include "src/__support/math_extras.h" // add_with_carry, sub_with_borrow
+#include "src/__support/math_extras.h" // SumCarry, DiffBorrow
#include "src/__support/number_pair.h"
#include <stddef.h> // For size_t
@@ -26,321 +25,71 @@
namespace LIBC_NAMESPACE {
-namespace multiword {
-
-// A type trait mapping unsigned integers to their half-width unsigned
-// counterparts.
+namespace internal {
template <typename T> struct half_width;
-template <> struct half_width<uint16_t> : cpp::type_identity<uint8_t> {};
-template <> struct half_width<uint32_t> : cpp::type_identity<uint16_t> {};
-#ifdef LIBC_TYPES_HAS_INT64
+
template <> struct half_width<uint64_t> : cpp::type_identity<uint32_t> {};
+template <> struct half_width<uint32_t> : cpp::type_identity<uint16_t> {};
+template <> struct half_width<uint16_t> : cpp::type_identity<uint8_t> {};
#ifdef LIBC_TYPES_HAS_INT128
template <> struct half_width<__uint128_t> : cpp::type_identity<uint64_t> {};
#endif // LIBC_TYPES_HAS_INT128
-#endif // LIBC_TYPES_HAS_INT64
-template <typename T> using half_width_t = typename half_width<T>::type;
-
-// An array of two elements that can be used in multiword operations.
-template <typename T> struct DoubleWide final : cpp::array<T, 2> {
- using UP = cpp::array<T, 2>;
- using UP::UP;
- LIBC_INLINE constexpr DoubleWide(T lo, T hi) : UP({lo, hi}) {}
-};
-
-// Converts an unsigned value into a DoubleWide<half_width_t<T>>.
-template <typename T> LIBC_INLINE constexpr auto split(T value) {
- static_assert(cpp::is_unsigned_v<T>);
- return cpp::bit_cast<DoubleWide<half_width_t<T>>>(value);
-}
-
-// The low part of a DoubleWide value.
-template <typename T> LIBC_INLINE constexpr T lo(const DoubleWide<T> &value) {
- return value[0];
-}
-// The high part of a DoubleWide value.
-template <typename T> LIBC_INLINE constexpr T hi(const DoubleWide<T> &value) {
- return value[1];
-}
-// The low part of an unsigned value.
-template <typename T> LIBC_INLINE constexpr half_width_t<T> lo(T value) {
- return lo(split(value));
-}
-// The high part of an unsigned value.
-template <typename T> LIBC_INLINE constexpr half_width_t<T> hi(T value) {
- return hi(split(value));
-}
-
-// Returns 'a' times 'b' in a DoubleWide<word>. Cannot overflow by construction.
-template <typename word>
-LIBC_INLINE constexpr DoubleWide<word> mul2(word a, word b) {
- if constexpr (cpp::is_same_v<word, uint8_t>) {
- return split<uint16_t>(uint16_t(a) * uint16_t(b));
- } else if constexpr (cpp::is_same_v<word, uint16_t>) {
- return split<uint32_t>(uint32_t(a) * uint32_t(b));
- }
-#ifdef LIBC_TYPES_HAS_INT64
- else if constexpr (cpp::is_same_v<word, uint32_t>) {
- return split<uint64_t>(uint64_t(a) * uint64_t(b));
- }
-#endif
-#ifdef LIBC_TYPES_HAS_INT128
- else if constexpr (cpp::is_same_v<word, uint64_t>) {
- return split<__uint128_t>(__uint128_t(a) * __uint128_t(b));
- }
-#endif
- else {
- using half_word = half_width_t<word>;
- const auto shiftl = [](word value) -> word {
- return value << cpp::numeric_limits<half_word>::digits;
- };
- const auto shiftr = [](word value) -> word {
- return value >> cpp::numeric_limits<half_word>::digits;
- };
- // Here we do a one digit multiplication where 'a' and 'b' are of type
- // word. We split 'a' and 'b' into half words and perform the classic long
- // multiplication with 'a' and 'b' being two-digit numbers.
-
- // a a_hi a_lo
- // x b => x b_hi b_lo
- // ---- -----------
- // c result
- // We convert 'lo' and 'hi' from 'half_word' to 'word' so multiplication
- // doesn't overflow.
- const word a_lo = lo(a);
- const word b_lo = lo(b);
- const word a_hi = hi(a);
- const word b_hi = hi(b);
- const word step1 = b_lo * a_lo; // no overflow;
- const word step2 = b_lo * a_hi; // no overflow;
- const word step3 = b_hi * a_lo; // no overflow;
- const word step4 = b_hi * a_hi; // no overflow;
- word lo_digit = step1;
- word hi_digit = step4;
- const word no_carry = 0;
- word carry;
- word _; // unused carry variable.
- lo_digit = add_with_carry<word>(lo_digit, shiftl(step2), no_carry, carry);
- hi_digit = add_with_carry<word>(hi_digit, shiftr(step2), carry, _);
- lo_digit = add_with_carry<word>(lo_digit, shiftl(step3), no_carry, carry);
- hi_digit = add_with_carry<word>(hi_digit, shiftr(step3), carry, _);
- return DoubleWide<word>(lo_digit, hi_digit);
- }
-}
-
-// In-place 'dst op= rhs' with operation with carry propagation. Returns carry.
-template <typename Function, typename word, size_t N, size_t M>
-LIBC_INLINE constexpr word inplace_binop(Function op_with_carry,
- cpp::array<word, N> &dst,
- const cpp::array<word, M> &rhs) {
- static_assert(N >= M);
- word carry_out = 0;
- for (size_t i = 0; i < N; ++i) {
- const bool has_rhs_value = i < M;
- const word rhs_value = has_rhs_value ? rhs[i] : 0;
- const word carry_in = carry_out;
- dst[i] = op_with_carry(dst[i], rhs_value, carry_in, carry_out);
- // stop early when rhs is over and no carry is to be propagated.
- if (!has_rhs_value && carry_out == 0)
- break;
- }
- return carry_out;
-}
-// In-place addition. Returns carry.
-template <typename word, size_t N, size_t M>
-LIBC_INLINE constexpr word add_with_carry(cpp::array<word, N> &dst,
- const cpp::array<word, M> &rhs) {
- return inplace_binop(LIBC_NAMESPACE::add_with_carry<word>, dst, rhs);
-}
+template <typename T> using half_width_t = typename half_width<T>::type;
-// In-place subtraction. Returns borrow.
-template <typename word, size_t N, size_t M>
-LIBC_INLINE constexpr word sub_with_borrow(cpp::array<word, N> &dst,
- const cpp::array<word, M> &rhs) {
- return inplace_binop(LIBC_NAMESPACE::sub_with_borrow<word>, dst, rhs);
-}
+template <typename T> constexpr NumberPair<T> full_mul(T a, T b) {
+ NumberPair<T> pa = split(a);
+ NumberPair<T> pb = split(b);
+ NumberPair<T> prod;
-// In-place multiply-add. Returns carry.
-// i.e., 'dst += b * c'
-template <typename word, size_t N>
-LIBC_INLINE constexpr word mul_add_with_carry(cpp::array<word, N> &dst, word b,
- word c) {
- return add_with_carry(dst, mul2(b, c));
-}
+ prod.lo = pa.lo * pb.lo; // exact
+ prod.hi = pa.hi * pb.hi; // exact
+ NumberPair<T> lo_hi = split(pa.lo * pb.hi); // exact
+ NumberPair<T> hi_lo = split(pa.hi * pb.lo); // exact
-// An array of two elements serving as an accumulator during multiword
-// computations.
-template <typename T> struct Accumulator final : cpp::array<T, 2> {
- using UP = cpp::array<T, 2>;
- LIBC_INLINE constexpr Accumulator() : UP({0, 0}) {}
- LIBC_INLINE constexpr T advance(T carry_in) {
- auto result = UP::front();
- UP::front() = UP::back();
- UP::back() = carry_in;
- return result;
- }
- LIBC_INLINE constexpr T sum() const { return UP::front(); }
- LIBC_INLINE constexpr T carry() const { return UP::back(); }
-};
+ constexpr size_t HALF_BIT_WIDTH = sizeof(T) * CHAR_BIT / 2;
-// In-place multiplication by a single word. Returns carry.
-template <typename word, size_t N>
-LIBC_INLINE constexpr word scalar_multiply_with_carry(cpp::array<word, N> &dst,
- word x) {
- Accumulator<word> acc;
- for (auto &val : dst) {
- const word carry = mul_add_with_carry(acc, val, x);
- val = acc.advance(carry);
- }
- return acc.carry();
-}
+ auto r1 = add_with_carry(prod.lo, lo_hi.lo << HALF_BIT_WIDTH, T(0));
+ prod.lo = r1.sum;
+ prod.hi = add_with_carry(prod.hi, lo_hi.hi, r1.carry).sum;
-// Multiplication of 'lhs' by 'rhs' into 'dst'. Returns carry.
-// This function is safe to use for signed numbers.
-// https://stackoverflow.com/a/20793834
-// https://pages.cs.wisc.edu/%7Emarkhill/cs354/Fall2008/beyond354/int.mult.html
-template <typename word, size_t O, size_t M, size_t N>
-LIBC_INLINE constexpr word multiply_with_carry(cpp::array<word, O> &dst,
- const cpp::array<word, M> &lhs,
- const cpp::array<word, N> &rhs) {
- static_assert(O >= M + N);
- Accumulator<word> acc;
- for (size_t i = 0; i < O; ++i) {
- const size_t lower_idx = i < N ? 0 : i - N + 1;
- const size_t upper_idx = i < M ? i : M - 1;
- word carry = 0;
- for (size_t j = lower_idx; j <= upper_idx; ++j)
- carry += mul_add_with_carry(acc, lhs[j], rhs[i - j]);
- dst[i] = acc.advance(carry);
- }
- return acc.carry();
-}
+ auto r2 = add_with_carry(prod.lo, hi_lo.lo << HALF_BIT_WIDTH, T(0));
+ prod.lo = r2.sum;
+ prod.hi = add_with_carry(prod.hi, hi_lo.hi, r2.carry).sum;
-template <typename word, size_t N>
-LIBC_INLINE constexpr void quick_mul_hi(cpp::array<word, N> &dst,
- const cpp::array<word, N> &lhs,
- const cpp::array<word, N> &rhs) {
- Accumulator<word> acc;
- word carry = 0;
- // First round of accumulation for those at N - 1 in the full product.
- for (size_t i = 0; i < N; ++i)
- carry += mul_add_with_carry(acc, lhs[i], rhs[N - 1 - i]);
- for (size_t i = N; i < 2 * N - 1; ++i) {
- acc.advance(carry);
- carry = 0;
- for (size_t j = i - N + 1; j < N; ++j)
- carry += mul_add_with_carry(acc, lhs[j], rhs[i - j]);
- dst[i - N] = acc.sum();
- }
- dst.back() = acc.carry();
+ return prod;
}
-template <typename word, size_t N>
-LIBC_INLINE constexpr bool is_negative(cpp::array<word, N> &array) {
- using signed_word = cpp::make_signed_t<word>;
- return cpp::bit_cast<signed_word>(array.back()) < 0;
+template <>
+LIBC_INLINE constexpr NumberPair<uint32_t> full_mul<uint32_t>(uint32_t a,
+ uint32_t b) {
+ uint64_t prod = uint64_t(a) * uint64_t(b);
+ NumberPair<uint32_t> result;
+ result.lo = uint32_t(prod);
+ result.hi = uint32_t(prod >> 32);
+ return result;
}
-// An enum for the shift function below.
-enum Direction { LEFT, RIGHT };
-
-// A bitwise shift on an array of elements.
-// TODO: Make the result UB when 'offset' is greater or equal to the number of
-// bits in 'array'. This will allow for better code performance.
-template <Direction direction, bool is_signed, typename word, size_t N>
-LIBC_INLINE constexpr cpp::array<word, N> shift(cpp::array<word, N> array,
- size_t offset) {
- static_assert(direction == LEFT || direction == RIGHT);
- constexpr size_t WORD_BITS = cpp::numeric_limits<word>::digits;
- constexpr size_t TOTAL_BITS = N * WORD_BITS;
- if (LIBC_UNLIKELY(offset == 0))
- return array;
- if (LIBC_UNLIKELY(offset >= TOTAL_BITS))
- return {};
#ifdef LIBC_TYPES_HAS_INT128
- if constexpr (TOTAL_BITS == 128) {
- using type = cpp::conditional_t<is_signed, __int128_t, __uint128_t>;
- auto tmp = cpp::bit_cast<type>(array);
- if constexpr (direction == LEFT)
- tmp <<= offset;
- else
- tmp >>= offset;
- return cpp::bit_cast<cpp::array<word, N>>(tmp);
- }
-#endif
- const bool is_neg = is_signed && is_negative(array);
- constexpr auto at = [](size_t index) -> int {
- // reverse iteration when direction == LEFT.
- if constexpr (direction == LEFT)
- return int(N) - int(index) - 1;
- return int(index);
- };
- const auto safe_get_at = [&](size_t index) -> word {
- // return appropriate value when accessing out of bound elements.
- const int i = at(index);
- if (i < 0)
- return 0;
- if (i >= int(N))
- return is_neg ? -1 : 0;
- return array[i];
- };
- const size_t index_offset = offset / WORD_BITS;
- const size_t bit_offset = offset % WORD_BITS;
-#ifdef LIBC_COMPILER_IS_CLANG
- __builtin_assume(index_offset < N);
-#endif
- cpp::array<word, N> out = {};
- for (size_t index = 0; index < N; ++index) {
- const word part1 = safe_get_at(index + index_offset);
- const word part2 = safe_get_at(index + index_offset + 1);
- word &dst = out[at(index)];
- if (bit_offset == 0)
- dst = part1; // no crosstalk between parts.
- else if constexpr (direction == LEFT)
- dst = (part1 << bit_offset) | (part2 >> (WORD_BITS - bit_offset));
- else
- dst = (part1 >> bit_offset) | (part2 << (WORD_BITS - bit_offset));
- }
- return out;
+template <>
+LIBC_INLINE constexpr NumberPair<uint64_t> full_mul<uint64_t>(uint64_t a,
+ uint64_t b) {
+ __uint128_t prod = __uint128_t(a) * __uint128_t(b);
+ NumberPair<uint64_t> result;
+ result.lo = uint64_t(prod);
+ result.hi = uint64_t(prod >> 64);
+ return result;
}
+#endif // LIBC_TYPES_HAS_INT128
-#define DECLARE_COUNTBIT(NAME, INDEX_EXPR) \
- template <typename word, size_t N> \
- LIBC_INLINE constexpr int NAME(const cpp::array<word, N> &val) { \
- int bit_count = 0; \
- for (size_t i = 0; i < N; ++i) { \
- const int word_count = cpp::NAME<word>(val[INDEX_EXPR]); \
- bit_count += word_count; \
- if (word_count != cpp::numeric_limits<word>::digits) \
- break; \
- } \
- return bit_count; \
- }
-
-DECLARE_COUNTBIT(countr_zero, i) // iterating forward
-DECLARE_COUNTBIT(countr_one, i) // iterating forward
-DECLARE_COUNTBIT(countl_zero, N - i - 1) // iterating backward
-DECLARE_COUNTBIT(countl_one, N - i - 1) // iterating backward
-
-} // namespace multiword
+} // namespace internal
template <size_t Bits, bool Signed, typename WordType = uint64_t>
struct BigInt {
-private:
static_assert(cpp::is_integral_v<WordType> && cpp::is_unsigned_v<WordType>,
"WordType must be unsigned integer.");
- struct Division {
- BigInt quotient;
- BigInt remainder;
- };
-
-public:
using word_type = WordType;
- using unsigned_type = BigInt<Bits, false, word_type>;
- using signed_type = BigInt<Bits, true, word_type>;
-
LIBC_INLINE_VAR static constexpr bool SIGNED = Signed;
LIBC_INLINE_VAR static constexpr size_t BITS = Bits;
LIBC_INLINE_VAR
@@ -351,7 +100,10 @@ struct BigInt {
LIBC_INLINE_VAR static constexpr size_t WORD_COUNT = Bits / WORD_SIZE;
- cpp::array<WordType, WORD_COUNT> val{}; // zero initialized.
+ 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;
@@ -360,67 +112,76 @@ struct BigInt {
template <size_t OtherBits, bool OtherSigned>
LIBC_INLINE constexpr BigInt(
const BigInt<OtherBits, OtherSigned, WordType> &other) {
- if (OtherBits >= Bits) { // truncate
+ if (OtherBits >= Bits) {
for (size_t i = 0; i < WORD_COUNT; ++i)
val[i] = other[i];
- } else { // zero or sign extend
+ } else {
size_t i = 0;
for (; i < OtherBits / WORD_SIZE; ++i)
val[i] = other[i];
- extend(i, Signed && other.is_neg());
+ WordType sign = 0;
+ if constexpr (Signed && OtherSigned) {
+ sign = static_cast<WordType>(
+ -static_cast<cpp::make_signed_t<WordType>>(other.is_neg()));
+ }
+ for (; i < WORD_COUNT; ++i)
+ val[i] = sign;
}
}
// Construct a BigInt from a C array.
- template <size_t N> LIBC_INLINE constexpr BigInt(const WordType (&nums)[N]) {
- static_assert(N == WORD_COUNT);
- for (size_t i = 0; i < WORD_COUNT; ++i)
+ template <size_t N, cpp::enable_if_t<N <= WORD_COUNT, int> = 0>
+ LIBC_INLINE constexpr BigInt(const WordType (&nums)[N]) {
+ size_t min_wordcount = N < WORD_COUNT ? N : WORD_COUNT;
+ size_t i = 0;
+ for (; i < min_wordcount; ++i)
val[i] = nums[i];
- }
- LIBC_INLINE constexpr explicit BigInt(
- const cpp::array<WordType, WORD_COUNT> &words) {
- val = words;
+ // If nums doesn't completely fill val, then fill the rest with zeroes.
+ for (; i < WORD_COUNT; ++i)
+ val[i] = 0;
}
// Initialize the first word to |v| and the rest to 0.
template <typename T, typename = cpp::enable_if_t<cpp::is_integral_v<T>>>
LIBC_INLINE constexpr BigInt(T v) {
- constexpr size_t T_SIZE = sizeof(T) * CHAR_BIT;
- const bool is_neg = Signed && (v < 0);
- for (size_t i = 0; i < WORD_COUNT; ++i) {
- if (v == 0) {
- extend(i, is_neg);
- return;
+ val[0] = static_cast<WordType>(v);
+
+ if constexpr (WORD_COUNT == 1)
+ return;
+
+ if constexpr (Bits < sizeof(T) * CHAR_BIT) {
+ for (int i = 1; i < WORD_COUNT; ++i) {
+ v >>= WORD_SIZE;
+ val[i] = static_cast<WordType>(v);
}
- val[i] = static_cast<WordType>(v);
- if constexpr (T_SIZE > WORD_SIZE)
+ return;
+ }
+
+ size_t i = 1;
+
+ if constexpr (WORD_SIZE < sizeof(T) * CHAR_BIT)
+ for (; i < sizeof(T) * CHAR_BIT / WORD_SIZE; ++i) {
v >>= WORD_SIZE;
- else
- v = 0;
+ val[i] = static_cast<WordType>(v);
+ }
+
+ WordType sign = (Signed && (v < 0)) ? ~WordType(0) : WordType(0);
+ for (; i < WORD_COUNT; ++i) {
+ val[i] = sign;
}
}
- LIBC_INLINE constexpr BigInt &operator=(const BigInt &other) = default;
- // constants
- LIBC_INLINE static constexpr BigInt zero() { return BigInt(); }
- LIBC_INLINE static constexpr BigInt one() { return BigInt(1); }
- LIBC_INLINE static constexpr BigInt all_ones() { return ~zero(); }
- LIBC_INLINE static constexpr BigInt min() {
- BigInt out;
- if constexpr (SIGNED)
- out.set_msb();
- return out;
- }
- LIBC_INLINE static constexpr BigInt max() {
- BigInt out = all_ones();
- if constexpr (SIGNED)
- out.clear_msb();
- return out;
+ LIBC_INLINE constexpr explicit BigInt(
+ const cpp::array<WordType, WORD_COUNT> &words) {
+ for (size_t i = 0; i < WORD_COUNT; ++i)
+ val[i] = words[i];
}
// TODO: Reuse the Sign type.
- LIBC_INLINE constexpr bool is_neg() const { return SIGNED && get_msb(); }
+ LIBC_INLINE constexpr bool is_neg() const {
+ return val.back() >> (WORD_SIZE - 1);
+ }
template <typename T> LIBC_INLINE constexpr explicit operator T() const {
return to<T>();
@@ -430,100 +191,200 @@ struct BigInt {
LIBC_INLINE constexpr cpp::enable_if_t<
cpp::is_integral_v<T> && !cpp::is_same_v<T, bool>, T>
to() const {
- constexpr size_t T_SIZE = sizeof(T) * CHAR_BIT;
T lo = static_cast<T>(val[0]);
- if constexpr (T_SIZE <= WORD_SIZE)
+
+ constexpr size_t T_BITS = sizeof(T) * CHAR_BIT;
+
+ if constexpr (T_BITS <= WORD_SIZE)
return lo;
+
constexpr size_t MAX_COUNT =
- T_SIZE > Bits ? WORD_COUNT : T_SIZE / WORD_SIZE;
+ T_BITS > Bits ? WORD_COUNT : T_BITS / WORD_SIZE;
for (size_t i = 1; i < MAX_COUNT; ++i)
lo += static_cast<T>(val[i]) << (WORD_SIZE * i);
- if constexpr (Signed && (T_SIZE > Bits)) {
+
+ if constexpr (Signed && (T_BITS > Bits)) {
// Extend sign for negative numbers.
constexpr T MASK = (~T(0) << Bits);
if (is_neg())
lo |= MASK;
}
+
return lo;
}
LIBC_INLINE constexpr explicit operator bool() const { return !is_zero(); }
+ LIBC_INLINE constexpr BigInt &operator=(const BigInt &other) = default;
+
LIBC_INLINE constexpr bool is_zero() const {
- for (auto part : val)
- if (part != 0)
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ if (val[i] != 0)
return false;
+ }
return true;
}
- // Add 'rhs' to this number and store the result in this number.
+ // Add x to this number and store the result in this number.
// Returns the carry value produced by the addition operation.
- LIBC_INLINE constexpr WordType add_overflow(const BigInt &rhs) {
- return multiword::add_with_carry(val, rhs.val);
+ LIBC_INLINE constexpr WordType add(const BigInt &x) {
+ SumCarry<WordType> s{0, 0};
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ s = add_with_carry(val[i], x.val[i], s.carry);
+ val[i] = s.sum;
+ }
+ return s.carry;
}
LIBC_INLINE constexpr BigInt operator+(const BigInt &other) const {
- BigInt result = *this;
- result.add_overflow(other);
+ BigInt result;
+ SumCarry<WordType> s{0, 0};
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ s = add_with_carry(val[i], other.val[i], s.carry);
+ result.val[i] = s.sum;
+ }
return result;
}
// This will only apply when initializing a variable from constant values, so
// it will always use the constexpr version of add_with_carry.
LIBC_INLINE constexpr BigInt operator+(BigInt &&other) const {
- // We use addition commutativity to reuse 'other' and prevent allocation.
- other.add_overflow(*this); // Returned carry value is ignored.
- return other;
+ BigInt result;
+ SumCarry<WordType> s{0, 0};
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ s = add_with_carry(val[i], other.val[i], s.carry);
+ result.val[i] = s.sum;
+ }
+ return result;
}
LIBC_INLINE constexpr BigInt &operator+=(const BigInt &other) {
- add_overflow(other); // Returned carry value is ignored.
+ add(other); // Returned carry value is ignored.
return *this;
}
- // Subtract 'rhs' to this number and store the result in this number.
+ // Subtract x to this number and store the result in this number.
// Returns the carry value produced by the subtraction operation.
- LIBC_INLINE constexpr WordType sub_overflow(const BigInt &rhs) {
- return multiword::sub_with_borrow(val, rhs.val);
+ LIBC_INLINE constexpr WordType sub(const BigInt &x) {
+ DiffBorrow<WordType> d{0, 0};
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ d = sub_with_borrow(val[i], x.val[i], d.borrow);
+ val[i] = d.
diff ;
+ }
+ return d.borrow;
}
LIBC_INLINE constexpr BigInt operator-(const BigInt &other) const {
- BigInt result = *this;
- result.sub_overflow(other); // Returned carry value is ignored.
+ BigInt result;
+ DiffBorrow<WordType> d{0, 0};
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ d = sub_with_borrow(val[i], other.val[i], d.borrow);
+ result.val[i] = d.
diff ;
+ }
return result;
}
LIBC_INLINE constexpr BigInt operator-(BigInt &&other) const {
- BigInt result = *this;
- result.sub_overflow(other); // Returned carry value is ignored.
+ BigInt result;
+ DiffBorrow<WordType> d{0, 0};
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ d = sub_with_borrow(val[i], other.val[i], d.borrow);
+ result.val[i] = d.
diff ;
+ }
return result;
}
LIBC_INLINE constexpr BigInt &operator-=(const BigInt &other) {
// TODO(lntue): Set overflow flag / errno when carry is true.
- sub_overflow(other); // Returned carry value is ignored.
+ sub(other);
return *this;
}
- // Multiply this number with x and store the result in this number.
+ // Multiply this number with x and store the result in this number. It is
+ // implemented using the long multiplication algorithm by splitting the
+ // 64-bit words of this number and |x| in to 32-bit halves but peforming
+ // the operations using 64-bit numbers. This ensures that we don't lose the
+ // carry bits.
+ // Returns the carry value produced by the multiplication operation.
LIBC_INLINE constexpr WordType mul(WordType x) {
- return multiword::scalar_multiply_with_carry(val, x);
+ BigInt<2 * WORD_SIZE, Signed, WordType> partial_sum(0);
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ NumberPair<WordType> prod = internal::full_mul(val[i], x);
+ BigInt<2 * WORD_SIZE, Signed, WordType> tmp({prod.lo, prod.hi});
+ const WordType carry = partial_sum.add(tmp);
+ val[i] = partial_sum.val[0];
+ partial_sum.val[0] = partial_sum.val[1];
+ partial_sum.val[1] = carry;
+ }
+ return partial_sum.val[1];
+ }
+
+ LIBC_INLINE constexpr BigInt operator*(const BigInt &other) const {
+ if constexpr (Signed) {
+ BigInt<Bits, false, WordType> a(*this);
+ BigInt<Bits, false, WordType> b(other);
+ const bool a_neg = a.is_neg();
+ const bool b_neg = b.is_neg();
+ if (a_neg)
+ a = -a;
+ if (b_neg)
+ b = -b;
+ BigInt<Bits, false, WordType> prod = a * b;
+ if (a_neg != b_neg)
+ prod = -prod;
+ return static_cast<BigInt<Bits, true, WordType>>(prod);
+ } else {
+ if constexpr (WORD_COUNT == 1) {
+ return {val[0] * other.val[0]};
+ } else {
+ BigInt result(0);
+ BigInt<2 * WORD_SIZE, Signed, WordType> partial_sum(0);
+ WordType carry = 0;
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ for (size_t j = 0; j <= i; j++) {
+ NumberPair<WordType> prod =
+ internal::full_mul(val[j], other.val[i - j]);
+ BigInt<2 * WORD_SIZE, Signed, WordType> tmp({prod.lo, prod.hi});
+ carry += partial_sum.add(tmp);
+ }
+ result.val[i] = partial_sum.val[0];
+ partial_sum.val[0] = partial_sum.val[1];
+ partial_sum.val[1] = carry;
+ carry = 0;
+ }
+ return result;
+ }
+ }
}
- // Return the full product.
+ // Return the full product, only unsigned for now.
template <size_t OtherBits>
- LIBC_INLINE constexpr auto
+ LIBC_INLINE constexpr BigInt<Bits + OtherBits, Signed, WordType>
ful_mul(const BigInt<OtherBits, Signed, WordType> &other) const {
- BigInt<Bits + OtherBits, Signed, WordType> result;
- multiword::multiply_with_carry(result.val, val, other.val);
+ BigInt<Bits + OtherBits, Signed, WordType> result(0);
+ BigInt<2 * WORD_SIZE, Signed, WordType> partial_sum(0);
+ WordType carry = 0;
+ constexpr size_t OTHER_WORDCOUNT =
+ BigInt<OtherBits, Signed, WordType>::WORD_COUNT;
+ for (size_t i = 0; i <= WORD_COUNT + OTHER_WORDCOUNT - 2; ++i) {
+ const size_t lower_idx =
+ i < OTHER_WORDCOUNT ? 0 : i - OTHER_WORDCOUNT + 1;
+ const size_t upper_idx = i < WORD_COUNT ? i : WORD_COUNT - 1;
+ for (size_t j = lower_idx; j <= upper_idx; ++j) {
+ NumberPair<WordType> prod =
+ internal::full_mul(val[j], other.val[i - j]);
+ BigInt<2 * WORD_SIZE, Signed, WordType> tmp({prod.lo, prod.hi});
+ carry += partial_sum.add(tmp);
+ }
+ result.val[i] = partial_sum.val[0];
+ partial_sum.val[0] = partial_sum.val[1];
+ partial_sum.val[1] = carry;
+ carry = 0;
+ }
+ result.val[WORD_COUNT + OTHER_WORDCOUNT - 1] = partial_sum.val[0];
return result;
}
- LIBC_INLINE constexpr BigInt operator*(const BigInt &other) const {
- // Perform full mul and truncate.
- return BigInt(ful_mul(other));
- }
-
// Fast hi part of the full product. The normal product `operator*` returns
// `Bits` least significant bits of the full product, while this function will
// approximate `Bits` most significant bits of the full product with errors
@@ -546,17 +407,39 @@ struct BigInt {
// 256 4 16 10 3
// 512 8 64 36 7
LIBC_INLINE constexpr BigInt quick_mul_hi(const BigInt &other) const {
- BigInt result;
- multiword::quick_mul_hi(result.val, val, other.val);
+ BigInt result(0);
+ BigInt<2 * WORD_SIZE, Signed, WordType> partial_sum(0);
+ WordType carry = 0;
+ // First round of accumulation for those at WORD_COUNT - 1 in the full
+ // product.
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ NumberPair<WordType> prod =
+ internal::full_mul(val[i], other.val[WORD_COUNT - 1 - i]);
+ BigInt<2 * WORD_SIZE, Signed, WordType> tmp({prod.lo, prod.hi});
+ carry += partial_sum.add(tmp);
+ }
+ for (size_t i = WORD_COUNT; i < 2 * WORD_COUNT - 1; ++i) {
+ partial_sum.val[0] = partial_sum.val[1];
+ partial_sum.val[1] = carry;
+ carry = 0;
+ for (size_t j = i - WORD_COUNT + 1; j < WORD_COUNT; ++j) {
+ NumberPair<WordType> prod =
+ internal::full_mul(val[j], other.val[i - j]);
+ BigInt<2 * WORD_SIZE, Signed, WordType> tmp({prod.lo, prod.hi});
+ carry += partial_sum.add(tmp);
+ }
+ result.val[i - WORD_COUNT] = partial_sum.val[0];
+ }
+ result.val[WORD_COUNT - 1] = partial_sum.val[1];
return result;
}
- // BigInt(x).pow_n(n) computes x ^ n.
- // Note 0 ^ 0 == 1.
+ // pow takes a power and sets this to its starting value to that power. Zero
+ // to the zeroth power returns 1.
LIBC_INLINE constexpr void pow_n(uint64_t power) {
- static_assert(!Signed);
- BigInt result = one();
+ BigInt result = 1;
BigInt cur_power = *this;
+
while (power > 0) {
if ((power % 2) > 0)
result *= cur_power;
@@ -566,23 +449,38 @@ struct BigInt {
*this = result;
}
- // Performs inplace signed / unsigned division. Returns remainder if not
- // dividing by zero.
- // For signed numbers it behaves like C++ signed integer division.
- // That is by truncating the fractionnal part
- // https://stackoverflow.com/a/3602857
- LIBC_INLINE constexpr cpp::optional<BigInt> div(const BigInt ÷r) {
- if (LIBC_UNLIKELY(divider.is_zero()))
+ // TODO: Make division work correctly for signed integers.
+
+ // div takes another BigInt of the same size and divides this by it. The value
+ // of this will be set to the quotient, and the return value is the remainder.
+ LIBC_INLINE constexpr cpp::optional<BigInt> div(const BigInt &other) {
+ BigInt remainder(0);
+ if (*this < other) {
+ remainder = *this;
+ *this = BigInt(0);
+ return remainder;
+ }
+ if (other == 1) {
+ return remainder;
+ }
+ if (other == 0) {
return cpp::nullopt;
- if (LIBC_UNLIKELY(divider == BigInt::one()))
- return BigInt::zero();
- Division result;
- if constexpr (SIGNED)
- result = divide_signed(*this, divider);
- else
- result = divide_unsigned(*this, divider);
- *this = result.quotient;
- return result.remainder;
+ }
+
+ BigInt quotient(0);
+ BigInt subtractor = other;
+ int cur_bit = static_cast<int>(subtractor.clz() - this->clz());
+ subtractor.shift_left(cur_bit);
+
+ for (; cur_bit >= 0 && *this > 0; --cur_bit, subtractor.shift_right(1)) {
+ if (*this >= subtractor) {
+ this->sub(subtractor);
+ quotient = quotient | (BigInt(1) << cur_bit);
+ }
+ }
+ remainder = *this;
+ *this = quotient;
+ return remainder;
}
// Efficiently perform BigInt / (x * 2^e), where x is a half-word-size
@@ -598,16 +496,19 @@ struct BigInt {
// computation of each step is now properly contained within WordType.
// And finally we perform some extra alignment steps for the remaining bits.
LIBC_INLINE constexpr cpp::optional<BigInt>
- div_uint_half_times_pow_2(multiword::half_width_t<WordType> x, size_t e) {
- BigInt remainder;
- if (x == 0)
+ div_uint_half_times_pow_2(internal::half_width_t<WordType> x, size_t e) {
+ BigInt remainder(0);
+
+ if (x == 0) {
return cpp::nullopt;
+ }
if (e >= Bits) {
remainder = *this;
- *this = BigInt<Bits, false, WordType>();
+ *this = BigInt<Bits, false, WordType>(0);
return remainder;
}
- BigInt quotient;
+
+ BigInt quotient(0);
WordType x_word = static_cast<WordType>(x);
constexpr size_t LOG2_WORD_SIZE = cpp::bit_width(WORD_SIZE) - 1;
constexpr size_t HALF_WORD_SIZE = WORD_SIZE >> 1;
@@ -732,22 +633,189 @@ struct BigInt {
return *this;
}
- LIBC_INLINE constexpr BigInt &operator<<=(size_t s) {
- val = multiword::shift<multiword::LEFT, SIGNED>(val, s);
- 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 = cpp::countl_zero(val[i]);
+ leading_zeroes += zeroes;
+ if (zeroes != word_digits)
+ 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 = cpp::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.
+ if (s >= WORD_SIZE) {
+ val[0] = 0;
+ return;
+ }
+ val[0] <<= s;
+ return;
+ }
+ if constexpr ((Bits == 64) && (WORD_SIZE == 32)) {
+ // Use builtin 64 bits for 32-bit base type if available;
+ if (s >= 64) {
+ val[0] = 0;
+ val[1] = 0;
+ return;
+ }
+ uint64_t tmp = uint64__t(val[0]) + (uint64_t(val[1]) << 62);
+ tmp <<= s;
+ val[0] = uint32_t(tmp);
+ val[1] = uint32_t(tmp >> 32);
+ return;
+ }
+#ifdef LIBC_TYPES_HAS_INT128
+ if constexpr ((Bits == 128) && (WORD_SIZE == 64)) {
+ // Use builtin 128 bits if available;
+ if (s >= 128) {
+ val[0] = 0;
+ val[1] = 0;
+ return;
+ }
+ __uint128_t tmp = __uint128_t(val[0]) + (__uint128_t(val[1]) << 64);
+ tmp <<= s;
+ val[0] = uint64_t(tmp);
+ val[1] = uint64_t(tmp >> 64);
+ return;
+ }
+#endif // LIBC_TYPES_HAS_INT128
+ if (LIBC_UNLIKELY(s == 0))
+ return;
+
+ const size_t drop = s / WORD_SIZE; // Number of words to drop
+ const size_t shift = s % WORD_SIZE; // Bits to shift in the remaining words.
+ size_t i = WORD_COUNT;
+
+ if (drop < WORD_COUNT) {
+ i = WORD_COUNT - 1;
+ if (shift > 0) {
+ for (size_t j = WORD_COUNT - 1 - drop; j > 0; --i, --j) {
+ val[i] = (val[j] << shift) | (val[j - 1] >> (WORD_SIZE - shift));
+ }
+ val[i] = val[0] << shift;
+ } else {
+ for (size_t j = WORD_COUNT - 1 - drop; j > 0; --i, --j) {
+ val[i] = val[j];
+ }
+ val[i] = val[0];
+ }
+ }
+
+ for (size_t j = 0; j < i; ++j) {
+ val[j] = 0;
+ }
}
LIBC_INLINE constexpr BigInt operator<<(size_t s) const {
- return BigInt(multiword::shift<multiword::LEFT, SIGNED>(val, s));
+ BigInt result(*this);
+ result.shift_left(s);
+ return result;
}
- LIBC_INLINE constexpr BigInt &operator>>=(size_t s) {
- val = multiword::shift<multiword::RIGHT, SIGNED>(val, s);
+ LIBC_INLINE constexpr BigInt &operator<<=(size_t s) {
+ shift_left(s);
return *this;
}
+ LIBC_INLINE constexpr void shift_right(size_t s) {
+ if constexpr ((Bits == 64) && (WORD_SIZE == 32)) {
+ // Use builtin 64 bits if available;
+ if (s >= 64) {
+ val[0] = 0;
+ val[1] = 0;
+ return;
+ }
+ uint64_t tmp = uint64_t(val[0]) + (uint64_t(val[1]) << 32);
+ if constexpr (Signed) {
+ tmp = static_cast<uint64_t>(static_cast<int64_t>(tmp) >> s);
+ } else {
+ tmp >>= s;
+ }
+ val[0] = uint32_t(tmp);
+ val[1] = uint32_t(tmp >> 32);
+ return;
+ }
+#ifdef LIBC_TYPES_HAS_INT128
+ if constexpr ((Bits == 128) && (WORD_SIZE == 64)) {
+ // Use builtin 128 bits if available;
+ if (s >= 128) {
+ val[0] = 0;
+ val[1] = 0;
+ return;
+ }
+ __uint128_t tmp = __uint128_t(val[0]) + (__uint128_t(val[1]) << 64);
+ if constexpr (Signed) {
+ tmp = static_cast<__uint128_t>(static_cast<__int128_t>(tmp) >> s);
+ } else {
+ tmp >>= s;
+ }
+ val[0] = uint64_t(tmp);
+ val[1] = uint64_t(tmp >> 64);
+ return;
+ }
+#endif // LIBC_TYPES_HAS_INT128
+
+ if (LIBC_UNLIKELY(s == 0))
+ return;
+ const size_t drop = s / WORD_SIZE; // Number of words to drop
+ const size_t shift = s % WORD_SIZE; // Bit shift in the remaining words.
+
+ size_t i = 0;
+ WordType sign = Signed ? is_neg() : 0;
+
+ if (drop < WORD_COUNT) {
+ if (shift > 0) {
+ for (size_t j = drop; j < WORD_COUNT - 1; ++i, ++j) {
+ val[i] = (val[j] >> shift) | (val[j + 1] << (WORD_SIZE - shift));
+ }
+ if constexpr (Signed) {
+ val[i] = static_cast<WordType>(
+ static_cast<cpp::make_signed_t<WordType>>(val[WORD_COUNT - 1]) >>
+ shift);
+ } else {
+ val[i] = val[WORD_COUNT - 1] >> shift;
+ }
+ ++i;
+ } else {
+ for (size_t j = drop; j < WORD_COUNT; ++i, ++j) {
+ val[i] = val[j];
+ }
+ }
+ }
+
+ for (; i < WORD_COUNT; ++i) {
+ val[i] = sign;
+ }
+ }
+
LIBC_INLINE constexpr BigInt operator>>(size_t s) const {
- return BigInt(multiword::shift<multiword::RIGHT, SIGNED>(val, s));
+ BigInt result(*this);
+ result.shift_right(s);
+ return result;
+ }
+
+ LIBC_INLINE constexpr BigInt &operator>>=(size_t s) {
+ shift_right(s);
+ return *this;
}
#define DEFINE_BINOP(OP) \
@@ -765,9 +833,10 @@ struct BigInt {
return lhs; \
}
- DEFINE_BINOP(&) // & and &=
- DEFINE_BINOP(|) // | and |=
- DEFINE_BINOP(^) // ^ and ^=
+ DEFINE_BINOP(&)
+ DEFINE_BINOP(|)
+ DEFINE_BINOP(^)
+
#undef DEFINE_BINOP
LIBC_INLINE constexpr BigInt operator~() const {
@@ -778,8 +847,8 @@ struct BigInt {
}
LIBC_INLINE constexpr BigInt operator-() const {
- BigInt result(*this);
- result.negate();
+ BigInt result = ~(*this);
+ result.add(BigInt(1));
return result;
}
@@ -796,6 +865,24 @@ struct BigInt {
return !(lhs == rhs);
}
+private:
+ LIBC_INLINE friend constexpr int cmp(const BigInt &lhs, const BigInt &rhs) {
+ const auto compare = [](WordType a, WordType b) {
+ return a == b ? 0 : a > b ? 1 : -1;
+ };
+ if constexpr (Signed) {
+ const bool lhs_is_neg = lhs.is_neg();
+ const bool rhs_is_neg = rhs.is_neg();
+ if (lhs_is_neg != rhs_is_neg)
+ return rhs_is_neg ? 1 : -1;
+ }
+ for (size_t i = WORD_COUNT; i-- > 0;)
+ if (auto cmp = compare(lhs[i], rhs[i]); cmp != 0)
+ return cmp;
+ return 0;
+ }
+
+public:
LIBC_INLINE friend constexpr bool operator>(const BigInt &lhs,
const BigInt &rhs) {
return cmp(lhs, rhs) > 0;
@@ -814,24 +901,24 @@ struct BigInt {
}
LIBC_INLINE constexpr BigInt &operator++() {
- increment();
+ add(BigInt(1));
return *this;
}
LIBC_INLINE constexpr BigInt operator++(int) {
BigInt oldval(*this);
- increment();
+ add(BigInt(1));
return oldval;
}
LIBC_INLINE constexpr BigInt &operator--() {
- decrement();
+ sub(BigInt(1));
return *this;
}
LIBC_INLINE constexpr BigInt operator--(int) {
BigInt oldval(*this);
- decrement();
+ sub(BigInt(1));
return oldval;
}
@@ -843,117 +930,9 @@ struct BigInt {
// Return the i-th word of the number.
LIBC_INLINE constexpr WordType &operator[](size_t i) { return val[i]; }
-private:
- LIBC_INLINE friend constexpr int cmp(const BigInt &lhs, const BigInt &rhs) {
- constexpr auto compare = [](WordType a, WordType b) {
- return a == b ? 0 : a > b ? 1 : -1;
- };
- if constexpr (Signed) {
- const bool lhs_is_neg = lhs.is_neg();
- const bool rhs_is_neg = rhs.is_neg();
- if (lhs_is_neg != rhs_is_neg)
- return rhs_is_neg ? 1 : -1;
- }
- for (size_t i = WORD_COUNT; i-- > 0;)
- if (auto cmp = compare(lhs[i], rhs[i]); cmp != 0)
- return cmp;
- return 0;
- }
-
- LIBC_INLINE constexpr void bitwise_not() {
- for (auto &part : val)
- part = ~part;
- }
-
- LIBC_INLINE constexpr void negate() {
- bitwise_not();
- increment();
- }
+ LIBC_INLINE WordType *data() { return val; }
- LIBC_INLINE constexpr void increment() {
- multiword::add_with_carry(val, cpp::array<WordType, 1>{1});
- }
-
- LIBC_INLINE constexpr void decrement() {
- multiword::add_with_carry(val, cpp::array<WordType, 1>{1});
- }
-
- LIBC_INLINE constexpr void extend(size_t index, bool is_neg) {
- const WordType value = is_neg ? cpp::numeric_limits<WordType>::max()
- : cpp::numeric_limits<WordType>::min();
- for (size_t i = index; i < WORD_COUNT; ++i)
- val[i] = value;
- }
-
- LIBC_INLINE constexpr bool get_msb() const {
- return val.back() >> (WORD_SIZE - 1);
- }
-
- LIBC_INLINE constexpr void set_msb() {
- val.back() |= mask_leading_ones<WordType, 1>();
- }
-
- LIBC_INLINE constexpr void clear_msb() {
- val.back() &= mask_trailing_ones<WordType, WORD_SIZE - 1>();
- }
-
- LIBC_INLINE constexpr void set_bit(size_t i) {
- const size_t word_index = i / WORD_SIZE;
- val[word_index] |= WordType(1) << (i % WORD_SIZE);
- }
-
- LIBC_INLINE constexpr static Division divide_unsigned(const BigInt ÷nd,
- const BigInt ÷r) {
- BigInt remainder = dividend;
- BigInt quotient;
- if (remainder >= divider) {
- BigInt subtractor = divider;
- int cur_bit = multiword::countl_zero(subtractor.val) -
- multiword::countl_zero(remainder.val);
- subtractor <<= cur_bit;
- for (; cur_bit >= 0 && remainder > 0; --cur_bit, subtractor >>= 1) {
- if (remainder < subtractor)
- continue;
- remainder -= subtractor;
- quotient.set_bit(cur_bit);
- }
- }
- return Division{quotient, remainder};
- }
-
- LIBC_INLINE constexpr static Division divide_signed(const BigInt ÷nd,
- const BigInt ÷r) {
- // Special case because it is not possible to negate the min value of a
- // signed integer.
- if (dividend == min() && divider == min())
- return Division{one(), zero()};
- // 1. Convert the dividend and divisor to unsigned representation.
- unsigned_type udividend(dividend);
- unsigned_type udivider(divider);
- // 2. Negate the dividend if it's negative, and similarly for the divisor.
- const bool dividend_is_neg = dividend.is_neg();
- const bool divider_is_neg = divider.is_neg();
- if (dividend_is_neg)
- udividend.negate();
- if (divider_is_neg)
- udivider.negate();
- // 3. Use unsigned multiword division algorithm.
- const auto unsigned_result = divide_unsigned(udividend, udivider);
- // 4. Convert the quotient and remainder to signed representation.
- Division result;
- result.quotient = signed_type(unsigned_result.quotient);
- result.remainder = signed_type(unsigned_result.remainder);
- // 5. Negate the quotient if the dividend and divisor had opposite signs.
- if (dividend_is_neg != divider_is_neg)
- result.quotient.negate();
- // 6. Negate the remainder if the dividend was negative.
- if (dividend_is_neg)
- result.remainder.negate();
- return result;
- }
-
- friend signed_type;
- friend unsigned_type;
+ LIBC_INLINE const WordType *data() const { return val; }
};
namespace internal {
@@ -983,8 +962,10 @@ using Int = BigInt<Bits, true, internal::WordTypeSelectorT<Bits>>;
// Provides limits of U/Int<128>.
template <> class cpp::numeric_limits<UInt<128>> {
public:
- LIBC_INLINE static constexpr UInt<128> max() { return UInt<128>::max(); }
- LIBC_INLINE static constexpr UInt<128> min() { return UInt<128>::min(); }
+ LIBC_INLINE static constexpr UInt<128> max() {
+ return UInt<128>({0xffff'ffff'ffff'ffff, 0xffff'ffff'ffff'ffff});
+ }
+ LIBC_INLINE static constexpr UInt<128> min() { return UInt<128>(0); }
// Meant to match std::numeric_limits interface.
// NOLINTNEXTLINE(readability-identifier-naming)
LIBC_INLINE_VAR static constexpr int digits = 128;
@@ -992,8 +973,12 @@ template <> class cpp::numeric_limits<UInt<128>> {
template <> class cpp::numeric_limits<Int<128>> {
public:
- LIBC_INLINE static constexpr Int<128> max() { return Int<128>::max(); }
- LIBC_INLINE static constexpr Int<128> min() { return Int<128>::min(); }
+ LIBC_INLINE static constexpr Int<128> max() {
+ return Int<128>({0xffff'ffff'ffff'ffff, 0x7fff'ffff'ffff'ffff});
+ }
+ LIBC_INLINE static constexpr Int<128> min() {
+ return Int<128>({0, 0x8000'0000'0000'0000});
+ }
// Meant to match std::numeric_limits interface.
// NOLINTNEXTLINE(readability-identifier-naming)
LIBC_INLINE_VAR static constexpr int digits = 128;
@@ -1127,28 +1112,30 @@ has_single_bit(T value) {
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
countr_zero(const T &value) {
- return multiword::countr_zero(value.val);
+ return value.ctz();
}
// Specialization of cpp::countl_zero ('bit.h') for BigInt.
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
countl_zero(const T &value) {
- return multiword::countl_zero(value.val);
+ return value.clz();
}
// Specialization of cpp::countl_one ('bit.h') for BigInt.
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
countl_one(T value) {
- return multiword::countl_one(value.val);
+ // TODO : Implement a faster version not involving operator~.
+ return cpp::countl_zero<T>(~value);
}
// Specialization of cpp::countr_one ('bit.h') for BigInt.
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
countr_one(T value) {
- return multiword::countr_one(value.val);
+ // TODO : Implement a faster version not involving operator~.
+ return cpp::countr_zero<T>(~value);
}
// Specialization of cpp::bit_width ('bit.h') for BigInt.
@@ -1195,59 +1182,65 @@ rotr(T value, int rotate) {
template <typename T, size_t count>
LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, T>
mask_trailing_ones() {
- static_assert(!T::SIGNED && count <= T::BITS);
- if (count == T::BITS)
- return T::all_ones();
- constexpr size_t QUOTIENT = count / T::WORD_SIZE;
- constexpr size_t REMAINDER = count % T::WORD_SIZE;
- T out; // zero initialized
- for (size_t i = 0; i <= QUOTIENT; ++i)
- out[i] = i < QUOTIENT
- ? -1
- : mask_trailing_ones<typename T::word_type, REMAINDER>();
+ static_assert(!T::SIGNED);
+ if (count == 0)
+ return T();
+ constexpr unsigned T_BITS = CHAR_BIT * sizeof(T);
+ static_assert(count <= T_BITS && "Invalid bit index");
+ using word_type = typename T::word_type;
+ T out;
+ constexpr int CHUNK_INDEX_CONTAINING_BIT =
+ static_cast<int>(count / T::WORD_SIZE);
+ int index = 0;
+ for (auto &word : out.val) {
+ if (index < CHUNK_INDEX_CONTAINING_BIT)
+ word = -1;
+ else if (index > CHUNK_INDEX_CONTAINING_BIT)
+ word = 0;
+ else
+ word = mask_trailing_ones<word_type, count % T::WORD_SIZE>();
+ ++index;
+ }
return out;
}
// Specialization of mask_leading_ones ('math_extras.h') for BigInt.
template <typename T, size_t count>
LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, T> mask_leading_ones() {
- static_assert(!T::SIGNED && count <= T::BITS);
- if (count == T::BITS)
- return T::all_ones();
- constexpr size_t QUOTIENT = (T::BITS - count - 1U) / T::WORD_SIZE;
- constexpr size_t REMAINDER = count % T::WORD_SIZE;
- T out; // zero initialized
- for (size_t i = QUOTIENT; i < T::WORD_COUNT; ++i)
- out[i] = i > QUOTIENT
- ? -1
- : mask_leading_ones<typename T::word_type, REMAINDER>();
+ static_assert(!T::SIGNED);
+ if (count == 0)
+ return T();
+ constexpr unsigned T_BITS = CHAR_BIT * sizeof(T);
+ static_assert(count <= T_BITS && "Invalid bit index");
+ using word_type = typename T::word_type;
+ T out;
+ constexpr int CHUNK_INDEX_CONTAINING_BIT =
+ static_cast<int>((T::BITS - count - 1ULL) / T::WORD_SIZE);
+ int index = 0;
+ for (auto &word : out.val) {
+ if (index < CHUNK_INDEX_CONTAINING_BIT)
+ word = 0;
+ else if (index > CHUNK_INDEX_CONTAINING_BIT)
+ word = -1;
+ else
+ word = mask_leading_ones<word_type, count % T::WORD_SIZE>();
+ ++index;
+ }
return out;
}
-// Specialization of mask_trailing_zeros ('math_extras.h') for BigInt.
-template <typename T, size_t count>
-LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, T>
-mask_trailing_zeros() {
- return mask_leading_ones<T, T::BITS - count>();
-}
-
-// Specialization of mask_leading_zeros ('math_extras.h') for BigInt.
-template <typename T, size_t count>
-LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, T>
-mask_leading_zeros() {
- return mask_trailing_ones<T, T::BITS - count>();
-}
-
// Specialization of count_zeros ('math_extras.h') for BigInt.
template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
+[[nodiscard]]
+LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
count_zeros(T value) {
return cpp::popcount(~value);
}
// Specialization of first_leading_zero ('math_extras.h') for BigInt.
template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
+[[nodiscard]]
+LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
first_leading_zero(T value) {
return value == cpp::numeric_limits<T>::max() ? 0
: cpp::countl_one(value) + 1;
@@ -1255,14 +1248,16 @@ first_leading_zero(T value) {
// Specialization of first_leading_one ('math_extras.h') for BigInt.
template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
+[[nodiscard]]
+LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
first_leading_one(T value) {
return first_leading_zero(~value);
}
// Specialization of first_trailing_zero ('math_extras.h') for BigInt.
template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
+[[nodiscard]]
+LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
first_trailing_zero(T value) {
return value == cpp::numeric_limits<T>::max() ? 0
: cpp::countr_zero(~value) + 1;
@@ -1270,7 +1265,8 @@ first_trailing_zero(T value) {
// Specialization of first_trailing_one ('math_extras.h') for BigInt.
template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
+[[nodiscard]]
+LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
first_trailing_one(T value) {
return value == cpp::numeric_limits<T>::max() ? 0
: cpp::countr_zero(value) + 1;
diff --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h
index 4c59cfd99c2e6c..1287c3e9a84fac 100644
--- a/libc/src/__support/float_to_string.h
+++ b/libc/src/__support/float_to_string.h
@@ -689,7 +689,7 @@ template <> class FloatToString<long double> {
wide_int float_as_int = mantissa;
- float_as_int <<= exponent;
+ float_as_int.shift_left(exponent);
int_block_index = 0;
while (float_as_int > 0) {
@@ -708,11 +708,10 @@ template <> class FloatToString<long double> {
const int SHIFT_AMOUNT = FLOAT_AS_INT_WIDTH + exponent;
static_assert(EXTRA_INT_WIDTH >= sizeof(long double) * 8);
- float_as_fixed <<= SHIFT_AMOUNT;
+ float_as_fixed.shift_left(SHIFT_AMOUNT);
// If there are still digits above the decimal point, handle those.
- if (cpp::countl_zero(float_as_fixed) <
- static_cast<int>(EXTRA_INT_WIDTH)) {
+ if (float_as_fixed.clz() < static_cast<int>(EXTRA_INT_WIDTH)) {
UInt<EXTRA_INT_WIDTH> above_decimal_point =
float_as_fixed >> FLOAT_AS_INT_WIDTH;
diff --git a/libc/src/__support/integer_literals.h b/libc/src/__support/integer_literals.h
index e99799c3512e2f..de1f88fbd3f3f5 100644
--- a/libc/src/__support/integer_literals.h
+++ b/libc/src/__support/integer_literals.h
@@ -151,15 +151,12 @@ template <size_t N> struct Parser<LIBC_NAMESPACE::UInt<N>> {
template <typename T>
LIBC_INLINE constexpr T parse_with_prefix(const char *ptr) {
using P = Parser<T>;
- if (ptr == nullptr)
- return T();
- if (ptr[0] == '0') {
- if (ptr[1] == 'b')
- return P::template parse<2>(ptr + 2);
- if (ptr[1] == 'x')
- return P::template parse<16>(ptr + 2);
- }
- return P::template parse<10>(ptr);
+ if (ptr[0] == '0' && ptr[1] == 'x')
+ return P::template parse<16>(ptr + 2);
+ else if (ptr[0] == '0' && ptr[1] == 'b')
+ return P::template parse<2>(ptr + 2);
+ else
+ return P::template parse<10>(ptr);
}
} // namespace internal
@@ -172,16 +169,6 @@ LIBC_INLINE constexpr auto operator""_u256(const char *x) {
return internal::parse_with_prefix<UInt<256>>(x);
}
-template <typename T> LIBC_INLINE constexpr T parse_bigint(const char *ptr) {
- if (ptr == nullptr)
- return T();
- if (ptr[0] == '-' || ptr[0] == '+') {
- auto positive = internal::parse_with_prefix<T>(ptr + 1);
- return ptr[0] == '-' ? -positive : positive;
- }
- return internal::parse_with_prefix<T>(ptr);
-}
-
} // namespace LIBC_NAMESPACE
#endif // LLVM_LIBC_SRC___SUPPORT_INTEGER_LITERALS_H
diff --git a/libc/src/__support/math_extras.h b/libc/src/__support/math_extras.h
index bb6424bfa8e4d4..70a8800b285d02 100644
--- a/libc/src/__support/math_extras.h
+++ b/libc/src/__support/math_extras.h
@@ -10,9 +10,9 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXTRAS_H
#define LLVM_LIBC_SRC___SUPPORT_MATH_EXTRAS_H
-#include "src/__support/CPP/bit.h" // countl_one, countr_zero
-#include "src/__support/CPP/limits.h" // CHAR_BIT, numeric_limits
-#include "src/__support/CPP/type_traits.h" // is_unsigned_v, is_constant_evaluated
+#include "src/__support/CPP/bit.h" // countl_one, countr_zero
+#include "src/__support/CPP/limits.h" // CHAR_BIT, numeric_limits
+#include "src/__support/CPP/type_traits.h" // is_unsigned_v
#include "src/__support/macros/attributes.h" // LIBC_INLINE
namespace LIBC_NAMESPACE {
@@ -32,94 +32,199 @@ mask_trailing_ones() {
template <typename T, size_t count>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
mask_leading_ones() {
- return T(~mask_trailing_ones<T, CHAR_BIT * sizeof(T) - count>());
+ constexpr T MASK(mask_trailing_ones<T, CHAR_BIT * sizeof(T) - count>());
+ return T(~MASK); // bitwise NOT performs integer promotion.
}
-// Create a bitmask with the count right-most bits set to 0, and all other bits
-// set to 1. Only unsigned types are allowed.
-template <typename T, size_t count>
-LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-mask_trailing_zeros() {
- return mask_leading_ones<T, CHAR_BIT * sizeof(T) - count>();
+// Add with carry
+template <typename T> struct SumCarry {
+ T sum;
+ T carry;
+};
+
+// This version is always valid for constexpr.
+template <typename T>
+LIBC_INLINE constexpr cpp::enable_if_t<
+ cpp::is_integral_v<T> && cpp::is_unsigned_v<T>, SumCarry<T>>
+add_with_carry_const(T a, T b, T carry_in) {
+ T tmp = a + carry_in;
+ T sum = b + tmp;
+ T carry_out = (sum < b) + (tmp < a);
+ return {sum, carry_out};
}
-// Create a bitmask with the count left-most bits set to 0, and all other bits
-// set to 1. Only unsigned types are allowed.
-template <typename T, size_t count>
-LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-mask_leading_zeros() {
- return mask_trailing_ones<T, CHAR_BIT * sizeof(T) - count>();
+template <typename T>
+LIBC_INLINE constexpr cpp::enable_if_t<
+ cpp::is_integral_v<T> && cpp::is_unsigned_v<T>, SumCarry<T>>
+add_with_carry(T a, T b, T carry_in) {
+ return add_with_carry_const<T>(a, b, carry_in);
+}
+
+#if __has_builtin(__builtin_addc)
+// https://clang.llvm.org/docs/LanguageExtensions.html#multiprecision-arithmetic-builtins
+
+template <>
+LIBC_INLINE constexpr SumCarry<unsigned char>
+add_with_carry<unsigned char>(unsigned char a, unsigned char b,
+ unsigned char carry_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return add_with_carry_const<unsigned char>(a, b, carry_in);
+ } else {
+ SumCarry<unsigned char> result{0, 0};
+ result.sum = __builtin_addcb(a, b, carry_in, &result.carry);
+ return result;
+ }
+}
+
+template <>
+LIBC_INLINE constexpr SumCarry<unsigned short>
+add_with_carry<unsigned short>(unsigned short a, unsigned short b,
+ unsigned short carry_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return add_with_carry_const<unsigned short>(a, b, carry_in);
+ } else {
+ SumCarry<unsigned short> result{0, 0};
+ result.sum = __builtin_addcs(a, b, carry_in, &result.carry);
+ return result;
+ }
+}
+
+template <>
+LIBC_INLINE constexpr SumCarry<unsigned int>
+add_with_carry<unsigned int>(unsigned int a, unsigned int b,
+ unsigned int carry_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return add_with_carry_const<unsigned int>(a, b, carry_in);
+ } else {
+ SumCarry<unsigned int> result{0, 0};
+ result.sum = __builtin_addc(a, b, carry_in, &result.carry);
+ return result;
+ }
+}
+
+template <>
+LIBC_INLINE constexpr SumCarry<unsigned long>
+add_with_carry<unsigned long>(unsigned long a, unsigned long b,
+ unsigned long carry_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return add_with_carry_const<unsigned long>(a, b, carry_in);
+ } else {
+ SumCarry<unsigned long> result{0, 0};
+ result.sum = __builtin_addcl(a, b, carry_in, &result.carry);
+ return result;
+ }
+}
+
+template <>
+LIBC_INLINE constexpr SumCarry<unsigned long long>
+add_with_carry<unsigned long long>(unsigned long long a, unsigned long long b,
+ unsigned long long carry_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return add_with_carry_const<unsigned long long>(a, b, carry_in);
+ } else {
+ SumCarry<unsigned long long> result{0, 0};
+ result.sum = __builtin_addcll(a, b, carry_in, &result.carry);
+ return result;
+ }
}
-// Returns whether 'a + b' overflows, the result is stored in 'res'.
+#endif // __has_builtin(__builtin_addc)
+
+// Subtract with borrow
+template <typename T> struct DiffBorrow {
+ T
diff ;
+ T borrow;
+};
+
+// This version is always valid for constexpr.
template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr bool add_overflow(T a, T b, T &res) {
- return __builtin_add_overflow(a, b, &res);
+LIBC_INLINE constexpr cpp::enable_if_t<
+ cpp::is_integral_v<T> && cpp::is_unsigned_v<T>, DiffBorrow<T>>
+sub_with_borrow_const(T a, T b, T borrow_in) {
+ T tmp = a - b;
+ T
diff = tmp - borrow_in;
+ T borrow_out = (
diff > tmp) + (tmp > a);
+ return {
diff , borrow_out};
}
-// Returns whether 'a - b' overflows, the result is stored in 'res'.
+// This version is not always valid for constepxr because it's overriden below
+// if builtins are available.
template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr bool sub_overflow(T a, T b, T &res) {
- return __builtin_sub_overflow(a, b, &res);
+LIBC_INLINE constexpr cpp::enable_if_t<
+ cpp::is_integral_v<T> && cpp::is_unsigned_v<T>, DiffBorrow<T>>
+sub_with_borrow(T a, T b, T borrow_in) {
+ return sub_with_borrow_const<T>(a, b, borrow_in);
}
-#define RETURN_IF(TYPE, BUILTIN) \
- if constexpr (cpp::is_same_v<T, TYPE>) \
- return BUILTIN(a, b, carry_in, carry_out);
+#if __has_builtin(__builtin_subc)
+// https://clang.llvm.org/docs/LanguageExtensions.html#multiprecision-arithmetic-builtins
-// Returns the result of 'a + b' taking into account 'carry_in'.
-// The carry out is stored in 'carry_out' it not 'nullptr', dropped otherwise.
-// We keep the pass by pointer interface for consistency with the intrinsic.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-add_with_carry(T a, T b, T carry_in, T &carry_out) {
- if constexpr (!cpp::is_constant_evaluated()) {
-#if __has_builtin(__builtin_addcb)
- RETURN_IF(unsigned char, __builtin_addcb)
-#elif __has_builtin(__builtin_addcs)
- RETURN_IF(unsigned short, __builtin_addcs)
-#elif __has_builtin(__builtin_addc)
- RETURN_IF(unsigned int, __builtin_addc)
-#elif __has_builtin(__builtin_addcl)
- RETURN_IF(unsigned long, __builtin_addcl)
-#elif __has_builtin(__builtin_addcll)
- RETURN_IF(unsigned long long, __builtin_addcll)
-#endif
+template <>
+LIBC_INLINE constexpr DiffBorrow<unsigned char>
+sub_with_borrow<unsigned char>(unsigned char a, unsigned char b,
+ unsigned char borrow_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return sub_with_borrow_const<unsigned char>(a, b, borrow_in);
+ } else {
+ DiffBorrow<unsigned char> result{0, 0};
+ result.
diff = __builtin_subcb(a, b, borrow_in, &result.borrow);
+ return result;
}
- T sum;
- T carry1 = add_overflow(a, b, sum);
- T carry2 = add_overflow(sum, carry_in, sum);
- carry_out = carry1 | carry2;
- return sum;
}
-// Returns the result of 'a - b' taking into account 'carry_in'.
-// The carry out is stored in 'carry_out' it not 'nullptr', dropped otherwise.
-// We keep the pass by pointer interface for consistency with the intrinsic.
-template <typename T>
-[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
-sub_with_borrow(T a, T b, T carry_in, T &carry_out) {
- if constexpr (!cpp::is_constant_evaluated()) {
-#if __has_builtin(__builtin_subcb)
- RETURN_IF(unsigned char, __builtin_subcb)
-#elif __has_builtin(__builtin_subcs)
- RETURN_IF(unsigned short, __builtin_subcs)
-#elif __has_builtin(__builtin_subc)
- RETURN_IF(unsigned int, __builtin_subc)
-#elif __has_builtin(__builtin_subcl)
- RETURN_IF(unsigned long, __builtin_subcl)
-#elif __has_builtin(__builtin_subcll)
- RETURN_IF(unsigned long long, __builtin_subcll)
-#endif
+template <>
+LIBC_INLINE constexpr DiffBorrow<unsigned short>
+sub_with_borrow<unsigned short>(unsigned short a, unsigned short b,
+ unsigned short borrow_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return sub_with_borrow_const<unsigned short>(a, b, borrow_in);
+ } else {
+ DiffBorrow<unsigned short> result{0, 0};
+ result.
diff = __builtin_subcs(a, b, borrow_in, &result.borrow);
+ return result;
+ }
+}
+
+template <>
+LIBC_INLINE constexpr DiffBorrow<unsigned int>
+sub_with_borrow<unsigned int>(unsigned int a, unsigned int b,
+ unsigned int borrow_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return sub_with_borrow_const<unsigned int>(a, b, borrow_in);
+ } else {
+ DiffBorrow<unsigned int> result{0, 0};
+ result.
diff = __builtin_subc(a, b, borrow_in, &result.borrow);
+ return result;
+ }
+}
+
+template <>
+LIBC_INLINE constexpr DiffBorrow<unsigned long>
+sub_with_borrow<unsigned long>(unsigned long a, unsigned long b,
+ unsigned long borrow_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return sub_with_borrow_const<unsigned long>(a, b, borrow_in);
+ } else {
+ DiffBorrow<unsigned long> result{0, 0};
+ result.
diff = __builtin_subcl(a, b, borrow_in, &result.borrow);
+ return result;
+ }
+}
+
+template <>
+LIBC_INLINE constexpr DiffBorrow<unsigned long long>
+sub_with_borrow<unsigned long long>(unsigned long long a, unsigned long long b,
+ unsigned long long borrow_in) {
+ if (__builtin_is_constant_evaluated()) {
+ return sub_with_borrow_const<unsigned long long>(a, b, borrow_in);
+ } else {
+ DiffBorrow<unsigned long long> result{0, 0};
+ result.
diff = __builtin_subcll(a, b, borrow_in, &result.borrow);
+ return result;
}
- T sub;
- T carry1 = sub_overflow(a, b, sub);
- T carry2 = sub_overflow(sub, carry_in, sub);
- carry_out = carry1 | carry2;
- return sub;
}
-#undef RETURN_IF
+#endif // __has_builtin(__builtin_subc)
template <typename T>
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
diff --git a/libc/src/__support/number_pair.h b/libc/src/__support/number_pair.h
index 2f713fc03520f9..ee6667b1299fe8 100644
--- a/libc/src/__support/number_pair.h
+++ b/libc/src/__support/number_pair.h
@@ -20,6 +20,17 @@ template <typename T> struct NumberPair {
T hi = T(0);
};
+template <typename T>
+cpp::enable_if_t<cpp::is_integral_v<T> && cpp::is_unsigned_v<T>,
+ NumberPair<T>> constexpr split(T a) {
+ constexpr size_t HALF_BIT_WIDTH = sizeof(T) * 4;
+ constexpr T LOWER_HALF_MASK = (T(1) << HALF_BIT_WIDTH) - T(1);
+ NumberPair<T> result;
+ result.lo = a & LOWER_HALF_MASK;
+ result.hi = a >> HALF_BIT_WIDTH;
+ return result;
+}
+
} // namespace LIBC_NAMESPACE
#endif // LLVM_LIBC_SRC___SUPPORT_NUMBER_PAIR_H
diff --git a/libc/test/src/__support/integer_literals_test.cpp b/libc/test/src/__support/integer_literals_test.cpp
index cbc906aa7c99ad..5298cf30156e20 100644
--- a/libc/test/src/__support/integer_literals_test.cpp
+++ b/libc/test/src/__support/integer_literals_test.cpp
@@ -133,24 +133,3 @@ TEST(LlvmLibcIntegerLiteralTest, u256) {
U256_MAX,
0xFFFFFFFF'FFFFFFFF'FFFFFFFF'FFFFFFFF'FFFFFFFF'FFFFFFFF'FFFFFFFF'FFFFFFFF_u256);
}
-
-TEST(LlvmLibcIntegerLiteralTest, parse_bigint) {
- using T = LIBC_NAMESPACE::Int<128>;
- struct {
- const char *str;
- T expected;
- } constexpr TEST_CASES[] = {
- {"0", 0}, {"-1", -1}, {"+1", 1}, {"-0xFF", -255}, {"-0b11", -3},
- };
- for (auto tc : TEST_CASES) {
- T actual = LIBC_NAMESPACE::parse_bigint<T>(tc.str);
- EXPECT_EQ(actual, tc.expected);
- }
-}
-
-TEST(LlvmLibcIntegerLiteralTest, parse_bigint_invalid) {
- using T = LIBC_NAMESPACE::Int<128>;
- const T expected; // default construction
- EXPECT_EQ(LIBC_NAMESPACE::parse_bigint<T>(nullptr), expected);
- EXPECT_EQ(LIBC_NAMESPACE::parse_bigint<T>(""), expected);
-}
diff --git a/libc/test/src/__support/math_extras_test.cpp b/libc/test/src/__support/math_extras_test.cpp
index 401e631ea4bac1..e88b3e1d6b687b 100644
--- a/libc/test/src/__support/math_extras_test.cpp
+++ b/libc/test/src/__support/math_extras_test.cpp
@@ -101,61 +101,4 @@ TYPED_TEST(LlvmLibcBitTest, CountZeros, UnsignedTypesNoBigInt) {
EXPECT_EQ(count_zeros<T>(cpp::numeric_limits<T>::max() >> i), i);
}
-using UnsignedTypes = testing::TypeList<
-#if defined(__SIZEOF_INT128__)
- __uint128_t,
-#endif
- unsigned char, unsigned short, unsigned int, unsigned long,
- unsigned long long>;
-
-TYPED_TEST(LlvmLibcBlockMathExtrasTest, add_overflow, UnsignedTypes) {
- constexpr T ZERO = cpp::numeric_limits<T>::min();
- constexpr T ONE(1);
- constexpr T MAX = cpp::numeric_limits<T>::max();
- constexpr T BEFORE_MAX = MAX - 1;
-
- const struct {
- T lhs;
- T rhs;
- T sum;
- bool carry;
- } TESTS[] = {
- {ZERO, ONE, ONE, false}, // 0x00 + 0x01 = 0x01
- {BEFORE_MAX, ONE, MAX, false}, // 0xFE + 0x01 = 0xFF
- {MAX, ONE, ZERO, true}, // 0xFF + 0x01 = 0x00 (carry)
- {MAX, MAX, BEFORE_MAX, true}, // 0xFF + 0xFF = 0xFE (carry)
- };
- for (auto tc : TESTS) {
- T sum;
- bool carry = add_overflow<T>(tc.lhs, tc.rhs, sum);
- EXPECT_EQ(sum, tc.sum);
- EXPECT_EQ(carry, tc.carry);
- }
-}
-
-TYPED_TEST(LlvmLibcBlockMathExtrasTest, sub_overflow, UnsignedTypes) {
- constexpr T ZERO = cpp::numeric_limits<T>::min();
- constexpr T ONE(1);
- constexpr T MAX = cpp::numeric_limits<T>::max();
- constexpr T BEFORE_MAX = MAX - 1;
-
- const struct {
- T lhs;
- T rhs;
- T sub;
- bool carry;
- } TESTS[] = {
- {ONE, ZERO, ONE, false}, // 0x01 - 0x00 = 0x01
- {MAX, MAX, ZERO, false}, // 0xFF - 0xFF = 0x00
- {ZERO, ONE, MAX, true}, // 0x00 - 0x01 = 0xFF (carry)
- {BEFORE_MAX, MAX, MAX, true}, // 0xFE - 0xFF = 0xFF (carry)
- };
- for (auto tc : TESTS) {
- T sub;
- bool carry = sub_overflow<T>(tc.lhs, tc.rhs, sub);
- EXPECT_EQ(sub, tc.sub);
- EXPECT_EQ(carry, tc.carry);
- }
-}
-
} // namespace LIBC_NAMESPACE
diff --git a/libc/test/src/__support/uint_test.cpp b/libc/test/src/__support/uint_test.cpp
index 5696e54c73f363..5764324ca28815 100644
--- a/libc/test/src/__support/uint_test.cpp
+++ b/libc/test/src/__support/uint_test.cpp
@@ -8,7 +8,6 @@
#include "src/__support/CPP/optional.h"
#include "src/__support/UInt.h"
-#include "src/__support/integer_literals.h" // parse_unsigned_bigint
#include "src/__support/macros/properties/types.h" // LIBC_TYPES_HAS_INT128
#include "include/llvm-libc-macros/math-macros.h" // HUGE_VALF, HUGE_VALF
@@ -16,195 +15,6 @@
namespace LIBC_NAMESPACE {
-enum Value { ZERO, ONE, TWO, MIN, MAX };
-
-template <typename T> auto create(Value value) {
- switch (value) {
- case ZERO:
- return T(0);
- case ONE:
- return T(1);
- case TWO:
- return T(2);
- case MIN:
- return T::min();
- case MAX:
- return T::max();
- }
-}
-
-using Types = testing::TypeList< //
-#ifdef LIBC_TYPES_HAS_INT64
- BigInt<64, false, uint64_t>, // 64-bits unsigned (1 x uint64_t)
- BigInt<64, true, uint64_t>, // 64-bits signed (1 x uint64_t)
-#endif
-#ifdef LIBC_TYPES_HAS_INT128
- BigInt<128, false, __uint128_t>, // 128-bits unsigned (1 x __uint128_t)
- BigInt<128, true, __uint128_t>, // 128-bits signed (1 x __uint128_t)
-#endif
- BigInt<16, false, uint16_t>, // 16-bits unsigned (1 x uint16_t)
- BigInt<16, true, uint16_t>, // 16-bits signed (1 x uint16_t)
- BigInt<64, false, uint16_t>, // 64-bits unsigned (4 x uint16_t)
- BigInt<64, true, uint16_t> // 64-bits signed (4 x uint16_t)
- >;
-
-#define ASSERT_SAME(A, B) ASSERT_TRUE((A) == (B))
-
-TYPED_TEST(LlvmLibcUIntClassTest, Additions, Types) {
- ASSERT_SAME(create<T>(ZERO) + create<T>(ZERO), create<T>(ZERO));
- ASSERT_SAME(create<T>(ONE) + create<T>(ZERO), create<T>(ONE));
- ASSERT_SAME(create<T>(ZERO) + create<T>(ONE), create<T>(ONE));
- ASSERT_SAME(create<T>(ONE) + create<T>(ONE), create<T>(TWO));
- // 2's complement addition works for signed and unsigned types.
- // - unsigned : 0xff + 0x01 = 0x00 (255 + 1 = 0)
- // - signed : 0xef + 0x01 = 0xf0 (127 + 1 = -128)
- ASSERT_SAME(create<T>(MAX) + create<T>(ONE), create<T>(MIN));
-}
-
-TYPED_TEST(LlvmLibcUIntClassTest, Subtraction, Types) {
- ASSERT_SAME(create<T>(ZERO) - create<T>(ZERO), create<T>(ZERO));
- ASSERT_SAME(create<T>(ONE) - create<T>(ONE), create<T>(ZERO));
- ASSERT_SAME(create<T>(ONE) - create<T>(ZERO), create<T>(ONE));
- // 2's complement subtraction works for signed and unsigned types.
- // - unsigned : 0x00 - 0x01 = 0xff ( 0 - 1 = 255)
- // - signed : 0xf0 - 0x01 = 0xef (-128 - 1 = 127)
- ASSERT_SAME(create<T>(MIN) - create<T>(ONE), create<T>(MAX));
-}
-
-TYPED_TEST(LlvmLibcUIntClassTest, Multiplication, Types) {
- ASSERT_SAME(create<T>(ZERO) * create<T>(ZERO), create<T>(ZERO));
- ASSERT_SAME(create<T>(ZERO) * create<T>(ONE), create<T>(ZERO));
- ASSERT_SAME(create<T>(ONE) * create<T>(ZERO), create<T>(ZERO));
- ASSERT_SAME(create<T>(ONE) * create<T>(ONE), create<T>(ONE));
- ASSERT_SAME(create<T>(ONE) * create<T>(TWO), create<T>(TWO));
- ASSERT_SAME(create<T>(TWO) * create<T>(ONE), create<T>(TWO));
- // - unsigned : 0xff x 0xff = 0x01 (mod 0xff)
- // - signed : 0xef x 0xef = 0x01 (mod 0xff)
- ASSERT_SAME(create<T>(MAX) * create<T>(MAX), create<T>(ONE));
-}
-
-template <typename T> void print(const char *msg, T value) {
- testing::tlog << msg;
- IntegerToString<T, radix::Hex> buffer(value);
- testing::tlog << buffer.view() << "\n";
-}
-
-TEST(LlvmLibcUIntClassTest, SignedAddSub) {
- // Computations performed by https://www.wolframalpha.com/
- using T = BigInt<128, true, uint32_t>;
- const T a = parse_bigint<T>("1927508279017230597");
- const T b = parse_bigint<T>("278789278723478925");
- const T s = parse_bigint<T>("2206297557740709522");
- // Addition
- ASSERT_SAME(a + b, s);
- ASSERT_SAME(b + a, s); // commutative
- // Subtraction
- ASSERT_SAME(a - s, -b);
- ASSERT_SAME(s - a, b);
-}
-
-TEST(LlvmLibcUIntClassTest, SignedMulDiv) {
- // Computations performed by https://www.wolframalpha.com/
- using T = BigInt<128, true, uint16_t>;
- struct {
- const char *a;
- const char *b;
- const char *mul;
- } const test_cases[] = {{"-4", "3", "-12"},
- {"-3", "-3", "9"},
- {"1927508279017230597", "278789278723478925",
- "537368642840747885329125014794668225"}};
- for (auto tc : test_cases) {
- const T a = parse_bigint<T>(tc.a);
- const T b = parse_bigint<T>(tc.b);
- const T mul = parse_bigint<T>(tc.mul);
- // Multiplication
- ASSERT_SAME(a * b, mul);
- ASSERT_SAME(b * a, mul); // commutative
- ASSERT_SAME(a * -b, -mul); // sign
- ASSERT_SAME(-a * b, -mul); // sign
- ASSERT_SAME(-a * -b, mul); // sign
- // Division
- ASSERT_SAME(mul / a, b);
- ASSERT_SAME(mul / b, a);
- ASSERT_SAME(-mul / a, -b); // sign
- ASSERT_SAME(mul / -a, -b); // sign
- ASSERT_SAME(-mul / -a, b); // sign
- }
-}
-
-TYPED_TEST(LlvmLibcUIntClassTest, Division, Types) {
- ASSERT_SAME(create<T>(ZERO) / create<T>(ONE), create<T>(ZERO));
- ASSERT_SAME(create<T>(MAX) / create<T>(ONE), create<T>(MAX));
- ASSERT_SAME(create<T>(MAX) / create<T>(MAX), create<T>(ONE));
- ASSERT_SAME(create<T>(ONE) / create<T>(ONE), create<T>(ONE));
- if constexpr (T::SIGNED) {
- // Special case found by fuzzing.
- ASSERT_SAME(create<T>(MIN) / create<T>(MIN), create<T>(ONE));
- }
- // - unsigned : 0xff / 0x02 = 0x7f
- // - signed : 0xef / 0x02 = 0x77
- ASSERT_SAME(create<T>(MAX) / create<T>(TWO), (create<T>(MAX) >> 1));
-
- using word_type = typename T::word_type;
- const T zero_one_repeated = T::all_ones() / T(0xff);
- const word_type pattern = word_type(~0) / word_type(0xff);
- for (const word_type part : zero_one_repeated.val) {
- if constexpr (T::SIGNED == false) {
- EXPECT_EQ(part, pattern);
- }
- }
-}
-
-TYPED_TEST(LlvmLibcUIntClassTest, is_neg, Types) {
- EXPECT_FALSE(create<T>(ZERO).is_neg());
- EXPECT_FALSE(create<T>(ONE).is_neg());
- EXPECT_FALSE(create<T>(TWO).is_neg());
- EXPECT_EQ(create<T>(MIN).is_neg(), T::SIGNED);
- EXPECT_FALSE(create<T>(MAX).is_neg());
-}
-
-TYPED_TEST(LlvmLibcUIntClassTest, Masks, Types) {
- if constexpr (!T::SIGNED) {
- constexpr size_t BITS = T::BITS;
- // mask_trailing_ones
- ASSERT_SAME((mask_trailing_ones<T, 0>()), T::zero());
- ASSERT_SAME((mask_trailing_ones<T, 1>()), T::one());
- ASSERT_SAME((mask_trailing_ones<T, BITS - 1>()), T::all_ones() >> 1);
- ASSERT_SAME((mask_trailing_ones<T, BITS>()), T::all_ones());
- // mask_leading_ones
- ASSERT_SAME((mask_leading_ones<T, 0>()), T::zero());
- ASSERT_SAME((mask_leading_ones<T, 1>()), T::one() << (BITS - 1));
- ASSERT_SAME((mask_leading_ones<T, BITS - 1>()), T::all_ones() - T::one());
- ASSERT_SAME((mask_leading_ones<T, BITS>()), T::all_ones());
- // mask_trailing_zeros
- ASSERT_SAME((mask_trailing_zeros<T, 0>()), T::all_ones());
- ASSERT_SAME((mask_trailing_zeros<T, 1>()), T::all_ones() - T::one());
- ASSERT_SAME((mask_trailing_zeros<T, BITS - 1>()), T::one() << (BITS - 1));
- ASSERT_SAME((mask_trailing_zeros<T, BITS>()), T::zero());
- // mask_trailing_zeros
- ASSERT_SAME((mask_leading_zeros<T, 0>()), T::all_ones());
- ASSERT_SAME((mask_leading_zeros<T, 1>()), T::all_ones() >> 1);
- ASSERT_SAME((mask_leading_zeros<T, BITS - 1>()), T::one());
- ASSERT_SAME((mask_leading_zeros<T, BITS>()), T::zero());
- }
-}
-
-TYPED_TEST(LlvmLibcUIntClassTest, CountBits, Types) {
- if constexpr (!T::SIGNED) {
- for (size_t i = 0; i <= T::BITS; ++i) {
- const auto l_one = T::all_ones() << i; // 0b111...000
- const auto r_one = T::all_ones() >> i; // 0b000...111
- const int zeros = i;
- const int ones = T::BITS - zeros;
- ASSERT_EQ(cpp::countr_one(r_one), ones);
- ASSERT_EQ(cpp::countl_one(l_one), ones);
- ASSERT_EQ(cpp::countr_zero(l_one), zeros);
- ASSERT_EQ(cpp::countl_zero(r_one), zeros);
- }
- }
-}
-
using LL_UInt64 = UInt<64>;
// We want to test UInt<128> explicitly. So, for
// convenience, we use a sugar which does not conflict with the UInt128 type
@@ -751,7 +561,7 @@ TEST(LlvmLibcUIntClassTest, FullMulTests) {
LL_UInt##Bits a = ~LL_UInt##Bits(0); \
LL_UInt##Bits hi = a.quick_mul_hi(a); \
LL_UInt##Bits trunc = static_cast<LL_UInt##Bits>(a.ful_mul(a) >> Bits); \
- uint64_t overflow = trunc.sub_overflow(hi); \
+ uint64_t overflow = trunc.sub(hi); \
EXPECT_EQ(overflow, uint64_t(0)); \
EXPECT_LE(uint64_t(trunc), uint64_t(Error)); \
} while (0)
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel
index c0d402a89ea3ce..4f976122967c4d 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel
@@ -87,7 +87,6 @@ libc_test(
srcs = ["uint_test.cpp"],
deps = [
"//libc:__support_cpp_optional",
- "//libc:__support_integer_literals",
"//libc:__support_macros_properties_types",
"//libc:__support_uint",
"//libc:llvm_libc_macros_math_macros",
More information about the llvm-branch-commits
mailing list