diff --git a/libcxx/include/experimental/__simd/scalar.h b/libcxx/include/experimental/__simd/scalar.h index 1add4653209ace..789270a42083ee 100644 --- a/libcxx/include/experimental/__simd/scalar.h +++ b/libcxx/include/experimental/__simd/scalar.h @@ -67,6 +67,20 @@ struct __simd_operations<_Tp, simd_abi::__scalar> { static _LIBCPP_HIDE_FROM_ABI void __store(_SimdStorage __s, _Up* __mem) noexcept { *__mem = static_cast<_Up>(__s.__data); } + + static _LIBCPP_HIDE_FROM_ABI void __increment(_SimdStorage& __s) noexcept { ++__s.__data; } + + static _LIBCPP_HIDE_FROM_ABI void __decrement(_SimdStorage& __s) noexcept { --__s.__data; } + + static _LIBCPP_HIDE_FROM_ABI _MaskStorage __negate(_SimdStorage __s) noexcept { return {!__s.__data}; } + + static _LIBCPP_HIDE_FROM_ABI _SimdStorage __bitwise_not(_SimdStorage __s) noexcept { + return {static_cast<_Tp>(~__s.__data)}; + } + + static _LIBCPP_HIDE_FROM_ABI _SimdStorage __unary_minus(_SimdStorage __s) noexcept { + return {static_cast<_Tp>(-__s.__data)}; + } }; template diff --git a/libcxx/include/experimental/__simd/simd.h b/libcxx/include/experimental/__simd/simd.h index b494111b504407..40c8a7603a42a3 100644 --- a/libcxx/include/experimental/__simd/simd.h +++ b/libcxx/include/experimental/__simd/simd.h @@ -25,15 +25,29 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL inline namespace parallelism_v2 { +template +class __simd_int_operators {}; + +template +class __simd_int_operators<_Simd, _Impl, true> { +public: + // unary operators for integral _Tp + _LIBCPP_HIDE_FROM_ABI _Simd operator~() const noexcept { + return _Simd(_Impl::__bitwise_not((*static_cast(this)).__s_), _Simd::storage_tag); + } +}; + // class template simd [simd.class] // TODO: implement simd class template -class simd { +class simd : public __simd_int_operators, __simd_operations<_Tp, _Abi>, is_integral_v<_Tp>> { using _Impl = __simd_operations<_Tp, _Abi>; using _Storage = typename _Impl::_SimdStorage; _Storage __s_; + friend class __simd_int_operators; + public: using value_type = _Tp; using reference = __simd_reference<_Tp, _Storage, value_type>; @@ -45,8 +59,10 @@ class simd { _LIBCPP_HIDE_FROM_ABI simd() noexcept = default; // explicit conversion from and to implementation-defined types - explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const; - explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s) : __s_(__s) {} + struct storage_tag_t {}; + static constexpr storage_tag_t storage_tag{}; + explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const { return __s_; } + explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s, storage_tag_t) : __s_(__s) {} // broadcast constructor template >, int> = 0> @@ -88,6 +104,37 @@ class simd { // scalar access [simd.subscr] _LIBCPP_HIDE_FROM_ABI reference operator[](size_t __i) noexcept { return reference(__s_, __i); } _LIBCPP_HIDE_FROM_ABI value_type operator[](size_t __i) const noexcept { return __s_.__get(__i); } + + // simd unary operators + _LIBCPP_HIDE_FROM_ABI simd& operator++() noexcept { + _Impl::__increment(__s_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI simd operator++(int) noexcept { + simd __r = *this; + _Impl::__increment(__s_); + return __r; + } + + _LIBCPP_HIDE_FROM_ABI simd& operator--() noexcept { + _Impl::__decrement(__s_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI simd operator--(int) noexcept { + simd __r = *this; + _Impl::__decrement(__s_); + return __r; + } + + _LIBCPP_HIDE_FROM_ABI mask_type operator!() const noexcept { + return mask_type(_Impl::__negate(__s_), mask_type::storage_tag); + } + + _LIBCPP_HIDE_FROM_ABI simd operator+() const noexcept { return *this; } + + _LIBCPP_HIDE_FROM_ABI simd operator-() const noexcept { return simd(_Impl::__unary_minus(__s_), storage_tag); } }; template diff --git a/libcxx/include/experimental/__simd/simd_mask.h b/libcxx/include/experimental/__simd/simd_mask.h index 890edfc4d4d729..a8bb49265ef0ff 100644 --- a/libcxx/include/experimental/__simd/simd_mask.h +++ b/libcxx/include/experimental/__simd/simd_mask.h @@ -42,8 +42,10 @@ class simd_mask { _LIBCPP_HIDE_FROM_ABI simd_mask() noexcept = default; // explicit conversion from and to implementation-defined types - explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const; - explicit _LIBCPP_HIDE_FROM_ABI simd_mask(const _Storage& __s) : __s_(__s) {} + struct storage_tag_t {}; + static constexpr storage_tag_t storage_tag{}; + explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const { return __s_; } + explicit _LIBCPP_HIDE_FROM_ABI simd_mask(const _Storage& __s, storage_tag_t) : __s_(__s) {} // broadcast constructor _LIBCPP_HIDE_FROM_ABI explicit simd_mask(value_type __v) noexcept : __s_(_Impl::__broadcast(__v)) {} diff --git a/libcxx/include/experimental/__simd/vec_ext.h b/libcxx/include/experimental/__simd/vec_ext.h index 316866b84873dd..1a6cc2fb84fae6 100644 --- a/libcxx/include/experimental/__simd/vec_ext.h +++ b/libcxx/include/experimental/__simd/vec_ext.h @@ -86,6 +86,16 @@ struct __simd_operations<_Tp, simd_abi::__vec_ext<_Np>> { for (size_t __i = 0; __i < _Np; __i++) __mem[__i] = static_cast<_Up>(__s.__data[__i]); } + + static _LIBCPP_HIDE_FROM_ABI void __increment(_SimdStorage& __s) noexcept { __s.__data = __s.__data + 1; } + + static _LIBCPP_HIDE_FROM_ABI void __decrement(_SimdStorage& __s) noexcept { __s.__data = __s.__data - 1; } + + static _LIBCPP_HIDE_FROM_ABI _MaskStorage __negate(_SimdStorage __s) noexcept { return {!__s.__data}; } + + static _LIBCPP_HIDE_FROM_ABI _SimdStorage __bitwise_not(_SimdStorage __s) noexcept { return {~__s.__data}; } + + static _LIBCPP_HIDE_FROM_ABI _SimdStorage __unary_minus(_SimdStorage __s) noexcept { return {-__s.__data}; } }; template diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp new file mode 100644 index 00000000000000..f903b53b31e555 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// +// [simd.class] +// simd& operator++() noexcept; +// simd operator++(int) noexcept; +// simd& operator--() noexcept; +// simd operator--(int) noexcept; +// mask_type operator!() const noexcept; +// simd operator~() const noexcept; +// simd operator+() const noexcept; +// simd operator-() const noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +template +struct CheckSimdPrefixIncrementOperator { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + ex::simd origin_simd([](T i) { return i; }); + static_assert(noexcept(++origin_simd)); + std::array expected_return_value, expected_value; + for (size_t i = 0; i < array_size; ++i) { + expected_return_value[i] = static_cast(i) + 1; + expected_value[i] = static_cast(i) + 1; + } + assert_simd_values_equal(++origin_simd, expected_return_value); + assert_simd_values_equal(origin_simd, expected_value); + } +}; + +template +struct CheckSimdPostfixIncrementOperator { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + ex::simd origin_simd([](T i) { return i; }); + static_assert(noexcept(origin_simd++)); + std::array expected_return_value, expected_value; + for (size_t i = 0; i < array_size; ++i) { + expected_return_value[i] = static_cast(i); + expected_value[i] = static_cast(i) + 1; + } + assert_simd_values_equal(origin_simd++, expected_return_value); + assert_simd_values_equal(origin_simd, expected_value); + } +}; + +template +struct CheckSimdPrefixDecrementOperator { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + ex::simd origin_simd([](T i) { return i; }); + static_assert(noexcept(--origin_simd)); + std::array expected_return_value, expected_value; + for (size_t i = 0; i < array_size; ++i) { + expected_return_value[i] = static_cast(i) - 1; + expected_value[i] = static_cast(i) - 1; + } + assert_simd_values_equal(--origin_simd, expected_return_value); + assert_simd_values_equal(origin_simd, expected_value); + } +}; + +template +struct CheckSimdPostfixDecrementOperator { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + ex::simd origin_simd([](T i) { return i; }); + static_assert(noexcept(origin_simd--)); + std::array expected_return_value, expected_value; + for (size_t i = 0; i < array_size; ++i) { + expected_return_value[i] = static_cast(i); + expected_value[i] = static_cast(i) - 1; + } + assert_simd_values_equal(origin_simd--, expected_return_value); + assert_simd_values_equal(origin_simd, expected_value); + } +}; + +template +struct CheckSimdNegationOperator { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + ex::simd origin_simd([](T i) { return i; }); + static_assert(noexcept(!origin_simd)); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = !static_cast(i); + assert_simd_mask_values_equal(!origin_simd, expected_value); + } +}; + +template +struct CheckSimdBitwiseNotOperator { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + ex::simd origin_simd([](T i) { return i; }); + static_assert(noexcept(~origin_simd)); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = ~static_cast(i); + assert_simd_values_equal(~origin_simd, expected_value); + } +}; + +template +struct CheckSimdPositiveSignOperator { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + ex::simd origin_simd([](T i) { return i; }); + static_assert(noexcept(+origin_simd)); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = +static_cast(i); + assert_simd_values_equal(+origin_simd, expected_value); + } +}; + +template +struct CheckSimdNegativeSignOperator { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + ex::simd origin_simd([](T i) { return i; }); + static_assert(noexcept(-origin_simd)); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = -static_cast(i); + assert_simd_values_equal(-origin_simd, expected_value); + } +}; + +template , class = void> +struct has_bitwise_not_op : std::false_type {}; + +template +struct has_bitwise_not_op>())>> : std::true_type {}; + +template +struct CheckSimdBitwiseNotTraits { + template + void operator()() { + // This function shall not participate in overload resolution unless + // T is an integral type. + if constexpr (std::is_integral_v) + static_assert(has_bitwise_not_op::value); + // T is not an integral type. + else + static_assert(!has_bitwise_not_op::value); + } +}; + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + types::for_each(types::integer_types(), TestAllSimdAbiFunctor()); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +}