From 3a9ae6c275c99d8086012e19ea3e1947a66a4c12 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 15 Jun 2025 12:21:52 +0300 Subject: [PATCH 01/16] semi-vectorization --- stl/inc/algorithm | 81 +++++++++ stl/src/vector_algorithms.cpp | 324 ++++++++++++++++++++++++++++++++++ 2 files changed, 405 insertions(+) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index e8afb54e353..e1183d376fb 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -85,6 +85,23 @@ const void* __stdcall __std_is_sorted_until_8u(const void* _First, const void* _ const void* __stdcall __std_is_sorted_until_f(const void* _First, const void* _Last, bool _Greater) noexcept; const void* __stdcall __std_is_sorted_until_d(const void* _First, const void* _Last, bool _Greater) noexcept; +__declspec(noalias) bool __stdcall __std_includes_less_1i( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +__declspec(noalias) bool __stdcall __std_includes_less_1u( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +__declspec(noalias) bool __stdcall __std_includes_less_2i( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +__declspec(noalias) bool __stdcall __std_includes_less_2u( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +__declspec(noalias) bool __stdcall __std_includes_less_4i( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +__declspec(noalias) bool __stdcall __std_includes_less_4u( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +__declspec(noalias) bool __stdcall __std_includes_less_8i( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; +__declspec(noalias) bool __stdcall __std_includes_less_8u( + const void* _First1, const void* _Last1, const void* _First2, const void* _Last2) noexcept; + // TRANSITION, DevCom-10610477 __declspec(noalias) void __stdcall __std_replace_4( void* _First, void* _Last, uint32_t _Old_val, uint32_t _New_val) noexcept; @@ -255,6 +272,40 @@ _Ty* _Is_sorted_until_vectorized(_Ty* const _First, _Ty* const _Last, const bool } } +template +bool _Includes_vectorized( + const _Ty* const _First1, const _Ty* const _Last1, const _Ty* const _First2, const _Ty* const _Last2) noexcept { + constexpr bool _Signed = is_signed_v<_Ty>; + + if constexpr (sizeof(_Ty) == 1) { + if constexpr (_Signed) { + return ::__std_includes_less_1i(_First1, _Last1, _First2, _Last2); + } else { + return ::__std_includes_less_1u(_First1, _Last1, _First2, _Last2); + } + } else if constexpr (sizeof(_Ty) == 2) { + if constexpr (_Signed) { + return ::__std_includes_less_2i(_First1, _Last1, _First2, _Last2); + } else { + return ::__std_includes_less_2u(_First1, _Last1, _First2, _Last2); + } + } else if constexpr (sizeof(_Ty) == 4) { + if constexpr (_Signed) { + return ::__std_includes_less_4i(_First1, _Last1, _First2, _Last2); + } else { + return ::__std_includes_less_4u(_First1, _Last1, _First2, _Last2); + } + } else if constexpr (sizeof(_Ty) == 8) { + if constexpr (_Signed) { + return ::__std_includes_less_8i(_First1, _Last1, _First2, _Last2); + } else { + return ::__std_includes_less_8u(_First1, _Last1, _First2, _Last2); + } + } else { + _STL_INTERNAL_STATIC_ASSERT(false); // unexpected size + } +} + template __declspec(noalias) void _Replace_vectorized( _Ty* const _First, _Ty* const _Last, const _TVal1 _Old_val, const _TVal2 _New_val) noexcept { @@ -383,6 +434,13 @@ constexpr bool _Output_iterator_for_vector_alg_is_safe() { } } +// Can we activate the vector algorithms for includes? +template > +constexpr bool _Vector_alg_includes_iterators_safe = + _Iterators_are_contiguous<_Iter1, _Iter2> // Iterators must be contiguous so we can get raw pointers. + && !_Iterator_is_volatile<_Iter1> && !_Iterator_is_volatile<_Iter2> // Iterators must not be volatile. + && is_same_v<_Elem, _Iter_value_t<_Iter2>> // Iterators value is same + && disjunction_v, is_pointer<_Elem>>; // Integral or pointer type. _STD_END #endif // _USE_STD_VECTOR_ALGORITHMS @@ -10165,6 +10223,15 @@ _NODISCARD _CONSTEXPR20 bool includes(_InIt1 _First1, _InIt1 _Last1, _InIt2 _Fir return false; } +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Vector_alg_includes_iterators_safe<_InIt1, _InIt2> && _Is_predicate_less<_InIt1, _Pr>) { + if (!_STD _Is_constant_evaluated()) { + return _STD _Includes_vectorized(_STD _To_address(_First1), _STD _To_address(_Last1), + _STD _To_address(_First2), _STD _To_address(_Last2)); + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + for (;;) { if (_DEBUG_LT_PRED(_Pred, *_UFirst1, *_UFirst2)) { ++_UFirst1; @@ -10254,6 +10321,20 @@ namespace ranges { return false; } +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Vector_alg_includes_iterators_safe<_It1, _It2> && _Is_predicate_less<_It1, _Pr> + && sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2> + && is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>) { + if (!_STD _Is_constant_evaluated()) { + const auto _First1_ptr = _STD to_address(_First1); + const auto _First2_ptr = _STD to_address(_First2); + const auto _Last1_ptr = _First1_ptr + static_cast(_Last1 - _First1); + const auto _Last2_ptr = _First2_ptr + static_cast(_Last2 - _First2); + return _STD _Includes_vectorized(_First1_ptr, _Last1_ptr, _First2_ptr, _Last2_ptr); + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + for (;;) { if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { ++_First1; diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index b83b5fe3969..b0d5ad308ce 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -6845,6 +6845,330 @@ void* __stdcall __std_unique_copy_8(const void* _First, const void* const _Last, } // extern "C" +namespace { + namespace _Sorted_ranges { +#ifdef _M_ARM64EC + static_assert(false, "No vectorization for _M_ARM64EC yet"); +#else // ^^^ defined(_M_ARM64EC) / !defined(_M_ARM64EC) vvv + struct _Traits_avx { + using _Guard = _Zeroupper_on_exit; + static constexpr size_t _Vec_size = 32; + static constexpr size_t _Tail_mask = 0x1C; + + static __m256i _Load(const void* const _Src) noexcept { + return _mm256_loadu_si256(reinterpret_cast(_Src)); + } + + static __m256i _Load_mask(const void* const _Src, const __m256i _Mask) noexcept { + return _mm256_maskload_epi32(reinterpret_cast(_Src), _Mask); + } + + static unsigned long _Mask(const __m256i _Val) noexcept { + return _mm256_movemask_epi8(_Val); + } + + static uint32_t _Bsf(const uint32_t _Val) noexcept { + return _tzcnt_u32(_Val); + } + }; + + struct _Traits_1_avx : _Traits_avx { + static __m256i _Broadcast(const uint8_t _Data) noexcept { + return _mm256_broadcastb_epi8(_mm_cvtsi32_si128(static_cast(_Data))); + } + + static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { + return _mm256_cmpgt_epi8(_First, _Second); + } + }; + + struct _Traits_2_avx : _Traits_avx { + static __m256i _Broadcast(const uint16_t _Data) noexcept { + return _mm256_broadcastw_epi16(_mm_cvtsi32_si128(static_cast(_Data))); + } + + static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { + return _mm256_cmpgt_epi16(_First, _Second); + } + }; + + struct _Traits_4_avx : _Traits_avx { + static __m256i _Broadcast(const uint32_t _Data) noexcept { + return _mm256_broadcastd_epi32(_mm_cvtsi32_si128(_Data)); + } + + static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { + return _mm256_cmpgt_epi32(_First, _Second); + } + }; + + struct _Traits_8_avx : _Traits_avx { + static __m256i _Broadcast(const uint64_t _Data) noexcept { + return _mm256_broadcastq_epi64(_mm_cvtsi64x_si128(_Data)); + } + + static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { + return _mm256_cmpgt_epi64(_First, _Second); + } + }; + + struct _Traits_sse { + using _Guard = char; + static constexpr size_t _Vec_size = 16; + static constexpr size_t _Tail_mask = 0; + + static __m128i _Load(const void* const _Src) noexcept { + return _mm_loadu_si128(reinterpret_cast(_Src)); + } + + static unsigned long _Mask(const __m128i _Val) noexcept { + return _mm_movemask_epi8(_Val); + } + + static uint32_t _Bsf(const uint32_t _Val) noexcept { + unsigned long _Index; + // CodeQL [SM02313] _Index is always initialized: _Val != 0; see explanastion at call sites. + _BitScanForward(&_Index, _Val); + return _Index; + } + }; + + struct _Traits_1_sse : _Traits_sse { + static __m128i _Broadcast(const uint8_t _Data) noexcept { + return _mm_shuffle_epi8(_mm_cvtsi32_si128(static_cast(_Data)), _mm_setzero_si128()); + } + + static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { + return _mm_cmpgt_epi8(_First, _Second); + } + }; + + struct _Traits_2_sse : _Traits_sse { + static __m128i _Broadcast(const uint16_t _Data) noexcept { + return _mm_shuffle_epi8(_mm_cvtsi32_si128(static_cast(_Data)), _mm_set1_epi16(0x0100)); + } + + static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { + return _mm_cmpgt_epi16(_First, _Second); + } + }; + + struct _Traits_4_sse : _Traits_sse { + static __m128i _Broadcast(const uint32_t _Data) noexcept { + return _mm_shuffle_epi32(_mm_cvtsi32_si128(_Data), _MM_SHUFFLE(0, 0, 0, 0)); + } + + static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { + return _mm_cmpgt_epi32(_First, _Second); + } + }; + + struct _Traits_8_sse : _Traits_sse { + static __m128i _Broadcast(const uint64_t _Data) noexcept { + return _mm_shuffle_epi32(_mm_cvtsi64x_si128(_Data), _MM_SHUFFLE(1, 0, 1, 0)); + } + + static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { + return _mm_cmpgt_epi64(_First, _Second); + } + }; +#endif // ^^^ !defined(_M_ARM64EC) ^^^ + + template + bool _Includes_impl( + const void* _First1, const void* const _Last1, const void* _First2, const void* const _Last2) noexcept { + if constexpr (!std::is_same_v<_Traits, void>) { +#ifdef _M_ARM64EC + static_assert(false, "No vectorization for _M_ARM64EC yet"); +#else // ^^^ defined(_M_ARM64EC) / !defined(_M_ARM64EC) vvv + + // Only skipping some parts of haystack that are less than current needle element is vectorzied. + // Otherwise this is scalar algorithm. + + constexpr uint32_t _All_ones_mask = uint32_t{(uint64_t{1} << _Traits::_Vec_size) - 1}; + constexpr uint32_t _Highest_one_mask = 1u << (_Traits::_Vec_size - 1); + [[maybe_unused]] typename _Traits::_Guard _Guard; // TRANSITION, DevCom-10331414 + + const size_t _Size_bytes_1 = _Byte_length(_First1, _Last1); + const void* _Stop1 = _First1; + _Advance_bytes(_Stop1, _Size_bytes_1 & ~(_Traits::_Vec_size - 1)); + + _Ty _Val2 = *reinterpret_cast(_First2); + auto _Start2 = _Traits::_Broadcast(_Val2); + + do { + const auto _Data1 = _Traits::_Load(_First1); + const void* _Next1 = _First1; + _Advance_bytes(_Next1, _Traits::_Vec_size); + + const uint32_t _Greater_start_2 = _Traits::_Mask(_Traits::_Cmp_gt(_Start2, _Data1)); + // Testing _Highest_one_mask can be a bit more effecient on AVX2 than comparing against + // _All_ones_mask (will test sign, and can share comparison with != 0 below). + if ((_Greater_start_2 & _Highest_one_mask) != 0) { + // Needle first element is greater than each element of haystack vector. + // Proceed to the next one, without updationg the needle comparand. + _First1 = _Next1; + } else { + if (_Greater_start_2 != 0) { + // Needle first element is greater than some first elements of haystack part. + // Advance past these elements. + // The input is nonzero because we handled that case with _Highest_one_mask branch above. + const uint32_t _Skip = _Traits::_Bsf(_Greater_start_2 ^ _All_ones_mask); + _Advance_bytes(_First1, _Skip); + } + + // The rest is scalar loop that completes the remaining vector-sized haystack part. + // Except that it updates current needle value to compare against. + do { + const _Ty _Val1 = *static_cast(_First1); + + if (_Val2 < _Val1) { + return false; + } + + if (_Val2 == _Val1) { + _Advance_bytes(_First2, sizeof(_Ty)); + if (_First2 == _Last2) { + return true; + } + + _Val2 = *reinterpret_cast(_First2); + } + + _Advance_bytes(_First1, sizeof(_Ty)); + } while (_First1 != _Next1); + + _Start2 = _Traits::_Broadcast(_Val2); + } + } while (_First1 != _Stop1); + + if constexpr (_Traits::_Tail_mask != 0) { + const size_t _Tail_bytes_size_1 = _Size_bytes_1 & _Traits::_Tail_mask; + if (_Tail_bytes_size_1 != 0) { + // Just try to advance pass less one omre time. + // Don't neet to repeat the scalar part here -- falling to scalar loop anyway. + const auto _Tail_mask = _Avx2_tail_mask_32(_Tail_bytes_size_1); + const auto _Data1 = _Traits::_Load_mask(_First1, _Tail_mask); + const auto _Cmp = _Traits::_Cmp_gt(_Start2, _Data1); + const uint32_t _Greater_start_2 = _Traits::_Mask(_mm256_and_si256(_Cmp, _Tail_mask)); + if (_Greater_start_2 != 0) { + // Needle first element is greater than some first elements of haystack part. + // Advance past these elements. + // The input is nonzero because tail mask will have zeros for remaining enements. + const uint32_t _Skip = _Traits::_Bsf(_Greater_start_2 ^ _All_ones_mask); + _Advance_bytes(_First1, _Skip); + } + } + } + + if (_First1 == _Last1) { + return false; + } +#endif // ^^^ !defined(_M_ARM64EC) ^^^ + } + + auto _Ptr1 = static_cast(_First1); + auto _Ptr2 = static_cast(_First2); + + for (;;) { + if (*_Ptr1 < *_Ptr2) { + ++_Ptr1; + if (_Ptr1 == _Last1) { + return false; + } + } else if (*_Ptr2 < *_Ptr1) { + return false; + } else { + ++_Ptr1; + ++_Ptr2; + if (_Ptr2 == _Last2) { + return true; + } else if (_Ptr1 == _Last1) { + return false; + } + } + } + } + + template + bool __stdcall _Includes_disp(const void* const _First1, const void* const _Last1, const void* const _First2, + const void* const _Last2) noexcept { + const size_t _Size_bytes_1 = _Byte_length(_First1, _Last1); + const size_t _Size_bytes_2 = _Byte_length(_First2, _Last2); + if (_Size_bytes_2 == 0) { + return true; + } else if (_Size_bytes_1 < _Size_bytes_2) { + return false; + } + +#ifndef _M_ARM64EC + if constexpr (std::_Is_any_of_v<_Ty, int8_t, int16_t, int32_t, int64_t>) { + if (_Size_bytes_1 >= 32 && _Use_avx2()) { + return _Includes_impl<_Traits_avx, _Ty>(_First1, _Last1, _First2, _Last2); + } + + if (_Size_bytes_1 >= 16 && _Use_sse42()) { + return _Includes_impl<_Traits_sse, _Ty>(_First1, _Last1, _First2, _Last2); + } + } +#endif // ^^^ !defined(_M_ARM64EC) ^^^ + return _Includes_impl(_First1, _Last1, _First2, _Last2); + } + } // namespace _Sorted_ranges +} // unnamed namespace + +extern "C" { + +__declspec(noalias) bool __stdcall __std_includes_less_1i( + const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { + return _Sorted_ranges::_Includes_disp<_Sorted_ranges::_Traits_1_avx, _Sorted_ranges::_Traits_1_sse, int8_t>( + _First1, _Last1, _First2, _Last2); +} + +__declspec(noalias) bool __stdcall __std_includes_less_1u( + const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { + return _Sorted_ranges::_Includes_disp<_Sorted_ranges::_Traits_1_avx, _Sorted_ranges::_Traits_1_sse, uint8_t>( + _First1, _Last1, _First2, _Last2); +} + +__declspec(noalias) bool __stdcall __std_includes_less_2i( + const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { + return _Sorted_ranges::_Includes_disp<_Sorted_ranges::_Traits_2_avx, _Sorted_ranges::_Traits_2_sse, int16_t>( + _First1, _Last1, _First2, _Last2); +} + +__declspec(noalias) bool __stdcall __std_includes_less_2u( + const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { + return _Sorted_ranges::_Includes_disp<_Sorted_ranges::_Traits_2_avx, _Sorted_ranges::_Traits_2_sse, uint16_t>( + _First1, _Last1, _First2, _Last2); +} + +__declspec(noalias) bool __stdcall __std_includes_less_4i( + const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { + return _Sorted_ranges::_Includes_disp<_Sorted_ranges::_Traits_4_avx, _Sorted_ranges::_Traits_4_sse, int32_t>( + _First1, _Last1, _First2, _Last2); +} + +__declspec(noalias) bool __stdcall __std_includes_less_4u( + const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { + return _Sorted_ranges::_Includes_disp<_Sorted_ranges::_Traits_4_avx, _Sorted_ranges::_Traits_4_sse, uint32_t>( + _First1, _Last1, _First2, _Last2); +} + +__declspec(noalias) bool __stdcall __std_includes_less_8i( + const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { + return _Sorted_ranges::_Includes_disp<_Sorted_ranges::_Traits_8_avx, _Sorted_ranges::_Traits_8_sse, int64_t>( + _First1, _Last1, _First2, _Last2); +} + +__declspec(noalias) bool __stdcall __std_includes_less_8u( + const void* const _First1, const void* const _Last1, const void* const _First2, const void* const _Last2) noexcept { + return _Sorted_ranges::_Includes_disp<_Sorted_ranges::_Traits_8_avx, _Sorted_ranges::_Traits_8_sse, uint64_t>( + _First1, _Last1, _First2, _Last2); +} + +} // extern "C" + namespace { namespace _Bitset_to_string { #ifdef _M_ARM64EC From 5051dfc7ee8f9400e55dc695b02175c527672ed4 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 15 Jun 2025 14:32:57 +0300 Subject: [PATCH 02/16] ARM fixup --- stl/src/vector_algorithms.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index b0d5ad308ce..c80301d5367 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -6848,7 +6848,14 @@ void* __stdcall __std_unique_copy_8(const void* _First, const void* const _Last, namespace { namespace _Sorted_ranges { #ifdef _M_ARM64EC - static_assert(false, "No vectorization for _M_ARM64EC yet"); + using _Traits_1_avx = void; + using _Traits_2_avx = void; + using _Traits_4_avx = void; + using _Traits_8_avx = void; + using _Traits_1_sse = void; + using _Traits_2_sse = void; + using _Traits_4_sse = void; + using _Traits_8_sse = void; #else // ^^^ defined(_M_ARM64EC) / !defined(_M_ARM64EC) vvv struct _Traits_avx { using _Guard = _Zeroupper_on_exit; From 9b2be190da29faee8e9b8d10df1caa7908f8a50d Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 28 Jun 2025 20:59:19 +0300 Subject: [PATCH 03/16] Unsigned benchmark --- benchmarks/src/includes.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index d349bde1687..4819d8ddad1 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -111,6 +111,16 @@ void common_args(auto bm) { } } +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); + +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); + BENCHMARK(bm_includes)->Apply(common_args); BENCHMARK(bm_includes)->Apply(common_args); BENCHMARK(bm_includes)->Apply(common_args); From 4b707bb6937d511836f5bd2a504f5c5f1b388325 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 28 Jun 2025 20:59:48 +0300 Subject: [PATCH 04/16] Unsgined vectorization --- stl/src/vector_algorithms.cpp | 64 +++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index c80301d5367..4c00538c775 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -6887,6 +6887,9 @@ namespace { static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { return _mm256_cmpgt_epi8(_First, _Second); } + static __m256i _Sign_correction(const __m256i _Data) noexcept { + return _mm256_sub_epi8(_Data, _mm256_set1_epi8(static_cast(0x80))); + } }; struct _Traits_2_avx : _Traits_avx { @@ -6897,6 +6900,10 @@ namespace { static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { return _mm256_cmpgt_epi16(_First, _Second); } + + static __m256i _Sign_correction(const __m256i _Data) noexcept { + return _mm256_sub_epi16(_Data, _mm256_set1_epi16(static_cast(0x8000))); + } }; struct _Traits_4_avx : _Traits_avx { @@ -6907,6 +6914,10 @@ namespace { static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { return _mm256_cmpgt_epi32(_First, _Second); } + + static __m256i _Sign_correction(const __m256i _Data) noexcept { + return _mm256_sub_epi32(_Data, _mm256_set1_epi32(static_cast(0x8000'0000))); + } }; struct _Traits_8_avx : _Traits_avx { @@ -6917,6 +6928,10 @@ namespace { static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { return _mm256_cmpgt_epi64(_First, _Second); } + + static __m256i _Sign_correction(const __m256i _Data) noexcept { + return _mm256_sub_epi64(_Data, _mm256_set1_epi64x(static_cast(0x8000'0000'0000'0000))); + } }; struct _Traits_sse { @@ -6948,6 +6963,10 @@ namespace { static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { return _mm_cmpgt_epi8(_First, _Second); } + + static __m128i _Sign_correction(const __m128i _Data) noexcept { + return _mm_sub_epi8(_Data, _mm_set1_epi8(static_cast(0x80))); + } }; struct _Traits_2_sse : _Traits_sse { @@ -6958,6 +6977,10 @@ namespace { static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { return _mm_cmpgt_epi16(_First, _Second); } + + static __m128i _Sign_correction(const __m128i _Data) noexcept { + return _mm_sub_epi16(_Data, _mm_set1_epi16(static_cast(0x8000))); + } }; struct _Traits_4_sse : _Traits_sse { @@ -6968,6 +6991,10 @@ namespace { static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { return _mm_cmpgt_epi32(_First, _Second); } + + static __m128i _Sign_correction(const __m128i _Data) noexcept { + return _mm_sub_epi32(_Data, _mm_set1_epi32(static_cast(0x8000'0000))); + } }; struct _Traits_8_sse : _Traits_sse { @@ -6978,6 +7005,10 @@ namespace { static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { return _mm_cmpgt_epi64(_First, _Second); } + + static __m128i _Sign_correction(const __m128i _Data) noexcept { + return _mm_sub_epi64(_Data, _mm_set1_epi64x(static_cast(0x8000'0000'0000'0000))); + } }; #endif // ^^^ !defined(_M_ARM64EC) ^^^ @@ -6992,6 +7023,7 @@ namespace { // Only skipping some parts of haystack that are less than current needle element is vectorzied. // Otherwise this is scalar algorithm. + constexpr bool _Is_signed = static_cast<_Ty>(-1) < _Ty{0}; constexpr uint32_t _All_ones_mask = uint32_t{(uint64_t{1} << _Traits::_Vec_size) - 1}; constexpr uint32_t _Highest_one_mask = 1u << (_Traits::_Vec_size - 1); [[maybe_unused]] typename _Traits::_Guard _Guard; // TRANSITION, DevCom-10331414 @@ -7002,9 +7034,16 @@ namespace { _Ty _Val2 = *reinterpret_cast(_First2); auto _Start2 = _Traits::_Broadcast(_Val2); + if constexpr (!_Is_signed) { + _Start2 = _Traits::_Sign_correction(_Start2); + } do { - const auto _Data1 = _Traits::_Load(_First1); + auto _Data1 = _Traits::_Load(_First1); + if constexpr (!_Is_signed) { + _Data1 = _Traits::_Sign_correction(_Data1); + } + const void* _Next1 = _First1; _Advance_bytes(_Next1, _Traits::_Vec_size); @@ -7046,6 +7085,9 @@ namespace { } while (_First1 != _Next1); _Start2 = _Traits::_Broadcast(_Val2); + if constexpr (!_Is_signed) { + _Start2 = _Traits::_Sign_correction(_Start2); + } } } while (_First1 != _Stop1); @@ -7054,8 +7096,12 @@ namespace { if (_Tail_bytes_size_1 != 0) { // Just try to advance pass less one omre time. // Don't neet to repeat the scalar part here -- falling to scalar loop anyway. - const auto _Tail_mask = _Avx2_tail_mask_32(_Tail_bytes_size_1); - const auto _Data1 = _Traits::_Load_mask(_First1, _Tail_mask); + const auto _Tail_mask = _Avx2_tail_mask_32(_Tail_bytes_size_1); + auto _Data1 = _Traits::_Load_mask(_First1, _Tail_mask); + if constexpr (!_Is_signed) { + _Data1 = _Traits::_Sign_correction(_Data1); + } + const auto _Cmp = _Traits::_Cmp_gt(_Start2, _Data1); const uint32_t _Greater_start_2 = _Traits::_Mask(_mm256_and_si256(_Cmp, _Tail_mask)); if (_Greater_start_2 != 0) { @@ -7109,14 +7155,12 @@ namespace { } #ifndef _M_ARM64EC - if constexpr (std::_Is_any_of_v<_Ty, int8_t, int16_t, int32_t, int64_t>) { - if (_Size_bytes_1 >= 32 && _Use_avx2()) { - return _Includes_impl<_Traits_avx, _Ty>(_First1, _Last1, _First2, _Last2); - } + if (_Size_bytes_1 >= 32 && _Use_avx2()) { + return _Includes_impl<_Traits_avx, _Ty>(_First1, _Last1, _First2, _Last2); + } - if (_Size_bytes_1 >= 16 && _Use_sse42()) { - return _Includes_impl<_Traits_sse, _Ty>(_First1, _Last1, _First2, _Last2); - } + if (_Size_bytes_1 >= 16 && _Use_sse42()) { + return _Includes_impl<_Traits_sse, _Ty>(_First1, _Last1, _First2, _Last2); } #endif // ^^^ !defined(_M_ARM64EC) ^^^ return _Includes_impl(_First1, _Last1, _First2, _Last2); From 3567d23cce6d83f4a3ea1713b0cc64e87ff1d06f Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 29 Jun 2025 13:23:47 +0300 Subject: [PATCH 05/16] coverage --- .../VSO_0000000_vector_algorithms/test.cpp | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index ed1ca6a12c1..bf3e1b3cd48 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -649,6 +649,66 @@ void test_is_sorted_until(mt19937_64& gen) { } } +#if _HAS_CXX17 +template +bool last_known_good_includes(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2) { + for (; first2 != last2; ++first1) { + if (first1 == last1 || *first2 < *first1) { + return false; + } + if (!(*first1 < *first2)) { + ++first2; + } + } + return true; +} + +template +void test_case_includes(const vector& hay, const vector& needle) { + const bool expected = last_known_good_includes(hay.begin(), hay.end(), needle.begin(), needle.end()); + const bool actual = includes(hay.begin(), hay.end(), needle.begin(), needle.end()); + assert(expected == actual); +#if _HAS_CXX20 + const bool actual_r = ranges::includes(hay, needle); + assert(expected == actual_r); +#endif // _HAS_CXX20 +} + +template +void test_includes(mt19937_64& gen) { + using Limits = numeric_limits; + + uniform_int_distribution> dis(Limits::min(), Limits::max()); + + vector sorted_random_data; + vector hay; + vector needle; + + sorted_random_data.resize(dataCount); + generate_n(sorted_random_data.data(), dataCount, [&dis, &gen] { return static_cast(dis(gen)); }); + sort(sorted_random_data.begin(), sorted_random_data.end()); + + hay.reserve(dataCount); + needle.reserve(dataCount); + + test_case_includes(hay, needle); + + for (size_t attempts = 0; attempts < dataCount; ++attempts) { + hay.push_back(sorted_random_data[attempts]); + + uniform_int_distribution len_dis(0, hay.size()); + + for (size_t needle_length = 0; needle_length < 4; ++needle_length) { + needle.clear(); + needle.resize(len_dis(gen)); + sample(hay.begin(), hay.end(), needle.data(), needle.size(), gen); + + test_case_includes(hay, needle); + } + } +} +#endif // _HAS_CXX17 + template void last_known_good_replace(FwdIt first, FwdIt last, const T old_val, const T new_val) { for (; first != last; ++first) { @@ -1209,6 +1269,19 @@ void test_vector_algorithms(mt19937_64& gen) { test_is_sorted_until(gen); test_is_sorted_until(gen); + // std::includes has been there forever, but we use std::sample in the test, and that one is C++17 +#if _HAS_CXX17 + test_includes(gen); + test_includes(gen); + test_includes(gen); + test_includes(gen); + test_includes(gen); + test_includes(gen); + test_includes(gen); + test_includes(gen); + test_includes(gen); +#endif // _HAS_CXX17 + // replace() is vectorized for 4 and 8 bytes only. test_replace(gen); test_replace(gen); From 54d8bf26c78f9d372dd37481812678d6d5e13e2a Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 30 Jun 2025 08:10:22 +0300 Subject: [PATCH 06/16] Fix x86 build --- stl/src/vector_algorithms.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 4c00538c775..f390af2ba03 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -6922,7 +6922,11 @@ namespace { struct _Traits_8_avx : _Traits_avx { static __m256i _Broadcast(const uint64_t _Data) noexcept { +#ifdef _WIN64 return _mm256_broadcastq_epi64(_mm_cvtsi64x_si128(_Data)); +#else // ^^^ defined(_WIN64) / !defined(_WIN64), workaround, _mm_cvtsi64x_si128 does not compile vvv + return _mm256_set1_epi64x(_Data); +#endif // ^^^ !defined(_WIN64) ^^^ } static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { @@ -6999,7 +7003,11 @@ namespace { struct _Traits_8_sse : _Traits_sse { static __m128i _Broadcast(const uint64_t _Data) noexcept { +#ifdef _WIN64 return _mm_shuffle_epi32(_mm_cvtsi64x_si128(_Data), _MM_SHUFFLE(1, 0, 1, 0)); +#else // ^^^ defined(_WIN64) / !defined(_WIN64), workaround, _mm_cvtsi64x_si128 does not compile vvv + return _mm_set1_epi64x(_Data); +#endif // ^^^ !defined(_WIN64) ^^^ } static __m128i _Cmp_gt(const __m128i _First, const __m128i _Second) noexcept { From 03ec3e15ca884a0d87e8bdf5e9e852f1fe0dd571 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 30 Jun 2025 12:51:04 +0300 Subject: [PATCH 07/16] missing newlines --- stl/src/vector_algorithms.cpp | 1 + tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index f390af2ba03..f20c3ebeafa 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -6887,6 +6887,7 @@ namespace { static __m256i _Cmp_gt(const __m256i _First, const __m256i _Second) noexcept { return _mm256_cmpgt_epi8(_First, _Second); } + static __m256i _Sign_correction(const __m256i _Data) noexcept { return _mm256_sub_epi8(_Data, _mm256_set1_epi8(static_cast(0x80))); } diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index bf3e1b3cd48..beb594f5da6 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -656,6 +656,7 @@ bool last_known_good_includes(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 las if (first1 == last1 || *first2 < *first1) { return false; } + if (!(*first1 < *first2)) { ++first2; } From c3bc7e392b01b121d6de73ceabadaded0ce88840 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 20 Oct 2025 14:57:26 -0700 Subject: [PATCH 08/16] Improve comments, fix typos. --- stl/inc/algorithm | 2 +- stl/src/vector_algorithms.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 36b9486e990..34c2a361c24 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -440,7 +440,7 @@ template > constexpr bool _Vector_alg_includes_iterators_safe = _Iterators_are_contiguous<_Iter1, _Iter2> // Iterators must be contiguous so we can get raw pointers. && !_Iterator_is_volatile<_Iter1> && !_Iterator_is_volatile<_Iter2> // Iterators must not be volatile. - && is_same_v<_Elem, _Iter_value_t<_Iter2>> // Iterators value is same + && is_same_v<_Elem, _Iter_value_t<_Iter2>> // Iterators have the same value type. && disjunction_v, is_pointer<_Elem>>; // Integral or pointer type. _STD_END #endif // _USE_STD_VECTOR_ALGORITHMS diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index e9059dc5fe6..d15951ff2d8 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -6999,7 +6999,7 @@ namespace { static uint32_t _Bsf(const uint32_t _Val) noexcept { unsigned long _Index; - // CodeQL [SM02313] _Index is always initialized: _Val != 0; see explanastion at call sites. + // CodeQL [SM02313] _Index is always initialized: _Val != 0; see explanation at call sites. _BitScanForward(&_Index, _Val); return _Index; } @@ -7074,7 +7074,7 @@ namespace { static_assert(false, "No vectorization for _M_ARM64EC yet"); #else // ^^^ defined(_M_ARM64EC) / !defined(_M_ARM64EC) vvv - // Only skipping some parts of haystack that are less than current needle element is vectorzied. + // Only skipping some parts of haystack that are less than current needle element is vectorized. // Otherwise this is scalar algorithm. constexpr bool _Is_signed = static_cast<_Ty>(-1) < _Ty{0}; @@ -7102,11 +7102,11 @@ namespace { _Advance_bytes(_Next1, _Traits::_Vec_size); const uint32_t _Greater_start_2 = _Traits::_Mask(_Traits::_Cmp_gt(_Start2, _Data1)); - // Testing _Highest_one_mask can be a bit more effecient on AVX2 than comparing against + // Testing _Highest_one_mask can be a bit more efficient on AVX2 than comparing against // _All_ones_mask (will test sign, and can share comparison with != 0 below). if ((_Greater_start_2 & _Highest_one_mask) != 0) { // Needle first element is greater than each element of haystack vector. - // Proceed to the next one, without updationg the needle comparand. + // Proceed to the next one, without updating the needle comparand. _First1 = _Next1; } else { if (_Greater_start_2 != 0) { @@ -7117,7 +7117,7 @@ namespace { _Advance_bytes(_First1, _Skip); } - // The rest is scalar loop that completes the remaining vector-sized haystack part. + // The rest is scalar loop that completes the remaining vector-sized haystack part. // Except that it updates current needle value to compare against. do { const _Ty _Val1 = *static_cast(_First1); @@ -7148,8 +7148,8 @@ namespace { if constexpr (_Traits::_Tail_mask != 0) { const size_t _Tail_bytes_size_1 = _Size_bytes_1 & _Traits::_Tail_mask; if (_Tail_bytes_size_1 != 0) { - // Just try to advance pass less one omre time. - // Don't neet to repeat the scalar part here -- falling to scalar loop anyway. + // Just try to advance past less one more time. + // Don't need to repeat the scalar part here - falling to scalar loop anyway. const auto _Tail_mask = _Avx2_tail_mask_32(_Tail_bytes_size_1); auto _Data1 = _Traits::_Load_mask(_First1, _Tail_mask); if constexpr (!_Is_signed) { @@ -7161,7 +7161,7 @@ namespace { if (_Greater_start_2 != 0) { // Needle first element is greater than some first elements of haystack part. // Advance past these elements. - // The input is nonzero because tail mask will have zeros for remaining enements. + // The input is nonzero because tail mask will have zeros for remaining elements. const uint32_t _Skip = _Traits::_Bsf(_Greater_start_2 ^ _All_ones_mask); _Advance_bytes(_First1, _Skip); } From 7a74b28edada1e674a2c52905bac5fd212826cc5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 20 Oct 2025 15:12:05 -0700 Subject: [PATCH 09/16] Use `is_constant_evaluated()` in C++20. --- stl/inc/algorithm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 34c2a361c24..5a00b6fd6fa 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10404,7 +10404,7 @@ namespace ranges { if constexpr (_Vector_alg_includes_iterators_safe<_It1, _It2> && _Is_predicate_less<_It1, _Pr> && sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2> && is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>) { - if (!_STD _Is_constant_evaluated()) { + if (!_STD is_constant_evaluated()) { const auto _First1_ptr = _STD to_address(_First1); const auto _First2_ptr = _STD to_address(_First2); const auto _Last1_ptr = _First1_ptr + static_cast(_Last1 - _First1); From b800237319db7753ec8d87b88946ea9cfa0df98b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 20 Oct 2025 15:22:32 -0700 Subject: [PATCH 10/16] Change a weird `for`-loop into a normal `while`-loop. --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 5 ++++- 1 file changed, 4 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 12cf41f8c59..debbb607e4d 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -652,7 +652,7 @@ void test_is_sorted_until(mt19937_64& gen) { #if _HAS_CXX17 template bool last_known_good_includes(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2) { - for (; first2 != last2; ++first1) { + while (first2 != last2) { if (first1 == last1 || *first2 < *first1) { return false; } @@ -660,7 +660,10 @@ bool last_known_good_includes(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 las if (!(*first1 < *first2)) { ++first2; } + + ++first1; } + return true; } From 2f1877ab5dc8e6980935afacdf76086ab524cfe1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 20 Oct 2025 15:28:39 -0700 Subject: [PATCH 11/16] Directly construct `sorted_random_data` with `dataCount` elements. --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index debbb607e4d..6af0b970067 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -684,14 +684,12 @@ void test_includes(mt19937_64& gen) { uniform_int_distribution> dis(Limits::min(), Limits::max()); - vector sorted_random_data; - vector hay; - vector needle; - - sorted_random_data.resize(dataCount); + vector sorted_random_data(dataCount); generate_n(sorted_random_data.data(), dataCount, [&dis, &gen] { return static_cast(dis(gen)); }); sort(sorted_random_data.begin(), sorted_random_data.end()); + vector hay; + vector needle; hay.reserve(dataCount); needle.reserve(dataCount); From a0be6538eb594d5183f9a1b2b2bfff040ffc6e72 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 20 Oct 2025 15:34:40 -0700 Subject: [PATCH 12/16] Prefer `begin()` over `data()`. --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 6af0b970067..c62fa5e2756 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -685,7 +685,7 @@ void test_includes(mt19937_64& gen) { uniform_int_distribution> dis(Limits::min(), Limits::max()); vector sorted_random_data(dataCount); - generate_n(sorted_random_data.data(), dataCount, [&dis, &gen] { return static_cast(dis(gen)); }); + generate(sorted_random_data.begin(), sorted_random_data.end(), [&dis, &gen] { return static_cast(dis(gen)); }); sort(sorted_random_data.begin(), sorted_random_data.end()); vector hay; @@ -703,7 +703,7 @@ void test_includes(mt19937_64& gen) { for (size_t needle_length = 0; needle_length < 4; ++needle_length) { needle.clear(); needle.resize(len_dis(gen)); - sample(hay.begin(), hay.end(), needle.data(), needle.size(), gen); + sample(hay.begin(), hay.end(), needle.begin(), needle.size(), gen); test_case_includes(hay, needle); } From 0f204ea9f6244625ca339564091afd4956c4d24b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 20 Oct 2025 15:40:25 -0700 Subject: [PATCH 13/16] We don't need to `clear()` the needle because `sample()` will overwrite it. --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 1 - 1 file changed, 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 c62fa5e2756..d6aa2427a6d 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -701,7 +701,6 @@ void test_includes(mt19937_64& gen) { uniform_int_distribution len_dis(0, hay.size()); for (size_t needle_length = 0; needle_length < 4; ++needle_length) { - needle.clear(); needle.resize(len_dis(gen)); sample(hay.begin(), hay.end(), needle.begin(), needle.size(), gen); From 546307bbdd7df0ee29188c0bc199402dc4d04ecc Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 20 Oct 2025 16:01:50 -0700 Subject: [PATCH 14/16] Important: Test an additional random element, to exercise negative cases. --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 6 +++++- 1 file changed, 5 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 d6aa2427a6d..5cbc73d076c 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -691,7 +691,7 @@ void test_includes(mt19937_64& gen) { vector hay; vector needle; hay.reserve(dataCount); - needle.reserve(dataCount); + needle.reserve(dataCount + 1); test_case_includes(hay, needle); @@ -703,7 +703,11 @@ void test_includes(mt19937_64& gen) { for (size_t needle_length = 0; needle_length < 4; ++needle_length) { needle.resize(len_dis(gen)); sample(hay.begin(), hay.end(), needle.begin(), needle.size(), gen); + test_case_includes(hay, needle); + // Look for an additional random element, typically (but not always) resulting in a negative test. + needle.push_back(static_cast(dis(gen))); + sort(needle.begin(), needle.end()); test_case_includes(hay, needle); } } From e84f5c189f29d5b759ec037e8c95e77801f5ed06 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Oct 2025 00:54:28 -0700 Subject: [PATCH 15/16] Use `is_void_v`, `is_signed_v`. --- stl/src/vector_algorithms.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index d15951ff2d8..020722fe0bc 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -7069,7 +7069,7 @@ namespace { template bool _Includes_impl( const void* _First1, const void* const _Last1, const void* _First2, const void* const _Last2) noexcept { - if constexpr (!std::is_same_v<_Traits, void>) { + if constexpr (!std::is_void_v<_Traits>) { #ifdef _M_ARM64EC static_assert(false, "No vectorization for _M_ARM64EC yet"); #else // ^^^ defined(_M_ARM64EC) / !defined(_M_ARM64EC) vvv @@ -7077,7 +7077,7 @@ namespace { // Only skipping some parts of haystack that are less than current needle element is vectorized. // Otherwise this is scalar algorithm. - constexpr bool _Is_signed = static_cast<_Ty>(-1) < _Ty{0}; + constexpr bool _Is_signed = std::is_signed_v<_Ty>; constexpr uint32_t _All_ones_mask = uint32_t{(uint64_t{1} << _Traits::_Vec_size) - 1}; constexpr uint32_t _Highest_one_mask = 1u << (_Traits::_Vec_size - 1); [[maybe_unused]] typename _Traits::_Guard _Guard; // TRANSITION, DevCom-10331414 From a21c88752a310954d159be834ca7c59bd87d4e57 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Oct 2025 01:38:31 -0700 Subject: [PATCH 16/16] Upgrade from xtr1common to type_traits (a core header) for is_void_v. --- 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 020722fe0bc..ea38e0e39d7 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #ifndef _M_ARM64EC #include