diff --git a/stl/inc/vector b/stl/inc/vector index 17902275c58..5a18de75a39 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -2882,6 +2882,65 @@ namespace pmr { using vector = _STD vector<_Ty, polymorphic_allocator<_Ty>>; } // namespace pmr #endif // _HAS_CXX17 + +#if _HAS_IF_CONSTEXPR +// VARIABLE TEMPLATE _Is_vb_iterator +template +_INLINE_VAR constexpr bool _Is_vb_iterator<_Vb_iterator<_Alloc>, _RequiresMutable> = true; + +template +_INLINE_VAR constexpr bool _Is_vb_iterator<_Vb_const_iterator<_Alloc>, false> = true; + +template +_CONSTEXPR20 void _Fill_vbool(_FwdIt _First, _FwdIt _Last, const _Ty& _Val) { + // Set [_First, _Last) to _Val + if (_First == _Last) { + return; + } + + _Vbase* _VbFirst = const_cast<_Vbase*>(_First._Myptr); + _Vbase* const _VbLast = const_cast<_Vbase*>(_Last._Myptr); + + const auto _FirstSourceMask = static_cast<_Vbase>(-1) << _First._Myoff; + const auto _FirstDestMask = ~_FirstSourceMask; + const auto _FillVal = static_cast<_Vbase>(_Val ? -1 : 0); + + if (_VbFirst == _VbLast) { + // We already excluded _First == _Last, so here _Last._Myoff > 0 and the shift is safe + const auto _LastSourceMask = static_cast<_Vbase>(-1) >> (_VBITS - _Last._Myoff); + const auto _LastDestMask = ~_LastSourceMask; + const auto _SourceMask = _FirstSourceMask & _LastSourceMask; + const auto _DestMask = _FirstDestMask | _LastDestMask; + *_VbFirst = (*_VbFirst & _DestMask) | (_FillVal & _SourceMask); + return; + } + + *_VbFirst = (*_VbFirst & _FirstDestMask) | (_FillVal & _FirstSourceMask); + ++_VbFirst; + +#ifdef __cpp_lib_is_constant_evaluated + if (_STD is_constant_evaluated()) { + for (; _VbFirst != _VbLast; ++_VbFirst) { + *_VbFirst = _FillVal; + } + } else +#endif // __cpp_lib_is_constant_evaluated + { + const auto _VbFirst_ch = reinterpret_cast(_VbFirst); + const auto _VbLast_ch = reinterpret_cast(_VbLast); + const auto _Count_ch = static_cast(_VbLast_ch - _VbFirst_ch); + const auto _ValChar = static_cast(_Val ? -1 : 0); + _CSTD memset(_VbFirst, _ValChar, _Count_ch); + _VbFirst = _VbLast; + } + + if (_Last._Myoff != 0) { + const auto _LastSourceMask = static_cast<_Vbase>(-1) >> (_VBITS - _Last._Myoff); + const auto _LastDestMask = ~_LastSourceMask; + *_VbFirst = (*_VbFirst & _LastDestMask) | (_FillVal & _LastSourceMask); + } +} +#endif // _HAS_IF_CONSTEXPR _STD_END #pragma pop_macro("new") diff --git a/stl/inc/xutility b/stl/inc/xutility index b22d75d57ef..0bebdd19d2b 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4302,6 +4302,10 @@ _OutIt _Copy_memmove(move_iterator<_InIt> _First, move_iterator<_InIt> _Last, _O } #if _HAS_IF_CONSTEXPR +// VARIABLE TEMPLATE _Is_vb_iterator +template +_INLINE_VAR constexpr bool _Is_vb_iterator = false; + template _CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...) @@ -4717,20 +4721,24 @@ template _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) { // copy _Val through [_First, _Last) _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_First); - const auto _ULast = _Get_unwrapped(_Last); - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Is_vb_iterator<_FwdIt, true>) { + _Fill_vbool(_First, _Last, _Val); + } else { + auto _UFirst = _Get_unwrapped(_First); + const auto _ULast = _Get_unwrapped(_Last); + if constexpr (_Fill_memset_is_safe) { #ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) + if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated - { - _CSTD memset(_UFirst, static_cast(_Val), static_cast(_ULast - _UFirst)); - return; + { + _CSTD memset(_UFirst, static_cast(_Val), static_cast(_ULast - _UFirst)); + return; + } } - } - for (; _UFirst != _ULast; ++_UFirst) { - *_UFirst = _Val; + for (; _UFirst != _ULast; ++_UFirst) { + *_UFirst = _Val; + } } } #else // ^^^ _HAS_IF_CONSTEXPR // !_HAS_IF_CONSTEXPR vvv @@ -4773,26 +4781,31 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _Val // copy _Val _Count times through [_Dest, ...) _Algorithm_int_t<_Diff> _Count = _Count_raw; if (0 < _Count) { - auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Is_vb_iterator<_OutIt, true>) { + const auto _Last = _Dest + static_cast(_Count); + _Fill_vbool(_Dest, _Last, _Val); + return _Last; + } else { + auto _UDest = _Get_unwrapped_n(_Dest, _Count); + if constexpr (_Fill_memset_is_safe) { #ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) + if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated - { - _CSTD memset(_UDest, static_cast(_Val), static_cast(_Count)); - _UDest += _Count; - _Seek_wrapped(_Dest, _UDest); - return _Dest; + { + _CSTD memset(_UDest, static_cast(_Val), static_cast(_Count)); + _UDest += _Count; + _Seek_wrapped(_Dest, _UDest); + return _Dest; + } } - } - for (; 0 < _Count; --_Count, (void) ++_UDest) { - *_UDest = _Val; - } + for (; 0 < _Count; --_Count, (void) ++_UDest) { + *_UDest = _Val; + } - _Seek_wrapped(_Dest, _UDest); + _Seek_wrapped(_Dest, _UDest); + } } - return _Dest; } diff --git a/tests/std/test.lst b/tests/std/test.lst index 04b21077e19..d639d5e2c81 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -157,6 +157,7 @@ tests\Dev11_1158803_regex_thread_safety tests\Dev11_1180290_filesystem_error_code tests\GH_000457_system_error_message tests\GH_000545_include_compare +tests\GH_000625_vector_bool_optimization tests\GH_000685_condition_variable_any tests\GH_000690_overaligned_function tests\GH_000890_pow_template diff --git a/tests/std/tests/GH_000625_vector_bool_optimization/env.lst b/tests/std/tests/GH_000625_vector_bool_optimization/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000625_vector_bool_optimization/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp new file mode 100644 index 00000000000..3f26088c87b --- /dev/null +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; + +constexpr int blockSize = 32; +static_assert(blockSize == _VBITS, "Invalid block size"); + +void test_fill_helper(const size_t length) { + // No offset + { + vector result_true(length, true); + result_true.resize(length + 3, false); + vector dest_true(length + 3, false); + fill(dest_true.begin(), prev(dest_true.end(), 3), true); + assert(dest_true == result_true); + + vector result_false(length, false); + result_false.resize(length + 3, true); + vector dest_false(length + 3, true); + fill(dest_false.begin(), prev(dest_false.end(), 3), false); + assert(dest_false == result_false); + + vector result_true_n(length, true); + result_true_n.resize(length + 3, false); + vector dest_true_n(length + 3, false); + const auto res_fill_n = fill_n(dest_true_n.begin(), length, true); + assert(dest_true_n == result_true_n); + assert(res_fill_n == prev(dest_true_n.end(), 3)); + + vector result_false_n(length, false); + result_false_n.resize(length + 3, true); + vector dest_false_n(length + 3, true); + fill_n(dest_false_n.begin(), length, false); + assert(dest_false_n == result_false_n); + } + + // With offset + { + vector result_true(length, true); + result_true.resize(length + 3, false); + result_true.insert(result_true.begin(), false); + vector dest_true(length + 4, false); + fill(next(dest_true.begin()), prev(dest_true.end(), 3), true); + assert(dest_true == result_true); + + vector result_false(length, false); + result_false.resize(length + 3, true); + result_false.insert(result_false.begin(), true); + vector dest_false(length + 4, true); + fill(next(dest_false.begin()), prev(dest_false.end(), 3), false); + assert(dest_false == result_false); + + vector result_true_n(length, true); + result_true_n.resize(length + 3, false); + result_true_n.insert(result_true_n.begin(), false); + vector dest_true_n(length + 4, false); + const auto res_fill_n = fill_n(next(dest_true_n.begin()), length, true); + assert(dest_true_n == result_true_n); + assert(res_fill_n == prev(dest_true_n.end(), 3)); + + vector result_false_n(length, false); + result_false_n.resize(length + 3, true); + result_false_n.insert(result_false_n.begin(), true); + vector dest_false_n(length + 4, true); + fill_n(next(dest_false_n.begin()), length, false); + assert(dest_false_n == result_false_n); + } +} + +bool test_fill() { + // Empty + test_fill_helper(0); + + // One block, ends within block + test_fill_helper(15); + + // One block, ends at block boundary + test_fill_helper(blockSize); + + // Multiple blocks, no memset, within block + test_fill_helper(blockSize + 11); + + // Multiple blocks, no memset, ends at block boundary + test_fill_helper(2 * blockSize); + + // Multiple blocks, with memset, within block + test_fill_helper(3 * blockSize + 5); + + // Multiple blocks, with memset, ends at block boundary + test_fill_helper(4 * blockSize); + return true; +} + +int main() { + test_fill(); +}