Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 55 additions & 23 deletions stl/inc/execution
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,35 @@ template <>
struct is_execution_policy<execution::unsequenced_policy> : true_type {};
#endif // _HAS_CXX20

template <class _Ty, class _FwdIt>
void _Construct_in_place_by_deref(_Ty& _Val, const _FwdIt& _Iter) {
::new (static_cast<void*>(_STD addressof(_Val))) _Ty(*_Iter);
}

template <class _Ty, class _UnaryOp, class _FwdIt>
void _Construct_in_place_by_transform_deref(_Ty& _Val, _UnaryOp _Transform_op, const _FwdIt& _Iter) {
::new (static_cast<void*>(_STD addressof(_Val))) _Ty(_Transform_op(*_Iter));
}

template <class _Ty, class _BinaryOp, class _ArgTy>
void _Implicitly_construct_in_place_by_binary_op(_Ty& _Val, _BinaryOp _Reduce_op, _ArgTy& _Left, _ArgTy& _Right) {
::new (static_cast<void*>(_STD addressof(_Val))) _Ty([&]() -> _Ty { return _Reduce_op(_Left, _Right); }());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And capturing it by reference too. Ditto below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point is performing implicit conversion in placement new with workaround for imperfectness of forwarding. So I guess copying should be avoided.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with capturing by reference since it doesn't affect the function signature, even though it should be a reference to a _Pass_fn.

}

template <class _Ty, class _BinaryOp, class _LeftTy, class _FwdIt>
void _Implicitly_construct_in_place_by_binary_op_deref_rhs(
_Ty& _Val, _BinaryOp _Reduce_op, _LeftTy&& _Left, const _FwdIt& _Iter) {
::new (static_cast<void*>(_STD addressof(_Val)))
_Ty([&]() -> _Ty { return _Reduce_op(_STD forward<_LeftTy>(_Left), *_Iter); }());
}

template <class _Ty, class _BinaryOp, class _UnaryOp, class _LeftTy, class _FwdIt>
void _Implicitly_construct_in_place_by_binary_op_transform_deref_rhs(
_Ty& _Val, _BinaryOp _Reduce_op, _UnaryOp _Transform_op, _LeftTy&& _Left, const _FwdIt& _Iter) {
::new (static_cast<void*>(_STD addressof(_Val)))
_Ty([&]() -> _Ty { return _Reduce_op(_STD forward<_LeftTy>(_Left), _Transform_op(*_Iter)); }());
}

struct _Parallelism_resources_exhausted : exception {
_NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override {
// return pointer to message string
Expand Down Expand Up @@ -3602,7 +3631,7 @@ struct _Scan_decoupled_lookback {
template <class _FwdIt, class _BinOp>
void _Apply_exclusive_predecessor(_Ty& _Preceding, _FwdIt _First, const _FwdIt _Last, _BinOp _Reduce_op) {
// apply _Preceding to [_First, _Last) and _Sum._Ref(), using _Reduce_op
_STD _Construct_in_place(_Sum._Ref(), _Reduce_op(_Preceding, _Local._Ref()));
_STD _Implicitly_construct_in_place_by_binary_op(_Sum._Ref(), _Reduce_op, _Preceding, _Local._Ref());
_State.store(_Local_available | _Sum_available);
*_First = _Preceding;

Expand All @@ -3615,7 +3644,7 @@ struct _Scan_decoupled_lookback {
template <class _FwdIt, class _BinOp>
void _Apply_inclusive_predecessor(_Ty& _Preceding, _FwdIt _First, const _FwdIt _Last, _BinOp _Reduce_op) {
// apply _Preceding to [_First, _Last) and _Sum._Ref(), using _Reduce_op
_STD _Construct_in_place(_Sum._Ref(), _Reduce_op(_Preceding, _Local._Ref()));
_STD _Implicitly_construct_in_place_by_binary_op(_Sum._Ref(), _Reduce_op, _Preceding, _Local._Ref());
_State.store(_Local_available | _Sum_available);

#pragma loop(ivdep)
Expand Down Expand Up @@ -3645,8 +3674,8 @@ typename _Iter_value_t<_BidIt>::value_type _Get_lookback_sum(const _BidIt _Curre
auto _Prev = _Current;
--_Prev;
auto _Prev_state = _Prev->_Get_available_state();
typename _Iter_value_t<_BidIt>::value_type _Result(
_Reduce_op(_Prev_state & _Sum_available ? _Prev->_Sum._Ref() : _Prev->_Local._Ref(), _Current->_Local._Ref()));
typename _Iter_value_t<_BidIt>::value_type _Result =
_Reduce_op(_Prev_state & _Sum_available ? _Prev->_Sum._Ref() : _Prev->_Local._Ref(), _Current->_Local._Ref());
while (!(_Prev_state & _Sum_available)) {
--_Prev;
_Prev_state = _Prev->_Get_available_state();
Expand Down Expand Up @@ -4334,9 +4363,9 @@ _FwdIt2 _Exclusive_scan_per_chunk(_FwdIt1 _First, const _FwdIt1 _Last, _FwdIt2 _
return _Dest;
}

_Ty _Tmp(_Reduce_op(_Val, *_First)); // temp to enable _First == _Dest
*_Dest = _Val;
_Val = _STD move(_Tmp);
_Ty _Tmp = _Reduce_op(_Val, *_First); // temp to enable _First == _Dest
*_Dest = _Val;
_Val = _STD move(_Tmp);
}
}

Expand All @@ -4346,13 +4375,13 @@ void _Exclusive_scan_per_chunk_complete(
// Sum for parallel exclusive_scan with predecessor available, into [_Dest, _Dest + (_Last - _First)) and stores
// successor sum in _Val.
// Pre: _Val is *uninitialized* && _First != _Last && predecessor sum is in _Init
_STD _Construct_in_place(_Val, _Reduce_op(_Init, *_First));
_STD _Implicitly_construct_in_place_by_binary_op_deref_rhs(_Val, _Reduce_op, _Init, _First);
*_Dest = _Init;
while (++_First != _Last) {
++_Dest;
_Ty _Tmp(_Reduce_op(_Val, *_First)); // temp to enable _First == _Dest
*_Dest = _STD move(_Val);
_Val = _STD move(_Tmp);
_Ty _Tmp = _Reduce_op(_Val, *_First); // temp to enable _First == _Dest
*_Dest = _STD move(_Val);
_Val = _STD move(_Tmp);
}
}

Expand Down Expand Up @@ -4471,9 +4500,10 @@ _FwdIt2 _Inclusive_scan_per_chunk(
// _Val.
// pre: _Val is *uninitialized* && _First != _Last
if constexpr (is_same_v<_No_init_tag, remove_const_t<remove_reference_t<_Ty_fwd>>>) {
_STD _Construct_in_place(_Val, *_First);
_STD _Construct_in_place_by_deref(_Val, _First);
} else {
_STD _Construct_in_place(_Val, _Reduce_op(_STD forward<_Ty_fwd>(_Predecessor), *_First));
_STD _Implicitly_construct_in_place_by_binary_op_deref_rhs(
_Val, _Reduce_op, _STD forward<_Ty_fwd>(_Predecessor), _First);
}

for (;;) {
Expand Down Expand Up @@ -4645,17 +4675,17 @@ _FwdIt2 _Transform_exclusive_scan_per_chunk(
// Local-sum for parallel transform_exclusive_scan; writes local sums into [_Dest + 1, _Dest + (_Last - _First)) and
// stores successor sum in _Val.
// pre: _Val is *uninitialized* && _First != _Last
_STD _Construct_in_place(_Val, _Transform_op(*_First));
_STD _Construct_in_place_by_transform_deref(_Val, _Transform_op, _First);
for (;;) {
++_First;
++_Dest;
if (_First == _Last) {
return _Dest;
}

_Ty _Tmp(_Reduce_op(_Val, _Transform_op(*_First))); // temp to enable _First == _Dest
*_Dest = _Val;
_Val = _STD move(_Tmp);
_Ty _Tmp = _Reduce_op(_Val, _Transform_op(*_First)); // temp to enable _First == _Dest
*_Dest = _Val;
_Val = _STD move(_Tmp);
}
}

Expand All @@ -4665,13 +4695,14 @@ void _Transform_exclusive_scan_per_chunk_complete(_FwdIt1 _First, const _FwdIt1
// Sum for parallel transform_exclusive_scan with predecessor available, into [_Dest, _Dest + (_Last - _First)) and
// stores successor sum in _Val.
// pre: _Val is *uninitialized* && _First != _Last && predecessor sum is in _Init
_STD _Construct_in_place(_Val, _Reduce_op(_Init, _Transform_op(*_First)));
_STD _Implicitly_construct_in_place_by_binary_op_transform_deref_rhs(
_Val, _Reduce_op, _Transform_op, _Init, _First);
*_Dest = _Init;
while (++_First != _Last) {
++_Dest;
_Ty _Tmp(_Reduce_op(_Val, _Transform_op(*_First))); // temp to enable _First == _Dest
*_Dest = _STD move(_Val);
_Val = _STD move(_Tmp);
_Ty _Tmp = _Reduce_op(_Val, _Transform_op(*_First)); // temp to enable _First == _Dest
*_Dest = _STD move(_Val);
_Val = _STD move(_Tmp);
}
}

Expand Down Expand Up @@ -4791,9 +4822,10 @@ _FwdIt2 _Transform_inclusive_scan_per_chunk(_FwdIt1 _First, const _FwdIt1 _Last,
// sum in _Val
// pre: _Val is *uninitialized* && _First != _Last
if constexpr (is_same_v<_No_init_tag, remove_const_t<remove_reference_t<_Ty_fwd>>>) {
_STD _Construct_in_place(_Val, _Transform_op(*_First));
_STD _Construct_in_place_by_transform_deref(_Val, _Transform_op, _First);
} else {
_STD _Construct_in_place(_Val, _Reduce_op(_STD forward<_Ty_fwd>(_Predecessor), _Transform_op(*_First)));
_STD _Implicitly_construct_in_place_by_binary_op_transform_deref_rhs(
_Val, _Reduce_op, _Transform_op, _STD forward<_Ty_fwd>(_Predecessor), _First);
}

for (;;) {
Expand Down
8 changes: 4 additions & 4 deletions stl/inc/numeric
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ _CONSTEXPR20 _OutIt exclusive_scan(const _InIt _First, const _InIt _Last, _OutIt
auto _UDest = _STD _Get_unwrapped_n(_Dest, _STD _Idl_distance<_InIt>(_UFirst, _ULast));
if (_UFirst != _ULast) {
for (;;) {
_Ty _Tmp(_Reduce_op(_Val, *_UFirst)); // temp to enable _First == _Dest, also requirement missing
*_UDest = _Val;
_Ty _Tmp = _Reduce_op(_Val, *_UFirst); // temp to enable _First == _Dest, also requirement missing
*_UDest = _Val;
++_UDest;
++_UFirst;
if (_UFirst == _ULast) {
Expand Down Expand Up @@ -389,8 +389,8 @@ _CONSTEXPR20 _OutIt transform_exclusive_scan(
auto _UDest = _STD _Get_unwrapped_n(_Dest, _STD _Idl_distance<_InIt>(_UFirst, _ULast));
if (_UFirst != _ULast) {
for (;;) {
_Ty _Tmp(_Reduce_op(_Val, _Transform_op(*_UFirst))); // temp to enable _First == _Dest
*_UDest = _Val;
_Ty _Tmp = _Reduce_op(_Val, _Transform_op(*_UFirst)); // temp to enable _First == _Dest
*_UDest = _Val;
++_UDest;
++_UFirst;
if (_UFirst == _ULast) {
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ tests\GH_003867_output_nan
tests\GH_004023_mdspan_fwd_prod_overflow
tests\GH_004040_container_nonmember_functions
tests\GH_004109_iter_value_t_direct_initialization
tests\GH_004129_conversion_in_new_numeric_algorithms
tests\GH_004201_chrono_formatter
tests\GH_004275_seeking_fancy_iterators
tests\GH_004388_unordered_meow_operator_equal
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_17_matrix.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// intentionally test narrowing conversion from int64_t to int32_t
#pragma warning(disable : 4244)

#include <cassert>
#include <cstdint>
#include <execution>
#include <numeric>
#include <type_traits>

using namespace std;
using namespace std::execution;

struct implicitly_convertible_to_i32_only {
int32_t n;

template <class = void>
explicit operator int32_t() const = delete;

operator int64_t() const noexcept {
return n;
}
};

static_assert(!is_constructible_v<int32_t, implicitly_convertible_to_i32_only>);
static_assert(is_convertible_v<implicitly_convertible_to_i32_only, int32_t>);

struct implicitly_validating_converter {
implicitly_convertible_to_i32_only operator()(int n) const noexcept {
return {n};
}
};

struct explicitly_convertible_to_i32_only {
int32_t n;

explicit operator int32_t() const noexcept {
return n;
}
};

static_assert(is_constructible_v<int32_t, explicitly_convertible_to_i32_only>);
static_assert(!is_convertible_v<explicitly_convertible_to_i32_only, int32_t>);

struct transformation_validating_converter {
explicitly_convertible_to_i32_only operator()(int n) const noexcept {
return {n};
}
};

struct implicitly_validating_plus {
implicitly_convertible_to_i32_only operator()(
implicitly_convertible_to_i32_only l, implicitly_convertible_to_i32_only r) const noexcept {
return implicitly_convertible_to_i32_only{l.n + r.n};
}

implicitly_convertible_to_i32_only operator()(int32_t l, implicitly_convertible_to_i32_only r) const noexcept {
return implicitly_convertible_to_i32_only{l + r.n};
}

implicitly_convertible_to_i32_only operator()(implicitly_convertible_to_i32_only l, int32_t r) const noexcept {
return implicitly_convertible_to_i32_only{l.n + r};
}

implicitly_convertible_to_i32_only operator()(int32_t l, int32_t r) const noexcept {
return implicitly_convertible_to_i32_only{l + r};
}
};

struct implicitly_validating_plus_for_transformation {
implicitly_convertible_to_i32_only operator()(
explicitly_convertible_to_i32_only l, explicitly_convertible_to_i32_only r) const noexcept {
return implicitly_convertible_to_i32_only{l.n + r.n};
}

implicitly_convertible_to_i32_only operator()(int32_t l, explicitly_convertible_to_i32_only r) const noexcept {
return implicitly_convertible_to_i32_only{l + r.n};
}

implicitly_convertible_to_i32_only operator()(explicitly_convertible_to_i32_only l, int32_t r) const noexcept {
return implicitly_convertible_to_i32_only{l.n + r};
}

implicitly_convertible_to_i32_only operator()(int32_t l, int32_t r) const noexcept {
return implicitly_convertible_to_i32_only{l + r};
}
};

struct implicitly_validating_multiplies {
implicitly_convertible_to_i32_only operator()(
implicitly_convertible_to_i32_only l, implicitly_convertible_to_i32_only r) const noexcept {
return implicitly_convertible_to_i32_only{l.n * r.n};
}

implicitly_convertible_to_i32_only operator()(int32_t l, implicitly_convertible_to_i32_only r) const noexcept {
return implicitly_convertible_to_i32_only{l * r.n};
}

implicitly_convertible_to_i32_only operator()(implicitly_convertible_to_i32_only l, int32_t r) const noexcept {
return implicitly_convertible_to_i32_only{l.n * r};
}

implicitly_convertible_to_i32_only operator()(int32_t l, int32_t r) const noexcept {
return implicitly_convertible_to_i32_only{l * r};
}
};

void test_copy_initialization_for_numeric_algorithms() {
int arr[1]{};
implicitly_convertible_to_i32_only brr[1]{};

assert(reduce(arr, arr, int32_t{}, implicitly_validating_plus{}) == 0);
assert(reduce(brr, brr, int32_t{}, implicitly_validating_plus{}) == 0);

assert(transform_reduce(arr, arr, arr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_multiplies{})
== 0);
assert(transform_reduce(arr, arr, brr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_multiplies{})
== 0);

assert(transform_reduce(arr, arr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_converter{}) == 0);
assert(transform_reduce(brr, brr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_converter{}) == 0);

assert(exclusive_scan(arr, arr, arr, int32_t{}, implicitly_validating_plus{}) == arr);

assert(inclusive_scan(arr, arr, arr, implicitly_validating_plus{}) == arr);
assert(inclusive_scan(arr, arr, arr, implicitly_validating_plus{}, int32_t{}) == arr);

assert(transform_exclusive_scan(arr, arr, arr, int32_t{}, implicitly_validating_plus_for_transformation{},
transformation_validating_converter{})
== arr);

assert(transform_inclusive_scan(brr, brr, brr, implicitly_validating_plus{}, implicitly_validating_converter{})
== brr);
assert(transform_inclusive_scan(arr, arr, arr, implicitly_validating_plus_for_transformation{},
transformation_validating_converter{}, int32_t{})
== arr);
}

template <const auto& ExPo>
void test_copy_initialization_for_parallel_numeric_algorithms() {
int arr[1]{};
implicitly_convertible_to_i32_only brr[1]{};

assert(reduce(ExPo, arr, arr, int32_t{}, implicitly_validating_plus{}) == 0);
assert(reduce(ExPo, brr, brr, int32_t{}, implicitly_validating_plus{}) == 0);

assert(transform_reduce(
ExPo, arr, arr, arr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_multiplies{})
== 0);
assert(transform_reduce(
ExPo, arr, arr, brr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_multiplies{})
== 0);

assert(transform_reduce(ExPo, arr, arr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_converter{})
== 0);
assert(transform_reduce(ExPo, brr, brr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_converter{})
== 0);

assert(exclusive_scan(ExPo, arr, arr, arr, int32_t{}, implicitly_validating_plus{}) == arr);

assert(inclusive_scan(ExPo, arr, arr, arr, implicitly_validating_plus{}) == arr);
assert(inclusive_scan(ExPo, arr, arr, arr, implicitly_validating_plus{}, int32_t{}) == arr);

assert(transform_exclusive_scan(ExPo, arr, arr, arr, int32_t{}, implicitly_validating_plus_for_transformation{},
transformation_validating_converter{})
== arr);

assert(
transform_inclusive_scan(ExPo, brr, brr, brr, implicitly_validating_plus{}, implicitly_validating_converter{})
== brr);
assert(transform_inclusive_scan(ExPo, arr, arr, arr, implicitly_validating_plus_for_transformation{},
transformation_validating_converter{}, int32_t{})
== arr);
}

int main() {
test_copy_initialization_for_numeric_algorithms();
test_copy_initialization_for_parallel_numeric_algorithms<seq>();
test_copy_initialization_for_parallel_numeric_algorithms<par>();
test_copy_initialization_for_parallel_numeric_algorithms<par_unseq>();
#if _HAS_CXX20
test_copy_initialization_for_parallel_numeric_algorithms<unseq>();
#endif // _HAS_CXX20
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ struct intermediateType {
intermediateType() = delete;
explicit intermediateType(int) {} // so that the test can make one of these
explicit intermediateType(inputType&) {} // Intermediate tmp(*first)
explicit intermediateType(bopResult&&) {} // Intermediate tmp(binary_op((one of tmp, move(tmp), *first), *first))
// Intermediate tmp = binary_op((one of tmp, move(tmp), *first), *first);
/* implicit */ intermediateType(bopResult&&) {}
intermediateType(const intermediateType&) = delete;
intermediateType(intermediateType&&) = default; // tmp = move(tmp)
intermediateType& operator=(const intermediateType&) = delete;
Expand Down
Loading