summaryrefslogtreecommitdiff
path: root/libcxx
diff options
context:
space:
mode:
authorAiden Grossman <aidengrossman@google.com>2025-09-09 03:19:14 +0000
committerAiden Grossman <aidengrossman@google.com>2025-09-09 03:19:14 +0000
commit8c3530cbde51ee85adff6ab20771367ada3a9b49 (patch)
tree74e1b33408f9c293c5408951ac3b1053dbd6210c /libcxx
parent4b6cd10bc5039e5bfa60a74cdac744969332adb2 (diff)
parent3b1ca5e7c5b94b10e3da554a060459a1a1e24495 (diff)
Created using spr 1.3.6 [skip ci]
Diffstat (limited to 'libcxx')
-rw-r--r--libcxx/include/CMakeLists.txt2
-rw-r--r--libcxx/include/__algorithm/make_heap.h20
-rw-r--r--libcxx/include/__algorithm/partial_sort.h2
-rw-r--r--libcxx/include/__algorithm/partial_sort_copy.h2
-rw-r--r--libcxx/include/__algorithm/sift_down.h37
-rw-r--r--libcxx/include/__config7
-rw-r--r--libcxx/include/__cxx03/__mutex/once_flag.h5
-rw-r--r--libcxx/include/__cxx03/locale10
-rw-r--r--libcxx/include/__cxx03/string22
-rw-r--r--libcxx/include/__random/binomial_distribution.h18
-rw-r--r--libcxx/include/__tree120
-rw-r--r--libcxx/include/__utility/default_three_way_comparator.h50
-rw-r--r--libcxx/include/__utility/lazy_synth_three_way_comparator.h90
-rw-r--r--libcxx/include/map45
-rw-r--r--libcxx/include/module.modulemap.in54
-rw-r--r--libcxx/include/string18
-rw-r--r--libcxx/test/benchmarks/spec.gen.py78
-rw-r--r--libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp2
-rw-r--r--libcxx/test/libcxx/algorithms/bad_iterator_traits.verify.cpp2
-rw-r--r--libcxx/test/libcxx/containers/associative/unord.map/abi.compile.pass.cpp6
-rw-r--r--libcxx/test/std/experimental/utilities/meta/meta.detect/detected_t.pass.cpp1
-rw-r--r--libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected.pass.cpp1
-rw-r--r--libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_convertible.pass.cpp1
-rw-r--r--libcxx/test/std/experimental/utilities/meta/meta.detect/is_detected_exact.pass.cpp1
-rw-r--r--libcxx/test/std/input.output/iostreams.base/fpos/fpos.operations/fpos.pass.cpp1
-rw-r--r--libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_overlong.pass.cpp2
-rw-r--r--libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp2
-rw-r--r--libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size.pass.cpp2
-rw-r--r--libcxx/test/std/thread/thread.mutex/thread.once/thread.once.callonce/call_once.pass.cpp2
-rw-r--r--libcxx/test/support/any_helpers.h3
-rw-r--r--libcxx/utils/generate_libcxx_cppm_in.py12
-rw-r--r--libcxx/utils/libcxx/test/format.py4
-rw-r--r--libcxx/utils/libcxx/test/params.py11
-rwxr-xr-xlibcxx/utils/parse-google-benchmark-results45
-rwxr-xr-xlibcxx/utils/parse-spec-results108
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:])