Repository: llvm/llvm-project
File: libcxx/include/__algorithm/find.h (C/C++)

Code before:
#include <__type_traits/invoke.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_equality_comparable.h>
#include <__type_traits/is_integral.h>
#include <__type_traits/is_signed.h>
#include <__utility/move.h>
#include <limits>

#if _LIBCPP_HAS_WIDE_CHARACTERS
#  include <cwchar>
#endif

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

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

// generic implementation
template <class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iter
__find_loop(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
  return std::__find_if(
      std::move(__first),
      std::move(__last),
      [&]<class _ValT>(_ValT&& __val) -> bool { return __val == __value; },
      __proj);
}

template <class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iter
__find(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
  return std::__find_loop(std::move(__first), std::move(__last), __value, __proj);
}

#if _LIBCPP_VECTORIZE_ALGORITHMS
template <class _Tp, class _Up>
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last, _Up __value) {
  if (!__libcpp_is_constant_evaluated()) {
    constexpr size_t __unroll_count = 4;
    constexpr size_t __vec_size     = __native_vector_size<_Tp>;
    using __vec                     = __simd_vector<_Tp, __vec_size>;

    auto __orig_first = __first;

    auto __values = static_cast<__simd_vector<_Tp, __vec_size>>(__value); // broadcast the value
    while (static_cast<size_t>(__last - __first) >= __unroll_count * __vec_size) [[__unlikely__]] {

Review comment (performance):
This is a bit tricky: this function explicitly advertises that it's implemented as a loop, and it's arguably important for that to be the case for the optimization logic below. Switching to `__find_if` means that this might not be the case anymore if we optimize `__find_if`.

Code after:
#include <__type_traits/invoke.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_equality_comparable.h>
#include <__type_traits/is_integral.h>
#include <__type_traits/is_signed.h>
#include <__utility/move.h>
#include <limits>

#if _LIBCPP_HAS_WIDE_CHARACTERS
#  include <cwchar>
#endif

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

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

// generic implementation
template <class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iter
__find_generic(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
  return std::__find_if(
      std::move(__first),
      std::move(__last),
      [&]<class _ValT>(_ValT&& __val) -> bool { return __val == __value; },
      __proj);
}

template <class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iter
__find(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
  return std::__find_generic(std::move(__first), std::move(__last), __value, __proj);
}

#if _LIBCPP_VECTORIZE_ALGORITHMS
template <class _Tp, class _Up>
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last, _Up __value) {
  if (!__libcpp_is_constant_evaluated()) {
    constexpr size_t __unroll_count = 4;
    constexpr size_t __vec_size     = __native_vector_size<_Tp>;
    using __vec                     = __simd_vector<_Tp, __vec_size>;

    auto __orig_first = __first;

    auto __values = static_cast<__simd_vector<_Tp, __vec_size>>(__value); // broadcast the value
    while (static_cast<size_t>(__last - __first) >= __unroll_count * __vec_size) [[__unlikely__]] {