Skip to content

[libc++] <experimental/simd> Add compound assignment operators for simd reference #86761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 4, 2024
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
1 change: 1 addition & 0 deletions libcxx/docs/Status/ParallelismProjects.csv
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Section,Description,Dependencies,Assignee,Complete
| `[parallel.simd.reference] <https://wg21.link/N4808>`_, "`Element references operator value_type() <https://github.com/llvm/llvm-project/pull/68960>`_", None, Yin Zhang, |Complete|
| `[parallel.simd.reference] <https://wg21.link/N4808>`_, "`Element references operator= <https://github.com/llvm/llvm-project/pull/70020>`_", None, Yin Zhang, |Complete|
| `[parallel.simd.reference] <https://wg21.link/N4808>`_, "`Element references swap functions <https://github.com/llvm/llvm-project/pull/86478>`_", None, Yin Zhang, |Complete|
| `[parallel.simd.reference] <https://wg21.link/N4808>`_, "`Element references compound assignment operators <https://github.com/llvm/llvm-project/pull/86761>`_", None, Yin Zhang, |Complete|
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`Class template simd declaration and alias <https://reviews.llvm.org/D144362>`_", [parallel.simd.abi], Yin Zhang, |Complete|
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd<>::size() <https://reviews.llvm.org/D144363>`_", [parallel.simd.traits] simd_size[_v], Yin Zhang, |Complete|
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd default constructor <https://github.com/llvm/llvm-project/pull/70424>`_", None, Yin Zhang, |Complete|
Expand Down
61 changes: 61 additions & 0 deletions libcxx/include/experimental/__simd/reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <__type_traits/is_assignable.h>
#include <__type_traits/is_same.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
#include <__utility/move.h>
#include <cstddef>
Expand Down Expand Up @@ -71,6 +72,66 @@ class __simd_reference {

template <class _Tp1, class _Storage1, class _Vp1>
friend void swap(__simd_reference<_Tp1, _Storage1, _Vp1>&& __a, _Vp1& __b) noexcept;

template <class _Up, class = decltype(std::declval<value_type&>() += std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator+=(_Up&& __v) && noexcept {
__set(__get() + static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() -= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator-=(_Up&& __v) && noexcept {
__set(__get() - static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() *= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator*=(_Up&& __v) && noexcept {
__set(__get() * static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() /= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator/=(_Up&& __v) && noexcept {
__set(__get() / static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() %= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator%=(_Up&& __v) && noexcept {
__set(__get() % static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() &= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator&=(_Up&& __v) && noexcept {
__set(__get() & static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() |= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator|=(_Up&& __v) && noexcept {
__set(__get() | static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() ^= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator^=(_Up&& __v) && noexcept {
__set(__get() ^ static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() <<= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator<<=(_Up&& __v) && noexcept {
__set(__get() << static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}

template <class _Up, class = decltype(std::declval<value_type&>() >>= std::declval<_Up>())>
_LIBCPP_HIDE_FROM_ABI __simd_reference operator>>=(_Up&& __v) && noexcept {
__set(__get() >> static_cast<value_type>(std::forward<_Up>(__v)));
return {__s_, __idx_};
}
};

template <class _Tp, class _Storage, class _Vp>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14

// <experimental/simd>
//
// [simd.reference]
// template<class U> reference+=(U&& x) && noexcept;
// template<class U> reference-=(U&& x) && noexcept;
// template<class U> reference*=(U&& x) && noexcept;
// template<class U> reference/=(U&& x) && noexcept;
// template<class U> reference%=(U&& x) && noexcept;

#include "../test_utils.h"
#include <experimental/simd>

namespace ex = std::experimental::parallelism_v2;

struct PlusAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) += rhs;
}
};

struct MinusAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) -= rhs;
}
};

struct MultipliesAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) *= rhs;
}
};

struct DividesAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) /= rhs;
}
};

struct ModulusAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) %= rhs;
}
};

template <typename T, typename SimdAbi, typename Op, typename OpAssign>
struct SimdReferenceOperatorHelper {
template <class U>
void operator()() const {
ex::simd<T, SimdAbi> origin_simd(static_cast<T>(3));
static_assert(noexcept(OpAssign{}(origin_simd[0], static_cast<U>(2))));
OpAssign{}(origin_simd[0], static_cast<U>(2));
assert((T)origin_simd[0] == (T)Op{}(static_cast<T>(3), static_cast<T>(std::forward<U>(2))));
}
};

template <class T, std::size_t>
struct CheckReferenceArithOperators {
template <class SimdAbi>
void operator()() {
types::for_each(simd_test_types(), SimdReferenceOperatorHelper<T, SimdAbi, std::plus<>, PlusAssign>());
types::for_each(simd_test_types(), SimdReferenceOperatorHelper<T, SimdAbi, std::minus<>, MinusAssign>());
types::for_each(simd_test_types(), SimdReferenceOperatorHelper<T, SimdAbi, std::multiplies<>, MultipliesAssign>());
types::for_each(simd_test_types(), SimdReferenceOperatorHelper<T, SimdAbi, std::divides<>, DividesAssign>());
}
};

template <class T, std::size_t>
struct CheckReferenceModOperators {
template <class SimdAbi>
void operator()() {
types::for_each(
simd_test_integer_types(), SimdReferenceOperatorHelper<T, SimdAbi, std::modulus<>, ModulusAssign>());
}
};

int main(int, char**) {
test_all_simd_abi<CheckReferenceArithOperators>();
types::for_each(types::integer_types(), TestAllSimdAbiFunctor<CheckReferenceModOperators>());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14

// <experimental/simd>
//
// [simd.reference]
// template<class U> reference|=(U&& x) && noexcept;
// template<class U> reference&=(U&& x) && noexcept;
// template<class U> reference^=(U&& x) && noexcept;
// template<class U> reference<<=(U&& x) && noexcept;
// template<class U> reference>>=(U&& x) && noexcept;

#include "../test_utils.h"
#include <experimental/simd>

namespace ex = std::experimental::parallelism_v2;

struct AndAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) &= rhs;
}
};

struct OrAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) |= rhs;
}
};

struct XorAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) ^= rhs;
}
};

struct LeftShiftAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) <<= rhs;
}
};

struct RightShiftAssign {
template <typename T, typename U>
void operator()(T&& lhs, const U& rhs) const noexcept {
std::forward<T>(lhs) >>= rhs;
}
};

struct LeftShift {
template <typename T, typename U>
T operator()(const T& lhs, const U& rhs) const noexcept {
return lhs << rhs;
}
};

struct RightShift {
template <typename T, typename U>
T operator()(const T& lhs, const U& rhs) const noexcept {
return lhs >> rhs;
}
};

template <typename T, typename SimdAbi, typename Op, typename OpAssign>
struct SimdReferenceOperatorHelper {
template <class U>
void operator()() const {
ex::simd<T, SimdAbi> origin_simd(static_cast<T>(3));
static_assert(noexcept(OpAssign{}(origin_simd[0], static_cast<U>(2))));
OpAssign{}(origin_simd[0], static_cast<U>(2));
assert((T)origin_simd[0] == (T)Op{}(static_cast<T>(3), static_cast<T>(std::forward<U>(2))));
}
};

template <typename T, typename SimdAbi, typename Op, typename OpAssign>
struct MaskReferenceOperatorHelper {
template <class U>
void operator()() const {
ex::simd_mask<T, SimdAbi> origin_mask(true);
static_assert(noexcept(OpAssign{}(origin_mask[0], static_cast<U>(false))));
OpAssign{}(origin_mask[0], static_cast<U>(false));
assert((bool)origin_mask[0] == (bool)Op{}(true, static_cast<bool>(std::forward<U>(false))));
}
};

template <class T, std::size_t>
struct CheckReferenceBitwiseOperators {
template <class SimdAbi>
void operator()() {
types::for_each(simd_test_integer_types(), SimdReferenceOperatorHelper<T, SimdAbi, std::bit_and<>, AndAssign>());
types::for_each(simd_test_integer_types(), SimdReferenceOperatorHelper<T, SimdAbi, std::bit_or<>, OrAssign>());
types::for_each(simd_test_integer_types(), SimdReferenceOperatorHelper<T, SimdAbi, std::bit_xor<>, XorAssign>());
types::for_each(simd_test_integer_types(), SimdReferenceOperatorHelper<T, SimdAbi, LeftShift, LeftShiftAssign>());
types::for_each(simd_test_integer_types(), SimdReferenceOperatorHelper<T, SimdAbi, RightShift, RightShiftAssign>());

types::for_each(simd_test_integer_types(), MaskReferenceOperatorHelper<T, SimdAbi, std::bit_and<>, AndAssign>());
types::for_each(simd_test_integer_types(), MaskReferenceOperatorHelper<T, SimdAbi, std::bit_or<>, OrAssign>());
types::for_each(simd_test_integer_types(), MaskReferenceOperatorHelper<T, SimdAbi, std::bit_xor<>, XorAssign>());
}
};

int main(int, char**) {
types::for_each(types::integer_types(), TestAllSimdAbiFunctor<CheckReferenceBitwiseOperators>());
return 0;
}
11 changes: 6 additions & 5 deletions libcxx/test/std/experimental/simd/test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,16 @@ using arithmetic_no_bool_types = types::concatenate_t<types::integer_types, type

// For interfaces with vectorizable type template parameters, we only use some common or boundary types
// as template parameters for testing to ensure that the compilation time of a single test does not exceed.
using simd_test_types =
using simd_test_integer_types =
types::type_list<char,
unsigned,
int,
int
#ifndef TEST_HAS_NO_INT128
__int128_t,
,
__int128_t
#endif
float,
double>;
>;
using simd_test_types = types::concatenate_t<simd_test_integer_types, types::type_list<float, double>>;

template <template <class T, std::size_t N> class Func>
void test_all_simd_abi() {
Expand Down
Loading