On Tue, 29 Apr 2025 at 09:45, Jonathan Wakely <jwak...@redhat.com> wrote:
>
> All of reduce, transform_reduce, exclusive_scan, and inclusive_scan,
> transform_exclusive_scan, and transform_inclusive_scan have a
> precondition that the type of init meets the Cpp17MoveConstructible
> requirements. It isn't required to be copy constructible, so when
> passing it to the next internal function it needs to be moved, not
> copied. We also need to move when creating local variables on the stack,
> and when returning as part of a pair.
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/117905
>         * include/pstl/glue_numeric_impl.h (reduce, transform_reduce)
>         (transform_reduce, inclusive_scan, transform_exclusive_scan)
>         (transform_inclusive_scan): Use std::move for __init parameter.
>         * include/pstl/numeric_impl.h (__brick_transform_reduce)
>         (__pattern_transform_reduce, __brick_transform_scan)
>         (__pattern_transform_scan): Likewise.
>         * include/std/numeric (inclusive_scan, transform_exclusive_scan):
>         Use std::move to create local copy of the first element.
>         * testsuite/26_numerics/pstl/numeric_ops/108236.cc: Move test
>         using move-only type to ...
>         * testsuite/26_numerics/pstl/numeric_ops/move_only.cc: New test.
> ---
>
> Tested x86_64-linux.

Pushed to trunk now. This should be backported too.

>
>  libstdc++-v3/include/pstl/glue_numeric_impl.h | 16 ++--
>  libstdc++-v3/include/pstl/numeric_impl.h      | 36 ++++----
>  libstdc++-v3/include/std/numeric              |  6 +-
>  .../26_numerics/pstl/numeric_ops/108236.cc    | 25 ------
>  .../26_numerics/pstl/numeric_ops/move_only.cc | 90 +++++++++++++++++++
>  5 files changed, 119 insertions(+), 54 deletions(-)
>  create mode 100644 
> libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/move_only.cc
>
> diff --git a/libstdc++-v3/include/pstl/glue_numeric_impl.h 
> b/libstdc++-v3/include/pstl/glue_numeric_impl.h
> index 10d4912deed..fe2d0fd47e2 100644
> --- a/libstdc++-v3/include/pstl/glue_numeric_impl.h
> +++ b/libstdc++-v3/include/pstl/glue_numeric_impl.h
> @@ -25,7 +25,7 @@ 
> __pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _Tp>
>  reduce(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator 
> __last, _Tp __init,
>         _BinaryOperation __binary_op)
>  {
> -    return transform_reduce(std::forward<_ExecutionPolicy>(__exec), __first, 
> __last, __init, __binary_op,
> +    return transform_reduce(std::forward<_ExecutionPolicy>(__exec), __first, 
> __last, std::move(__init), __binary_op,
>                              __pstl::__internal::__no_op());
>  }
>
> @@ -33,7 +33,7 @@ template <class _ExecutionPolicy, class _ForwardIterator, 
> class _Tp>
>  __pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _Tp>
>  reduce(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator 
> __last, _Tp __init)
>  {
> -    return transform_reduce(std::forward<_ExecutionPolicy>(__exec), __first, 
> __last, __init, std::plus<_Tp>(),
> +    return transform_reduce(std::forward<_ExecutionPolicy>(__exec), __first, 
> __last, std::move(__init), std::plus<_Tp>(),
>                              __pstl::__internal::__no_op());
>  }
>
> @@ -58,7 +58,7 @@ transform_reduce(_ExecutionPolicy&& __exec, 
> _ForwardIterator1 __first1, _Forward
>
>      typedef typename iterator_traits<_ForwardIterator1>::value_type 
> _InputType;
>      return __pstl::__internal::__pattern_transform_reduce(__dispatch_tag, 
> std::forward<_ExecutionPolicy>(__exec),
> -                                                          __first1, __last1, 
> __first2, __init, std::plus<_InputType>(),
> +                                                          __first1, __last1, 
> __first2, std::move(__init), std::plus<_InputType>(),
>                                                            
> std::multiplies<_InputType>());
>  }
>
> @@ -70,7 +70,7 @@ transform_reduce(_ExecutionPolicy&& __exec, 
> _ForwardIterator1 __first1, _Forward
>  {
>      auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, 
> __first1, __first2);
>      return __pstl::__internal::__pattern_transform_reduce(__dispatch_tag, 
> std::forward<_ExecutionPolicy>(__exec),
> -                                                          __first1, __last1, 
> __first2, __init, __binary_op1,
> +                                                          __first1, __last1, 
> __first2, std::move(__init), __binary_op1,
>                                                            __binary_op2);
>  }
>
> @@ -81,7 +81,7 @@ transform_reduce(_ExecutionPolicy&& __exec, 
> _ForwardIterator __first, _ForwardIt
>  {
>      auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, 
> __first);
>      return __pstl::__internal::__pattern_transform_reduce(__dispatch_tag, 
> std::forward<_ExecutionPolicy>(__exec),
> -                                                          __first, __last, 
> __init, __binary_op, __unary_op);
> +                                                          __first, __last, 
> std::move(__init), __binary_op, __unary_op);
>  }
>
>  // [exclusive.scan]
> @@ -139,7 +139,7 @@ inclusive_scan(_ExecutionPolicy&& __exec, 
> _ForwardIterator1 __first, _ForwardIte
>                 _ForwardIterator2 __result, _BinaryOperation __binary_op, _Tp 
> __init)
>  {
>      return transform_inclusive_scan(std::forward<_ExecutionPolicy>(__exec), 
> __first, __last, __result, __binary_op,
> -                                    __pstl::__internal::__no_op(), __init);
> +                                    __pstl::__internal::__no_op(), 
> std::move(__init));
>  }
>
>  // [transform.exclusive.scan]
> @@ -154,7 +154,7 @@ transform_exclusive_scan(_ExecutionPolicy&& __exec, 
> _ForwardIterator1 __first, _
>      auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, 
> __first, __result);
>
>      return __pstl::__internal::__pattern_transform_scan(__dispatch_tag, 
> std::forward<_ExecutionPolicy>(__exec), __first,
> -                                                        __last, __result, 
> __unary_op, __init, __binary_op,
> +                                                        __last, __result, 
> __unary_op, std::move(__init), __binary_op,
>                                                          
> /*inclusive=*/std::false_type());
>  }
>
> @@ -170,7 +170,7 @@ transform_inclusive_scan(_ExecutionPolicy&& __exec, 
> _ForwardIterator1 __first, _
>      auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, 
> __first, __result);
>
>      return __pstl::__internal::__pattern_transform_scan(__dispatch_tag, 
> std::forward<_ExecutionPolicy>(__exec), __first,
> -                                                        __last, __result, 
> __unary_op, __init, __binary_op,
> +                                                        __last, __result, 
> __unary_op, std::move(__init), __binary_op,
>                                                          
> /*inclusive=*/std::true_type());
>  }
>
> diff --git a/libstdc++-v3/include/pstl/numeric_impl.h 
> b/libstdc++-v3/include/pstl/numeric_impl.h
> index e1ebec16039..b285a667653 100644
> --- a/libstdc++-v3/include/pstl/numeric_impl.h
> +++ b/libstdc++-v3/include/pstl/numeric_impl.h
> @@ -35,7 +35,7 @@ __brick_transform_reduce(_ForwardIterator1 __first1, 
> _ForwardIterator1 __last1,
>                           _BinaryOperation1 __binary_op1, _BinaryOperation2 
> __binary_op2,
>                           /*is_vector=*/std::false_type) noexcept
>  {
> -    return std::inner_product(__first1, __last1, __first2, __init, 
> __binary_op1, __binary_op2);
> +    return std::inner_product(__first1, __last1, __first2, 
> std::move(__init), __binary_op1, __binary_op2);
>  }
>
>  template <class _RandomAccessIterator1, class _RandomAccessIterator2, class 
> _Tp, class _BinaryOperation1,
> @@ -48,7 +48,7 @@ __brick_transform_reduce(_RandomAccessIterator1 __first1, 
> _RandomAccessIterator1
>  {
>      typedef typename 
> std::iterator_traits<_RandomAccessIterator1>::difference_type _DifferenceType;
>      return __unseq_backend::__simd_transform_reduce(
> -        __last1 - __first1, __init, __binary_op1,
> +        __last1 - __first1, std::move(__init), __binary_op1,
>          [=, &__binary_op2](_DifferenceType __i) { return 
> __binary_op2(__first1[__i], __first2[__i]); });
>  }
>
> @@ -59,7 +59,7 @@ __pattern_transform_reduce(_Tag, _ExecutionPolicy&&, 
> _ForwardIterator1 __first1,
>                             _ForwardIterator2 __first2, _Tp __init, 
> _BinaryOperation1 __binary_op1,
>                             _BinaryOperation2 __binary_op2) noexcept
>  {
> -    return __brick_transform_reduce(__first1, __last1, __first2, __init, 
> __binary_op1, __binary_op2,
> +    return __brick_transform_reduce(__first1, __last1, __first2, 
> std::move(__init), __binary_op1, __binary_op2,
>                                      typename _Tag::__is_vector{});
>  }
>
> @@ -79,12 +79,12 @@ __pattern_transform_reduce(__parallel_tag<_IsVector> 
> __tag, _ExecutionPolicy&& _
>                  __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), 
> __first1, __last1,
>                  [__first1, __first2, __binary_op2](_RandomAccessIterator1 
> __i) mutable
>                  { return __binary_op2(*__i, *(__first2 + (__i - __first1))); 
> },
> -                __init,
> +                std::move(__init),
>                  __binary_op1, // Combine
>                  [__first1, __first2, __binary_op1, 
> __binary_op2](_RandomAccessIterator1 __i, _RandomAccessIterator1 __j,
>                                                                   _Tp __init) 
> -> _Tp
>                  {
> -                    return __internal::__brick_transform_reduce(__i, __j, 
> __first2 + (__i - __first1), __init,
> +                    return __internal::__brick_transform_reduce(__i, __j, 
> __first2 + (__i - __first1), std::move(__init),
>                                                                  
> __binary_op1, __binary_op2, _IsVector{});
>                  });
>          });
> @@ -99,7 +99,7 @@ _Tp
>  __brick_transform_reduce(_ForwardIterator __first, _ForwardIterator __last, 
> _Tp __init, _BinaryOperation __binary_op,
>                           _UnaryOperation __unary_op, 
> /*is_vector=*/std::false_type) noexcept
>  {
> -    return std::transform_reduce(__first, __last, __init, __binary_op, 
> __unary_op);
> +    return std::transform_reduce(__first, __last, std::move(__init), 
> __binary_op, __unary_op);
>  }
>
>  template <class _RandomAccessIterator, class _Tp, class _UnaryOperation, 
> class _BinaryOperation>
> @@ -110,7 +110,7 @@ __brick_transform_reduce(_RandomAccessIterator __first, 
> _RandomAccessIterator __
>  {
>      typedef typename 
> std::iterator_traits<_RandomAccessIterator>::difference_type _DifferenceType;
>      return __unseq_backend::__simd_transform_reduce(
> -        __last - __first, __init, __binary_op,
> +        __last - __first, std::move(__init), __binary_op,
>          [=, &__unary_op](_DifferenceType __i) { return 
> __unary_op(__first[__i]); });
>  }
>
> @@ -120,7 +120,7 @@ _Tp
>  __pattern_transform_reduce(_Tag, _ExecutionPolicy&&, _ForwardIterator 
> __first, _ForwardIterator __last, _Tp __init,
>                             _BinaryOperation __binary_op, _UnaryOperation 
> __unary_op) noexcept
>  {
> -    return __internal::__brick_transform_reduce(__first, __last, __init, 
> __binary_op, __unary_op,
> +    return __internal::__brick_transform_reduce(__first, __last, 
> std::move(__init), __binary_op, __unary_op,
>                                                  typename 
> _Tag::__is_vector{});
>  }
>
> @@ -138,9 +138,9 @@ __pattern_transform_reduce(__parallel_tag<_IsVector> 
> __tag, _ExecutionPolicy&& _
>          {
>              return __par_backend::__parallel_transform_reduce(
>                  __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), 
> __first, __last,
> -                [__unary_op](_RandomAccessIterator __i) mutable { return 
> __unary_op(*__i); }, __init, __binary_op,
> +                [__unary_op](_RandomAccessIterator __i) mutable { return 
> __unary_op(*__i); }, std::move(__init), __binary_op,
>                  [__unary_op, __binary_op](_RandomAccessIterator __i, 
> _RandomAccessIterator __j, _Tp __init) {
> -                    return __internal::__brick_transform_reduce(__i, __j, 
> __init, __binary_op, __unary_op, _IsVector{});
> +                    return __internal::__brick_transform_reduce(__i, __j, 
> std::move(__init), __binary_op, __unary_op, _IsVector{});
>                  });
>          });
>  }
> @@ -181,7 +181,7 @@ __brick_transform_scan(_RandomAccessIterator __first, 
> _RandomAccessIterator __la
>          __init = __binary_op(__init, __unary_op(*__first));
>          *__result = __init;
>      }
> -    return std::make_pair(__result, __init);
> +    return std::make_pair(__result, std::move(__init));
>  }
>
>  // type is arithmetic and binary operation is a user defined operation.
> @@ -199,11 +199,11 @@ __brick_transform_scan(_RandomAccessIterator __first, 
> _RandomAccessIterator __la
>                         /*is_vector=*/std::true_type) noexcept
>  {
>  #if defined(_PSTL_UDS_PRESENT)
> -    return __unseq_backend::__simd_scan(__first, __last - __first, __result, 
> __unary_op, __init, __binary_op,
> +    return __unseq_backend::__simd_scan(__first, __last - __first, __result, 
> __unary_op, std::move(__init), __binary_op,
>                                          _Inclusive());
>  #else
>      // We need to call serial brick here to call function for inclusive and 
> exclusive scan that depends on _Inclusive() value
> -    return __internal::__brick_transform_scan(__first, __last, __result, 
> __unary_op, __init, __binary_op, _Inclusive(),
> +    return __internal::__brick_transform_scan(__first, __last, __result, 
> __unary_op, std::move(__init), __binary_op, _Inclusive(),
>                                                
> /*is_vector=*/std::false_type());
>  #endif
>  }
> @@ -215,7 +215,7 @@ __brick_transform_scan(_RandomAccessIterator __first, 
> _RandomAccessIterator __la
>                         _UnaryOperation __unary_op, _Tp __init, 
> _BinaryOperation __binary_op, _Inclusive,
>                         /*is_vector=*/std::true_type) noexcept
>  {
> -    return __internal::__brick_transform_scan(__first, __last, __result, 
> __unary_op, __init, __binary_op, _Inclusive(),
> +    return __internal::__brick_transform_scan(__first, __last, __result, 
> __unary_op, std::move(__init), __binary_op, _Inclusive(),
>                                                
> /*is_vector=*/std::false_type());
>  }
>
> @@ -247,19 +247,19 @@ __pattern_transform_scan(__parallel_tag<_IsVector> 
> __tag, _ExecutionPolicy&& __e
>          {
>              __par_backend::__parallel_transform_scan(
>                  __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), 
> __last - __first,
> -                [__first, __unary_op](_DifferenceType __i) mutable { return 
> __unary_op(__first[__i]); }, __init,
> +                [__first, __unary_op](_DifferenceType __i) mutable { return 
> __unary_op(__first[__i]); }, std::move(__init),
>                  __binary_op,
>                  [__first, __unary_op, __binary_op](_DifferenceType __i, 
> _DifferenceType __j, _Tp __init)
>                  {
>                      // Execute serial __brick_transform_reduce, due to the 
> explicit SIMD vectorization (reduction) requires a commutative operation for 
> the guarantee of correct scan.
> -                    return __internal::__brick_transform_reduce(__first + 
> __i, __first + __j, __init, __binary_op,
> +                    return __internal::__brick_transform_reduce(__first + 
> __i, __first + __j, std::move(__init), __binary_op,
>                                                                  __unary_op,
>                                                                  
> /*__is_vector*/ std::false_type());
>                  },
>                  [__first, __unary_op, __binary_op, __result](_DifferenceType 
> __i, _DifferenceType __j, _Tp __init)
>                  {
>                      return __internal::__brick_transform_scan(__first + __i, 
> __first + __j, __result + __i, __unary_op,
> -                                                              __init, 
> __binary_op, _Inclusive(), _IsVector{})
> +                                                              
> std::move(__init), __binary_op, _Inclusive(), _IsVector{})
>                          .second;
>                  });
>              return __result + (__last - __first);
> @@ -286,7 +286,7 @@ __pattern_transform_scan(__parallel_tag<_IsVector> __tag, 
> _ExecutionPolicy&& __e
>          [&]()
>          {
>              __par_backend::__parallel_strict_scan(
> -                __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), 
> __n, __init,
> +                __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), 
> __n, std::move(__init),
>                  [__first, __unary_op, __binary_op, __result](_DifferenceType 
> __i, _DifferenceType __len)
>                  {
>                      return __internal::__brick_transform_scan(__first + __i, 
> __first + (__i + __len), __result + __i,
> diff --git a/libstdc++-v3/include/std/numeric 
> b/libstdc++-v3/include/std/numeric
> index 490963ee46d..cbabf031216 100644
> --- a/libstdc++-v3/include/std/numeric
> +++ b/libstdc++-v3/include/std/numeric
> @@ -582,7 +582,7 @@ namespace __detail
>      {
>        if (__first != __last)
>         {
> -         auto __init = *__first;
> +         auto __init = std::move(*__first);
>           *__result++ = __init;
>           ++__first;
>           if (__first != __last)
> @@ -645,8 +645,8 @@ namespace __detail
>      {
>        while (__first != __last)
>         {
> -         auto __v = __init;
> -         __init = __binary_op(__init, __unary_op(*__first));
> +         auto __v = std::move(__init);
> +         __init = __binary_op(__v, __unary_op(*__first));
>           ++__first;
>           *__result++ = std::move(__v);
>         }
> diff --git a/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/108236.cc 
> b/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/108236.cc
> index e0e3027b321..cbef8ab67dd 100644
> --- a/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/108236.cc
> +++ b/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/108236.cc
> @@ -8,30 +8,6 @@
>  #include <execution>
>  #include <testsuite_hooks.h>
>
> -struct Mint
> -{
> -  Mint(int i = 0) : val(i) { }
> -  Mint(Mint&&) = default;
> -  Mint& operator=(Mint&&) = default;
> -
> -  operator int() const { return val; }
> -
> -private:
> -  int val;
> -};
> -
> -void
> -test_move_only()
> -{
> -  const int input[]{10, 20, 30};
> -  int output[3];
> -  std::exclusive_scan(std::execution::seq, input, input+3, output, Mint(5),
> -                     std::plus<int>{});
> -  VERIFY( output[0] == 5 );
> -  VERIFY( output[1] == 15 );
> -  VERIFY( output[2] == 35 );
> -}
> -
>  void
>  test_pr108236()
>  {
> @@ -45,6 +21,5 @@ test_pr108236()
>
>  int main()
>  {
> -  test_move_only();
>    test_pr108236();
>  }
> diff --git a/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/move_only.cc 
> b/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/move_only.cc
> new file mode 100644
> index 00000000000..38ad3c2877e
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/move_only.cc
> @@ -0,0 +1,90 @@
> +// { dg-options "-ltbb" }
> +// { dg-do run { target c++17 } }
> +// { dg-require-effective-target tbb_backend }
> +
> +#include <numeric>
> +#include <execution>
> +#include <testsuite_hooks.h>
> +
> +struct Mint
> +{
> +  Mint(int i = 0) : val(i) { }
> +
> +  Mint(Mint&& m) : val(m.val) { m.val = -1; }
> +
> +  Mint& operator=(Mint&& m)
> +  {
> +    val = m.val;
> +    m.val = -1;
> +    return *this;
> +  }
> +
> +  operator int() const
> +  {
> +    VERIFY(val >= 0); // Check we don't read value of a moved-from instance.
> +    return val;
> +  }
> +
> +  friend Mint operator+(const Mint& lhs, const Mint& rhs)
> +  { return Mint(lhs.val + rhs.val); }
> +
> +private:
> +  int val;
> +};
> +
> +void
> +test_reduce()
> +{
> +  Mint input[]{1, 2, 3};
> +  Mint m = std::reduce(std::execution::seq, input, input+3);
> +  VERIFY( static_cast<int>(m) == 6 );
> +
> +  m = std::reduce(std::execution::seq, input, input+3, Mint(100));
> +  VERIFY( static_cast<int>(m) == 106 );
> +
> +  m = std::reduce(std::execution::seq, input, input+3, Mint(200),
> +                 std::plus<>{});
> +  VERIFY( static_cast<int>(m) == 206 );
> +}
> +
> +void
> +test_transform_reduce()
> +{
> +}
> +
> +void
> +test_exclusive_scan()
> +{
> +  const int input[]{10, 20, 30};
> +  int output[3];
> +  std::exclusive_scan(std::execution::seq, input, input+3, output, Mint(5),
> +                     std::plus<int>{});
> +  VERIFY( output[0] == 5 );
> +  VERIFY( output[1] == 15 );
> +  VERIFY( output[2] == 35 );
> +}
> +
> +void
> +test_inclusive_scan()
> +{
> +}
> +
> +void
> +test_transform_exclusive_scan()
> +{
> +}
> +
> +void
> +test_transform_inclusive_scan()
> +{
> +}
> +
> +int main()
> +{
> +  test_reduce();
> +  test_transform_reduce();
> +  test_exclusive_scan();
> +  test_inclusive_scan();
> +  test_transform_exclusive_scan();
> +  test_transform_inclusive_scan();
> +}
> --
> 2.49.0
>

Reply via email to