mirror of
https://github.com/intel/llvm.git
synced 2026-01-20 19:07:53 +08:00
[libc++] Forward more often to memmove in copy
In D122982 I accidentally disabled the memmove optimization. This re-enables it and adds more cases where copy forwards to memmove. Fixes https://github.com/llvm/llvm-project/issues/33687 Reviewed By: var-const, #libc, ldionne Spies: pkasting, ayzhao, dcheng, xbolva00, libcxx-commits Differential Revision: https://reviews.llvm.org/D124328
This commit is contained in:
@@ -56,25 +56,41 @@ pair<_InValueT*, _OutValueT*> __copy_impl(_InValueT* __first, _InValueT* __last,
|
||||
return std::make_pair(__first + __n, __result + __n);
|
||||
}
|
||||
|
||||
template <class _InValueT,
|
||||
class _OutValueT,
|
||||
class = __enable_if_t<is_same<typename remove_const<_InValueT>::type, _OutValueT>::value
|
||||
&& is_trivially_copy_assignable<_OutValueT>::value> >
|
||||
template <class _Iter>
|
||||
using __is_trivially_copy_assignable_unwrapped =
|
||||
_And<__is_cpp17_contiguous_iterator<_Iter>, is_trivially_copy_assignable<__iter_value_type<_Iter> > >;
|
||||
|
||||
template <class _InIter,
|
||||
class _OutIter,
|
||||
class = __enable_if_t<is_same<typename remove_const<typename iterator_traits<_InIter>::value_type>::type,
|
||||
typename iterator_traits<_OutIter>::value_type>::value
|
||||
&& __is_trivially_copy_assignable_unwrapped<_InIter>::value
|
||||
&& __is_trivially_copy_assignable_unwrapped<_OutIter>::value> >
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
pair<reverse_iterator<_InValueT*>, reverse_iterator<_OutValueT*> >
|
||||
__copy_impl(reverse_iterator<_InValueT*> __first,
|
||||
reverse_iterator<_InValueT*> __last,
|
||||
reverse_iterator<_OutValueT*> __result) {
|
||||
auto __first_base = __first.base();
|
||||
auto __last_base = __last.base();
|
||||
auto __result_base = __result.base();
|
||||
pair<reverse_iterator<_InIter>, reverse_iterator<_OutIter> >
|
||||
__copy_impl(reverse_iterator<_InIter> __first,
|
||||
reverse_iterator<_InIter> __last,
|
||||
reverse_iterator<_OutIter> __result) {
|
||||
auto __first_base = std::__unwrap_iter(__first.base());
|
||||
auto __last_base = std::__unwrap_iter(__last.base());
|
||||
auto __result_base = std::__unwrap_iter(__result.base());
|
||||
auto __result_first = __result_base - (__first_base - __last_base);
|
||||
std::__copy_impl(__last_base, __first_base, __result_first);
|
||||
return std::make_pair(__last, reverse_iterator<_OutValueT*>(__result_first));
|
||||
return std::make_pair(__last, reverse_iterator<_OutIter>(std::__rewrap_iter(__result.base(), __result_first)));
|
||||
}
|
||||
|
||||
template <class _InIter, class _Sent, class _OutIter>
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
pair<reverse_iterator<reverse_iterator<_InIter> >, reverse_iterator<reverse_iterator<_OutIter> > >
|
||||
__copy_impl(reverse_iterator<reverse_iterator<_InIter> > __first,
|
||||
reverse_iterator<reverse_iterator<_Sent> > __last,
|
||||
reverse_iterator<reverse_iterator<_OutIter> > __result);
|
||||
|
||||
template <class _InIter, class _Sent, class _OutIter,
|
||||
__enable_if_t<!(is_copy_constructible<_InIter>::value
|
||||
&& is_copy_constructible<_Sent>::value
|
||||
&& is_copy_constructible<_OutIter>::value), int> = 0 >
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
pair<_InIter, _OutIter> __copy(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
return std::__copy_impl(std::move(__first), std::move(__last), std::move(__result));
|
||||
}
|
||||
@@ -82,13 +98,26 @@ pair<_InIter, _OutIter> __copy(_InIter __first, _Sent __last, _OutIter __result)
|
||||
template <class _InIter, class _Sent, class _OutIter,
|
||||
__enable_if_t<is_copy_constructible<_InIter>::value
|
||||
&& is_copy_constructible<_Sent>::value
|
||||
&& is_copy_constructible<_OutIter>::value> >
|
||||
&& is_copy_constructible<_OutIter>::value, int> = 0>
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
pair<_InIter, _OutIter> __copy(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
pair<_InIter, _OutIter>
|
||||
__copy(_InIter __first, _Sent __last, _OutIter __result) {
|
||||
auto __ret = std::__copy_impl(std::__unwrap_iter(__first), std::__unwrap_iter(__last), std::__unwrap_iter(__result));
|
||||
return std::make_pair(std::__rewrap_iter(__first, __ret.first), std::__rewrap_iter(__result, __ret.second));
|
||||
}
|
||||
|
||||
// __unwrap_iter can't unwrap random_access_iterators, so we need to unwrap two reverse_iterators manually
|
||||
template <class _InIter, class _Sent, class _OutIter>
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
pair<reverse_iterator<reverse_iterator<_InIter> >, reverse_iterator<reverse_iterator<_OutIter> > >
|
||||
__copy_impl(reverse_iterator<reverse_iterator<_InIter> > __first,
|
||||
reverse_iterator<reverse_iterator<_Sent> > __last,
|
||||
reverse_iterator<reverse_iterator<_OutIter> > __result) {
|
||||
auto __ret = std::__copy(__first.base().base(), __last.base().base(), __result.base().base());
|
||||
return std::make_pair(reverse_iterator<reverse_iterator<_InIter> >(reverse_iterator<_InIter>(__ret.first)),
|
||||
reverse_iterator<reverse_iterator<_OutIter> >(reverse_iterator<_OutIter>(__ret.second)));
|
||||
}
|
||||
|
||||
template <class _InputIterator, class _OutputIterator>
|
||||
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
|
||||
_OutputIterator
|
||||
|
||||
@@ -469,27 +469,28 @@ template <class _Up>
|
||||
struct __is_cpp17_contiguous_iterator<_Up*> : true_type {};
|
||||
|
||||
|
||||
template <class _Iter>
|
||||
class __wrap_iter;
|
||||
|
||||
template <class _Tp>
|
||||
struct __is_exactly_cpp17_input_iterator
|
||||
: public integral_constant<bool,
|
||||
__has_iterator_category_convertible_to<_Tp, input_iterator_tag>::value &&
|
||||
!__has_iterator_category_convertible_to<_Tp, forward_iterator_tag>::value> {};
|
||||
|
||||
#if _LIBCPP_STD_VER >= 17
|
||||
template<class _InputIterator>
|
||||
using __iter_value_type = typename iterator_traits<_InputIterator>::value_type;
|
||||
|
||||
template<class _InputIterator>
|
||||
using __iter_key_type = remove_const_t<typename iterator_traits<_InputIterator>::value_type::first_type>;
|
||||
using __iter_key_type = typename remove_const<typename iterator_traits<_InputIterator>::value_type::first_type>::type;
|
||||
|
||||
template<class _InputIterator>
|
||||
using __iter_mapped_type = typename iterator_traits<_InputIterator>::value_type::second_type;
|
||||
|
||||
template<class _InputIterator>
|
||||
using __iter_to_alloc_type = pair<
|
||||
add_const_t<typename iterator_traits<_InputIterator>::value_type::first_type>,
|
||||
typename add_const<typename iterator_traits<_InputIterator>::value_type::first_type>::type,
|
||||
typename iterator_traits<_InputIterator>::value_type::second_type>;
|
||||
#endif // _LIBCPP_STD_VER >= 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
// UNSUPPORTED: debug_level=1
|
||||
|
||||
// <algorithm>
|
||||
|
||||
// This test checks that std::copy forwards to memmove when appropriate.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
struct S {
|
||||
int i;
|
||||
constexpr S(int i_) : i(i_) {}
|
||||
S(const S&) = default;
|
||||
S(S&&) = delete;
|
||||
constexpr S& operator=(const S&) = default;
|
||||
S& operator=(S&&) = delete;
|
||||
constexpr bool operator==(const S&) const = default;
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<S>);
|
||||
|
||||
template <class T>
|
||||
struct NotIncrementableIt {
|
||||
T* i;
|
||||
using iterator_category = std::contiguous_iterator_tag;
|
||||
using iterator_concept = std::contiguous_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
|
||||
constexpr NotIncrementableIt(T* i_) : i(i_) {}
|
||||
|
||||
friend constexpr bool operator==(const NotIncrementableIt& lhs, const NotIncrementableIt& rhs) {
|
||||
return lhs.i == rhs.i;
|
||||
}
|
||||
|
||||
constexpr T& operator*() { return *i; }
|
||||
constexpr T* operator->() { return i; }
|
||||
constexpr T* operator->() const { return i; }
|
||||
|
||||
constexpr NotIncrementableIt& operator++() {
|
||||
assert(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr NotIncrementableIt& operator--() {
|
||||
assert(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend constexpr NotIncrementableIt operator+(const NotIncrementableIt& it, ptrdiff_t size) { return it.i + size; }
|
||||
};
|
||||
|
||||
static_assert(std::__is_cpp17_contiguous_iterator<NotIncrementableIt<S>>::value);
|
||||
|
||||
template <class Iter>
|
||||
constexpr void test_normal() {
|
||||
S a[] = {1, 2, 3, 4};
|
||||
S b[] = {0, 0, 0, 0};
|
||||
std::copy(Iter(a), Iter(a + 4), Iter(b));
|
||||
assert(std::equal(a, a + 4, b));
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
constexpr void test_reverse() {
|
||||
S a[] = {1, 2, 3, 4};
|
||||
S b[] = {0, 0, 0, 0};
|
||||
std::copy(std::make_reverse_iterator(Iter(a + 4)),
|
||||
std::make_reverse_iterator(Iter(a)),
|
||||
std::make_reverse_iterator(Iter(b + 4)));
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
constexpr void test_reverse_reverse() {
|
||||
S a[] = {1, 2, 3, 4};
|
||||
S b[] = {0, 0, 0, 0};
|
||||
std::copy(std::make_reverse_iterator(std::make_reverse_iterator(Iter(a))),
|
||||
std::make_reverse_iterator(std::make_reverse_iterator(Iter(a + 4))),
|
||||
std::make_reverse_iterator(std::make_reverse_iterator(Iter(b))));
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
test_normal<S*>();
|
||||
test_normal<NotIncrementableIt<S>>();
|
||||
test_reverse<S*>();
|
||||
test_reverse<NotIncrementableIt<S>>();
|
||||
test_reverse_reverse<S*>();
|
||||
test_reverse_reverse<NotIncrementableIt<S>>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
}
|
||||
@@ -38,7 +38,7 @@ test(S s, typename S::size_type pos1, typename S::size_type n1, const typename S
|
||||
}
|
||||
|
||||
template <class S>
|
||||
TEST_CONSTEXPR_CXX20 void test0()
|
||||
TEST_CONSTEXPR_CXX20 bool test0()
|
||||
{
|
||||
test(S(""), 0, 0, "", S(""));
|
||||
test(S(""), 0, 0, "12345", S("12345"));
|
||||
@@ -140,10 +140,12 @@ TEST_CONSTEXPR_CXX20 void test0()
|
||||
test(S("abcdefghij"), 1, 1, "12345", S("a12345cdefghij"));
|
||||
test(S("abcdefghij"), 1, 1, "1234567890", S("a1234567890cdefghij"));
|
||||
test(S("abcdefghij"), 1, 1, "12345678901234567890", S("a12345678901234567890cdefghij"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class S>
|
||||
TEST_CONSTEXPR_CXX20 void test1()
|
||||
TEST_CONSTEXPR_CXX20 bool test1()
|
||||
{
|
||||
test(S("abcdefghij"), 1, 4, "", S("afghij"));
|
||||
test(S("abcdefghij"), 1, 4, "12345", S("a12345fghij"));
|
||||
@@ -245,10 +247,12 @@ TEST_CONSTEXPR_CXX20 void test1()
|
||||
test(S("abcdefghijklmnopqrst"), 10, 9, "12345", S("abcdefghij12345t"));
|
||||
test(S("abcdefghijklmnopqrst"), 10, 9, "1234567890", S("abcdefghij1234567890t"));
|
||||
test(S("abcdefghijklmnopqrst"), 10, 9, "12345678901234567890", S("abcdefghij12345678901234567890t"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class S>
|
||||
TEST_CONSTEXPR_CXX20 void test2()
|
||||
TEST_CONSTEXPR_CXX20 bool test2()
|
||||
{
|
||||
test(S("abcdefghijklmnopqrst"), 10, 10, "", S("abcdefghij"));
|
||||
test(S("abcdefghijklmnopqrst"), 10, 10, "12345", S("abcdefghij12345"));
|
||||
@@ -266,14 +270,36 @@ TEST_CONSTEXPR_CXX20 void test2()
|
||||
test(S("abcdefghijklmnopqrst"), 20, 0, "12345", S("abcdefghijklmnopqrst12345"));
|
||||
test(S("abcdefghijklmnopqrst"), 20, 0, "1234567890", S("abcdefghijklmnopqrst1234567890"));
|
||||
test(S("abcdefghijklmnopqrst"), 20, 0, "12345678901234567890", S("abcdefghijklmnopqrst12345678901234567890"));
|
||||
|
||||
{ // test replacing into self
|
||||
S s_short = "123/";
|
||||
S s_long = "Lorem ipsum dolor sit amet, consectetur/";
|
||||
|
||||
s_short.replace(s_short.begin(), s_short.begin(), s_short.c_str());
|
||||
assert(s_short == "123/123/");
|
||||
s_short.replace(s_short.begin(), s_short.begin(), s_short.c_str());
|
||||
assert(s_short == "123/123/123/123/");
|
||||
s_short.replace(s_short.begin(), s_short.begin(), s_short.c_str());
|
||||
assert(s_short == "123/123/123/123/123/123/123/123/");
|
||||
|
||||
s_long.replace(s_long.begin(), s_long.begin(), s_long.c_str());
|
||||
assert(s_long == "Lorem ipsum dolor sit amet, consectetur/Lorem ipsum dolor sit amet, consectetur/");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
TEST_CONSTEXPR_CXX20 void test() {
|
||||
{
|
||||
typedef std::string S;
|
||||
test0<S>();
|
||||
test1<S>();
|
||||
test2<S>();
|
||||
#if TEST_STD_VER > 17
|
||||
static_assert(test0<S>());
|
||||
static_assert(test1<S>());
|
||||
static_assert(test2<S>());
|
||||
#endif
|
||||
}
|
||||
#if TEST_STD_VER >= 11
|
||||
{
|
||||
@@ -281,34 +307,18 @@ TEST_CONSTEXPR_CXX20 bool test() {
|
||||
test0<S>();
|
||||
test1<S>();
|
||||
test2<S>();
|
||||
#if TEST_STD_VER > 17
|
||||
static_assert(test0<S>());
|
||||
static_assert(test1<S>());
|
||||
static_assert(test2<S>());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
{ // test replacing into self
|
||||
typedef std::string S;
|
||||
S s_short = "123/";
|
||||
S s_long = "Lorem ipsum dolor sit amet, consectetur/";
|
||||
|
||||
s_short.replace(s_short.begin(), s_short.begin(), s_short.c_str());
|
||||
assert(s_short == "123/123/");
|
||||
s_short.replace(s_short.begin(), s_short.begin(), s_short.c_str());
|
||||
assert(s_short == "123/123/123/123/");
|
||||
s_short.replace(s_short.begin(), s_short.begin(), s_short.c_str());
|
||||
assert(s_short == "123/123/123/123/123/123/123/123/");
|
||||
|
||||
s_long.replace(s_long.begin(), s_long.begin(), s_long.c_str());
|
||||
assert(s_long == "Lorem ipsum dolor sit amet, consectetur/Lorem ipsum dolor sit amet, consectetur/");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test();
|
||||
#if TEST_STD_VER > 17
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ test(S s, typename S::size_type pos1, typename S::size_type n1, SV sv, S expecte
|
||||
}
|
||||
|
||||
template <class S, class SV>
|
||||
TEST_CONSTEXPR_CXX20 void test0()
|
||||
TEST_CONSTEXPR_CXX20 bool test0()
|
||||
{
|
||||
test(S(""), 0, 0, SV(""), S(""));
|
||||
test(S(""), 0, 0, SV("12345"), S("12345"));
|
||||
@@ -138,10 +138,12 @@ TEST_CONSTEXPR_CXX20 void test0()
|
||||
test(S("abcdefghij"), 1, 1, SV("12345"), S("a12345cdefghij"));
|
||||
test(S("abcdefghij"), 1, 1, SV("1234567890"), S("a1234567890cdefghij"));
|
||||
test(S("abcdefghij"), 1, 1, SV("12345678901234567890"), S("a12345678901234567890cdefghij"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class S, class SV>
|
||||
TEST_CONSTEXPR_CXX20 void test1()
|
||||
TEST_CONSTEXPR_CXX20 bool test1()
|
||||
{
|
||||
test(S("abcdefghij"), 1, 4, SV(""), S("afghij"));
|
||||
test(S("abcdefghij"), 1, 4, SV("12345"), S("a12345fghij"));
|
||||
@@ -243,10 +245,12 @@ TEST_CONSTEXPR_CXX20 void test1()
|
||||
test(S("abcdefghijklmnopqrst"), 10, 9, SV("12345"), S("abcdefghij12345t"));
|
||||
test(S("abcdefghijklmnopqrst"), 10, 9, SV("1234567890"), S("abcdefghij1234567890t"));
|
||||
test(S("abcdefghijklmnopqrst"), 10, 9, SV("12345678901234567890"), S("abcdefghij12345678901234567890t"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class S, class SV>
|
||||
TEST_CONSTEXPR_CXX20 void test2()
|
||||
TEST_CONSTEXPR_CXX20 bool test2()
|
||||
{
|
||||
test(S("abcdefghijklmnopqrst"), 10, 10, SV(""), S("abcdefghij"));
|
||||
test(S("abcdefghijklmnopqrst"), 10, 10, SV("12345"), S("abcdefghij12345"));
|
||||
@@ -264,15 +268,22 @@ TEST_CONSTEXPR_CXX20 void test2()
|
||||
test(S("abcdefghijklmnopqrst"), 20, 0, SV("12345"), S("abcdefghijklmnopqrst12345"));
|
||||
test(S("abcdefghijklmnopqrst"), 20, 0, SV("1234567890"), S("abcdefghijklmnopqrst1234567890"));
|
||||
test(S("abcdefghijklmnopqrst"), 20, 0, SV("12345678901234567890"), S("abcdefghijklmnopqrst12345678901234567890"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
TEST_CONSTEXPR_CXX20 void test() {
|
||||
{
|
||||
typedef std::string S;
|
||||
typedef std::string_view SV;
|
||||
test0<S, SV>();
|
||||
test1<S, SV>();
|
||||
test2<S, SV>();
|
||||
#if TEST_STD_VER > 17
|
||||
static_assert(test0<S, SV>());
|
||||
static_assert(test1<S, SV>());
|
||||
static_assert(test2<S, SV>());
|
||||
#endif
|
||||
}
|
||||
#if TEST_STD_VER >= 11
|
||||
{
|
||||
@@ -281,18 +292,18 @@ TEST_CONSTEXPR_CXX20 bool test() {
|
||||
test0<S, SV>();
|
||||
test1<S, SV>();
|
||||
test2<S, SV>();
|
||||
#if TEST_STD_VER > 17
|
||||
static_assert(test0<S, SV>());
|
||||
static_assert(test1<S, SV>());
|
||||
static_assert(test2<S, SV>());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test();
|
||||
#if TEST_STD_VER > 17
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user