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. 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