[libcxx-commits] [libcxx] [libc++][pstl] Default implementation of parallel std::reverse_copy (PR #208085)
Michael G. Kazakov via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jul 8 07:46:13 PDT 2026
https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/208085
>From d0733a0157e03c566fc6dedce3a340b3a74422ca Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 7 Jul 2026 20:15:09 +0100
Subject: [PATCH 1/2] Added a default implementation of parallel
std::reverse_copy based on std::copy + std::reverse_iterator
---
libcxx/include/__algorithm/pstl.h | 17 +++
libcxx/include/__pstl/backend_fwd.h | 7 ++
libcxx/include/__pstl/backends/default.h | 17 +++
.../pstl.iterator-requirements.verify.cpp | 14 ++-
.../algorithms/pstl.nodiscard.verify.cpp | 2 +
.../alg.reverse/pstl.reverse_copy.pass.cpp | 116 ++++++++++++++++++
.../pstl.exception_handling.pass.cpp | 7 ++
7 files changed, 178 insertions(+), 2 deletions(-)
create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..55654637df57f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -347,6 +347,23 @@ generate_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, _Ge
std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__n), std::move(__gen));
}
+template <class _ExecutionPolicy,
+ class _BidirectionalIterator,
+ class _ForwardIterator,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator reverse_copy(
+ _ExecutionPolicy&& __policy,
+ _BidirectionalIterator __first,
+ _BidirectionalIterator __last,
+ _ForwardIterator __result) {
+ _LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(_BidirectionalIterator, "reverse_copy requires a BidirectionalIterator");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reverse_copy requires a ForwardIterator");
+ using _Implementation = __pstl::__dispatch<__pstl::__reverse_copy, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result));
+}
+
template <class _ExecutionPolicy,
class _ForwardIterator,
class _Predicate,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..6b72350d43a10 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -159,6 +159,13 @@ struct __generate_n;
// optional<__empty>
// operator()(_Policy&&, _ForwardIterator __first, _Size __n, _Generator __gen) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __reverse_copy;
+// template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _BidirectionalIterator __first, _BidirectionalIterator __last,
+// _ForwardIterator __result) const noexcept;
+
template <class _Backend, class _ExecutionPolicy>
struct __merge;
// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..6cbd3bcd0af64 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -21,6 +21,7 @@
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
#include <__pstl/backend_fwd.h>
#include <__pstl/dispatch.h>
#include <__utility/empty.h>
@@ -529,6 +530,22 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
}
};
+template <class _ExecutionPolicy>
+struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+ operator()(_Policy&& __policy,
+ _BidirectionalIterator __first,
+ _BidirectionalIterator __last,
+ _ForwardIterator __result) const noexcept {
+ using _Copy = __dispatch<__copy, __current_configuration, _ExecutionPolicy>;
+ return _Copy()(__policy,
+ std::reverse_iterator<_BidirectionalIterator>(std::move(__last)),
+ std::reverse_iterator<_BidirectionalIterator>(std::move(__first)),
+ std::move(__result));
+ }
+};
+
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..4aa435245e863 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -31,12 +31,16 @@
#include "test_iterators.h"
-using non_forward_iterator = cpp17_input_iterator<int*>;
+using non_forward_iterator = cpp17_input_iterator<int*>;
+using non_bidirectional_iterator = forward_iterator<int*>;
struct non_output_iterator : forward_iterator<int*> {
constexpr int const& operator*() const; // prevent it from being an output iterator
};
-void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execution::sequenced_policy pol) {
+void f(non_forward_iterator non_fwd,
+ non_output_iterator non_output,
+ non_bidirectional_iterator non_bidir,
+ std::execution::sequenced_policy pol) {
auto pred = [](auto&&...) -> bool { return true; };
auto func = [](auto&&...) -> int { return 1; };
int* it = nullptr;
@@ -99,6 +103,12 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
(void)std::generate_n(pol, non_fwd, n, func); // expected-error@*:* {{static assertion failed: generate_n}}
}
+ {
+ (void)std::reverse_copy(
+ pol, non_bidir, non_bidir, it); // expected-error@*:* {{static assertion failed: reverse_copy}}
+ (void)std::reverse_copy(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: reverse_copy}}
+ }
+
{
(void)std::is_partitioned(
pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..38e85fb3b6b83 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -52,4 +52,6 @@ void test() {
std::is_sorted(std::execution::par, std::begin(a), std::end(a));
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
new file mode 100644
index 0000000000000..b805fab2f1fb0
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+// class BidirectionalIterator,
+// class ForwardIterator>
+// ForwardIterator reverse_copy(ExecutionPolicy&& exec,
+// BidirectionalIterator first,
+// BidirectionalIterator last,
+// ForwardIterator result);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(reverse_copy);
+
+static_assert(sfinae_test_reverse_copy<int, int*, int*, int*>);
+static_assert(!sfinae_test_reverse_copy<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+ constexpr size_t affix = 16;
+ // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+ for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+ callable(i);
+ }
+ if (size <= affix)
+ return;
+ // size - 16, size - 15, ..., size - 1
+ for (size_t i = size - affix; i < size; ++i) {
+ callable(i);
+ }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ int ia[] = {0};
+ int ib[std::size(ia)] = {-1};
+ Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+ assert(r == Iter2(std::begin(ib)));
+ assert(ib[0] == -1);
+ r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+ assert(ia[0] == 0);
+ }
+ {
+ int ia[] = {0, 1};
+ int ib[std::size(ia)] = {-1};
+ Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+ assert(r == Iter2(std::end(ib)));
+ assert(ib[0] == 1);
+ assert(ib[1] == 0);
+ }
+ {
+ int ia[] = {0, 1, 2};
+ int ib[std::size(ia)] = {-1};
+ Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+ assert(r == Iter2(std::end(ib)));
+ assert(ib[0] == 2);
+ assert(ib[1] == 1);
+ assert(ib[2] == 0);
+ }
+ {
+ int ia[] = {0, 1, 2, 3};
+ int ib[std::size(ia)] = {-1};
+ Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+ assert(r == Iter2(std::end(ib)));
+ assert(ib[0] == 3);
+ assert(ib[1] == 2);
+ assert(ib[2] == 1);
+ assert(ib[3] == 0);
+ }
+ {
+ int ia[1073];
+ int ib[1073];
+ std::iota(std::begin(ia), std::end(ia), 1);
+ runway_sample(std::size(ia) + 1, [&](size_t i) {
+ Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+ assert(r == Iter2(std::begin(ib) + i));
+ for (size_t j = 0; j < i; ++j) {
+ assert(ib[j] == static_cast<int>(i - j));
+ }
+ });
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(
+ types::concatenate_t<types::bidirectional_iterator_list<int*>, types::bidirectional_iterator_list<const int*>>{},
+ types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+ }});
+}
\ No newline at end of file
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..546862169813e 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -195,6 +195,13 @@ int main(int, char**) {
assert_non_throwing([=, &policy] { (void)std::generate_n(policy, std::move(first1), n, gen); });
}
+ {
+ // reverse_copy(first, last, dest)
+ assert_non_throwing([=, &policy] {
+ (void)std::reverse_copy(policy, std::move(first1), std::move(last1), std::move(dest));
+ });
+ }
+
{
auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
>From 6632a8261585b97670183b719c2fbab3330f839e Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 8 Jul 2026 15:45:42 +0100
Subject: [PATCH 2/2] Added a missing line in the top comments
---
libcxx/include/__pstl/backends/default.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 6cbd3bcd0af64..49ab69b497ddd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -93,6 +93,7 @@ namespace __pstl {
// - move
// - copy
// - copy_n
+// - reverse_copy
// - rotate_copy
//
More information about the libcxx-commits
mailing list