Skip to content

Commit

Permalink
[libc++] Forward to std::{,w}memchr in std::find
Browse files Browse the repository at this point in the history
Reviewed By: #libc, ldionne

Spies: Mordante, libcxx-commits, ldionne, mikhail.ramalho

Differential Revision: https://reviews.llvm.org/D144394
  • Loading branch information
philnik777 committed May 25, 2023
1 parent 0f64d4f commit 1fd08ed
Show file tree
Hide file tree
Showing 17 changed files with 396 additions and 106 deletions.
1 change: 1 addition & 0 deletions libcxx/benchmarks/CMakeLists.txt
Expand Up @@ -159,6 +159,7 @@ endfunction()
set(BENCHMARK_TESTS
algorithms.partition_point.bench.cpp
algorithms/equal.bench.cpp
algorithms/find.bench.cpp
algorithms/lower_bound.bench.cpp
algorithms/make_heap.bench.cpp
algorithms/make_heap_then_sort_heap.bench.cpp
Expand Down
49 changes: 49 additions & 0 deletions libcxx/benchmarks/algorithms/find.bench.cpp
@@ -0,0 +1,49 @@
//===----------------------------------------------------------------------===//
//
// 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 <cstring>
#include <random>
#include <vector>

template <class T>
static void bm_find(benchmark::State& state) {
std::vector<T> vec1(state.range(), '1');
std::mt19937_64 rng(std::random_device{}());

for (auto _ : state) {
auto idx = rng() % vec1.size();
vec1[idx] = '2';
benchmark::DoNotOptimize(vec1);
benchmark::DoNotOptimize(std::find(vec1.begin(), vec1.end(), T('2')));
vec1[idx] = '1';
}
}
BENCHMARK(bm_find<char>)->DenseRange(1, 8)->Range(16, 1 << 20);
BENCHMARK(bm_find<short>)->DenseRange(1, 8)->Range(16, 1 << 20);
BENCHMARK(bm_find<int>)->DenseRange(1, 8)->Range(16, 1 << 20);

template <class T>
static void bm_ranges_find(benchmark::State& state) {
std::vector<T> vec1(state.range(), '1');
std::mt19937_64 rng(std::random_device{}());

for (auto _ : state) {
auto idx = rng() % vec1.size();
vec1[idx] = '2';
benchmark::DoNotOptimize(vec1);
benchmark::DoNotOptimize(std::ranges::find(vec1, T('2')));
vec1[idx] = '1';
}
}
BENCHMARK(bm_ranges_find<char>)->DenseRange(1, 8)->Range(16, 1 << 20);
BENCHMARK(bm_ranges_find<short>)->DenseRange(1, 8)->Range(16, 1 << 20);
BENCHMARK(bm_ranges_find<int>)->DenseRange(1, 8)->Range(16, 1 << 20);

BENCHMARK_MAIN();
53 changes: 49 additions & 4 deletions libcxx/include/__algorithm/find.h
Expand Up @@ -10,23 +10,68 @@
#ifndef _LIBCPP___ALGORITHM_FIND_H
#define _LIBCPP___ALGORITHM_FIND_H

#include <__algorithm/unwrap_iter.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
#include <__string/constexpr_c_functions.h>
#include <__type_traits/is_same.h>

#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
# include <cwchar>
#endif

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

template <class _InputIterator, class _Tp>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
find(_InputIterator __first, _InputIterator __last, const _Tp& __value) {
template <class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iter
__find_impl(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
for (; __first != __last; ++__first)
if (*__first == __value)
if (std::__invoke(__proj, *__first) == __value)
break;
return __first;
}

template <class _Tp,
class _Up,
class _Proj,
__enable_if_t<__is_identity<_Proj>::value && __libcpp_is_trivially_equality_comparable<_Tp, _Up>::value &&
sizeof(_Tp) == 1,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp*
__find_impl(_Tp* __first, _Tp* __last, const _Up& __value, _Proj&) {
if (auto __ret = std::__constexpr_memchr(__first, __value, __last - __first))
return __ret;
return __last;
}

#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class _Tp,
class _Up,
class _Proj,
__enable_if_t<__is_identity<_Proj>::value && __libcpp_is_trivially_equality_comparable<_Tp, _Up>::value &&
sizeof(_Tp) == sizeof(wchar_t) && _LIBCPP_ALIGNOF(_Tp) >= _LIBCPP_ALIGNOF(wchar_t),
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp*
__find_impl(_Tp* __first, _Tp* __last, const _Up& __value, _Proj&) {
if (auto __ret = std::__constexpr_wmemchr(__first, __value, __last - __first))
return __ret;
return __last;
}
#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS

template <class _InputIterator, class _Tp>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
find(_InputIterator __first, _InputIterator __last, const _Tp& __value) {
__identity __proj;
return std::__rewrap_iter(
__first, std::__find_impl(std::__unwrap_iter(__first), std::__unwrap_iter(__last), __value, __proj));
}

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___ALGORITHM_FIND_H
20 changes: 16 additions & 4 deletions libcxx/include/__algorithm/ranges_find.h
Expand Up @@ -9,7 +9,9 @@
#ifndef _LIBCPP___ALGORITHM_RANGES_FIND_H
#define _LIBCPP___ALGORITHM_RANGES_FIND_H

#include <__algorithm/find.h>
#include <__algorithm/ranges_find_if.h>
#include <__algorithm/unwrap_range.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
Expand All @@ -33,20 +35,30 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
namespace __find {
struct __fn {
template <class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI static constexpr _Iter
__find_unwrap(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
if constexpr (forward_iterator<_Iter>) {
auto [__first_un, __last_un] = std::__unwrap_range(__first, std::move(__last));
return std::__rewrap_range<_Sent>(
std::move(__first), std::__find_impl(std::move(__first_un), std::move(__last_un), __value, __proj));
} else {
return std::__find_impl(std::move(__first), std::move(__last), __value, __proj);
}
}

template <input_iterator _Ip, sentinel_for<_Ip> _Sp, class _Tp, class _Proj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<_Ip, _Proj>, const _Tp*>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr
_Ip operator()(_Ip __first, _Sp __last, const _Tp& __value, _Proj __proj = {}) const {
auto __pred = [&](auto&& __e) { return std::forward<decltype(__e)>(__e) == __value; };
return ranges::__find_if_impl(std::move(__first), std::move(__last), __pred, __proj);
return __find_unwrap(std::move(__first), std::move(__last), __value, __proj);
}

template <input_range _Rp, class _Tp, class _Proj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Rp>, _Proj>, const _Tp*>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr
borrowed_iterator_t<_Rp> operator()(_Rp&& __r, const _Tp& __value, _Proj __proj = {}) const {
auto __pred = [&](auto&& __e) { return std::forward<decltype(__e)>(__e) == __value; };
return ranges::__find_if_impl(ranges::begin(__r), ranges::end(__r), __pred, __proj);
return __find_unwrap(ranges::begin(__r), ranges::end(__r), __value, __proj);
}
};
} // namespace __find
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__string/char_traits.h
Expand Up @@ -244,7 +244,7 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char>
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
if (__n == 0)
return nullptr;
return std::__constexpr_char_memchr(__s, static_cast<int>(__a), __n);
return std::__constexpr_memchr(__s, __a, __n);
}

static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
Expand Down
36 changes: 23 additions & 13 deletions libcxx/include/__string/constexpr_c_functions.h
Expand Up @@ -14,6 +14,7 @@
#include <__type_traits/is_equality_comparable.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_lexicographically_comparable.h>
#include <__type_traits/remove_cv.h>
#include <cstddef>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
Expand Down Expand Up @@ -95,20 +96,29 @@ __constexpr_memcmp_equal(const _Tp* __lhs, const _Up* __rhs, size_t __count) {
}
}

inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const char*
__constexpr_char_memchr(const char* __str, int __char, size_t __count) {
#if __has_builtin(__builtin_char_memchr)
return __builtin_char_memchr(__str, __char, __count);
#else
if (!__libcpp_is_constant_evaluated())
return static_cast<const char*>(__builtin_memchr(__str, __char, __count));
for (; __count; --__count) {
if (*__str == __char)
return __str;
++__str;
}
return nullptr;
template <class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __constexpr_memchr(_Tp* __str, _Up __value, size_t __count) {
static_assert(sizeof(_Tp) == 1 && __libcpp_is_trivially_equality_comparable<_Tp, _Up>::value,
"Calling memchr on non-trivially equality comparable types is unsafe.");

if (__libcpp_is_constant_evaluated()) {
// use __builtin_char_memchr to optimize constexpr evaluation if we can
#if _LIBCPP_STD_VER >= 17 && __has_builtin(__builtin_char_memchr)
if constexpr (is_same_v<remove_cv_t<_Tp>, char> && is_same_v<remove_cv_t<_Up>, char>)
return __builtin_char_memchr(__str, __value, __count);
#endif

for (; __count; --__count) {
if (*__str == __value)
return __str;
++__str;
}
return nullptr;
} else {
char __value_buffer = 0;
__builtin_memcpy(&__value_buffer, &__value, sizeof(char));
return static_cast<_Tp*>(__builtin_memchr(__str, __value_buffer, __count));
}
}

_LIBCPP_END_NAMESPACE_STD
Expand Down
32 changes: 23 additions & 9 deletions libcxx/include/cwchar
Expand Up @@ -104,7 +104,11 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,

#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__type_traits/apply_cv.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_equality_comparable.h>
#include <__type_traits/is_same.h>
#include <__type_traits/remove_cv.h>
#include <cwctype>

#include <wchar.h>
Expand Down Expand Up @@ -222,21 +226,31 @@ __constexpr_wmemcmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __count)
#endif
}

inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const wchar_t*
__constexpr_wmemchr(const wchar_t* __str, wchar_t __char, size_t __count) {
#if __has_feature(cxx_constexpr_string_builtins)
return __builtin_wmemchr(__str, __char, __count);
#else
if (!__libcpp_is_constant_evaluated())
return std::wmemchr(__str, __char, __count);
template <class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __constexpr_wmemchr(_Tp* __str, _Up __value, size_t __count) {
static_assert(sizeof(_Tp) == sizeof(wchar_t)&& _LIBCPP_ALIGNOF(_Tp) >= _LIBCPP_ALIGNOF(wchar_t) &&
__libcpp_is_trivially_equality_comparable<_Tp, _Tp>::value,
"Calling wmemchr on non-trivially equality comparable types is unsafe.");

#if __has_builtin(__builtin_wmemchr)
if (!__libcpp_is_constant_evaluated()) {
wchar_t __value_buffer = 0;
__builtin_memcpy(&__value_buffer, &__value, sizeof(wchar_t));
return reinterpret_cast<_Tp*>(
__builtin_wmemchr(reinterpret_cast<__apply_cv_t<_Tp, wchar_t>*>(__str), __value_buffer, __count));
}
# if _LIBCPP_STD_VER >= 17
else if constexpr (is_same_v<remove_cv_t<_Tp>, wchar_t>)
return __builtin_wmemchr(__str, __value, __count);
# endif
#endif // __has_builtin(__builtin_wmemchr)

for (; __count; --__count) {
if (*__str == __char)
if (*__str == __value)
return __str;
++__str;
}
return nullptr;
#endif
}

_LIBCPP_END_NAMESPACE_STD
Expand Down
Expand Up @@ -29,6 +29,6 @@ static_assert(std::__constexpr_memcmp_equal(Banane, Banane, 6), "");

constexpr bool test_constexpr_wmemchr() {
const char str[] = "Banane";
return std::__constexpr_char_memchr(str, 'n', 6) == str + 2;
return std::__constexpr_memchr(str, 'n', 6) == str + 2;
}
static_assert(test_constexpr_wmemchr(), "");
4 changes: 4 additions & 0 deletions libcxx/test/libcxx/transitive_includes/cxx03.csv
Expand Up @@ -7,6 +7,7 @@ algorithm cstdint
algorithm cstdlib
algorithm cstring
algorithm ctime
algorithm cwchar
algorithm execution
algorithm initializer_list
algorithm iosfwd
Expand Down Expand Up @@ -191,6 +192,7 @@ coroutine version
cstddef version
ctgmath ccomplex
ctgmath cmath
cwchar cstddef
cwchar cwctype
cwctype cctype
deque algorithm
Expand All @@ -201,6 +203,7 @@ deque cstddef
deque cstdint
deque cstdlib
deque cstring
deque cwchar
deque functional
deque initializer_list
deque iosfwd
Expand Down Expand Up @@ -950,6 +953,7 @@ vector cstddef
vector cstdint
vector cstdlib
vector cstring
vector cwchar
vector initializer_list
vector iosfwd
vector limits
Expand Down
4 changes: 4 additions & 0 deletions libcxx/test/libcxx/transitive_includes/cxx11.csv
Expand Up @@ -7,6 +7,7 @@ algorithm cstdint
algorithm cstdlib
algorithm cstring
algorithm ctime
algorithm cwchar
algorithm execution
algorithm initializer_list
algorithm iosfwd
Expand Down Expand Up @@ -191,6 +192,7 @@ coroutine version
cstddef version
ctgmath ccomplex
ctgmath cmath
cwchar cstddef
cwchar cwctype
cwctype cctype
deque algorithm
Expand All @@ -201,6 +203,7 @@ deque cstddef
deque cstdint
deque cstdlib
deque cstring
deque cwchar
deque functional
deque initializer_list
deque iosfwd
Expand Down Expand Up @@ -951,6 +954,7 @@ vector cstddef
vector cstdint
vector cstdlib
vector cstring
vector cwchar
vector initializer_list
vector iosfwd
vector limits
Expand Down
4 changes: 4 additions & 0 deletions libcxx/test/libcxx/transitive_includes/cxx14.csv
Expand Up @@ -7,6 +7,7 @@ algorithm cstdint
algorithm cstdlib
algorithm cstring
algorithm ctime
algorithm cwchar
algorithm execution
algorithm initializer_list
algorithm iosfwd
Expand Down Expand Up @@ -191,6 +192,7 @@ coroutine version
cstddef version
ctgmath ccomplex
ctgmath cmath
cwchar cstddef
cwchar cwctype
cwctype cctype
deque algorithm
Expand All @@ -201,6 +203,7 @@ deque cstddef
deque cstdint
deque cstdlib
deque cstring
deque cwchar
deque functional
deque initializer_list
deque iosfwd
Expand Down Expand Up @@ -953,6 +956,7 @@ vector cstddef
vector cstdint
vector cstdlib
vector cstring
vector cwchar
vector initializer_list
vector iosfwd
vector limits
Expand Down

0 comments on commit 1fd08ed

Please sign in to comment.