From b1e4baf7e7d6fdd64e4da3b605fd17f1157eef82 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 18 Jun 2024 23:08:02 +0300 Subject: [PATCH 01/35] Sedicated test coverage for floating minmax of +0.0 and -0.0 only --- .../VSO_0000000_vector_algorithms/test.cpp | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 5f817218064..2a7ffeedde7 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -392,7 +392,7 @@ void test_min_max_element(mt19937_64& gen) { } template -void test_min_max_element_floating(mt19937_64& gen) { +void test_min_max_element_floating_any(mt19937_64& gen) { normal_distribution dis(-100000.0, 100000.0); constexpr auto input_of_input_size = dataCount / 2; @@ -416,6 +416,27 @@ void test_min_max_element_floating(mt19937_64& gen) { } } +template +void test_min_max_element_floating_zero(mt19937_64& gen) { + vector input_of_input{-0.0, +0.0}; + + uniform_int_distribution idx_dis(0, input_of_input.size() - 1); + + vector input; + input.reserve(dataCount); + test_case_min_max_element(input); + for (size_t attempts = 0; attempts < dataCount; ++attempts) { + input.push_back(input_of_input[idx_dis(gen)]); + test_case_min_max_element(input); + } +} + +template +void test_min_max_element_floating(mt19937_64& gen) { + test_min_max_element_floating_any(gen); + test_min_max_element_floating_zero(gen); +} + void test_min_max_element_pointers(mt19937_64& gen) { const short arr[20]{}; From 18cc8b103933609e7b348cf7765456a31c0cfaad Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 19 Jun 2024 09:00:07 +0300 Subject: [PATCH 02/35] expand test --- tests/std/include/test_min_max_element_support.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/std/include/test_min_max_element_support.hpp b/tests/std/include/test_min_max_element_support.hpp index a717c7ef85f..22e8ae5ff07 100644 --- a/tests/std/include/test_min_max_element_support.hpp +++ b/tests/std/include/test_min_max_element_support.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -116,6 +117,13 @@ void test_case_min_max_element(const std::vector& input) { assert(*expected_max == actual_max_value); assert(*expected_minmax.first == actual_minmax_value.min); assert(*expected_minmax.second == actual_minmax_value.max); + + if constexpr (std::is_floating_point_v) { + assert(signbit(*expected_min) == signbit(actual_min_value)); + assert(signbit(*expected_max) == signbit(actual_max_value)); + assert(signbit(*expected_minmax.first) == signbit(actual_minmax_value.min)); + assert(signbit(*expected_minmax.second) == signbit(actual_minmax_value.max)); + } } #endif // _HAS_CXX20 } From fba7701b2c4498596565cf0b566fd491e7711e2c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 20 Jun 2024 19:13:35 +0300 Subject: [PATCH 03/35] expand test with canned simple case --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 2a7ffeedde7..f8c5c550683 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -433,6 +433,8 @@ void test_min_max_element_floating_zero(mt19937_64& gen) { template void test_min_max_element_floating(mt19937_64& gen) { + test_case_min_max_element(vector{+0.0, -0.0}); + test_min_max_element_floating_any(gen); test_min_max_element_floating_zero(gen); } From f0b43fba1d24f5ca420821e50eef1af882968522 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 20 Jun 2024 19:13:50 +0300 Subject: [PATCH 04/35] Fix the bug --- stl/src/vector_algorithms.cpp | 161 +++++++++++++++++++++++++++++----- 1 file changed, 138 insertions(+), 23 deletions(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 44cca169203..3a69d7e033e 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -1415,8 +1415,8 @@ namespace { template static __m128 _H_func(const __m128 _Cur, _Fn _Funct) noexcept { __m128 _H_min_val = _Cur; - _H_min_val = _Funct(_H_min_val, _mm_shuffle_ps(_H_min_val, _H_min_val, _MM_SHUFFLE(1, 0, 3, 2))); - _H_min_val = _Funct(_H_min_val, _mm_shuffle_ps(_H_min_val, _H_min_val, _MM_SHUFFLE(2, 3, 0, 1))); + _H_min_val = _Funct(_mm_shuffle_ps(_H_min_val, _H_min_val, _MM_SHUFFLE(2, 3, 0, 1)), _H_min_val); + _H_min_val = _Funct(_mm_shuffle_ps(_H_min_val, _H_min_val, _MM_SHUFFLE(1, 0, 3, 2)), _H_min_val); return _H_min_val; } @@ -1428,6 +1428,10 @@ namespace { return _H_func(_Cur, [](__m128 _Val1, __m128 _Val2) { return _mm_max_ps(_Val1, _Val2); }); } + static __m128 _H_max_r(const __m128 _Cur) noexcept { + return _H_func(_Cur, [](__m128 _Val1, __m128 _Val2) { return _mm_max_ps(_Val2, _Val1); }); + } + static __m128i _H_min_u(const __m128i _Cur) noexcept { return _Minmax_traits_4_sse::_H_min_u(_Cur); } @@ -1457,10 +1461,14 @@ namespace { } static __m128 _Min(const __m128 _First, const __m128 _Second, __m128 = _mm_undefined_ps()) noexcept { - return _mm_min_ps(_First, _Second); + return _mm_min_ps(_Second, _First); } static __m128 _Max(const __m128 _First, const __m128 _Second, __m128 = _mm_undefined_ps()) noexcept { + return _mm_max_ps(_Second, _First); + } + + static __m128 _Max_r(const __m128 _First, const __m128 _Second, __m128 = _mm_undefined_ps()) noexcept { return _mm_max_ps(_First, _Second); } @@ -1485,9 +1493,9 @@ namespace { template static __m256 _H_func(const __m256 _Cur, _Fn _Funct) noexcept { __m256 _H_min_val = _Cur; - _H_min_val = _Funct(_H_min_val, _mm256_permute2f128_ps(_H_min_val, _mm256_undefined_ps(), 0x01)); - _H_min_val = _Funct(_H_min_val, _mm256_shuffle_ps(_H_min_val, _H_min_val, _MM_SHUFFLE(1, 0, 3, 2))); - _H_min_val = _Funct(_H_min_val, _mm256_shuffle_ps(_H_min_val, _H_min_val, _MM_SHUFFLE(2, 3, 0, 1))); + _H_min_val = _Funct(_mm256_shuffle_ps(_H_min_val, _H_min_val, _MM_SHUFFLE(2, 3, 0, 1)), _H_min_val); + _H_min_val = _Funct(_mm256_shuffle_ps(_H_min_val, _H_min_val, _MM_SHUFFLE(1, 0, 3, 2)), _H_min_val); + _H_min_val = _Funct(_mm256_permute2f128_ps(_H_min_val, _mm256_undefined_ps(), 0x01), _H_min_val); return _H_min_val; } @@ -1499,6 +1507,10 @@ namespace { return _H_func(_Cur, [](__m256 _Val1, __m256 _Val2) { return _mm256_max_ps(_Val1, _Val2); }); } + static __m256 _H_max_r(const __m256 _Cur) noexcept { + return _H_func(_Cur, [](__m256 _Val1, __m256 _Val2) { return _mm256_max_ps(_Val2, _Val1); }); + } + static __m256i _H_min_u(const __m256i _Cur) noexcept { return _Minmax_traits_4_avx::_H_min_u(_Cur); } @@ -1528,10 +1540,14 @@ namespace { } static __m256 _Min(const __m256 _First, const __m256 _Second, __m256 = _mm256_undefined_ps()) noexcept { - return _mm256_min_ps(_First, _Second); + return _mm256_min_ps(_Second, _First); } static __m256 _Max(const __m256 _First, const __m256 _Second, __m256 = _mm256_undefined_ps()) noexcept { + return _mm256_max_ps(_Second, _First); + } + + static __m256 _Max_r(const __m256 _First, const __m256 _Second, __m256 = _mm256_undefined_ps()) noexcept { return _mm256_max_ps(_First, _Second); } @@ -1575,7 +1591,7 @@ namespace { template static __m128d _H_func(const __m128d _Cur, _Fn _Funct) noexcept { __m128d _H_min_val = _Cur; - _H_min_val = _Funct(_H_min_val, _mm_shuffle_pd(_H_min_val, _H_min_val, 1)); + _H_min_val = _Funct(_mm_shuffle_pd(_H_min_val, _H_min_val, 1), _H_min_val); return _H_min_val; } @@ -1587,6 +1603,10 @@ namespace { return _H_func(_Cur, [](__m128d _Val1, __m128d _Val2) { return _mm_max_pd(_Val1, _Val2); }); } + static __m128d _H_max_r(const __m128d _Cur) noexcept { + return _H_func(_Cur, [](__m128d _Val1, __m128d _Val2) { return _mm_max_pd(_Val2, _Val1); }); + } + static __m128i _H_min_u(const __m128i _Cur) noexcept { return _Minmax_traits_8_sse::_H_min_u(_Cur); } @@ -1615,10 +1635,14 @@ namespace { } static __m128d _Min(const __m128d _First, const __m128d _Second, __m128d = _mm_undefined_pd()) noexcept { - return _mm_min_pd(_First, _Second); + return _mm_min_pd(_Second, _First); } static __m128d _Max(const __m128d _First, const __m128d _Second, __m128d = _mm_undefined_pd()) noexcept { + return _mm_max_pd(_Second, _First); + } + + static __m128d _Max_r(const __m128d _First, const __m128d _Second, __m128d = _mm_undefined_pd()) noexcept { return _mm_max_pd(_First, _Second); } @@ -1643,8 +1667,8 @@ namespace { template static __m256d _H_func(const __m256d _Cur, _Fn _Funct) noexcept { __m256d _H_min_val = _Cur; - _H_min_val = _Funct(_H_min_val, _mm256_permute4x64_pd(_H_min_val, _MM_SHUFFLE(1, 0, 3, 2))); - _H_min_val = _Funct(_H_min_val, _mm256_shuffle_pd(_H_min_val, _H_min_val, 0b0101)); + _H_min_val = _Funct(_mm256_permute4x64_pd(_H_min_val, _MM_SHUFFLE(1, 0, 3, 2)), _H_min_val); + _H_min_val = _Funct(_mm256_shuffle_pd(_H_min_val, _H_min_val, 0b0101), _H_min_val); return _H_min_val; } @@ -1656,6 +1680,10 @@ namespace { return _H_func(_Cur, [](__m256d _Val1, __m256d _Val2) { return _mm256_max_pd(_Val1, _Val2); }); } + static __m256d _H_max_r(const __m256d _Cur) noexcept { + return _H_func(_Cur, [](__m256d _Val1, __m256d _Val2) { return _mm256_max_pd(_Val2, _Val1); }); + } + static __m256i _H_min_u(const __m256i _Cur) noexcept { return _Minmax_traits_8_avx::_H_min_u(_Cur); } @@ -1685,10 +1713,14 @@ namespace { } static __m256d _Min(const __m256d _First, const __m256d _Second, __m256d = _mm256_undefined_pd()) noexcept { - return _mm256_min_pd(_First, _Second); + return _mm256_min_pd(_Second, _First); } static __m256d _Max(const __m256d _First, const __m256d _Second, __m256d = _mm256_undefined_pd()) noexcept { + return _mm256_max_pd(_Second, _First); + } + + static __m256d _Max_r(const __m256d _First, const __m256d _Second, __m256d = _mm256_undefined_pd()) noexcept { return _mm256_max_pd(_First, _Second); } @@ -2029,7 +2061,9 @@ namespace { } if constexpr ((_Mode & _Mode_max) != 0) { - if constexpr (_Sign || _Sign_correction) { + if constexpr (_Traits::_Is_floating && _Mode == _Mode_both) { + _Cur_vals_max = _Traits::_Max_r(_Cur_vals_max, _Cur_vals); // Update the current maximum + } else if constexpr (_Sign || _Sign_correction) { _Cur_vals_max = _Traits::_Max(_Cur_vals_max, _Cur_vals); // Update the current maximum } else { _Cur_vals_max = _Traits::_Max_u(_Cur_vals_max, _Cur_vals); // Update the current maximum @@ -2051,7 +2085,11 @@ namespace { } if constexpr ((_Mode & _Mode_max) != 0) { - if constexpr (_Sign || _Sign_correction) { + if constexpr (_Traits::_Is_floating && _Mode == _Mode_both) { + const auto _H_max = + _Traits::_H_max_r(_Cur_vals_max); // Vector populated by the largest element + _Cur_max_val = _Traits::_Get_any(_H_max); // Get any element of it + } else if constexpr (_Sign || _Sign_correction) { const auto _H_max = _Traits::_H_max(_Cur_vals_max); // Vector populated by the largest element _Cur_max_val = _Traits::_Get_any(_H_max); // Get any element of it @@ -2087,23 +2125,100 @@ namespace { _Advance_bytes(_First, sizeof(_Ty)); } - for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { +#if !defined(_M_IX86_FP) || _M_IX86_FP == 2 + // TRANSITION, DevCom-10686775: Spell out SSE2 minss/maxss/minsd/maxsd explicitly + // to avoid the compiler messing with comparison. Not applicable to /arch:IA32 + if constexpr (_STD is_same_v<_Ty, float>) { + __m128 _Cur_min_sse = _mm_undefined_ps(); + __m128 _Cur_max_sse = _mm_undefined_ps(); + if constexpr ((_Mode & _Mode_min) != 0) { - if (*_Ptr < _Cur_min_val) { - _Cur_min_val = *_Ptr; + _Cur_min_sse = _mm_set_ss(_Cur_min_val); + } + + if constexpr ((_Mode & _Mode_max) != 0) { + _Cur_max_sse = _mm_set_ss(_Cur_max_val); + } + + for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { + __m128 _Cur = _mm_load_ss(_Ptr); + + if constexpr ((_Mode & _Mode_min) != 0) { + _Cur_min_sse = _mm_min_ss(_Cur, _Cur_min_sse); } + + if constexpr (_Mode == _Mode_max) { + _Cur_max_sse = _mm_max_ss(_Cur, _Cur_max_sse); + } else if constexpr (_Mode == _Mode_both) { + _Cur_max_sse = _mm_max_ss(_Cur_max_sse, _Cur); + } + } + + if constexpr ((_Mode & _Mode_min) != 0) { + _Cur_min_val = _mm_cvtss_f32(_Cur_min_sse); + } + + if constexpr ((_Mode & _Mode_max) != 0) { + _Cur_max_val = _mm_cvtss_f32(_Cur_max_sse); + } + } else if constexpr (_STD is_same_v<_Ty, double>) { + __m128d _Cur_min_sse = _mm_undefined_pd(); + __m128d _Cur_max_sse = _mm_undefined_pd(); + + if constexpr ((_Mode & _Mode_min) != 0) { + _Cur_min_sse = _mm_set_sd(_Cur_min_val); } if constexpr ((_Mode & _Mode_max) != 0) { - if (_Cur_max_val < *_Ptr) { - _Cur_max_val = *_Ptr; + _Cur_max_sse = _mm_set_sd(_Cur_max_val); + } + + for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { + __m128d _Cur = _mm_load_sd(_Ptr); + + if constexpr ((_Mode & _Mode_min) != 0) { + _Cur_min_sse = _mm_min_sd(_Cur, _Cur_min_sse); + } + + if constexpr (_Mode == _Mode_max) { + _Cur_max_sse = _mm_max_sd(_Cur, _Cur_max_sse); + } else if constexpr (_Mode == _Mode_both) { + _Cur_max_sse = _mm_max_sd(_Cur_max_sse, _Cur); } } - // _Mode_both could have been handled separately with 'else'. - // We have _Cur_min_val / _Cur_max_val initialized by processing at least one element, - // so the 'else' would be correct here. - // But still separate 'if' statements promote branchless codegen. + if constexpr ((_Mode & _Mode_min) != 0) { + _Cur_min_val = _mm_cvtsd_f64(_Cur_min_sse); + } + + if constexpr ((_Mode & _Mode_max) != 0) { + _Cur_max_val = _mm_cvtsd_f64(_Cur_max_sse); + } + } else +#endif // !defined(_M_IX86_FP) || _M_IX86_FP != 0 + { + for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { + if constexpr ((_Mode & _Mode_min) != 0) { + if (*_Ptr < _Cur_min_val) { + _Cur_min_val = *_Ptr; + } + } + + if constexpr (_Mode == _Mode_max) { + if (_Cur_max_val < *_Ptr) { + _Cur_max_val = *_Ptr; + } + } else if constexpr (_Mode == _Mode_both) { + if (_Cur_max_val <= *_Ptr) { + _Cur_max_val = *_Ptr; + } + } + + // _Mode_both could have been handled separately with 'else'. + // We have _Cur_min_val / _Cur_max_val initialized by processing at least one element, + // so the 'else' would be correct here. + // But still separate 'if' statements promote branchless codegen. + } } if constexpr (_Mode == _Mode_min) { From 4040d48d26a651628b29cf0a0c817186c568691c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 20 Jun 2024 19:22:23 +0300 Subject: [PATCH 05/35] fix merge error --- stl/src/vector_algorithms.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 6f97892e8a5..ee1f2e66c85 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -2132,7 +2132,6 @@ namespace { __m128 _Cur_min_sse = _mm_undefined_ps(); __m128 _Cur_max_sse = _mm_undefined_ps(); - for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { if constexpr ((_Mode & _Mode_min) != 0) { _Cur_min_sse = _mm_set_ss(_Cur_min_val); } @@ -2198,6 +2197,7 @@ namespace { } else #endif // !defined(_M_IX86_FP) || _M_IX86_FP != 0 { +#pragma loop(no_vector) // TRANSITION, VSO-2093761: work around a compiler back-end assertion for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { if constexpr ((_Mode & _Mode_min) != 0) { if (*_Ptr < _Cur_min_val) { From 75acc885f67c07f6d61a953468ec0a8d1439a717 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 20 Jun 2024 20:47:18 +0300 Subject: [PATCH 06/35] more interesting predefined cases --- .../VSO_0000000_vector_algorithms/test.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index f8c5c550683..9eb8107b42f 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -418,7 +418,7 @@ void test_min_max_element_floating_any(mt19937_64& gen) { template void test_min_max_element_floating_zero(mt19937_64& gen) { - vector input_of_input{-0.0, +0.0}; + vector input_of_input{-0, +0}; uniform_int_distribution idx_dis(0, input_of_input.size() - 1); @@ -435,6 +435,21 @@ template void test_min_max_element_floating(mt19937_64& gen) { test_case_min_max_element(vector{+0.0, -0.0}); + test_case_min_max_element(vector{+0.0, +0.0, -0.0}); + test_case_min_max_element(vector{+0.0, -0.0, +0.0}); + test_case_min_max_element(vector{-0.0, +0.0, +0.0}); + + test_case_min_max_element(vector{+0.0, +0.0, +0.0, -0.0}); + test_case_min_max_element(vector{+0.0, +0.0, -0.0, +0.0}); + test_case_min_max_element(vector{+0.0, -0.0, +0.0, +0.0}); + test_case_min_max_element(vector{-0.0, +0.0, +0.0, +0.0}); + + test_case_min_max_element(vector{+0.0, +0.0, +0.0, +0.0, -0.0}); + test_case_min_max_element(vector{+0.0, +0.0, +0.0, -0.0, +0.0}); + test_case_min_max_element(vector{+0.0, +0.0, -0.0, +0.0, +0.0}); + test_case_min_max_element(vector{+0.0, -0.0, +0.0, +0.0, +0.0}); + test_case_min_max_element(vector{-0.0, +0.0, +0.0, +0.0, +0.0}); + test_min_max_element_floating_any(gen); test_min_max_element_floating_zero(gen); } From 48c23b5683deeef98601c70138dfc8e898012283 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 21 Jun 2024 17:10:32 +0300 Subject: [PATCH 07/35] Even more coverage --- .../VSO_0000000_vector_algorithms/test.cpp | 87 +++++++++++-------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 9eb8107b42f..9aa716cacde 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -391,6 +391,20 @@ void test_min_max_element(mt19937_64& gen) { } } +template +void test_min_max_element_floating_with_values(mt19937_64& gen, const std::vector& input_of_input) { + + uniform_int_distribution idx_dis(0, input_of_input.size() - 1); + + vector input; + input.reserve(dataCount); + test_case_min_max_element(input); + for (size_t attempts = 0; attempts < dataCount; ++attempts) { + input.push_back(input_of_input[idx_dis(gen)]); + test_case_min_max_element(input); + } +} + template void test_min_max_element_floating_any(mt19937_64& gen) { normal_distribution dis(-100000.0, 100000.0); @@ -405,53 +419,58 @@ void test_min_max_element_floating_any(mt19937_64& gen) { input_of_input[i] = dis(gen); } - uniform_int_distribution idx_dis(0, input_of_input_size - 1); - - vector input; - input.reserve(dataCount); - test_case_min_max_element(input); - for (size_t attempts = 0; attempts < dataCount; ++attempts) { - input.push_back(input_of_input[idx_dis(gen)]); - test_case_min_max_element(input); - } + test_min_max_element_floating_with_values(gen, input_of_input); } template void test_min_max_element_floating_zero(mt19937_64& gen) { - vector input_of_input{-0, +0}; - - uniform_int_distribution idx_dis(0, input_of_input.size() - 1); - - vector input; - input.reserve(dataCount); - test_case_min_max_element(input); - for (size_t attempts = 0; attempts < dataCount; ++attempts) { - input.push_back(input_of_input[idx_dis(gen)]); - test_case_min_max_element(input); - } + test_min_max_element_floating_with_values(gen, {-0, +0}); + test_min_max_element_floating_with_values(gen, {-0, +0, +1, -1}); } template -void test_min_max_element_floating(mt19937_64& gen) { - test_case_min_max_element(vector{+0.0, -0.0}); +void test_min_max_element_floating_zero_predef() { + for (size_t len = 2; len != 16; ++len) { + for (size_t pos = 0; pos != len; ++pos) { + vector v(len, +0.0); + v[pos] = -0.0; + test_case_min_max_element(v); + + for (size_t i = 0; i != pos; ++i) { + v[i] = +1.0; + } - test_case_min_max_element(vector{+0.0, +0.0, -0.0}); - test_case_min_max_element(vector{+0.0, -0.0, +0.0}); - test_case_min_max_element(vector{-0.0, +0.0, +0.0}); + test_case_min_max_element(v); - test_case_min_max_element(vector{+0.0, +0.0, +0.0, -0.0}); - test_case_min_max_element(vector{+0.0, +0.0, -0.0, +0.0}); - test_case_min_max_element(vector{+0.0, -0.0, +0.0, +0.0}); - test_case_min_max_element(vector{-0.0, +0.0, +0.0, +0.0}); + for (size_t i = 0; i != pos; ++i) { + v[i] = -1, 0; + } + + test_case_min_max_element(v); - test_case_min_max_element(vector{+0.0, +0.0, +0.0, +0.0, -0.0}); - test_case_min_max_element(vector{+0.0, +0.0, +0.0, -0.0, +0.0}); - test_case_min_max_element(vector{+0.0, +0.0, -0.0, +0.0, +0.0}); - test_case_min_max_element(vector{+0.0, -0.0, +0.0, +0.0, +0.0}); - test_case_min_max_element(vector{-0.0, +0.0, +0.0, +0.0, +0.0}); + for (size_t i = 0; i != pos; ++i) { + v[i] = +0.0; + } + for (size_t i = pos + 1; i != len; ++i) { + v[i] = +1.0; + } + + test_case_min_max_element(v); + + for (size_t i = pos + 1; i != len; ++i) { + v[i] = -1.0; + } + } + } +} + + +template +void test_min_max_element_floating(mt19937_64& gen) { test_min_max_element_floating_any(gen); test_min_max_element_floating_zero(gen); + test_min_max_element_floating_zero_predef(); } void test_min_max_element_pointers(mt19937_64& gen) { From 7d00070ac76851c2dd1e61b20a9a9dd95253dffa Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 21 Jun 2024 17:13:12 +0300 Subject: [PATCH 08/35] Implement `minmax` in terms of `minmax_element` --- stl/src/vector_algorithms.cpp | 171 +++++++--------------------------- 1 file changed, 32 insertions(+), 139 deletions(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index ee1f2e66c85..b43cb973ece 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -1385,9 +1385,6 @@ namespace { static constexpr _Signed_t _Init_min_val = __builtin_huge_valf(); static constexpr _Signed_t _Init_max_val = -__builtin_huge_valf(); - using _Minmax_i_t = _Min_max_f; - using _Minmax_u_t = void; - #ifndef _M_ARM64EC #ifdef _M_IX86 static constexpr bool _Has_portion_max = false; @@ -1428,10 +1425,6 @@ namespace { return _H_func(_Cur, [](__m128 _Val1, __m128 _Val2) { return _mm_max_ps(_Val1, _Val2); }); } - static __m128 _H_max_r(const __m128 _Cur) noexcept { - return _H_func(_Cur, [](__m128 _Val1, __m128 _Val2) { return _mm_max_ps(_Val2, _Val1); }); - } - static __m128i _H_min_u(const __m128i _Cur) noexcept { return _Minmax_traits_4_sse::_H_min_u(_Cur); } @@ -1468,10 +1461,6 @@ namespace { return _mm_max_ps(_Second, _First); } - static __m128 _Max_r(const __m128 _First, const __m128 _Second, __m128 = _mm_undefined_ps()) noexcept { - return _mm_max_ps(_First, _Second); - } - static __m128i _Mask_cast(const __m128 _Mask) noexcept { return _mm_castps_si128(_Mask); } @@ -1507,10 +1496,6 @@ namespace { return _H_func(_Cur, [](__m256 _Val1, __m256 _Val2) { return _mm256_max_ps(_Val1, _Val2); }); } - static __m256 _H_max_r(const __m256 _Cur) noexcept { - return _H_func(_Cur, [](__m256 _Val1, __m256 _Val2) { return _mm256_max_ps(_Val2, _Val1); }); - } - static __m256i _H_min_u(const __m256i _Cur) noexcept { return _Minmax_traits_4_avx::_H_min_u(_Cur); } @@ -1547,10 +1532,6 @@ namespace { return _mm256_max_ps(_Second, _First); } - static __m256 _Max_r(const __m256 _First, const __m256 _Second, __m256 = _mm256_undefined_ps()) noexcept { - return _mm256_max_ps(_First, _Second); - } - static __m256i _Mask_cast(const __m256 _Mask) noexcept { return _mm256_castps_si256(_Mask); } @@ -1566,9 +1547,6 @@ namespace { static constexpr _Signed_t _Init_min_val = __builtin_huge_val(); static constexpr _Signed_t _Init_max_val = -__builtin_huge_val(); - using _Minmax_i_t = _Min_max_d; - using _Minmax_u_t = void; - #ifndef _M_ARM64EC static constexpr bool _Has_portion_max = false; #endif // !defined(_M_ARM64EC) @@ -1603,10 +1581,6 @@ namespace { return _H_func(_Cur, [](__m128d _Val1, __m128d _Val2) { return _mm_max_pd(_Val1, _Val2); }); } - static __m128d _H_max_r(const __m128d _Cur) noexcept { - return _H_func(_Cur, [](__m128d _Val1, __m128d _Val2) { return _mm_max_pd(_Val2, _Val1); }); - } - static __m128i _H_min_u(const __m128i _Cur) noexcept { return _Minmax_traits_8_sse::_H_min_u(_Cur); } @@ -1642,10 +1616,6 @@ namespace { return _mm_max_pd(_Second, _First); } - static __m128d _Max_r(const __m128d _First, const __m128d _Second, __m128d = _mm_undefined_pd()) noexcept { - return _mm_max_pd(_First, _Second); - } - static __m128i _Mask_cast(const __m128d _Mask) noexcept { return _mm_castpd_si128(_Mask); } @@ -1680,10 +1650,6 @@ namespace { return _H_func(_Cur, [](__m256d _Val1, __m256d _Val2) { return _mm256_max_pd(_Val1, _Val2); }); } - static __m256d _H_max_r(const __m256d _Cur) noexcept { - return _H_func(_Cur, [](__m256d _Val1, __m256d _Val2) { return _mm256_max_pd(_Val2, _Val1); }); - } - static __m256i _H_min_u(const __m256i _Cur) noexcept { return _Minmax_traits_8_avx::_H_min_u(_Cur); } @@ -1720,10 +1686,6 @@ namespace { return _mm256_max_pd(_Second, _First); } - static __m256d _Max_r(const __m256d _First, const __m256d _Second, __m256d = _mm256_undefined_pd()) noexcept { - return _mm256_max_pd(_First, _Second); - } - static __m256i _Mask_cast(const __m256d _Mask) noexcept { return _mm256_castpd_si256(_Mask); } @@ -2014,6 +1976,14 @@ namespace { template <_Min_max_mode _Mode, class _Traits, bool _Sign> auto __std_minmax_impl(const void* _First, const void* const _Last) noexcept { + static_assert(!_Traits::_Is_floating, "This does not work for floats, see bellow"); + // The value-based vectorized rather than the position-based one does not work for floats. + // Efficient vectorization needs to find vertical minmax first, and then the horizontal one. + // This alters order of comparison: index zero element is first compared against + // vector size equal index element and only in the end against index one element. + // With equivalent but distinguishable +0.0 and -0.0 values, the altered comparison order + // will not produce the expected result in some cases (will return +0.0 instead of -0.0 or the reverse) + using _Ty = std::conditional_t<_Sign, typename _Traits::_Signed_t, typename _Traits::_Unsigned_t>; _Ty _Cur_min_val; // initialized in both of the branches below @@ -2061,9 +2031,7 @@ namespace { } if constexpr ((_Mode & _Mode_max) != 0) { - if constexpr (_Traits::_Is_floating && _Mode == _Mode_both) { - _Cur_vals_max = _Traits::_Max_r(_Cur_vals_max, _Cur_vals); // Update the current maximum - } else if constexpr (_Sign || _Sign_correction) { + if constexpr (_Sign || _Sign_correction) { _Cur_vals_max = _Traits::_Max(_Cur_vals_max, _Cur_vals); // Update the current maximum } else { _Cur_vals_max = _Traits::_Max_u(_Cur_vals_max, _Cur_vals); // Update the current maximum @@ -2085,11 +2053,7 @@ namespace { } if constexpr ((_Mode & _Mode_max) != 0) { - if constexpr (_Traits::_Is_floating && _Mode == _Mode_both) { - const auto _H_max = - _Traits::_H_max_r(_Cur_vals_max); // Vector populated by the largest element - _Cur_max_val = _Traits::_Get_any(_H_max); // Get any element of it - } else if constexpr (_Sign || _Sign_correction) { + if constexpr (_Sign || _Sign_correction) { const auto _H_max = _Traits::_H_max(_Cur_vals_max); // Vector populated by the largest element _Cur_max_val = _Traits::_Get_any(_H_max); // Get any element of it @@ -2125,101 +2089,28 @@ namespace { _Advance_bytes(_First, sizeof(_Ty)); } -#if !defined(_M_IX86_FP) || _M_IX86_FP == 2 - // TRANSITION, DevCom-10686775: Spell out SSE2 minss/maxss/minsd/maxsd explicitly - // to avoid the compiler messing with comparison. Not applicable to /arch:IA32 - if constexpr (_STD is_same_v<_Ty, float>) { - __m128 _Cur_min_sse = _mm_undefined_ps(); - __m128 _Cur_max_sse = _mm_undefined_ps(); - +#pragma loop(no_vector) // TRANSITION, VSO-2093761: work around a compiler back-end assertion + for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { if constexpr ((_Mode & _Mode_min) != 0) { - _Cur_min_sse = _mm_set_ss(_Cur_min_val); - } - - if constexpr ((_Mode & _Mode_max) != 0) { - _Cur_max_sse = _mm_set_ss(_Cur_max_val); - } - - for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { - __m128 _Cur = _mm_load_ss(_Ptr); - - if constexpr ((_Mode & _Mode_min) != 0) { - _Cur_min_sse = _mm_min_ss(_Cur, _Cur_min_sse); - } - - if constexpr (_Mode == _Mode_max) { - _Cur_max_sse = _mm_max_ss(_Cur, _Cur_max_sse); - } else if constexpr (_Mode == _Mode_both) { - _Cur_max_sse = _mm_max_ss(_Cur_max_sse, _Cur); + if (*_Ptr < _Cur_min_val) { + _Cur_min_val = *_Ptr; } } - if constexpr ((_Mode & _Mode_min) != 0) { - _Cur_min_val = _mm_cvtss_f32(_Cur_min_sse); - } - - if constexpr ((_Mode & _Mode_max) != 0) { - _Cur_max_val = _mm_cvtss_f32(_Cur_max_sse); - } - } else if constexpr (_STD is_same_v<_Ty, double>) { - __m128d _Cur_min_sse = _mm_undefined_pd(); - __m128d _Cur_max_sse = _mm_undefined_pd(); - - if constexpr ((_Mode & _Mode_min) != 0) { - _Cur_min_sse = _mm_set_sd(_Cur_min_val); - } - - if constexpr ((_Mode & _Mode_max) != 0) { - _Cur_max_sse = _mm_set_sd(_Cur_max_val); - } - - for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { - __m128d _Cur = _mm_load_sd(_Ptr); - - if constexpr ((_Mode & _Mode_min) != 0) { - _Cur_min_sse = _mm_min_sd(_Cur, _Cur_min_sse); + if constexpr (_Mode == _Mode_max) { + if (_Cur_max_val < *_Ptr) { + _Cur_max_val = *_Ptr; } - - if constexpr (_Mode == _Mode_max) { - _Cur_max_sse = _mm_max_sd(_Cur, _Cur_max_sse); - } else if constexpr (_Mode == _Mode_both) { - _Cur_max_sse = _mm_max_sd(_Cur_max_sse, _Cur); + } else if constexpr (_Mode == _Mode_both) { + if (_Cur_max_val <= *_Ptr) { + _Cur_max_val = *_Ptr; } } - if constexpr ((_Mode & _Mode_min) != 0) { - _Cur_min_val = _mm_cvtsd_f64(_Cur_min_sse); - } - - if constexpr ((_Mode & _Mode_max) != 0) { - _Cur_max_val = _mm_cvtsd_f64(_Cur_max_sse); - } - } else -#endif // !defined(_M_IX86_FP) || _M_IX86_FP != 0 - { -#pragma loop(no_vector) // TRANSITION, VSO-2093761: work around a compiler back-end assertion - for (auto _Ptr = static_cast(_First); _Ptr != _Last; ++_Ptr) { - if constexpr ((_Mode & _Mode_min) != 0) { - if (*_Ptr < _Cur_min_val) { - _Cur_min_val = *_Ptr; - } - } - - if constexpr (_Mode == _Mode_max) { - if (_Cur_max_val < *_Ptr) { - _Cur_max_val = *_Ptr; - } - } else if constexpr (_Mode == _Mode_both) { - if (_Cur_max_val <= *_Ptr) { - _Cur_max_val = *_Ptr; - } - } - - // _Mode_both could have been handled separately with 'else'. - // We have _Cur_min_val / _Cur_max_val initialized by processing at least one element, - // so the 'else' would be correct here. - // But still separate 'if' statements promote branchless codegen. - } + // _Mode_both could have been handled separately with 'else'. + // We have _Cur_min_val / _Cur_max_val initialized by processing at least one element, + // so the 'else' would be correct here. + // But still separate 'if' statements promote branchless codegen. } if constexpr (_Mode == _Mode_min) { @@ -2372,11 +2263,11 @@ __declspec(noalias) uint64_t __stdcall __std_min_8u(const void* const _First, co } __declspec(noalias) float __stdcall __std_min_f(const void* const _First, const void* const _Last) noexcept { - return __std_minmax_disp<_Mode_min, _Minmax_traits_f, true>(_First, _Last); + return *static_cast(__std_minmax_element_disp<_Mode_min, _Minmax_traits_f>(_First, _Last, false)); } __declspec(noalias) double __stdcall __std_min_d(const void* const _First, const void* const _Last) noexcept { - return __std_minmax_disp<_Mode_min, _Minmax_traits_d, true>(_First, _Last); + return *static_cast(__std_minmax_element_disp<_Mode_min, _Minmax_traits_d>(_First, _Last, false)); } __declspec(noalias) int8_t __stdcall __std_max_1i(const void* const _First, const void* const _Last) noexcept { @@ -2412,11 +2303,11 @@ __declspec(noalias) uint64_t __stdcall __std_max_8u(const void* const _First, co } __declspec(noalias) float __stdcall __std_max_f(const void* const _First, const void* const _Last) noexcept { - return __std_minmax_disp<_Mode_max, _Minmax_traits_f, true>(_First, _Last); + return *static_cast(__std_minmax_element_disp<_Mode_max, _Minmax_traits_f>(_First, _Last, false)); } __declspec(noalias) double __stdcall __std_max_d(const void* const _First, const void* const _Last) noexcept { - return __std_minmax_disp<_Mode_max, _Minmax_traits_d, true>(_First, _Last); + return *static_cast(__std_minmax_element_disp<_Mode_max, _Minmax_traits_d>(_First, _Last, false)); } __declspec(noalias) _Min_max_1i __stdcall __std_minmax_1i(const void* const _First, const void* const _Last) noexcept { @@ -2452,11 +2343,13 @@ __declspec(noalias) _Min_max_8u __stdcall __std_minmax_8u(const void* const _Fir } __declspec(noalias) _Min_max_f __stdcall __std_minmax_f(const void* const _First, const void* const _Last) noexcept { - return __std_minmax_disp<_Mode_both, _Minmax_traits_f, true>(_First, _Last); + _Min_max_element_t _Result = __std_minmax_element_disp<_Mode_both, _Minmax_traits_f>(_First, _Last, false); + return {*static_cast(_Result._Min), *static_cast(_Result._Max)}; } __declspec(noalias) _Min_max_d __stdcall __std_minmax_d(const void* const _First, const void* const _Last) noexcept { - return __std_minmax_disp<_Mode_both, _Minmax_traits_d, true>(_First, _Last); + _Min_max_element_t _Result = __std_minmax_element_disp<_Mode_both, _Minmax_traits_d>(_First, _Last, false); + return {*static_cast(_Result._Min), *static_cast(_Result._Max)}; } } // extern "C" From 4302235efd49f1ab55e4e48d642092466c22837f Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 21 Jun 2024 17:16:32 +0300 Subject: [PATCH 09/35] Fix ascending order --- stl/src/vector_algorithms.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index b43cb973ece..64dc67708d8 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -1637,8 +1637,8 @@ namespace { template static __m256d _H_func(const __m256d _Cur, _Fn _Funct) noexcept { __m256d _H_min_val = _Cur; - _H_min_val = _Funct(_mm256_permute4x64_pd(_H_min_val, _MM_SHUFFLE(1, 0, 3, 2)), _H_min_val); _H_min_val = _Funct(_mm256_shuffle_pd(_H_min_val, _H_min_val, 0b0101), _H_min_val); + _H_min_val = _Funct(_mm256_permute4x64_pd(_H_min_val, _MM_SHUFFLE(1, 0, 3, 2)), _H_min_val); return _H_min_val; } From 1f124c44646201693674b7acb6f39b6bf789c205 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 21 Jun 2024 18:27:52 +0300 Subject: [PATCH 10/35] tail correctness is not needed anymore --- stl/src/vector_algorithms.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 64dc67708d8..40bbe7ed886 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -2097,14 +2097,10 @@ namespace { } } - if constexpr (_Mode == _Mode_max) { + if constexpr ((_Mode & _Mode_max) != 0) { if (_Cur_max_val < *_Ptr) { _Cur_max_val = *_Ptr; } - } else if constexpr (_Mode == _Mode_both) { - if (_Cur_max_val <= *_Ptr) { - _Cur_max_val = *_Ptr; - } } // _Mode_both could have been handled separately with 'else'. From b34c73e9ee45b8cddc040921570792370300c82f Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 22 Jun 2024 12:09:38 +0300 Subject: [PATCH 11/35] even better random coverage --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index bff2fcd6429..b2f02e54d50 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -425,7 +425,8 @@ void test_min_max_element_floating_any(mt19937_64& gen) { template void test_min_max_element_floating_zero(mt19937_64& gen) { test_min_max_element_floating_with_values(gen, {-0, +0}); - test_min_max_element_floating_with_values(gen, {-0, +0, +1, -1}); + test_min_max_element_floating_with_values(gen, {-0, +0, +1}); + test_min_max_element_floating_with_values(gen, {-0, +0, -1}); } template From 893792fe02791a7ba9dc61ed3e2bb3744a18a5e6 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 22 Jun 2024 12:10:51 +0300 Subject: [PATCH 12/35] Don't check zeros for fp:fast --- tests/std/include/test_min_max_element_support.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/std/include/test_min_max_element_support.hpp b/tests/std/include/test_min_max_element_support.hpp index 22e8ae5ff07..3f8d3f2df9f 100644 --- a/tests/std/include/test_min_max_element_support.hpp +++ b/tests/std/include/test_min_max_element_support.hpp @@ -118,12 +118,16 @@ void test_case_min_max_element(const std::vector& input) { assert(*expected_minmax.first == actual_minmax_value.min); assert(*expected_minmax.second == actual_minmax_value.max); +#ifndef _M_FP_FAST + // With /fp:fast mode the compiler does not try to produce the code that correctly + // distincts +0.0 and -0.0, so the algorithms are not expected to either. if constexpr (std::is_floating_point_v) { assert(signbit(*expected_min) == signbit(actual_min_value)); assert(signbit(*expected_max) == signbit(actual_max_value)); assert(signbit(*expected_minmax.first) == signbit(actual_minmax_value.min)); assert(signbit(*expected_minmax.second) == signbit(actual_minmax_value.max)); } +#endif // !defined(_M_FP_FAST) } #endif // _HAS_CXX20 } From ff3e15b822513c9c69db9abe3066c375c53099ae Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 22 Jun 2024 12:16:30 +0300 Subject: [PATCH 13/35] Restore the full optimization, it is fine for `/fp:fast` --- stl/src/vector_algorithms.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 40bbe7ed886..b7377234b95 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -1385,6 +1385,9 @@ namespace { static constexpr _Signed_t _Init_min_val = __builtin_huge_valf(); static constexpr _Signed_t _Init_max_val = -__builtin_huge_valf(); + using _Minmax_i_t = _Min_max_f; + using _Minmax_u_t = void; + #ifndef _M_ARM64EC #ifdef _M_IX86 static constexpr bool _Has_portion_max = false; @@ -1547,6 +1550,9 @@ namespace { static constexpr _Signed_t _Init_min_val = __builtin_huge_val(); static constexpr _Signed_t _Init_max_val = -__builtin_huge_val(); + using _Minmax_i_t = _Min_max_d; + using _Minmax_u_t = void; + #ifndef _M_ARM64EC static constexpr bool _Has_portion_max = false; #endif // !defined(_M_ARM64EC) @@ -1976,13 +1982,17 @@ namespace { template <_Min_max_mode _Mode, class _Traits, bool _Sign> auto __std_minmax_impl(const void* _First, const void* const _Last) noexcept { - static_assert(!_Traits::_Is_floating, "This does not work for floats, see bellow"); - // The value-based vectorized rather than the position-based one does not work for floats. + // The value-based vectorized rather than the position-based one does not always produce + // the expected results for floatting point types. + // // Efficient vectorization needs to find vertical minmax first, and then the horizontal one. // This alters order of comparison: index zero element is first compared against // vector size equal index element and only in the end against index one element. // With equivalent but distinguishable +0.0 and -0.0 values, the altered comparison order // will not produce the expected result in some cases (will return +0.0 instead of -0.0 or the reverse) + // + // The result is still acceptable for /fp:fast when +0.0 / -0.0 are not expected to be properly distinguished, + // and even the compiler itself takes advantage of it. using _Ty = std::conditional_t<_Sign, typename _Traits::_Signed_t, typename _Traits::_Unsigned_t>; @@ -2259,11 +2269,11 @@ __declspec(noalias) uint64_t __stdcall __std_min_8u(const void* const _First, co } __declspec(noalias) float __stdcall __std_min_f(const void* const _First, const void* const _Last) noexcept { - return *static_cast(__std_minmax_element_disp<_Mode_min, _Minmax_traits_f>(_First, _Last, false)); + return __std_minmax_disp<_Mode_min, _Minmax_traits_f, true>(_First, _Last); } __declspec(noalias) double __stdcall __std_min_d(const void* const _First, const void* const _Last) noexcept { - return *static_cast(__std_minmax_element_disp<_Mode_min, _Minmax_traits_d>(_First, _Last, false)); + return __std_minmax_disp<_Mode_min, _Minmax_traits_d, true>(_First, _Last); } __declspec(noalias) int8_t __stdcall __std_max_1i(const void* const _First, const void* const _Last) noexcept { @@ -2299,11 +2309,11 @@ __declspec(noalias) uint64_t __stdcall __std_max_8u(const void* const _First, co } __declspec(noalias) float __stdcall __std_max_f(const void* const _First, const void* const _Last) noexcept { - return *static_cast(__std_minmax_element_disp<_Mode_max, _Minmax_traits_f>(_First, _Last, false)); + return __std_minmax_disp<_Mode_max, _Minmax_traits_f, true>(_First, _Last); } __declspec(noalias) double __stdcall __std_max_d(const void* const _First, const void* const _Last) noexcept { - return *static_cast(__std_minmax_element_disp<_Mode_max, _Minmax_traits_d>(_First, _Last, false)); + return __std_minmax_disp<_Mode_max, _Minmax_traits_d, true>(_First, _Last); } __declspec(noalias) _Min_max_1i __stdcall __std_minmax_1i(const void* const _First, const void* const _Last) noexcept { @@ -2339,13 +2349,11 @@ __declspec(noalias) _Min_max_8u __stdcall __std_minmax_8u(const void* const _Fir } __declspec(noalias) _Min_max_f __stdcall __std_minmax_f(const void* const _First, const void* const _Last) noexcept { - _Min_max_element_t _Result = __std_minmax_element_disp<_Mode_both, _Minmax_traits_f>(_First, _Last, false); - return {*static_cast(_Result._Min), *static_cast(_Result._Max)}; + return __std_minmax_disp<_Mode_both, _Minmax_traits_f, true>(_First, _Last); } __declspec(noalias) _Min_max_d __stdcall __std_minmax_d(const void* const _First, const void* const _Last) noexcept { - _Min_max_element_t _Result = __std_minmax_element_disp<_Mode_both, _Minmax_traits_d>(_First, _Last, false); - return {*static_cast(_Result._Min), *static_cast(_Result._Max)}; + return __std_minmax_disp<_Mode_both, _Minmax_traits_d, true>(_First, _Last); } } // extern "C" From 8bdda8bc7acd85e6984f0e60d3e8cf18def54f84 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 22 Jun 2024 12:35:45 +0300 Subject: [PATCH 14/35] disable value-based floating vector algorithms for non-fast-math in headers --- stl/inc/algorithm | 4 ++-- stl/inc/xutility | 30 ++++++++++++++++++++++++------ stl/src/vector_algorithms.cpp | 2 +- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 5510edb3d97..fb25b58f2f7 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10205,7 +10205,7 @@ _NODISCARD constexpr pair<_Ty, _Ty> minmax(initializer_list<_Ty> _Ilist, _Pr _Pr _STL_ASSERT( _Ilist.size() != 0, "An initializer_list passed to std::minmax must not be empty. (N4971 [alg.min.max]/21)"); #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (_Is_min_max_optimization_safe) { + if constexpr (_Is_min_max_value_optimization_safe) { if (!_STD _Is_constant_evaluated()) { const auto _Result = _STD _Minmax_vectorized(_Ilist.begin(), _Ilist.end()); return {static_cast<_Ty>(_Result._Min), static_cast<_Ty>(_Result._Max)}; @@ -10334,7 +10334,7 @@ namespace ranges { using _Vty = iter_value_t<_It>; #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe<_It, _Pr> + if constexpr (is_same_v<_Pj, identity> && _Is_min_max_value_optimization_safe<_It, _Pr> && sized_sentinel_for<_Se, _It>) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_First); diff --git a/stl/inc/xutility b/stl/inc/xutility index ee83e903b9e..63e2e134edf 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -6783,6 +6783,24 @@ constexpr bool _Is_min_max_optimization_safe = // Activate the vector algorithms #endif // _HAS_CXX20 is_same<_Pr, less<>>, is_same<_Pr, less<_Elem>>>>; // predicate is less +// The value-based vectorized rather than the position-based one does not always produce +// the expected results for floatting point types. +// +// Efficient vectorization needs to find vertical minmax first, and then the horizontal one. +// This alters order of comparison: index zero element is first compared against +// vector size equal index element and only in the end against index one element. +// With equivalent but distinguishable +0.0 and -0.0 values, the altered comparison order +// will not produce the expected result in some cases (will return +0.0 instead of -0.0 or the reverse) +// +// The result is still acceptable for /fp:fast when +0.0 / -0.0 are not expected to be properly distinguished, +// and the compiler itself takes advantage of it. +template > +constexpr bool _Is_min_max_value_optimization_safe = // Activate the vector algorithms for ranges::min/max? +#ifndef _M_FP_FAST + !is_floating_point_v<_Elem> && +#endif // !_M_FP_FAST + _Is_min_max_optimization_safe<_Iter, _Pr, _Elem>; + template constexpr _FwdIt _Max_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find largest element #if _USE_STD_VECTOR_ALGORITHMS @@ -6913,7 +6931,7 @@ _NODISCARD constexpr _Ty(max)(initializer_list<_Ty> _Ilist, _Pr _Pred) { _STL_ASSERT( _Ilist.size() != 0, "An initializer_list passed to std::max must not be empty. (N4971 [alg.min.max]/13)"); #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (_Is_min_max_optimization_safe) { + if constexpr (_Is_min_max_value_optimization_safe) { if (!_Is_constant_evaluated()) { return static_cast<_Ty>(_STD _Max_vectorized(_Ilist.begin(), _Ilist.end())); } @@ -6959,7 +6977,7 @@ namespace ranges { _STL_ASSERT(_First != _Last, "An initializer_list passed to std::ranges::max must not be empty. (N4971 [alg.min.max]/13)"); #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe) { + if constexpr (is_same_v<_Pj, identity> && _Is_min_max_value_optimization_safe) { if (!_STD is_constant_evaluated()) { return static_cast<_Ty>(_STD _Max_vectorized(_First, _Last)); } @@ -6978,7 +6996,7 @@ namespace ranges { _STL_ASSERT( _UFirst != _ULast, "A range passed to std::ranges::max must not be empty. (N4971 [alg.min.max]/13)"); #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe + if constexpr (is_same_v<_Pj, identity> && _Is_min_max_value_optimization_safe && sized_sentinel_for) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_UFirst); @@ -7137,7 +7155,7 @@ _NODISCARD constexpr _Ty(min)(initializer_list<_Ty> _Ilist, _Pr _Pred) { _STL_ASSERT( _Ilist.size() != 0, "An initializer_list passed to std::min must not be empty. (N4971 [alg.min.max]/5)"); #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (_Is_min_max_optimization_safe) { + if constexpr (_Is_min_max_value_optimization_safe) { if (!_Is_constant_evaluated()) { return static_cast<_Ty>(_STD _Min_vectorized(_Ilist.begin(), _Ilist.end())); } @@ -7177,7 +7195,7 @@ namespace ranges { _STL_ASSERT(_First != _Last, "An initializer_list passed to std::ranges::min must not be empty. (N4971 [alg.min.max]/5)"); #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe) { + if constexpr (is_same_v<_Pj, identity> && _Is_min_max_value_optimization_safe) { if (!_STD is_constant_evaluated()) { return static_cast<_Ty>(_STD _Min_vectorized(_First, _Last)); } @@ -7196,7 +7214,7 @@ namespace ranges { _STL_ASSERT( _UFirst != _ULast, "A range passed to std::ranges::min must not be empty. (N4971 [alg.min.max]/5)"); #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (is_same_v<_Pj, identity> && _Is_min_max_optimization_safe + if constexpr (is_same_v<_Pj, identity> && _Is_min_max_value_optimization_safe && sized_sentinel_for) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_UFirst); diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index b7377234b95..4eb77104cb7 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -1992,7 +1992,7 @@ namespace { // will not produce the expected result in some cases (will return +0.0 instead of -0.0 or the reverse) // // The result is still acceptable for /fp:fast when +0.0 / -0.0 are not expected to be properly distinguished, - // and even the compiler itself takes advantage of it. + // and the compiler itself takes advantage of it. using _Ty = std::conditional_t<_Sign, typename _Traits::_Signed_t, typename _Traits::_Unsigned_t>; From 3f0b77f40a35e68b93f8d024488deb354eeef5f5 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 22 Jun 2024 14:53:05 +0300 Subject: [PATCH 15/35] /fp:fast coverage --- .../VSO_0000000_vector_algorithms/test.cpp | 87 --------- .../env.lst | 54 ++++++ .../test.cpp | 166 ++++++++++++++++++ 3 files changed, 220 insertions(+), 87 deletions(-) create mode 100644 tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst create mode 100644 tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index b2f02e54d50..d28f97aaa47 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -391,89 +391,6 @@ void test_min_max_element(mt19937_64& gen) { } } -template -void test_min_max_element_floating_with_values(mt19937_64& gen, const std::vector& input_of_input) { - - uniform_int_distribution idx_dis(0, input_of_input.size() - 1); - - vector input; - input.reserve(dataCount); - test_case_min_max_element(input); - for (size_t attempts = 0; attempts < dataCount; ++attempts) { - input.push_back(input_of_input[idx_dis(gen)]); - test_case_min_max_element(input); - } -} - -template -void test_min_max_element_floating_any(mt19937_64& gen) { - normal_distribution dis(-100000.0, 100000.0); - - constexpr auto input_of_input_size = dataCount / 2; - vector input_of_input(input_of_input_size); - input_of_input[0] = -numeric_limits::infinity(); - input_of_input[1] = +numeric_limits::infinity(); - input_of_input[2] = -0.0; - input_of_input[3] = +0.0; - for (size_t i = 4; i < input_of_input_size; ++i) { - input_of_input[i] = dis(gen); - } - - test_min_max_element_floating_with_values(gen, input_of_input); -} - -template -void test_min_max_element_floating_zero(mt19937_64& gen) { - test_min_max_element_floating_with_values(gen, {-0, +0}); - test_min_max_element_floating_with_values(gen, {-0, +0, +1}); - test_min_max_element_floating_with_values(gen, {-0, +0, -1}); -} - -template -void test_min_max_element_floating_zero_predef() { - for (size_t len = 2; len != 16; ++len) { - for (size_t pos = 0; pos != len; ++pos) { - vector v(len, +0.0); - v[pos] = -0.0; - test_case_min_max_element(v); - - for (size_t i = 0; i != pos; ++i) { - v[i] = +1.0; - } - - test_case_min_max_element(v); - - for (size_t i = 0; i != pos; ++i) { - v[i] = -1, 0; - } - - test_case_min_max_element(v); - - for (size_t i = 0; i != pos; ++i) { - v[i] = +0.0; - } - - for (size_t i = pos + 1; i != len; ++i) { - v[i] = +1.0; - } - - test_case_min_max_element(v); - - for (size_t i = pos + 1; i != len; ++i) { - v[i] = -1.0; - } - } - } -} - - -template -void test_min_max_element_floating(mt19937_64& gen) { - test_min_max_element_floating_any(gen); - test_min_max_element_floating_zero(gen); - test_min_max_element_floating_zero_predef(); -} - void test_min_max_element_pointers(mt19937_64& gen) { const short arr[20]{}; @@ -959,10 +876,6 @@ void test_vector_algorithms(mt19937_64& gen) { test_min_max_element(gen); test_min_max_element(gen); - test_min_max_element_floating(gen); - test_min_max_element_floating(gen); - test_min_max_element_floating(gen); - test_min_max_element_pointers(gen); test_min_max_element_special_cases(); // SSE2 vectors diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst b/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst new file mode 100644 index 00000000000..0173530a3d9 --- /dev/null +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst @@ -0,0 +1,54 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +# Copied from ..\usual_matrix.lst, /fp lines excluded +PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++14 /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MD /std:c++17 /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++20 /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MD /std:c++20 /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- /Zc:noexceptTypes-" +ASAN PM_CL="/EHsc /MD /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- /Zc:noexceptTypes- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:char8_t- /w14640 /Zc:threadSafeInit- /Zc:preprocessor" +ASAN PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:wchar_t- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++17 /permissive- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MDd /std:c++17 /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++20 /permissive- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MDd /std:c++20 /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MT /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +# No corresponding ASAN config, since the above differs from another config only in IDL +PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/clr /MD /std:c++20 /w14640 /Zc:threadSafeInit-" +PM_CL="/clr /MDd /std:c++20 /w14640 /Zc:threadSafeInit-" +PM_CL="/clr:pure /MD /std:c++14" +PM_CL="/clr:pure /MDd /std:c++14" +PM_CL="/BE /c /EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit-" +PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive- /w14640 /Zc:threadSafeInit-" +PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MDd /std:c++17 /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive- /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" +RUNALL_CROSSLIST +* PM_CL="/fp:strict +* PM_CL="/fp:precise +* PM_CL="/fp:fast +RUNALL_CROSSLIST +* PM_CL="" # Test default setting +* PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test escape hatch, see GH-1751 diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp new file mode 100644 index 00000000000..042a4def9cd --- /dev/null +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if _HAS_CXX20 +#include +#include +#endif // _HAS_CXX20 + +#include "test_min_max_element_support.hpp" + +using namespace std; + +#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension +#endif // __clang__ + +#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) +extern "C" long __isa_enabled; + +void disable_instructions(ISA_AVAILABILITY isa) { + __isa_enabled &= ~(1UL << static_cast(isa)); +} +#endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) + +void initialize_randomness(mt19937_64& gen) { + constexpr size_t n = mt19937_64::state_size; + constexpr size_t w = mt19937_64::word_size; + static_assert(w % 32 == 0, "w should be evenly divisible by 32"); + constexpr size_t k = w / 32; + + vector vec(n * k); + + random_device rd; + generate(vec.begin(), vec.end(), ref(rd)); + + printf("This is a randomized test.\n"); + printf("DO NOT IGNORE/RERUN ANY FAILURES.\n"); + printf("You must report them to the STL maintainers.\n\n"); + + printf("Seed vector: "); + for (const auto& e : vec) { + printf("%u,", e); + } + printf("\n"); + + seed_seq seq(vec.cbegin(), vec.cend()); + gen.seed(seq); +} + +constexpr size_t dataCount = 1024; + +template +void test_min_max_element_floating_with_values(mt19937_64& gen, const std::vector& input_of_input) { + uniform_int_distribution idx_dis(0, input_of_input.size() - 1); + + vector input; + input.reserve(dataCount); + test_case_min_max_element(input); + for (size_t attempts = 0; attempts < dataCount; ++attempts) { + input.push_back(input_of_input[idx_dis(gen)]); + test_case_min_max_element(input); + } +} + +template +void test_min_max_element_floating_any(mt19937_64& gen) { + normal_distribution dis(-100000.0, 100000.0); + + constexpr auto input_of_input_size = dataCount / 2; + vector input_of_input(input_of_input_size); + input_of_input[0] = -numeric_limits::infinity(); + input_of_input[1] = +numeric_limits::infinity(); + input_of_input[2] = -0.0; + input_of_input[3] = +0.0; + for (size_t i = 4; i < input_of_input_size; ++i) { + input_of_input[i] = dis(gen); + } + + test_min_max_element_floating_with_values(gen, input_of_input); +} + +template +void test_min_max_element_floating_zero(mt19937_64& gen) { + test_min_max_element_floating_with_values(gen, {-0, +0}); + test_min_max_element_floating_with_values(gen, {-0, +0, +1}); + test_min_max_element_floating_with_values(gen, {-0, +0, -1}); +} + +template +void test_min_max_element_floating_zero_predef() { + for (size_t len = 2; len != 16; ++len) { + for (size_t pos = 0; pos != len; ++pos) { + vector v(len, +0.0); + v[pos] = -0.0; + test_case_min_max_element(v); + + for (size_t i = 0; i != pos; ++i) { + v[i] = +1.0; + } + + test_case_min_max_element(v); + + for (size_t i = 0; i != pos; ++i) { + v[i] = -1, 0; + } + + test_case_min_max_element(v); + + for (size_t i = 0; i != pos; ++i) { + v[i] = +0.0; + } + + for (size_t i = pos + 1; i != len; ++i) { + v[i] = +1.0; + } + + test_case_min_max_element(v); + + for (size_t i = pos + 1; i != len; ++i) { + v[i] = -1.0; + } + } + } +} + +template +void test_min_max_element_floating(mt19937_64& gen) { + test_min_max_element_floating_any(gen); + test_min_max_element_floating_zero(gen); + test_min_max_element_floating_zero_predef(); +} + +void test_vector_algorithms(mt19937_64& gen) { + test_min_max_element_floating(gen); + test_min_max_element_floating(gen); +} + +int main() { + mt19937_64 gen; + initialize_randomness(gen); + + test_vector_algorithms(gen); +#ifndef _M_CEE_PURE +#if defined(_M_IX86) || defined(_M_X64) + disable_instructions(__ISA_AVAILABLE_AVX2); + test_vector_algorithms(gen); + + disable_instructions(__ISA_AVAILABLE_SSE42); + test_vector_algorithms(gen); +#endif // defined(_M_IX86) || defined(_M_X64) +#endif // _M_CEE_PURE +} From b7b19d07c42bcb8a2723acbedd35c2ed3510103c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 22 Jun 2024 16:11:34 +0300 Subject: [PATCH 16/35] fix Both_val --- stl/inc/algorithm | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index fb25b58f2f7..d0cb7462f3d 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10334,13 +10334,21 @@ namespace ranges { using _Vty = iter_value_t<_It>; #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (is_same_v<_Pj, identity> && _Is_min_max_value_optimization_safe<_It, _Pr> - && sized_sentinel_for<_Se, _It>) { - if (!_STD is_constant_evaluated()) { - const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + (_Last - _First); - const auto _Result = _STD _Minmax_vectorized(_First_ptr, _Last_ptr); - return {static_cast<_Vty>(_Result._Min), static_cast<_Vty>(_Result._Max)}; + if constexpr (is_same_v<_Pj, identity> && sized_sentinel_for<_Se, _It>) { + if constexpr (_Is_min_max_value_optimization_safe<_It, _Pr>) { + if (!_STD is_constant_evaluated()) { + const auto _First_ptr = _STD to_address(_First); + const auto _Last_ptr = _First_ptr + (_Last - _First); + const auto _Result = _STD _Minmax_vectorized(_First_ptr, _Last_ptr); + return {static_cast<_Vty>(_Result._Min), static_cast<_Vty>(_Result._Max)}; + } + } else if constexpr (_Is_min_max_optimization_safe<_It, _Pr>) { + if (!_STD is_constant_evaluated()) { + const auto _First_ptr = _STD to_address(_First); + const auto _Last_ptr = _First_ptr + (_Last - _First); + const auto _Result = _STD _Minmax_element_vectorized(_First_ptr, _Last_ptr); + return {*static_cast(_Result.first), *static_cast(_Result.second)}; + } } } #endif // _USE_STD_VECTOR_ALGORITHMS From 1a818d61570691419122c55194c06451c45e0604 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 23 Jun 2024 09:18:45 +0300 Subject: [PATCH 17/35] common header --- .../test_vector_algorithms_support.hpp | 62 +++++++++++++++++ .../VSO_0000000_vector_algorithms/test.cpp | 68 ++----------------- .../test.cpp | 55 +-------------- 3 files changed, 70 insertions(+), 115 deletions(-) create mode 100644 tests/std/include/test_vector_algorithms_support.hpp diff --git a/tests/std/include/test_vector_algorithms_support.hpp b/tests/std/include/test_vector_algorithms_support.hpp new file mode 100644 index 00000000000..f38f6ff63c7 --- /dev/null +++ b/tests/std/include/test_vector_algorithms_support.hpp @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension +#endif // __clang__ + +void initialize_randomness(std::mt19937_64& gen) { + constexpr size_t n = std::mt19937_64::state_size; + constexpr size_t w = std::mt19937_64::word_size; + static_assert(w % 32 == 0, "w should be evenly divisible by 32"); + constexpr size_t k = w / 32; + + std::vector vec(n * k); + + std::random_device rd; + std::generate(vec.begin(), vec.end(), ref(rd)); + + printf("This is a randomized test.\n"); + printf("DO NOT IGNORE/RERUN ANY FAILURES.\n"); + printf("You must report them to the STL maintainers.\n\n"); + + printf("Seed vector: "); + for (const auto& e : vec) { + printf("%u,", e); + } + printf("\n"); + + std::seed_seq seq(vec.cbegin(), vec.cend()); + gen.seed(seq); +} + +#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) +extern "C" long __isa_enabled; + +void disable_instructions(ISA_AVAILABILITY isa) { + __isa_enabled &= ~(1UL << static_cast(isa)); +} +#endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) + +constexpr size_t dataCount = 1024; + +void run_randomized_tests_with_different_isa_levels(void tests(std::mt19937_64& gen)) { + std::mt19937_64 gen; + initialize_randomness(gen); + + tests(gen); +#ifndef _M_CEE_PURE +#if defined(_M_IX86) || defined(_M_X64) + disable_instructions(__ISA_AVAILABLE_AVX2); + tests(gen); + + disable_instructions(__ISA_AVAILABLE_SSE42); + tests(gen); +#endif // defined(_M_IX86) || defined(_M_X64) +#endif // _M_CEE_PURE +} diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index d28f97aaa47..d36904df1d3 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -10,10 +10,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -25,49 +23,10 @@ #endif // _HAS_CXX20 #include "test_min_max_element_support.hpp" +#include "test_vector_algorithms_support.hpp" using namespace std; -#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension -#endif // __clang__ - -void initialize_randomness(mt19937_64& gen) { - constexpr size_t n = mt19937_64::state_size; - constexpr size_t w = mt19937_64::word_size; - static_assert(w % 32 == 0, "w should be evenly divisible by 32"); - constexpr size_t k = w / 32; - - vector vec(n * k); - - random_device rd; - generate(vec.begin(), vec.end(), ref(rd)); - - printf("This is a randomized test.\n"); - printf("DO NOT IGNORE/RERUN ANY FAILURES.\n"); - printf("You must report them to the STL maintainers.\n\n"); - - printf("Seed vector: "); - for (const auto& e : vec) { - printf("%u,", e); - } - printf("\n"); - - seed_seq seq(vec.cbegin(), vec.cend()); - gen.seed(seq); -} - -#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) -extern "C" long __isa_enabled; - -void disable_instructions(ISA_AVAILABILITY isa) { - __isa_enabled &= ~(1UL << static_cast(isa)); -} -#endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) - -constexpr size_t dataCount = 1024; - template ptrdiff_t last_known_good_count(FwdIt first, FwdIt last, T v) { ptrdiff_t result = 0; @@ -1132,24 +1091,9 @@ int main() { #if _HAS_CXX20 assert(test_constexpr()); #endif // _HAS_CXX20 - - mt19937_64 gen; - initialize_randomness(gen); - - test_vector_algorithms(gen); - test_various_containers(); - test_bitset(gen); -#ifndef _M_CEE_PURE -#if defined(_M_IX86) || defined(_M_X64) - disable_instructions(__ISA_AVAILABLE_AVX2); - test_vector_algorithms(gen); - test_various_containers(); - test_bitset(gen); - - disable_instructions(__ISA_AVAILABLE_SSE42); - test_vector_algorithms(gen); - test_various_containers(); - test_bitset(gen); -#endif // defined(_M_IX86) || defined(_M_X64) -#endif // _M_CEE_PURE + run_randomized_tests_with_different_isa_levels([](mt19937_64& gen) { + test_vector_algorithms(gen); + test_various_containers(); + test_bitset(gen); + }); } diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp index 042a4def9cd..5648129ab98 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp @@ -20,49 +20,10 @@ #endif // _HAS_CXX20 #include "test_min_max_element_support.hpp" +#include "test_vector_algorithms_support.hpp" using namespace std; -#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension -#endif // __clang__ - -#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) -extern "C" long __isa_enabled; - -void disable_instructions(ISA_AVAILABILITY isa) { - __isa_enabled &= ~(1UL << static_cast(isa)); -} -#endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) - -void initialize_randomness(mt19937_64& gen) { - constexpr size_t n = mt19937_64::state_size; - constexpr size_t w = mt19937_64::word_size; - static_assert(w % 32 == 0, "w should be evenly divisible by 32"); - constexpr size_t k = w / 32; - - vector vec(n * k); - - random_device rd; - generate(vec.begin(), vec.end(), ref(rd)); - - printf("This is a randomized test.\n"); - printf("DO NOT IGNORE/RERUN ANY FAILURES.\n"); - printf("You must report them to the STL maintainers.\n\n"); - - printf("Seed vector: "); - for (const auto& e : vec) { - printf("%u,", e); - } - printf("\n"); - - seed_seq seq(vec.cbegin(), vec.cend()); - gen.seed(seq); -} - -constexpr size_t dataCount = 1024; - template void test_min_max_element_floating_with_values(mt19937_64& gen, const std::vector& input_of_input) { uniform_int_distribution idx_dis(0, input_of_input.size() - 1); @@ -150,17 +111,5 @@ void test_vector_algorithms(mt19937_64& gen) { } int main() { - mt19937_64 gen; - initialize_randomness(gen); - - test_vector_algorithms(gen); -#ifndef _M_CEE_PURE -#if defined(_M_IX86) || defined(_M_X64) - disable_instructions(__ISA_AVAILABLE_AVX2); - test_vector_algorithms(gen); - - disable_instructions(__ISA_AVAILABLE_SSE42); - test_vector_algorithms(gen); -#endif // defined(_M_IX86) || defined(_M_X64) -#endif // _M_CEE_PURE + run_randomized_tests_with_different_isa_levels(test_vector_algorithms); } From e1a5ff762ea28999228c2974aa0feb99934c5e9e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 08:25:28 -0700 Subject: [PATCH 18/35] Add `#pragma once` to new test header. --- tests/std/include/test_vector_algorithms_support.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/include/test_vector_algorithms_support.hpp b/tests/std/include/test_vector_algorithms_support.hpp index f38f6ff63c7..12953131954 100644 --- a/tests/std/include/test_vector_algorithms_support.hpp +++ b/tests/std/include/test_vector_algorithms_support.hpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#pragma once + #include #include #include From 437454e10d5162a86584761fd8772f8a48b63b2a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 08:29:56 -0700 Subject: [PATCH 19/35] Include more headers. * `` for `generate` * `` for `CHAR_BIT` (pre-existing) * `` for `signbit` * `` for `size_t` * `` for `uint32_t` * `` for `printf` * `` for `ref` * `` for `mt19937_64` --- tests/std/include/test_min_max_element_support.hpp | 1 + tests/std/include/test_vector_algorithms_support.hpp | 5 +++++ tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/tests/std/include/test_min_max_element_support.hpp b/tests/std/include/test_min_max_element_support.hpp index 3f8d3f2df9f..3242a807bc8 100644 --- a/tests/std/include/test_min_max_element_support.hpp +++ b/tests/std/include/test_min_max_element_support.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/tests/std/include/test_vector_algorithms_support.hpp b/tests/std/include/test_vector_algorithms_support.hpp index 12953131954..d8c0d71c1ab 100644 --- a/tests/std/include/test_vector_algorithms_support.hpp +++ b/tests/std/include/test_vector_algorithms_support.hpp @@ -3,6 +3,11 @@ #pragma once +#include +#include +#include +#include +#include #include #include #include diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index d36904df1d3..992ec6f2b87 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include From 684cd27cf82791dfd10b059c0387bc4c8fbafa84 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 09:50:48 -0700 Subject: [PATCH 20/35] Include fewer headers. --- .../tests/VSO_0000000_vector_algorithms/test.cpp | 1 - .../VSO_0000000_vector_algorithms_floats/test.cpp | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 992ec6f2b87..1e8e501e50d 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp index 5648129ab98..ece01e2fbaf 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp @@ -1,24 +1,11 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include -#include #include -#include -#include -#include -#include #include #include -#include -#include #include -#if _HAS_CXX20 -#include -#include -#endif // _HAS_CXX20 - #include "test_min_max_element_support.hpp" #include "test_vector_algorithms_support.hpp" From 206ef5c73585464040e97fc920bc4fd2ae9326ce Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 08:35:31 -0700 Subject: [PATCH 21/35] Add `std::` qualification. --- .../include/test_min_max_element_support.hpp | 8 +++---- .../test_vector_algorithms_support.hpp | 24 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/std/include/test_min_max_element_support.hpp b/tests/std/include/test_min_max_element_support.hpp index 3242a807bc8..f23627a4627 100644 --- a/tests/std/include/test_min_max_element_support.hpp +++ b/tests/std/include/test_min_max_element_support.hpp @@ -123,10 +123,10 @@ void test_case_min_max_element(const std::vector& input) { // With /fp:fast mode the compiler does not try to produce the code that correctly // distincts +0.0 and -0.0, so the algorithms are not expected to either. if constexpr (std::is_floating_point_v) { - assert(signbit(*expected_min) == signbit(actual_min_value)); - assert(signbit(*expected_max) == signbit(actual_max_value)); - assert(signbit(*expected_minmax.first) == signbit(actual_minmax_value.min)); - assert(signbit(*expected_minmax.second) == signbit(actual_minmax_value.max)); + assert(std::signbit(*expected_min) == std::signbit(actual_min_value)); + assert(std::signbit(*expected_max) == std::signbit(actual_max_value)); + assert(std::signbit(*expected_minmax.first) == std::signbit(actual_minmax_value.min)); + assert(std::signbit(*expected_minmax.second) == std::signbit(actual_minmax_value.max)); } #endif // !defined(_M_FP_FAST) } diff --git a/tests/std/include/test_vector_algorithms_support.hpp b/tests/std/include/test_vector_algorithms_support.hpp index d8c0d71c1ab..62e27b45b27 100644 --- a/tests/std/include/test_vector_algorithms_support.hpp +++ b/tests/std/include/test_vector_algorithms_support.hpp @@ -18,25 +18,25 @@ #endif // __clang__ void initialize_randomness(std::mt19937_64& gen) { - constexpr size_t n = std::mt19937_64::state_size; - constexpr size_t w = std::mt19937_64::word_size; + constexpr std::size_t n = std::mt19937_64::state_size; + constexpr std::size_t w = std::mt19937_64::word_size; static_assert(w % 32 == 0, "w should be evenly divisible by 32"); - constexpr size_t k = w / 32; + constexpr std::size_t k = w / 32; - std::vector vec(n * k); + std::vector vec(n * k); std::random_device rd; - std::generate(vec.begin(), vec.end(), ref(rd)); + std::generate(vec.begin(), vec.end(), std::ref(rd)); - printf("This is a randomized test.\n"); - printf("DO NOT IGNORE/RERUN ANY FAILURES.\n"); - printf("You must report them to the STL maintainers.\n\n"); + std::printf("This is a randomized test.\n"); + std::printf("DO NOT IGNORE/RERUN ANY FAILURES.\n"); + std::printf("You must report them to the STL maintainers.\n\n"); - printf("Seed vector: "); + std::printf("Seed vector: "); for (const auto& e : vec) { - printf("%u,", e); + std::printf("%u,", e); } - printf("\n"); + std::printf("\n"); std::seed_seq seq(vec.cbegin(), vec.cend()); gen.seed(seq); @@ -50,7 +50,7 @@ void disable_instructions(ISA_AVAILABILITY isa) { } #endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) -constexpr size_t dataCount = 1024; +constexpr std::size_t dataCount = 1024; void run_randomized_tests_with_different_isa_levels(void tests(std::mt19937_64& gen)) { std::mt19937_64 gen; From 7a0d538e05b9377344b0195f5fec7dc9769e99b2 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 09:31:36 -0700 Subject: [PATCH 22/35] Remove `std::` qualification. --- tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp index ece01e2fbaf..63ba9ae36a7 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp @@ -12,7 +12,7 @@ using namespace std; template -void test_min_max_element_floating_with_values(mt19937_64& gen, const std::vector& input_of_input) { +void test_min_max_element_floating_with_values(mt19937_64& gen, const vector& input_of_input) { uniform_int_distribution idx_dis(0, input_of_input.size() - 1); vector input; From 7891ef76c1b2b920461baac03c73eac2f13ef217 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 08:38:14 -0700 Subject: [PATCH 23/35] Drop unnecessary `if constexpr` suppression. --- tests/std/include/test_vector_algorithms_support.hpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/std/include/test_vector_algorithms_support.hpp b/tests/std/include/test_vector_algorithms_support.hpp index 62e27b45b27..74a9bf8b0d8 100644 --- a/tests/std/include/test_vector_algorithms_support.hpp +++ b/tests/std/include/test_vector_algorithms_support.hpp @@ -12,11 +12,6 @@ #include #include -#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension -#endif // __clang__ - void initialize_randomness(std::mt19937_64& gen) { constexpr std::size_t n = std::mt19937_64::state_size; constexpr std::size_t w = std::mt19937_64::word_size; From 158a007f3fa61988e9f2de2c909866a89778015f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 08:43:34 -0700 Subject: [PATCH 24/35] Take a function object instead of a function pointer. --- tests/std/include/test_vector_algorithms_support.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/std/include/test_vector_algorithms_support.hpp b/tests/std/include/test_vector_algorithms_support.hpp index 74a9bf8b0d8..06958366cd5 100644 --- a/tests/std/include/test_vector_algorithms_support.hpp +++ b/tests/std/include/test_vector_algorithms_support.hpp @@ -47,7 +47,8 @@ void disable_instructions(ISA_AVAILABILITY isa) { constexpr std::size_t dataCount = 1024; -void run_randomized_tests_with_different_isa_levels(void tests(std::mt19937_64& gen)) { +template +void run_randomized_tests_with_different_isa_levels(TestFunc tests) { std::mt19937_64 gen; initialize_randomness(gen); From 1058249f127fbbe6a8d7a62f77cb174d574d25a4 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 08:45:09 -0700 Subject: [PATCH 25/35] Header-only functions should be `inline`. --- tests/std/include/test_vector_algorithms_support.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/include/test_vector_algorithms_support.hpp b/tests/std/include/test_vector_algorithms_support.hpp index 06958366cd5..78b9a5eec0b 100644 --- a/tests/std/include/test_vector_algorithms_support.hpp +++ b/tests/std/include/test_vector_algorithms_support.hpp @@ -12,7 +12,7 @@ #include #include -void initialize_randomness(std::mt19937_64& gen) { +inline void initialize_randomness(std::mt19937_64& gen) { constexpr std::size_t n = std::mt19937_64::state_size; constexpr std::size_t w = std::mt19937_64::word_size; static_assert(w % 32 == 0, "w should be evenly divisible by 32"); @@ -40,7 +40,7 @@ void initialize_randomness(std::mt19937_64& gen) { #if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) extern "C" long __isa_enabled; -void disable_instructions(ISA_AVAILABILITY isa) { +inline void disable_instructions(ISA_AVAILABILITY isa) { __isa_enabled &= ~(1UL << static_cast(isa)); } #endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) From 1384cf87163a3597c14aabaac33bb4803f4fa0eb Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 08:48:47 -0700 Subject: [PATCH 26/35] Use consistent preprocessor guards. --- tests/std/include/test_vector_algorithms_support.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/std/include/test_vector_algorithms_support.hpp b/tests/std/include/test_vector_algorithms_support.hpp index 78b9a5eec0b..5cdffd9ffe4 100644 --- a/tests/std/include/test_vector_algorithms_support.hpp +++ b/tests/std/include/test_vector_algorithms_support.hpp @@ -53,13 +53,12 @@ void run_randomized_tests_with_different_isa_levels(TestFunc tests) { initialize_randomness(gen); tests(gen); -#ifndef _M_CEE_PURE -#if defined(_M_IX86) || defined(_M_X64) + +#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) disable_instructions(__ISA_AVAILABLE_AVX2); tests(gen); disable_instructions(__ISA_AVAILABLE_SSE42); tests(gen); -#endif // defined(_M_IX86) || defined(_M_X64) -#endif // _M_CEE_PURE +#endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) } From e50cf1251fd21f33ef274a2fa41d8dbd62139558 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 09:00:36 -0700 Subject: [PATCH 27/35] Add new test to test.lst. --- tests/std/test.lst | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/test.lst b/tests/std/test.lst index f75a8adcd78..7dda49e7f12 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -709,6 +709,7 @@ tests\VSO_0000000_regex_use tests\VSO_0000000_string_view_idl tests\VSO_0000000_type_traits tests\VSO_0000000_vector_algorithms +tests\VSO_0000000_vector_algorithms_floats tests\VSO_0000000_wcfb01_idempotent_container_destructors tests\VSO_0000000_wchar_t_filebuf_xsmeown tests\VSO_0095468_clr_exception_ptr_bad_alloc From 129c8d704fc6f58d985d2c1c420c5759424aadda Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 09:53:49 -0700 Subject: [PATCH 28/35] Fix code typo: `-1, 0` => `-1.0` --- tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp index 63ba9ae36a7..9c3266d7542 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp @@ -63,7 +63,7 @@ void test_min_max_element_floating_zero_predef() { test_case_min_max_element(v); for (size_t i = 0; i != pos; ++i) { - v[i] = -1, 0; + v[i] = -1.0; } test_case_min_max_element(v); From 3ccad6c210a438732cd49e9b5377082f560d44c1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 09:58:30 -0700 Subject: [PATCH 29/35] Fix bug: `-0` => `-0.0` Add other point-zeros for consistency. --- .../std/tests/VSO_0000000_vector_algorithms_floats/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp index 9c3266d7542..c43d6a2a5b1 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/test.cpp @@ -43,9 +43,9 @@ void test_min_max_element_floating_any(mt19937_64& gen) { template void test_min_max_element_floating_zero(mt19937_64& gen) { - test_min_max_element_floating_with_values(gen, {-0, +0}); - test_min_max_element_floating_with_values(gen, {-0, +0, +1}); - test_min_max_element_floating_with_values(gen, {-0, +0, -1}); + test_min_max_element_floating_with_values(gen, {-0.0, +0.0}); + test_min_max_element_floating_with_values(gen, {-0.0, +0.0, +1.0}); + test_min_max_element_floating_with_values(gen, {-0.0, +0.0, -1.0}); } template From 09450f62ccf6ea42b96c77e911e960425ac653ab Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 20 Aug 2024 07:54:02 -0700 Subject: [PATCH 30/35] Add missing quotes. --- .../std/tests/VSO_0000000_vector_algorithms_floats/env.lst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst b/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst index 0173530a3d9..c1b2fc264f2 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst @@ -46,9 +46,9 @@ PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsin PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MDd /std:c++17 /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive- /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" RUNALL_CROSSLIST -* PM_CL="/fp:strict -* PM_CL="/fp:precise -* PM_CL="/fp:fast +* PM_CL="/fp:strict" +* PM_CL="/fp:precise" +* PM_CL="/fp:fast" RUNALL_CROSSLIST * PM_CL="" # Test default setting * PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test escape hatch, see GH-1751 From b1fb57bb947ba682ae08a9c33ff8edecf0e391e4 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 20 Aug 2024 08:01:48 -0700 Subject: [PATCH 31/35] Drop /fp options instead of whole lines. --- .../tests/VSO_0000000_vector_algorithms_floats/env.lst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst b/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst index c1b2fc264f2..c78b79e9262 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst @@ -3,7 +3,7 @@ RUNALL_INCLUDE ..\prefix.lst RUNALL_CROSSLIST -# Copied from ..\usual_matrix.lst, /fp lines excluded +# Copied from ..\usual_matrix.lst, /fp options excluded PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++14 /w14640 /Zc:threadSafeInit-" ASAN PM_CL="/EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /w14640 /Zc:threadSafeInit-" @@ -18,6 +18,8 @@ PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:wcha ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" +PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++14 /w14640 /Zc:threadSafeInit- /Zc:preprocessor" +ASAN PM_CL="/EHsc /MDd /std:c++14 /w14640 /Zc:threadSafeInit- /Zc:preprocessor -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++17 /permissive- /w14640 /Zc:threadSafeInit-" ASAN PM_CL="/EHsc /MDd /std:c++17 /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++20 /permissive- /w14640 /Zc:threadSafeInit-" @@ -28,6 +30,8 @@ PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /analyze: ASAN PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" # No corresponding ASAN config, since the above differs from another config only in IDL +PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" +ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive- /w14640 /Zc:threadSafeInit-" ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /w14640 /Zc:threadSafeInit-" @@ -45,6 +49,9 @@ PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MDd /std:c++17 /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive- /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MTd /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- --start-no-unused-arguments" +# TRANSITION, GH-3568 +# PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive- /w14640 /Zc:threadSafeInit- -fsanitize=undefined -fno-sanitize-recover=undefined --start-no-unused-arguments" RUNALL_CROSSLIST * PM_CL="/fp:strict" * PM_CL="/fp:precise" From 8d55da16761d37b9f4e2626f472f20904b274260 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 20 Aug 2024 08:39:35 -0700 Subject: [PATCH 32/35] Adjust endif comment to match. --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index ead020d1494..dbcf2f84c13 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -6816,7 +6816,7 @@ template > constexpr bool _Is_min_max_value_optimization_safe = // Activate the vector algorithms for ranges::min/max? #ifndef _M_FP_FAST !is_floating_point_v<_Elem> && -#endif // !_M_FP_FAST +#endif // ^^^ !defined(_M_FP_FAST) ^^^ _Is_min_max_optimization_safe<_Iter, _Pr, _Elem>; template From 2e947b6d270360ef2eebd2e20bab95191ad09d76 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 20 Aug 2024 09:31:48 -0700 Subject: [PATCH 33/35] Drop duplicate comment in vector_algorithms.cpp. --- stl/src/vector_algorithms.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 9c46f0f4c7a..8d57d7df204 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -1982,18 +1982,6 @@ namespace { template <_Min_max_mode _Mode, class _Traits, bool _Sign> auto __std_minmax_impl(const void* _First, const void* const _Last) noexcept { - // The value-based vectorized rather than the position-based one does not always produce - // the expected results for floatting point types. - // - // Efficient vectorization needs to find vertical minmax first, and then the horizontal one. - // This alters order of comparison: index zero element is first compared against - // vector size equal index element and only in the end against index one element. - // With equivalent but distinguishable +0.0 and -0.0 values, the altered comparison order - // will not produce the expected result in some cases (will return +0.0 instead of -0.0 or the reverse) - // - // The result is still acceptable for /fp:fast when +0.0 / -0.0 are not expected to be properly distinguished, - // and the compiler itself takes advantage of it. - using _Ty = std::conditional_t<_Sign, typename _Traits::_Signed_t, typename _Traits::_Unsigned_t>; _Ty _Cur_min_val; // initialized in both of the branches below From b7c840c430b35830dbed872cd43ec987e725888d Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 09:14:48 -0700 Subject: [PATCH 34/35] Improve comments. --- stl/inc/xutility | 10 +++++----- tests/std/include/test_min_max_element_support.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index dbcf2f84c13..f6fe60bae2b 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -6801,14 +6801,14 @@ constexpr bool _Is_min_max_optimization_safe = // Activate the vector algorithms #endif // _HAS_CXX20 is_same<_Pr, less<>>, is_same<_Pr, less<_Elem>>>>; // predicate is less -// The value-based vectorized rather than the position-based one does not always produce -// the expected results for floatting point types. +// Unlike the position-based vectorized implementation, the value-based vectorized implementation +// does not always produce the expected results for floating-point types. // -// Efficient vectorization needs to find vertical minmax first, and then the horizontal one. -// This alters order of comparison: index zero element is first compared against +// Efficient vectorization needs to find the vertical minmax first, and then the horizontal one. +// This alters the order of comparison: index zero element is first compared against // vector size equal index element and only in the end against index one element. // With equivalent but distinguishable +0.0 and -0.0 values, the altered comparison order -// will not produce the expected result in some cases (will return +0.0 instead of -0.0 or the reverse) +// will not produce the expected result in some cases (will return +0.0 instead of -0.0 or the reverse). // // The result is still acceptable for /fp:fast when +0.0 / -0.0 are not expected to be properly distinguished, // and the compiler itself takes advantage of it. diff --git a/tests/std/include/test_min_max_element_support.hpp b/tests/std/include/test_min_max_element_support.hpp index f23627a4627..621c76fe960 100644 --- a/tests/std/include/test_min_max_element_support.hpp +++ b/tests/std/include/test_min_max_element_support.hpp @@ -121,7 +121,7 @@ void test_case_min_max_element(const std::vector& input) { #ifndef _M_FP_FAST // With /fp:fast mode the compiler does not try to produce the code that correctly - // distincts +0.0 and -0.0, so the algorithms are not expected to either. + // distinguishes +0.0 and -0.0, so the algorithms are not expected to either. if constexpr (std::is_floating_point_v) { assert(std::signbit(*expected_min) == std::signbit(actual_min_value)); assert(std::signbit(*expected_max) == std::signbit(actual_max_value)); From e1c4d4ffd99542a5aafc1bcb1a3ef557771446bf Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 24 Aug 2024 19:48:00 -0700 Subject: [PATCH 35/35] Drop /clr and /clr:pure lines. --- .../std/tests/VSO_0000000_vector_algorithms_floats/env.lst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst b/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst index c78b79e9262..b8b3805e451 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst +++ b/tests/std/tests/VSO_0000000_vector_algorithms_floats/env.lst @@ -3,7 +3,7 @@ RUNALL_INCLUDE ..\prefix.lst RUNALL_CROSSLIST -# Copied from ..\usual_matrix.lst, /fp options excluded +# Copied from ..\usual_matrix.lst, /fp options excluded, /clr and /clr:pure lines dropped PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++14 /w14640 /Zc:threadSafeInit-" ASAN PM_CL="/EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /w14640 /Zc:threadSafeInit-" @@ -38,10 +38,6 @@ PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /w14640 / ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit-" ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- /w14640 /Zc:threadSafeInit- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/clr /MD /std:c++20 /w14640 /Zc:threadSafeInit-" -PM_CL="/clr /MDd /std:c++20 /w14640 /Zc:threadSafeInit-" -PM_CL="/clr:pure /MD /std:c++14" -PM_CL="/clr:pure /MDd /std:c++14" PM_CL="/BE /c /EHsc /MD /std:c++14 /w14640 /Zc:threadSafeInit-" PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive- /w14640 /Zc:threadSafeInit-" PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive- /w14640 /Zc:threadSafeInit-"