Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions stl/inc/limits
Original file line number Diff line number Diff line change
Expand Up @@ -1063,8 +1063,6 @@ extern "C" {
extern int __isa_available;
}

#if _HAS_CXX20

#ifdef __clang__
#define _TZCNT_U32 __builtin_ia32_tzcnt_u32
#define _TZCNT_U64 __builtin_ia32_tzcnt_u64
Expand Down Expand Up @@ -1150,8 +1148,6 @@ _NODISCARD int _Checked_x86_x64_countr_zero(const _Ty _Val) noexcept {
#endif // __AVX2__
}

#endif // _HAS_CXX20

#endif // _HAS_TZCNT_BSF_INTRINSICS

#if (defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC))) && !defined(_M_CEE_PURE) && !defined(__CUDACC__) \
Expand Down Expand Up @@ -1202,16 +1198,39 @@ constexpr bool _Is_standard_unsigned_integer =
_Is_any_of_v<remove_cv_t<_Ty>, unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long>;

template <class _Ty, enable_if_t<_Is_standard_unsigned_integer<_Ty>, int> = 0>
_NODISCARD constexpr int _Countr_zero(const _Ty _Val) noexcept {
#if _HAS_CXX20 && _HAS_TZCNT_BSF_INTRINSICS
if (!_STD is_constant_evaluated()) {
_NODISCARD _CONSTEXPR20 int _Countr_zero(const _Ty _Val) noexcept {
#if _HAS_TZCNT_BSF_INTRINSICS
#if _HAS_CXX20
if (!_STD is_constant_evaluated())
#endif // _HAS_CXX20
{
return _Checked_x86_x64_countr_zero(_Val);
}
#endif // _HAS_CXX20 && _HAS_TZCNT_BSF_INTRINSICS
// C++17 constexpr gcd() calls this function, so it should be constexpr unless we detect runtime evaluation.
#endif // _HAS_TZCNT_BSF_INTRINSICS
return _Countr_zero_fallback(_Val);
}

template <class _Ty, class _Fn>
constexpr decltype(auto) _Select_countr_zero_impl(_Fn _Callback) {
// TRANSITION, DevCom-1527995: Lambdas in this function ensure inlining
#if _HAS_TZCNT_BSF_INTRINSICS && _HAS_CXX20
if (!_STD is_constant_evaluated()) {
#ifdef __AVX2__
return _Callback([](_Ty _Val) { return _Countr_zero_tzcnt(_Val); });
#else // ^^^ AVX2 ^^^ / vvv not AVX2 vvv
const bool _Definitely_have_tzcnt = __isa_available >= __ISA_AVAILABLE_AVX2;
if (_Definitely_have_tzcnt) {
return _Callback([](_Ty _Val) { return _Countr_zero_tzcnt(_Val); });
} else {
return _Callback([](_Ty _Val) { return _Countr_zero_bsf(_Val); });
}
#endif // ^^^ not AVX2 ^^^
}
#endif // ^^^ _HAS_TZCNT_BSF_INTRINSICS && _HAS_CXX20 ^^^
// C++17 constexpr gcd() calls this function, so it should be constexpr unless we detect runtime evaluation.
return _Callback([](_Ty _Val) { return _Countr_zero_fallback(_Val); });
}

template <class _Ty, enable_if_t<_Is_standard_unsigned_integer<_Ty>, int> _Enabled = 0>
_NODISCARD _CONSTEXPR20 int _Popcount(const _Ty _Val) noexcept {
#if _HAS_POPCNT_INTRINSICS || _HAS_NEON_INTRINSICS
Expand Down
52 changes: 27 additions & 25 deletions stl/inc/numeric
Original file line number Diff line number Diff line change
Expand Up @@ -548,35 +548,37 @@ _NODISCARD constexpr common_type_t<_Mt, _Nt> gcd(const _Mt _Mx, const _Nt _Nx) n
// calculate greatest common divisor
static_assert(_Is_nonbool_integral<_Mt> && _Is_nonbool_integral<_Nt>, "GCD requires nonbool integral types");

using _Common = common_type_t<_Mt, _Nt>;
using _Common_unsigned = make_unsigned_t<_Common>;
_Common_unsigned _Mx_magnitude = _Abs_u(_Mx);
_Common_unsigned _Nx_magnitude = _Abs_u(_Nx);
if (_Mx_magnitude == 0U) {
return static_cast<_Common>(_Nx_magnitude);
}

if (_Nx_magnitude == 0U) {
return static_cast<_Common>(_Mx_magnitude);
}
using _Common = common_type_t<_Mt, _Nt>;
using _Common_unsigned = make_unsigned_t<_Common>;

return _Select_countr_zero_impl<_Common_unsigned>([=](auto _Countr_zero_impl) {
_Common_unsigned _Mx_magnitude = _Abs_u(_Mx);
_Common_unsigned _Nx_magnitude = _Abs_u(_Nx);
if (_Mx_magnitude == 0U) {
return static_cast<_Common>(_Nx_magnitude);
}

const auto _Mx_trailing_zeroes = static_cast<unsigned long>(_Countr_zero(_Mx_magnitude));
const auto _Common_factors_of_2 =
(_STD min)(_Mx_trailing_zeroes, static_cast<unsigned long>(_Countr_zero(_Nx_magnitude)));
_Nx_magnitude >>= _Common_factors_of_2;
_Mx_magnitude >>= _Mx_trailing_zeroes;
do {
_Nx_magnitude >>= static_cast<unsigned long>(_Countr_zero(_Nx_magnitude));
if (_Mx_magnitude > _Nx_magnitude) {
_Common_unsigned _Temp = _Mx_magnitude;
_Mx_magnitude = _Nx_magnitude;
_Nx_magnitude = _Temp;
if (_Nx_magnitude == 0U) {
return static_cast<_Common>(_Mx_magnitude);
}

_Nx_magnitude -= _Mx_magnitude;
} while (_Nx_magnitude != 0U);
const auto _Mx_trailing_zeroes = static_cast<unsigned long>(_Countr_zero_impl(_Mx_magnitude));
const auto _Common_factors_of_2 =
(_STD min)(_Mx_trailing_zeroes, static_cast<unsigned long>(_Countr_zero_impl(_Nx_magnitude)));
_Nx_magnitude >>= _Common_factors_of_2;
_Mx_magnitude >>= _Mx_trailing_zeroes;
do {
_Nx_magnitude >>= static_cast<unsigned long>(_Countr_zero_impl(_Nx_magnitude));
if (_Mx_magnitude > _Nx_magnitude) {
_Common_unsigned _Temp = _Mx_magnitude;
_Mx_magnitude = _Nx_magnitude;
_Nx_magnitude = _Temp;
}

return static_cast<_Common>(_Mx_magnitude << _Common_factors_of_2);
_Nx_magnitude -= _Mx_magnitude;
} while (_Nx_magnitude != 0U);
return static_cast<_Common>(_Mx_magnitude << _Common_factors_of_2);
});
}

template <class _Mt, class _Nt>
Expand Down
26 changes: 26 additions & 0 deletions tests/std/tests/Dev11_1074023_constexpr/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <mutex>
#endif // _M_CEE
#include <new>
#include <numeric>
#include <random>
#include <ratio>
#include <regex>
Expand Down Expand Up @@ -956,6 +957,27 @@ static_assert(hardware_constructive_interference_size == 64);
static_assert(hardware_destructive_interference_size == 64);
#endif // _HAS_CXX17

// P0295R0 gcd(), lcm()
constexpr bool test_gcd_lcm() {
#if _HAS_CXX17
assert(gcd(0, 0) == 0);
assert(gcd(3125, 2401) == 1);
assert(gcd(3840, 2160) == 240);
assert(gcd(4096, 8192) == 4096);
assert(gcd(19937, 19937) == 19937);

assert(lcm(0, 0) == 0);
assert(lcm(0, 1729) == 0);
assert(lcm(1729, 0) == 0);
assert(lcm(1729, 1729) == 1729);
assert(lcm(4096, 8192) == 8192);
assert(lcm(1920, 1200) == 9600);
assert(lcm(25, 49) == 1225);
#endif // _HAS_CXX17

return true;
}

int main() {
test_all_constants();
test_all_bitmasks();
Expand Down Expand Up @@ -1020,4 +1042,8 @@ int main() {
== "1111111011011100101110101001100001110110010101000011001000010000");
assert(bitset<75>(0xFEDCBA9876543210ULL).to_string()
== "000000000001111111011011100101110101001100001110110010101000011001000010000");

// P0295R0 gcd(), lcm()
test_gcd_lcm();
STATIC_ASSERT(test_gcd_lcm());
}