[libcxx-commits] [libcxx] 3f78683 - [libc++] Implements 128-bit support in to_chars.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 7 08:32:33 PDT 2022


Author: Mark de Wever
Date: 2022-07-07T17:32:27+02:00
New Revision: 3f78683353e272224e9bcc223f7ba05ab374a8c2

URL: https://github.com/llvm/llvm-project/commit/3f78683353e272224e9bcc223f7ba05ab374a8c2
DIFF: https://github.com/llvm/llvm-project/commit/3f78683353e272224e9bcc223f7ba05ab374a8c2.diff

LOG: [libc++] Implements 128-bit support in to_chars.

This is required by the Standard and makes it possible to add full
128-bit support to format.

The patch also fixes 128-bit from_chars "support". One unit test
required a too large value, this failed on 128-bit; the fix was to add
more characters to the input.

Note only base 10 has been optimized. Other bases can be optimized.

Note the 128-bit lookup table could be made smaller. This will be done later. I
really want to get 128-bit working in to_chars and format in the upcomming
LLVM 15 release, these optimizations aren't critical.

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D128929

Added: 
    

Modified: 
    libcxx/include/__bits
    libcxx/include/__charconv/tables.h
    libcxx/include/__charconv/to_chars_base_10.h
    libcxx/include/charconv
    libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
    libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
    libcxx/test/support/charconv_test_helpers.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__bits b/libcxx/include/__bits
index 1eee8f576e9e1..92ef5c0a7b492 100644
--- a/libcxx/include/__bits
+++ b/libcxx/include/__bits
@@ -43,6 +43,23 @@ int __libcpp_clz(unsigned long __x)      _NOEXCEPT { return __builtin_clzl(__x);
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
 int __libcpp_clz(unsigned long long __x) _NOEXCEPT { return __builtin_clzll(__x); }
 
+#  ifndef _LIBCPP_HAS_NO_INT128
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+int __libcpp_clz(__uint128_t __x) _NOEXCEPT {
+  // The function is written in this form due to C++ constexpr limitations.
+  // The algorithm:
+  // - Test whether any bit in the high 64-bits is set
+  // - No bits set:
+  //   - The high 64-bits contain 64 leading zeros,
+  //   - Add the result of the low 64-bits.
+  // - Any bits set:
+  //   - The number of leading zeros of the input is the number of leading
+  //     zeros in the high 64-bits.
+  return ((__x >> 64) == 0)
+           ? (64 + __builtin_clzll(static_cast<unsigned long long>(__x)))
+           : __builtin_clzll(static_cast<unsigned long long>(__x >> 64));
+}
+#  endif
 
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
 int __libcpp_popcount(unsigned __x)           _NOEXCEPT { return __builtin_popcount(__x); }

diff  --git a/libcxx/include/__charconv/tables.h b/libcxx/include/__charconv/tables.h
index a2f7f7ce29b37..83f39e6fd2efd 100644
--- a/libcxx/include/__charconv/tables.h
+++ b/libcxx/include/__charconv/tables.h
@@ -35,6 +35,11 @@ struct __table {
 
   static const uint32_t __pow10_32[10];
   static const uint64_t __pow10_64[20];
+#  ifndef _LIBCPP_HAS_NO_INT128
+  // TODO FMT Reduce the number of entries in this table.
+  static const __uint128_t __pow10_128[40];
+  static const int __pow10_128_offset = 0;
+#  endif
   static const char __digits_base_10[200];
 };
 
@@ -106,6 +111,51 @@ const uint64_t __table<_Tp>::__pow10_64[20] = {UINT64_C(0),
                                                UINT64_C(1000000000000000000),
                                                UINT64_C(10000000000000000000)};
 
+#  ifndef _LIBCPP_HAS_NO_INT128
+template <class _Tp>
+const __uint128_t __table<_Tp>::__pow10_128[40] = {
+    UINT64_C(0),
+    UINT64_C(10),
+    UINT64_C(100),
+    UINT64_C(1000),
+    UINT64_C(10000),
+    UINT64_C(100000),
+    UINT64_C(1000000),
+    UINT64_C(10000000),
+    UINT64_C(100000000),
+    UINT64_C(1000000000),
+    UINT64_C(10000000000),
+    UINT64_C(100000000000),
+    UINT64_C(1000000000000),
+    UINT64_C(10000000000000),
+    UINT64_C(100000000000000),
+    UINT64_C(1000000000000000),
+    UINT64_C(10000000000000000),
+    UINT64_C(100000000000000000),
+    UINT64_C(1000000000000000000),
+    UINT64_C(10000000000000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000000000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000000000000000),
+    __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000000000),
+    (__uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000000000)) * 10};
+#  endif
+
 template <class _Tp>
 const char __table<_Tp>::__digits_base_10[200] = {
     // clang-format off

diff  --git a/libcxx/include/__charconv/to_chars_base_10.h b/libcxx/include/__charconv/to_chars_base_10.h
index 91c209559aff9..d25deffc592fc 100644
--- a/libcxx/include/__charconv/to_chars_base_10.h
+++ b/libcxx/include/__charconv/to_chars_base_10.h
@@ -14,11 +14,15 @@
 #include <__charconv/tables.h>
 #include <__config>
 #include <cstdint>
+#include <limits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 #ifndef _LIBCPP_CXX03_LANG
@@ -62,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI inline char* __append9(char* __first, uint32_t __value) no
   return __itoa::__append8(__itoa::__append1(__first, __value / 100000000), __value % 100000000);
 }
 
-// This function is used for uint32_t and uint64_t.
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI char* __append10(char* __first, _Tp __value) noexcept {
   return __itoa::__append8(__itoa::__append2(__first, static_cast<uint32_t>(__value / 100000000)),
@@ -118,10 +121,65 @@ _LIBCPP_HIDE_FROM_ABI inline char* __base_10_u64(char* __buffer, uint64_t __valu
   return __itoa::__append10(__buffer, __value);
 }
 
+#  ifndef _LIBCPP_HAS_NO_INT128
+/// \returns 10^\a exp
+///
+/// \pre \a exp [19, 39]
+///
+/// \note The lookup table contains a partial set of exponents limiting the
+/// range that can be used. However the range is sufficient for
+/// \ref __base_10_u128.
+_LIBCPP_HIDE_FROM_ABI inline __uint128_t __pow_10(int __exp) noexcept {
+  _LIBCPP_ASSERT(__exp >= __table<>::__pow10_128_offset, "Index out of bounds");
+  return __table<>::__pow10_128[__exp - __table<>::__pow10_128_offset];
+}
+
+_LIBCPP_HIDE_FROM_ABI inline char* __base_10_u128(char* __buffer, __uint128_t __value) noexcept {
+  _LIBCPP_ASSERT(
+      __value > numeric_limits<uint64_t>::max(), "The optimizations for this algorithm fail when this isn't true.");
+
+  // Unlike the 64 to 32 bit case the 128 bit case the "upper half" can't be
+  // stored in the "lower half". Instead we first need to handle the top most
+  // digits separately.
+  //
+  // Maximum unsigned values
+  // 64  bit                             18'446'744'073'709'551'615 (20 digits)
+  // 128 bit    340'282'366'920'938'463'463'374'607'431'768'211'455 (39 digits)
+  // step 1     ^                                                   ([0-1] digits)
+  // step 2      ^^^^^^^^^^^^^^^^^^^^^^^^^                          ([0-19] digits)
+  // step 3                               ^^^^^^^^^^^^^^^^^^^^^^^^^ (19 digits)
+  if (__value >= __itoa::__pow_10(38)) {
+    // step 1
+    __buffer = __itoa::__append1(__buffer, static_cast<uint32_t>(__value / __itoa::__pow_10(38)));
+    __value %= __itoa::__pow_10(38);
+
+    // step 2 always 19 digits.
+    // They are handled here since leading zeros need to be appended to the buffer,
+    __buffer = __itoa::__append9(__buffer, static_cast<uint32_t>(__value / __itoa::__pow_10(29)));
+    __value %= __itoa::__pow_10(29);
+    __buffer = __itoa::__append10(__buffer, static_cast<uint64_t>(__value / __itoa::__pow_10(19)));
+    __value %= __itoa::__pow_10(19);
+  }
+  else {
+    // step 2
+    // This version needs to determine the position of the leading non-zero digit.
+    __buffer = __base_10_u64(__buffer, static_cast<uint64_t>(__value / __itoa::__pow_10(19)));
+    __value %= __itoa::__pow_10(19);
+  }
+
+  // Step 3
+  __buffer = __itoa::__append9(__buffer, static_cast<uint32_t>(__value / 10000000000));
+  __buffer = __itoa::__append10(__buffer, static_cast<uint64_t>(__value % 10000000000));
+
+  return __buffer;
+}
+#  endif
 } // namespace __itoa
 
 #endif // _LIBCPP_CXX03_LANG
 
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP___CHARCONV_TO_CHARS_BASE_10_H

diff  --git a/libcxx/include/charconv b/libcxx/include/charconv
index 6a63e5fe9057b..c3db69a39e4b5 100644
--- a/libcxx/include/charconv
+++ b/libcxx/include/charconv
@@ -117,32 +117,22 @@ from_chars_result from_chars(const char*, const char*, bool, int = 10) = delete;
 namespace __itoa
 {
 
-
 template <typename _Tp, typename = void>
-struct _LIBCPP_HIDDEN __traits_base
-{
-    using type = uint64_t;
-
-    static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v)
-    {
-        auto __t = (64 - std::__libcpp_clz(static_cast<type>(__v | 1))) * 1233 >> 12;
-        return __t - (__v < __table<>::__pow10_64[__t]) + 1;
-    }
-
-    static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v)
-    {
-        return __itoa::__base_10_u64(__p, __v);
-    }
-
-    static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_64)& __pow() { return __table<>::__pow10_64; }
-};
+struct _LIBCPP_HIDDEN __traits_base;
 
 template <typename _Tp>
-struct _LIBCPP_HIDDEN
-    __traits_base<_Tp, decltype(void(uint32_t{declval<_Tp>()}))>
+struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) <= sizeof(uint32_t)>>
 {
     using type = uint32_t;
 
+    /// The width estimation using a log10 algorithm.
+    ///
+    /// The algorithm is based on
+    /// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+    /// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that
+    /// function requires its input to have at least one bit set the value of
+    /// zero is set to one. This means the first element of the lookup table is
+    /// zero.
     static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v)
     {
         auto __t = (32 - std::__libcpp_clz(static_cast<type>(__v | 1))) * 1233 >> 12;
@@ -157,6 +147,61 @@ struct _LIBCPP_HIDDEN
     static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_32)& __pow() { return __table<>::__pow10_32; }
 };
 
+template <typename _Tp>
+struct _LIBCPP_HIDDEN
+    __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(uint64_t)>> {
+  using type = uint64_t;
+
+  /// The width estimation using a log10 algorithm.
+  ///
+  /// The algorithm is based on
+  /// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+  /// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that
+  /// function requires its input to have at least one bit set the value of
+  /// zero is set to one. This means the first element of the lookup table is
+  /// zero.
+  static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) {
+    auto __t = (64 - std::__libcpp_clz(static_cast<type>(__v | 1))) * 1233 >> 12;
+    return __t - (__v < __table<>::__pow10_64[__t]) + 1;
+  }
+
+  static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u64(__p, __v); }
+
+  static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_64)& __pow() { return __table<>::__pow10_64; }
+};
+
+
+#  ifndef _LIBCPP_HAS_NO_INT128
+template <typename _Tp>
+struct _LIBCPP_HIDDEN
+    __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(__uint128_t)> > {
+  using type = __uint128_t;
+
+  /// The width estimation using a log10 algorithm.
+  ///
+  /// The algorithm is based on
+  /// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+  /// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that
+  /// function requires its input to have at least one bit set the value of
+  /// zero is set to one. This means the first element of the lookup table is
+  /// zero.
+  static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) {
+    _LIBCPP_ASSERT(__v > numeric_limits<uint64_t>::max(), "The optimizations for this algorithm fail when this isn't true.");
+    // There's always a bit set in the upper 64-bits.
+    auto __t = (128 - std::__libcpp_clz(static_cast<uint64_t>(__v >> 64))) * 1233 >> 12;
+    _LIBCPP_ASSERT(__t >= __table<>::__pow10_128_offset, "Index out of bounds");
+    // __t is adjusted since the lookup table misses the lower entries.
+    return __t - (__v < __table<>::__pow10_128[__t - __table<>::__pow10_128_offset]) + 1;
+  }
+
+  static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u128(__p, __v); }
+
+  // TODO FMT This pow function should get an index.
+  // By moving this to its own header it can be reused by the pow function in to_chars_base_10.
+  static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_128)& __pow() { return __table<>::__pow10_128; }
+};
+#endif
+
 template <typename _Tp>
 inline _LIBCPP_HIDE_FROM_ABI bool
 __mul_overflowed(unsigned char __a, _Tp __b, unsigned char& __r)
@@ -271,6 +316,28 @@ __to_chars_itoa(char* __first, char* __last, _Tp __value, false_type)
         return {__last, errc::value_too_large};
 }
 
+#  ifndef _LIBCPP_HAS_NO_INT128
+template <>
+inline _LIBCPP_HIDE_FROM_ABI to_chars_result
+__to_chars_itoa(char* __first, char* __last, __uint128_t __value, false_type)
+{
+    // When the value fits in 64-bits use the 64-bit code path. This reduces
+    // the number of expensive calculations on 128-bit values.
+    //
+    // NOTE the 128-bit code path requires this optimization.
+    if(__value <= numeric_limits<uint64_t>::max())
+        return __to_chars_itoa(__first, __last, static_cast<uint64_t>(__value), false_type());
+
+    using __tx = __itoa::__traits<__uint128_t>;
+    auto __
diff  = __last - __first;
+
+    if (__tx::digits <= __
diff  || __tx::__width(__value) <= __
diff )
+        return {__tx::__convert(__first, __value), errc(0)};
+    else
+        return {__last, errc::value_too_large};
+}
+#endif
+
 template <typename _Tp>
 inline _LIBCPP_HIDE_FROM_ABI to_chars_result
 __to_chars_integral(char* __first, char* __last, _Tp __value, int __base,
@@ -493,7 +560,6 @@ to_chars(char* __first, char* __last, _Tp __value)
 {
   using _Type = __make_32_64_or_128_bit_t<_Tp>;
   static_assert(!is_same<_Type, void>::value, "unsupported integral type used in to_chars");
-  static_assert(sizeof(_Tp) <= sizeof(int64_t), "128-bit integral support isn't available yet in to_chars");
   return std::__to_chars_itoa(__first, __last, static_cast<_Type>(__value), is_signed<_Tp>());
 }
 
@@ -504,7 +570,6 @@ to_chars(char* __first, char* __last, _Tp __value, int __base)
   _LIBCPP_ASSERT(2 <= __base && __base <= 36, "base not in [2, 36]");
 
   using _Type = __make_32_64_or_128_bit_t<_Tp>;
-  static_assert(sizeof(_Tp) <= sizeof(int64_t), "128-bit integral support isn't available yet in to_chars");
   return std::__to_chars_integral(__first, __last, static_cast<_Type>(__value), __base, is_signed<_Tp>());
 }
 

diff  --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
index 0c10c0dfb5bca..fd47cb4bbb6cf 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
@@ -40,7 +40,8 @@ struct test_basics
         }
 
         {
-            char s[] = "0X7BAtSGHDkEIXZg ";
+            // The string has more characters than valid in an 128-bit value.
+            char s[] = "0X7BAtSGHDkEIXZgQRfYChLpOzRnM ";
 
             // The letters from a (or A) through z (or Z) are ascribed the
             // values 10 through 35; (C11 7.22.1.4/3)
@@ -88,13 +89,15 @@ struct test_signed
     void operator()()
     {
         std::from_chars_result r;
-        T x;
+        T x = 42;
 
         {
             // If the pattern allows for an optional sign,
             // but the string has no digit characters following the sign,
             char s[] = "- 9+12";
             r = std::from_chars(s, s + sizeof(s), x);
+            // value is unmodified,
+            assert(x == 42);
             // no characters match the pattern.
             assert(r.ptr == s);
             assert(r.ec == std::errc::invalid_argument);

diff  --git a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
index 70f85a537568a..db52ac9fd3144 100644
--- a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
@@ -19,6 +19,33 @@
 #include "test_macros.h"
 #include "charconv_test_helpers.h"
 
+#ifndef TEST_HAS_NO_INT128
+__uint128_t make_u128(__uint128_t a, uint64_t b) {
+  a *= 1000000000000000000UL;
+  a *= 10;
+  return a + b;
+}
+
+__uint128_t make_u128(__uint128_t a, uint64_t b, uint64_t c) {
+  a *= 10000000000000ULL;
+  a += b;
+  a *= 10000000000000ULL;
+  return a + c;
+}
+
+__int128_t make_i128(__int128_t a, int64_t b) {
+  if (a < 0)
+    return -make_u128(-a, b);
+  return make_u128(a, b);
+}
+
+__int128_t make_i128(__int128_t a, __int128_t b, int64_t c) {
+  if (a < 0)
+    return -make_u128(-a, b, c);
+  return make_u128(a, b, c);
+}
+#endif
+
 template <typename T>
 struct test_basics : to_chars_test_base<T>
 {
@@ -61,6 +88,27 @@ struct test_basics : to_chars_test_base<T>
         test(123456789012345678UL, "123456789012345678");
         test(1234567890123456789UL, "1234567890123456789");
         test(12345678901234567890UL, "12345678901234567890");
+#ifndef TEST_HAS_NO_INT128
+        test(make_u128(12UL, 3456789012345678901UL), "123456789012345678901");
+        test(make_u128(123UL, 4567890123456789012UL), "1234567890123456789012");
+        test(make_u128(1234UL, 5678901234567890123UL), "12345678901234567890123");
+        test(make_u128(12345UL, 6789012345678901234UL), "123456789012345678901234");
+        test(make_u128(123456UL, 7890123456789012345UL), "1234567890123456789012345");
+        test(make_u128(1234567UL, 8901234567890123456UL), "12345678901234567890123456");
+        test(make_u128(12345678UL, 9012345678901234567UL), "123456789012345678901234567");
+        test(make_u128(123456789UL, 123456789012345678UL), "1234567890123456789012345678");
+        test(make_u128(123UL, 4567890123456UL, 7890123456789UL), "12345678901234567890123456789");
+        test(make_u128(1234UL, 5678901234567UL, 8901234567890UL), "123456789012345678901234567890");
+        test(make_u128(12345UL, 6789012345678UL, 9012345678901UL), "1234567890123456789012345678901");
+        test(make_u128(123456UL, 7890123456789UL, 123456789012UL), "12345678901234567890123456789012");
+        test(make_u128(1234567UL, 8901234567890UL, 1234567890123UL), "123456789012345678901234567890123");
+        test(make_u128(12345678UL, 9012345678901UL, 2345678901234UL), "1234567890123456789012345678901234");
+        test(make_u128(123456789UL, 123456789012UL, 3456789012345UL), "12345678901234567890123456789012345");
+        test(make_u128(1234567890UL, 1234567890123UL, 4567890123456UL), "123456789012345678901234567890123456");
+        test(make_u128(12345678901UL, 2345678901234UL, 5678901234567UL), "1234567890123456789012345678901234567");
+        test(make_u128(123456789012UL, 3456789012345UL, 6789012345678UL), "12345678901234567890123456789012345678");
+        test(make_u128(1234567890123UL, 4567890123456UL, 7890123456789UL), "123456789012345678901234567890123456789");
+#endif
 
         // Test special cases with zeros inside a value string representation,
         // to_chars algorithm processes them in a special way and should not
@@ -86,6 +134,27 @@ struct test_basics : to_chars_test_base<T>
         test(100000000000000000UL, "100000000000000000");
         test(1000000000000000000UL, "1000000000000000000");
         test(10000000000000000000UL, "10000000000000000000");
+#ifndef TEST_HAS_NO_INT128
+        test(make_u128(10UL, 0), "100000000000000000000");
+        test(make_u128(100UL, 0), "1000000000000000000000");
+        test(make_u128(1000UL, 0), "10000000000000000000000");
+        test(make_u128(10000UL, 0), "100000000000000000000000");
+        test(make_u128(100000UL, 0), "1000000000000000000000000");
+        test(make_u128(1000000UL, 0), "10000000000000000000000000");
+        test(make_u128(10000000UL, 0), "100000000000000000000000000");
+        test(make_u128(100000000UL, 0), "1000000000000000000000000000");
+        test(make_u128(100UL, 0, 0), "10000000000000000000000000000");
+        test(make_u128(1000UL, 0, 0), "100000000000000000000000000000");
+        test(make_u128(10000UL, 0, 0), "1000000000000000000000000000000");
+        test(make_u128(100000UL, 0, 0), "10000000000000000000000000000000");
+        test(make_u128(1000000UL, 0, 0), "100000000000000000000000000000000");
+        test(make_u128(10000000UL, 0, 0), "1000000000000000000000000000000000");
+        test(make_u128(100000000UL, 0, 0), "10000000000000000000000000000000000");
+        test(make_u128(1000000000UL, 0, 0), "100000000000000000000000000000000000");
+        test(make_u128(10000000000UL, 0, 0), "1000000000000000000000000000000000000");
+        test(make_u128(100000000000UL, 0, 0), "10000000000000000000000000000000000000");
+        test(make_u128(1000000000000UL, 0, 0), "100000000000000000000000000000000000000");
+#endif
 
         for (int b = 2; b < 37; ++b)
         {
@@ -138,6 +207,29 @@ struct test_signed : to_chars_test_base<T>
         test(-12345678901234567L, "-12345678901234567");
         test(-123456789012345678L, "-123456789012345678");
         test(-1234567890123456789L, "-1234567890123456789");
+#ifndef TEST_HAS_NO_INT128
+        test(make_i128(-1L, 2345678901234567890L), "-12345678901234567890");
+        test(make_i128(-12L, 3456789012345678901L), "-123456789012345678901");
+        test(make_i128(-123L, 4567890123456789012L), "-1234567890123456789012");
+        test(make_i128(-1234L, 5678901234567890123L), "-12345678901234567890123");
+        test(make_i128(-12345L, 6789012345678901234L), "-123456789012345678901234");
+        test(make_i128(-123456L, 7890123456789012345L), "-1234567890123456789012345");
+        test(make_i128(-1234567L, 8901234567890123456L), "-12345678901234567890123456");
+        test(make_i128(-12345678L, 9012345678901234567L), "-123456789012345678901234567");
+        test(make_i128(-123456789L, 123456789012345678L), "-1234567890123456789012345678");
+        test(make_i128(-1234567890L, 1234567890123456789L), "-12345678901234567890123456789");
+        test(make_i128(-123L, 4567890123456L, 7890123456789L), "-12345678901234567890123456789");
+        test(make_i128(-1234L, 5678901234567L, 8901234567890L), "-123456789012345678901234567890");
+        test(make_i128(-12345L, 6789012345678L, 9012345678901L), "-1234567890123456789012345678901");
+        test(make_i128(-123456L, 7890123456789L, 123456789012L), "-12345678901234567890123456789012");
+        test(make_i128(-1234567L, 8901234567890L, 1234567890123L), "-123456789012345678901234567890123");
+        test(make_i128(-12345678L, 9012345678901L, 2345678901234L), "-1234567890123456789012345678901234");
+        test(make_i128(-123456789L, 123456789012L, 3456789012345L), "-12345678901234567890123456789012345");
+        test(make_i128(-1234567890L, 1234567890123L, 4567890123456L), "-123456789012345678901234567890123456");
+        test(make_i128(-12345678901L, 2345678901234L, 5678901234567L), "-1234567890123456789012345678901234567");
+        test(make_i128(-123456789012L, 3456789012345L, 6789012345678L), "-12345678901234567890123456789012345678");
+        test(make_i128(-1234567890123L, 4567890123456L, 7890123456789L), "-123456789012345678901234567890123456789");
+#endif
 
         // Test special cases with zeros inside a value string representation,
         // to_chars algorithm processes them in a special way and should not
@@ -161,6 +253,29 @@ struct test_signed : to_chars_test_base<T>
         test(-10000000000000000L, "-10000000000000000");
         test(-100000000000000000L, "-100000000000000000");
         test(-1000000000000000000L, "-1000000000000000000");
+#ifndef TEST_HAS_NO_INT128
+        test(make_i128(-1L, 0L), "-10000000000000000000");
+        test(make_i128(-10L, 0L), "-100000000000000000000");
+        test(make_i128(-100L, 0L), "-1000000000000000000000");
+        test(make_i128(-1000L, 0L), "-10000000000000000000000");
+        test(make_i128(-10000L, 0L), "-100000000000000000000000");
+        test(make_i128(-100000L, 0L), "-1000000000000000000000000");
+        test(make_i128(-1000000L, 0L), "-10000000000000000000000000");
+        test(make_i128(-10000000L, 0L), "-100000000000000000000000000");
+        test(make_i128(-100000000L, 0L), "-1000000000000000000000000000");
+        test(make_i128(-1000000000L, 0L), "-10000000000000000000000000000");
+        test(make_i128(-100L, 0L, 0L), "-10000000000000000000000000000");
+        test(make_i128(-1000L, 0L, 0L), "-100000000000000000000000000000");
+        test(make_i128(-10000L, 0L, 0L), "-1000000000000000000000000000000");
+        test(make_i128(-100000L, 0L, 0L), "-10000000000000000000000000000000");
+        test(make_i128(-1000000L, 0L, 0L), "-100000000000000000000000000000000");
+        test(make_i128(-10000000L, 0L, 0L), "-1000000000000000000000000000000000");
+        test(make_i128(-100000000L, 0L, 0L), "-10000000000000000000000000000000000");
+        test(make_i128(-1000000000L, 0L, 0L), "-100000000000000000000000000000000000");
+        test(make_i128(-10000000000L, 0L, 0L), "-1000000000000000000000000000000000000");
+        test(make_i128(-100000000000L, 0L, 0L), "-10000000000000000000000000000000000000");
+        test(make_i128(-1000000000000L, 0L, 0L), "-100000000000000000000000000000000000000");
+#endif
 
         for (int b = 2; b < 37; ++b)
         {

diff  --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index 32a8d48b4bfd3..c8d104510f26f 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -71,8 +71,7 @@ template <typename X, typename T>
 constexpr bool
 fits_in(T v)
 {
-    return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(),
-                       std::is_signed<X>());
+  return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(), std::is_signed<X>());
 }
 
 template <typename X>
@@ -115,8 +114,16 @@ struct to_chars_test_base
             assert(buf[i] == static_cast<char>(i + 1));
         *r.ptr = '\0';
 
-        auto a = fromchars(buf, r.ptr, args...);
-        assert(v == a);
+#ifndef TEST_HAS_NO_INT128
+        if (sizeof(X) == sizeof(__int128_t)) {
+            auto a = fromchars128_impl(buf, r.ptr, args...);
+            assert(v == a);
+        } else
+#endif
+        {
+            auto a = fromchars_impl(buf, r.ptr, args...);
+            assert(v == a);
+        }
 
         auto ep = r.ptr - 1;
         r = to_chars(buf, ep, v, args...);
@@ -125,7 +132,7 @@ struct to_chars_test_base
     }
 
 private:
-    static long long fromchars(char const* p, char const* ep, int base, true_type)
+    static long long fromchars_impl(char const* p, char const* ep, int base, true_type)
     {
         char* last;
         auto r = strtoll(p, &last, base);
@@ -134,7 +141,7 @@ struct to_chars_test_base
         return r;
     }
 
-    static unsigned long long fromchars(char const* p, char const* ep, int base, false_type)
+    static unsigned long long fromchars_impl(char const* p, char const* ep, int base, false_type)
     {
         char* last;
         auto r = strtoull(p, &last, base);
@@ -142,14 +149,57 @@ struct to_chars_test_base
 
         return r;
     }
+#ifndef TEST_HAS_NO_INT128
+    static __int128_t fromchars128_impl(char const* p, char const* ep, int base, true_type)
+    {
+        char* last;
+        __int128_t r = strtoll(p, &last, base);
+        if(errno != ERANGE) {
+            assert(last == ep);
+            return r;
+        }
+
+        // When the value doesn't fit in a long long use from_chars. This is
+        // not ideal since it does a round-trip test instead if using an
+        // external source.
+        std::from_chars_result s = std::from_chars(p, ep, r, base);
+        assert(s.ec == std::errc{});
+        assert(s.ptr == ep);
 
-    static auto fromchars(char const* p, char const* ep, int base = 10)
-    -> decltype(fromchars(p, ep, base, std::is_signed<X>()))
+        return r;
+    }
+
+    static __uint128_t fromchars128_impl(char const* p, char const* ep, int base, false_type)
     {
-        return fromchars(p, ep, base, std::is_signed<X>());
+        char* last;
+        __uint128_t r = strtoull(p, &last, base);
+        if(errno != ERANGE) {
+            assert(last == ep);
+            return r;
+        }
+
+        std::from_chars_result s = std::from_chars(p, ep, r, base);
+        assert(s.ec == std::errc{});
+        assert(s.ptr == ep);
+
+        return r;
+    }
+
+    static auto fromchars128_impl(char const* p, char const* ep, int base = 10)
+    -> decltype(fromchars128_impl(p, ep, base, std::is_signed<X>()))
+    {
+        return fromchars128_impl(p, ep, base, std::is_signed<X>());
+    }
+
+#endif
+
+    static auto fromchars_impl(char const* p, char const* ep, int base = 10)
+    -> decltype(fromchars_impl(p, ep, base, std::is_signed<X>()))
+    {
+        return fromchars_impl(p, ep, base, std::is_signed<X>());
     }
 
-    char buf[100];
+    char buf[150];
 };
 
 template <typename X>
@@ -201,7 +251,7 @@ struct roundtrip_test_base
     }
 
 private:
-    char buf[100];
+    char buf[150];
 };
 
 template <typename... T>
@@ -227,9 +277,29 @@ constexpr auto concat(L1, L2) -> concat_t<L1, L2>
     return {};
 }
 
-auto all_signed = type_list<char, signed char, short, int, long, long long>();
-auto all_unsigned = type_list<unsigned char, unsigned short, unsigned int,
-                              unsigned long, unsigned long long>();
+auto all_signed = type_list<
+    char,
+    signed char,
+    short,
+    int,
+    long,
+    long long
+#ifndef TEST_HAS_NO_INT128
+    ,
+    __int128_t
+#endif
+    >();
+auto all_unsigned = type_list<
+    unsigned char,
+    unsigned short,
+    unsigned int,
+    unsigned long,
+    unsigned long long
+#ifndef TEST_HAS_NO_INT128
+    ,
+    __uint128_t
+#endif
+    >();
 auto integrals = concat(all_signed, all_unsigned);
 
 template <template <typename> class Fn, typename... Ts>


        


More information about the libcxx-commits mailing list