[libcxx-commits] [libcxx] 410a2a2 - [libc++][pstl] Default implementation of parallel std::reverse_copy (#208085)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jul 11 16:30:02 PDT 2026


Author: Michael G. Kazakov
Date: 2026-07-12T07:29:57+08:00
New Revision: 410a2a236355a432c6a73cbd3338badabd280dc8

URL: https://github.com/llvm/llvm-project/commit/410a2a236355a432c6a73cbd3338badabd280dc8
DIFF: https://github.com/llvm/llvm-project/commit/410a2a236355a432c6a73cbd3338badabd280dc8.diff

LOG: [libc++][pstl] Default implementation of parallel std::reverse_copy (#208085)

This PR adds a default parallel implementation of `std::reverse_copy`
based on `std::reverse_iterator` + parallel `std::copy`.

Effectively it's a one-liner:
```c++
    return _Copy()(__policy,
                   std::reverse_iterator<_BidirectionalIterator>(std::move(__last)),
                   std::reverse_iterator<_BidirectionalIterator>(std::move(__first)),
                   std::move(__result));
```

Included tests check that:

- Semantics of the function is correct.
- The function correctly SFINAE out when the first argument is not an
execution policy.
- The `nodiscard` policy is followed.
- The `noexcept` policy is followed.
- `static_assert` verifies iterators' categories (bidirectional and
forward).

Part of #99938.

---------

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>

Added: 
    libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp

Modified: 
    libcxx/include/__algorithm/pstl.h
    libcxx/include/__pstl/backend_fwd.h
    libcxx/include/__pstl/backends/default.h
    libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
    libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
    libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 3530b3207f59ef..2bf9b4276baab6 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -395,6 +395,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 1ea121367fbc7d..a84a768cad5707 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -166,6 +166,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 cb963a130f7432..5c78d4efa412b9 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -23,6 +23,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 <__type_traits/desugars_to.h>
@@ -96,6 +97,7 @@ namespace __pstl {
 // - move
 // - copy
 // - copy_n
+// - reverse_copy
 // - rotate_copy
 //
 
@@ -557,6 +559,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 aee3f97019071d..fbe3460b67f174 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;
@@ -110,6 +114,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 747d62bc668379..049c8dc88c2da2 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -56,4 +56,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 00000000000000..d81dde163cf023
--- /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>{});
+      }});
+}

diff  --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 9e6c8f5b8ce409..8af2a0a15fd5cc 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -210,6 +210,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; });
 


        


More information about the libcxx-commits mailing list