diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -789,6 +789,7 @@ expected experimental/__config experimental/__memory + experimental/__simd/declaration.h experimental/__simd/scalar.h experimental/__simd/utility.h experimental/__simd/vec_ext.h diff --git a/libcxx/include/experimental/__simd/declaration.h b/libcxx/include/experimental/__simd/declaration.h new file mode 100644 --- /dev/null +++ b/libcxx/include/experimental/__simd/declaration.h @@ -0,0 +1,31 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_EXPERIMENTAL___SIMD_DECLARATION_H +#define _LIBCPP_EXPERIMENTAL___SIMD_DECLARATION_H + +#include + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD + +template +struct __simd_storage; + +template +struct __mask_storage; + +template +struct __simd_traits; + +template +struct __mask_traits; + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD + +#endif // _LIBCPP_EXPERIMENTAL___SIMD_DECLARATION_H diff --git a/libcxx/include/experimental/__simd/scalar.h b/libcxx/include/experimental/__simd/scalar.h --- a/libcxx/include/experimental/__simd/scalar.h +++ b/libcxx/include/experimental/__simd/scalar.h @@ -12,6 +12,7 @@ #include #include +#include _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD_ABI @@ -22,4 +23,46 @@ _LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD_ABI +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD + +template +struct __simd_storage<_Tp, simd_abi::__scalar> { + _Tp __data; + + _Tp __get(size_t __idx) const noexcept { return (&__data)[__idx]; } + void __set(size_t __idx, _Tp __v) noexcept { (&__data)[__idx] = __v; } +}; + +template +struct __mask_storage<_Tp, simd_abi::__scalar> : __simd_storage {}; + +template +struct __simd_traits<_Tp, simd_abi::__scalar> { + using _Simd = __simd_storage<_Tp, simd_abi::__scalar>; + using _Mask = __mask_storage<_Tp, simd_abi::__scalar>; + + static _Simd __broadcast(_Tp __v) noexcept { return {__v}; } + + template + static _Simd __generate(_Generator&& __g) noexcept { + return {__g(std::integral_constant())}; + } + + template + static void __load(_Simd& __s, const _Up* __mem) noexcept { + __s.__data = static_cast<_Tp>(__mem[0]); + } +}; + +template +struct __mask_traits<_Tp, simd_abi::__scalar> { + using _Mask = __mask_storage<_Tp, simd_abi::__scalar>; + + static _Mask __broadcast(bool __v) noexcept { return {__v}; } + + static void __load(_Mask& __s, const bool* __mem) noexcept { __s.__data = __mem[0]; } +}; + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD + #endif // _LIBCPP_EXPERIMENTAL___SIMD_SCALAR_H diff --git a/libcxx/include/experimental/__simd/utility.h b/libcxx/include/experimental/__simd/utility.h --- a/libcxx/include/experimental/__simd/utility.h +++ b/libcxx/include/experimental/__simd/utility.h @@ -72,6 +72,33 @@ return false; } +template +auto __choose_mask_type() { + if constexpr (sizeof(_Tp) == 1) { + return uint8_t{}; + } else if constexpr (sizeof(_Tp) == 2) { + return uint16_t{}; + } else if constexpr (sizeof(_Tp) == 4) { + return uint32_t{}; + } else if constexpr (sizeof(_Tp) == 8) { + return uint64_t{}; + } +#ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) == 16) { + return __uint128_t{}; + } +#endif +} + +template +auto __set_all_bits(bool __v) { + using _Up = decltype(__choose_mask_type<_Tp>()); + _Up __res = 0; + for (unsigned long __i = 0; __i < __CHAR_BIT__ * sizeof(_Tp); __i++) + __res |= static_cast<_Up>(__v) << __i; + return __res; +} + _LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD #endif // _LIBCPP_EXPERIMENTAL___SIMD_UTILITY_H diff --git a/libcxx/include/experimental/__simd/vec_ext.h b/libcxx/include/experimental/__simd/vec_ext.h --- a/libcxx/include/experimental/__simd/vec_ext.h +++ b/libcxx/include/experimental/__simd/vec_ext.h @@ -12,6 +12,8 @@ #include #include +#include +#include _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD_ABI @@ -23,4 +25,70 @@ _LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD_ABI +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD + +template +struct __simd_storage<_Tp, simd_abi::__vec_ext<_Np>> { + _Tp __data __attribute__((vector_size(sizeof(_Tp) * _Np))); + + _Tp __get(size_t __idx) const noexcept { return __data[__idx]; } + void __set(size_t __idx, _Tp __v) noexcept { __data[__idx] = __v; } +}; + +template +struct __mask_storage<_Tp, simd_abi::__vec_ext<_Np>> + : __simd_storage()), simd_abi::__vec_ext<_Np>> {}; + +template +struct __simd_traits<_Tp, simd_abi::__vec_ext<_Np>> { + using _Simd = __simd_storage<_Tp, simd_abi::__vec_ext<_Np>>; + using _Mask = __mask_storage<_Tp, simd_abi::__vec_ext<_Np>>; + + static _Simd __broadcast(_Tp __v) noexcept { + return __generate([=](size_t) { return __v; }); + } + + template + static _Simd __generate_init(_Generator&& __g, std::index_sequence<_Is...>) { + return {__g(std::integral_constant())...}; + } + + template + static _Simd __generate(_Generator&& __g) noexcept { + return __generate_init(std::forward<_Generator>(__g), std::make_index_sequence<_Np>()); + } + + template + static void __load(_Simd& __s, const _Up* __mem) noexcept { + for (size_t __i = 0; __i < _Np; __i++) + __s.__data[__i] = static_cast<_Tp>(__mem[__i]); + } +}; + +template +struct __mask_traits<_Tp, simd_abi::__vec_ext<_Np>> { + using _Mask = __mask_storage<_Tp, simd_abi::__vec_ext<_Np>>; + + static _Mask __broadcast(bool __v) noexcept { + return __generate([=](size_t) { return __set_all_bits<_Tp>(__v); }); + } + + template + static _Mask __generate_init(_Generator&& __g, std::index_sequence<_Is...>) { + return _Mask{{__g(std::integral_constant())...}}; + } + + template + static _Mask __generate(_Generator&& __g) noexcept { + return __generate_init(std::forward<_Generator>(__g), std::make_index_sequence<_Np>()); + } + + static void __load(_Mask& __s, const bool* __mem) noexcept { + for (size_t __i = 0; __i < _Np; __i++) + __s.__data[__i] = __set_all_bits<_Tp>(__mem[__i]); + } +}; + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD + #endif // _LIBCPP_EXPERIMENTAL___SIMD_VEC_EXT_H diff --git a/libcxx/include/experimental/simd b/libcxx/include/experimental/simd --- a/libcxx/include/experimental/simd +++ b/libcxx/include/experimental/simd @@ -265,16 +265,38 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD -template class __simd_reference { // exposition only +template class __simd_reference { // exposition only + template friend class simd; + template friend class simd_mask; + + _Storage& __s_; + size_t __idx_; + + __simd_reference(_Storage& __s, size_t __idx) : __s_(__s), __idx_(__idx) {} + + _Vp __get() const { return __s_.__get(__idx_); } + + void __set(_Vp __v) { + if constexpr (is_same_v<_Vp, bool>) + __s_.__set(__idx_, __set_all_bits<_Tp>(__v)); + else + __s_.__set(__idx_, __v); + } + public: using value_type = _Vp; __simd_reference() = delete; __simd_reference(const __simd_reference&) = delete; - operator value_type() const noexcept; + operator value_type() const noexcept { return __get(); } - template __simd_reference operator=(_Up&& __x) && noexcept; + template + enable_if_t() = declval<_Up>())>>, __simd_reference> + operator=(_Up&& __v) && noexcept { + __set(static_cast(std::forward<_Up>(__v))); + return {__s_, __idx_}; + } template __simd_reference operator+=(_Up&& __x) && noexcept; template __simd_reference operator-=(_Up&& __x) && noexcept; @@ -569,9 +591,14 @@ // 9.6, Class template simd defination template class simd { + using _Impl = __simd_traits<_Tp, _Abi>; + using _Storage = typename _Impl::_Simd; + + _Storage __s_; + public: using value_type = _Tp; - using reference = __simd_reference; + using reference = __simd_reference<_Tp, _Storage, value_type>; using mask_type = simd_mask<_Tp, _Abi>; using abi_type = _Abi; @@ -581,19 +608,30 @@ // 9.6.4, simd constructors template(), int> = 0> - simd(_Up&& __value) noexcept; - template simd(const simd<_Up, simd_abi::fixed_size>&) noexcept; + simd(_Up&& __value) noexcept : __s_(_Impl::__broadcast(static_cast(__value))) {} + + template> && + __is_non_narrowing_arithmetic_convertible<_Up, value_type>(), int> = 0> + simd(const simd<_Up, simd_abi::fixed_size>& __value) noexcept { + for (size_t __i = 0; __i < size(); __i++) + (*this)[__i] = static_cast<_Tp>(__value[__i]); + } + template(make_index_sequence()), int> = 0> - explicit simd(_Gen&& __gen) noexcept; - template simd(const _Up* __mem, _Flags); + explicit simd(_Gen&& __gen) noexcept : __s_(_Impl::__generate(std::forward<_Gen>(__gen))) {} + + template() && is_simd_flag_type_v<_Flags>, int> = 0> + simd(const _Up* __mem, _Flags) { + _Impl::__load(__s_, _Flags::template __apply(__mem)); + } // 9.6.5, simd copy functions template void copy_from(const _Up* __mem, _Flags); template void copy_to(_Up* __mem, _Flags); // 9.6.6, simd subscript operators - reference operator[](size_t); - value_type operator[](size_t) const; + reference operator[](size_t __i) { return reference(__s_, __i); } + value_type operator[](size_t __i) const { return __s_.__get(__i); } // 9.6.7, simd unary operators simd& operator++() noexcept; @@ -644,9 +682,14 @@ // 9.8, Class template simd_mask defination template class simd_mask { + using _Impl = __mask_traits<_Tp, _Abi>; + using _Storage = typename _Impl::_Mask; + + _Storage __s_; + public: using value_type = bool; - using reference = __simd_reference; + using reference = __simd_reference<_Tp, _Storage, value_type>; using simd_type = simd<_Tp, _Abi>; using abi_type = _Abi; @@ -655,18 +698,26 @@ simd_mask() noexcept = default; // 9.8.3, Constructors - explicit simd_mask(value_type) noexcept; - template - simd_mask(const simd_mask<_Up, simd_abi::fixed_size>&) noexcept; - template simd_mask(const value_type* __mem, _Flags); + explicit simd_mask(value_type __value) noexcept : __s_(_Impl::__broadcast(__value)) {} + + template && is_same_v>, int> = 0> + simd_mask(const simd_mask<_Up, simd_abi::fixed_size>& __value) noexcept { + for (size_t __i = 0; __i < size(); __i++) + (*this)[__i] = __value[__i]; + } + + template, int> = 0> + simd_mask(const value_type* __mem, _Flags) { + _Impl::__load(__s_, _Flags::template __apply(__mem)); + } // 9.8.4, Copy functions template void copy_from(const value_type* __mem, _Flags); template void copy_to(value_type* __mem, _Flags); // 9.8.5, Subscript operators - reference operator[](size_t); - value_type operator[](size_t) const; + reference operator[](size_t __i) { return reference(__s_, __i); } + value_type operator[](size_t __i) const { return __s_.__get(__i); } // 9.8.6, Unary operators simd_mask operator!() const noexcept; diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_ctor.pass.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_ctor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_ctor.pass.cpp @@ -0,0 +1,181 @@ +//===----------------------------------------------------------------------===// +// +// 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] +// template simd(U&& value) noexcept; +// template simd(const simd>&) noexcept; +// template explicit simd(G&& gen) noexcept; +// template simd(const U* mem, Flags f); + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; +template +class implicit_type { + T val; + +public: + implicit_type(T val) : val(val) {} + operator T() const { return val; } +}; + +#define GENARETE_FOR_BROADCAST_SIMD_CTOR(_Up, suffix) \ + if constexpr (ex::__is_non_narrowing_arithmetic_convertible<_Up, _Tp>()) { \ + ex::simd<_Tp, SimdAbi> broadcast_from_type_##suffix(static_cast<_Up>(3)); \ + assert_simd_value_correct(broadcast_from_type_##suffix, origin_value); \ + } + +struct CheckBroadCastSimdCtor { + template + void operator()() { + constexpr size_t array_size = ex::simd_size_v<_Tp, SimdAbi>; + std::array<_Tp, array_size> origin_value; + for (size_t i = 0; i < array_size; ++i) + origin_value[i] = static_cast<_Tp>(3); + + GENARETE_FOR_BROADCAST_SIMD_CTOR(char16_t, char16) + GENARETE_FOR_BROADCAST_SIMD_CTOR(char32_t, char32) + GENARETE_FOR_BROADCAST_SIMD_CTOR(unsigned char, uchar) + GENARETE_FOR_BROADCAST_SIMD_CTOR(signed char, schar) + GENARETE_FOR_BROADCAST_SIMD_CTOR(wchar_t, wchar) + GENARETE_FOR_BROADCAST_SIMD_CTOR(unsigned short, ushort) + GENARETE_FOR_BROADCAST_SIMD_CTOR(short, short) + GENARETE_FOR_BROADCAST_SIMD_CTOR(unsigned int, uint) + GENARETE_FOR_BROADCAST_SIMD_CTOR(int, int) + GENARETE_FOR_BROADCAST_SIMD_CTOR(unsigned long, ulong) + GENARETE_FOR_BROADCAST_SIMD_CTOR(long, long) + GENARETE_FOR_BROADCAST_SIMD_CTOR(unsigned long long, ulonglong) + GENARETE_FOR_BROADCAST_SIMD_CTOR(long long, longlong) + GENARETE_FOR_BROADCAST_SIMD_CTOR(float, float) + GENARETE_FOR_BROADCAST_SIMD_CTOR(double, double) + GENARETE_FOR_BROADCAST_SIMD_CTOR(long double, longdouble) + + implicit_type<_Tp> implicit_value_3(3); + ex::simd<_Tp, SimdAbi> expected_simd_from_implicit_convert(implicit_value_3); + assert_simd_value_correct(expected_simd_from_implicit_convert, origin_value); + + int int_value_3 = 3; + ex::simd<_Tp, SimdAbi> expected_simd_from_int(int_value_3); + assert_simd_value_correct(expected_simd_from_int, origin_value); + + if constexpr (std::is_unsigned_v<_Tp>) { + unsigned int uint_value_3 = static_cast(3); + ex::simd<_Tp, SimdAbi> expected_simd_from_uint(uint_value_3); + assert_simd_value_correct(expected_simd_from_uint, origin_value); + } + } +}; + +#define GENARETE_FOR_FIXED_SIMD_CTOR(_Up, suffix) \ + if constexpr (ex::__is_non_narrowing_arithmetic_convertible<_Up, _Tp>()) { \ + ex::simd<_Up, SimdAbi> suffix##_simd([](_Up i) { return i; }); \ + ex::simd<_Tp, SimdAbi> fixed_from_type_##suffix(suffix##_simd); \ + std::array<_Tp, _Np> expected_value_in_##suffix; \ + for (size_t i = 0; i < _Np; i++) \ + expected_value_in_##suffix[i] = static_cast<_Tp>(suffix##_simd[i]); \ + assert_simd_value_correct(fixed_from_type_##suffix, expected_value_in_##suffix); \ + } + +struct CheckFixedSimdCtor { + template + void operator()() { + if constexpr (std::is_same_v>) { + GENARETE_FOR_FIXED_SIMD_CTOR(char16_t, char16) + GENARETE_FOR_FIXED_SIMD_CTOR(char32_t, char32) + GENARETE_FOR_FIXED_SIMD_CTOR(unsigned char, uchar) + GENARETE_FOR_FIXED_SIMD_CTOR(signed char, schar) + GENARETE_FOR_FIXED_SIMD_CTOR(wchar_t, wchar) + GENARETE_FOR_FIXED_SIMD_CTOR(unsigned short, ushort) + GENARETE_FOR_FIXED_SIMD_CTOR(short, short) + GENARETE_FOR_FIXED_SIMD_CTOR(unsigned int, uint) + GENARETE_FOR_FIXED_SIMD_CTOR(int, int) + GENARETE_FOR_FIXED_SIMD_CTOR(unsigned long, ulong) + GENARETE_FOR_FIXED_SIMD_CTOR(long, long) + GENARETE_FOR_FIXED_SIMD_CTOR(unsigned long long, ulonglong) + GENARETE_FOR_FIXED_SIMD_CTOR(long long, longlong) + GENARETE_FOR_FIXED_SIMD_CTOR(float, float) + GENARETE_FOR_FIXED_SIMD_CTOR(double, double) + GENARETE_FOR_FIXED_SIMD_CTOR(long double, longdouble) + } + } +}; + +struct CheckGenerateSimdCtor { + template + void operator()() { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + constexpr size_t array_size = origin_simd.size(); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i); + assert_simd_value_correct(origin_simd, expected_value); + } +}; + +struct CheckLoadSimdCtor { + template + void operator()() { + { + constexpr auto alignas_size = ex::memory_alignment_v, _Tp>; + constexpr auto array_size = ex::simd_size_v<_Tp, SimdAbi>; + alignas(alignas_size) _Tp buffer[array_size]; + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; i++) { + buffer[i] = static_cast<_Tp>(i + 1); + expected_value[i] = buffer[i]; + } + ex::simd<_Tp, SimdAbi> origin_simd(buffer, ex::vector_aligned_tag()); + assert_simd_value_correct(origin_simd, expected_value); + } + { + constexpr auto alignas_size = 32; + constexpr auto array_size = ex::simd_size_v<_Tp, SimdAbi>; + alignas(alignas_size) _Tp buffer[array_size]; + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; i++) { + buffer[i] = static_cast<_Tp>(i + 1); + expected_value[i] = buffer[i]; + } + ex::simd<_Tp, SimdAbi> origin_simd(buffer, ex::overaligned_tag()); + assert_simd_value_correct(origin_simd, expected_value); + } + { + constexpr auto alignas_size = std::bit_ceil(alignof(_Tp)); + constexpr auto array_size = ex::simd_size_v<_Tp, SimdAbi>; + alignas(alignas_size) _Tp buffer[array_size]; + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; i++) { + buffer[i] = static_cast<_Tp>(i + 1); + expected_value[i] = buffer[i]; + } + ex::simd<_Tp, SimdAbi> origin_simd(buffer, ex::element_aligned_tag()); + assert_simd_value_correct(origin_simd, expected_value); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi, _Np>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_default.pass.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_default.pass.cpp @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// 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() = default; +// simd(const simd&) = default; +// simd& operator=(const simd&) = default; +// simd(simd&&) noexcept = default; +// simd& operator=(simd&&) noexcept = default; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +// See https://www.open-std.org/jtc1/sc22/WG21/docs/papers/2019/n4808.pdf +// Default intialization performs no initialization of the elements; value-initialization initializes each element with T(). +// [ Note: Thus, default initialization leaves the elements in an indeterminate state. — end note ] +struct CheckSimdDefaultCtor { + template + void operator()() { + ex::simd<_Tp, SimdAbi> pure_simd; + // trash value in default ctor + static_assert(pure_simd.size() > 0); + } +}; +struct CheckSimdCopyCtor { + template + void operator()() { + ex::simd<_Tp, SimdAbi> pure_simd([](_Tp i) { return i; }); + ex::simd<_Tp, SimdAbi> from_copy_ctor(pure_simd); + std::array<_Tp, pure_simd.size()> expected_value; + for (size_t i = 0; i < pure_simd.size(); ++i) + expected_value[i] = pure_simd[i]; + assert_simd_value_correct(from_copy_ctor, expected_value); + } +}; +struct CheckSimdMoveCtor { + template + void operator()() { + ex::simd<_Tp, SimdAbi> pure_simd([](_Tp i) { return i; }); + ex::simd<_Tp, SimdAbi> from_move_ctor(std::move(pure_simd)); + std::array<_Tp, pure_simd.size()> expected_value; + for (size_t i = 0; i < pure_simd.size(); ++i) + expected_value[i] = pure_simd[i]; + assert_simd_value_correct(from_move_ctor, expected_value); + } +}; +struct CheckSimdCopyAssignment { + template + void operator()() { + ex::simd<_Tp, SimdAbi> pure_simd([](_Tp i) { return i; }); + ex::simd<_Tp, SimdAbi> from_copy_assignment; + from_copy_assignment = pure_simd; + std::array<_Tp, pure_simd.size()> expected_value; + for (size_t i = 0; i < pure_simd.size(); ++i) + expected_value[i] = pure_simd[i]; + assert_simd_value_correct(from_copy_assignment, expected_value); + } +}; +struct CheckSimdMoveAssignment { + template + void operator()() { + ex::simd<_Tp, SimdAbi> pure_simd([](_Tp i) { return i; }); + ex::simd<_Tp, SimdAbi> from_move_assignment; + from_move_assignment = std::move(pure_simd); + std::array<_Tp, pure_simd.size()> expected_value; + for (size_t i = 0; i < pure_simd.size(); ++i) + expected_value[i] = pure_simd[i]; + assert_simd_value_correct(from_move_assignment, expected_value); + } +}; +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_subscr.pass.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_subscr.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_subscr.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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] +// reference operator[](size_t i); +// value_type operator[](size_t i) const; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdReferenceSubscr { + template + void operator()() { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + for (size_t i = 0; i < origin_simd.size(); ++i) { + static_assert(is_simd_reference::value); + assert(origin_simd[i] == static_cast<_Tp>(i)); + } + } +}; +struct CheckSimdValueTypeSubscr { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + for (size_t i = 0; i < origin_simd.size(); ++i) { + static_assert(std::is_pod_v); + static_assert(std::is_same_v<_Tp, decltype(origin_simd[i])>); + assert(origin_simd[i] == static_cast<_Tp>(i)); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_ctor.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_ctor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_ctor.pass.cpp @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// +// 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.mask.class] +// explicit simd_mask(value_type) noexcept; +// template simd_mask(const simd_mask>&) noexcept; +// template simd_mask(const value_type* mem, Flags); + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckBroadCastSimdMaskCtor { + template + void operator()() { + constexpr size_t array_size = ex::simd_size_v<_Tp, SimdAbi>; + const ex::simd_mask<_Tp, SimdAbi> mask_ctor_from_broadcast(false); + const std::array expected_value{}; + assert_simd_mask_value_correct(mask_ctor_from_broadcast, expected_value); + } +}; + +#define GENERATE_FOR_FIXED_SIMD_MASK_CTOR(_Up, suffix) \ + ex::simd_mask<_Up, SimdAbi> suffix##_mask; \ + std::array expected_value_in_##suffix; \ + for (size_t i = 0; i < _Np; i++) { \ + suffix##_mask[i] = static_cast(i % 2); \ + expected_value_in_##suffix[i] = suffix##_mask[i]; \ + } \ + ex::simd_mask<_Tp, SimdAbi> fixed_mask_from_type_##suffix(suffix##_mask); \ + assert_simd_mask_value_correct(fixed_mask_from_type_##suffix, expected_value_in_##suffix); + +struct CheckFixedSimdMaskCtor { + template + void operator()() { + if constexpr (std::is_same_v>) { + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(char16_t, char16) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(char32_t, char32) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(unsigned char, uchar) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(signed char, schar) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(wchar_t, wchar) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(unsigned short, ushort) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(short, short) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(unsigned int, uint) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(int, int) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(unsigned long, ulong) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(long, long) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(unsigned long long, ulonglong) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(long long, longlong) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(float, float) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(double, double) + GENERATE_FOR_FIXED_SIMD_MASK_CTOR(long double, longdouble) + } + } +}; + +struct CheckLoadSimdMaskCtor { + template + void operator()() { + constexpr size_t array_size = ex::simd_size_v<_Tp, SimdAbi>; + { + alignas(ex::memory_alignment_v>) bool buffer[array_size]; + std::array expected_value; + for (size_t i = 0; i < array_size; i++) { + buffer[i] = static_cast(i % 2); + expected_value[i] = buffer[i]; + } + ex::simd_mask<_Tp, SimdAbi> simd_mask(buffer, ex::vector_aligned_tag()); + assert_simd_mask_value_correct(simd_mask, expected_value); + } + { + alignas(8) bool buffer[array_size]; + std::array expected_value; + for (size_t i = 0; i < array_size; i++) { + buffer[i] = static_cast(i % 2); + expected_value[i] = buffer[i]; + } + ex::simd_mask<_Tp, SimdAbi> simd_mask(buffer, ex::overaligned_tag<8>()); + assert_simd_mask_value_correct(simd_mask, expected_value); + } + { + alignas(alignof(bool)) bool buffer[array_size]; + std::array expected_value; + for (size_t i = 0; i < array_size; i++) { + buffer[i] = static_cast(i % 2); + expected_value[i] = buffer[i]; + } + ex::simd_mask<_Tp, SimdAbi> simd_mask(buffer, ex::element_aligned_tag()); + assert_simd_mask_value_correct(simd_mask, expected_value); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi, _Np>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_default.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_default.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// 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.mask.class] +// simd_mask() = default; +// simd_mask(const simd_mask&) = default; +// simd_mask& operator=(const simd_mask&) = default; +// simd_mask(simd_mask&&) noexcept = default; +// simd_mask& operator=(simd_mask&&) noexcept = default; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +// See https://www.open-std.org/jtc1/sc22/WG21/docs/papers/2019/n4808.pdf +// Default intialization performs no initialization of the elements; value-initialization initializes each element with T(). +// [ Note: Thus, default initialization leaves the elements in an indeterminate state. — end note ] +struct CheckSimdMaskDefaultCtor { + template + void operator()() { + ex::simd_mask<_Tp, SimdAbi> pure_mask; + // trash value in default ctor + static_assert(pure_mask.size() > 0); + } +}; +struct CheckSimdMaskCopyCtor { + template + void operator()() { + ex::simd_mask<_Tp, SimdAbi> pure_mask; + std::array expected_value; + for (size_t i = 0; i < pure_mask.size(); i++) { + pure_mask[i] = static_cast(i % 2); + expected_value[i] = pure_mask[i]; + } + ex::simd_mask<_Tp, SimdAbi> from_copy_ctor(pure_mask); + assert_simd_mask_value_correct(from_copy_ctor, expected_value); + } +}; +struct CheckSimdMaskMoveCtor { + template + void operator()() { + ex::simd_mask<_Tp, SimdAbi> pure_mask; + std::array expected_value; + for (size_t i = 0; i < pure_mask.size(); i++) { + pure_mask[i] = static_cast(i % 2); + expected_value[i] = pure_mask[i]; + } + ex::simd_mask<_Tp, SimdAbi> from_move_ctor(std::move(pure_mask)); + assert_simd_mask_value_correct(from_move_ctor, expected_value); + } +}; +struct CheckSimdMaskCopyAssignment { + template + void operator()() { + ex::simd_mask<_Tp, SimdAbi> pure_mask; + std::array expected_value; + for (size_t i = 0; i < pure_mask.size(); i++) { + pure_mask[i] = static_cast(i % 2); + expected_value[i] = pure_mask[i]; + } + ex::simd_mask<_Tp, SimdAbi> from_copy_assignment; + from_copy_assignment = pure_mask; + assert_simd_mask_value_correct(from_copy_assignment, expected_value); + } +}; +struct CheckSimdMaskMoveAssignment { + template + void operator()() { + ex::simd_mask<_Tp, SimdAbi> pure_mask; + std::array expected_value; + for (size_t i = 0; i < pure_mask.size(); i++) { + pure_mask[i] = static_cast(i % 2); + expected_value[i] = pure_mask[i]; + } + ex::simd_mask<_Tp, SimdAbi> from_move_assignment; + from_move_assignment = std::move(pure_mask); + assert_simd_mask_value_correct(from_move_assignment, expected_value); + } +}; +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_subscr.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_subscr.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_subscr.pass.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// 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.mask.class] +// reference operator[](size_t i); +// value_type operator[](size_t i) const; + +#include "../test_utils.h" +#include +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdMaskReferenceSubscr { + template + void operator()() { + ex::simd_mask<_Tp, SimdAbi> origin_simd_mask(true); + for (size_t i = 0; i < origin_simd_mask.size(); ++i) { + static_assert(is_simd_reference::value); + assert(origin_simd_mask[i] == true); + } + } +}; +struct CheckSimdMaskValueTypeSubscr { + template + void operator()() { + const ex::simd_mask<_Tp, SimdAbi> origin_simd_mask(true); + for (size_t i = 0; i < origin_simd_mask.size(); ++i) { + static_assert(std::is_pod_v); + static_assert(std::is_same_v); + assert(origin_simd_mask[i] == true); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/simd.reference/reference_value_type.pass.cpp b/libcxx/test/std/experimental/simd/simd.reference/reference_value_type.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.reference/reference_value_type.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// 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.reference] +// operator value_type() const noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; +struct CheckSimdReferenceValueType { + template + void operator()() { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + for (size_t i = 0; i < origin_simd.size(); i++) { + static_assert(std::is_same_v); + assert(_Tp(origin_simd[i]) == static_cast<_Tp>(i)); + } + } +}; +struct CheckMaskReferenceValueType { + template + void operator()() { + ex::simd_mask<_Tp, SimdAbi> origin_mask(true); + for (size_t i = 0; i < origin_mask.size(); i++) { + static_assert(std::is_same_v); + assert(bool(origin_mask[i]) == true); + } + } +}; +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/test_utils.h b/libcxx/test/std/experimental/simd/test_utils.h --- a/libcxx/test/std/experimental/simd/test_utils.h +++ b/libcxx/test/std/experimental/simd/test_utils.h @@ -14,6 +14,12 @@ namespace ex = std::experimental::parallelism_v2; +template +struct is_simd_reference : std::false_type {}; + +template +struct is_simd_reference> : std::true_type {}; + template void test_simd_abi(); @@ -52,4 +58,19 @@ test_all_simd_abi(integer_seq_from_make_integer); test_all_simd_abi(integer_seq_from_make_integer); } + +template +void assert_simd_value_correct(const ex::simd<_Tp, SimdAbi>& origin_simd, + const std::array<_Tp, ArraySize>& expected_value) { + for (size_t i = 0; i < origin_simd.size(); ++i) + assert(origin_simd[i] == expected_value[i]); +} + +template +void assert_simd_mask_value_correct(const ex::simd_mask<_Tp, SimdAbi>& origin_mask, + const std::array& expected_value) { + for (size_t i = 0; i < origin_mask.size(); ++i) + assert(origin_mask[i] == expected_value[i]); +} + #endif // TEST_UTIL_H