https://gcc.gnu.org/g:675b6856ca66366632598cb0f5ccedbb337ccc0b

commit r17-4533-g675b6856ca66366632598cb0f5ccedbb337ccc0b
Author: Tomasz KamiƄski <[email protected]>
Date:   Tue Jul 28 11:13:26 2026 +0200

    libstdc++: Use of Lemire's algorithm for targets that do not provide 
__UINT32_TYPE__ && __UINT64_TYPE__.
    
    As the implementation already mask lowest _Bits when converting __product
    to _Up, it works for any integer with at least _Bits, and no longer
    requires integers with exactly 32 (__UINT32_TYPE__) or 64 (__UINT64_TYPE__).
    
    The type _Up and _Wp are now defined in _Select_uint_least_t of _Bits
    and 2 * _Bits respectively. Thus primary _Select_uint_least_t primary
    template and partial specializations are now extracted to uniform_int_dist.h
    header. The measurments on x86_32 indicated that using Lemire algorithm
    with __rand_uint128 leads to worse performance, we only forward declared
    corresponding _Select_uint_least_t specialization in uniform_int_dist.h,
    and keep it definition (and __rand_uint128) in much larger bits/random.h
    header.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/random.h (_Select_uint_least_t): Move primary
            template and most of partial specializations to...
            * include/bits/uniform_int_dist.h (_Select_uint_least_t):
            Extracted from bits/random.h.
            (uniform_int_distribution::_S_nd): Replace _Up and _Wp template
            paramteters with Select_uint_least_t of _Bits and 2 * _Bits
            respectively.
            (uniform_int_distribution::operator()): Adjust calls to _S_nd
            to pass only _Bits, and remove checks for presence of
            __UINT64_TYPE__ and __UINT32_TYPE__ types.
    
    Reviewed-by: Jonathan Wakely <[email protected]>

Diff:
---
 libstdc++-v3/include/bits/random.h           | 34 ++--------------
 libstdc++-v3/include/bits/uniform_int_dist.h | 61 +++++++++++++++++++++-------
 2 files changed, 51 insertions(+), 44 deletions(-)

diff --git a/libstdc++-v3/include/bits/random.h 
b/libstdc++-v3/include/bits/random.h
index 1a2900e2b9d8..e2b29961bd92 100644
--- a/libstdc++-v3/include/bits/random.h
+++ b/libstdc++-v3/include/bits/random.h
@@ -569,36 +569,10 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2)
       struct _Shift<_UIntType, __w, true>
       { static constexpr _UIntType __value = _UIntType(1) << __w; };
 
-    template<int __s,
-            int __which = ((__s <= __CHAR_BIT__ * sizeof (int))
-                           + (__s <= __CHAR_BIT__ * sizeof (long))
-                           + (__s <= __CHAR_BIT__ * sizeof (long long))
-                           /* assume long long no bigger than __int128 */
-                           + (__s <= 128))>
-      struct _Select_uint_least_t
-      {
-       static_assert(__which < 0, /* needs to be dependent */
-                     "sorry, would be too much trouble for a slow result");
-      };
-
-    template<int __s>
-      struct _Select_uint_least_t<__s, 4>
-      { using type = unsigned int; };
-
-    template<int __s>
-      struct _Select_uint_least_t<__s, 3>
-      { using type = unsigned long; };
-
-    template<int __s>
-      struct _Select_uint_least_t<__s, 2>
-      { using type = unsigned long long; };
-
-#if __SIZEOF_INT128__ > __SIZEOF_LONG_LONG__
-    template<int __s>
-      struct _Select_uint_least_t<__s, 1>
-      { __extension__ using type = unsigned __int128; };
-#elif __has_builtin(__builtin_add_overflow) \
-    && __has_builtin(__builtin_sub_overflow) \
+// Primary template and other specializtions are defined in 
bits/uniform_int_dist.h.
+#if __SIZEOF_INT128__ <= __SIZEOF_LONG_LONG__ \
+    && __has_builtin(__builtin_add_overflow)  \
+    && __has_builtin(__builtin_sub_overflow)  \
     && defined __UINT64_TYPE__
     template<int __s>
       struct _Select_uint_least_t<__s, 1>
diff --git a/libstdc++-v3/include/bits/uniform_int_dist.h 
b/libstdc++-v3/include/bits/uniform_int_dist.h
index f19f051b8052..554a82cd72ee 100644
--- a/libstdc++-v3/include/bits/uniform_int_dist.h
+++ b/libstdc++-v3/include/bits/uniform_int_dist.h
@@ -72,6 +72,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       {
        return ((__x - 1) & __x) == 0;
       }
+
+    template<int __s,
+            int __which = ((__s <= __CHAR_BIT__ * sizeof (int))
+                           + (__s <= __CHAR_BIT__ * sizeof (long))
+                           + (__s <= __CHAR_BIT__ * sizeof (long long))
+                           /* assume long long no bigger than __int128 */
+                           + (__s <= 128))>
+      struct _Select_uint_least_t
+      {
+       static_assert(__which < 0, /* needs to be dependent */
+                     "sorry, would be too much trouble for a slow result");
+      };
+
+    template<int __s>
+      struct _Select_uint_least_t<__s, 4>
+      { using type = unsigned int; };
+
+    template<int __s>
+      struct _Select_uint_least_t<__s, 3>
+      { using type = unsigned long; };
+
+    template<int __s>
+      struct _Select_uint_least_t<__s, 2>
+      { using type = unsigned long long; };
+
+// Alternate specialization for  
+#if __SIZEOF_INT128__ > __SIZEOF_LONG_LONG__
+    template<int __s>
+      struct _Select_uint_least_t<__s, 1>
+      { __extension__ using type = unsigned __int128; };
+#elif __has_builtin(__builtin_add_overflow)  \
+    && __has_builtin(__builtin_sub_overflow)  \
+    && defined __UINT64_TYPE__
+    template<int __s>
+      struct _Select_uint_least_t<__s, 1>; // Defined in bits/random.h
+#endif
   }
   /// @endcond
 
@@ -252,16 +288,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Lemire's nearly divisionless algorithm.
       // Returns an unbiased random number from __g downscaled to [0,__range)
       // using an unsigned type _Wp twice as wide as unsigned type _Up.
-      template<size_t _Bits, typename _Wp, typename _Urbg, typename _Up>
-       static _Up
-       _S_nd(_Urbg& __g, _Up __range)
+      template<size_t _Bits, typename _Urbg>
+       static typename __detail::_Select_uint_least_t<_Bits>::type
+       _S_nd(_Urbg& __g, typename __detail::_Select_uint_least_t<_Bits>::type 
__range)
        {
+         using _Up = typename __detail::_Select_uint_least_t<_Bits>::type;
+         using _Wp = typename __detail::_Select_uint_least_t<2 * _Bits>::type;
          using _Up_traits = __gnu_cxx::__int_traits<_Up>;
-         using _Wp_traits = __gnu_cxx::__int_traits<_Wp>;
-         static_assert(!_Up_traits::__is_signed, "U must be unsigned");
-         static_assert(!_Wp_traits::__is_signed, "W must be unsigned");
-         static_assert(_Wp_traits::__digits == (2 * _Up_traits::__digits),
-                       "W must be twice as wide as U");
+
          constexpr auto __min = _Urbg::min();
          constexpr _Up __mask = (_Bits < _Up_traits::__digits)
                                 ? (_Up(1) << _Bits) - 1 : ~_Up(0);
@@ -324,23 +358,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
-#if defined __UINT64_TYPE__ && defined __UINT32_TYPE__ && 
__has_builtin(__builtin_popcountg)
+#if __has_builtin(__builtin_popcountg)
            constexpr auto __bits = __builtin_popcountg(__urngrange);
            if constexpr (__detail::_Power_of_2(__urngrange + 1) && __bits <= 
32)
              {
                // __urng produces values that use no more than 32-bits,
-               // so use 64-bit integers to downscale to desired range.
+               // so 64-bit integer is sufficient to downscale to desired 
range.
                __UINT32_TYPE__ __u32erange = __uerange;
-               __ret = _S_nd<__bits, __UINT64_TYPE__>(__urng, __u32erange);
+               __ret = _S_nd<__bits>(__urng, __u32erange);
              }
 # if __SIZEOF_INT128__
            else if constexpr (__detail::_Power_of_2(__urngrange + 1) && __bits 
<= 64)
              {
                // __urng produces values that use no more than 64-bits,
-               // so use 128-bit integers to downscale to desired range.
+               // so 128-bit integer is sufficient to downscale to desired 
range.
                __UINT64_TYPE__ __u64erange = __uerange;
-               __ret = __extension__ _S_nd<__bits, unsigned __int128>(
-                         __urng, __u64erange);
+               __ret = _S_nd<__bits>(__urng, __u64erange);
              }
 # endif
            else

Reply via email to