[libcxx-commits] [libcxx] [libc++][pstl] Implementation of parallel std::reverse() based on parallel for_each (PR #213487)

Michael G. Kazakov via libcxx-commits libcxx-commits at lists.llvm.org
Sat Aug 1 13:55:03 PDT 2026


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/213487

>From 24201ea1cc305bfb42ce1fea8d329bb6af264250 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 1 Aug 2026 21:17:19 +0100
Subject: [PATCH 1/2] Implementation of a parallel reverse() on top of a
 parallel for_each()

---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__algorithm/pstl.h             | 12 +++
 libcxx/include/__pstl/backend_fwd.h           |  6 ++
 libcxx/include/__pstl/backends/libdispatch.h  |  5 +
 libcxx/include/__pstl/backends/serial.h       | 11 +++
 libcxx/include/__pstl/backends/std_thread.h   |  5 +
 libcxx/include/__pstl/cpu_algos/reverse.h     | 68 ++++++++++++++
 .../pstl.iterator-requirements.verify.cpp     |  4 +
 .../alg.reverse/pstl.reverse.pass.cpp         | 92 +++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  5 +
 10 files changed, 209 insertions(+)
 create mode 100644 libcxx/include/__pstl/cpu_algos/reverse.h
 create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5034d5f6fe95b..dc1d8bb26bac4 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -691,6 +691,7 @@ set(files
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
   __pstl/cpu_algos/mismatch.h
+  __pstl/cpu_algos/reverse.h
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index bf6c2a030d348..2c628c4c8698f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -560,6 +560,18 @@ 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 _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void
+reverse(_ExecutionPolicy&& __policy, _BidirectionalIterator __first, _BidirectionalIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(_BidirectionalIterator, "reverse requires a BidirectionalIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__reverse, __pstl::__current_configuration, _RawPolicy>;
+  __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last));
+}
+
 template <class _ExecutionPolicy,
           class _BidirectionalIterator,
           class _ForwardIterator,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index eaa987a2a3c94..248c90ba16596 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -166,6 +166,12 @@ struct __generate_n;
 // optional<__empty>
 // operator()(_Policy&&, _ForwardIterator __first, _Size __n, _Generator __gen) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __reverse;
+// template <class _Policy, class _BidirectionalIterator>
+// optional<__empty>
+// operator()(_Policy&&, _BidirectionalIterator __first, _BidirectionalIterator __last) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __reverse_copy;
 // template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index 5e89c6fe31cd8..361c86937534c 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -38,6 +38,7 @@
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
 #include <__pstl/cpu_algos/mismatch.h>
+#include <__pstl/cpu_algos/reverse.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -373,6 +374,10 @@ template <class _ExecutionPolicy>
 struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __reverse<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_reverse<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index d90022ee37145..6dc062057f64c 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -14,6 +14,7 @@
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
 #include <__algorithm/mismatch.h>
+#include <__algorithm/reverse.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -106,6 +107,16 @@ struct __merge<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __reverse<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _BidirectionalIterator>
+  _LIBCPP_HIDE_FROM_ABI optional<__empty>
+  operator()(_Policy&&, _BidirectionalIterator __first, _BidirectionalIterator __last) const noexcept {
+    std::reverse(std::move(__first), std::move(__last));
+    return __empty{};
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index 93935d22b9442..5fdf6f8aeab90 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -18,6 +18,7 @@
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
 #include <__pstl/cpu_algos/mismatch.h>
+#include <__pstl/cpu_algos/reverse.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -105,6 +106,10 @@ template <class _ExecutionPolicy>
 struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __reverse<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_reverse<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/reverse.h b/libcxx/include/__pstl/cpu_algos/reverse.h
new file mode 100644
index 0000000000000..5e5a1a0a4529e
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/reverse.h
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_REVERSE_H
+#define _LIBCPP___PSTL_CPU_ALGOS_REVERSE_H
+
+#include <__algorithm/reverse.h>
+#include <__algorithm/swap_ranges.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/reverse_iterator.h>
+#include <__optional/optional.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_reverse {
+  template <class _Policy, class _ForwardIterator>
+  _LIBCPP_HIDE_FROM_ABI optional<__empty>
+  operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+      // Perform a chunked for_each on the first half of the range.
+      auto __n = (__last - __first) / 2;
+      return __cpu_traits<_Backend>::__for_each(
+          __first, __first + __n, [__first, __last](_ForwardIterator __i, _ForwardIterator __j) {
+            // Derive the last position of the mirrored range.
+            _ForwardIterator __mirror_last = __last - (__i - __first);
+            // Swap the elements in the range of the first half with their mirrored counterparts in the second half.
+            std::swap_ranges(
+                std::move(__i), std::move(__j), std::reverse_iterator<_ForwardIterator>(std::move(__mirror_last)));
+          });
+    } else {
+      // Non-random access iterators currently cannot be processed in parallel, use the sequential implementation.
+      std::reverse(std::move(__first), std::move(__last));
+      return __empty{};
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_REVERSE_H
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 636c4cf84ab2d..a8ab2501517e0 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -125,6 +125,10 @@ void f(non_forward_iterator non_fwd,
         pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: lexicographical_compare}}
   }
 
+  {
+    std::reverse(pol, non_bidir, non_bidir); // expected-error@*:* {{static assertion failed: reverse}}
+  }
+
   {
     (void)std::reverse_copy(
         pol, non_bidir, non_bidir, it);            // expected-error@*:* {{static assertion failed: reverse_copy}}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse.pass.cpp
new file mode 100644
index 0000000000000..11a64a026bb7a
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   void reverse(ExecutionPolicy&& exec,
+//                BidirectionalIterator first,
+//                BidirectionalIterator last);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+#include <type_traits>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(reverse);
+
+static_assert(sfinae_test_reverse<int, int*, int*>);
+static_assert(!sfinae_test_reverse<std::execution::parallel_policy, int*, int*>);
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // Check the return type
+      static_assert(std::is_same_v<
+                    void,
+                    decltype(std::reverse(
+                        std::declval<std::execution::parallel_policy>(), std::declval<Iter>(), std::declval<Iter>()))>);
+    }
+    { // Empty range
+      int a[] = {0};
+      std::reverse(policy, Iter(std::begin(a)), Iter(std::begin(a)));
+    }
+    { // Single element
+      int a[] = {0};
+      std::reverse(policy, Iter(std::begin(a)), Iter(std::end(a)));
+      assert(a[0] == 0);
+    }
+    { // Two elements
+      int a[] = {0, 1};
+      std::reverse(policy, Iter(std::begin(a)), Iter(std::end(a)));
+      assert(a[0] == 1);
+      assert(a[1] == 0);
+    }
+    { // Three elements
+      int a[] = {0, 1, 2};
+      std::reverse(policy, Iter(std::begin(a)), Iter(std::end(a)));
+      assert(a[0] == 2);
+      assert(a[1] == 1);
+      assert(a[2] == 0);
+    }
+    { // Four elements
+      int a[] = {0, 1, 2, 3};
+      std::reverse(policy, Iter(std::begin(a)), Iter(std::end(a)));
+      assert(a[0] == 3);
+      assert(a[1] == 2);
+      assert(a[2] == 1);
+      assert(a[3] == 0);
+    }
+    { // Many iotaed elements, reverse up to a sampled position, check and reverse back
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 1);
+      runway_sample(std::size(a) + 1, [&](size_t i) {
+        std::reverse(policy, Iter(std::begin(a)), Iter(std::begin(a) + i));
+        for (size_t j = 0; j < i; ++j) {
+          assert(a[j] == static_cast<int>(i - j));
+        }
+        std::reverse(policy, Iter(std::begin(a)), Iter(std::begin(a) + i));
+      });
+    }
+  }
+};
+
+int main(int, char**) { types::for_each(types::bidirectional_iterator_list<int*>{}, TestIteratorWithPolicies<Test>{}); }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a505e1441d2a9..66cd7ff632ef6 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -226,6 +226,11 @@ int main(int, char**) {
         });
       }
 
+      {
+        // reverse(first, last)
+        assert_non_throwing([=, &policy] { std::reverse(policy, std::move(first1), std::move(last1)); });
+      }
+
       {
         // reverse_copy(first, last, dest)
         assert_non_throwing([=, &policy] {

>From 26f9c9a9589f80225aa4c3a4944f708cef9eeffe Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 1 Aug 2026 21:54:49 +0100
Subject: [PATCH 2/2] modulemap

---
 libcxx/include/module.modulemap.in | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 6b491e4e2589f..f56d48784737b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2419,6 +2419,9 @@ module std {
       module mismatch {
         header "__pstl/cpu_algos/mismatch.h"
       }
+      module reverse {
+        header "__pstl/cpu_algos/reverse.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty



More information about the libcxx-commits mailing list