aboutsummaryrefslogtreecommitdiff
path: root/libcxx
diff options
context:
space:
mode:
Diffstat (limited to 'libcxx')
-rw-r--r--libcxx/docs/ReleaseNotes/21.rst3
-rw-r--r--libcxx/docs/ReleaseNotes/22.rst4
-rw-r--r--libcxx/docs/Status/Cxx2cIssues.csv2
-rw-r--r--libcxx/docs/Status/Cxx2cPapers.csv2
-rw-r--r--libcxx/include/CMakeLists.txt1
-rw-r--r--libcxx/include/__algorithm/generate.h6
-rw-r--r--libcxx/include/__hash_table100
-rw-r--r--libcxx/include/__tuple/tuple_like_ext.h41
-rw-r--r--libcxx/include/module.modulemap.in1
-rw-r--r--libcxx/include/tuple21
-rw-r--r--libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate.pass.cpp11
-rw-r--r--libcxx/test/std/atomics/atomics.types.generic/cas_non_power_of_2.pass.cpp76
-rw-r--r--libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/erase_range.pass.cpp31
-rw-r--r--libcxx/test/std/containers/unord/unord.multiset/erase_range.pass.cpp17
-rw-r--r--libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp13
-rwxr-xr-xlibcxx/utils/compare-benchmarks15
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]