diff options
| author | Aiden Grossman <aidengrossman@google.com> | 2025-09-09 03:19:14 +0000 |
|---|---|---|
| committer | Aiden Grossman <aidengrossman@google.com> | 2025-09-09 03:19:14 +0000 |
| commit | 8c3530cbde51ee85adff6ab20771367ada3a9b49 (patch) | |
| tree | 74e1b33408f9c293c5408951ac3b1053dbd6210c /libcxx | |
| parent | 4b6cd10bc5039e5bfa60a74cdac744969332adb2 (diff) | |
| parent | 3b1ca5e7c5b94b10e3da554a060459a1a1e24495 (diff) | |
[𝘀𝗽𝗿] changes introduced through rebaseusers/boomanaiden154/main.lit-remove-python-27-code-paths-in-builtin-diff
Created using spr 1.3.6
[skip ci]
Diffstat (limited to 'libcxx')
35 files changed, 638 insertions, 148 deletions
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index f2f1ec50d5b7..db918a16e9a6 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -911,6 +911,7 @@ set(files __utility/cmp.h __utility/convert_to_integral.h __utility/declval.h + __utility/default_three_way_comparator.h __utility/element_count.h __utility/empty.h __utility/exception_guard.h @@ -921,6 +922,7 @@ set(files __utility/integer_sequence.h __utility/is_pointer_in_range.h __utility/is_valid_range.h + __utility/lazy_synth_three_way_comparator.h __utility/move.h __utility/no_destroy.h __utility/pair.h diff --git a/libcxx/include/__algorithm/make_heap.h b/libcxx/include/__algorithm/make_heap.h index e8f0cdb27333..8cfeda2b5981 100644 --- a/libcxx/include/__algorithm/make_heap.h +++ b/libcxx/include/__algorithm/make_heap.h @@ -12,9 +12,11 @@ #include <__algorithm/comp.h> #include <__algorithm/comp_ref_type.h> #include <__algorithm/iterator_operations.h> +#include <__algorithm/push_heap.h> #include <__algorithm/sift_down.h> #include <__config> #include <__iterator/iterator_traits.h> +#include <__type_traits/is_arithmetic.h> #include <__utility/move.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -31,13 +33,23 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) { __comp_ref_type<_Compare> __comp_ref = __comp; - using difference_type = typename iterator_traits<_RandomAccessIterator>::difference_type; - difference_type __n = __last - __first; + using __diff_t = __iter_diff_t<_RandomAccessIterator>; + const __diff_t __n = __last - __first; + + static const bool __assume_both_children = is_arithmetic<__iter_value_type<_RandomAccessIterator> >::value; + + // While it would be correct to always assume we have both children, in practice we observed this to be a performance + // improvement only for arithmetic types. + const __diff_t __sift_down_n = __assume_both_children ? ((__n & 1) ? __n : __n - 1) : __n; + if (__n > 1) { // start from the first parent, there is no need to consider children - for (difference_type __start = (__n - 2) / 2; __start >= 0; --__start) { - std::__sift_down<_AlgPolicy>(__first, __comp_ref, __n, __first + __start); + + for (__diff_t __start = (__sift_down_n - 2) / 2; __start >= 0; --__start) { + std::__sift_down<_AlgPolicy, __assume_both_children>(__first, __comp_ref, __sift_down_n, __start); } + if _LIBCPP_CONSTEXPR (__assume_both_children) + std::__sift_up<_AlgPolicy>(__first, __last, __comp, __n); } } diff --git a/libcxx/include/__algorithm/partial_sort.h b/libcxx/include/__algorithm/partial_sort.h index 7f8d0c49147e..4b39ae0cf2df 100644 --- a/libcxx/include/__algorithm/partial_sort.h +++ b/libcxx/include/__algorithm/partial_sort.h @@ -45,7 +45,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator __part for (; __i != __last; ++__i) { if (__comp(*__i, *__first)) { _IterOps<_AlgPolicy>::iter_swap(__i, __first); - std::__sift_down<_AlgPolicy>(__first, __comp, __len, __first); + std::__sift_down<_AlgPolicy, false>(__first, __comp, __len, 0); } } std::__sort_heap<_AlgPolicy>(std::move(__first), std::move(__middle), __comp); diff --git a/libcxx/include/__algorithm/partial_sort_copy.h b/libcxx/include/__algorithm/partial_sort_copy.h index 172f53b290d5..2230dfc9cc4a 100644 --- a/libcxx/include/__algorithm/partial_sort_copy.h +++ b/libcxx/include/__algorithm/partial_sort_copy.h @@ -60,7 +60,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<_InputIterator, _Random for (; __first != __last; ++__first) if (std::__invoke(__comp, std::__invoke(__proj1, *__first), std::__invoke(__proj2, *__result_first))) { *__result_first = *__first; - std::__sift_down<_AlgPolicy>(__result_first, __projected_comp, __len, __result_first); + std::__sift_down<_AlgPolicy, false>(__result_first, __projected_comp, __len, 0); } std::__sort_heap<_AlgPolicy>(__result_first, __r, __projected_comp); } diff --git a/libcxx/include/__algorithm/sift_down.h b/libcxx/include/__algorithm/sift_down.h index 42803e30631f..e01c9b2b00f8 100644 --- a/libcxx/include/__algorithm/sift_down.h +++ b/libcxx/include/__algorithm/sift_down.h @@ -24,59 +24,60 @@ _LIBCPP_PUSH_MACROS _LIBCPP_BEGIN_NAMESPACE_STD -template <class _AlgPolicy, class _Compare, class _RandomAccessIterator> +template <class _AlgPolicy, bool __assume_both_children, class _Compare, class _RandomAccessIterator> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __sift_down(_RandomAccessIterator __first, _Compare&& __comp, - typename iterator_traits<_RandomAccessIterator>::difference_type __len, - _RandomAccessIterator __start) { + __iter_diff_t<_RandomAccessIterator> __len, + __iter_diff_t<_RandomAccessIterator> __start) { using _Ops = _IterOps<_AlgPolicy>; typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type; typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type; // left-child of __start is at 2 * __start + 1 // right-child of __start is at 2 * __start + 2 - difference_type __child = __start - __first; + difference_type __child = __start; if (__len < 2 || (__len - 2) / 2 < __child) return; - __child = 2 * __child + 1; - _RandomAccessIterator __child_i = __first + __child; + __child = 2 * __child + 1; - if ((__child + 1) < __len && __comp(*__child_i, *(__child_i + difference_type(1)))) { + if _LIBCPP_CONSTEXPR (__assume_both_children) { + // right-child exists and is greater than left-child + __child += __comp(__first[__child], __first[__child + 1]); + } else if ((__child + 1) < __len && __comp(__first[__child], __first[__child + 1])) { // right-child exists and is greater than left-child - ++__child_i; ++__child; } // check if we are in heap-order - if (__comp(*__child_i, *__start)) + if (__comp(__first[__child], __first[__start])) // we are, __start is larger than its largest child return; - value_type __top(_Ops::__iter_move(__start)); + value_type __top(_Ops::__iter_move(__first + __start)); do { // we are not in heap-order, swap the parent with its largest child - *__start = _Ops::__iter_move(__child_i); - __start = __child_i; + __first[__start] = _Ops::__iter_move(__first + __child); + __start = __child; if ((__len - 2) / 2 < __child) break; // recompute the child based off of the updated parent - __child = 2 * __child + 1; - __child_i = __first + __child; + __child = 2 * __child + 1; - if ((__child + 1) < __len && __comp(*__child_i, *(__child_i + difference_type(1)))) { + if _LIBCPP_CONSTEXPR (__assume_both_children) { + __child += __comp(__first[__child], __first[__child + 1]); + } else if ((__child + 1) < __len && __comp(__first[__child], __first[__child + 1])) { // right-child exists and is greater than left-child - ++__child_i; ++__child; } // check if we are in heap-order - } while (!__comp(*__child_i, __top)); - *__start = std::move(__top); + } while (!__comp(__first[__child], __top)); + __first[__start] = std::move(__top); } template <class _AlgPolicy, class _Compare, class _RandomAccessIterator> diff --git a/libcxx/include/__config b/libcxx/include/__config index c197851f1c8f..23b9123fa691 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1156,6 +1156,13 @@ typedef __char32_t char32_t; # define _LIBCPP_LIFETIMEBOUND # endif +// This is to work around https://llvm.org/PR156809 +# ifndef _LIBCPP_CXX03_LANG +# define _LIBCPP_CTOR_LIFETIMEBOUND _LIBCPP_LIFETIMEBOUND +# else +# define _LIBCPP_CTOR_LIFETIMEBOUND +# endif + # if __has_cpp_attribute(_Clang::__noescape__) # define _LIBCPP_NOESCAPE [[_Clang::__noescape__]] # else diff --git a/libcxx/include/__cxx03/__mutex/once_flag.h b/libcxx/include/__cxx03/__mutex/once_flag.h index eaeaa6121bff..529aa7707177 100644 --- a/libcxx/include/__cxx03/__mutex/once_flag.h +++ b/libcxx/include/__cxx03/__mutex/once_flag.h @@ -10,6 +10,7 @@ #define _LIBCPP___CXX03___MUTEX_ONCE_FLAG_H #include <__cxx03/__config> +#include <__cxx03/__memory/addressof.h> #include <__cxx03/__memory/shared_ptr.h> // __libcpp_acquire_load #include <__cxx03/__tuple/tuple_indices.h> #include <__cxx03/__tuple/tuple_size.h> @@ -82,7 +83,7 @@ template <class _Callable> inline _LIBCPP_HIDE_FROM_ABI void call_once(once_flag& __flag, _Callable& __func) { if (__libcpp_acquire_load(&__flag.__state_) != once_flag::_Complete) { __call_once_param<_Callable> __p(__func); - std::__call_once(__flag.__state_, &__p, &__call_once_proxy<_Callable>); + std::__call_once(__flag.__state_, std::addressof(__p), std::addressof(__call_once_proxy<_Callable>)); } } @@ -90,7 +91,7 @@ template <class _Callable> inline _LIBCPP_HIDE_FROM_ABI void call_once(once_flag& __flag, const _Callable& __func) { if (__libcpp_acquire_load(&__flag.__state_) != once_flag::_Complete) { __call_once_param<const _Callable> __p(__func); - std::__call_once(__flag.__state_, &__p, &__call_once_proxy<const _Callable>); + std::__call_once(__flag.__state_, std::addressof(__p), std::addressof(__call_once_proxy<const _Callable>)); } } diff --git a/libcxx/include/__cxx03/locale b/libcxx/include/__cxx03/locale index 64162f5a4ff2..57267ab39022 100644 --- a/libcxx/include/__cxx03/locale +++ b/libcxx/include/__cxx03/locale @@ -2460,6 +2460,8 @@ _LIBCPP_HIDE_FROM_ABI void __double_or_nothing(unique_ptr<_Tp, void (*)(void*)>& __throw_bad_alloc(); if (__owns) __b.release(); + else + std::memcpy(__t, __b.get(), __cur_cap); __b = unique_ptr<_Tp, void (*)(void*)>(__t, free); __new_cap /= sizeof(_Tp); __n = __b.get() + __n_off; @@ -2655,20 +2657,22 @@ _InputIterator money_get<_CharT, _InputIterator>::do_get( char_type __atoms[sizeof(__src) - 1]; __ct.widen(__src, __src + (sizeof(__src) - 1), __atoms); char __nbuf[__bz]; - char* __nc = __nbuf; + char* __nc = __nbuf; + const char* __nc_in = __nc; unique_ptr<char, void (*)(void*)> __h(nullptr, free); if (__wn - __wb.get() > __bz - 2) { __h.reset((char*)malloc(static_cast<size_t>(__wn - __wb.get() + 2))); if (__h.get() == nullptr) __throw_bad_alloc(); - __nc = __h.get(); + __nc = __h.get(); + __nc_in = __nc; } if (__neg) *__nc++ = '-'; for (const char_type* __w = __wb.get(); __w < __wn; ++__w, ++__nc) *__nc = __src[std::find(__atoms, std::end(__atoms), *__w) - __atoms]; *__nc = char(); - if (sscanf(__nbuf, "%Lf", &__v) != 1) + if (sscanf(__nc_in, "%Lf", &__v) != 1) __throw_runtime_error("money_get error"); } if (__b == __e) diff --git a/libcxx/include/__cxx03/string b/libcxx/include/__cxx03/string index 7d54030d0b66..178140486105 100644 --- a/libcxx/include/__cxx03/string +++ b/libcxx/include/__cxx03/string @@ -1101,12 +1101,20 @@ public: _LIBCPP_HIDE_FROM_ABI size_type length() const _NOEXCEPT { return size(); } _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { - size_type __m = __alloc_traits::max_size(__alloc()); - if (__m <= std::numeric_limits<size_type>::max() / 2) { - return __m - __alignment; + if (size_type __m = __alloc_traits::max_size(__alloc()); __m <= std::numeric_limits<size_type>::max() / 2) { + size_type __res = __m - __alignment; + + // When the __endian_factor == 2, our string representation assumes that the capacity + // (including the null terminator) is always even, so we have to make sure the lowest bit isn't set when the + // string grows to max_size() + if (__endian_factor == 2) + __res &= ~size_type(1); + + // We have to allocate space for the null terminator, but max_size() doesn't include it. + return __res - 1; } else { bool __uses_lsb = __endian_factor == 2; - return __uses_lsb ? __m - __alignment : (__m / 2) - __alignment; + return __uses_lsb ? __m - __alignment - 1 : (__m / 2) - __alignment - 1; } } @@ -1970,11 +1978,11 @@ void basic_string<_CharT, _Traits, _Allocator>::__grow_by_and_replace( size_type __n_add, const value_type* __p_new_stuff) { size_type __ms = max_size(); - if (__delta_cap > __ms - __old_cap - 1) + if (__delta_cap > __ms - __old_cap) __throw_length_error(); pointer __old_p = __get_pointer(); size_type __cap = - __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1; + __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms; __annotate_delete(); auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1); pointer __p = __allocation.ptr; @@ -2017,7 +2025,7 @@ void __throw_length_error(); pointer __old_p = __get_pointer(); size_type __cap = - __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1; + __old_cap < __ms / 2 - __alignment ? __recommend(std::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms; __annotate_delete(); auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1); pointer __p = __allocation.ptr; diff --git a/libcxx/include/__random/binomial_distribution.h b/libcxx/include/__random/binomial_distribution.h index b4b434082776..e1e852ec9a3a 100644 --- a/libcxx/include/__random/binomial_distribution.h +++ b/libcxx/include/__random/binomial_distribution.h @@ -97,13 +97,23 @@ public: } }; -// The LLVM C library provides this with conflicting `noexcept` attributes. -#if !defined(_LIBCPP_MSVCRT_LIKE) && !defined(__LLVM_LIBC__) -extern "C" double lgamma_r(double, int*); +// Some libc declares the math functions to be `noexcept`. +#if defined(_LIBCPP_GLIBC_PREREQ) +# if _LIBCPP_GLIBC_PREREQ(2, 8) +# define _LIBCPP_LGAMMA_R_NOEXCEPT _NOEXCEPT +# endif +#elif defined(__LLVM_LIBC__) +# define _LIBCPP_LGAMMA_R_NOEXCEPT _NOEXCEPT +#else +# define _LIBCPP_LGAMMA_R_NOEXCEPT +#endif + +#if !defined(_LIBCPP_MSVCRT_LIKE) +extern "C" double lgamma_r(double, int*) _LIBCPP_LGAMMA_R_NOEXCEPT; #endif inline _LIBCPP_HIDE_FROM_ABI double __libcpp_lgamma(double __d) { -#if defined(_LIBCPP_MSVCRT_LIKE) || defined(__LLVM_LIBC__) +#if defined(_LIBCPP_MSVCRT_LIKE) return lgamma(__d); #else int __sign; diff --git a/libcxx/include/__tree b/libcxx/include/__tree index c5f52f404eff..81a73342cc61 100644 --- a/libcxx/include/__tree +++ b/libcxx/include/__tree @@ -34,6 +34,7 @@ #include <__type_traits/is_swappable.h> #include <__type_traits/remove_const.h> #include <__utility/forward.h> +#include <__utility/lazy_synth_three_way_comparator.h> #include <__utility/move.h> #include <__utility/pair.h> #include <__utility/swap.h> @@ -861,9 +862,21 @@ public: using const_iterator = __tree_const_iterator<_Tp, __node_pointer, difference_type>; _LIBCPP_HIDE_FROM_ABI explicit __tree(const value_compare& __comp) _NOEXCEPT_( - is_nothrow_default_constructible<__node_allocator>::value&& is_nothrow_copy_constructible<value_compare>::value); - _LIBCPP_HIDE_FROM_ABI explicit __tree(const allocator_type& __a); - _LIBCPP_HIDE_FROM_ABI __tree(const value_compare& __comp, const allocator_type& __a); + is_nothrow_default_constructible<__node_allocator>::value&& is_nothrow_copy_constructible<value_compare>::value) + : __size_(0), __value_comp_(__comp) { + __begin_node_ = __end_node(); + } + + _LIBCPP_HIDE_FROM_ABI explicit __tree(const allocator_type& __a) + : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0) { + __begin_node_ = __end_node(); + } + + _LIBCPP_HIDE_FROM_ABI __tree(const value_compare& __comp, const allocator_type& __a) + : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(__comp) { + __begin_node_ = __end_node(); + } + _LIBCPP_HIDE_FROM_ABI __tree(const __tree& __t); _LIBCPP_HIDE_FROM_ABI __tree& operator=(const __tree& __t); template <class _ForwardIterator> @@ -873,13 +886,20 @@ public: _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t) _NOEXCEPT_( is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value); _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t, const allocator_type& __a); + _LIBCPP_HIDE_FROM_ABI __tree& operator=(__tree&& __t) _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value && ((__node_traits::propagate_on_container_move_assignment::value && is_nothrow_move_assignable<__node_allocator>::value) || - allocator_traits<__node_allocator>::is_always_equal::value)); + allocator_traits<__node_allocator>::is_always_equal::value)) { + __move_assign(__t, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>()); + return *this; + } - _LIBCPP_HIDE_FROM_ABI ~__tree(); + _LIBCPP_HIDE_FROM_ABI ~__tree() { + static_assert(is_copy_constructible<value_compare>::value, "Comparator must be copy-constructible."); + destroy(__root()); + } _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return iterator(__begin_node_); } _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return const_iterator(__begin_node_); } @@ -1203,7 +1223,7 @@ private: _LIBCPP_HIDE_FROM_ABI __node_holder __construct_node(_Args&&... __args); // TODO: Make this _LIBCPP_HIDE_FROM_ABI - _LIBCPP_HIDDEN void destroy(__node_pointer __nd) _NOEXCEPT; + _LIBCPP_HIDDEN void destroy(__node_pointer __nd) _NOEXCEPT { (__tree_deleter(__node_alloc_))(__nd); } _LIBCPP_HIDE_FROM_ABI void __move_assign(__tree& __t, false_type); _LIBCPP_HIDE_FROM_ABI void __move_assign(__tree& __t, true_type) _NOEXCEPT_( @@ -1372,25 +1392,6 @@ private: } }; -template <class _Tp, class _Compare, class _Allocator> -__tree<_Tp, _Compare, _Allocator>::__tree(const value_compare& __comp) _NOEXCEPT_( - is_nothrow_default_constructible<__node_allocator>::value&& is_nothrow_copy_constructible<value_compare>::value) - : __size_(0), __value_comp_(__comp) { - __begin_node_ = __end_node(); -} - -template <class _Tp, class _Compare, class _Allocator> -__tree<_Tp, _Compare, _Allocator>::__tree(const allocator_type& __a) - : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0) { - __begin_node_ = __end_node(); -} - -template <class _Tp, class _Compare, class _Allocator> -__tree<_Tp, _Compare, _Allocator>::__tree(const value_compare& __comp, const allocator_type& __a) - : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(__comp) { - __begin_node_ = __end_node(); -} - // Precondition: __size_ != 0 template <class _Tp, class _Compare, class _Allocator> typename __tree<_Tp, _Compare, _Allocator>::__node_pointer @@ -1588,27 +1589,6 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) { } template <class _Tp, class _Compare, class _Allocator> -__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(__tree&& __t) - _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value && - ((__node_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable<__node_allocator>::value) || - allocator_traits<__node_allocator>::is_always_equal::value)) { - __move_assign(__t, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>()); - return *this; -} - -template <class _Tp, class _Compare, class _Allocator> -__tree<_Tp, _Compare, _Allocator>::~__tree() { - static_assert(is_copy_constructible<value_compare>::value, "Comparator must be copy-constructible."); - destroy(__root()); -} - -template <class _Tp, class _Compare, class _Allocator> -void __tree<_Tp, _Compare, _Allocator>::destroy(__node_pointer __nd) _NOEXCEPT { - (__tree_deleter(__node_alloc_))(__nd); -} - -template <class _Tp, class _Compare, class _Allocator> void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t) #if _LIBCPP_STD_VER <= 11 _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> && @@ -1749,14 +1729,18 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) { } __node_base_pointer* __node_ptr = __root_ptr(); + auto __comp = __lazy_synth_three_way_comparator<_Compare, _Key, value_type>(value_comp()); + while (true) { - if (value_comp()(__v, __nd->__get_value())) { + auto __comp_res = __comp(__v, __nd->__get_value()); + + if (__comp_res.__less()) { if (__nd->__left_ == nullptr) return _Pair(static_cast<__end_node_pointer>(__nd), __nd->__left_); __node_ptr = std::addressof(__nd->__left_); __nd = static_cast<__node_pointer>(__nd->__left_); - } else if (value_comp()(__nd->__get_value(), __v)) { + } else if (__comp_res.__greater()) { if (__nd->__right_ == nullptr) return _Pair(static_cast<__end_node_pointer>(__nd), __nd->__right_); @@ -2065,10 +2049,12 @@ template <class _Key> typename __tree<_Tp, _Compare, _Allocator>::size_type __tree<_Tp, _Compare, _Allocator>::__count_unique(const _Key& __k) const { __node_pointer __rt = __root(); + auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp()); while (__rt != nullptr) { - if (value_comp()(__k, __rt->__get_value())) { + auto __comp_res = __comp(__k, __rt->__get_value()); + if (__comp_res.__less()) { __rt = static_cast<__node_pointer>(__rt->__left_); - } else if (value_comp()(__rt->__get_value(), __k)) + } else if (__comp_res.__greater()) __rt = static_cast<__node_pointer>(__rt->__right_); else return 1; @@ -2082,11 +2068,13 @@ typename __tree<_Tp, _Compare, _Allocator>::size_type __tree<_Tp, _Compare, _Allocator>::__count_multi(const _Key& __k) const { __end_node_pointer __result = __end_node(); __node_pointer __rt = __root(); + auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp()); while (__rt != nullptr) { - if (value_comp()(__k, __rt->__get_value())) { + auto __comp_res = __comp(__k, __rt->__get_value()); + if (__comp_res.__less()) { __result = static_cast<__end_node_pointer>(__rt); __rt = static_cast<__node_pointer>(__rt->__left_); - } else if (value_comp()(__rt->__get_value(), __k)) + } else if (__comp_res.__greater()) __rt = static_cast<__node_pointer>(__rt->__right_); else return std::distance( @@ -2159,11 +2147,13 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) { using _Pp = pair<iterator, iterator>; __end_node_pointer __result = __end_node(); __node_pointer __rt = __root(); + auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp()); while (__rt != nullptr) { - if (value_comp()(__k, __rt->__get_value())) { + auto __comp_res = __comp(__k, __rt->__get_value()); + if (__comp_res.__less()) { __result = static_cast<__end_node_pointer>(__rt); __rt = static_cast<__node_pointer>(__rt->__left_); - } else if (value_comp()(__rt->__get_value(), __k)) + } else if (__comp_res.__greater()) __rt = static_cast<__node_pointer>(__rt->__right_); else return _Pp(iterator(__rt), @@ -2181,11 +2171,13 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) const { using _Pp = pair<const_iterator, const_iterator>; __end_node_pointer __result = __end_node(); __node_pointer __rt = __root(); + auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp()); while (__rt != nullptr) { - if (value_comp()(__k, __rt->__get_value())) { + auto __comp_res = __comp(__k, __rt->__get_value()); + if (__comp_res.__less()) { __result = static_cast<__end_node_pointer>(__rt); __rt = static_cast<__node_pointer>(__rt->__left_); - } else if (value_comp()(__rt->__get_value(), __k)) + } else if (__comp_res.__greater()) __rt = static_cast<__node_pointer>(__rt->__right_); else return _Pp( @@ -2202,12 +2194,14 @@ pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) { using _Pp = pair<iterator, iterator>; __end_node_pointer __result = __end_node(); - __node_pointer __rt = __root(); + __node_pointer __rt = __root(); + auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp()); while (__rt != nullptr) { - if (value_comp()(__k, __rt->__get_value())) { + auto __comp_res = __comp(__k, __rt->__get_value()); + if (__comp_res.__less()) { __result = static_cast<__end_node_pointer>(__rt); __rt = static_cast<__node_pointer>(__rt->__left_); - } else if (value_comp()(__rt->__get_value(), __k)) + } else if (__comp_res.__greater()) __rt = static_cast<__node_pointer>(__rt->__right_); else return _Pp(__lower_bound(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)), @@ -2223,12 +2217,14 @@ pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator, __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) const { using _Pp = pair<const_iterator, const_iterator>; __end_node_pointer __result = __end_node(); - __node_pointer __rt = __root(); + __node_pointer __rt = __root(); + auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp()); while (__rt != nullptr) { - if (value_comp()(__k, __rt->__get_value())) { + auto __comp_res = __comp(__k, __rt->__get_value()); + if (__comp_res.__less()) { __result = static_cast<__end_node_pointer>(__rt); __rt = static_cast<__node_pointer>(__rt->__left_); - } else if (value_comp()(__rt->__get_value(), __k)) + } else if (__comp_res.__greater()) __rt = static_cast<__node_pointer>(__rt->__right_); else return _Pp(__lower_bound(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)), diff --git a/libcxx/include/__utility/default_three_way_comparator.h b/libcxx/include/__utility/default_three_way_comparator.h new file mode 100644 index 000000000000..ce423c6ce98e --- /dev/null +++ b/libcxx/include/__utility/default_three_way_comparator.h @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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___UTILITY_DEFAULT_THREE_WAY_COMPARATOR_H +#define _LIBCPP___UTILITY_DEFAULT_THREE_WAY_COMPARATOR_H + +#include <__config> +#include <__type_traits/enable_if.h> +#include <__type_traits/is_arithmetic.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +// This struct can be specialized to provide a three way comparator between _LHS and _RHS. +// The return value should be +// - less than zero if (lhs_val < rhs_val) +// - greater than zero if (rhs_val < lhs_val) +// - zero otherwise +template <class _LHS, class _RHS, class = void> +struct __default_three_way_comparator; + +template <class _Tp> +struct __default_three_way_comparator<_Tp, _Tp, __enable_if_t<is_arithmetic<_Tp>::value> > { + _LIBCPP_HIDE_FROM_ABI static int operator()(_Tp __lhs, _Tp __rhs) { + if (__lhs < __rhs) + return -1; + if (__lhs > __rhs) + return 1; + return 0; + } +}; + +template <class _LHS, class _RHS, bool = true> +inline const bool __has_default_three_way_comparator_v = false; + +template <class _LHS, class _RHS> +inline const bool + __has_default_three_way_comparator_v< _LHS, _RHS, sizeof(__default_three_way_comparator<_LHS, _RHS>) >= 0> = true; + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___UTILITY_DEFAULT_THREE_WAY_COMPARATOR_H diff --git a/libcxx/include/__utility/lazy_synth_three_way_comparator.h b/libcxx/include/__utility/lazy_synth_three_way_comparator.h new file mode 100644 index 000000000000..ca98845f0419 --- /dev/null +++ b/libcxx/include/__utility/lazy_synth_three_way_comparator.h @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// 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___UTILITY_LAZY_SYNTH_THREE_WAY_COMPARATOR_H +#define _LIBCPP___UTILITY_LAZY_SYNTH_THREE_WAY_COMPARATOR_H + +#include <__config> +#include <__type_traits/desugars_to.h> +#include <__type_traits/enable_if.h> +#include <__utility/default_three_way_comparator.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +// This file implements a __lazy_synth_three_way_comparator, which tries to build an efficient three way comparison from +// a binary comparator. That is done in multiple steps: +// 1) Check whether the comparator desugars to a less-than operator +// If that is the case, check whether there exists a specialization of `__default_three_way_comparator`, which +// can be specialized to implement a three way comparator for the specific types. +// 2) Fall back to doing a lazy less than/greater than comparison + +_LIBCPP_BEGIN_NAMESPACE_STD + +template <class _Comparator, class _LHS, class _RHS> +struct __lazy_compare_result { + const _Comparator& __comp_; + const _LHS& __lhs_; + const _RHS& __rhs_; + + _LIBCPP_HIDE_FROM_ABI + __lazy_compare_result(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp, + _LIBCPP_CTOR_LIFETIMEBOUND const _LHS& __lhs, + _LIBCPP_CTOR_LIFETIMEBOUND const _RHS& __rhs) + : __comp_(__comp), __lhs_(__lhs), __rhs_(__rhs) {} + + _LIBCPP_HIDE_FROM_ABI bool __less() const { return __comp_(__lhs_, __rhs_); } + _LIBCPP_HIDE_FROM_ABI bool __greater() const { return __comp_(__rhs_, __lhs_); } +}; + +// This class provides three way comparison between _LHS and _RHS as efficiently as possible. This can be specialized if +// a comparator only compares part of the object, potentially allowing an efficient three way comparison between the +// subobjects. The specialization should use the __lazy_synth_three_way_comparator for the subobjects to achieve this. +template <class _Comparator, class _LHS, class _RHS, class = void> +struct __lazy_synth_three_way_comparator { + const _Comparator& __comp_; + + _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp) + : __comp_(__comp) {} + + _LIBCPP_HIDE_FROM_ABI __lazy_compare_result<_Comparator, _LHS, _RHS> + operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) const { + return __lazy_compare_result<_Comparator, _LHS, _RHS>(__comp_, __lhs, __rhs); + } +}; + +struct __eager_compare_result { + int __res_; + + _LIBCPP_HIDE_FROM_ABI explicit __eager_compare_result(int __res) : __res_(__res) {} + + _LIBCPP_HIDE_FROM_ABI bool __less() const { return __res_ < 0; } + _LIBCPP_HIDE_FROM_ABI bool __greater() const { return __res_ > 0; } +}; + +template <class _Comparator, class _LHS, class _RHS> +struct __lazy_synth_three_way_comparator<_Comparator, + _LHS, + _RHS, + __enable_if_t<__desugars_to_v<__less_tag, _Comparator, _LHS, _RHS> && + __has_default_three_way_comparator_v<_LHS, _RHS> > > { + // This lifetimebound annotation is technically incorrect, but other specializations actually capture the lifetime of + // the comparator. + _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {} + + // Same comment as above. + _LIBCPP_HIDE_FROM_ABI static __eager_compare_result + operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) { + return __eager_compare_result(__default_three_way_comparator<_LHS, _RHS>()(__lhs, __rhs)); + } +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___UTILITY_LAZY_SYNTH_THREE_WAY_COMPARATOR_H diff --git a/libcxx/include/map b/libcxx/include/map index a734df671fd6..f428c781e503 100644 --- a/libcxx/include/map +++ b/libcxx/include/map @@ -603,6 +603,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred); // C++20 # include <__type_traits/remove_const.h> # include <__type_traits/type_identity.h> # include <__utility/forward.h> +# include <__utility/lazy_synth_three_way_comparator.h> # include <__utility/pair.h> # include <__utility/piecewise_construct.h> # include <__utility/swap.h> @@ -702,6 +703,50 @@ public: # endif }; +# if _LIBCPP_STD_VER >= 14 +template <class _MapValueT, class _Key, class _Compare> +struct __lazy_synth_three_way_comparator<__map_value_compare<_Key, _MapValueT, _Compare>, _MapValueT, _MapValueT> { + __lazy_synth_three_way_comparator<_Compare, _Key, _Key> __comp_; + + __lazy_synth_three_way_comparator( + _LIBCPP_CTOR_LIFETIMEBOUND const __map_value_compare<_Key, _MapValueT, _Compare>& __comp) + : __comp_(__comp.key_comp()) {} + + _LIBCPP_HIDE_FROM_ABI auto + operator()(_LIBCPP_LIFETIMEBOUND const _MapValueT& __lhs, _LIBCPP_LIFETIMEBOUND const _MapValueT& __rhs) const { + return __comp_(__lhs.first, __rhs.first); + } +}; + +template <class _MapValueT, class _Key, class _TransparentKey, class _Compare> +struct __lazy_synth_three_way_comparator<__map_value_compare<_Key, _MapValueT, _Compare>, _TransparentKey, _MapValueT> { + __lazy_synth_three_way_comparator<_Compare, _TransparentKey, _Key> __comp_; + + __lazy_synth_three_way_comparator( + _LIBCPP_CTOR_LIFETIMEBOUND const __map_value_compare<_Key, _MapValueT, _Compare>& __comp) + : __comp_(__comp.key_comp()) {} + + _LIBCPP_HIDE_FROM_ABI auto + operator()(_LIBCPP_LIFETIMEBOUND const _TransparentKey& __lhs, _LIBCPP_LIFETIMEBOUND const _MapValueT& __rhs) const { + return __comp_(__lhs, __rhs.first); + } +}; + +template <class _MapValueT, class _Key, class _TransparentKey, class _Compare> +struct __lazy_synth_three_way_comparator<__map_value_compare<_Key, _MapValueT, _Compare>, _MapValueT, _TransparentKey> { + __lazy_synth_three_way_comparator<_Compare, _Key, _TransparentKey> __comp_; + + __lazy_synth_three_way_comparator( + _LIBCPP_CTOR_LIFETIMEBOUND const __map_value_compare<_Key, _MapValueT, _Compare>& __comp) + : __comp_(__comp.key_comp()) {} + + _LIBCPP_HIDE_FROM_ABI auto + operator()(_LIBCPP_LIFETIMEBOUND const _MapValueT& __lhs, _LIBCPP_LIFETIMEBOUND const _TransparentKey& __rhs) const { + return __comp_(__lhs.first, __rhs); + } +}; +# endif // _LIBCPP_STD_VER >= 14 + template <class _Key, class _CP, class _Compare, bool __b> inline _LIBCPP_HIDE_FROM_ABI void swap(__map_value_compare<_Key, _CP, _Compare, __b>& __x, __map_value_compare<_Key, _CP, _Compare, __b>& __y) diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 7ac7b510f826..63cf8e847751 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -2144,41 +2144,43 @@ module std [system] { } module utility { - module as_const { header "__utility/as_const.h" } - module as_lvalue { header "__utility/as_lvalue.h" } - module auto_cast { + module as_const { header "__utility/as_const.h" } + module as_lvalue { header "__utility/as_lvalue.h" } + module auto_cast { header "__utility/auto_cast.h" export std_core.type_traits.decay // the macro expansion uses that trait } - module cmp { header "__utility/cmp.h" } - module convert_to_integral { header "__utility/convert_to_integral.h" } - module element_count { header "__utility/element_count.h" } - module exception_guard { header "__utility/exception_guard.h" } - module exchange { header "__utility/exchange.h" } - module forward_like { header "__utility/forward_like.h" } - module in_place { + module cmp { header "__utility/cmp.h" } + module convert_to_integral { header "__utility/convert_to_integral.h" } + module default_three_way_comparator { header "__utility/default_three_way_comparator.h" } + module element_count { header "__utility/element_count.h" } + module exception_guard { header "__utility/exception_guard.h" } + module exchange { header "__utility/exchange.h" } + module forward_like { header "__utility/forward_like.h" } + module in_place { header "__utility/in_place.h" export std_core.type_traits.integral_constant } - module integer_sequence { header "__utility/integer_sequence.h" } - module is_pointer_in_range { header "__utility/is_pointer_in_range.h" } - module is_valid_range { header "__utility/is_valid_range.h" } - module move { header "__utility/move.h" } - module no_destroy { header "__utility/no_destroy.h" } - module pair { + module integer_sequence { header "__utility/integer_sequence.h" } + module is_pointer_in_range { header "__utility/is_pointer_in_range.h" } + module is_valid_range { header "__utility/is_valid_range.h" } + module lazy_synth_three_way_comparator { header "__utility/lazy_synth_three_way_comparator.h" } + module move { header "__utility/move.h" } + module no_destroy { header "__utility/no_destroy.h" } + module pair { header "__utility/pair.h" export std.utility.piecewise_construct } - module piecewise_construct { header "__utility/piecewise_construct.h" } - module priority_tag { header "__utility/priority_tag.h" } - module private_constructor_tag { header "__utility/private_constructor_tag.h" } - module rel_ops { header "__utility/rel_ops.h" } - module scope_guard { header "__utility/scope_guard.h" } - module small_buffer { header "__utility/small_buffer.h" } - module swap { header "__utility/swap.h" } - module to_underlying { header "__utility/to_underlying.h" } - module try_key_extraction { header "__utility/try_key_extraction.h" } - module unreachable { header "__utility/unreachable.h" } + module piecewise_construct { header "__utility/piecewise_construct.h" } + module priority_tag { header "__utility/priority_tag.h" } + module private_constructor_tag { header "__utility/private_constructor_tag.h" } + module rel_ops { header "__utility/rel_ops.h" } + module scope_guard { header "__utility/scope_guard.h" } + module small_buffer { header "__utility/small_buffer.h" } + module swap { header "__utility/swap.h" } + module to_underlying { header "__utility/to_underlying.h" } + module try_key_extraction { header "__utility/try_key_extraction.h" } + module unreachable { header "__utility/unreachable.h" } header "utility" export * diff --git a/libcxx/include/string b/libcxx/include/string index 19a538d96e85..0abdfebcb863 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -639,13 +639,12 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len ); # include <__type_traits/remove_cvref.h> # include <__type_traits/void_t.h> # include <__utility/auto_cast.h> -# include <__utility/declval.h> +# include <__utility/default_three_way_comparator.h> # include <__utility/forward.h> # include <__utility/is_pointer_in_range.h> # include <__utility/move.h> # include <__utility/scope_guard.h> # include <__utility/swap.h> -# include <__utility/unreachable.h> # include <climits> # include <cstdio> // EOF # include <cstring> @@ -966,7 +965,7 @@ private: std::__wrap_iter<const_pointer>(__get_pointer() + size())); # else return const_iterator(__p); -# endif // _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING +# endif // _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING } public: @@ -2522,6 +2521,19 @@ _LIBCPP_STRING_V1_EXTERN_TEMPLATE_LIST(_LIBCPP_DECLARE, wchar_t) # endif # undef _LIBCPP_DECLARE +template <class _CharT, class _Traits, class _Alloc> +struct __default_three_way_comparator<basic_string<_CharT, _Traits, _Alloc>, basic_string<_CharT, _Traits, _Alloc> > { + using __string_t _LIBCPP_NODEBUG = basic_string<_CharT, _Traits, _Alloc>; + + _LIBCPP_HIDE_FROM_ABI static int operator()(const __string_t& __lhs, const __string_t& __rhs) { + auto __min_len = std::min(__lhs.size(), __rhs.size()); + auto __ret = _Traits::compare(__lhs.data(), __rhs.data(), __min_len); + if (__ret == 0) + return __lhs.size() == __rhs.size() ? 0 : __lhs.size() < __rhs.size() ? -1 : 1; + return __ret; + } +}; + # if _LIBCPP_STD_VER >= 17 template <class _InputIterator, class _CharT = __iter_value_type<_InputIterator>, diff --git a/libcxx/test/benchmarks/spec.gen.py b/libcxx/test/benchmarks/spec.gen.py new file mode 100644 index 000000000000..eb411823c25b --- /dev/null +++ b/libcxx/test/benchmarks/spec.gen.py @@ -0,0 +1,78 @@ +# ===----------------------------------------------------------------------===## +# +# 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 +# +# ===----------------------------------------------------------------------===## + +# REQUIRES: enable-spec-benchmarks + +# RUN: mkdir -p %T +# RUN: echo "%{cxx}" > %T/cxx.subs +# RUN: echo "%{compile_flags}" > %T/compile_flags.subs +# RUN: echo "%{flags}" > %T/flags.subs +# RUN: echo "%{link_flags}" > %T/link_flags.subs +# RUN: echo "%{spec_dir}" > %T/spec_dir.subs +# RUN: %{python} %s %T +# END. + +import json +import pathlib +import sys + +test_dir = pathlib.Path(sys.argv[1]) +cxx = (test_dir / 'cxx.subs').open().read().strip() +compile_flags = (test_dir / 'compile_flags.subs').open().read().strip() +flags = (test_dir / 'flags.subs').open().read().strip() +link_flags = (test_dir / 'link_flags.subs').open().read().strip() +spec_dir = pathlib.Path((test_dir / 'spec_dir.subs').open().read().strip()) + +# Setup the configuration file +test_dir.mkdir(parents=True, exist_ok=True) +spec_config = test_dir / 'spec-config.cfg' +spec_config.write_text(f""" +default: + ignore_errors = 1 + iterations = 1 + label = spec-stdlib + log_line_width = 4096 + makeflags = --jobs=8 + mean_anyway = 1 + output_format = csv + preenv = 0 + reportable = 0 + tune = base + copies = 1 + threads = 1 + CC = cc -O3 + CXX = {cxx} {compile_flags} {flags} {link_flags} -Wno-error + CC_VERSION_OPTION = --version + CXX_VERSION_OPTION = --version + EXTRA_PORTABILITY = -DSPEC_NO_CXX17_SPECIAL_MATH_FUNCTIONS # because libc++ doesn't implement the special math functions yet +""") + +# Build the list of benchmarks. We take all intrate and fprate benchmarks that contain C++ and +# discard the ones that contain Fortran, since this test suite isn't set up to build Fortran code. +spec_benchmarks = set() +no_fortran = set() +with open(spec_dir / 'benchspec' / 'CPU' / 'intrate_any_cpp.bset', 'r') as f: + spec_benchmarks.update(json.load(f)['benchmarks']) +with open(spec_dir / 'benchspec' / 'CPU' / 'fprate_any_cpp.bset', 'r') as f: + spec_benchmarks.update(json.load(f)['benchmarks']) +with open(spec_dir / 'benchspec' / 'CPU' / 'no_fortran.bset', 'r') as f: + no_fortran.update(json.load(f)['benchmarks']) +spec_benchmarks &= no_fortran + +for benchmark in spec_benchmarks: + print(f'#--- {benchmark}.sh.test') + print(f'RUN: rm -rf %T') # clean up any previous (potentially incomplete) run + print(f'RUN: mkdir %T') + print(f'RUN: cp {spec_config} %T/spec-config.cfg') + print(f'RUN: %{{spec_dir}}/bin/runcpu --config %T/spec-config.cfg --size train --output-root %T --rebuild {benchmark}') + print(f'RUN: rm -rf %T/benchspec') # remove the temporary directory, which can become quite large + + # Parse the results into a LNT-compatible format. This also errors out if there are no CSV files, which + # means that the benchmark didn't run properly (the `runcpu` command above never reports a failure). + print(f'RUN: %{{libcxx-dir}}/utils/parse-spec-results %T/result/CPUv8.001.*.train.csv --output-format=lnt > %T/results.lnt') + print(f'RUN: cat %T/results.lnt') diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp index 7b3f81e21d8a..6bfcb5d4bfcd 100644 --- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp +++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - // <string> // This test ensures that the correct max_size() is returned depending on the platform. diff --git a/libcxx/test/libcxx/algorithms/bad_iterator_traits.verify.cpp b/libcxx/test/libcxx/algorithms/bad_iterator_traits.verify.cpp index a0b5b88bb9f0..63824171a25c 100644 --- a/libcxx/test/libcxx/algorithms/bad_iterator_traits.verify.cpp +++ b/libcxx/test/libcxx/algorithms/bad_iterator_traits.verify.cpp @@ -57,5 +57,5 @@ struct BadIter { // behavior when these iterators are passed to standard algorithms. void test() { std::sort(BadIter(), BadIter()); - //expected-error-re@*:* {{static assertion failed {{.*}}It looks like your iterator's `iterator_traits<It>::reference` does not match the return type of dereferencing the iterator}} + //expected-error-re@*:* 2 {{static assertion failed {{.*}}It looks like your iterator's `iterator_traits<It>::reference` does not match the return type of dereferencing the iterator}} } diff --git a/libcxx/test/libcxx/containers/associative/unord.map/abi.compile.pass.cpp b/libcxx/test/libcxx/containers/associative/unord.map/abi.compile.pass.cpp index 39fc9fe94a83..a9fafe0452ac 100644 --- a/libcxx/test/libcxx/containers/associative/unord.map/abi.compile.pass.cpp +++ b/libcxx/test/libcxx/containers/associative/unord.map/abi.compile.pass.cpp @@ -209,9 +209,9 @@ static_assert(TEST_ALIGNOF(unordered_map_alloc<char, final_small_iter_allocator< ""); static_assert(sizeof(std::unordered_map<int, int, FinalHash, UnalignedEqualTo>) == 24, ""); -static_assert(sizeof(std::unordered_map<int, int, FinalHash, FinalEqualTo>) == 24, ""); -static_assert(sizeof(std::unordered_map<int, int, NonEmptyHash, FinalEqualTo>) == 28, ""); -static_assert(sizeof(std::unordered_map<int, int, NonEmptyHash, NonEmptyEqualTo>) == 32, ""); +static_assert(sizeof(std::unordered_map<int, int, FinalHash, FinalEqualTo>) == 28, ""); +static_assert(sizeof(std::unordered_map<int, int, NonEmptyHash, FinalEqualTo>) == 32, ""); +static_assert(sizeof(std::unordered_map<int, int, NonEmptyHash, NonEmptyEqualTo>) == 36, ""); static_assert(TEST_ALIGNOF(std::unordered_map<int, int, FinalHash, UnalignedEqualTo>) == 4, ""); static_assert(TEST_ALIGNOF(std::unordered_map<int, int, FinalHash, FinalEqualTo>) == 4, ""); diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/detected_t.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/detected_t.pass.cpp index b64b86f1b05e..4f9f26cb4587 100644 --- a/libcxx/test/std/experimental/utilities/meta/meta.detect/detected_t.pass.cpp +++ b/libcxx/test/std/experimental/utilities/meta/meta.detect/detected_t.pass.cpp @@ -11,6 +11,7 @@ #include <experimental/type_traits> #include <string> +#include <utility> #include "test_macros.h" diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected.pass.cpp index 4a977917f1ec..6960c71abb33 100644 --- a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected.pass.cpp +++ b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected.pass.cpp @@ -11,6 +11,7 @@ #include <experimental/type_traits> #include <string> +#include <utility> #include "test_macros.h" diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_convertible.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_convertible.pass.cpp index 6906461f1edc..de1d223b9189 100644 --- a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_convertible.pass.cpp +++ b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_convertible.pass.cpp @@ -11,6 +11,7 @@ #include <experimental/type_traits> #include <string> +#include <utility> #include "test_macros.h" diff --git a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_exact.pass.cpp b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_exact.pass.cpp index 500187e86dd0..2161c11e7d2c 100644 --- a/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_exact.pass.cpp +++ b/libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_exact.pass.cpp @@ -11,6 +11,7 @@ #include <experimental/type_traits> #include <string> +#include <utility> #include "test_macros.h" diff --git a/libcxx/test/std/input.output/iostreams.base/fpos/fpos.operations/fpos.pass.cpp b/libcxx/test/std/input.output/iostreams.base/fpos/fpos.operations/fpos.pass.cpp index c3d3e0cfc411..8e2f84fba0de 100644 --- a/libcxx/test/std/input.output/iostreams.base/fpos/fpos.operations/fpos.pass.cpp +++ b/libcxx/test/std/input.output/iostreams.base/fpos/fpos.operations/fpos.pass.cpp @@ -14,6 +14,7 @@ #include <cassert> #include <ios> #include <type_traits> +#include <utility> #include "test_macros.h" diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp index 8fe74cdaca5e..0b7a38e5104c 100644 --- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp +++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp @@ -16,8 +16,6 @@ // Ensure that money_get::do_get correct works when the input doesn't fit into the stack buffer // (100 characters currently). -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - #include <cassert> #include <cstddef> #include <ios> diff --git a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp index ac660d8fe994..45a52acab464 100644 --- a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp @@ -8,8 +8,6 @@ // UNSUPPORTED: no-exceptions -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - // <string> // size_type max_size() const; // constexpr since C++20 diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp index 425a481b7163..8a13f11cf156 100644 --- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - // <string> // basic_string<charT,traits,Allocator>& diff --git a/libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp index ea5b63d2804a..121705f2e3a9 100644 --- a/libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp +++ b/libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp @@ -8,8 +8,6 @@ // UNSUPPORTED: no-threads -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - // <mutex> // struct once_flag; diff --git a/libcxx/test/support/any_helpers.h b/libcxx/test/support/any_helpers.h index 0e5563142d58..66f393b108cc 100644 --- a/libcxx/test/support/any_helpers.h +++ b/libcxx/test/support/any_helpers.h @@ -9,9 +9,10 @@ #ifndef ANY_HELPERS_H #define ANY_HELPERS_H +#include <cassert> #include <typeinfo> #include <type_traits> -#include <cassert> +#include <utility> namespace std { namespace experimental {} } diff --git a/libcxx/utils/generate_libcxx_cppm_in.py b/libcxx/utils/generate_libcxx_cppm_in.py index 39076a61b55b..26d680a0db31 100644 --- a/libcxx/utils/generate_libcxx_cppm_in.py +++ b/libcxx/utils/generate_libcxx_cppm_in.py @@ -9,11 +9,19 @@ import os.path import sys -from libcxx.header_information import module_c_headers, module_headers, header_restrictions, headers_not_available, libcxx_root +from libcxx.header_information import ( + module_c_headers, + module_headers, + header_restrictions, + headers_not_available, + libcxx_root, +) def write_file(module): - with open(libcxx_root / "modules" / f"{module}.cppm.in", "w") as module_cpp_in: + with open( + libcxx_root / "modules" / f"{module}.cppm.in", "w", encoding="utf-8" + ) as module_cpp_in: module_cpp_in.write( """\ // -*- C++ -*- diff --git a/libcxx/utils/libcxx/test/format.py b/libcxx/utils/libcxx/test/format.py index a0b7b5bdb5b9..5765afec399c 100644 --- a/libcxx/utils/libcxx/test/format.py +++ b/libcxx/utils/libcxx/test/format.py @@ -12,6 +12,8 @@ import lit.formats import os import re +THIS_FILE = os.path.abspath(__file__) +LIBCXX_UTILS = os.path.dirname(os.path.dirname(os.path.dirname(THIS_FILE))) def _getTempPaths(test): """ @@ -353,6 +355,8 @@ class CxxStandardLibraryTest(lit.formats.FileBasedTest): ] if "enable-benchmarks=run" in test.config.available_features: steps += ["%dbg(EXECUTED AS) %{exec} %t.exe --benchmark_out=%T/benchmark-result.json --benchmark_out_format=json"] + parse_results = os.path.join(LIBCXX_UTILS, 'parse-google-benchmark-results') + steps += [f"{parse_results} %T/benchmark-result.json --output-format=lnt > %T/results.lnt"] return self._executeShTest(test, litConfig, steps) elif re.search('[.]gen[.][^.]+$', filename): # This only happens when a generator test is not supported return self._executeShTest(test, litConfig, []) diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py index 81c613421a46..6f013a75195a 100644 --- a/libcxx/utils/libcxx/test/params.py +++ b/libcxx/utils/libcxx/test/params.py @@ -375,6 +375,17 @@ DEFAULT_PARAMETERS = [ actions=lambda mode: [AddFeature(f"enable-benchmarks={mode}")], ), Parameter( + name="spec_dir", + type=str, + default="none", + help="Path to the SPEC benchmarks. This is required in order to run the SPEC benchmarks as part of " + "the libc++ test suite. If provided, the appropriate SPEC toolset must already be built and installed.", + actions=lambda spec_dir: [ + AddSubstitution("%{spec_dir}", spec_dir), + AddFeature('enable-spec-benchmarks') + ] if spec_dir != "none" else [], + ), + Parameter( name="long_tests", choices=[True, False], type=bool, diff --git a/libcxx/utils/parse-google-benchmark-results b/libcxx/utils/parse-google-benchmark-results new file mode 100755 index 000000000000..280c8045db6c --- /dev/null +++ b/libcxx/utils/parse-google-benchmark-results @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +import argparse +import csv +import json +import sys + +def main(argv): + parser = argparse.ArgumentParser( + prog='parse-google-benchmark-results', + description='Parse Google Benchmark result files (in JSON format) into CSV or LNT compatible output.') + parser.add_argument('filename', type=argparse.FileType('r'), nargs='+', + help='One of more JSON files to extract the results from. The results parsed from each ' + 'file are concatenated together.') + parser.add_argument('--timing', type=str, choices=['real_time', 'cpu_time'], default='real_time', + help='The timing to extract from the Google Benchmark results. This can either be the ' + '"real time" or the "CPU time". Default is "real time".') + parser.add_argument('--output-format', type=str, choices=['csv', 'lnt'], default='csv', + help='The desired output format for the data. `csv` is CSV format and `lnt` is a format compatible with ' + '`lnt importreport` (see https://llvm.org/docs/lnt/importing_data.html#importing-data-in-a-text-file).') + args = parser.parse_args(argv) + + # Parse the data from all files, aggregating the results + headers = ['Benchmark', args.timing] + rows = [] + for file in args.filename: + js = json.load(file) + for bm in js['benchmarks']: + row = [bm['name'], bm[args.timing]] + rows.append(row) + + # Print the results in the right format + if args.output_format == 'csv': + writer = csv.writer(sys.stdout) + writer.writerow(headers) + for row in rows: + writer.writerow(row) + elif args.output_format == 'lnt': + benchmark = headers.index('Benchmark') + time = headers.index(args.timing) + for row in rows: + print(f'{row[benchmark].replace(".", "_")}.execution_time {row[time]}') + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/libcxx/utils/parse-spec-results b/libcxx/utils/parse-spec-results new file mode 100755 index 000000000000..3aff206f8959 --- /dev/null +++ b/libcxx/utils/parse-spec-results @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 + +import argparse +import csv +import sys + +def parse_table(rows, table_title): + """ + Parse a CSV table out of an iterator over rows. + + Return a tuple containing (extracted headers, extracted rows). + """ + in_table = False + rows_iter = iter(rows) + extracted = [] + headers = None + while True: + try: + row = next(rows_iter) + except StopIteration: + break + + if not in_table and row == [table_title]: + in_table = True + next_row = next(rows_iter) + assert next_row == [], f'There should be an empty row after the title of the table, found {next_row}' + headers = next(rows_iter) # Extract the headers + continue + + elif in_table and row == []: # An empty row marks the end of the table + in_table = False + break + + elif in_table: + extracted.append(row) + + assert len(extracted) != 0, f'Could not extract rows from the table, this is suspicious. Table title was {table_title}' + assert headers is not None, f'Could not extract headers from the table, this is suspicious. Table title was {table_title}' + + return (headers, extracted) + +def main(argv): + parser = argparse.ArgumentParser( + prog='parse-spec-results', + description='Parse SPEC result files (in CSV format) and extract the selected result table, in the selected format.') + parser.add_argument('filename', type=argparse.FileType('r'), nargs='+', + help='One of more CSV files to extract the results from. The results parsed from each file are concatenated ' + 'together.') + parser.add_argument('--table', type=str, choices=['full', 'selected'], default='full', + help='The name of the table to extract from SPEC results. `full` means extracting the Full Results Table ' + 'and `selected` means extracting the Selected Results Table. Default is `full`.') + parser.add_argument('--output-format', type=str, choices=['csv', 'lnt'], default='csv', + help='The desired output format for the data. `csv` is CSV format and `lnt` is a format compatible with ' + '`lnt importreport` (see https://llvm.org/docs/lnt/importing_data.html#importing-data-in-a-text-file).') + parser.add_argument('--extract', type=str, + help='A comma-separated list of headers to extract from the table. If provided, only the data associated to ' + 'those headers will be present in the resulting data. Invalid header names are diagnosed. Please make ' + 'sure to use appropriate quoting for header names that contain spaces. This option only makes sense ' + 'when the output format is CSV.') + parser.add_argument('--keep-not-run', action='store_true', + help='Keep entries whose \'Base Status\' is marked as \'NR\', aka \'Not Run\'. By default, such entries are discarded.') + args = parser.parse_args(argv) + + if args.table == 'full': + table_title = 'Full Results Table' + elif args.table == 'selected': + table_title = 'Selected Results Table' + + # Parse the headers and the rows in each file, aggregating all the results + headers = None + rows = [] + for file in args.filename: + reader = csv.reader(file) + (parsed_headers, parsed_rows) = parse_table(reader, table_title) + assert headers is None or headers == parsed_headers, f'Found files with different headers: {headers} and {parsed_headers}' + headers = parsed_headers + rows.extend(parsed_rows) + + # Remove rows that were not run unless we were asked to keep them + if not args.keep_not_run: + not_run = headers.index('Base Status') + rows = [row for row in rows if row[not_run] != 'NR'] + + if args.extract is not None: + if args.output_format != 'csv': + raise RuntimeError('Passing --extract requires the output format to be csv') + for h in args.extract.split(','): + if h not in headers: + raise RuntimeError(f'Header name {h} was not present in the parsed headers {headers}') + + extracted_fields = [headers.index(h) for h in args.extract.split(',')] + headers = [headers[i] for i in extracted_fields] + rows = [[row[i] for i in extracted_fields] for row in rows] + + # Print the results in the right format + if args.output_format == 'csv': + writer = csv.writer(sys.stdout) + writer.writerow(headers) + for row in rows: + writer.writerow(row) + elif args.output_format == 'lnt': + benchmark = headers.index('Benchmark') + time = headers.index('Est. Base Run Time') + for row in rows: + print(f'{row[benchmark].replace(".", "_")}.execution_time {row[time]}') + +if __name__ == '__main__': + main(sys.argv[1:]) |
