[libc++] Forward to std::memcmp for trivially comparable types in equal

Reviewed By: #libc, ldionne

Spies: ldionne, Mordante, libcxx-commits

Differential Revision: https://reviews.llvm.org/D139554
This commit is contained in:
Nikolas Klauser
2022-12-07 18:51:28 +01:00
parent 1e6752b853
commit b4ecfd3c46
24 changed files with 516 additions and 130 deletions

View File

@@ -107,7 +107,7 @@ endif()
add_library( cxx-benchmarks-flags-libcxx INTERFACE)
target_link_libraries( cxx-benchmarks-flags-libcxx INTERFACE cxx-benchmarks-flags)
target_compile_options(cxx-benchmarks-flags-libcxx INTERFACE ${SANITIZER_FLAGS} -Wno-user-defined-literals -Wno-suggest-override)
target_link_options( cxx-benchmarks-flags-libcxx INTERFACE -nodefaultlibs "-L${BENCHMARK_LIBCXX_INSTALL}/lib" ${SANITIZER_FLAGS})
target_link_options( cxx-benchmarks-flags-libcxx INTERFACE -nodefaultlibs "-L${BENCHMARK_LIBCXX_INSTALL}/lib" "-L${BENCHMARK_LIBCXX_INSTALL}/lib64" ${SANITIZER_FLAGS})
set(libcxx_benchmark_targets)
@@ -158,6 +158,7 @@ endfunction()
#==============================================================================
set(BENCHMARK_TESTS
algorithms.partition_point.bench.cpp
algorithms/equal.bench.cpp
algorithms/lower_bound.bench.cpp
algorithms/make_heap.bench.cpp
algorithms/make_heap_then_sort_heap.bench.cpp

View File

@@ -0,0 +1,46 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include <algorithm>
#include <benchmark/benchmark.h>
#include <vector>
static void bm_equal_iter(benchmark::State& state) {
std::vector<char> vec1(state.range(), '1');
std::vector<char> vec2(state.range(), '1');
for (auto _ : state) {
benchmark::DoNotOptimize(vec1);
benchmark::DoNotOptimize(vec2);
benchmark::DoNotOptimize(std::equal(vec1.begin(), vec1.end(), vec2.begin()));
}
}
BENCHMARK(bm_equal_iter)->DenseRange(1, 8)->Range(16, 1 << 20);
static void bm_equal(benchmark::State& state) {
std::vector<char> vec1(state.range(), '1');
std::vector<char> vec2(state.range(), '1');
for (auto _ : state) {
benchmark::DoNotOptimize(vec1);
benchmark::DoNotOptimize(vec2);
benchmark::DoNotOptimize(std::equal(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
}
}
BENCHMARK(bm_equal)->DenseRange(1, 8)->Range(16, 1 << 20);
static void bm_ranges_equal(benchmark::State& state) {
std::vector<char> vec1(state.range(), '1');
std::vector<char> vec2(state.range(), '1');
for (auto _ : state) {
benchmark::DoNotOptimize(vec1);
benchmark::DoNotOptimize(vec2);
benchmark::DoNotOptimize(std::ranges::equal(vec1, vec2));
}
}
BENCHMARK(bm_ranges_equal)->DenseRange(1, 8)->Range(16, 1 << 20);
BENCHMARK_MAIN();

View File

@@ -42,6 +42,8 @@ Implemented Papers
Improvements and New Features
-----------------------------
- ``std::equal`` and ``std::ranges::equal`` are now forwarding to ``std::memcmp`` for integral types and pointers,
which can lead up to 40x performance improvements.
- ``std::string_view`` now provides iterators that check for out-of-bounds accesses when the safe
libc++ mode is enabled.

View File

@@ -638,6 +638,7 @@ set(files
__type_traits/is_destructible.h
__type_traits/is_empty.h
__type_traits/is_enum.h
__type_traits/is_equality_comparable.h
__type_traits/is_final.h
__type_traits/is_floating_point.h
__type_traits/is_function.h
@@ -702,6 +703,7 @@ set(files
__type_traits/nat.h
__type_traits/negation.h
__type_traits/noexcept_move_assign_container.h
__type_traits/predicate_traits.h
__type_traits/promote.h
__type_traits/rank.h
__type_traits/remove_all_extents.h

View File

@@ -10,6 +10,8 @@
#define _LIBCPP___ALGORITHM_COMP_H
#include <__config>
#include <__type_traits/integral_constant.h>
#include <__type_traits/predicate_traits.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -24,6 +26,9 @@ struct __equal_to {
}
};
template <class _Lhs, class _Rhs>
struct __is_trivial_equality_predicate<__equal_to, _Lhs, _Rhs> : true_type {};
template <class _T1, class _T2 = _T1>
struct __less
{

View File

@@ -11,9 +11,20 @@
#define _LIBCPP___ALGORITHM_EQUAL_H
#include <__algorithm/comp.h>
#include <__algorithm/unwrap_iter.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
#include <__iterator/distance.h>
#include <__iterator/iterator_traits.h>
#include <__string/constexpr_c_functions.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_equality_comparable.h>
#include <__type_traits/is_volatile.h>
#include <__type_traits/predicate_traits.h>
#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -22,23 +33,42 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate __pred) {
_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_iter_impl(
_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate& __pred) {
for (; __first1 != __last1; ++__first1, (void)++__first2)
if (!__pred(*__first1, *__first2))
return false;
return true;
}
template <
class _Tp,
class _Up,
class _BinaryPredicate,
__enable_if_t<__is_trivial_equality_predicate<_BinaryPredicate, _Tp, _Up>::value && !is_volatile<_Tp>::value &&
!is_volatile<_Up>::value && __is_trivially_equality_comparable<_Tp, _Up>::value,
int> = 0>
_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
__equal_iter_impl(_Tp* __first1, _Tp* __last1, _Up* __first2, _BinaryPredicate&) {
return std::__constexpr_memcmp(__first1, __first2, (__last1 - __first1) * sizeof(_Tp)) == 0;
}
template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate __pred) {
return std::__equal_iter_impl(
std::__unwrap_iter(__first1), std::__unwrap_iter(__last1), std::__unwrap_iter(__first2), __pred);
}
template <class _InputIterator1, class _InputIterator2>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2) {
return std::equal(__first1, __last1, __first2, __equal_to());
}
#if _LIBCPP_STD_VER >= 14
template <class _BinaryPredicate, class _InputIterator1, class _InputIterator2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
__equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2,
_BinaryPredicate __pred, input_iterator_tag, input_iterator_tag) {
for (; __first1 != __last1 && __first2 != __last2; ++__first1, (void)++__first2)
@@ -47,19 +77,52 @@ __equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __fir
return __first1 == __last1 && __first2 == __last2;
}
template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Pred, class _Proj1, class _Proj2>
_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_impl(
_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, _Pred& __comp, _Proj1& __proj1, _Proj2& __proj2) {
while (__first1 != __last1 && __first2 != __last2) {
if (!std::__invoke(__comp, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
return false;
++__first1;
++__first2;
}
return __first1 == __last1 && __first2 == __last2;
}
template <class _Tp,
class _Up,
class _Pred,
class _Proj1,
class _Proj2,
__enable_if_t<__is_trivial_equality_predicate<_Pred, _Tp, _Up>::value && __is_identity<_Proj1>::value &&
__is_identity<_Proj2>::value && !is_volatile<_Tp>::value && !is_volatile<_Up>::value &&
__is_trivially_equality_comparable<_Tp, _Up>::value,
int> = 0>
_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_impl(
_Tp* __first1, _Tp* __last1, _Up* __first2, _Up*, _Pred&, _Proj1&, _Proj2&) {
return std::__constexpr_memcmp(__first1, __first2, (__last1 - __first1) * sizeof(_Tp)) == 0;
}
template <class _BinaryPredicate, class _RandomAccessIterator1, class _RandomAccessIterator2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
__equal(_RandomAccessIterator1 __first1, _RandomAccessIterator1 __last1, _RandomAccessIterator2 __first2,
_RandomAccessIterator2 __last2, _BinaryPredicate __pred, random_access_iterator_tag,
random_access_iterator_tag) {
if (_VSTD::distance(__first1, __last1) != _VSTD::distance(__first2, __last2))
return false;
return _VSTD::equal<_RandomAccessIterator1, _RandomAccessIterator2,
_BinaryPredicate&>(__first1, __last1, __first2, __pred);
__identity __proj;
return std::__equal_impl(
std::__unwrap_iter(__first1),
std::__unwrap_iter(__last1),
std::__unwrap_iter(__first2),
std::__unwrap_iter(__last2),
__pred,
__proj,
__proj);
}
template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2,
_BinaryPredicate __pred) {
return _VSTD::__equal<_BinaryPredicate&>(
@@ -68,7 +131,7 @@ equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first
}
template <class _InputIterator1, class _InputIterator2>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2) {
return std::__equal(
__first1,

View File

@@ -55,25 +55,12 @@ struct _ProjectedPred {
};
template <class _Pred, class _Proj, class = void>
struct __can_use_pristine_comp : false_type {};
template <class _Pred, class _Proj>
struct __can_use_pristine_comp<_Pred, _Proj, __enable_if_t<
!is_member_pointer<typename decay<_Pred>::type>::value && (
#if _LIBCPP_STD_VER >= 20
is_same<typename decay<_Proj>::type, identity>::value ||
#endif
is_same<typename decay<_Proj>::type, __identity>::value
)
> > : true_type {};
template <class _Pred, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static
__enable_if_t<
!__can_use_pristine_comp<_Pred, _Proj>::value,
_ProjectedPred<_Pred, _Proj>
>
template <class _Pred,
class _Proj,
__enable_if_t<!(!is_member_pointer<typename decay<_Pred>::type>::value &&
__is_identity<typename decay<_Proj>::type>::value),
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static _ProjectedPred<_Pred, _Proj>
__make_projected(_Pred& __pred, _Proj& __proj) {
return _ProjectedPred<_Pred, _Proj>(__pred, __proj);
}
@@ -81,13 +68,12 @@ __make_projected(_Pred& __pred, _Proj& __proj) {
// Avoid creating the functor and just use the pristine comparator -- for certain algorithms, this would enable
// optimizations that rely on the type of the comparator. Additionally, this results in less layers of indirection in
// the call stack when the comparator is invoked, even in an unoptimized build.
template <class _Pred, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static
__enable_if_t<
__can_use_pristine_comp<_Pred, _Proj>::value,
_Pred&
>
__make_projected(_Pred& __pred, _Proj&) {
template <class _Pred,
class _Proj,
__enable_if_t<!is_member_pointer<typename decay<_Pred>::type>::value &&
__is_identity<typename decay<_Proj>::type>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static _Pred& __make_projected(_Pred& __pred, _Proj&) {
return __pred;
}
@@ -102,7 +88,7 @@ namespace ranges {
template <class _Comp, class _Proj1, class _Proj2>
_LIBCPP_HIDE_FROM_ABI constexpr static
decltype(auto) __make_projected_comp(_Comp& __comp, _Proj1& __proj1, _Proj2& __proj2) {
if constexpr (same_as<decay_t<_Proj1>, identity> && same_as<decay_t<_Proj2>, identity> &&
if constexpr (__is_identity<decay_t<_Proj1>>::value && __is_identity<decay_t<_Proj2>>::value &&
!is_member_pointer_v<decay_t<_Comp>>) {
// Avoid creating the lambda and just use the pristine comparator -- for certain algorithms, this would enable
// optimizations that rely on the type of the comparator.

View File

@@ -9,6 +9,8 @@
#ifndef _LIBCPP___ALGORITHM_RANGES_EQUAL_H
#define _LIBCPP___ALGORITHM_RANGES_EQUAL_H
#include <__algorithm/equal.h>
#include <__algorithm/unwrap_range.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
@@ -31,29 +33,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
namespace __equal {
struct __fn {
private:
template <class _Iter1, class _Sent1,
class _Iter2, class _Sent2,
class _Pred,
class _Proj1,
class _Proj2>
_LIBCPP_HIDE_FROM_ABI constexpr static
bool __equal_impl(_Iter1 __first1, _Sent1 __last1,
_Iter2 __first2, _Sent2 __last2,
_Pred& __pred,
_Proj1& __proj1,
_Proj2& __proj2) {
while (__first1 != __last1 && __first2 != __last2) {
if (!std::invoke(__pred, std::invoke(__proj1, *__first1), std::invoke(__proj2, *__first2)))
return false;
++__first1;
++__first2;
}
return __first1 == __last1 && __first2 == __last2;
}
public:
template <input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
class _Pred = ranges::equal_to,
@@ -70,11 +49,13 @@ public:
if (__last1 - __first1 != __last2 - __first2)
return false;
}
return __equal_impl(std::move(__first1), std::move(__last1),
std::move(__first2), std::move(__last2),
__pred,
__proj1,
__proj2);
auto __unwrapped1 = std::__unwrap_range(std::move(__first1), std::move(__last1));
auto __unwrapped2 = std::__unwrap_range(std::move(__first2), std::move(__last2));
return std::__equal_impl(std::move(__unwrapped1.first), std::move(__unwrapped1.second),
std::move(__unwrapped2.first), std::move(__unwrapped2.second),
__pred,
__proj1,
__proj2);
}
template <input_range _Range1,
@@ -93,11 +74,13 @@ public:
if (ranges::distance(__range1) != ranges::distance(__range2))
return false;
}
return __equal_impl(ranges::begin(__range1), ranges::end(__range1),
ranges::begin(__range2), ranges::end(__range2),
__pred,
__proj1,
__proj2);
auto __unwrapped1 = std::__unwrap_range(ranges::begin(__range1), ranges::end(__range1));
auto __unwrapped2 = std::__unwrap_range(ranges::begin(__range2), ranges::end(__range2));
return std::__equal_impl(std::move(__unwrapped1.first), std::move(__unwrapped1.second),
std::move(__unwrapped2.first), std::move(__unwrapped2.second),
__pred,
__proj1,
__proj2);
return false;
}
};

View File

@@ -64,6 +64,14 @@ decltype(_Impl::__unwrap(std::declval<_Iter>())) __unwrap_iter(_Iter __i) _NOEXC
return _Impl::__unwrap(__i);
}
// Allow input_iterators to be passed to __unwrap_iter (but not __rewrap_iter)
#if _LIBCPP_STD_VER >= 20
template <class _Iter, __enable_if_t<!is_copy_constructible<_Iter>::value, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI constexpr _Iter __unwrap_iter(_Iter __i) noexcept {
return __i;
}
#endif
template <class _OrigIter, class _Iter, class _Impl = __unwrap_iter_impl<_OrigIter> >
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _OrigIter __rewrap_iter(_OrigIter __orig_iter, _Iter __iter) _NOEXCEPT {
return _Impl::__rewrap(std::move(__orig_iter), std::move(__iter));

View File

@@ -43,7 +43,7 @@ struct __unwrap_range_impl {
}
_LIBCPP_HIDE_FROM_ABI static constexpr auto
__rewrap(_Iter __orig_iter, decltype(std::__unwrap_iter(__orig_iter)) __iter)
__rewrap(_Iter __orig_iter, decltype(std::__unwrap_iter(std::move(__orig_iter))) __iter)
requires random_access_iterator<_Iter> && sized_sentinel_for<_Sent, _Iter>
{
return std::__rewrap_iter(std::move(__orig_iter), std::move(__iter));

View File

@@ -11,6 +11,7 @@
#define _LIBCPP___FUNCTIONAL_IDENTITY_H
#include <__config>
#include <__type_traits/integral_constant.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -19,6 +20,9 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
struct __is_identity : false_type {};
struct __identity {
template <class _Tp>
_LIBCPP_NODISCARD _LIBCPP_CONSTEXPR _Tp&& operator()(_Tp&& __t) const _NOEXCEPT {
@@ -28,6 +32,9 @@ struct __identity {
using is_transparent = void;
};
template <>
struct __is_identity<__identity> : true_type {};
#if _LIBCPP_STD_VER >= 20
struct identity {
@@ -39,6 +46,10 @@ struct identity {
using is_transparent = void;
};
template <>
struct __is_identity<identity> : true_type {};
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD

View File

@@ -13,6 +13,8 @@
#include <__config>
#include <__functional/binary_function.h>
#include <__functional/unary_function.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/predicate_traits.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -341,6 +343,14 @@ struct _LIBCPP_TEMPLATE_VIS equal_to<void>
};
#endif
template <class _Tp>
struct __is_trivial_equality_predicate<equal_to<_Tp>, _Tp, _Tp> : true_type {};
#if _LIBCPP_STD_VER >= 14
template <class _Tp>
struct __is_trivial_equality_predicate<equal_to<>, _Tp, _Tp> : true_type {};
#endif
#if _LIBCPP_STD_VER >= 14
template <class _Tp = void>
#else

View File

@@ -13,6 +13,8 @@
#include <__concepts/equality_comparable.h>
#include <__concepts/totally_ordered.h>
#include <__config>
#include <__type_traits/integral_constant.h>
#include <__type_traits/predicate_traits.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -93,6 +95,9 @@ struct greater_equal {
} // namespace ranges
template <class _Lhs, class _Rhs>
struct __is_trivial_equality_predicate<ranges::equal_to, _Lhs, _Rhs> : true_type {};
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD

View File

@@ -11,6 +11,8 @@
#include <__config>
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_equality_comparable.h>
#include <__type_traits/is_same.h>
#include <cstddef>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -33,21 +35,32 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_st
return __builtin_strlen(__str);
}
template <class _Tp>
template <class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
__constexpr_memcmp(const _Tp* __lhs, const _Tp* __rhs, size_t __count) {
#ifdef _LIBCPP_COMPILER_GCC
__constexpr_memcmp(const _Tp* __lhs, const _Up* __rhs, size_t __count) {
static_assert(
__is_trivially_equality_comparable<_Tp, _Up>::value, "_Tp and _Up have to be trivially equality comparable");
if (__libcpp_is_constant_evaluated()) {
for (; __count; --__count, ++__lhs, ++__rhs) {
#ifdef _LIBCPP_COMPILER_CLANG_BASED
if (sizeof(_Tp) == 1 && !is_same<_Tp, bool>::value)
return __builtin_memcmp(__lhs, __rhs, __count);
#endif
while (__count != 0) {
if (*__lhs < *__rhs)
return -1;
if (*__rhs < *__lhs)
return 1;
__count -= sizeof(_Tp);
++__lhs;
++__rhs;
}
return 0;
} else {
return __builtin_memcmp(__lhs, __rhs, __count);
}
#endif
return __builtin_memcmp(__lhs, __rhs, __count);
}
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const char*

View File

@@ -0,0 +1,62 @@
//===----------------------------------------------------------------------===//
//
// 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___TYPE_TRAITS_IS_EQUAILITY_COMPARABLE_H
#define _LIBCPP___TYPE_TRAITS_IS_EQUAILITY_COMPARABLE_H
#include <__config>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_integral.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_void.h>
#include <__type_traits/remove_cv.h>
#include <__type_traits/void_t.h>
#include <__utility/declval.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp, class _Up, class = void>
struct __is_equality_comparable : false_type {};
template <class _Tp, class _Up>
struct __is_equality_comparable<_Tp, _Up, __void_t<decltype(std::declval<_Tp>() == std::declval<_Up>())> > : true_type {
};
// A type is_trivially_equality_comparable if the expression `a == b` is equivalent to `std::memcmp(&a, &b, sizeof(T))`
// (with `a` and `b` being of type `T`). There is no compiler built-in to check this, so we can only do this for known
// types. In particular, these are the integral types and raw pointers.
//
// The following types are not trivially equality comparable:
// floating-point types: different bit-patterns can compare equal. (e.g 0.0 and -0.0)
// enums: The user is allowed to specialize operator== for enums
// pointers that don't have the same type (ignoring cv-qualifiers): pointers to virtual bases are equality comparable,
// but don't have the same bit-pattern. An exception to this is comparing to a void-pointer. There the bit-pattern is
// always compared.
template <class _Tp, class _Up>
struct __is_trivially_equality_comparable
: integral_constant<bool,
__is_equality_comparable<_Tp, _Up>::value && is_integral<_Tp>::value &&
is_same<__remove_cv_t<_Tp>, __remove_cv_t<_Up> >::value> {};
// TODO: Use is_pointer_inverconvertible_base_of
template <class _Tp, class _Up>
struct __is_trivially_equality_comparable<_Tp*, _Up*>
: integral_constant<
bool,
__is_equality_comparable<_Tp*, _Up*>::value &&
(is_same<__remove_cv_t<_Tp>, __remove_cv_t<_Up> >::value || is_void<_Tp>::value || is_void<_Up>::value)> {
};
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___TYPE_TRAITS_IS_EQUAILITY_COMPARABLE_H

View File

@@ -0,0 +1,26 @@
//===----------------------------------------------------------------------===//
//
// 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___TYPE_TRAITS_PREDICATE_TRAITS
#define _LIBCPP___TYPE_TRAITS_PREDICATE_TRAITS
#include <__config>
#include <__type_traits/integral_constant.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Pred, class _Lhs, class _Rhs>
struct __is_trivial_equality_predicate : false_type {};
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___TYPE_TRAITS_PREDICATE_TRAITS

View File

@@ -604,7 +604,10 @@ module std [system] {
module unique { private header "__algorithm/unique.h" }
module unique_copy { private header "__algorithm/unique_copy.h" }
module unwrap_iter { private header "__algorithm/unwrap_iter.h" }
module unwrap_range { private header "__algorithm/unwrap_range.h" }
module unwrap_range {
private header "__algorithm/unwrap_range.h"
export utility.__utility.pair
}
module upper_bound { private header "__algorithm/upper_bound.h" }
}
}
@@ -1491,6 +1494,10 @@ module std [system] {
module is_destructible { private header "__type_traits/is_destructible.h" }
module is_empty { private header "__type_traits/is_empty.h" }
module is_enum { private header "__type_traits/is_enum.h" }
module is_equality_comparable {
private header "__type_traits/is_equality_comparable.h"
export integral_constant
}
module is_final { private header "__type_traits/is_final.h" }
module is_floating_point { private header "__type_traits/is_floating_point.h" }
module is_function { private header "__type_traits/is_function.h" }
@@ -1561,6 +1568,7 @@ module std [system] {
module nat { private header "__type_traits/nat.h" }
module negation { private header "__type_traits/negation.h" }
module noexcept_move_assign_container { private header "__type_traits/noexcept_move_assign_container.h" }
module predicate_traits { private header "__type_traits/predicate_traits.h" }
module promote { private header "__type_traits/promote.h" }
module rank { private header "__type_traits/rank.h" }
module remove_all_extents { private header "__type_traits/remove_all_extents.h" }

View File

@@ -651,6 +651,7 @@ END-SCRIPT
#include <__type_traits/is_destructible.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_destructible.h'}}
#include <__type_traits/is_empty.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_empty.h'}}
#include <__type_traits/is_enum.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_enum.h'}}
#include <__type_traits/is_equality_comparable.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_equality_comparable.h'}}
#include <__type_traits/is_final.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_final.h'}}
#include <__type_traits/is_floating_point.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_floating_point.h'}}
#include <__type_traits/is_function.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_function.h'}}
@@ -715,6 +716,7 @@ END-SCRIPT
#include <__type_traits/nat.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/nat.h'}}
#include <__type_traits/negation.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/negation.h'}}
#include <__type_traits/noexcept_move_assign_container.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/noexcept_move_assign_container.h'}}
#include <__type_traits/predicate_traits.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/predicate_traits.h'}}
#include <__type_traits/promote.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/promote.h'}}
#include <__type_traits/rank.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/rank.h'}}
#include <__type_traits/remove_all_extents.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/remove_all_extents.h'}}

View File

@@ -0,0 +1,58 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// ADDITIONAL_COMPILE_FLAGS: -Wno-private-header
#include <__type_traits/is_equality_comparable.h>
enum Enum : int {};
enum class EnumClass : int {};
static_assert(std::__is_trivially_equality_comparable<int, int>::value, "");
static_assert(std::__is_trivially_equality_comparable<const int, int>::value, "");
static_assert(std::__is_trivially_equality_comparable<int, const int>::value, "");
static_assert(std::__is_trivially_equality_comparable<unsigned int, unsigned int>::value, "");
static_assert(std::__is_trivially_equality_comparable<const unsigned int, unsigned int>::value, "");
static_assert(!std::__is_trivially_equality_comparable<unsigned int, int>::value, "");
static_assert(!std::__is_trivially_equality_comparable<long, int>::value, "");
static_assert(!std::__is_trivially_equality_comparable<int, long>::value, "");
static_assert(std::__is_trivially_equality_comparable<int*, int*>::value, "");
static_assert(std::__is_trivially_equality_comparable<int*, void*>::value, "");
static_assert(!std::__is_trivially_equality_comparable<int*, long*>::value, "");
static_assert(!std::__is_trivially_equality_comparable<Enum, int>::value, "");
static_assert(!std::__is_trivially_equality_comparable<EnumClass, int>::value, "");
static_assert(!std::__is_trivially_equality_comparable<float, int>::value, "");
static_assert(!std::__is_trivially_equality_comparable<double, long long>::value, "");
static_assert(!std::__is_trivially_equality_comparable<float, int>::value, "");
static_assert(!std::__is_trivially_equality_comparable<float, float>::value, "");
static_assert(!std::__is_trivially_equality_comparable<double, double>::value, "");
static_assert(!std::__is_trivially_equality_comparable<long double, long double>::value, "");
struct S {
char c;
};
struct S2 {
char c;
};
struct VirtualBase : virtual S {};
struct NonVirtualBase : S, S2 {};
static_assert(!std::__is_trivially_equality_comparable<S*, VirtualBase*>::value, "");
static_assert(!std::__is_trivially_equality_comparable<S2*, VirtualBase*>::value, "");
// This is trivially_equality_comparable, but we can't detect it currently
static_assert(!std::__is_trivially_equality_comparable<S*, NonVirtualBase*>::value, "");

View File

@@ -20,68 +20,119 @@
#include <algorithm>
#include <cassert>
#include <functional>
#include "test_macros.h"
#include "test_iterators.h"
#include "test_macros.h"
#include "type_algorithms.h"
template <class Iter1, class Iter2 = Iter1>
void test_equal() {
int a[] = {0, 1, 2, 3, 4, 5};
const unsigned s = sizeof(a) / sizeof(a[0]);
int b[s] = {0, 1, 2, 5, 4, 5};
template <class UnderlyingType, class Iter1>
struct Test {
template <class Iter2>
TEST_CONSTEXPR_CXX20 void operator()() {
UnderlyingType a[] = {0, 1, 2, 3, 4, 5};
const unsigned s = sizeof(a) / sizeof(a[0]);
UnderlyingType b[s] = {0, 1, 2, 5, 4, 5};
assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a)));
assert(std::equal(Iter2(a), Iter2(a + s), Iter1(a)));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b)));
assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a)));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b)));
#if TEST_STD_VER >= 14
assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s)));
assert(std::equal(Iter2(a), Iter2(a + s), Iter1(a), Iter1(a + s)));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s - 1)));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b), Iter2(b + s)));
assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a), std::equal_to<>()));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b), std::equal_to<>()));
assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s)));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s - 1)));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b), Iter2(b + s)));
assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s), std::equal_to<>()));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s - 1), std::equal_to<>()));
assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b), Iter2(b + s), std::equal_to<>()));
#endif
}
};
struct TestNarrowingEqualTo {
template <class UnderlyingType>
TEST_CONSTEXPR_CXX20 void operator()() {
UnderlyingType a[] = {
UnderlyingType(0x1000),
UnderlyingType(0x1001),
UnderlyingType(0x1002),
UnderlyingType(0x1003),
UnderlyingType(0x1004)};
UnderlyingType b[] = {
UnderlyingType(0x1600),
UnderlyingType(0x1601),
UnderlyingType(0x1602),
UnderlyingType(0x1603),
UnderlyingType(0x1604)};
assert(std::equal(a, a + 5, b, std::equal_to<char>()));
#if TEST_STD_VER >= 14
assert(std::equal(a, a + 5, b, b + 5, std::equal_to<char>()));
#endif
}
};
template <class UnderlyingType, class TypeList>
struct TestIter2 {
template <class Iter1>
TEST_CONSTEXPR_CXX20 void operator()() {
meta::for_each(TypeList(), Test<UnderlyingType, Iter1>());
}
};
struct AddressCompare {
int i = 0;
TEST_CONSTEXPR_CXX20 AddressCompare(int) {}
operator char() { return static_cast<char>(i); }
friend TEST_CONSTEXPR_CXX20 bool operator==(const AddressCompare& lhs, const AddressCompare& rhs) {
return &lhs == &rhs;
}
friend TEST_CONSTEXPR_CXX20 bool operator!=(const AddressCompare& lhs, const AddressCompare& rhs) {
return &lhs != &rhs;
}
};
TEST_CONSTEXPR_CXX20 bool test() {
meta::for_each(meta::cpp17_input_iterator_list<int*>(), TestIter2<int, meta::cpp17_input_iterator_list<int*> >());
meta::for_each(meta::cpp17_input_iterator_list<char*>(), TestIter2<char, meta::cpp17_input_iterator_list<char*> >());
meta::for_each(meta::cpp17_input_iterator_list<AddressCompare*>(),
TestIter2<AddressCompare, meta::cpp17_input_iterator_list<AddressCompare*> >());
meta::for_each(meta::integral_types(), TestNarrowingEqualTo());
return true;
}
#if TEST_STD_VER > 17
TEST_CONSTEXPR bool test_constexpr() {
int ia[] = {1, 3, 6, 7};
int ib[] = {1, 3};
int ic[] = {1, 3, 5, 7};
typedef cpp17_input_iterator<int*> II;
typedef bidirectional_iterator<int*> BI;
struct Base {};
struct Derived : virtual Base {};
return !std::equal(std::begin(ia), std::end(ia), std::begin(ic))
&& !std::equal(std::begin(ia), std::end(ia), std::begin(ic), std::end(ic))
&& std::equal(std::begin(ib), std::end(ib), std::begin(ic))
&& !std::equal(std::begin(ib), std::end(ib), std::begin(ic), std::end(ic))
&& std::equal(II(std::begin(ib)), II(std::end(ib)), II(std::begin(ic)))
&& !std::equal(BI(std::begin(ib)), BI(std::end(ib)), BI(std::begin(ic)), BI(std::end(ic)))
;
}
int main(int, char**) {
test();
#if TEST_STD_VER >= 20
static_assert(test());
#endif
meta::for_each(meta::as_pointers<meta::cv_qualified_versions<int> >(),
TestIter2<int, meta::as_pointers<meta::cv_qualified_versions<int> > >());
meta::for_each(meta::as_pointers<meta::cv_qualified_versions<char> >(),
TestIter2<char, meta::as_pointers<meta::cv_qualified_versions<char> > >());
int main(int, char**)
{
test_equal<cpp17_input_iterator<const int*> >();
test_equal<random_access_iterator<const int*> >();
{
Derived d;
Derived* a[] = {&d, nullptr};
Base* b[] = {&d, nullptr};
// Test all combinations of cv-qualifiers:
test_equal<int*>();
test_equal<int*, const int*>();
test_equal<int*, volatile int*>();
test_equal<int*, const volatile int*>();
test_equal<const int*>();
test_equal<const int*, volatile int*>();
test_equal<const int*, const volatile int*>();
test_equal<volatile int*>();
test_equal<volatile int*, const volatile int*>();
test_equal<const volatile int*>();
#if TEST_STD_VER > 17
static_assert(test_constexpr());
assert(std::equal(a, a + 2, b));
#if TEST_STD_VER >= 14
assert(std::equal(a, a + 2, b, b + 2));
#endif
}
return 0;
}

View File

@@ -88,6 +88,21 @@ constexpr void test_iterators() {
}
}
{ // check that false is returned for non-equal ranges
{
int a[] = {1, 2, 3, 4};
int b[] = {1, 2, 4, 4};
assert(!std::ranges::equal(Iter1(a), Sent1(Iter1(a + 4)), Iter2(b), Sent2(Iter2(b + 4))));
}
{
int a[] = {1, 2, 3, 4};
int b[] = {1, 2, 4, 4};
auto range1 = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 4)));
auto range2 = std::ranges::subrange(Iter2(b), Sent2(Iter2(b + 4)));
assert(!std::ranges::equal(range1, range2));
}
}
{ // check that the predicate is used (return true)
{
int a[] = {1, 2, 3, 4};

View File

@@ -8,6 +8,11 @@
// <algorithm>
// https://buildkite.com/llvm-project/libcxx-ci/builds/15823#0184fc0b-d56b-4774-9e1d-35fe24e09e37
// It seems like the CI gcc version is buggy. I can't reproduce the failure on my system or on
// godbolt (https://godbolt.org/z/rsPv8e8fn).
// UNSUPPORTED: gcc-12
#include <algorithm>
#include <cstddef>
#include <functional>

View File

@@ -1418,23 +1418,33 @@ template <std::ranges::input_range R>
requires std::ranges::viewable_range<R&&>
ProxyRange(R&&) -> ProxyRange<std::views::all_t<R&&>>;
#endif // TEST_STD_VER > 17
namespace meta {
template <class Ptr>
using random_access_iterator_list = type_list<Ptr, contiguous_iterator<Ptr>, random_access_iterator<Ptr>>;
using random_access_iterator_list =
type_list<Ptr,
#if TEST_STD_VER >= 20
contiguous_iterator<Ptr>,
#endif
random_access_iterator<Ptr> >;
template <class Ptr>
using bidirectional_iterator_list =
concatenate_t<random_access_iterator_list<Ptr>, type_list<bidirectional_iterator<Ptr>>>;
concatenate_t<random_access_iterator_list<Ptr>, type_list<bidirectional_iterator<Ptr> > >;
template <class Ptr>
using forward_iterator_list = concatenate_t<bidirectional_iterator_list<Ptr>, type_list<forward_iterator<Ptr>>>;
using forward_iterator_list = concatenate_t<bidirectional_iterator_list<Ptr>, type_list<forward_iterator<Ptr> > >;
template <class Ptr>
using cpp17_input_iterator_list = concatenate_t<forward_iterator_list<Ptr>, type_list<cpp17_input_iterator<Ptr> > >;
#if TEST_STD_VER >= 20
template <class Ptr>
using cpp20_input_iterator_list =
concatenate_t<forward_iterator_list<Ptr>, type_list<cpp20_input_iterator<Ptr>, cpp17_input_iterator<Ptr>>>;
#endif
} // namespace meta
#endif // TEST_STD_VER > 17
#endif // SUPPORT_TEST_ITERATORS_H

View File

@@ -100,6 +100,20 @@ using integral_types = concatenate_t<character_types, signed_integer_types, unsi
using floating_point_types = type_list<float, double, long double>;
using arithmetic_types = concatenate_t<integral_types, floating_point_types>;
template <class T>
using cv_qualified_versions = type_list<T, const T, volatile T, const volatile T>;
template <class T>
struct type_list_as_pointers;
template <class... Types>
struct type_list_as_pointers<type_list<Types...> > {
using type = type_list<Types*...>;
};
template <class T>
using as_pointers = typename type_list_as_pointers<T>::type;
} // namespace meta
#endif // TEST_SUPPORT_TYPE_ALGORITHMS_H