From 2a96a0e176bf54fb8240160b6c53b5c228c888d7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 25 Apr 2025 06:43:52 -0700 Subject: [PATCH 1/3] `_Uint _Cx` always satisfies `_Cx <= ULLONG_MAX`. --- stl/inc/random | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/random b/stl/inc/random index f866dab8eaa..d68e98d55d3 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -500,7 +500,7 @@ _NODISCARD _Uint _Next_linear_congruential_value(_Uint _Prev) noexcept { const auto _Mul = static_cast(_Prev) * static_cast(_Ax) + static_cast(_Cx); return static_cast<_Uint>(_Mul % _Mx); - } else if constexpr (_Cx <= ULLONG_MAX && static_cast<_Uint>(_Mx - 1) <= (ULLONG_MAX - _Cx) / _Ax) { + } else if constexpr (static_cast<_Uint>(_Mx - 1) <= (ULLONG_MAX - _Cx) / _Ax) { // unsigned long long is sufficient to store intermediate calculation const auto _Mul = static_cast(_Prev) * _Ax + _Cx; return static_cast<_Uint>(_Mul % _Mx); From 2d337f934913e4656e38d212095797b9d5490d67 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 25 Apr 2025 07:12:37 -0700 Subject: [PATCH 2/3] Use `_Unsigned128`, preserve multprec.cpp for bincompat. We can drop `_INLINE_VAR` from `_MP_len`, giving it internal linkage within multprec.cpp. We don't need `extern "C++"` on the declarations - that was for modules. We need to add `_CRTIMP2_PURE` to the definitions. I thought we had harmonized all declarations and definitions, but we missed these. --- stl/inc/random | 15 ++------------- stl/src/multprec.cpp | 15 +++++++++++---- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/stl/inc/random b/stl/inc/random index d68e98d55d3..90ae36415a5 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -461,14 +461,6 @@ _NODISCARD _Real _Nrand_impl(_Gen& _Gx) { // build a floating-point value from r } } -_INLINE_VAR constexpr int _MP_len = 5; -using _MP_arr = uint64_t[_MP_len]; - -extern "C++" _NODISCARD _CRTIMP2_PURE uint64_t __CLRCALL_PURE_OR_CDECL _MP_Get(_MP_arr) noexcept; -extern "C++" _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _MP_Add(_MP_arr, uint64_t) noexcept; -extern "C++" _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _MP_Mul(_MP_arr, uint64_t, uint64_t) noexcept; -extern "C++" _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _MP_Rem(_MP_arr, uint64_t) noexcept; - template _NODISCARD _Uint _Next_linear_congruential_value(_Uint _Prev) noexcept { // Choose intermediate type: @@ -505,11 +497,8 @@ _NODISCARD _Uint _Next_linear_congruential_value(_Uint _Prev) noexcept { const auto _Mul = static_cast(_Prev) * _Ax + _Cx; return static_cast<_Uint>(_Mul % _Mx); } else { // no intermediate integral type fits; fall back to multiprecision - _MP_arr _Wx; - _MP_Mul(_Wx, _Prev, _Ax); - _MP_Add(_Wx, _Cx); - _MP_Rem(_Wx, _Mx); - return static_cast<_Uint>(_MP_Get(_Wx)); + const auto _Mul = _Unsigned128{_Prev} * _Ax + _Cx; + return static_cast<_Uint>(_Mul % _Mx); } } diff --git a/stl/src/multprec.cpp b/stl/src/multprec.cpp index 0043cfbfb96..0b877a215ad 100644 --- a/stl/src/multprec.cpp +++ b/stl/src/multprec.cpp @@ -7,11 +7,15 @@ #include _STD_BEGIN +constexpr int _MP_len = 5; +using _MP_arr = uint64_t[_MP_len]; + constexpr int shift = _STD numeric_limits::digits / 2; constexpr unsigned long long mask = ~(~0ULL << shift); constexpr unsigned long long maxVal = mask + 1; -[[nodiscard]] unsigned long long __CLRCALL_PURE_OR_CDECL _MP_Get( +// TRANSITION, ABI: preserved for binary compatibility +[[nodiscard]] _CRTIMP2_PURE unsigned long long __CLRCALL_PURE_OR_CDECL _MP_Get( _MP_arr u) noexcept { // convert multi-word value to scalar value return (u[1] << shift) + u[0]; } @@ -32,7 +36,8 @@ static void add(unsigned long long* u, int ulen, unsigned long long* v, } } -void __CLRCALL_PURE_OR_CDECL _MP_Add( +// TRANSITION, ABI: preserved for binary compatibility +_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _MP_Add( _MP_arr u, unsigned long long v0) noexcept { // add scalar value to multi-word value unsigned long long v[2]; v[0] = v0 & mask; @@ -50,7 +55,8 @@ static void mul( } } -void __CLRCALL_PURE_OR_CDECL _MP_Mul( +// TRANSITION, ABI: preserved for binary compatibility +_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _MP_Mul( _MP_arr w, unsigned long long u0, unsigned long long v0) noexcept { // multiply multi-word value by multi-word value constexpr int m = 2; constexpr int n = 2; @@ -106,7 +112,8 @@ static void div(_MP_arr u, return ulen; } -void __CLRCALL_PURE_OR_CDECL _MP_Rem( +// TRANSITION, ABI: preserved for binary compatibility +_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _MP_Rem( _MP_arr u, unsigned long long v0) noexcept { // divide multi-word value by value, leaving remainder in u unsigned long long v[2]; v[0] = v0 & mask; From a102151c47e1cc0328eaf7c1c089d589c40456ff Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 25 Apr 2025 07:26:10 -0700 Subject: [PATCH 3/3] Reduce multprec.cpp dependencies to slightly improve throughput. We can hardcode `64` instead of including `` for `numeric_limits::digits`. `uint64_t` and `unsigned long long` are synonyms. In fact, this file was already using `unsigned long long` for the definitions, even though the declarations originally said `uint64_t`. We moved the declarations out of `` so we don't need it anymore. All we need is ``. --- stl/src/multprec.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stl/src/multprec.cpp b/stl/src/multprec.cpp index 0b877a215ad..326fd9cbb7c 100644 --- a/stl/src/multprec.cpp +++ b/stl/src/multprec.cpp @@ -3,14 +3,13 @@ // implements multiprecision math for random number generators -#include -#include +#include _STD_BEGIN constexpr int _MP_len = 5; -using _MP_arr = uint64_t[_MP_len]; +using _MP_arr = unsigned long long[_MP_len]; -constexpr int shift = _STD numeric_limits::digits / 2; +constexpr int shift = 64 / 2; constexpr unsigned long long mask = ~(~0ULL << shift); constexpr unsigned long long maxVal = mask + 1;