From d2a46e6480f350725a6add30662e315b7a8aacfe Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Sun, 22 Oct 2023 10:23:22 +0200 Subject: [PATCH] [libc++][PSTL] Implement std::move Reviewed By: #libc, ldionne Spies: ldionne, libcxx-commits Differential Revision: https://reviews.llvm.org/D155330 --- libcxx/docs/Status/PSTLPaper.csv | 2 +- libcxx/include/CMakeLists.txt | 1 + libcxx/include/__algorithm/pstl_backend.h | 3 + libcxx/include/__algorithm/pstl_move.h | 78 +++++++++++++++ libcxx/include/algorithm | 1 + ..._customization_points_not_working.pass.cpp | 13 ++- .../alg.move/pstl.exception_handling.pass.cpp | 39 ++++++++ .../alg.move/pstl.move.pass.cpp | 99 +++++++++++++++++++ 8 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 libcxx/include/__algorithm/pstl_move.h create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.move.pass.cpp diff --git a/libcxx/docs/Status/PSTLPaper.csv b/libcxx/docs/Status/PSTLPaper.csv index f4e130a5de984..1ca0a6ed3539b 100644 --- a/libcxx/docs/Status/PSTLPaper.csv +++ b/libcxx/docs/Status/PSTLPaper.csv @@ -36,7 +36,7 @@ Section,Description,Assignee,Complete | `[alg.min.max] `_,std::min_element,Nikolas Klauser,|Not Started| | `[alg.min.max] `_,std::minmax_element,Nikolas Klauser,|Not Started| | `[mismatch] `_,std::mismatch,Nikolas Klauser,|Not Started| -| `[alg.move] `_,std::move,Nikolas Klauser,|In Progress| +| `[alg.move] `_,std::move,Nikolas Klauser,|Complete| | `[alg.none.of] `_,std::none_of,Nikolas Klauser,|Complete| | `[alg.nth.element] `_,std::nth_element,Nikolas Klauser,|Not Started| | `[alg.sort] `_,std::partial_sort,Nikolas Klauser,|Not Started| diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 85470c5b337f3..e425af4fda8d6 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -94,6 +94,7 @@ set(files __algorithm/pstl_generate.h __algorithm/pstl_is_partitioned.h __algorithm/pstl_merge.h + __algorithm/pstl_move.h __algorithm/pstl_replace.h __algorithm/pstl_sort.h __algorithm/pstl_stable_sort.h diff --git a/libcxx/include/__algorithm/pstl_backend.h b/libcxx/include/__algorithm/pstl_backend.h index 0bf2cca5eef48..1f5c13d49eda1 100644 --- a/libcxx/include/__algorithm/pstl_backend.h +++ b/libcxx/include/__algorithm/pstl_backend.h @@ -131,6 +131,9 @@ implemented, all the algorithms will eventually forward to the basis algorithms _OutIterator __result, _Comp __comp); + template + optional<_OutIterator> __pstl_move(_Backend, _Iterator __first, _Iterator __last, _OutIterator __result); + template optional<_Tp> __pstl_reduce(_Backend, _Iterator __first, _Iterator __last, _Tp __init, _BinaryOperation __op); diff --git a/libcxx/include/__algorithm/pstl_move.h b/libcxx/include/__algorithm/pstl_move.h new file mode 100644 index 0000000000000..775dc26049313 --- /dev/null +++ b/libcxx/include/__algorithm/pstl_move.h @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// 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___ALGORITHM_PSTL_MOVE_H +#define _LIBCPP___ALGORITHM_PSTL_MOVE_H + +#include <__algorithm/copy_n.h> +#include <__algorithm/pstl_frontend_dispatch.h> +#include <__algorithm/pstl_transform.h> +#include <__config> +#include <__functional/identity.h> +#include <__iterator/iterator_traits.h> +#include <__type_traits/enable_if.h> +#include <__type_traits/is_constant_evaluated.h> +#include <__type_traits/is_execution_policy.h> +#include <__type_traits/is_trivially_copyable.h> +#include <__type_traits/remove_cvref.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17 + +_LIBCPP_BEGIN_NAMESPACE_STD + +// TODO: Use the std::copy/move shenanigans to forward to std::memmove +// Investigate whether we want to still forward to std::transform(policy) +// in that case for the execution::par part, or whether we actually want +// to run everything serially in that case. + +template +void __pstl_move(); + +template , + enable_if_t, int> = 0> +[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardOutIterator> +__move(_ExecutionPolicy&& __policy, + _ForwardIterator&& __first, + _ForwardIterator&& __last, + _ForwardOutIterator&& __result) noexcept { + return std::__pstl_frontend_dispatch( + _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_move, _RawPolicy), + [&__policy](_ForwardIterator __g_first, _ForwardIterator __g_last, _ForwardOutIterator __g_result) { + return std::__transform(__policy, __g_first, __g_last, __g_result, [](auto&& __v) { return std::move(__v); }); + }, + std::move(__first), + std::move(__last), + std::move(__result)); +} + +template , + enable_if_t, int> = 0> +_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator +move(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _ForwardOutIterator __result) { + auto __res = std::__move(__policy, std::move(__first), std::move(__last), std::move(__result)); + if (!__res) + std::__throw_bad_alloc(); + return *__res; +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17 + +#endif // _LIBCPP___ALGORITHM_PSTL_MOVE_H diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index 69ba9537dda69..ac335cb39e829 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -1833,6 +1833,7 @@ template #include <__algorithm/pstl_generate.h> #include <__algorithm/pstl_is_partitioned.h> #include <__algorithm/pstl_merge.h> +#include <__algorithm/pstl_move.h> #include <__algorithm/pstl_replace.h> #include <__algorithm/pstl_sort.h> #include <__algorithm/pstl_stable_sort.h> diff --git a/libcxx/test/libcxx/algorithms/pstl.robust_against_customization_points_not_working.pass.cpp b/libcxx/test/libcxx/algorithms/pstl.robust_against_customization_points_not_working.pass.cpp index e3733bf468da4..071fc50b2fe54 100644 --- a/libcxx/test/libcxx/algorithms/pstl.robust_against_customization_points_not_working.pass.cpp +++ b/libcxx/test/libcxx/algorithms/pstl.robust_against_customization_points_not_working.pass.cpp @@ -172,6 +172,15 @@ optional<__empty> __pstl_fill_n(TestBackend, ForwardIterator, Size, Func) { return __empty{}; } +bool pstl_move_called = false; + +template +ForwardIterator __pstl_move(TestBackend, ForwardIterator, Size, Func) { + assert(!pstl_move_called); + pstl_move_called = true; + return 0; +} + bool pstl_is_partitioned_called = false; template @@ -352,7 +361,9 @@ int main(int, char**) { (void)std::generate_n(TestPolicy{}, std::begin(a), std::size(a), pred); assert(std::pstl_generate_n_called); (void)std::is_partitioned(TestPolicy{}, std::begin(a), std::end(a), pred); - assert(std::pstl_generate_n_called); + assert(std::pstl_is_partitioned_called); + (void)std::move(TestPolicy{}, std::begin(a), std::end(a), std::begin(a)); + assert(std::pstl_move_called); (void)std::replace(TestPolicy{}, std::begin(a), std::end(a), 0, 0); assert(std::pstl_replace_called); (void)std::replace_if(TestPolicy{}, std::begin(a), std::end(a), pred, 0); diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp new file mode 100644 index 0000000000000..ffe4f1866ebe6 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.exception_handling.pass.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: no-exceptions +// REQUIRES: has-unix-headers + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +// check that std::move(ExecutionPolicy) terminates on user-thrown exceptions + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" +#include "test_iterators.h" + +int main(int, char**) { + test_execution_policies([](auto&& policy) { + EXPECT_STD_TERMINATE([&] { + try { + int a[] = {1, 2}; + int b[] = {1, 2}; + (void)std::move(policy, + util::throw_on_move_iterator(std::begin(a), 1), + util::throw_on_move_iterator(std::end(a), 1), + util::throw_on_move_iterator(std::begin(b), 1)); + } catch (const util::iterator_error&) { + assert(false); + } + std::terminate(); // make the test pass in case the algorithm didn't move the iterator + }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.move.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.move.pass.cpp new file mode 100644 index 0000000000000..e4cc5649ce5d8 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/pstl.move.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +// + +// template +// ForwardIterator2 move(ExecutionPolicy&& policy, +// ForwardIterator1 first, ForwardIterator1 last, +// ForwardIterator2 result); + +#include +#include + +#include "test_macros.h" +#include "test_execution_policies.h" +#include "test_iterators.h" + +EXECUTION_POLICY_SFINAE_TEST(move); + +static_assert(sfinae_test_move); +static_assert(!sfinae_test_move); + +template +struct TestInt { + template + void operator()(Policy&& policy) { + // simple test + for (const int size : {0, 1, 2, 100, 350}) { + std::vector a(size); + for (int i = 0; i != size; ++i) + a[i] = i + 1; + + std::vector out(std::size(a)); + decltype(auto) ret = + std::move(policy, Iter1(std::data(a)), Iter1(std::data(a) + std::size(a)), Iter2(std::data(out))); + static_assert(std::is_same_v); + assert(base(ret) == std::data(out) + std::size(out)); + for (int i = 0; i != size; ++i) + assert(out[i] == i + 1); + } + } +}; + +struct MovedToTester { + bool moved_to = false; + MovedToTester() = default; + MovedToTester(MovedToTester&&) {} + MovedToTester& operator=(MovedToTester&&) { + assert(!moved_to); + moved_to = true; + return *this; + } + ~MovedToTester() = default; +}; + +template +struct TestNonTrivial { + template + void operator()(Policy&& policy) { + // simple test + for (const int size : {0, 1, 2, 100, 350}) { + std::vector a(size); + + std::vector out(std::size(a)); + auto ret = std::move(policy, Iter1(std::data(a)), Iter1(std::data(a) + std::size(a)), Iter2(std::data(out))); + assert(base(ret) == std::data(out) + std::size(out)); + assert(std::all_of(std::begin(out), std::end(out), [](MovedToTester& t) { return t.moved_to; })); + assert(std::none_of(std::begin(a), std::end(a), [](MovedToTester& t) { return t.moved_to; })); + } + } +}; + +int main(int, char**) { + types::for_each(types::forward_iterator_list{}, types::apply_type_identity{[](auto v) { + using Iter = typename decltype(v)::type; + types::for_each( + types::forward_iterator_list{}, + TestIteratorWithPolicies< types::partial_instantiation::template apply>{}); + }}); + + types::for_each( + types::forward_iterator_list{}, types::apply_type_identity{[](auto v) { + using Iter = typename decltype(v)::type; + types::for_each( + types::forward_iterator_list{}, + TestIteratorWithPolicies< types::partial_instantiation::template apply>{}); + }}); + + return 0; +}