diff --git a/stl/inc/algorithm b/stl/inc/algorithm index d9422ad4140..586a63f1c70 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3929,16 +3929,19 @@ namespace ranges { _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_STD move(_First)); const auto _ULast = _Get_unwrapped(_STD move(_Last)); - if constexpr (_Fill_memset_is_safe) { -#ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) -#endif // __cpp_lib_is_constant_evaluated - { + if (!_STD is_constant_evaluated()) { + if constexpr (_Fill_memset_is_safe) { const auto _Distance = static_cast(_ULast - _UFirst); _Fill_memset(_UFirst, _Value, _Distance); - _UFirst += _Distance; - _Seek_wrapped(_First, _UFirst); + _Seek_wrapped(_First, _UFirst + _Distance); return _First; + } else if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Value)) { + const auto _Distance = static_cast(_ULast - _UFirst); + _Fill_zero_memset(_UFirst, _Distance); + _Seek_wrapped(_First, _UFirst + _Distance); + return _First; + } } } @@ -3969,15 +3972,17 @@ namespace ranges { constexpr _It operator()(_It _First, iter_difference_t<_It> _Count, const _Ty& _Value) const { if (_Count > 0) { auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); - if constexpr (_Fill_memset_is_safe) { -#ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) -#endif // __cpp_lib_is_constant_evaluated - { + if (!_STD is_constant_evaluated()) { + if constexpr (_Fill_memset_is_safe) { _Fill_memset(_UFirst, _Value, static_cast(_Count)); - _UFirst += _Count; - _Seek_wrapped(_First, _UFirst); // no need to move since _UFirst is a pointer + _Seek_wrapped(_First, _UFirst + _Count); // no need to move since _UFirst is a pointer return _First; + } else if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Value)) { + _Fill_zero_memset(_UFirst, static_cast(_Count)); + _Seek_wrapped(_First, _UFirst + _Count); // no need to move since _UFirst is a pointer + return _First; + } } } diff --git a/stl/inc/memory b/stl/inc/memory index d168f49e698..006c97a1342 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -428,10 +428,17 @@ namespace ranges { if constexpr (_Fill_memset_is_safe<_It, _Ty>) { const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); - const auto _Diff = static_cast(_OFinal - _OFirst); - _Fill_memset(_OFirst, _Val, _Diff); + _Fill_memset(_OFirst, _Val, static_cast(_OFinal - _OFirst)); return _OFinal; } else { + if constexpr (_Fill_zero_memset_is_safe<_It, _Ty>) { + if (_Is_all_bits_zero(_Val)) { + const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); + _Fill_zero_memset(_OFirst, static_cast(_OFinal - _OFirst)); + return _OFinal; + } + } + _Uninitialized_backout _Backout{_STD move(_OFirst)}; while (_Backout._Last != _OLast) { @@ -458,11 +465,19 @@ _NoThrowFwdIt uninitialized_fill_n(_NoThrowFwdIt _First, const _Diff _Count_raw, } auto _UFirst = _Get_unwrapped_n(_First, _Count); - if constexpr (_Fill_memset_is_safe<_Unwrapped_n_t, _Tval>) { + if constexpr (_Fill_memset_is_safe) { _Fill_memset(_UFirst, _Val, static_cast(_Count)); _UFirst += _Count; } else { - _Uninitialized_backout<_Unwrapped_n_t> _Backout{_UFirst}; + if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Val)) { + _Fill_zero_memset(_UFirst, static_cast(_Count)); + _Seek_wrapped(_First, _UFirst + _Count); + return _First; + } + } + + _Uninitialized_backout _Backout{_UFirst}; for (; _Count > 0; --_Count) { _Backout._Emplace_back(_Val); @@ -527,10 +542,18 @@ namespace ranges { } auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); - if constexpr (_Fill_memset_is_safe<_It, _Ty>) { + if constexpr (_Fill_memset_is_safe) { _Fill_memset(_UFirst, _Val, static_cast(_Count)); _Seek_wrapped(_First, _UFirst + _Count); } else { + if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Val)) { + _Fill_zero_memset(_UFirst, static_cast(_Count)); + _Seek_wrapped(_First, _UFirst + _Count); + return _First; + } + } + _Uninitialized_backout _Backout{_STD move(_UFirst)}; for (; _Count > 0; --_Count) { @@ -2386,6 +2409,12 @@ void _Uninitialized_fill_multidimensional_n(_Ty* const _Out, const size_t _Size, } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty>) { _Fill_memset(_Out, _Val, _Size); } else { + if constexpr (_Fill_zero_memset_is_safe<_Ty*, _Ty>) { + if (_Is_all_bits_zero(_Val)) { + _Fill_zero_memset(_Out, _Size); + return; + } + } _Uninitialized_rev_destroying_backout _Backout{_Out}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Backout._Emplace_back(_Val); @@ -2730,6 +2759,13 @@ void _Uninitialized_fill_multidimensional_n_al(_Ty* const _Out, const size_t _Si } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, const _Ty&>::value) { _Fill_memset(_Out, _Val, _Size); } else { + if constexpr (_Fill_zero_memset_is_safe<_Ty*, + _Ty> && _Uses_default_construct<_Alloc, _Ty*, const _Ty&>::value) { + if (_Is_all_bits_zero(_Val)) { + _Fill_zero_memset(_Out, _Size); + return; + } + } _Uninitialized_rev_destroying_backout_al _Backout{_Out, _Al}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Backout._Emplace_back(_Val); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 61482e6f834..eef57cdbbf8 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1737,6 +1737,12 @@ _Alloc_ptr_t<_Alloc> _Uninitialized_fill_n( _Fill_memset(_Unfancy(_First), _Val, static_cast(_Count)); return _First + _Count; } else { + if constexpr (_Fill_zero_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { + if (_Is_all_bits_zero(_Val)) { + _Fill_zero_memset(_Unfancy(_First), static_cast(_Count)); + return _First + _Count; + } + } _Uninitialized_backout_al<_Alloc> _Backout{_First, _Al}; for (; 0 < _Count; --_Count) { _Backout._Emplace_back(_Val); @@ -1787,6 +1793,12 @@ void uninitialized_fill(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, c if constexpr (_Fill_memset_is_safe<_Unwrapped_t, _Tval>) { _Fill_memset(_UFirst, _Val, static_cast(_ULast - _UFirst)); } else { + if constexpr (_Fill_zero_memset_is_safe<_Unwrapped_t, _Tval>) { + if (_Is_all_bits_zero(_Val)) { + _Fill_zero_memset(_UFirst, static_cast(_ULast - _UFirst)); + return; + } + } _Uninitialized_backout<_Unwrapped_t> _Backout{_UFirst}; while (_Backout._Last != _ULast) { _Backout._Emplace_back(_Val); diff --git a/stl/inc/xutility b/stl/inc/xutility index fa244555d08..90d8cd3c8af 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1485,10 +1485,6 @@ _NODISCARD constexpr _Ty* _Get_unwrapped_n(_Ty* const _Src, _Diff) { } #endif // _HAS_IF_CONSTEXPR -template -using _Unwrapped_n_t = - _Remove_cvref_t(), _Iter_diff_t<_Remove_cvref_t<_Iter>>{}))>; - // FUNCTION TEMPLATE _Seek_wrapped template _INLINE_VAR constexpr bool _Wrapped_seekable_v = false; @@ -4813,12 +4809,33 @@ _INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, template _INLINE_VAR constexpr bool _Fill_memset_is_safe<_FwdIt, _Ty, false> = false; +template > +_INLINE_VAR constexpr bool _Fill_zero_memset_is_safe = + conjunction_v, is_scalar<_Iter_value_t<_FwdIt>>, negation>>, + negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; + +template +_INLINE_VAR constexpr bool _Fill_zero_memset_is_safe<_FwdIt, _Ty, false> = false; + template void _Fill_memset(_DestTy* const _Dest, const _Ty _Val, const size_t _Count) { _DestTy _Dest_val = _Val; // implicitly convert (a cast would suppress warnings); also handles _DestTy being bool _CSTD memset(_Dest, static_cast(_Dest_val), _Count); } +template +void _Fill_zero_memset(_DestTy* const _Dest, const size_t _Count) { + _CSTD memset(_Dest, 0, _Count * sizeof(_DestTy)); +} + +template +_NODISCARD bool _Is_all_bits_zero(const _Ty& _Val) { + // checks if scalar type has all bits set to zero + _STL_INTERNAL_STATIC_ASSERT(is_scalar_v<_Ty> && !is_member_pointer_v<_Ty>); + constexpr _Ty _Zero{}; + return _CSTD memcmp(&_Val, &_Zero, sizeof(_Ty)) == 0; +} + #if _HAS_IF_CONSTEXPR template _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) { @@ -4829,13 +4846,18 @@ _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _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 - { + { + if constexpr (_Fill_memset_is_safe) { _Fill_memset(_UFirst, _Val, static_cast(_ULast - _UFirst)); return; + } else if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Val)) { + _Fill_zero_memset(_UFirst, static_cast(_ULast - _UFirst)); + return; + } } } @@ -4890,15 +4912,20 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _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 - { + { + if constexpr (_Fill_memset_is_safe) { _Fill_memset(_UDest, _Val, static_cast(_Count)); - _UDest += _Count; - _Seek_wrapped(_Dest, _UDest); + _Seek_wrapped(_Dest, _UDest + _Count); return _Dest; + } else if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Val)) { + _Fill_zero_memset(_UDest, static_cast(_Count)); + _Seek_wrapped(_Dest, _UDest + _Count); + return _Dest; + } } } diff --git a/tests/std/tests/P0674R1_make_shared_for_arrays/test.cpp b/tests/std/tests/P0674R1_make_shared_for_arrays/test.cpp index 3a3304571c7..0a9082fe926 100644 --- a/tests/std/tests/P0674R1_make_shared_for_arrays/test.cpp +++ b/tests/std/tests/P0674R1_make_shared_for_arrays/test.cpp @@ -221,6 +221,11 @@ void test_make_shared_array_known_bounds() { test_make_init_destruct_order(); // success multidimensional test_make_init_destruct_order(); // failure multidimensional + + shared_ptr p7 = make_shared(0); + for (int i = 0; i < 7; ++i) { + assert(p7[i] == 0); + } } void test_make_shared_array_unknown_bounds() { @@ -286,6 +291,11 @@ void test_make_shared_array_unknown_bounds() { test_make_init_destruct_order(2u); // success multidimensional test_make_init_destruct_order(3u); // failure multidimensional + + shared_ptr p8 = make_shared(7u, 0); + for (int i = 0; i < 7; ++i) { + assert(p8[i] == 0); + } } int constructCount = 0; @@ -506,6 +516,12 @@ void test_allocate_shared_array_known_bounds() { test_allocate_init_destruct_order(); // success multidimensional test_allocate_init_destruct_order(); // failure multidimensional + + allocator a7; + shared_ptr p7 = allocate_shared(a7, 0); + for (int i = 0; i < 7; ++i) { + assert(p7[i] == 0); + } } void test_allocate_shared_array_unknown_bounds() { @@ -599,6 +615,12 @@ void test_allocate_shared_array_unknown_bounds() { test_allocate_init_destruct_order(2u); // success multidimensional test_allocate_init_destruct_order(3u); // failure multidimensional + + allocator a8; + shared_ptr p8 = allocate_shared(a8, 7u, 0); + for (int i = 0; i < 7; ++i) { + assert(p8[i] == 0); + } } int main() { diff --git a/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp b/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp index 0bea140c685..ca8ebd049fb 100644 --- a/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_fill/test.cpp @@ -33,11 +33,19 @@ struct instantiator { } { // Validate int is properly converted to bool bool output[] = {false, true, false}; - fill(ranges::begin(output), ranges::end(output), 5); + fill(output, 5); for (const bool& elem : output) { assert(elem == true); } } + { // Validate zero-ing + int output[] = {13, 42, 1367}; + auto result = fill(output, 0); + for (const auto& elem : output) { + assert(elem == 0); + } + assert(result == ranges::end(output)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp index 622794b11c3..4799a43f149 100644 --- a/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_fill_n/test.cpp @@ -43,6 +43,14 @@ struct instantiator { assert(elem == true); } } + { // Validate zero-ing + int output[] = {13, 42, 1367}; + auto result = fill_n(ranges::begin(output), ranges::distance(output), 0); + for (const auto& elem : output) { + assert(elem == 0); + } + assert(result == ranges::end(output)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp index 1defdd193d2..2c7801a5348 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp @@ -100,6 +100,23 @@ struct instantiator { assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); } + + { // Validate int is properly converted to bool + bool output[] = {false, true, false}; + uninitialized_fill(output, 5); + for (const bool& elem : output) { + assert(elem == true); + } + } + + { // Validate zero-ing + int output[] = {13, 42, 1367}; + auto result = uninitialized_fill(output, 0); + for (const auto& elem : output) { + assert(elem == 0); + } + assert(result == ranges::end(output)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp index 97da0b88fd1..4d0be9f186c 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp @@ -66,18 +66,37 @@ struct instantiator { static void call() { using ranges::uninitialized_fill_n, ranges::equal, ranges::equal_to, ranges::iterator_t; - holder mem; - Write wrapped_input{mem.as_span()}; + { + holder mem; + Write wrapped_input{mem.as_span()}; + + int_wrapper::clear_counts(); + const same_as> auto result = uninitialized_fill_n(wrapped_input.begin(), 3, 42); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 0); + assert(result == wrapped_input.end()); + assert(equal(wrapped_input, expected, equal_to{}, &int_wrapper::val)); + not_ranges_destroy(wrapped_input); + assert(int_wrapper::constructions == 3); + assert(int_wrapper::destructions == 3); + } - int_wrapper::clear_counts(); - const same_as> auto result = uninitialized_fill_n(wrapped_input.begin(), 3, 42); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 0); - assert(result == wrapped_input.end()); - assert(equal(wrapped_input, expected, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_input); - assert(int_wrapper::constructions == 3); - assert(int_wrapper::destructions == 3); + { // Validate int is properly converted to bool + bool output[] = {false, true, false}; + uninitialized_fill_n(ranges::begin(output), ranges::distance(output), 5); + for (const bool& elem : output) { + assert(elem == true); + } + } + + { // Validate zero-ing + int output[] = {13, 42, 1367}; + auto result = uninitialized_fill_n(ranges::begin(output), ranges::distance(output), 0); + for (const auto& elem : output) { + assert(elem == 0); + } + assert(result == ranges::end(output)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 3ad80d1b8fa..9429d072929 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -1841,7 +1841,6 @@ namespace unwrap_move_only { STATIC_ASSERT(!std::_Unwrappable_for_offset_v const&>); STATIC_ASSERT(std::_Unwrappable_for_offset_v>); STATIC_ASSERT(!std::_Unwrappable_for_offset_v const>); - STATIC_ASSERT(std::same_as>, iter>); STATIC_ASSERT(!std::_Wrapped_seekable_v, iter&>); STATIC_ASSERT(!std::_Wrapped_seekable_v, iter const&>); diff --git a/tests/std/tests/VSO_0180469_fill_family/test.cpp b/tests/std/tests/VSO_0180469_fill_family/test.cpp index 2ec62a9404f..c932d75f092 100644 --- a/tests/std/tests/VSO_0180469_fill_family/test.cpp +++ b/tests/std/tests/VSO_0180469_fill_family/test.cpp @@ -10,10 +10,12 @@ // calls std::fill and std::uninitialized_fill with (signed*, signed*, unsigned) #include #include +#include #include #include #include #include +#include using namespace std; @@ -168,4 +170,22 @@ int main() { assert(elem == true); } } + + // Test floating-point negative zero + { + float output[] = {1.0f, 2.0f, 3.0f}; + fill(output, output + 3, -0.0f); + for (const float& elem : output) { + assert(elem == 0.0f); // elem is positive or negative zero + assert(signbit(elem)); // elem is negative + } + } + + // Test (indirectly) _Uninitialized_fill_n with zero + { + vector vec(43, nullptr); + for (const auto& p : vec) { + assert(p == nullptr); + } + } }