406 changes: 280 additions & 126 deletions libc/src/__support/UInt.h

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion libc/src/__support/UInt128.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===-- A 128 bit unsigned int type -----------------------------*- C++ -*-===//
//===-- 128-bit signed and unsigned int types -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand All @@ -13,8 +13,10 @@

#if !defined(__SIZEOF_INT128__)
using UInt128 = __llvm_libc::cpp::UInt<128>;
using Int128 = __llvm_libc::cpp::Int<128>;
#else
using UInt128 = __uint128_t;
using Int128 = __int128_t;
#endif

#endif // LLVM_LIBC_SRC_SUPPORT_UINT128_H
26 changes: 15 additions & 11 deletions libc/src/__support/builtin_wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,51 +23,55 @@ namespace __llvm_libc {
// compiler match for us.
namespace __internal {

template <typename T> LIBC_INLINE int correct_zero(T val, int bits) {
template <typename T> LIBC_INLINE int constexpr correct_zero(T val, int bits) {
if (val == T(0))
return sizeof(T(0)) * 8;
else
return bits;
}

template <typename T> LIBC_INLINE int clz(T val);
template <typename T> LIBC_INLINE constexpr int clz(T val);
template <> LIBC_INLINE int clz<unsigned int>(unsigned int val) {
return __builtin_clz(val);
}
template <> LIBC_INLINE int clz<unsigned long int>(unsigned long int val) {
template <>
LIBC_INLINE constexpr int clz<unsigned long int>(unsigned long int val) {
return __builtin_clzl(val);
}
template <>
LIBC_INLINE int clz<unsigned long long int>(unsigned long long int val) {
LIBC_INLINE constexpr int
clz<unsigned long long int>(unsigned long long int val) {
return __builtin_clzll(val);
}

template <typename T> LIBC_INLINE int ctz(T val);
template <typename T> LIBC_INLINE constexpr int ctz(T val);
template <> LIBC_INLINE int ctz<unsigned int>(unsigned int val) {
return __builtin_ctz(val);
}
template <> LIBC_INLINE int ctz<unsigned long int>(unsigned long int val) {
template <>
LIBC_INLINE constexpr int ctz<unsigned long int>(unsigned long int val) {
return __builtin_ctzl(val);
}
template <>
LIBC_INLINE int ctz<unsigned long long int>(unsigned long long int val) {
LIBC_INLINE constexpr int
ctz<unsigned long long int>(unsigned long long int val) {
return __builtin_ctzll(val);
}
} // namespace __internal

template <typename T> LIBC_INLINE int safe_ctz(T val) {
template <typename T> LIBC_INLINE constexpr int safe_ctz(T val) {
return __internal::correct_zero(val, __internal::ctz(val));
}

template <typename T> LIBC_INLINE int unsafe_ctz(T val) {
template <typename T> LIBC_INLINE constexpr int unsafe_ctz(T val) {
return __internal::ctz(val);
}

template <typename T> LIBC_INLINE int safe_clz(T val) {
template <typename T> LIBC_INLINE constexpr int safe_clz(T val) {
return __internal::correct_zero(val, __internal::clz(val));
}

template <typename T> LIBC_INLINE int unsafe_clz(T val) {
template <typename T> LIBC_INLINE constexpr int unsafe_clz(T val) {
return __internal::clz(val);
}

Expand Down
4 changes: 2 additions & 2 deletions libc/src/__support/float_to_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,8 @@ LIBC_INLINE uint32_t mul_shift_mod_1e9(const MantissaInt mantissa,
cpp::UInt<MID_INT_SIZE + MANT_INT_SIZE> val(large);
// TODO: Find a better way to force __uint128_t to be UInt<128>
cpp::UInt<MANT_INT_SIZE> wide_mant(0);
wide_mant[0] = mantissa & (uint64_t(-1));
wide_mant[1] = mantissa >> 64;
wide_mant[0] = static_cast<size_t>(mantissa & (uint64_t(-1)));
wide_mant[1] = static_cast<size_t>(mantissa >> 64);
val = (val * wide_mant) >> shift_amount;

return val.div_uint32_times_pow_2(1000000000, 0).value()[0];
Expand Down
9 changes: 4 additions & 5 deletions libc/src/__support/integer_to_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,10 @@ class IntegerToString {
return convert<16>(val, buffer, lowercase);
}

template <typename T,
cpp::enable_if_t<cpp::is_integral_v<T> && cpp::is_unsigned_v<T> &&
(sizeof(T) > sizeof(uintmax_t)) &&
sizeof(T) % sizeof(uintmax_t) == 0,
int> = 0>
template <typename T, cpp::enable_if_t<cpp::is_integral_v<T> &&
(sizeof(T) > sizeof(uintmax_t)) &&
sizeof(T) % sizeof(uintmax_t) == 0,
int> = 0>
LIBC_INLINE static cpp::optional<cpp::string_view>
hex(T val, cpp::span<char> buffer, bool lowercase = true) {
// We will assume the buffer is exactly sized, which will be the case if
Expand Down
19 changes: 15 additions & 4 deletions libc/src/__support/str_to_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "src/__support/CPP/optional.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/dyadic_float.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/UInt128.h"
#include "src/__support/builtin_wrappers.h"
Expand Down Expand Up @@ -301,7 +302,8 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
}

// Shifting to 65 bits for 80 bit floats and 113 bits for 128 bit floats
BitsType msb = final_approx_upper >> (BITS_IN_MANTISSA - 1);
uint32_t msb =
static_cast<uint32_t>(final_approx_upper >> (BITS_IN_MANTISSA - 1));
BitsType final_mantissa =
final_approx_upper >>
(msb + BITS_IN_MANTISSA -
Expand Down Expand Up @@ -571,7 +573,16 @@ clinger_fast_path(ExpandedFloat<T> init_num,
}

fputil::FPBits<T> result;
T float_mantissa = static_cast<T>(mantissa);
T float_mantissa;
if constexpr (cpp::is_same_v<typename fputil::FPBits<T>::UIntType,
cpp::UInt<128>>) {
float_mantissa = static_cast<T>(fputil::DyadicFloat<128>(
false, 0,
fputil::DyadicFloat<128>::MantissaType(
{uint64_t(mantissa), uint64_t(mantissa >> 64)})));
} else {
float_mantissa = static_cast<T>(mantissa);
}

if (exp10 == 0) {
result = fputil::FPBits<T>(float_mantissa);
Expand Down Expand Up @@ -806,7 +817,7 @@ LIBC_INLINE FloatConvertReturn<T> binary_exp_to_float(ExpandedFloat<T> init_num,

BitsType round_bit_mask = BitsType(1) << (amount_to_shift_right - 1);
BitsType sticky_mask = round_bit_mask - 1;
bool round_bit = mantissa & round_bit_mask;
bool round_bit = static_cast<bool>(mantissa & round_bit_mask);
bool sticky_bit = static_cast<bool>(mantissa & sticky_mask) || truncated;

if (amount_to_shift_right < NUMBITS) {
Expand All @@ -816,7 +827,7 @@ LIBC_INLINE FloatConvertReturn<T> binary_exp_to_float(ExpandedFloat<T> init_num,
} else {
mantissa = 0;
}
bool least_significant_bit = mantissa & BitsType(1);
bool least_significant_bit = static_cast<bool>(mantissa & BitsType(1));

// TODO: check that this rounding behavior is correct.

Expand Down
1 change: 1 addition & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,7 @@ add_header_library(
log_range_reduction.h
DEPENDS
.common_constants
libc.src.__support.uint128
libc.src.__support.FPUtil.dyadic_float
)

Expand Down
13 changes: 7 additions & 6 deletions libc/src/math/generic/log_range_reduction.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "common_constants.h"
#include "src/__support/FPUtil/dyadic_float.h"
#include "src/__support/UInt128.h"

namespace __llvm_libc {

Expand Down Expand Up @@ -59,9 +60,9 @@ log_range_reduction(double m_x, const LogRR &log_table,
int64_t s3 = static_cast<int64_t>(S3[idx3]); // |s| < 2^-13, ulp = 2^-21
int64_t spv3 = (s3 << 55) + vv2; // |s + v| < 2^-21, ulp = 2^-76
// |s*v| < 2^-27, ulp = 2^(-76-21) = 2^-97
__int128_t sv3 = static_cast<__int128_t>(s3) * static_cast<__int128_t>(vv2);
Int128 sv3 = static_cast<Int128>(s3) * static_cast<Int128>(vv2);
// |vv3| < 2^-21, ulp = 2^-97
__int128_t vv3 = (static_cast<__int128_t>(spv3) << 21) + sv3;
Int128 vv3 = (static_cast<Int128>(spv3) << 21) + sv3;

// Range reduction - Step 4
// Output range: vv4 in [-0x1.0002143p-29 , 0x1p-29]
Expand All @@ -70,13 +71,13 @@ log_range_reduction(double m_x, const LogRR &log_table,

sum = fputil::quick_add(sum, log_table.step_4[idx4]);

__int128_t s4 = static_cast<__int128_t>(S4[idx4]); // |s| < 2^-21, ulp = 2^-28
Int128 s4 = static_cast<Int128>(S4[idx4]); // |s| < 2^-21, ulp = 2^-28
// |s + v| < 2^-28, ulp = 2^-97
__int128_t spv4 = (s4 << 69) + vv3;
Int128 spv4 = (s4 << 69) + vv3;
// |s*v| < 2^-42, ulp = 2^(-97-28) = 2^-125
__int128_t sv4 = s4 * vv3;
Int128 sv4 = s4 * vv3;
// |vv4| < 2^-28, ulp = 2^-125
__int128_t vv4 = (spv4 << 28) + sv4;
Int128 vv4 = (spv4 << 28) + sv4;

return (vv4 < 0) ? Float128(true, -125,
MType({static_cast<uint64_t>(-vv4),
Expand Down
2 changes: 1 addition & 1 deletion libc/src/stdio/printf_core/char_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace __llvm_libc {
namespace printf_core {

LIBC_INLINE int convert_char(Writer *writer, const FormatSection &to_conv) {
char c = to_conv.conv_val_raw;
char c = static_cast<char>(to_conv.conv_val_raw);

constexpr int string_len = 1;

Expand Down
16 changes: 10 additions & 6 deletions libc/src/stdio/printf_core/float_dec_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ namespace printf_core {
using MantissaInt = fputil::FPBits<long double>::UIntType;

// Returns true if value is divisible by 2^p.
LIBC_INLINE constexpr bool multiple_of_power_of_2(const uint64_t value,
const uint32_t p) {
return (value & ((uint64_t(1) << p) - 1)) == 0;
template <typename T>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_integral_v<T>, bool>
multiple_of_power_of_2(T value, uint32_t p) {
return (value & ((T(1) << p) - 1)) == 0;
}

constexpr size_t BLOCK_SIZE = 9;
Expand Down Expand Up @@ -1148,7 +1149,8 @@ LIBC_INLINE int convert_float_decimal(Writer *writer,
float_bits);
}
} else {
fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<double>::UIntType float_raw =
static_cast<fputil::FPBits<double>::UIntType>(to_conv.conv_val_raw);
fputil::FPBits<double> float_bits(float_raw);
if (!float_bits.is_inf_or_nan()) {
return convert_float_decimal_typed<double>(writer, to_conv, float_bits);
Expand All @@ -1168,7 +1170,8 @@ LIBC_INLINE int convert_float_dec_exp(Writer *writer,
float_bits);
}
} else {
fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<double>::UIntType float_raw =
static_cast<fputil::FPBits<double>::UIntType>(to_conv.conv_val_raw);
fputil::FPBits<double> float_bits(float_raw);
if (!float_bits.is_inf_or_nan()) {
return convert_float_dec_exp_typed<double>(writer, to_conv, float_bits);
Expand All @@ -1188,7 +1191,8 @@ LIBC_INLINE int convert_float_dec_auto(Writer *writer,
float_bits);
}
} else {
fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<double>::UIntType float_raw =
static_cast<fputil::FPBits<double>::UIntType>(to_conv.conv_val_raw);
fputil::FPBits<double> float_bits(float_raw);
if (!float_bits.is_inf_or_nan()) {
return convert_float_dec_auto_typed<double>(writer, to_conv, float_bits);
Expand Down
10 changes: 6 additions & 4 deletions libc/src/stdio/printf_core/float_hex_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ LIBC_INLINE int convert_float_hex_exp(Writer *writer,
} else {
mantissa_width = fputil::MantissaWidth<double>::VALUE;
exponent_bias = fputil::FPBits<double>::EXPONENT_BIAS;
fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<double>::UIntType float_raw =
static_cast<fputil::FPBits<double>::UIntType>(to_conv.conv_val_raw);
fputil::FPBits<double> float_bits(float_raw);
is_negative = float_bits.get_sign();
exponent = float_bits.get_exponent();
Expand Down Expand Up @@ -146,9 +147,10 @@ LIBC_INLINE int convert_float_hex_exp(Writer *writer,

size_t mant_cur = mant_len;
size_t first_non_zero = 1;
for (; mant_cur > 0; --mant_cur, mantissa /= 16) {
char new_digit = ((mantissa % 16) > 9) ? ((mantissa % 16) - 10 + a)
: ((mantissa % 16) + '0');
for (; mant_cur > 0; --mant_cur, mantissa >>= 4) {
char mant_mod_16 = static_cast<char>(mantissa) & 15;
char new_digit =
(mant_mod_16 > 9) ? (mant_mod_16 - 10 + a) : (mant_mod_16 + '0');
mant_buffer[mant_cur - 1] = new_digit;
if (new_digit != '0' && first_non_zero < mant_cur)
first_non_zero = mant_cur;
Expand Down
3 changes: 2 additions & 1 deletion libc/src/stdio/printf_core/float_inf_nan_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ LIBC_INLINE int convert_inf_nan(Writer *writer, const FormatSection &to_conv) {
is_negative = float_bits.get_sign();
mantissa = float_bits.get_explicit_mantissa();
} else {
fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<double>::UIntType float_raw =
static_cast<fputil::FPBits<double>::UIntType>(to_conv.conv_val_raw);
fputil::FPBits<double> float_bits(float_raw);
is_negative = float_bits.get_sign();
mantissa = float_bits.get_explicit_mantissa();
Expand Down
2 changes: 1 addition & 1 deletion libc/src/stdio/printf_core/int_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {
static constexpr size_t BITS_IN_BYTE = 8;
static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;

uintmax_t num = to_conv.conv_val_raw;
uintmax_t num = static_cast<uintmax_t>(to_conv.conv_val_raw);
bool is_negative = false;
FormatFlags flags = to_conv.flags;

Expand Down
10 changes: 8 additions & 2 deletions libc/test/UnitTest/LibcTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,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> && cpp::is_unsigned_v<T> &&
(sizeof(T) > sizeof(uint64_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");
Expand Down Expand Up @@ -226,6 +225,13 @@ template bool test<__uint128_t>(RunContext *Ctx, TestCond Cond, __uint128_t LHS,
const char *RHSStr, Location Loc);
#endif

template bool test<__llvm_libc::cpp::Int<128>>(RunContext *Ctx, TestCond Cond,
__llvm_libc::cpp::Int<128> LHS,
__llvm_libc::cpp::Int<128> RHS,
const char *LHSStr,
const char *RHSStr,
Location Loc);

template bool test<__llvm_libc::cpp::UInt<128>>(RunContext *Ctx, TestCond Cond,
__llvm_libc::cpp::UInt<128> LHS,
__llvm_libc::cpp::UInt<128> RHS,
Expand Down
75 changes: 75 additions & 0 deletions libc/test/src/__support/uint_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ using LL_UInt320 = __llvm_libc::cpp::UInt<320>;
using LL_UInt512 = __llvm_libc::cpp::UInt<512>;
using LL_UInt1024 = __llvm_libc::cpp::UInt<1024>;

using LL_Int128 = __llvm_libc::cpp::Int<128>;
using LL_Int192 = __llvm_libc::cpp::Int<192>;

TEST(LlvmLibcUIntClassTest, BasicInit) {
LL_UInt128 empty;
LL_UInt128 half_val(12345);
Expand Down Expand Up @@ -560,3 +563,75 @@ TEST(LlvmLibcUIntClassTest, DivUInt32TimesPow2Tests) {
TEST_QUICK_DIV_UINT32_POW2(1000000000, 75);
TEST_QUICK_DIV_UINT32_POW2(1000000000, 101);
}

TEST(LlvmLibcUIntClassTest, ComparisonInt128Tests) {
LL_Int128 a(123);
LL_Int128 b(0);
LL_Int128 c(-1);

ASSERT_TRUE(a == a);
ASSERT_TRUE(b == b);
ASSERT_TRUE(c == c);

ASSERT_TRUE(a != b);
ASSERT_TRUE(a != c);
ASSERT_TRUE(b != a);
ASSERT_TRUE(b != c);
ASSERT_TRUE(c != a);
ASSERT_TRUE(c != b);

ASSERT_TRUE(a > b);
ASSERT_TRUE(a >= b);
ASSERT_TRUE(a > c);
ASSERT_TRUE(a >= c);
ASSERT_TRUE(b > c);
ASSERT_TRUE(b >= c);

ASSERT_TRUE(b < a);
ASSERT_TRUE(b <= a);
ASSERT_TRUE(c < a);
ASSERT_TRUE(c <= a);
ASSERT_TRUE(c < b);
ASSERT_TRUE(c <= b);
}

TEST(LlvmLibcUIntClassTest, BasicArithmeticInt128Tests) {
LL_Int128 a(123);
LL_Int128 b(0);
LL_Int128 c(-3);

ASSERT_EQ(a * a, LL_Int128(123 * 123));
ASSERT_EQ(a * c, LL_Int128(-369));
ASSERT_EQ(c * a, LL_Int128(-369));
ASSERT_EQ(c * c, LL_Int128(9));
ASSERT_EQ(a * b, b);
ASSERT_EQ(b * a, b);
ASSERT_EQ(b * c, b);
ASSERT_EQ(c * b, b);
}

#ifdef __SIZEOF_INT128__

TEST(LlvmLibcUIntClassTest, ConstructorFromUInt128Tests) {
__uint128_t a = (__uint128_t(123) << 64) + 1;
__int128_t b = -static_cast<__int128_t>(a);
LL_Int128 c(a);
LL_Int128 d(b);

LL_Int192 e(a);
LL_Int192 f(b);

ASSERT_EQ(static_cast<int>(c), 1);
ASSERT_EQ(static_cast<int>(c >> 64), 123);
ASSERT_EQ(static_cast<uint64_t>(d), static_cast<uint64_t>(b));
ASSERT_EQ(static_cast<uint64_t>(d >> 64), static_cast<uint64_t>(b >> 64));
ASSERT_EQ(c + d, LL_Int128(a + b));

ASSERT_EQ(static_cast<int>(e), 1);
ASSERT_EQ(static_cast<int>(e >> 64), 123);
ASSERT_EQ(static_cast<uint64_t>(f), static_cast<uint64_t>(b));
ASSERT_EQ(static_cast<uint64_t>(f >> 64), static_cast<uint64_t>(b >> 64));
ASSERT_EQ(LL_UInt192(e + f), LL_UInt192(a + b));
}

#endif // __SIZEOF_INT128__
2 changes: 2 additions & 0 deletions utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ libc_support_library(
":__support_fputil_fenv_impl",
":__support_fputil_fp_bits",
":__support_fputil_rounding_mode",
":__support_fputil_dyadic_float",
":__support_str_to_integer",
":__support_str_to_num_result",
":__support_uint128",
Expand Down Expand Up @@ -1140,6 +1141,7 @@ libc_support_library(
hdrs = ["src/math/generic/log_range_reduction.h"],
deps = [
":__support_common",
":__support_uint128",
":__support_fputil_dyadic_float",
":common_constants",
],
Expand Down