Skip to content

Commit

Permalink
Implement vectorized min_ / max_element for ints (microsoft#2447)
Browse files Browse the repository at this point in the history
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
  • Loading branch information
2 people authored and fsb4000 committed Aug 13, 2022
1 parent d6d2c93 commit 0986811
Show file tree
Hide file tree
Showing 4 changed files with 936 additions and 0 deletions.
129 changes: 129 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ _STL_DISABLE_CLANG_WARNINGS
#undef new

#if _USE_STD_VECTOR_ALGORITHMS

_EXTERN_C
struct _Min_max_element_t {
const void* _Min;
const void* _Max;
};

// The "noalias" attribute tells the compiler optimizer that pointers going into these hand-vectorized algorithms
// won't be stored beyond the lifetime of the function, and that the function will only reference arrays denoted by
// those pointers. The optimizer also assumes in that case that a pointer parameter is not returned to the caller via
Expand All @@ -34,7 +40,77 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_4(
const void* _First, const void* _Last, void* _Dest) noexcept;
__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_8(
const void* _First, const void* _Last, void* _Dest) noexcept;

const void* __stdcall __std_min_element_1(const void* _First, const void* _Last, bool _Signed) noexcept;
const void* __stdcall __std_min_element_2(const void* _First, const void* _Last, bool _Signed) noexcept;
const void* __stdcall __std_min_element_4(const void* _First, const void* _Last, bool _Signed) noexcept;
const void* __stdcall __std_min_element_8(const void* _First, const void* _Last, bool _Signed) noexcept;

const void* __stdcall __std_max_element_1(const void* _First, const void* _Last, bool _Signed) noexcept;
const void* __stdcall __std_max_element_2(const void* _First, const void* _Last, bool _Signed) noexcept;
const void* __stdcall __std_max_element_4(const void* _First, const void* _Last, bool _Signed) noexcept;
const void* __stdcall __std_max_element_8(const void* _First, const void* _Last, bool _Signed) noexcept;

_Min_max_element_t __stdcall __std_minmax_element_1(const void* _First, const void* _Last, bool _Signed) noexcept;
_Min_max_element_t __stdcall __std_minmax_element_2(const void* _First, const void* _Last, bool _Signed) noexcept;
_Min_max_element_t __stdcall __std_minmax_element_4(const void* _First, const void* _Last, bool _Signed) noexcept;
_Min_max_element_t __stdcall __std_minmax_element_8(const void* _First, const void* _Last, bool _Signed) noexcept;
_END_EXTERN_C

template <class _Ty>
_Ty* __std_min_element(_Ty* _First, _Ty* _Last) noexcept {
constexpr bool _Signed = _STD is_signed_v<_Ty>;

if constexpr (sizeof(_Ty) == 1) {
return const_cast<_Ty*>(static_cast<const _Ty*>(__std_min_element_1(_First, _Last, _Signed)));
} else if constexpr (sizeof(_Ty) == 2) {
return const_cast<_Ty*>(static_cast<const _Ty*>(__std_min_element_2(_First, _Last, _Signed)));
} else if constexpr (sizeof(_Ty) == 4) {
return const_cast<_Ty*>(static_cast<const _Ty*>(__std_min_element_4(_First, _Last, _Signed)));
} else if constexpr (sizeof(_Ty) == 8) {
return const_cast<_Ty*>(static_cast<const _Ty*>(__std_min_element_8(_First, _Last, _Signed)));
} else {
static_assert(_STD _Always_false<_Ty>, "Unexpected size");
}
}

template <class _Ty>
_Ty* __std_max_element(_Ty* _First, _Ty* _Last) noexcept {
constexpr bool _Signed = _STD is_signed_v<_Ty>;

if constexpr (sizeof(_Ty) == 1) {
return const_cast<_Ty*>(static_cast<const _Ty*>(__std_max_element_1(_First, _Last, _Signed)));
} else if constexpr (sizeof(_Ty) == 2) {
return const_cast<_Ty*>(static_cast<const _Ty*>(__std_max_element_2(_First, _Last, _Signed)));
} else if constexpr (sizeof(_Ty) == 4) {
return const_cast<_Ty*>(static_cast<const _Ty*>(__std_max_element_4(_First, _Last, _Signed)));
} else if constexpr (sizeof(_Ty) == 8) {
return const_cast<_Ty*>(static_cast<const _Ty*>(__std_max_element_8(_First, _Last, _Signed)));
} else {
static_assert(_STD _Always_false<_Ty>, "Unexpected size");
}
}

template <class _Ty>
_STD pair<_Ty*, _Ty*> __std_minmax_element(_Ty* _First, _Ty* _Last) noexcept {
constexpr bool _Signed = _STD is_signed_v<_Ty>;

_Min_max_element_t _Res;

if constexpr (sizeof(_Ty) == 1) {
_Res = __std_minmax_element_1(_First, _Last, _Signed);
} else if constexpr (sizeof(_Ty) == 2) {
_Res = __std_minmax_element_2(_First, _Last, _Signed);
} else if constexpr (sizeof(_Ty) == 4) {
_Res = __std_minmax_element_4(_First, _Last, _Signed);
} else if constexpr (sizeof(_Ty) == 8) {
_Res = __std_minmax_element_8(_First, _Last, _Signed);
} else {
static_assert(_STD _Always_false<_Ty>, "Unexpected size");
}

return {const_cast<_Ty*>(static_cast<const _Ty*>(_Res._Min)), const_cast<_Ty*>(static_cast<const _Ty*>(_Res._Max))};
}
#endif // _USE_STD_VECTOR_ALGORITHMS

_STD_BEGIN
Expand Down Expand Up @@ -9297,8 +9373,33 @@ namespace ranges {
#endif // __cpp_lib_concepts
#endif // _HAS_CXX17

template <class _Iter, class _Pr, class _Elem = _Iter_value_t<_Iter>>
_INLINE_VAR constexpr bool _Is_min_max_optimization_safe = // Activate the vector algorithms for min_/max_element?
_Iterator_is_contiguous<_Iter> // The iterator must be contiguous so we can get raw pointers.
&& !_Iterator_is_volatile<_Iter> // The iterator must not be volatile.
&& conjunction_v<disjunction<is_integral<_Elem>, is_pointer<_Elem>>, // Element is of integral or pointer type.
disjunction< // And either of the following:
#ifdef __cpp_lib_concepts
is_same<_Pr, _RANGES less>, // predicate is ranges::less
#endif // __cpp_lib_concepts
is_same<_Pr, less<>>, is_same<_Pr, less<_Elem>>>>; // predicate is less

template <class _FwdIt, class _Pr>
constexpr _FwdIt _Max_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find largest element
#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (_Is_min_max_optimization_safe<_FwdIt, _Pr>) {
if (!_Is_constant_evaluated()) {
const auto _First_ptr = _To_address(_First);
const auto _Result = __std_max_element(_First_ptr, _To_address(_Last));
if constexpr (is_pointer_v<_FwdIt>) {
return _Result;
} else {
return _First + (_Result - _First_ptr);
}
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS

_FwdIt _Found = _First;
if (_First != _Last) {
while (++_First != _Last) {
Expand Down Expand Up @@ -9390,6 +9491,20 @@ namespace ranges {

template <class _FwdIt, class _Pr>
constexpr _FwdIt _Min_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { // find smallest element
#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (_Is_min_max_optimization_safe<_FwdIt, _Pr>) {
if (!_Is_constant_evaluated()) {
const auto _First_ptr = _To_address(_First);
const auto _Result = __std_min_element(_First_ptr, _To_address(_Last));
if constexpr (is_pointer_v<_FwdIt>) {
return _Result;
} else {
return _First + (_Result - _First_ptr);
}
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS

_FwdIt _Found = _First;
if (_First != _Last) {
while (++_First != _Last) {
Expand Down Expand Up @@ -9481,6 +9596,20 @@ namespace ranges {

template <class _FwdIt, class _Pr>
constexpr pair<_FwdIt, _FwdIt> _Minmax_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred) {
#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (_Is_min_max_optimization_safe<_FwdIt, _Pr>) {
if (!_Is_constant_evaluated()) {
const auto _First_ptr = _To_address(_First);
const auto _Result = __std_minmax_element(_First_ptr, _To_address(_Last));
if constexpr (is_pointer_v<_FwdIt>) {
return _Result;
} else {
return {_First + (_Result.first - _First_ptr), _First + (_Result.second - _First_ptr)};
}
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS

// find smallest and largest elements
pair<_FwdIt, _FwdIt> _Found(_First, _First);

Expand Down
4 changes: 4 additions & 0 deletions stl/inc/xtr1common
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ template <class _Ty, class... _Types>
_INLINE_VAR constexpr bool _Is_any_of_v = // true if and only if _Ty is in _Types
disjunction_v<is_same<_Ty, _Types>...>;

_NODISCARD constexpr bool _Is_constant_evaluated() noexcept { // Internal function for any standard mode
return __builtin_is_constant_evaluated();
}

#if _HAS_CXX20
_NODISCARD constexpr bool is_constant_evaluated() noexcept {
return __builtin_is_constant_evaluated();
Expand Down
Loading

0 comments on commit 0986811

Please sign in to comment.