diff options
Diffstat (limited to 'libcxx')
-rw-r--r-- | libcxx/docs/ReleaseNotes/21.rst | 3 | ||||
-rw-r--r-- | libcxx/docs/ReleaseNotes/22.rst | 4 | ||||
-rw-r--r-- | libcxx/docs/Status/Cxx2cIssues.csv | 2 | ||||
-rw-r--r-- | libcxx/docs/Status/Cxx2cPapers.csv | 2 | ||||
-rw-r--r-- | libcxx/include/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libcxx/include/__algorithm/generate.h | 6 | ||||
-rw-r--r-- | libcxx/include/__hash_table | 100 | ||||
-rw-r--r-- | libcxx/include/__tuple/tuple_like_ext.h | 41 | ||||
-rw-r--r-- | libcxx/include/module.modulemap.in | 1 | ||||
-rw-r--r-- | libcxx/include/tuple | 21 | ||||
-rw-r--r-- | libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate.pass.cpp | 11 | ||||
-rw-r--r-- | libcxx/test/std/atomics/atomics.types.generic/cas_non_power_of_2.pass.cpp | 76 | ||||
-rw-r--r-- | libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/erase_range.pass.cpp | 31 | ||||
-rw-r--r-- | libcxx/test/std/containers/unord/unord.multiset/erase_range.pass.cpp | 17 | ||||
-rw-r--r-- | libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp | 13 | ||||
-rwxr-xr-x | libcxx/utils/compare-benchmarks | 15 |
16 files changed, 245 insertions, 99 deletions
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index 642bf7d..2b1aa28 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -53,8 +53,7 @@ Implemented Papers - P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github <https://github.com/llvm/llvm-project/issues/105252>`__) - P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__) - P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github <https://github.com/llvm/llvm-project/issues/105260>`__) -- P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://github.com/llvm/llvm-project/issues/105424>`__) -- P3379R0: Constrain ``std::expected equality`` operators (`Github <https://github.com/llvm/llvm-project/issues/118135>`__) +- P3379R0: Constrain ``std::expected`` equality operators (`Github <https://github.com/llvm/llvm-project/issues/118135>`__) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 0fd7e53..e95cbc0 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -44,6 +44,7 @@ Implemented Papers - P3223R2: Making ``std::istream::ignore`` less surprising (`Github <https://llvm.org/PR148178>`__) - P3060R3: Add ``std::views::indices(n)`` (`Github <https://llvm.org/PR148175>`__) - P2835R7: Expose ``std::atomic_ref``'s object address (`Github <https://llvm.org/PR118377>`__) +- P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://llvm.org/PR105424>`__) - P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__) Improvements and New Features @@ -61,6 +62,7 @@ Improvements and New Features has been improved by up to 3x - The performance of ``insert(iterator, iterator)`` of ``map``, ``set``, ``multimap`` and ``multiset`` has been improved by up to 2.5x +- The performance of ``erase(iterator, iterator)`` in the unordered containers has been improved by up to 1.9x - The performance of ``map::insert_or_assign`` has been improved by up to 2x - ``ofstream::write`` has been optimized to pass through large strings to system calls directly instead of copying them in chunks into a buffer. @@ -74,6 +76,8 @@ Improvements and New Features - The ``std::{fill, fill_n}`` and ``std::ranges::{fill, fill_n}`` algorithms have been optimized for segmented iterators, resulting in a performance improvement of at least 10x for ``std::deque<int>`` iterators and ``std::join_view<std::vector<std::vector<int>>>`` iterators. +- The ``std::generate`` algorithm has been optimized for segmented iterators, resulting in a performance improvement for + ``std::deque<short>`` and ``std::join_view<vector<vector<short>>>`` iterators. Deprecations and Removals ------------------------- diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index 7bf7bc9..237217a 100644 --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -149,5 +149,5 @@ "`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Adopted Yet","|Complete|","16","`#105356 <https://github.com/llvm/llvm-project/issues/105356>`__","" "`LWG4139 <https://wg21.link/LWG4139>`__","ยง[time.zone.leap] recursive constraint in ``<=>``","Not Adopted Yet","|Complete|","20","`#118369 <https://github.com/llvm/llvm-project/issues/118369>`__","" "`LWG3456 <https://wg21.link/LWG3456>`__","Pattern used by ``std::from_chars`` is underspecified (option B)","Not Adopted Yet","|Complete|","20","`#118370 <https://github.com/llvm/llvm-project/issues/118370>`__","" -"`LWG3882 <https://wg21.link/LWG3882>`__","``tuple`` relational operators have confused friendships","Not Adopted Yet","|Complete|","21","The comparsion operators are constrained harder than the proposed resolution. libstdc++ and MSVC STL do the same.","" +"`LWG3882 <https://wg21.link/LWG3882>`__","``tuple`` relational operators have confused friendships","Not Adopted Yet","|Complete|","22","The comparsion operators are constrained harder than the proposed resolution. libstdc++ and MSVC STL do the same.","" "","","","","","" diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 9b83047..0eedc82 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -59,7 +59,7 @@ "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","`#105421 <https://github.com/llvm/llvm-project/issues/105421>`__","" "`P2810R4 <https://wg21.link/P2810R4>`__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","","`#105422 <https://github.com/llvm/llvm-project/issues/105422>`__","" "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__","" -"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","21","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented." +"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented." "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__","" "`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","" "","","","","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index ddace8b..dd1e713 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -782,7 +782,6 @@ set(files __tuple/sfinae_helpers.h __tuple/tuple_element.h __tuple/tuple_like.h - __tuple/tuple_like_ext.h __tuple/tuple_like_no_subrange.h __tuple/tuple_size.h __tuple/tuple_types.h diff --git a/libcxx/include/__algorithm/generate.h b/libcxx/include/__algorithm/generate.h index c95b527..c4cd75c 100644 --- a/libcxx/include/__algorithm/generate.h +++ b/libcxx/include/__algorithm/generate.h @@ -9,7 +9,9 @@ #ifndef _LIBCPP___ALGORITHM_GENERATE_H #define _LIBCPP___ALGORITHM_GENERATE_H +#include <__algorithm/for_each.h> #include <__config> +#include <__utility/forward.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -20,8 +22,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD template <class _ForwardIterator, class _Generator> inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void generate(_ForwardIterator __first, _ForwardIterator __last, _Generator __gen) { - for (; __first != __last; ++__first) - *__first = __gen(); + using __iter_ref = decltype(*__first); + std::for_each(__first, __last, [&](__iter_ref __element) { std::forward<__iter_ref>(__element) = __gen(); }); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table index 6b65e73..5432abb 100644 --- a/libcxx/include/__hash_table +++ b/libcxx/include/__hash_table @@ -1037,7 +1037,21 @@ private: } _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table&, false_type) _NOEXCEPT {} - _LIBCPP_HIDE_FROM_ABI void __deallocate_node(__next_pointer __np) _NOEXCEPT; + _LIBCPP_HIDE_FROM_ABI void __deallocate_node(__node_pointer __nd) _NOEXCEPT { + auto& __alloc = __node_alloc(); + __node_traits::destroy(__alloc, std::addressof(__nd->__get_value())); + std::__destroy_at(std::__to_address(__nd)); + __node_traits::deallocate(__alloc, __nd, 1); + } + + _LIBCPP_HIDE_FROM_ABI void __deallocate_node_list(__next_pointer __np) _NOEXCEPT { + while (__np != nullptr) { + __next_pointer __next = __np->__next_; + __deallocate_node(__np->__upcast()); + __np = __next; + } + } + _LIBCPP_HIDE_FROM_ABI __next_pointer __detach() _NOEXCEPT; template <class _From, class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0> @@ -1175,7 +1189,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::~__hash_table() { static_assert(is_copy_constructible<hasher>::value, "Hasher must be copy-constructible."); #endif - __deallocate_node(__first_node_.__next_); + __deallocate_node_list(__first_node_.__next_); } template <class _Tp, class _Hash, class _Equal, class _Alloc> @@ -1251,7 +1265,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(const __hash_table& __other) // At this point we either have consumed the whole incoming hash table, or we don't have any more nodes to reuse in // the destination. Either continue with constructing new nodes, or deallocate the left over nodes. if (__own_iter->__next_) { - __deallocate_node(__own_iter->__next_); + __deallocate_node_list(__own_iter->__next_); __own_iter->__next_ = nullptr; } else { __copy_construct(__other_iter, __own_iter, __current_chash); @@ -1263,19 +1277,6 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(const __hash_table& __other) } template <class _Tp, class _Hash, class _Equal, class _Alloc> -void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__deallocate_node(__next_pointer __np) _NOEXCEPT { - __node_allocator& __na = __node_alloc(); - while (__np != nullptr) { - __next_pointer __next = __np->__next_; - __node_pointer __real_np = __np->__upcast(); - __node_traits::destroy(__na, std::addressof(__real_np->__get_value())); - std::__destroy_at(std::addressof(*__real_np)); - __node_traits::deallocate(__na, __real_np, 1); - __np = __next; - } -} - -template <class _Tp, class _Hash, class _Equal, class _Alloc> typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::__next_pointer __hash_table<_Tp, _Hash, _Equal, _Alloc>::__detach() _NOEXCEPT { size_type __bc = bucket_count(); @@ -1318,7 +1319,7 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u, max_load_factor() = __u.max_load_factor(); if (bucket_count() != 0) { __next_pointer __cache = __detach(); - auto __guard = std::__make_scope_guard([&] { __deallocate_node(__cache); }); + auto __guard = std::__make_scope_guard([&] { __deallocate_node_list(__cache); }); const_iterator __i = __u.begin(); while (__cache != nullptr && __u.size() != 0) { __assign_value(__cache->__upcast()->__get_value(), std::move(__u.remove(__i++)->__get_value())); @@ -1353,7 +1354,7 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__assign_unique(_InputIterator __ if (bucket_count() != 0) { __next_pointer __cache = __detach(); - auto __guard = std::__make_scope_guard([&] { __deallocate_node(__cache); }); + auto __guard = std::__make_scope_guard([&] { __deallocate_node_list(__cache); }); for (; __cache != nullptr && __first != __last; ++__first) { __assign_value(__cache->__upcast()->__get_value(), *__first); __next_pointer __next = __cache->__next_; @@ -1374,7 +1375,7 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__assign_multi(_InputIterator __f "__assign_multi may only be called with the containers value type or the nodes value type"); if (bucket_count() != 0) { __next_pointer __cache = __detach(); - auto __guard = std::__make_scope_guard([&] { __deallocate_node(__cache); }); + auto __guard = std::__make_scope_guard([&] { __deallocate_node_list(__cache); }); for (; __cache != nullptr && __first != __last; ++__first) { __assign_value(__cache->__upcast()->__get_value(), *__first); __next_pointer __next = __cache->__next_; @@ -1413,7 +1414,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::end() const _NOEXCEPT { template <class _Tp, class _Hash, class _Equal, class _Alloc> void __hash_table<_Tp, _Hash, _Equal, _Alloc>::clear() _NOEXCEPT { if (size() > 0) { - __deallocate_node(__first_node_.__next_); + __deallocate_node_list(__first_node_.__next_); __first_node_.__next_ = nullptr; size_type __bc = bucket_count(); for (size_type __i = 0; __i < __bc; ++__i) @@ -1873,12 +1874,61 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::erase(const_iterator __p) { template <class _Tp, class _Hash, class _Equal, class _Alloc> typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::iterator __hash_table<_Tp, _Hash, _Equal, _Alloc>::erase(const_iterator __first, const_iterator __last) { - for (const_iterator __p = __first; __first != __last; __p = __first) { - ++__first; - erase(__p); + if (__first == __last) + return iterator(__last.__node_); + + // current node + __next_pointer __current = __first.__node_; + size_type __bucket_count = bucket_count(); + size_t __chash = std::__constrain_hash(__current->__hash(), __bucket_count); + // find previous node + __next_pointer __before_first = __bucket_list_[__chash]; + for (; __before_first->__next_ != __current; __before_first = __before_first->__next_) + ; + + __next_pointer __last_node = __last.__node_; + + // If __before_first is in the same bucket (i.e. the first element we erase is not the first in the bucket), clear + // this bucket first without re-linking it + if (__before_first != __first_node_.__ptr() && + std::__constrain_hash(__before_first->__hash(), __bucket_count) == __chash) { + while (__current != __last_node) { + auto __next = __current->__next_; + __deallocate_node(__current->__upcast()); + __current = __next; + --__size_; + + if (__next) { + if (auto __next_chash = std::__constrain_hash(__next->__hash(), __bucket_count); __next_chash != __chash) { + __bucket_list_[__next_chash] = __before_first; + __chash = __next_chash; + break; + } + } + } + } + + while (__current != __last_node) { + auto __next = __current->__next_; + __deallocate_node(__current->__upcast()); + __current = __next; + --__size_; + + // When switching buckets, set the old bucket to be empty and update the next bucket to have __before_first as its + // before-first element + if (__next) { + if (auto __next_chash = std::__constrain_hash(__next->__hash(), __bucket_count); __next_chash != __chash) { + __bucket_list_[__chash] = nullptr; + __bucket_list_[__next_chash] = __before_first; + __chash = __next_chash; + } + } } - __next_pointer __np = __last.__node_; - return iterator(__np); + + // re-link __before_first with __last + __before_first->__next_ = __current; + + return iterator(__last.__node_); } template <class _Tp, class _Hash, class _Equal, class _Alloc> diff --git a/libcxx/include/__tuple/tuple_like_ext.h b/libcxx/include/__tuple/tuple_like_ext.h deleted file mode 100644 index 5a6748a..0000000 --- a/libcxx/include/__tuple/tuple_like_ext.h +++ /dev/null @@ -1,41 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H -#define _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H - -#include <__config> -#include <__cstddef/size_t.h> -#include <__fwd/array.h> -#include <__fwd/pair.h> -#include <__fwd/tuple.h> -#include <__type_traits/integral_constant.h> - -#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -# pragma GCC system_header -#endif - -_LIBCPP_BEGIN_NAMESPACE_STD - -template <class _Tp> -struct __tuple_like_ext : false_type {}; - -#ifndef _LIBCPP_CXX03_LANG -template <class... _Tp> -struct __tuple_like_ext<tuple<_Tp...> > : true_type {}; -#endif - -template <class _T1, class _T2> -struct __tuple_like_ext<pair<_T1, _T2> > : true_type {}; - -template <class _Tp, size_t _Size> -struct __tuple_like_ext<array<_Tp, _Size> > : true_type {}; - -_LIBCPP_END_NAMESPACE_STD - -#endif // _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 894093b..a86d6c6 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -2115,7 +2115,6 @@ module std [system] { module ignore { header "__tuple/ignore.h" } module sfinae_helpers { header "__tuple/sfinae_helpers.h" } module tuple_element { header "__tuple/tuple_element.h" } - module tuple_like_ext { header "__tuple/tuple_like_ext.h" } module tuple_like_no_subrange { header "__tuple/tuple_like_no_subrange.h" } module tuple_like { header "__tuple/tuple_like.h" } module tuple_size { header "__tuple/tuple_size.h" } diff --git a/libcxx/include/tuple b/libcxx/include/tuple index b0d0c38..5f3bb72 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -234,7 +234,6 @@ template <class... Types> # include <__tuple/ignore.h> # include <__tuple/tuple_element.h> # include <__tuple/tuple_like.h> -# include <__tuple/tuple_like_ext.h> # include <__tuple/tuple_size.h> # include <__tuple/tuple_types.h> # include <__type_traits/common_reference.h> @@ -1273,15 +1272,19 @@ operator<=(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) { template <class... _Tuples> struct __tuple_cat_return_impl; -template <class _Tuple> -struct __tuple_cat_return_impl<_Tuple> { - using type _LIBCPP_NODEBUG = _Tuple; +template <class... _Types> +struct __tuple_cat_return_impl<tuple<_Types...>> { + using type _LIBCPP_NODEBUG = tuple<_Types...>; }; -template <class... _Types0, template <class...> class _Tuple, class... _Types1, class... _Tuples> -struct __tuple_cat_return_impl<tuple<_Types0...>, _Tuple<_Types1...>, _Tuples...> +template <class... _Types0, class... _Types1, class... _Tuples> +struct __tuple_cat_return_impl<tuple<_Types0...>, tuple<_Types1...>, _Tuples...> : __tuple_cat_return_impl<tuple<_Types0..., _Types1...>, _Tuples...> {}; +template <class... _Types0, class _Tp, class _Up, class... _Tuples> +struct __tuple_cat_return_impl<tuple<_Types0...>, pair<_Tp, _Up>, _Tuples...> + : __tuple_cat_return_impl<tuple<_Types0..., _Tp, _Up>, _Tuples...> {}; + template <class, class, class> struct __tuple_cat_array; @@ -1369,11 +1372,7 @@ __tuple_cat_select_element_wise(_TupleSrc&& __src, __index_sequence<_Indices...> return _TupleDst(std::get<_Indices>(std::forward<_TupleSrc>(__src))...); } -template <class _Tuple0, - class... _Tuples, - __enable_if_t< - _And<__tuple_like_ext<__remove_cvref_t<_Tuple0>>, __tuple_like_ext<__remove_cvref_t<_Tuples>>...>::value, - int> = 0> +template <class _Tuple0, class... _Tuples> inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __tuple_cat_return_t<_Tuple0, _Tuples...> tuple_cat(_Tuple0&& __t0, _Tuples&&... __tpls) { using _T0 _LIBCPP_NODEBUG = __libcpp_remove_reference_t<_Tuple0>; diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate.pass.cpp index 29d32d7..4591d7e 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate.pass.cpp @@ -16,6 +16,7 @@ #include <algorithm> #include <cassert> +#include <deque> #include "test_macros.h" #include "test_iterators.h" @@ -51,12 +52,22 @@ test() assert(ia[3] == 1); } +void deque_test() { + int sizes[] = {0, 1, 2, 1023, 1024, 1025, 2047, 2048, 2049}; + for (const int size : sizes) { + std::deque<int> d(size); + std::generate(d.begin(), d.end(), gen_test()); + assert(std::all_of(d.begin(), d.end(), [](int x) { return x == 1; })); + } +} + int main(int, char**) { test<forward_iterator<int*> >(); test<bidirectional_iterator<int*> >(); test<random_access_iterator<int*> >(); test<int*>(); + deque_test(); #if TEST_STD_VER > 17 static_assert(test_constexpr()); diff --git a/libcxx/test/std/atomics/atomics.types.generic/cas_non_power_of_2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/cas_non_power_of_2.pass.cpp new file mode 100644 index 0000000..13bd761 --- /dev/null +++ b/libcxx/test/std/atomics/atomics.types.generic/cas_non_power_of_2.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// https://github.com/llvm/llvm-project/issues/30023 +// compare exchange does not work with types of which the size is not a power of 2 + +// XFAIL: clang-19, clang-20, clang-21, apple-clang-15, apple-clang-16, apple-clang-17 +// UNSUPPORTED: c++03 + +// TODO: remove the UNSUPPORTED clang-22 once libc++ CI's clang is updated to include +// the fix https://github.com/llvm/llvm-project/pull/78707 +// UNSUPPORTED: clang-22 + +#include <atomic> +#include <cstring> +#include <cassert> + +template <int Size> +struct S { + char data[Size]; + + explicit S(char v = 0) noexcept { memset(&data[0], v, sizeof(data)); } + + // only used in the test to check the results. Not used in atomic operations. + friend bool operator==(const S& lhs, const S& rhs) noexcept { + return memcmp(&lhs.data[0], &rhs.data[0], sizeof(lhs.data)) == 0; + } + friend bool operator!=(const S& lhs, const S& rhs) noexcept { return !(lhs == rhs); } +}; + +template <int Size> +struct Expected { + Expected(S<Size> ss) : s(ss) {} + + S<Size> s; + bool b = true; // used to validate that s's operation won't overwrite the memory next to it +}; + +template <int Size> +void test() { + using T = S<Size>; + std::atomic<T> a(T(0)); + Expected<Size> expected{T(17)}; + + assert(a.load() != expected.s); + assert(expected.b); + + auto r1 = a.compare_exchange_strong(expected.s, T(18), std::memory_order_relaxed); + + assert(!r1); + assert(expected.s == T(0)); // expected.s is modified by compare_exchange_strong + assert(expected.b); + assert(a.load() == T(0)); + + auto r2 = a.compare_exchange_strong(expected.s, T(18), std::memory_order_relaxed); + assert(r2); + assert(a.load() == T(18)); + assert(expected.s == T(0)); + assert(expected.b); +} + +int main(int, char**) { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + test<6>(); + + return 0; +} diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/erase_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/erase_range.pass.cpp index f8a2bdd..38b75c0 100644 --- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/erase_range.pass.cpp +++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/erase_range.pass.cpp @@ -91,6 +91,37 @@ int main(int, char**) { assert(c.size() == 0); assert(k == c.end()); } + { // Make sure we're properly destroying the elements when erasing + { // When erasing part of a bucket + std::unordered_multimap<int, std::string> map; + map.insert(std::make_pair(1, "This is a long string to make sure ASan can detect a memory leak")); + map.insert(std::make_pair(1, "This is another long string to make sure ASan can detect a memory leak")); + map.erase(++map.begin(), map.end()); + } + { // When erasing the whole bucket + std::unordered_multimap<int, std::string> map; + map.insert(std::make_pair(1, "This is a long string to make sure ASan can detect a memory leak")); + map.insert(std::make_pair(1, "This is another long string to make sure ASan can detect a memory leak")); + map.erase(map.begin(), map.end()); + } + } + { // Make sure that we're properly updating the bucket list when starting within a bucket + struct MyHash { + size_t operator()(size_t v) const { return v; } + }; + std::unordered_multimap<size_t, size_t, MyHash> map; + size_t collision_val = 2 + map.bucket_count(); // try to get a bucket collision + map.rehash(3); + map.insert(std::pair<size_t, size_t>(1, 1)); + map.insert(std::pair<size_t, size_t>(collision_val, 1)); + map.insert(std::pair<size_t, size_t>(2, 1)); + LIBCPP_ASSERT(map.bucket(2) == map.bucket(collision_val)); + + auto erase = map.equal_range(2); + map.erase(erase.first, erase.second); + for (const auto& v : map) + assert(v.first == 1 || v.first == collision_val); + } #if TEST_STD_VER >= 11 { typedef std::unordered_multimap<int, diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase_range.pass.cpp index 62724fd..3bc686e 100644 --- a/libcxx/test/std/containers/unord/unord.multiset/erase_range.pass.cpp +++ b/libcxx/test/std/containers/unord/unord.multiset/erase_range.pass.cpp @@ -47,6 +47,23 @@ int main(int, char**) { assert(c.size() == 0); assert(k == c.end()); } + { // Make sure that we're properly updating the bucket list when starting within a bucket + struct MyHash { + size_t operator()(size_t v) const { return v; } + }; + std::unordered_multiset<size_t, MyHash> map; + size_t collision_val = 2 + map.bucket_count(); // try to get a bucket collision + map.rehash(3); + map.insert(1); + map.insert(collision_val); + map.insert(2); + LIBCPP_ASSERT(map.bucket(2) == map.bucket(collision_val)); + + auto erase = map.equal_range(2); + map.erase(erase.first, erase.second); + for (const auto& v : map) + assert(v == 1 || v == collision_val); + } #if TEST_STD_VER >= 11 { typedef std::unordered_multiset<int, std::hash<int>, std::equal_to<int>, min_allocator<int>> C; diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp index 881a5d2..1c9d72e 100644 --- a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp +++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// REQUIRES: std-at-least-c++23 // These compilers don't support __builtin_is_implicit_lifetime yet. -// UNSUPPORTED: clang-19, gcc-14, gcc-15, apple-clang-16, apple-clang-17 +// UNSUPPORTED: clang-19, gcc-15, apple-clang-17 // <type_traits> @@ -139,13 +139,6 @@ constexpr void test_is_implicit_lifetime() { test_is_implicit_lifetime<T[94], true>(); } -struct ArithmeticTypesTest { - template <class T> - constexpr void operator()() { - test_is_implicit_lifetime<T>(); - } -}; - constexpr bool test() { // Standard fundamental C++ types @@ -155,7 +148,7 @@ constexpr bool test() { test_is_implicit_lifetime<const void, false>(); test_is_implicit_lifetime<volatile void, false>(); - types::for_each(types::arithmetic_types(), ArithmeticTypesTest{}); + types::for_each(types::arithmetic_types(), []<typename T> { test_is_implicit_lifetime<T>(); }); test_is_implicit_lifetime<Enum>(); test_is_implicit_lifetime<SignedEnum>(); diff --git a/libcxx/utils/compare-benchmarks b/libcxx/utils/compare-benchmarks index 988e243..b5bd880 100755 --- a/libcxx/utils/compare-benchmarks +++ b/libcxx/utils/compare-benchmarks @@ -65,9 +65,16 @@ def plain_text_comparison(data, metric, baseline_name=None, candidate_name=None) """ data = data.replace(numpy.nan, None) # avoid NaNs in tabulate output headers = ['Benchmark', baseline_name, candidate_name, 'Difference', '% Difference'] - fmt = (None, '.2f', '.2f', '.2f', '.2f') - table = data[['benchmark', f'{metric}_0', f'{metric}_1', 'difference', 'percent']].set_index('benchmark') - return tabulate.tabulate(table, headers=headers, floatfmt=fmt, numalign='right') + fmt = (None, '.2f', '.2f', '.2f', '.2%') + table = data[['benchmark', f'{metric}_0', f'{metric}_1', 'difference', 'percent']] + + # Compute the geomean and report on their difference + geomean_0 = statistics.geometric_mean(data[f'{metric}_0'].dropna()) + geomean_1 = statistics.geometric_mean(data[f'{metric}_1'].dropna()) + geomean_row = ['Geomean', geomean_0, geomean_1, (geomean_1 - geomean_0), (geomean_1 - geomean_0) / geomean_0] + table.loc[table.index.max() + 1] = geomean_row + + return tabulate.tabulate(table.set_index('benchmark'), headers=headers, floatfmt=fmt, numalign='right') def create_chart(data, metric, subtitle=None, series_names=None): """ @@ -154,7 +161,7 @@ def main(argv): # If we have exactly two data sets, compute additional info in new columns if len(lnt_inputs) == 2: data['difference'] = data[f'{args.metric}_1'] - data[f'{args.metric}_0'] - data['percent'] = 100 * (data['difference'] / data[f'{args.metric}_0']) + data['percent'] = data['difference'] / data[f'{args.metric}_0'] if args.filter is not None: keeplist = [b for b in data['benchmark'] if re.search(args.filter, b) is not None] |