diff options
Diffstat (limited to 'libcxx')
24 files changed, 502 insertions, 82 deletions
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 5e8fe2e..0fd7e53 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -71,6 +71,9 @@ Improvements and New Features - The ``std::distance`` and ``std::ranges::distance`` algorithms have been optimized for segmented iterators (e.g., ``std::join_view`` iterators), reducing the complexity from ``O(n)`` to ``O(n / segment_size)``. Benchmarks show performance improvements of over 1600x in favorable cases with large segment sizes (e.g., 1024). +- The ``std::{fill, fill_n}`` and ``std::ranges::{fill, fill_n}`` algorithms have been optimized for segmented iterators, + resulting in a performance improvement of at least 10x for ``std::deque<int>`` iterators and + ``std::join_view<std::vector<std::vector<int>>>`` iterators. Deprecations and Removals ------------------------- diff --git a/libcxx/include/__algorithm/fill.h b/libcxx/include/__algorithm/fill.h index 1ce3eadb..328ebb6 100644 --- a/libcxx/include/__algorithm/fill.h +++ b/libcxx/include/__algorithm/fill.h @@ -10,8 +10,11 @@ #define _LIBCPP___ALGORITHM_FILL_H #include <__algorithm/fill_n.h> +#include <__algorithm/for_each_segment.h> #include <__config> #include <__iterator/iterator_traits.h> +#include <__iterator/segmented_iterator.h> +#include <__type_traits/enable_if.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -21,23 +24,40 @@ _LIBCPP_BEGIN_NAMESPACE_STD // fill isn't specialized for std::memset, because the compiler already optimizes the loop to a call to std::memset. -template <class _ForwardIterator, class _Tp> -inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void -__fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, forward_iterator_tag) { +template <class _ForwardIterator, class _Sentinel, class _Tp> +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _ForwardIterator +__fill(_ForwardIterator __first, _Sentinel __last, const _Tp& __value) { for (; __first != __last; ++__first) *__first = __value; + return __first; } -template <class _RandomAccessIterator, class _Tp> -inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void -__fill(_RandomAccessIterator __first, _RandomAccessIterator __last, const _Tp& __value, random_access_iterator_tag) { - std::fill_n(__first, __last - __first, __value); +template <class _RandomAccessIterator, + class _Tp, + __enable_if_t<__has_random_access_iterator_category<_RandomAccessIterator>::value && + !__is_segmented_iterator_v<_RandomAccessIterator>, + int> = 0> +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator +__fill(_RandomAccessIterator __first, _RandomAccessIterator __last, const _Tp& __value) { + return std::__fill_n(__first, __last - __first, __value); +} + +#ifndef _LIBCPP_CXX03_LANG +template <class _SegmentedIterator, class _Tp, __enable_if_t<__is_segmented_iterator_v<_SegmentedIterator>, int> = 0> +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 +_SegmentedIterator __fill(_SegmentedIterator __first, _SegmentedIterator __last, const _Tp& __value) { + using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator; + std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) { + std::__fill(__lfirst, __llast, __value); + }); + return __last; } +#endif // !_LIBCPP_CXX03_LANG template <class _ForwardIterator, class _Tp> inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) { - std::__fill(__first, __last, __value, typename iterator_traits<_ForwardIterator>::iterator_category()); + std::__fill(__first, __last, __value); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/fill_n.h b/libcxx/include/__algorithm/fill_n.h index 0da78e1..2bfacf3 100644 --- a/libcxx/include/__algorithm/fill_n.h +++ b/libcxx/include/__algorithm/fill_n.h @@ -9,10 +9,17 @@ #ifndef _LIBCPP___ALGORITHM_FILL_N_H #define _LIBCPP___ALGORITHM_FILL_N_H +#include <__algorithm/for_each_n_segment.h> #include <__algorithm/min.h> #include <__config> #include <__fwd/bit_reference.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/segmented_iterator.h> #include <__memory/pointer_traits.h> +#include <__type_traits/conjunction.h> +#include <__type_traits/enable_if.h> +#include <__type_traits/integral_constant.h> +#include <__type_traits/negation.h> #include <__utility/convert_to_integral.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -26,9 +33,38 @@ _LIBCPP_BEGIN_NAMESPACE_STD // fill_n isn't specialized for std::memset, because the compiler already optimizes the loop to a call to std::memset. -template <class _OutputIterator, class _Size, class _Tp> +template <class _OutputIterator, + class _Size, + class _Tp +#ifndef _LIBCPP_CXX03_LANG + , + __enable_if_t<!_And<_BoolConstant<__is_segmented_iterator_v<_OutputIterator>>, + __has_random_access_local_iterator<_OutputIterator>>::value, + int> = 0 +#endif + > inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator -__fill_n(_OutputIterator __first, _Size __n, const _Tp& __value); +__fill_n(_OutputIterator __first, _Size __n, const _Tp& __value) { + for (; __n > 0; ++__first, (void)--__n) + *__first = __value; + return __first; +} + +#ifndef _LIBCPP_CXX03_LANG +template < class _OutputIterator, + class _Size, + class _Tp, + __enable_if_t<_And<_BoolConstant<__is_segmented_iterator_v<_OutputIterator>>, + __has_random_access_local_iterator<_OutputIterator>>::value, + int> = 0> +inline _LIBCPP_HIDE_FROM_ABI +_LIBCPP_CONSTEXPR_SINCE_CXX14 _OutputIterator __fill_n(_OutputIterator __first, _Size __n, const _Tp& __value) { + using __local_iterator_t = typename __segmented_iterator_traits<_OutputIterator>::__local_iterator; + return std::__for_each_n_segment(__first, __n, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) { + std::__fill_n(__lfirst, __llast - __lfirst, __value); + }); +} +#endif // !_LIBCPP_CXX03_LANG template <bool _FillVal, class _Cp> _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void @@ -70,14 +106,6 @@ __fill_n(__bit_iterator<_Cp, false> __first, _Size __n, const bool& __value) { template <class _OutputIterator, class _Size, class _Tp> inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator -__fill_n(_OutputIterator __first, _Size __n, const _Tp& __value) { - for (; __n > 0; ++__first, (void)--__n) - *__first = __value; - return __first; -} - -template <class _OutputIterator, class _Size, class _Tp> -inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator fill_n(_OutputIterator __first, _Size __n, const _Tp& __value) { return std::__fill_n(__first, std::__convert_to_integral(__n), __value); } diff --git a/libcxx/include/__algorithm/ranges_fill.h b/libcxx/include/__algorithm/ranges_fill.h index c248009..814ae63 100644 --- a/libcxx/include/__algorithm/ranges_fill.h +++ b/libcxx/include/__algorithm/ranges_fill.h @@ -9,12 +9,14 @@ #ifndef _LIBCPP___ALGORITHM_RANGES_FILL_H #define _LIBCPP___ALGORITHM_RANGES_FILL_H -#include <__algorithm/ranges_fill_n.h> +#include <__algorithm/fill.h> +#include <__algorithm/fill_n.h> #include <__config> #include <__iterator/concepts.h> #include <__ranges/access.h> #include <__ranges/concepts.h> #include <__ranges/dangling.h> +#include <__utility/move.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -31,12 +33,11 @@ namespace ranges { struct __fill { template <class _Type, output_iterator<const _Type&> _Iter, sentinel_for<_Iter> _Sent> _LIBCPP_HIDE_FROM_ABI constexpr _Iter operator()(_Iter __first, _Sent __last, const _Type& __value) const { - if constexpr (random_access_iterator<_Iter> && sized_sentinel_for<_Sent, _Iter>) { - return ranges::fill_n(__first, __last - __first, __value); + if constexpr (sized_sentinel_for<_Sent, _Iter>) { + auto __n = __last - __first; + return std::__fill_n(std::move(__first), __n, __value); } else { - for (; __first != __last; ++__first) - *__first = __value; - return __first; + return std::__fill(std::move(__first), std::move(__last), __value); } } diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h index 2d33b9c..c9936df 100644 --- a/libcxx/include/__configuration/abi.h +++ b/libcxx/include/__configuration/abi.h @@ -30,8 +30,20 @@ #elif _LIBCPP_ABI_FORCE_MICROSOFT # define _LIBCPP_ABI_MICROSOFT #else +// Windows uses the Microsoft ABI # if defined(_WIN32) && defined(_MSC_VER) # define _LIBCPP_ABI_MICROSOFT + +// 32-bit ARM uses the Itanium ABI with a few differences (array cookies, etc), +// and so does 64-bit ARM on Apple platforms. +# elif defined(__arm__) || (defined(__APPLE__) && defined(__aarch64__)) +# define _LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES + +// Non-Apple 64-bit ARM uses the vanilla Itanium ABI +# elif defined(__aarch64__) +# define _LIBCPP_ABI_ITANIUM + +// We assume that other architectures also use the vanilla Itanium ABI too # else # define _LIBCPP_ABI_ITANIUM # endif diff --git a/libcxx/include/__cxx03/__bit_reference b/libcxx/include/__cxx03/__bit_reference index 76027e2..ac0005f 100644 --- a/libcxx/include/__cxx03/__bit_reference +++ b/libcxx/include/__cxx03/__bit_reference @@ -167,7 +167,7 @@ _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_aligned( unsigned __clz = __bits_per_word - __first.__ctz_; difference_type __dn = std::min(static_cast<difference_type>(__clz), __n); __n -= __dn; - __storage_type __m = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz - __dn)); + __storage_type __m = (__storage_type(~0) << __first.__ctz_) & (__storage_type(~0) >> (__clz - __dn)); __storage_type __b = *__first.__seg_ & __m; *__result.__seg_ &= ~__m; *__result.__seg_ |= __b; @@ -185,7 +185,7 @@ _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_aligned( // do last word if (__n > 0) { __first.__seg_ += __nw; - __storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n); + __storage_type __m = __storage_type(~0) >> (__bits_per_word - __n); __storage_type __b = *__first.__seg_ & __m; *__result.__seg_ &= ~__m; *__result.__seg_ |= __b; @@ -210,11 +210,11 @@ _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_unaligned( unsigned __clz_f = __bits_per_word - __first.__ctz_; difference_type __dn = std::min(static_cast<difference_type>(__clz_f), __n); __n -= __dn; - __storage_type __m = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz_f - __dn)); + __storage_type __m = (__storage_type(~0) << __first.__ctz_) & (__storage_type(~0) >> (__clz_f - __dn)); __storage_type __b = *__first.__seg_ & __m; unsigned __clz_r = __bits_per_word - __result.__ctz_; __storage_type __ddn = std::min<__storage_type>(__dn, __clz_r); - __m = (~__storage_type(0) << __result.__ctz_) & (~__storage_type(0) >> (__clz_r - __ddn)); + __m = (__storage_type(~0) << __result.__ctz_) & (__storage_type(~0) >> (__clz_r - __ddn)); *__result.__seg_ &= ~__m; if (__result.__ctz_ > __first.__ctz_) *__result.__seg_ |= __b << (__result.__ctz_ - __first.__ctz_); @@ -224,7 +224,7 @@ _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_unaligned( __result.__ctz_ = static_cast<unsigned>((__ddn + __result.__ctz_) % __bits_per_word); __dn -= __ddn; if (__dn > 0) { - __m = ~__storage_type(0) >> (__bits_per_word - __dn); + __m = __storage_type(~0) >> (__bits_per_word - __dn); *__result.__seg_ &= ~__m; *__result.__seg_ |= __b >> (__first.__ctz_ + __ddn); __result.__ctz_ = static_cast<unsigned>(__dn); @@ -235,7 +235,7 @@ _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_unaligned( // __first.__ctz_ == 0; // do middle words unsigned __clz_r = __bits_per_word - __result.__ctz_; - __storage_type __m = ~__storage_type(0) << __result.__ctz_; + __storage_type __m = __storage_type(~0) << __result.__ctz_; for (; __n >= __bits_per_word; __n -= __bits_per_word, ++__first.__seg_) { __storage_type __b = *__first.__seg_; *__result.__seg_ &= ~__m; @@ -246,17 +246,17 @@ _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_unaligned( } // do last word if (__n > 0) { - __m = ~__storage_type(0) >> (__bits_per_word - __n); + __m = __storage_type(~0) >> (__bits_per_word - __n); __storage_type __b = *__first.__seg_ & __m; __storage_type __dn = std::min(__n, static_cast<difference_type>(__clz_r)); - __m = (~__storage_type(0) << __result.__ctz_) & (~__storage_type(0) >> (__clz_r - __dn)); + __m = (__storage_type(~0) << __result.__ctz_) & (__storage_type(~0) >> (__clz_r - __dn)); *__result.__seg_ &= ~__m; *__result.__seg_ |= __b << __result.__ctz_; __result.__seg_ += (__dn + __result.__ctz_) / __bits_per_word; __result.__ctz_ = static_cast<unsigned>((__dn + __result.__ctz_) % __bits_per_word); __n -= __dn; if (__n > 0) { - __m = ~__storage_type(0) >> (__bits_per_word - __n); + __m = __storage_type(~0) >> (__bits_per_word - __n); *__result.__seg_ &= ~__m; *__result.__seg_ |= __b >> __dn; __result.__ctz_ = static_cast<unsigned>(__n); diff --git a/libcxx/include/__cxx03/__verbose_abort b/libcxx/include/__cxx03/__verbose_abort index 4fcfffa..52d1297 100644 --- a/libcxx/include/__cxx03/__verbose_abort +++ b/libcxx/include/__cxx03/__verbose_abort @@ -21,7 +21,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD // This function should never be called directly from the code -- it should only be called through // the _LIBCPP_VERBOSE_ABORT macro. _LIBCPP_NORETURN _LIBCPP_AVAILABILITY_VERBOSE_ABORT _LIBCPP_OVERRIDABLE_FUNC_VIS -_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) void __libcpp_verbose_abort(const char* __format, ...); +_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) void __libcpp_verbose_abort(const char* __format, ...) _NOEXCEPT; // _LIBCPP_VERBOSE_ABORT(format, args...) // diff --git a/libcxx/include/__cxx03/vector b/libcxx/include/__cxx03/vector index dbaa33c..43e82cd 100644 --- a/libcxx/include/__cxx03/vector +++ b/libcxx/include/__cxx03/vector @@ -1630,7 +1630,7 @@ private: return __n * __bits_per_word; } _LIBCPP_HIDE_FROM_ABI static size_type __external_cap_to_internal(size_type __n) _NOEXCEPT { - return (__n - 1) / __bits_per_word + 1; + return __n > 0 ? (__n - 1) / __bits_per_word + 1 : size_type(0); } public: @@ -2142,11 +2142,13 @@ void vector<bool, _Allocator>::reserve(size_type __n) { template <class _Allocator> void vector<bool, _Allocator>::shrink_to_fit() _NOEXCEPT { - if (__external_cap_to_internal(size()) > __cap()) { + if (__external_cap_to_internal(size()) < __cap()) { #ifndef _LIBCPP_HAS_NO_EXCEPTIONS try { #endif // _LIBCPP_HAS_NO_EXCEPTIONS - vector(*this, allocator_type(__alloc())).swap(*this); + vector __v(*this, allocator_type(__alloc())); + if (__v.__cap() < __cap()) + __v.swap(*this); #ifndef _LIBCPP_HAS_NO_EXCEPTIONS } catch (...) { } diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table index 74923ddb..6b65e73 100644 --- a/libcxx/include/__hash_table +++ b/libcxx/include/__hash_table @@ -808,7 +808,7 @@ public: } { __node_holder __h = __construct_node_hash(__hash, std::forward<_Args>(__args2)...); - if (size() + 1 > __bc * max_load_factor() || __bc == 0) { + if (size() + 1 > __bc * max_load_factor()) { __rehash_unique(std::max<size_type>(2 * __bc + !std::__is_hash_power2(__bc), size_type(__math::ceil(float(size() + 1) / max_load_factor())))); __bc = bucket_count(); diff --git a/libcxx/include/__memory/array_cookie.h b/libcxx/include/__memory/array_cookie.h index 806a9e9..be59f36 100644 --- a/libcxx/include/__memory/array_cookie.h +++ b/libcxx/include/__memory/array_cookie.h @@ -13,6 +13,7 @@ #include <__config> #include <__configuration/abi.h> #include <__cstddef/size_t.h> +#include <__memory/addressof.h> #include <__type_traits/integral_constant.h> #include <__type_traits/is_trivially_destructible.h> #include <__type_traits/negation.h> @@ -26,14 +27,15 @@ _LIBCPP_BEGIN_NAMESPACE_STD // Trait representing whether a type requires an array cookie at the start of its allocation when // allocated as `new T[n]` and deallocated as `delete[] array`. // -// Under the Itanium C++ ABI [1], we know that an array cookie is available unless `T` is trivially -// destructible and the call to `operator delete[]` is not a sized operator delete. Under ABIs other -// than the Itanium ABI, we assume there are no array cookies. +// Under the Itanium C++ ABI [1] and the ARM ABI which derives from it, we know that an array cookie is available +// unless `T` is trivially destructible and the call to `operator delete[]` is not a sized operator delete. Under +// other ABIs, we assume there are no array cookies. // // [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies -#ifdef _LIBCPP_ABI_ITANIUM +#if defined(_LIBCPP_ABI_ITANIUM) || defined(_LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES) // TODO: Use a builtin instead -// TODO: We should factor in the choice of the usual deallocation function in this determination. +// TODO: We should factor in the choice of the usual deallocation function in this determination: +// a cookie may be available in more cases but we ignore those for now. template <class _Tp> struct __has_array_cookie : _Not<is_trivially_destructible<_Tp> > {}; #else @@ -41,13 +43,79 @@ template <class _Tp> struct __has_array_cookie : false_type {}; #endif +struct __itanium_array_cookie { + size_t __element_count; +}; + +template <class _Tp> +struct [[__gnu__::__aligned__(_LIBCPP_ALIGNOF(_Tp))]] __arm_array_cookie { + size_t __element_size; + size_t __element_count; +}; + +// Return the element count in the array cookie located before the given pointer. +// +// In the Itanium ABI [1] +// ---------------------- +// The element count is stored immediately before the first element of the array. If the preferred alignment +// of array elements (which is different from the ABI alignment) is more than that of size_t, additional +// padding bytes exist before the array cookie. Assuming array elements of size and alignment 16 bytes, that +// gives us the following layout: +// +// |ooooooooxxxxxxxxaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd| +// ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// | ^^^^^^^^ | +// | | array elements +// padding | +// element count +// +// +// In the Itanium ABI with ARM differences [2] +// ------------------------------------------- +// The array cookie is stored at the very start of the allocation and it has the following form: +// +// struct array_cookie { +// std::size_t element_size; // element_size != 0 +// std::size_t element_count; +// }; +// +// Assuming elements of size and alignment 32 bytes, this gives us the following layout: +// +// |xxxxxxxxXXXXXXXXooooooooooooooooaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| +// ^^^^^^^^ ^^^^^^^^^^^^^^^^ +// | ^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// element size | padding | +// element count array elements +// +// We must be careful to take into account the alignment of the array cookie, which may result in padding +// bytes between the element count and the first element of the array. Note that for ARM, the compiler +// aligns the array cookie using the ABI alignment, not the preferred alignment of array elements. +// +// [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies +// [2]: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-C++-differences template <class _Tp> // Avoid failures when -fsanitize-address-poison-custom-array-cookie is enabled -_LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") size_t __get_array_cookie(_Tp const* __ptr) { +_LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") size_t __get_array_cookie([[__maybe_unused__]] _Tp const* __ptr) { static_assert( __has_array_cookie<_Tp>::value, "Trying to access the array cookie of a type that is not guaranteed to have one"); - size_t const* __cookie = reinterpret_cast<size_t const*>(__ptr) - 1; // TODO: Use a builtin instead - return *__cookie; + +#if defined(_LIBCPP_ABI_ITANIUM) + using _ArrayCookie = __itanium_array_cookie; +#elif defined(_LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES) + using _ArrayCookie = __arm_array_cookie<_Tp>; +#else + static_assert(false, "The array cookie layout is unknown on this ABI"); + struct _ArrayCookie { // dummy definition required to make the function parse + size_t element_count; + }; +#endif + + char const* __array_cookie_start = reinterpret_cast<char const*>(__ptr) - sizeof(_ArrayCookie); + _ArrayCookie __cookie; + // This is necessary to avoid violating strict aliasing. It's valid because _ArrayCookie is an + // implicit lifetime type. + __builtin_memcpy(std::addressof(__cookie), __array_cookie_start, sizeof(_ArrayCookie)); + return __cookie.__element_count; } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__tree b/libcxx/include/__tree index d7d074a0..0738c8c 100644 --- a/libcxx/include/__tree +++ b/libcxx/include/__tree @@ -1119,15 +1119,15 @@ public: _LIBCPP_HIDE_FROM_ABI _InsertReturnType __node_handle_insert_unique(_NodeHandle&&); template <class _NodeHandle> _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_unique(const_iterator, _NodeHandle&&); - template <class _Tree> - _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_unique(_Tree& __source); + template <class _Comp2> + _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source); template <class _NodeHandle> _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_multi(_NodeHandle&&); template <class _NodeHandle> _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_multi(const_iterator, _NodeHandle&&); - template <class _Tree> - _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(_Tree& __source); + template <class _Comp2> + _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source); template <class _NodeHandle> _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const&); @@ -2020,11 +2020,10 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_hand } template <class _Tp, class _Compare, class _Allocator> -template <class _Tree> -_LIBCPP_HIDE_FROM_ABI void __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(_Tree& __source) { - static_assert(is_same<typename _Tree::__node_pointer, __node_pointer>::value, ""); - - for (typename _Tree::iterator __i = __source.begin(); __i != __source.end();) { +template <class _Comp2> +_LIBCPP_HIDE_FROM_ABI void +__tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source) { + for (iterator __i = __source.begin(); __i != __source.end();) { __node_pointer __src_ptr = __i.__get_np(); auto [__parent, __child] = __find_equal(__src_ptr->__get_value()); ++__i; @@ -2065,11 +2064,10 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(const_iterator __h } template <class _Tp, class _Compare, class _Allocator> -template <class _Tree> -_LIBCPP_HIDE_FROM_ABI void __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(_Tree& __source) { - static_assert(is_same<typename _Tree::__node_pointer, __node_pointer>::value, ""); - - for (typename _Tree::iterator __i = __source.begin(); __i != __source.end();) { +template <class _Comp2> +_LIBCPP_HIDE_FROM_ABI void +__tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source) { + for (iterator __i = __source.begin(); __i != __source.end();) { __node_pointer __src_ptr = __i.__get_np(); __end_node_pointer __parent; __node_base_pointer& __child = __find_leaf_high(__parent, __src_ptr->__get_value()); diff --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h index 14dc0c1..68864e2 100644 --- a/libcxx/include/__utility/cmp.h +++ b/libcxx/include/__utility/cmp.h @@ -26,10 +26,18 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER >= 20 +template <typename _Tp, typename _Ip> +concept __comparison_can_promote_to = + sizeof(_Tp) < sizeof(_Ip) || (sizeof(_Tp) == sizeof(_Ip) && __signed_integer<_Tp>); + template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up> _LIBCPP_HIDE_FROM_ABI constexpr bool cmp_equal(_Tp __t, _Up __u) noexcept { if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>) return __t == __u; + else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>) + return static_cast<int>(__t) == static_cast<int>(__u); + else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>) + return static_cast<long long>(__t) == static_cast<long long>(__u); else if constexpr (is_signed_v<_Tp>) return __t < 0 ? false : make_unsigned_t<_Tp>(__t) == __u; else @@ -45,6 +53,10 @@ template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up> _LIBCPP_HIDE_FROM_ABI constexpr bool cmp_less(_Tp __t, _Up __u) noexcept { if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>) return __t < __u; + else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>) + return static_cast<int>(__t) < static_cast<int>(__u); + else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>) + return static_cast<long long>(__t) < static_cast<long long>(__u); else if constexpr (is_signed_v<_Tp>) return __t < 0 ? true : make_unsigned_t<_Tp>(__t) < __u; else diff --git a/libcxx/test/benchmarks/utility/cmp.bench.cpp b/libcxx/test/benchmarks/utility/cmp.bench.cpp new file mode 100644 index 0000000..1ed179a --- /dev/null +++ b/libcxx/test/benchmarks/utility/cmp.bench.cpp @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +#include <utility> +#include "../CartesianBenchmarks.h" +#include "benchmark/benchmark.h" + +namespace { + +enum ValueType : size_t { + SChar, + UChar, + Short, + UShort, + Int, + UInt, + Long, + ULong, + LongLong, + ULongLong, +#ifndef TEST_HAS_NO_INT128 + Int128, + UInt128, +#endif +}; + +struct AllValueTypes : EnumValuesAsTuple<AllValueTypes, ValueType, 6> { + static constexpr const char* Names[] = { + "schar", + "uchar", + "short", + "ushort", + "int", + "uint", + "long", + "ulong", + "longlong", + "ulonglong", +#ifndef TEST_HAS_NO_INT128 + "int128", + "uint128" +#endif + }; +}; + +using TestType = + std::tuple< signed char, + unsigned char, + short, + unsigned short, + int, + unsigned int, + long, + unsigned long, + long long, + unsigned long long +#ifndef TEST_HAS_NO_INT128 + , + __int128_t, + __uint128_t +#endif + >; + +template <typename TType, typename UType> +struct CmpEqual { + static void run(benchmark::State& state) { + using T = std::tuple_element_t<TType::value, TestType>; + using U = std::tuple_element_t<UType::value, TestType>; + + T x1 = T{127}, x2 = T{111}; + U y1 = U{123}, y2 = U{1}; + for (auto _ : state) { + benchmark::DoNotOptimize(x1); + benchmark::DoNotOptimize(x2); + benchmark::DoNotOptimize(y1); + benchmark::DoNotOptimize(y2); + benchmark::DoNotOptimize(std::cmp_equal(x1, y1)); + benchmark::DoNotOptimize(std::cmp_equal(y1, x1)); + benchmark::DoNotOptimize(std::cmp_equal(x1, x1)); + benchmark::DoNotOptimize(std::cmp_equal(y1, y1)); + + benchmark::DoNotOptimize(std::cmp_equal(x2, y2)); + benchmark::DoNotOptimize(std::cmp_equal(y2, x2)); + benchmark::DoNotOptimize(std::cmp_equal(x2, x2)); + benchmark::DoNotOptimize(std::cmp_equal(y2, y2)); + } + } + + static std::string name() { return "BM_CmpEqual" + TType::name() + UType::name(); } +}; + +template <typename TType, typename UType> +struct CmpLess { + static void run(benchmark::State& state) { + using T = std::tuple_element_t<TType::value, TestType>; + using U = std::tuple_element_t<UType::value, TestType>; + + T x1 = T{127}, x2 = T{111}; + U y1 = U{123}, y2 = U{1}; + for (auto _ : state) { + benchmark::DoNotOptimize(x1); + benchmark::DoNotOptimize(x2); + benchmark::DoNotOptimize(y1); + benchmark::DoNotOptimize(y2); + benchmark::DoNotOptimize(std::cmp_less(x1, y1)); + benchmark::DoNotOptimize(std::cmp_less(y1, x1)); + benchmark::DoNotOptimize(std::cmp_less(x1, x1)); + benchmark::DoNotOptimize(std::cmp_less(y1, y1)); + + benchmark::DoNotOptimize(std::cmp_less(x2, y2)); + benchmark::DoNotOptimize(std::cmp_less(y2, x2)); + benchmark::DoNotOptimize(std::cmp_less(x2, x2)); + benchmark::DoNotOptimize(std::cmp_less(y2, y2)); + } + } + + static std::string name() { return "BM_CmpLess" + TType::name() + UType::name(); } +}; + +} // namespace + +int main(int argc, char** argv) { + benchmark::Initialize(&argc, argv); + if (benchmark::ReportUnrecognizedArguments(argc, argv)) + return 1; + + makeCartesianProductBenchmark<CmpEqual, AllValueTypes, AllValueTypes>(); + makeCartesianProductBenchmark<CmpLess, AllValueTypes, AllValueTypes>(); + benchmark::RunSpecifiedBenchmarks(); + + return 0; +} diff --git a/libcxx/test/libcxx-03/assertions/customize_verbose_abort.link-time.pass.cpp b/libcxx/test/libcxx-03/assertions/customize_verbose_abort.link-time.pass.cpp index 390c6b6..3c7a2d4 100644 --- a/libcxx/test/libcxx-03/assertions/customize_verbose_abort.link-time.pass.cpp +++ b/libcxx/test/libcxx-03/assertions/customize_verbose_abort.link-time.pass.cpp @@ -12,9 +12,7 @@ // failures when back-deploying. // XFAIL: availability-verbose_abort-missing -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - -#include <__verbose_abort> +#include <__cxx03/__verbose_abort> #include <cstdlib> void std::__libcpp_verbose_abort(char const*, ...) _NOEXCEPT { std::exit(EXIT_SUCCESS); } diff --git a/libcxx/test/libcxx-03/language.support/support.dynamic/libcpp_deallocate.sh.cpp b/libcxx/test/libcxx-03/language.support/support.dynamic/libcpp_deallocate.sh.cpp index 7ead65c..a9fe04f 100644 --- a/libcxx/test/libcxx-03/language.support/support.dynamic/libcpp_deallocate.sh.cpp +++ b/libcxx/test/libcxx-03/language.support/support.dynamic/libcpp_deallocate.sh.cpp @@ -21,8 +21,6 @@ // GCC doesn't support the aligned-allocation flags. // XFAIL: gcc -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - // RUN: %{build} -faligned-allocation -fsized-deallocation // RUN: %{run} // RUN: %{build} -faligned-allocation -fno-sized-deallocation -DNO_SIZE @@ -40,7 +38,7 @@ TEST_DIAGNOSTIC_PUSH TEST_CLANG_DIAGNOSTIC_IGNORED("-Wprivate-header") -#include <__memory/aligned_alloc.h> +#include <__cxx03/__memory/aligned_alloc.h> TEST_DIAGNOSTIC_POP struct alloc_stats { @@ -138,42 +136,42 @@ void test_libcpp_dealloc() { std::size_t with_size_val = 2; { - std::__libcpp_deallocate_unsized<char>(static_cast<char*>(p), under_align_val); + std::__libcpp_deallocate_unsized(p, under_align_val); assert(stats.expect_plain()); } stats.reset(); #if defined(NO_SIZE) && defined(NO_ALIGN) { - std::__libcpp_deallocate<char>(static_cast<char*>(p), std::__element_count(with_size_val), over_align_val); + std::__libcpp_deallocate(p, with_size_val, over_align_val); assert(stats.expect_plain()); } stats.reset(); #elif defined(NO_SIZE) { - std::__libcpp_deallocate<char>(static_cast<char*>(p), std::__element_count(with_size_val), over_align_val); + std::__libcpp_deallocate(p, with_size_val, over_align_val); assert(stats.expect_align(over_align_val)); } stats.reset(); #elif defined(NO_ALIGN) { - std::__libcpp_deallocate<char>(static_cast<char*>(p), std::__element_count(with_size_val), over_align_val); + std::__libcpp_deallocate(p, with_size_val, over_align_val); assert(stats.expect_size(with_size_val)); } stats.reset(); #else { - std::__libcpp_deallocate<char>(static_cast<char*>(p), std::__element_count(with_size_val), over_align_val); + std::__libcpp_deallocate(p, with_size_val, over_align_val); assert(stats.expect_size_align(with_size_val, over_align_val)); } stats.reset(); { - std::__libcpp_deallocate_unsized<char>(static_cast<char*>(p), over_align_val); + std::__libcpp_deallocate_unsized(p, over_align_val); assert(stats.expect_align(over_align_val)); } stats.reset(); { - std::__libcpp_deallocate<char>(static_cast<char*>(p), std::__element_count(with_size_val), under_align_val); + std::__libcpp_deallocate(p, with_size_val, under_align_val); assert(stats.expect_size(with_size_val)); } stats.reset(); diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp index 3c9cf9b..bede567 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp @@ -12,8 +12,6 @@ // constexpr OutIter // constexpr after C++17 // copy(InIter first, InIter last, OutIter result); -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - #include <algorithm> #include <cassert> #include <vector> diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill.pass.cpp index 9b403db..b0a74b8 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill.pass.cpp @@ -17,6 +17,8 @@ #include <array> #include <cassert> #include <cstddef> +#include <deque> +#include <ranges> #include <vector> #include "sized_allocator.h" @@ -93,6 +95,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) { return true; } +/*TEST_CONSTEXPR_CXX26*/ void test_deque() { // TODO: Mark as TEST_CONSTEXPR_CXX26 once std::deque is constexpr + std::deque<int> in(20); + std::deque<int> expected(in.size(), 42); + std::fill(in.begin(), in.end(), 42); + assert(in == expected); +} + TEST_CONSTEXPR_CXX20 bool test() { types::for_each(types::forward_iterator_list<char*>(), Test<char>()); types::for_each(types::forward_iterator_list<int*>(), Test<int>()); @@ -138,6 +147,20 @@ TEST_CONSTEXPR_CXX20 bool test() { } } + if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr + test_deque(); + +#if TEST_STD_VER >= 20 + { // Verify that join_view of vectors work properly. + std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}}; + auto jv = std::ranges::join_view(v); + std::fill(jv.begin(), jv.end(), 42); + for (const auto& vec : v) + for (auto n : vec) + assert(n == 42); + } +#endif + return true; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp index e42172c..5dc9b90 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp @@ -17,6 +17,8 @@ #include <array> #include <cassert> #include <cstddef> +#include <deque> +#include <ranges> #include <vector> #include "sized_allocator.h" @@ -126,6 +128,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) { return true; } +/*TEST_CONSTEXPR_CXX26*/ void test_deque() { // TODO: Mark as TEST_CONSTEXPR_CXX26 once std::deque is constexpr + std::deque<int> in(20); + std::deque<int> expected(in.size(), 42); + std::fill_n(in.begin(), in.size(), 42); + assert(in == expected); +} + TEST_CONSTEXPR_CXX20 bool test() { types::for_each(types::forward_iterator_list<char*>(), Test<char>()); types::for_each(types::forward_iterator_list<int*>(), Test<int>()); @@ -221,6 +230,20 @@ TEST_CONSTEXPR_CXX20 bool test() { } } + if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr + test_deque(); + +#if TEST_STD_VER >= 20 + { + std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}}; + auto jv = std::ranges::join_view(v); + std::fill_n(jv.begin(), std::distance(jv.begin(), jv.end()), 42); + for (const auto& vec : v) + for (auto n : vec) + assert(n == 42); + } +#endif + return true; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill.pass.cpp index 61a659f..7ae0a06 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill.pass.cpp @@ -18,6 +18,7 @@ #include <algorithm> #include <array> #include <cassert> +#include <deque> #include <ranges> #include <string> #include <vector> @@ -128,6 +129,13 @@ constexpr bool test_vector_bool(std::size_t N) { } #endif +/*TEST_CONSTEXPR_CXX26*/ void test_deque() { // TODO: Mark as TEST_CONSTEXPR_CXX26 once std::deque is constexpr + std::deque<int> in(20); + std::deque<int> expected(in.size(), 42); + std::ranges::fill(in, 42); + assert(in == expected); +} + constexpr bool test() { test_iterators<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>(); test_iterators<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>(); @@ -227,6 +235,20 @@ constexpr bool test() { } #endif + if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr + test_deque(); + +#if TEST_STD_VER >= 20 + { + std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}}; + auto jv = std::ranges::join_view(v); + std::ranges::fill(jv, 42); + for (const auto& vec : v) + for (auto n : vec) + assert(n == 42); + } +#endif + return true; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp index 2d6e24a..25db892 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp @@ -16,6 +16,7 @@ #include <algorithm> #include <array> #include <cassert> +#include <deque> #include <ranges> #include <string> #include <vector> @@ -101,6 +102,13 @@ constexpr bool test_vector_bool(std::size_t N) { } #endif +/*TEST_CONSTEXPR_CXX26*/ void test_deque() { // TODO: Mark as TEST_CONSTEXPR_CXX26 once std::deque is constexpr + std::deque<int> in(20); + std::deque<int> expected(in.size(), 42); + std::ranges::fill_n(std::ranges::begin(in), std::ranges::size(in), 42); + assert(in == expected); +} + constexpr bool test() { test_iterators<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>(); test_iterators<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>(); @@ -175,6 +183,20 @@ constexpr bool test() { } #endif + if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr + test_deque(); + +#if TEST_STD_VER >= 20 + { + std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}}; + auto jv = std::ranges::join_view(v); + std::ranges::fill_n(std::ranges::begin(jv), std::ranges::distance(jv), 42); + for (const auto& vec : v) + for (auto n : vec) + assert(n == 42); + } +#endif + return true; } diff --git a/libcxx/test/std/containers/sequences/vector.bool/shrink_to_fit.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/shrink_to_fit.pass.cpp index 665867a..bf17733 100644 --- a/libcxx/test/std/containers/sequences/vector.bool/shrink_to_fit.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector.bool/shrink_to_fit.pass.cpp @@ -11,8 +11,6 @@ // void shrink_to_fit(); -// XFAIL: FROZEN-CXX03-HEADERS-FIXME - #include <cassert> #include <climits> #include <vector> diff --git a/libcxx/test/std/language.support/support.runtime/cstdalign.compile.pass.cpp b/libcxx/test/std/language.support/support.runtime/cstdalign.compile.pass.cpp index 69296df..d289ef6 100644 --- a/libcxx/test/std/language.support/support.runtime/cstdalign.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.runtime/cstdalign.compile.pass.cpp @@ -10,7 +10,7 @@ // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS -// XFAIL: FROZEN-CXX03-HEADERS-FIXME +// UNSUPPORTED: c++03 #include <cstdalign> diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp index b7cc123..f7390ef 100644 --- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp +++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp @@ -58,15 +58,18 @@ void test() { { { std::unique_ptr<WithCookie[]> ptr(new WithCookie[5]); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range"); } { std::unique_ptr<WithCookie[]> ptr = std::make_unique<WithCookie[]>(5); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range"); } #if TEST_STD_VER >= 20 { std::unique_ptr<WithCookie[]> ptr = std::make_unique_for_overwrite<WithCookie[]>(5); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = WithCookie(), "unique_ptr<T[]>::operator[](index): index out of range"); } #endif @@ -82,11 +85,13 @@ void test() { { { std::unique_ptr<NoCookie[]> ptr = std::make_unique<NoCookie[]>(5); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range"); } # if TEST_STD_VER >= 20 { std::unique_ptr<NoCookie[]> ptr = std::make_unique_for_overwrite<NoCookie[]>(5); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = NoCookie(), "unique_ptr<T[]>::operator[](index): index out of range"); } # endif @@ -101,6 +106,7 @@ void test() { { std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5); std::unique_ptr<T[]> other(std::move(ptr)); + assert(&other[1] == other.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range"); } @@ -109,6 +115,7 @@ void test() { std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5); std::unique_ptr<T[]> other; other = std::move(ptr); + assert(&other[1] == other.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range"); } @@ -116,6 +123,7 @@ void test() { { std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5); std::unique_ptr<T[], MyDeleter> other(std::move(ptr)); + assert(&other[1] == other.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range"); } @@ -124,6 +132,7 @@ void test() { std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5); std::unique_ptr<T[], MyDeleter> other; other = std::move(ptr); + assert(&other[1] == other.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range"); } }); @@ -144,6 +153,34 @@ struct WithCookie { char padding[Size]; }; +template <std::size_t Size> +struct alignas(128) OveralignedNoCookie { + char padding[Size]; +}; + +template <std::size_t Size> +struct alignas(128) OveralignedWithCookie { + OveralignedWithCookie() = default; + OveralignedWithCookie(OveralignedWithCookie const&) {} + OveralignedWithCookie& operator=(OveralignedWithCookie const&) { return *this; } + ~OveralignedWithCookie() {} + char padding[Size]; +}; + +// These types have a different ABI alignment (alignof) and preferred alignment (__alignof) on some platforms. +// Make sure things work with these types because array cookies can be sensitive to preferred alignment on some +// platforms. +struct WithCookiePreferredAlignment { + WithCookiePreferredAlignment() = default; + WithCookiePreferredAlignment(WithCookiePreferredAlignment const&) {} + WithCookiePreferredAlignment& operator=(WithCookiePreferredAlignment const&) { return *this; } + ~WithCookiePreferredAlignment() {} + long double data; +}; +struct NoCookiePreferredAlignment { + long double data; +}; + int main(int, char**) { test<WithCookie<1>, NoCookie<1>>(); test<WithCookie<2>, NoCookie<2>>(); @@ -153,7 +190,18 @@ int main(int, char**) { test<WithCookie<16>, NoCookie<16>>(); test<WithCookie<32>, NoCookie<32>>(); test<WithCookie<256>, NoCookie<256>>(); + + test<OveralignedWithCookie<1>, OveralignedNoCookie<1>>(); + test<OveralignedWithCookie<2>, OveralignedNoCookie<2>>(); + test<OveralignedWithCookie<3>, OveralignedNoCookie<3>>(); + test<OveralignedWithCookie<4>, OveralignedNoCookie<4>>(); + test<OveralignedWithCookie<8>, OveralignedNoCookie<8>>(); + test<OveralignedWithCookie<16>, OveralignedNoCookie<16>>(); + test<OveralignedWithCookie<32>, OveralignedNoCookie<32>>(); + test<OveralignedWithCookie<256>, OveralignedNoCookie<256>>(); + test<std::string, int>(); + test<WithCookiePreferredAlignment, NoCookiePreferredAlignment>(); return 0; } diff --git a/libcxx/utils/compare-benchmarks b/libcxx/utils/compare-benchmarks index 988e243..d165c73 100755 --- a/libcxx/utils/compare-benchmarks +++ b/libcxx/utils/compare-benchmarks @@ -65,9 +65,16 @@ def plain_text_comparison(data, metric, baseline_name=None, candidate_name=None) """ data = data.replace(numpy.nan, None) # avoid NaNs in tabulate output headers = ['Benchmark', baseline_name, candidate_name, 'Difference', '% Difference'] - fmt = (None, '.2f', '.2f', '.2f', '.2f') - table = data[['benchmark', f'{metric}_0', f'{metric}_1', 'difference', 'percent']].set_index('benchmark') - return tabulate.tabulate(table, headers=headers, floatfmt=fmt, numalign='right') + fmt = (None, '.2f', '.2f', '.2f', '.2%') + table = data[['benchmark', f'{metric}_0', f'{metric}_1', 'difference', 'percent']] + + # Compute the geomean and report on their difference + geomean_0 = statistics.geometric_mean(data[f'{metric}_0'].dropna()) + geomean_1 = statistics.geometric_mean(data[f'{metric}_1'].dropna()) + geomean_row = ['Geomean', geomean_0, geomean_1, (geomean_1 - geomean_0), (geomean_1 - geomean_0) / geomean_0] + table.loc[table.index.max()] = geomean_row + + return tabulate.tabulate(table.set_index('benchmark'), headers=headers, floatfmt=fmt, numalign='right') def create_chart(data, metric, subtitle=None, series_names=None): """ @@ -154,7 +161,7 @@ def main(argv): # If we have exactly two data sets, compute additional info in new columns if len(lnt_inputs) == 2: data['difference'] = data[f'{args.metric}_1'] - data[f'{args.metric}_0'] - data['percent'] = 100 * (data['difference'] / data[f'{args.metric}_0']) + data['percent'] = data['difference'] / data[f'{args.metric}_0'] if args.filter is not None: keeplist = [b for b in data['benchmark'] if re.search(args.filter, b) is not None] |