diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -511,6 +511,11 @@ __ranges/view_interface.h __ranges/views.h __ranges/zip_view.h + __simd/vec_ext.h + __simd/config.h + __simd/scalar.h + __simd/simd_storage.h + __simd/utility.h __split_buffer __std_stream __string/char_traits.h diff --git a/libcxx/include/__simd/config.h b/libcxx/include/__simd/config.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__simd/config.h @@ -0,0 +1,236 @@ +// -*- 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___SIMD_CONFIG_H +#define _LIBCPP___SIMD_CONFIG_H + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD + +#ifdef __MMX__ +# define _LIBCPP_SIMD_HAVE_MMX 1 +#else +# define _LIBCPP_SIMD_HAVE_MMX 0 +#endif + +#ifdef __SSE__ +# define _LIBCPP_SIMD_HAVE_SSE 1 +#else +# define _LIBCPP_SIMD_HAVE_SSE 0 +#endif + +#ifdef __SSE2__ +# define _LIBCPP_SIMD_HAVE_SSE2 1 +#else +# define _LIBCPP_SIMD_HAVE_SSE2 0 +#endif + +#ifdef __SSE3__ +# define _LIBCPP_SIMD_HAVE_SSE3 1 +#else +# define _LIBCPP_SIMD_HAVE_SSE3 0 +#endif + +#ifdef __SSSE3__ +# define _LIBCPP_SIMD_HAVE_SSSE3 1 +#else +# define _LIBCPP_SIMD_HAVE_SSSE3 0 +#endif + +#ifdef __SSE4_1__ +# define _LIBCPP_SIMD_HAVE_SSE4_1 1 +#else +# define _LIBCPP_SIMD_HAVE_SSE4_1 0 +#endif + +#ifdef __SSE4_2__ +# define _LIBCPP_SIMD_HAVE_SSE4_2 1 +#else +# define _LIBCPP_SIMD_HAVE_SSE4_2 0 +#endif + +#ifdef __XOP__ +# define _LIBCPP_SIMD_HAVE_XOP 1 +#else +# define _LIBCPP_SIMD_HAVE_XOP 0 +#endif + +#ifdef __AVX__ +# define _LIBCPP_SIMD_HAVE_AVX 1 +#else +# define _LIBCPP_SIMD_HAVE_AVX 0 +#endif + +#ifdef __AVX2__ +# define _LIBCPP_SIMD_HAVE_AVX2 1 +#else +# define _LIBCPP_SIMD_HAVE_AVX2 0 +#endif + +#ifdef __BMI__ +# define _LIBCPP_SIMD_HAVE_BMI1 1 +#else +# define _LIBCPP_SIMD_HAVE_BMI1 0 +#endif + +#ifdef __BMI2__ +# define _LIBCPP_SIMD_HAVE_BMI2 1 +#else +# define _LIBCPP_SIMD_HAVE_BMI2 0 +#endif + +#ifdef __LZCNT__ +# define _LIBCPP_SIMD_HAVE_LZCNT 1 +#else +# define _LIBCPP_SIMD_HAVE_LZCNT 0 +#endif + +#ifdef __SSE4A__ +# define _LIBCPP_SIMD_HAVE_SSE4A 1 +#else +# define _LIBCPP_SIMD_HAVE_SSE4A 0 +#endif + +#ifdef __FMA__ +# define _LIBCPP_SIMD_HAVE_FMA 1 +#else +# define _LIBCPP_SIMD_HAVE_FMA 0 +#endif + +#ifdef __FMA4__ +# define _LIBCPP_SIMD_HAVE_FMA4 1 +#else +# define _LIBCPP_SIMD_HAVE_FMA4 0 +#endif + +#ifdef __F16C__ +# define _LIBCPP_SIMD_HAVE_F16C 1 +#else +# define _LIBCPP_SIMD_HAVE_F16C 0 +#endif + +#ifdef __POPCNT__ +# define _LIBCPP_SIMD_HAVE_POPCNT 1 +#else +# define _LIBCPP_SIMD_HAVE_POPCNT 0 +#endif + +#ifdef __AVX512F__ +# define _LIBCPP_SIMD_HAVE_AVX512F 1 +#else +# define _LIBCPP_SIMD_HAVE_AVX512F 0 +#endif + +#ifdef __AVX512DQ__ +# define _LIBCPP_SIMD_HAVE_AVX512DQ 1 +#else +# define _LIBCPP_SIMD_HAVE_AVX512DQ 0 +#endif + +#ifdef __AVX512VL__ +# define _LIBCPP_SIMD_HAVE_AVX512VL 1 +#else +# define _LIBCPP_SIMD_HAVE_AVX512VL 0 +#endif + +#ifdef __AVX512BW__ +# define _LIBCPP_SIMD_HAVE_AVX512BW 1 +#else +# define _LIBCPP_SIMD_HAVE_AVX512BW 0 +#endif + +#if _LIBCPP_SIMD_HAVE_SSE +# define _LIBCPP_SIMD_HAVE_SSE_ABI 1 +#else +# define _LIBCPP_SIMD_HAVE_SSE_ABI 0 +#endif +#if _LIBCPP_SIMD_HAVE_SSE2 +# define _LIBCPP_SIMD_HAVE_FULL_SSE_ABI 1 +#else +# define _LIBCPP_SIMD_HAVE_FULL_SSE_ABI 0 +#endif + +#if _LIBCPP_SIMD_HAVE_AVX +# define _LIBCPP_SIMD_HAVE_AVX_ABI 1 +#else +# define _LIBCPP_SIMD_HAVE_AVX_ABI 0 +#endif +#if _LIBCPP_SIMD_HAVE_AVX2 +# define _LIBCPP_SIMD_HAVE_FULL_AVX_ABI 1 +#else +# define _LIBCPP_SIMD_HAVE_FULL_AVX_ABI 0 +#endif + +#if _LIBCPP_SIMD_HAVE_AVX512F +# define _LIBCPP_SIMD_HAVE_AVX512_ABI 1 +#else +# define _LIBCPP_SIMD_HAVE_AVX512_ABI 0 +#endif +#if _LIBCPP_SIMD_HAVE_AVX512BW +# define _LIBCPP_SIMD_HAVE_FULL_AVX512_ABI 1 +#else +# define _LIBCPP_SIMD_HAVE_FULL_AVX512_ABI 0 +#endif + +#if _LIBCPP_SIMD_HAVE_SSE || _LIBCPP_SIMD_HAVE_MMX +# define _LIBCPP_SIMD_X86INTRIN 1 +#else +# define _LIBCPP_SIMD_X86INTRIN 0 +#endif + +#ifdef __ARM_NEON +# define _LIBCPP_SIMD_HAVE_NEON 1 +#else +# define _LIBCPP_SIMD_HAVE_NEON 0 +#endif + +#if defined __ARM_NEON && (__ARM_ARCH >= 8 || defined __aarch64__) +# define _LIBCPP_SIMD_HAVE_NEON_A32 1 +#else +# define _LIBCPP_SIMD_HAVE_NEON_A32 0 +#endif + +#if defined __ARM_NEON && defined __aarch64__ +# define _LIBCPP_SIMD_HAVE_NEON_A64 1 +#else +# define _LIBCPP_SIMD_HAVE_NEON_A64 0 +#endif + +#ifdef _ARCH_PWR10 +# define _LIBCPP_SIMD_HAVE_POWER10VEC 1 +#else +# define _LIBCPP_SIMD_HAVE_POWER10VEC 0 +#endif + +#ifdef __POWER9_VECTOR__ +# define _LIBCPP_SIMD_HAVE_POWER9VEC 1 +#else +# define _LIBCPP_SIMD_HAVE_POWER9VEC 0 +#endif + +#ifdef __POWER8_VECTOR__ +# define _LIBCPP_SIMD_HAVE_POWER8VEC 1 +#else +# define _LIBCPP_SIMD_HAVE_POWER8VEC _LIBCPP_SIMD_HAVE_POWER9VEC +#endif + +#ifdef __VSX__ +# define _LIBCPP_SIMD_HAVE_VSX 1 +#else +# define _LIBCPP_SIMD_HAVE_VSX _LIBCPP_SIMD_HAVE_POWER8VEC +#endif + +#ifdef __ALTIVEC__ +# define _LIBCPP_SIMD_HAVE_PPC 1 +#else +# define _LIBCPP_SIMD_HAVE_PPC _LIBCPP_SIMD_HAVE_VSX +#endif + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD + +#endif // _LIBCPP___SIMD_CONFIG_H diff --git a/libcxx/include/__simd/scalar.h b/libcxx/include/__simd/scalar.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__simd/scalar.h @@ -0,0 +1,164 @@ +// -*- 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___SIMD_SCALAR_H +#define _LIBCPP___SIMD_SCALAR_H + +#include <__simd/simd_storage.h> +#include <__simd/utility.h> +#include + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD + +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 + static void __store(_Simd __s, _Up* __mem) noexcept { + *__mem = static_cast<_Up>(__s.__data); + } + + static void __increment(_Simd& __s) noexcept { ++__s.__data; } + + static void __decrement(_Simd& __s) noexcept { --__s.__data; } + + static _Mask __negate(_Simd __s) noexcept { return {!__s.__data}; } + + static _Simd __bitwise_not(_Simd __s) noexcept { return {static_cast<_Tp>(~__s.__data)}; } + + static _Simd __unary_minus(_Simd __s) noexcept { return {static_cast<_Tp>(-__s.__data)}; } + + static _Simd __plus(_Simd __lhs, _Simd __rhs) noexcept { return {static_cast<_Tp>(__lhs.__data + __rhs.__data)}; } + + static _Simd __minus(_Simd __lhs, _Simd __rhs) noexcept { return {static_cast<_Tp>(__lhs.__data - __rhs.__data)}; } + + static _Simd __multiplies(_Simd __lhs, _Simd __rhs) noexcept { + return {static_cast<_Tp>(__lhs.__data * __rhs.__data)}; + } + + static _Simd __divides(_Simd __lhs, _Simd __rhs) noexcept { return {static_cast<_Tp>(__lhs.__data / __rhs.__data)}; } + + static _Simd __modulus(_Simd __lhs, _Simd __rhs) noexcept { return {static_cast<_Tp>(__lhs.__data % __rhs.__data)}; } + + static _Simd __bitwise_and(_Simd __lhs, _Simd __rhs) noexcept { + return {static_cast<_Tp>(__lhs.__data & __rhs.__data)}; + } + + static _Simd __bitwise_or(_Simd __lhs, _Simd __rhs) noexcept { + return {static_cast<_Tp>(__lhs.__data | __rhs.__data)}; + } + + static _Simd __bitwise_xor(_Simd __lhs, _Simd __rhs) noexcept { + return {static_cast<_Tp>(__lhs.__data ^ __rhs.__data)}; + } + + static _Simd __shift_left(_Simd __lhs, _Simd __rhs) noexcept { + return {static_cast<_Tp>(__lhs.__data << __rhs.__data)}; + } + + static _Simd __shift_right(_Simd __lhs, _Simd __rhs) noexcept { + return {static_cast<_Tp>(__lhs.__data >> __rhs.__data)}; + } + + static _Simd __shift_left(_Simd __lhs, int __rhs) noexcept { return {static_cast<_Tp>(__lhs.__data << __rhs)}; } + + static _Simd __shift_right(_Simd __lhs, int __rhs) noexcept { return {static_cast<_Tp>(__lhs.__data >> __rhs)}; } + + static _Mask __equal_to(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data == __rhs.__data}; } + + static _Mask __not_equal_to(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data != __rhs.__data}; } + + static _Mask __less_equal(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data <= __rhs.__data}; } + + static _Mask __less(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data < __rhs.__data}; } + + static _Tp __hmin(_Simd __s) { return __s.__data; } + + static _Tp __hmax(_Simd __s) { return __s.__data; } + + static _Simd __min(_Simd __a, _Simd __b) noexcept { return {std::min(__a.__data, __b.__data)}; } + + static _Simd __max(_Simd __a, _Simd __b) noexcept { return {std::max(__a.__data, __b.__data)}; } + + static std::pair<_Simd, _Simd> __minmax(_Simd __a, _Simd __b) noexcept { return {__min(__a, __b), __max(__a, __b)}; } + + static _Simd __clamp(_Simd __v, _Simd __lo, _Simd __hi) noexcept { return __min(__max(__v, __lo), __hi); } + + static _Simd __masked_assign(_Simd& __s, _Mask __m, _Simd __v) noexcept { + __s.__data = __m.__data ? __v.__data : __s.__data; + return __s; + } + + template + static _Tp __reduce(const _Simd& __s, _BinaryOp) { return __s.__data; } +}; + +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]; + } + + static void __store(_Mask __s, bool* __mem) noexcept { + __mem[0] = __s.__data; + } + + static _Mask __negate(_Mask __s) noexcept { return {{!__s.__data}}; } + + static _Mask __logical_and(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data && __rhs.__data}}; } + + static _Mask __logical_or(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data || __rhs.__data}}; } + + static _Mask __bitwise_and(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data && __rhs.__data}}; } + + static _Mask __bitwise_or(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data || __rhs.__data}}; } + + static _Mask __bitwise_xor(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data != __rhs.__data}}; } + + static bool __all_of(_Mask __s) noexcept { return __s.__data; } + + static bool __any_of(_Mask __s) noexcept { return __s.__data; } + + static bool __none_of(_Mask __s) noexcept { return !__s.__data; } + + static bool __some_of(_Mask) noexcept { return false; } + + static int __popcount(_Mask __s) noexcept { return __s.__data; } + + static int __find_first_set(_Mask) { return 0; } + + static int __find_last_set(_Mask) { return 0; } + + static _Mask __masked_assign(_Mask& __s, _Mask __m, _Mask __v) noexcept { + __s.__data = __m.__data ? __v.__data : __s.__data; + return __s; + } +}; + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD + +#endif // _LIBCPP___SIMD_SCALAR_H diff --git a/libcxx/include/__simd/simd_storage.h b/libcxx/include/__simd/simd_storage.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__simd/simd_storage.h @@ -0,0 +1,106 @@ +// -*- 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___SIMD_SIMD_STORAGE_H +#define _LIBCPP___SIMD_SIMD_STORAGE_H + +#include +#include +#include + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD_ABI + +struct __scalar { + static constexpr bool __is_abi_tag = true; + static constexpr size_t __simd_size = 1; +}; + +template +struct __vec_ext { + static constexpr bool __is_abi_tag = _Np > 0 && _Np <= 32; + static constexpr size_t __simd_size = _Np; +}; + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD_ABI + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD + +// TODO: replace it by std::bit_ceil when bump to C++20 +constexpr size_t __next_pow_of_2(size_t __val) { + size_t __pow = 1; + while (__pow < __val) + __pow <<= 1; + return __pow; +} + +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; +} + +template +struct __simd_storage; + +template +struct __mask_storage; + +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_storage<_Tp, simd_abi::__vec_ext<_Np>> { +#if defined(_LIBCPP_COMPILER_CLANG_BASED) + _Tp __data __attribute__((vector_size(sizeof(_Tp) * _Np))); +#else + _Tp __data __attribute__((vector_size(__next_pow_of_2(sizeof(_Tp) * _Np)))); +#endif + + _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>> {}; + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD + +#endif // _LIBCPP___SIMD_SIMD_STORAGE_H diff --git a/libcxx/include/__simd/utility.h b/libcxx/include/__simd/utility.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__simd/utility.h @@ -0,0 +1,83 @@ +// -*- 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___SIMD_UTILITY_H +#define _LIBCPP___SIMD_UTILITY_H + +#include + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD + +template +struct __simd_traits; + +template +struct __mask_traits; + +class _Bool { + const bool __v; + +public: + constexpr _Bool(bool __b) : __v(__b) {} + _Bool(int) = delete; + constexpr operator bool() const { return __v; } +}; + +template +constexpr decltype(_To{std::declval<_From>()}, true) __is_non_narrowing_convertible_impl(_From) { + return true; +} + +template +constexpr bool __is_non_narrowing_convertible_impl(...) { + return false; +} + +template +constexpr bool __is_non_narrowing_arithmetic_convertible() { + if constexpr (std::is_arithmetic_v<_To> && std::is_arithmetic_v<_From>) + return __is_non_narrowing_convertible_impl<_To>(_From{}); + else + return false; +} + +template +constexpr _Tp __variadic_sum(_Args... __args) { + return (static_cast<_Tp>(__args) + ...); +} + +template +constexpr bool __is_vectorizable() { + return std::is_arithmetic_v<_Tp> && !std::is_const_v<_Tp> && !std::is_volatile_v<_Tp> && !std::is_same_v<_Tp, bool>; +} + +template +constexpr bool __can_broadcast() { + return (std::is_arithmetic_v<_Up> && __is_non_narrowing_arithmetic_convertible<_Up, _Tp>()) || + (!std::is_arithmetic_v<_Up> && std::is_convertible_v<_Up, _Tp>) || + std::is_same_v, int> || + (std::is_same_v, unsigned int> && std::is_unsigned_v<_Tp>); +} + +template +constexpr decltype(std::forward_as_tuple(std::declval<_Generator>()(std::integral_constant())...), + bool()) +__can_generate(std::index_sequence<__indicies...>) { + return !__variadic_sum( + !__can_broadcast<_Tp, decltype(std::declval<_Generator>()(std::integral_constant()))>()...); +} + +template +bool __can_generate(...) { + return false; +} + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD + +#endif // _LIBCPP___SIMD_UTILITY_H diff --git a/libcxx/include/__simd/vec_ext.h b/libcxx/include/__simd/vec_ext.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__simd/vec_ext.h @@ -0,0 +1,238 @@ +// -*- 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___SIMD_vec_ext_H +#define _LIBCPP___SIMD_vec_ext_H + +#include <__simd/simd_storage.h> +#include <__simd/utility.h> +#include + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD + +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 { + // TODO: Optimizeable + return __generate([=](size_t) { return __v; }); + } + + template + static _Simd __generate_init(_Generator&& __g, std::index_sequence<_Is...>) { + // TODO: Optimizeable + // _Simd specified here is to work around GCC + return _Simd{{__g(std::integral_constant())...}}; + } + + template + static _Simd __generate(_Generator&& __g) noexcept { + // TODO: Optimizeable + return __generate_init(std::forward<_Generator>(__g), std::make_index_sequence<_Np>()); + } + + template + static void __load(_Simd& __s, const _Up* __mem) noexcept { + // TODO: Optimize with intrinsics + for (size_t __i = 0; __i < _Np; __i++) + __s.__data[__i] = static_cast<_Tp>(__mem[__i]); + } + + template + static void __store(_Simd __s, _Up* __mem) noexcept { + // TODO: Optimize with intrinsics + for (size_t __i = 0; __i < _Np; __i++) + __mem[__i] = static_cast<_Up>(__s.__data[__i]); + } + + static void __increment(_Simd& __s) noexcept { __s.__data = __s.__data + 1; } + + static void __decrement(_Simd& __s) noexcept { __s.__data = __s.__data - 1; } + + static _Mask __negate(_Simd __s) noexcept { return {!__s.__data}; } + + static _Simd __bitwise_not(_Simd __s) noexcept { return {~__s.__data}; } + + static _Simd __unary_minus(_Simd __s) noexcept { return {-__s.__data}; } + + static _Simd __plus(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data + __rhs.__data}; } + + static _Simd __minus(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data - __rhs.__data}; } + + static _Simd __multiplies(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data * __rhs.__data}; } + + static _Simd __divides(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data / __rhs.__data}; } + + static _Simd __modulus(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data % __rhs.__data}; } + + static _Simd __bitwise_and(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data & __rhs.__data}; } + + static _Simd __bitwise_or(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data | __rhs.__data}; } + + static _Simd __bitwise_xor(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data ^ __rhs.__data}; } + + static _Simd __shift_left(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data << __rhs.__data}; } + + static _Simd __shift_right(_Simd __lhs, _Simd __rhs) noexcept { return {__lhs.__data >> __rhs.__data}; } + + static _Simd __shift_left(_Simd __lhs, int __rhs) noexcept { return {__lhs.__data << __rhs}; } + + static _Simd __shift_right(_Simd __lhs, int __rhs) noexcept { return {__lhs.__data >> __rhs}; } + + static _Mask __equal_to(_Simd __lhs, _Simd __rhs) noexcept { return {{__lhs.__data == __rhs.__data}}; } + + static _Mask __not_equal_to(_Simd __lhs, _Simd __rhs) noexcept { return {{__lhs.__data != __rhs.__data}}; } + + static _Mask __less_equal(_Simd __lhs, _Simd __rhs) noexcept { return {{__lhs.__data <= __rhs.__data}}; } + + static _Mask __less(_Simd __lhs, _Simd __rhs) noexcept { return {{__lhs.__data < __rhs.__data}}; } + + static _Tp __hmin(_Simd __s) noexcept { return __builtin_reduce_min(__s.__data); } + + static _Tp __hmax(_Simd __s) noexcept { return __builtin_reduce_max(__s.__data); } + + static _Simd __min(_Simd __a, _Simd __b) noexcept { return {__a.__data < __b.__data ? __a.__data : __b.__data}; } + + static _Simd __max(_Simd __a, _Simd __b) noexcept { return {__a.__data > __b.__data ? __a.__data : __b.__data}; } + + static std::pair<_Simd, _Simd> __minmax(_Simd __a, _Simd __b) noexcept { return {__min(__a, __b), __max(__a, __b)}; } + + static _Simd __clamp(_Simd __v, _Simd __lo, _Simd __hi) noexcept { return __min(__max(__v, __lo), __hi); } + + static _Simd __masked_assign(_Simd& __s, _Mask __m, _Simd __v) noexcept { + __s.__data = __m.__data ? __v.__data : __s.__data; + return __s; + } + + static _Tp __reduce(const _Simd& __s, plus<>) { return __builtin_reduce_add(__s.__data); } + + static _Tp __reduce(const _Simd& __s, multiplies<>) { return __builtin_reduce_mul(__s.__data); } + + static _Tp __reduce(const _Simd& __s, bit_and<>) { return __builtin_reduce_and(__s.__data); } + + static _Tp __reduce(const _Simd& __s, bit_or<>) { return __builtin_reduce_or(__s.__data); } + + static _Tp __reduce(const _Simd& __s, bit_xor<>) { return __builtin_reduce_xor(__s.__data); } +}; + +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 { + // TODO: Optimizeable + return __generate([=](size_t) { return __set_all_bits<_Tp>(__v); }); + } + + template + static _Mask __generate_init(_Generator&& __g, std::index_sequence<_Is...>) { + // TODO: Optimizeable + // _Simd specified here is to work around GCC + return _Mask{{__g(std::integral_constant())...}}; + } + + template + static _Mask __generate(_Generator&& __g) noexcept { + // TODO: Optimizeable + return __generate_init(std::forward<_Generator>(__g), std::make_index_sequence<_Np>()); + } + + static void __load(_Mask& __s, const bool* __mem) noexcept { + // TODO: Optimize with intrinsics + for (size_t __i = 0; __i < _Np; __i++) + __s.__data[__i] = __set_all_bits<_Tp>(__mem[__i]); + } + + static void __store(_Mask __s, bool* __mem) noexcept { + // TODO: Optimize with intrinsics + for (size_t __i = 0; __i < _Np; __i++) + __mem[__i] = static_cast(__s.__data[__i]); + } + + static _Mask __negate(_Mask __s) noexcept { return {{~__s.__data}}; } + + static _Mask __logical_and(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data & __rhs.__data}}; } + + static _Mask __logical_or(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data | __rhs.__data}}; } + + static _Mask __bitwise_and(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data & __rhs.__data}}; } + + static _Mask __bitwise_or(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data | __rhs.__data}}; } + + static _Mask __bitwise_xor(_Mask __lhs, _Mask __rhs) noexcept { return {{__lhs.__data ^ __rhs.__data}}; } + + static bool __all_of(_Mask __s) noexcept { + // TODO: Optimizeable + for (size_t __i = 0; __i < _Np; ++__i) + if (!__s.__data[__i]) + return false; + return true; + } + + static bool __any_of(_Mask __s) noexcept { + // TODO: Optimizeable + for (size_t __i = 0; __i < _Np; ++__i) + if (__s.__data[__i]) + return true; + return false; + } + + static bool __none_of(_Mask __s) noexcept { + // TODO: Optimizeable + for (size_t __i = 0; __i < _Np; ++__i) + if (__s.__data[__i]) + return false; + return true; + } + + static bool __some_of(_Mask __s) noexcept { + // TODO: Optimizeable + for (size_t __i = 1; __i < _Np; ++__i) + if (__s.__data[__i] != __s.__data[__i - 1]) + return true; + return false; + } + + static int __popcount(_Mask __s) noexcept { + // TODO: Optimizeable + int __count = 0; + for (size_t __i = 0; __i < _Np; ++__i) + __count += __s.__data[__i] != 0; + return __count; + } + + static int __find_first_set(_Mask __s) { + // TODO: Optimizeable + size_t __i = 0; + for (; __i < _Np; ++__i) + if (__s.__data[__i]) + break; + return __i; + } + + static int __find_last_set(_Mask __s) { + // TODO: Optimizeable + size_t __i = 1; + for (; __i < _Np; ++__i) + if (__s.__data[_Np - __i]) + break; + return _Np - __i; + } + + static _Mask __masked_assign(_Mask& __s, _Mask __m, _Mask __v) noexcept { + __s.__data = __m.__data ? __v.__data : __s.__data; + return __s; + } +}; + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD + +#endif // _LIBCPP___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 @@ -10,6 +10,8 @@ #ifndef _LIBCPP_EXPERIMENTAL_SIMD #define _LIBCPP_EXPERIMENTAL_SIMD +#define __cpp_lib_experimental_parallel_simd 201803 + /* experimental/simd synopsis @@ -25,6 +27,10 @@ template using compatible = implementation-defined; template using native = implementation-defined; +template struct deduce { using type = see below; }; +template using deduce_t = + typename deduce::type; + } // simd_abi struct element_aligned_tag {}; @@ -34,7 +40,7 @@ inline constexpr vector_aligned_tag vector_aligned{}; template inline constexpr overaligned_tag overaligned{}; -// traits [simd.traits] +// 9.4, simd type traits template struct is_abi_tag; template inline constexpr bool is_abi_tag_v = is_abi_tag::value; @@ -45,60 +51,87 @@ template inline constexpr bool is_simd_mask_v = is_simd_mask::value; template struct is_simd_flag_type; -template inline constexpr bool is_simd_flag_type_v = is_simd_flag_type::value; - -template struct abi_for_size { using type = see below; }; -template using abi_for_size_t = typename abi_for_size::type; +template inline constexpr bool is_simd_flag_type_v = + is_simd_flag_type::value; template > struct simd_size; template > -inline constexpr size_t simd_size_v = simd_size::value; + inline constexpr size_t simd_size_v = simd_size::value; template struct memory_alignment; template -inline constexpr size_t memory_alignment_v = memory_alignment::value; + inline constexpr size_t memory_alignment_v = memory_alignment::value; + +template struct rebind_simd { using type = see below; }; +template using rebind_simd_t = typename rebind_simd::type; +template struct resize_simd { using type = see below; }; +template using resize_simd_t = typename resize_simd::type; -// class template simd [simd.class] +// 9.6, Class template simd template > class simd; template using native_simd = simd>; template using fixed_size_simd = simd>; -// class template simd_mask [simd.mask.class] +// 9.8, Class template simd_mask template > class simd_mask; template using native_simd_mask = simd_mask>; -template using fixed_size_simd_mask = simd_mask>; +template using fixed_size_simd_mask = + simd_mask>; -// casts [simd.casts] -template see below simd_cast(const simd&); -template see below static_simd_cast(const simd&); +// 9.7.5, Casts +template see below simd_cast(const simd&) noexcept; +template see below static_simd_cast(const simd&) noexcept; template -fixed_size_simd> to_fixed_size(const simd&) noexcept; + fixed_size_simd> + to_fixed_size(const simd&) noexcept; template -fixed_size_simd_mask> to_fixed_size(const simd_mask&) noexcept; -template native_simd to_native(const fixed_size_simd&) noexcept; + fixed_size_simd_mask> + to_fixed_size(const simd_mask&) noexcept; template -native_simd_mask to_native(const fixed_size_simd_mask> &) noexcept; -template simd to_compatible(const fixed_size_simd&) noexcept; -template simd_mask to_compatible(const fixed_size_simd_mask&) noexcept; + native_simd to_native(const fixed_size_simd&) noexcept; +template + native_simd_mask to_native(const fixed_size_simd_mask> &) noexcept; +template + simd to_compatible(const fixed_size_simd&) noexcept; +template + simd_mask to_compatible(const fixed_size_simd_mask&) noexcept; template -tuple>...> split(const simd&); + tuple>...> + split(const simd&); template -tuple>...> split(const simd_mask&); + tuple>...> + split(const simd_mask&); template -array / V::size()> split( -const simd&); + array / V::size()> + split(const simd&) noexcept; template -array / V::size()> split( -const simd_mask&); - -template -simd + ...)>> concat(const simd&...); -template -simd_mask + ...)>> concat(const simd_mask&...); - -// reductions [simd.mask.reductions] + array / V::size()> + split(const simd_mask&) noexcept; + +template + array / N, simd>, N> + split_by(const simd& x) noexcept; +template + array / N, simd_mask>, N> + split_by(const simd_mask& x) noexcept; + +template + simd + ...)>> + concat(const simd&...) noexcept; +template + simd_mask + ...)>> + concat(const simd_mask&...) noexcept; + +template + resize_simd * N, simd> + concat(const array, N>& arr) noexcept; +template + resize_simd * N, simd_mask> + concat(const array, N>& arr) noexcept; + +// 9.9.4, reductions template bool all_of(const simd_mask&) noexcept; template bool any_of(const simd_mask&) noexcept; template bool none_of(const simd_mask&) noexcept; @@ -107,200 +140,204 @@ template int find_first_set(const simd_mask&); template int find_last_set(const simd_mask&); -bool all_of(see below) noexcept; -bool any_of(see below) noexcept; -bool none_of(see below) noexcept; -bool some_of(see below) noexcept; -int popcount(see below) noexcept; -int find_first_set(see below) noexcept; -int find_last_set(see below) noexcept; +bool all_of(T) noexcept; +bool any_of(T) noexcept; +bool none_of(T) noexcept; +bool some_of(T) noexcept; +int popcount(T) noexcept; +int find_first_set(T); +int find_last_set(T); -// masked assignment [simd.whereexpr] +// 9.5, Where expression class templates template class const_where_expression; template class where_expression; -// masked assignment [simd.mask.where] -template struct nodeduce { using type = T; }; // exposition only - -template using nodeduce_t = typename nodeduce::type; // exposition only - +// 9.9.5, Where functions template -where_expression, simd> -where(const typename simd::mask_type&, simd&) noexcept; + where_expression, simd> + where(const typename simd::mask_type&, simd&) noexcept; template -const_where_expression, const simd> -where(const typename simd::mask_type&, const simd&) noexcept; + const_where_expression, const simd> + where(const typename simd::mask_type&, const simd&) noexcept; template -where_expression, simd_mask> -where(const nodeduce_t>&, simd_mask&) noexcept; + where_expression, simd_mask> + where(const type_identity_t>&, simd_mask&) noexcept; template -const_where_expression, const simd_mask> -where(const nodeduce_t>&, const simd_mask&) noexcept; - -template where_expression where(see below k, T& d) noexcept; + const_where_expression, const simd_mask> + where(const type_identity_t>&, const simd_mask&) noexcept; template -const_where_expression where(see below k, const T& d) noexcept; + where_expression + where(see below k, T& d) noexcept; -// reductions [simd.reductions] -template > -T reduce(const simd&, BinaryOperation = BinaryOperation()); +template + const_where_expression + where(see below k, const T& d) noexcept; +// 9.7.4, Reductions +template > + T reduce(const simd&, + BinaryOperation = {}); template -typename V::value_type reduce(const const_where_expression& x, -typename V::value_type neutral_element, BinaryOperation binary_op); - + typename V::value_type reduce(const const_where_expression& x, + typename V::value_type neutral_element, + BinaryOperation binary_op); template -typename V::value_type reduce(const const_where_expression& x, plus<> binary_op = plus<>()); - + typename V::value_type reduce(const const_where_expression& x, + plus<> binary_op = {}) noexcept; template -typename V::value_type reduce(const const_where_expression& x, multiplies<> binary_op); - + typename V::value_type reduce(const const_where_expression& x, + multiplies<> binary_op) noexcept; template -typename V::value_type reduce(const const_where_expression& x, bit_and<> binary_op); - + typename V::value_type reduce(const const_where_expression& x, + bit_and<> binary_op) noexcept; template -typename V::value_type reduce(const const_where_expression& x, bit_or<> binary_op); - + typename V::value_type reduce(const const_where_expression& x, + bit_or<> binary_op) noexcept; template -typename V::value_type reduce(const const_where_expression& x, bit_xor<> binary_op); - -template T hmin(const simd&); -template T hmin(const const_where_expression&); -template T hmax(const simd&); -template T hmax(const const_where_expression&); - -// algorithms [simd.alg] -template simd min(const simd&, const simd&) noexcept; - -template simd max(const simd&, const simd&) noexcept; + typename V::value_type reduce(const const_where_expression& x, + bit_xor<> binary_op) noexcept; template -std::pair, simd> minmax(const simd&, const simd&) noexcept; - + T hmin(const simd&) noexcept; +template + typename V::value_type hmin(const const_where_expression&) noexcept; template -simd clamp(const simd& v, const simd& lo, const simd& hi); + T hmax(const simd&) noexcept; +template + typename V::value_type hmax(const const_where_expression&) noexcept; -// [simd.whereexpr] -template -class const_where_expression { - const M& mask; // exposition only - T& data; // exposition only +// 9.7.6, Algorithms +template + simd + min(const simd& a, const simd& b) noexcept; +template + simd + max(const simd& a, const simd& b) noexcept; +template + pair, simd> + minmax(const simd& a, const simd& b) noexcept; +template + simd + clamp(const simd& v, + const simd& lo, + const simd& hi); + +// 9.5, Where expression class templates [simd.whereexpr] +template class const_where_expression { + const M mask; // exposition only + T& data; // exposition only public: const_where_expression(const const_where_expression&) = delete; const_where_expression& operator=(const const_where_expression&) = delete; - remove_const_t operator-() const &&; + + T operator-() const && noexcept; + T operator+() const && noexcept; + T operator~() const && noexcept; + template void copy_to(U* mem, Flags f) const &&; }; template class where_expression : public const_where_expression { public: - where_expression(const where_expression&) = delete; - where_expression& operator=(const where_expression&) = delete; - template void operator=(U&& x); - template void operator+=(U&& x); - template void operator-=(U&& x); - template void operator*=(U&& x); - template void operator/=(U&& x); - template void operator%=(U&& x); - template void operator&=(U&& x); - template void operator|=(U&& x); - template void operator^=(U&& x); - template void operator<<=(U&& x); - template void operator>>=(U&& x); - void operator++(); - void operator++(int); - void operator--(); - void operator--(int); - template void copy_from(const U* mem, Flags); + template void operator=(U&& x) noexcept; + template void operator+=(U&& x) noexcept; + template void operator-=(U&& x) noexcept; + template void operator*=(U&& x) noexcept; + template void operator/=(U&& x) noexcept; + template void operator%=(U&& x) noexcept; + template void operator&=(U&& x) noexcept; + template void operator|=(U&& x) noexcept; + template void operator^=(U&& x) noexcept; + template void operator<<=(U&& x) noexcept; + template void operator>>=(U&& x) noexcept; + + void operator++() && noexcept; + void operator++(int) && noexcept; + void operator--() && noexcept; + void operator--(int) && noexcept; + + template void copy_from(const U* mem, Flags) &&; }; -// [simd.class] +// 9.6.1, Class template simd overview [simd.class.overview] template class simd { public: using value_type = T; using reference = see below; using mask_type = simd_mask; - using abi_type = Abi; - static constexpr size_t size() noexcept; - simd() = default; - - // implicit type conversion constructor - template simd(const simd>&); - // implicit broadcast constructor (see below for constraints) - template simd(U&& value); + static constexpr size_t size() noexcept; - // generator constructor (see below for constraints) - template explicit simd(G&& gen); + simd() noexcept = default; - // load constructor + // 9.6.4, simd constructors + template simd(U&& value) noexcept; + template simd(const simd>&) noexcept; + template explicit simd(G&& gen) noexcept; template simd(const U* mem, Flags f); - // loads [simd.load] + // 9.6.5, simd copy functions template void copy_from(const U* mem, Flags f); + template void copy_to(U* mem, Flags f); - // stores [simd.store] - template void copy_to(U* mem, Flags f) const; - - // scalar access [simd.subscr] + // 9.6.6, simd subscript operators reference operator[](size_t); value_type operator[](size_t) const; - // unary operators [simd.unary] - simd& operator++(); - simd operator++(int); - simd& operator--(); - simd operator--(int); - mask_type operator!() const; - simd operator~() const; // see below - simd operator+() const; - simd operator-() const; - - // binary operators [simd.binary] - friend simd operator+ (const simd&, const simd&); - friend simd operator- (const simd&, const simd&); - friend simd operator* (const simd&, const simd&); - friend simd operator/ (const simd&, const simd&); - friend simd operator% (const simd&, const simd&); - friend simd operator& (const simd&, const simd&); - friend simd operator| (const simd&, const simd&); - friend simd operator^ (const simd&, const simd&); - friend simd operator<<(const simd&, const simd&); - friend simd operator>>(const simd&, const simd&); - friend simd operator<<(const simd&, int); - friend simd operator>>(const simd&, int); - - // compound assignment [simd.cassign] - friend simd& operator+= (simd&, const simd&); - friend simd& operator-= (simd&, const simd&); - friend simd& operator*= (simd&, const simd&); - friend simd& operator/= (simd&, const simd&); - friend simd& operator%= (simd&, const simd&); - - friend simd& operator&= (simd&, const simd&); - friend simd& operator|= (simd&, const simd&); - friend simd& operator^= (simd&, const simd&); - friend simd& operator<<=(simd&, const simd&); - friend simd& operator>>=(simd&, const simd&); - friend simd& operator<<=(simd&, int); - friend simd& operator>>=(simd&, int); - - // compares [simd.comparison] - friend mask_type operator==(const simd&, const simd&); - friend mask_type operator!=(const simd&, const simd&); - friend mask_type operator>=(const simd&, const simd&); - friend mask_type operator<=(const simd&, const simd&); - friend mask_type operator> (const simd&, const simd&); - friend mask_type operator< (const simd&, const simd&); + // 9.6.7, simd unary operators + 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; + + // 9.7.1, simd binary operators + friend simd operator+ (const simd&, const simd&) noexcept; + friend simd operator- (const simd&, const simd&) noexcept; + friend simd operator* (const simd&, const simd&) noexcept; + friend simd operator/ (const simd&, const simd&) noexcept; + friend simd operator% (const simd&, const simd&) noexcept; + friend simd operator& (const simd&, const simd&) noexcept; + friend simd operator| (const simd&, const simd&) noexcept; + friend simd operator^ (const simd&, const simd&) noexcept; + friend simd operator<<(const simd&, const simd&) noexcept; + friend simd operator>>(const simd&, const simd&) noexcept; + friend simd operator<<(const simd&, int) noexcept; + friend simd operator>>(const simd&, int) noexcept; + + // 9.7.2, simd compound assignment + friend simd& operator+= (simd&, const simd&) noexcept; + friend simd& operator-= (simd&, const simd&) noexcept; + friend simd& operator*= (simd&, const simd&) noexcept; + friend simd& operator/= (simd&, const simd&) noexcept; + friend simd& operator%= (simd&, const simd&) noexcept; + friend simd& operator&= (simd&, const simd&) noexcept; + friend simd& operator|= (simd&, const simd&) noexcept; + friend simd& operator^= (simd&, const simd&) noexcept; + friend simd& operator<<=(simd&, const simd&) noexcept; + friend simd& operator>>=(simd&, const simd&) noexcept; + friend simd& operator<<=(simd&, int) noexcept; + friend simd& operator>>=(simd&, int) noexcept; + + // 9.7.3, simd compare operators + friend mask_type operator==(const simd&, const simd&) noexcept; + friend mask_type operator!=(const simd&, const simd&) noexcept; + friend mask_type operator>=(const simd&, const simd&) noexcept; + friend mask_type operator<=(const simd&, const simd&) noexcept; + friend mask_type operator> (const simd&, const simd&) noexcept; + friend mask_type operator< (const simd&, const simd&) noexcept; }; -// [simd.math] +// 9.7.7, Math library [simd.math] template using scharv = simd; // exposition only template using shortv = simd; // exposition only template using intv = simd; // exposition only @@ -597,50 +634,48 @@ template simd_div_t> div(longv numer, longv denom); template simd_div_t> div(llongv numer, llongv denom); -// [simd.mask.class] -template -class simd_mask { +// 9.8.1, Class template simd_mask overview [simd.mask.overview] +template class simd_mask { public: using value_type = bool; using reference = see below; using simd_type = simd; using abi_type = Abi; - static constexpr size_t size() noexcept; - simd_mask() = default; - // broadcast constructor - explicit simd_mask(value_type) noexcept; + static constexpr size_t size() noexcept; - // implicit type conversion constructor - template simd_mask(const simd_mask>&) noexcept; + simd_mask() noexcept = default; - // load constructor + // 9.8.3, Constructors + explicit simd_mask(value_type) noexcept; + template + simd_mask(const simd_mask>&) noexcept; template simd_mask(const value_type* mem, Flags); - // loads [simd.mask.copy] + // 9.8.4, Copy functions template void copy_from(const value_type* mem, Flags); template void copy_to(value_type* mem, Flags) const; - // scalar access [simd.mask.subscr] + // 9.8.5, Subscript operators reference operator[](size_t); value_type operator[](size_t) const; - // unary operators [simd.mask.unary] + // 9.8.6, Unary operators simd_mask operator!() const noexcept; - // simd_mask binary operators [simd.mask.binary] + // 9.9.1, Binary operators friend simd_mask operator&&(const simd_mask&, const simd_mask&) noexcept; friend simd_mask operator||(const simd_mask&, const simd_mask&) noexcept; friend simd_mask operator& (const simd_mask&, const simd_mask&) noexcept; friend simd_mask operator| (const simd_mask&, const simd_mask&) noexcept; friend simd_mask operator^ (const simd_mask&, const simd_mask&) noexcept; - // simd_mask compound assignment [simd.mask.cassign] + // 9.9.2, Compound assignment friend simd_mask& operator&=(simd_mask&, const simd_mask&) noexcept; friend simd_mask& operator|=(simd_mask&, const simd_mask&) noexcept; friend simd_mask& operator^=(simd_mask&, const simd_mask&) noexcept; - // simd_mask compares [simd.mask.comparison] + // 9.9.3, Comparisons friend simd_mask operator==(const simd_mask&, const simd_mask&) noexcept; friend simd_mask operator!=(const simd_mask&, const simd_mask&) noexcept; }; @@ -650,12 +685,13 @@ */ -#include <__assert> // all public C++ headers provide the assertion handler -#include <__functional/operations.h> -#include -#include +#include <__simd/vec_ext.h> +#include <__simd/config.h> +#include <__simd/scalar.h> #include -#include +#include +#include +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -668,907 +704,1357 @@ #if _LIBCPP_STD_VER >= 17 -enum class _StorageKind { - _Scalar, - _Array, - _VecExt, -}; - -template <_StorageKind __kind, int _Np> -struct __simd_abi {}; - -template -class __simd_storage {}; - -template -class __simd_storage<_Tp, __simd_abi<_StorageKind::_Array, __num_element>> { - std::array<_Tp, __num_element> __storage_; +// TODO: handle _LIBCPP_HAS_NO_VECTOR_EXTENSION +template +class __simd_reference { template - friend struct simd; + friend class simd; template - friend struct simd_mask; - -public: - _Tp __get(size_t __index) const noexcept { return __storage_[__index]; } - void __set(size_t __index, _Tp __val) noexcept { - __storage_[__index] = __val; - } -}; + friend class simd_mask; -template -class __simd_storage<_Tp, __simd_abi<_StorageKind::_Scalar, 1>> { - _Tp __storage_; + _Storage& __s_; + size_t __idx_; - template - friend struct simd; + __simd_reference(_Storage& __s, size_t __idx) : __s_(__s), __idx_(__idx) {} - template - friend struct simd_mask; + _ValueType __get() const { return __s_.__get(__idx_); } -public: - _Tp __get(size_t __index) const noexcept { return (&__storage_)[__index]; } - void __set(size_t __index, _Tp __val) noexcept { - (&__storage_)[__index] = __val; + void __set(_ValueType __v) { + if constexpr (is_same_v<_ValueType, bool>) + __s_.__set(__idx_, __set_all_bits<_Tp>(__v)); + else + __s_.__set(__idx_, __v); } -}; - -#ifndef _LIBCPP_HAS_NO_VECTOR_EXTENSION - -_LIBCPP_HIDE_FROM_ABI constexpr size_t __floor_pow_of_2(size_t __val) { - return ((__val - 1) & __val) == 0 ? __val - : __floor_pow_of_2((__val - 1) & __val); -} - -_LIBCPP_HIDE_FROM_ABI constexpr size_t __ceil_pow_of_2(size_t __val) { - return __val == 1 ? 1 : __floor_pow_of_2(__val - 1) << 1; -} - -template -struct __vec_ext_traits { -#if !defined(_LIBCPP_COMPILER_CLANG_BASED) - typedef _Tp type __attribute__((vector_size(__ceil_pow_of_2(__bytes)))); -#endif -}; - -#if defined(_LIBCPP_COMPILER_CLANG_BASED) -#define _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, _NUM_ELEMENT) \ - template <> \ - struct __vec_ext_traits<_TYPE, sizeof(_TYPE) * _NUM_ELEMENT> { \ - using type = \ - _TYPE __attribute__((vector_size(sizeof(_TYPE) * _NUM_ELEMENT))); \ - } - -#define _LIBCPP_SPECIALIZE_VEC_EXT_32(_TYPE) \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 1); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 2); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 3); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 4); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 5); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 6); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 7); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 8); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 9); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 10); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 11); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 12); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 13); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 14); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 15); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 16); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 17); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 18); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 19); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 20); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 21); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 22); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 23); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 24); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 25); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 26); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 27); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 28); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 29); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 30); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 31); \ - _LIBCPP_SPECIALIZE_VEC_EXT(_TYPE, 32) - -_LIBCPP_SPECIALIZE_VEC_EXT_32(char); -_LIBCPP_SPECIALIZE_VEC_EXT_32(char16_t); -_LIBCPP_SPECIALIZE_VEC_EXT_32(char32_t); -_LIBCPP_SPECIALIZE_VEC_EXT_32(wchar_t); -_LIBCPP_SPECIALIZE_VEC_EXT_32(signed char); -_LIBCPP_SPECIALIZE_VEC_EXT_32(signed short); -_LIBCPP_SPECIALIZE_VEC_EXT_32(signed int); -_LIBCPP_SPECIALIZE_VEC_EXT_32(signed long); -_LIBCPP_SPECIALIZE_VEC_EXT_32(signed long long); -_LIBCPP_SPECIALIZE_VEC_EXT_32(unsigned char); -_LIBCPP_SPECIALIZE_VEC_EXT_32(unsigned short); -_LIBCPP_SPECIALIZE_VEC_EXT_32(unsigned int); -_LIBCPP_SPECIALIZE_VEC_EXT_32(unsigned long); -_LIBCPP_SPECIALIZE_VEC_EXT_32(unsigned long long); -_LIBCPP_SPECIALIZE_VEC_EXT_32(float); -_LIBCPP_SPECIALIZE_VEC_EXT_32(double); -_LIBCPP_SPECIALIZE_VEC_EXT_32(long double); - -#undef _LIBCPP_SPECIALIZE_VEC_EXT_32 -#undef _LIBCPP_SPECIALIZE_VEC_EXT -#endif - -template -class __simd_storage<_Tp, __simd_abi<_StorageKind::_VecExt, __num_element>> { - using _StorageType = - typename __vec_ext_traits<_Tp, sizeof(_Tp) * __num_element>::type; - - _StorageType __storage_; - - template - friend struct simd; - - template - friend struct simd_mask; public: - _Tp __get(size_t __index) const noexcept { return __storage_[__index]; } - void __set(size_t __index, _Tp __val) noexcept { - __storage_[__index] = __val; - } -}; - -#endif // _LIBCPP_HAS_NO_VECTOR_EXTENSION - -template -class __simd_reference { - static_assert(std::is_same<_Vp, _Tp>::value, ""); - - template - friend struct simd; - - template - friend struct simd_mask; - - __simd_storage<_Tp, _Abi>* __ptr_; - size_t __index_; + using value_type = _ValueType; - __simd_reference(__simd_storage<_Tp, _Abi>* __ptr, size_t __index) - : __ptr_(__ptr), __index_(__index) {} + __simd_reference() = delete; + __simd_reference(const __simd_reference&) = delete; - __simd_reference(const __simd_reference&) = default; + operator value_type() const noexcept { return __get(); } -public: - __simd_reference() = delete; - __simd_reference& operator=(const __simd_reference&) = delete; + template + enable_if_t() = declval<_Up>())>>, __simd_reference> + operator=(_Up&& __v) && noexcept { + __set(static_cast(std::forward<_Up>(__v))); + return {__s_, __idx_}; + } - operator _Vp() const { return __ptr_->__get(__index_); } +# define _LIBCXX_SIMD_REFERENCE_OP_(__op) \ + template \ + enable_if_t() __op declval<_Up>())>>, __simd_reference> \ + operator __op##=(_Up&& __v)&& noexcept { \ + __set(__get() __op static_cast(std::forward<_Up>(__v))); \ + return {__s_, __idx_}; \ + } + _LIBCXX_SIMD_REFERENCE_OP_(+) + _LIBCXX_SIMD_REFERENCE_OP_(-) + _LIBCXX_SIMD_REFERENCE_OP_(*) + _LIBCXX_SIMD_REFERENCE_OP_(/) + _LIBCXX_SIMD_REFERENCE_OP_(%) + _LIBCXX_SIMD_REFERENCE_OP_(^) + _LIBCXX_SIMD_REFERENCE_OP_(<<) + _LIBCXX_SIMD_REFERENCE_OP_(>>) +# undef _LIBCXX_SIMD_REFERENCE_OP_ - __simd_reference operator=(_Vp __value) && { - __ptr_->__set(__index_, __value); - return *this; + template + enable_if_t() &= declval<_Up>())>>, __simd_reference> + operator&=(_Up&& __v) && noexcept { + if constexpr (is_same_v<_ValueType, bool>) + __set(__get() && static_cast(std::forward<_Up>(__v))); + else + __set(__get() & static_cast(std::forward<_Up>(__v))); + return {__s_, __idx_}; } - __simd_reference operator++() && { - return std::move(*this) = __ptr_->__get(__index_) + 1; + template + enable_if_t() |= declval<_Up>())>>, __simd_reference> + operator|=(_Up&& __v) && noexcept { + if constexpr (is_same_v<_ValueType, bool>) + __set(__get() || static_cast(std::forward<_Up>(__v))); + else + __set(__get() | static_cast(std::forward<_Up>(__v))); + return {__s_, __idx_}; } - _Vp operator++(int) && { - auto __val = __ptr_->__get(__index_); - __ptr_->__set(__index_, __val + 1); - return __val; + template &>())> + __simd_reference operator++() && noexcept { + __set(__get() + 1); + return {__s_, __idx_}; } - __simd_reference operator--() && { - return std::move(*this) = __ptr_->__get(__index_) - 1; + template &>()++)> + value_type operator++(int) && noexcept { + auto __r = __get(); + __set(__get() + 1); + return __r; } - _Vp operator--(int) && { - auto __val = __ptr_->__get(__index_); - __ptr_->__set(__index_, __val - 1); - return __val; + template &>())> + __simd_reference operator--() && noexcept { + __set(__get() - 1); + return {__s_, __idx_}; } - __simd_reference operator+=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) + __value; + template &>()--)> + value_type operator--(int) && noexcept { + auto __r = __get(); + __set(__get() - 1); + return __r; } - __simd_reference operator-=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) - __value; + friend void swap(__simd_reference&& __a, __simd_reference&& __b) noexcept { + value_type __tmp = static_cast<__simd_reference&&>(__a); + static_cast<__simd_reference&&>(__a) = static_cast(__b); + static_cast<__simd_reference&&>(__b) = std::move(__tmp); } - __simd_reference operator*=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) * __value; + friend void swap(value_type& __a, __simd_reference&& __b) noexcept { + value_type __tmp(std::move(__a)); + __a = static_cast(__b); + static_cast<__simd_reference&&>(__b) = std::move(__tmp); } - __simd_reference operator/=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) / __value; + friend void swap(__simd_reference&& __a, value_type& __b) noexcept { + value_type __tmp(__a); + static_cast<__simd_reference&&>(__a) = std::move(__b); + __b = std::move(__tmp); } +}; - __simd_reference operator%=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) % __value; - } +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD - __simd_reference operator>>=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) >> __value; - } +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD_ABI +// TODO: Elaboration of implementation-defined ABIs - __simd_reference operator<<=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) << __value; - } +using scalar = __scalar; - __simd_reference operator&=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) & __value; - } +template +inline constexpr size_t max_fixed_size = 32; - __simd_reference operator|=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) | __value; - } +template +using fixed_size = __vec_ext<_Np>; +template +using compatible = __vec_ext<16 / sizeof(_Tp)>; +# if defined(__AVX__) +template +using native = __vec_ext<_LIBCPP_NATIVE_SIMD_WIDTH_IN_BYTES / sizeof(_Tp)>; +# else +template +using native = __vec_ext<16 / sizeof(_Tp)>; +# endif - __simd_reference operator^=(_Vp __value) && { - return std::move(*this) = __ptr_->__get(__index_) ^ __value; - } +template +struct deduce { + using type = fixed_size<_Np>; }; -template -_LIBCPP_HIDE_FROM_ABI constexpr decltype(_To{std::declval<_From>()}, true) -__is_non_narrowing_convertible_impl(_From) { - return true; -} +template +using deduce_t = typename deduce<_Tp, _Np>::type; -template -_LIBCPP_HIDE_FROM_ABI constexpr bool __is_non_narrowing_convertible_impl(...) { - return false; -} +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD_ABI -template -_LIBCPP_HIDE_FROM_ABI -constexpr typename std::enable_if::value && - std::is_arithmetic<_From>::value, - bool>::type -__is_non_narrowing_arithmetic_convertible() { - return __is_non_narrowing_convertible_impl<_To>(_From{}); -} +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD -template -_LIBCPP_HIDE_FROM_ABI -constexpr typename std::enable_if::value && - std::is_arithmetic<_From>::value), - bool>::type -__is_non_narrowing_arithmetic_convertible() { - return false; -} +// Class template simd [parallel.simd.class] +template > +class simd; template -_LIBCPP_HIDE_FROM_ABI constexpr _Tp __variadic_sum() { - return _Tp{}; -} +using native_simd = simd<_Tp, simd_abi::native<_Tp>>; -template -_LIBCPP_HIDE_FROM_ABI constexpr _Tp __variadic_sum(_Up __first, _Args... __rest) { - return static_cast<_Tp>(__first) + __variadic_sum<_Tp>(__rest...); -} +template +using fixed_size_simd = simd<_Tp, simd_abi::fixed_size<_Np>>; -template -struct __nodeduce { - using type = _Tp; -}; +// Class template simd_mask [parallel.simd.mask.class] +template > +class simd_mask; template -_LIBCPP_HIDE_FROM_ABI constexpr bool __vectorizable() { - return std::is_arithmetic<_Tp>::value && !std::is_const<_Tp>::value && - !std::is_volatile<_Tp>::value && !std::is_same<_Tp, bool>::value; -} - -_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD -_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD_ABI +using native_simd_mask = simd_mask<_Tp, simd_abi::native<_Tp>>; -using scalar = __simd_abi<_StorageKind::_Scalar, 1>; +template +using fixed_size_simd_mask = simd_mask<_Tp, simd_abi::fixed_size<_Np>>; -template -using fixed_size = __simd_abi<_StorageKind::_Array, _Np>; +// Memory alignment +struct element_aligned_tag { + template + static constexpr size_t __alignment = alignof(_Up); -template -inline constexpr size_t max_fixed_size = 32; + template + static constexpr _Up* __apply(_Up* __ptr) { + return __ptr; + } +}; -template -using compatible = fixed_size<16 / sizeof(_Tp)>; +struct vector_aligned_tag { + template + static constexpr size_t __alignment = bit_ceil(sizeof(_Up) * _Tp::size()); -#ifndef _LIBCPP_HAS_NO_VECTOR_EXTENSION -template -using native = __simd_abi<_StorageKind::_VecExt, - _LIBCPP_NATIVE_SIMD_WIDTH_IN_BYTES / sizeof(_Tp)>; -#else -template -using native = - fixed_size<_Tp, _LIBCPP_NATIVE_SIMD_WIDTH_IN_BYTES / sizeof(_Tp)>; -#endif // _LIBCPP_HAS_NO_VECTOR_EXTENSION + template + static constexpr _Up* __apply(_Up* __ptr) { + return static_cast<_Up*>(__builtin_assume_aligned(__ptr, __alignment<_Tp, _Up>)); + } +}; -_LIBCPP_END_NAMESPACE_EXPERIMENTAL_SIMD_ABI -_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_SIMD +template +struct overaligned_tag { + template + static constexpr size_t __alignment = _Np; -template > -class simd; -template > -class simd_mask; + template + static constexpr _Up* __apply(_Up* __ptr) { + return static_cast<_Up*>(__builtin_assume_aligned(__ptr, _Np)); + } +}; -struct element_aligned_tag {}; -struct vector_aligned_tag {}; -template -struct overaligned_tag {}; inline constexpr element_aligned_tag element_aligned{}; + inline constexpr vector_aligned_tag vector_aligned{}; + template inline constexpr overaligned_tag<_Np> overaligned{}; -// traits [simd.traits] +// simd type traits [parallel.simd.traits] +template +struct __is_abi_tag_impl : false_type {}; + +template +struct __is_abi_tag_impl<_Tp, enable_if_t<_Tp::__is_abi_tag>> : bool_constant<_Tp::__is_abi_tag> {}; + template -struct is_abi_tag : std::integral_constant {}; +struct is_abi_tag : __is_abi_tag_impl<_Tp> {}; -template <_StorageKind __kind, int _Np> -struct is_abi_tag<__simd_abi<__kind, _Np>> - : std::integral_constant {}; +template +inline constexpr bool is_abi_tag_v = is_abi_tag<_Tp>::value; template -struct is_simd : std::integral_constant {}; +struct is_simd : false_type {}; template -struct is_simd> : std::integral_constant {}; +struct is_simd> : true_type {}; + +template +inline constexpr bool is_simd_v = is_simd<_Tp>::value; template -struct is_simd_mask : std::integral_constant {}; +struct is_simd_mask : false_type {}; template -struct is_simd_mask> : std::integral_constant { -}; +struct is_simd_mask> : true_type {}; + +template +inline constexpr bool is_simd_mask_v = is_simd_mask<_Tp>::value; template -struct is_simd_flag_type : std::integral_constant {}; +struct is_simd_flag_type : false_type {}; template <> -struct is_simd_flag_type - : std::integral_constant {}; +struct is_simd_flag_type : true_type {}; template <> -struct is_simd_flag_type - : std::integral_constant {}; +struct is_simd_flag_type : true_type {}; -template -struct is_simd_flag_type> - : std::integral_constant {}; +template +struct is_simd_flag_type> : true_type {}; -template -inline constexpr bool is_abi_tag_v = is_abi_tag<_Tp>::value; -template -inline constexpr bool is_simd_v = is_simd<_Tp>::value; -template -inline constexpr bool is_simd_mask_v = is_simd_mask<_Tp>::value; template inline constexpr bool is_simd_flag_type_v = is_simd_flag_type<_Tp>::value; -template -struct abi_for_size { - using type = simd_abi::fixed_size<_Np>; -}; -template -using abi_for_size_t = typename abi_for_size<_Tp, _Np>::type; -template > -struct simd_size; - -template -struct simd_size<_Tp, __simd_abi<__kind, _Np>> - : std::integral_constant { - static_assert( - std::is_arithmetic<_Tp>::value && - !std::is_same<__remove_const_t<_Tp>, bool>::value, - "Element type should be vectorizable"); -}; +template +struct __simd_size_impl {}; -// TODO: implement it. -template -struct memory_alignment; +template +struct __simd_size_impl<_Tp, _Abi, enable_if_t<__is_vectorizable<_Tp>() && is_abi_tag_v<_Abi>>> + : integral_constant {}; + +template > +struct simd_size : __simd_size_impl<_Tp, _Abi> {}; template > inline constexpr size_t simd_size_v = simd_size<_Tp, _Abi>::value; +template +struct __memory_alignment_impl {}; + +template +struct __memory_alignment_impl< + _Tp, + _Up, + enable_if_t<(is_simd_v<_Tp> && __is_vectorizable<_Up>()) || (is_simd_mask_v<_Tp> && is_same_v<_Up, bool>)>> + : integral_constant> {}; + +template +struct memory_alignment : __memory_alignment_impl<_Tp, _Up> {}; + template inline constexpr size_t memory_alignment_v = memory_alignment<_Tp, _Up>::value; -// class template simd [simd.class] -template -using native_simd = simd<_Tp, simd_abi::native<_Tp>>; -template -using fixed_size_simd = simd<_Tp, simd_abi::fixed_size<_Np>>; +template +struct __rebind_simd_impl {}; -// class template simd_mask [simd.mask.class] -template -using native_simd_mask = simd_mask<_Tp, simd_abi::native<_Tp>>; +template +struct __rebind_simd_impl<_Tp, simd<_Up, _Abi>, void_t, _Abi>> > { + using type = simd<_Tp, simd_abi::deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>>; +}; -template -using fixed_size_simd_mask = simd_mask<_Tp, simd_abi::fixed_size<_Np>>; +template +struct __rebind_simd_impl<_Tp, simd_mask<_Up, _Abi>, void_t, _Abi>> > { + using type = simd_mask<_Tp, simd_abi::deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>>; +}; -// casts [simd.casts] -template -struct __static_simd_cast_traits { - template - static simd<_Tp, _Abi> __apply(const simd<_Up, _Abi>& __v); +template +struct rebind_simd : __rebind_simd_impl<_Tp, _Vp> {}; + +template +using rebind_simd_t = typename rebind_simd<_Tp, _Vp>::type; + +template +struct __resize_simd_impl {}; + +template +struct __resize_simd_impl<_Np, simd<_Tp, _Abi>, void_t> > { + using type = simd<_Tp, simd_abi::deduce_t<_Tp, _Np, _Abi>>; +}; + +template +struct __resize_simd_impl<_Np, simd_mask<_Tp, _Abi>, void_t> > { + using type = simd_mask<_Tp, simd_abi::deduce_t<_Tp, _Np, _Abi>>; }; -template -struct __static_simd_cast_traits> { - template - static typename std::enable_if::size() == - simd<_Tp, _NewAbi>::size(), - simd<_Tp, _NewAbi>>::type - __apply(const simd<_Up, _Abi>& __v); +template +struct resize_simd : __resize_simd_impl<_Np, _Vp> {}; + +template +using resize_simd_t = typename resize_simd<_Np, _Vp>::type; + +// Casts [parallel.simd.casts] +template +struct __safe_make_signed { + using type = _Tp; }; template -struct __simd_cast_traits { - template - static typename std::enable_if< - __is_non_narrowing_arithmetic_convertible<_Up, _Tp>(), - simd<_Tp, _Abi>>::type - __apply(const simd<_Up, _Abi>& __v); +struct __safe_make_signed<_Tp, enable_if_t>> { + using type = make_signed_t<_Tp>; }; -template -struct __simd_cast_traits> { - template - static typename std::enable_if< - __is_non_narrowing_arithmetic_convertible<_Up, _Tp>() && - simd<_Up, _Abi>::size() == simd<_Tp, _NewAbi>::size(), - simd<_Tp, _NewAbi>>::type - __apply(const simd<_Up, _Abi>& __v); +template +struct __static_simd_cast_return_type {}; + +template +struct __static_simd_cast_return_type<_Tp, + _Up, + _Abi, + enable_if_t && _Tp::size() == simd<_Up, _Abi>::size()>> { + using type = _Tp; }; template -_LIBCPP_HIDE_FROM_ABI auto simd_cast(const simd<_Up, _Abi>& __v) - -> decltype(__simd_cast_traits<_Tp>::__apply(__v)) { - return __simd_cast_traits<_Tp>::__apply(__v); -} +struct __static_simd_cast_return_type< + _Tp, + _Up, + _Abi, + enable_if_t && + (is_same_v<_Tp, _Up> || is_same_v<__safe_make_signed<_Tp>, __safe_make_signed<_Up>>) >> { + using type = simd<_Tp, _Abi>; +}; template -_LIBCPP_HIDE_FROM_ABI auto static_simd_cast(const simd<_Up, _Abi>& __v) - -> decltype(__static_simd_cast_traits<_Tp>::__apply(__v)) { - return __static_simd_cast_traits<_Tp>::__apply(__v); -} +struct __static_simd_cast_return_type<_Tp, + _Up, + _Abi, + enable_if_t && !is_same_v<_Tp, _Up> && + !is_same_v<__safe_make_signed<_Up>, __safe_make_signed<_Tp>>>> { + using type = simd<_Tp, simd_abi::fixed_size::size()>>; +}; -template -fixed_size_simd<_Tp, simd_size<_Tp, _Abi>::value> -to_fixed_size(const simd<_Tp, _Abi>&) noexcept; +template +struct __simd_cast_return_type {}; -template -fixed_size_simd_mask<_Tp, simd_size<_Tp, _Abi>::value> -to_fixed_size(const simd_mask<_Tp, _Abi>&) noexcept; +template +struct __simd_cast_return_type<_Tp, + _Up, + _Abi, + enable_if_t && _Tp::size() == simd<_Up, _Abi>::size() && + is_convertible_v<_Up, typename _Tp::value_type>>> { + using type = _Tp; +}; -template -native_simd<_Tp> to_native(const fixed_size_simd<_Tp, _Np>&) noexcept; +template +struct __simd_cast_return_type<_Tp, _Up, _Abi, enable_if_t && is_same_v<_Tp, _Up>>> { + using type = simd<_Tp, _Abi>; +}; -template -native_simd_mask<_Tp> to_native(const fixed_size_simd_mask<_Tp, _Np>&) noexcept; +template +struct __simd_cast_return_type<_Tp, + _Up, + _Abi, + enable_if_t && !is_same_v<_Tp, _Up> && is_convertible_v<_Up, _Tp>>> { + using type = simd<_Tp, simd_abi::fixed_size::size()>>; +}; -template -simd<_Tp> to_compatible(const fixed_size_simd<_Tp, _Np>&) noexcept; +template +enable_if_t && is_simd_v<_From> && _To::size() == _From::size(), _To> +__simd_conversion(_From __v) { // TODO: Optimizeable + _To res; + for (size_t __i = 0; __i < _From::size(); __i++) { + res[__i] = static_cast(__v[__i]); + } + return res; +} -template -simd_mask<_Tp> to_compatible(const fixed_size_simd_mask<_Tp, _Np>&) noexcept; +template +enable_if_t && is_simd_mask_v<_From> && _To::size() == _From::size(), _To> +__mask_conversion(_From __m) { // TODO: Optimizeable + _To res; + for (size_t __i = 0; __i < _From::size(); __i++) { + res[__i] = __m[__i]; + } + return res; +} -template -tuple>...> split(const simd<_Tp, _Abi>&); +template +typename __simd_cast_return_type<_Tp, _Up, _Abi>::type simd_cast(const simd<_Up, _Abi>& __v) { + return __simd_conversion::type, simd<_Up, _Abi>>(__v); +} -template -tuple>...> -split(const simd_mask<_Tp, _Abi>&); +template +typename __static_simd_cast_return_type<_Tp, _Up, _Abi>::type static_simd_cast(const simd<_Up, _Abi>& __v) { + return __simd_conversion::type, simd<_Up, _Abi>>(__v); +} -template -array<_SimdType, simd_size::value / - _SimdType::size()> -split(const simd&); +template +fixed_size_simd<_Tp, simd_size_v<_Tp, _Abi>> to_fixed_size(const simd<_Tp, _Abi>& __v) noexcept { + return __simd_conversion>, simd<_Tp, _Abi>>(__v); +} + +template +fixed_size_simd_mask<_Tp, simd_size_v<_Tp, _Abi>> to_fixed_size(const simd_mask<_Tp, _Abi>& __m) noexcept { + return __mask_conversion>, simd_mask<_Tp, _Abi>>(__m); +} + +template +enable_if_t> == _Np, native_simd<_Tp>> +to_native(const fixed_size_simd<_Tp, _Np>& __v) noexcept { + return __simd_conversion, fixed_size_simd<_Tp, _Np>>(__v); +} + +template +enable_if_t> == _Np, native_simd_mask<_Tp>> +to_native(const fixed_size_simd_mask<_Tp, _Np>& __m) noexcept { + return __mask_conversion, fixed_size_simd_mask<_Tp, _Np>>(__m); +} + +template +enable_if_t> == _Np, simd<_Tp>> +to_compatible(const fixed_size_simd<_Tp, _Np>& __v) noexcept { + return __simd_conversion, fixed_size_simd<_Tp, _Np>>(__v); +} + +template +enable_if_t> == _Np, simd_mask<_Tp>> +to_compatible(const fixed_size_simd_mask<_Tp, _Np>& __m) noexcept { + return __mask_conversion, fixed_size_simd_mask<_Tp, _Np>>(__m); +} + +template +void __split_assign(const _Simd& __v, _SimdType& __s, size_t& __count) { + for (size_t __i = 0; __i < __s.size(); __i++) + __s[__i] = __v[__count++]; +} + +template +void __split_tuple_impl(const _SimdType& __v, _Tuple& __t, size_t& __count, index_sequence<_Ip...>) { + (__split_assign(__v, get<_Ip>(__t), __count), ...); +} + +template +enable_if_t<((_Sizes + ...) == simd_size_v<_Tp, _Abi>), tuple>...>> +split(const simd<_Tp, _Abi>& __v) noexcept { + tuple>...> __t; + size_t __count = 0; + __split_tuple_impl(__v, __t, __count, make_index_sequence{}); + return __t; +} + +template +enable_if_t<((_Sizes + ...) == simd_size_v<_Tp, _Abi>), tuple>...>> +split(const simd_mask<_Tp, _Abi>& __m) noexcept { + tuple>...> __t; + size_t __count = 0; + __split_tuple_impl(__m, __t, __count, make_index_sequence{}); + return __t; +} template -array<_SimdType, simd_size::value / - _SimdType::size()> -split(const simd_mask&); +// FIX ME: Why simd_size_v gives error here? +enable_if_t && (simd_size::value % _SimdType::size() == 0), + array<_SimdType, simd_size_v / _SimdType::size()>> +split(const simd& __v) noexcept { + const size_t __size = _SimdType::size(); + array<_SimdType, simd_size_v / __size> __arr{}; + for (size_t __i = 0; __i < __arr.size(); __i++) { + for (size_t __j = 0; __j < __size; __j++) + __arr[__i][__j] = __v[__i * __size + __j]; + } + return __arr; +} + +template +enable_if_t< + is_simd_mask_v<_SimdMaskType> && + (simd_size_v % _SimdMaskType::size() == 0), + array<_SimdMaskType, simd_size_v / _SimdMaskType::size()>> +split(const simd_mask& __m) noexcept { + const size_t __size = _SimdMaskType::size(); + array<_SimdMaskType, simd_size_v / __size> __arr{}; + for (size_t __i = 0; __i < __arr.size(); __i++) { + for (size_t __j = 0; __j < __size; __j++) + __arr[__i][__j] = __m[__i * __size + __j]; + } + return __arr; +} + +template +enable_if_t % _Num == 0, + array / _Num, simd<_Tp, _Abi>>, _Num>> +split_by(const simd<_Tp, _Abi>& __v) noexcept { + const size_t __size = simd_size_v<_Tp, _Abi> / _Num; + array>, _Num> __arr{}; + for (size_t __i = 0; __i < __arr.size(); __i++) { + for (size_t __j = 0; __j < __size; __j++) + __arr[__i][__j] = __v[__i * __size + __j]; + } + return __arr; +} + +template +enable_if_t % _Num == 0, + array / _Num, simd_mask<_Tp, _Abi>>, _Num>> +split_by(const simd_mask<_Tp, _Abi>& __m) noexcept { + const size_t __size = simd_size_v<_Tp, _Abi> / _Num; + array>, _Num> __arr{}; + for (size_t __i = 0; __i < __arr.size(); __i++) { + for (size_t __j = 0; __j < __size; __j++) + __arr[__i][__j] = __m[__i * __size + __j]; + } + return __arr; +} + +template +void __concat_assign(const _Simd& __v, _SimdType& __s, size_t& __count) { + for (size_t __i = 0; __i < __v.size(); __i++) + __s[__count++] = __v[__i]; +} template -simd<_Tp, abi_for_size_t<_Tp, __variadic_sum(simd_size<_Tp, _Abis>::value...)>> -concat(const simd<_Tp, _Abis>&...); +simd<_Tp, simd_abi::deduce_t<_Tp, (simd_size_v<_Tp, _Abis> + ...)>> concat(const simd<_Tp, _Abis>&... __v) noexcept { + simd<_Tp, simd_abi::deduce_t<_Tp, (simd_size_v<_Tp, _Abis> + ...)>> __res; + size_t __count = 0; + (__concat_assign(__v, __res, __count), ...); + return __res; +} template -simd_mask<_Tp, - abi_for_size_t<_Tp, __variadic_sum(simd_size<_Tp, _Abis>::value...)>> -concat(const simd_mask<_Tp, _Abis>&...); +simd_mask<_Tp, simd_abi::deduce_t<_Tp, (simd_size_v<_Tp, _Abis> + ...)>> +concat(const simd_mask<_Tp, _Abis>&... __m) noexcept { + simd_mask<_Tp, simd_abi::deduce_t<_Tp, (simd_size_v<_Tp, _Abis> + ...)>> __res; + size_t __count = 0; + (__concat_assign(__m, __res, __count), ...); + return __res; +} -// reductions [simd.mask.reductions] +template +resize_simd_t * _Num, simd<_Tp, _Abi>> concat(const array, _Num>& __arr) noexcept { + const size_t __size = simd_size_v<_Tp, _Abi>; + resize_simd_t<__size * _Num, simd<_Tp, _Abi>> __s; + for (size_t __i = 0; __i < _Num; __i++) { + for (size_t __j = 0; __j < __size; __j++) + __s[__i * __size + __j] = __arr[__i][__j]; + } + return __s; +} + +template +resize_simd_t * _Num, simd_mask<_Tp, _Abi>> +concat(const array, _Num>& __arr) noexcept { + const size_t __size = simd_size_v<_Tp, _Abi>; + resize_simd_t<__size * _Num, simd_mask<_Tp, _Abi>> __m; + for (size_t __i = 0; __i < _Num; __i++) { + for (size_t __j = 0; __j < __size; __j++) + __m[__i * __size + __j] = __arr[__i][__j]; + } + return __m; +} + +// Reductions [parallel.simd.mask.reductions] template -bool all_of(const simd_mask<_Tp, _Abi>&) noexcept; +bool all_of(const simd_mask<_Tp, _Abi>& __m) noexcept { + return __mask_traits<_Tp, _Abi>::__all_of(__m.__s_); +} + template -bool any_of(const simd_mask<_Tp, _Abi>&) noexcept; +bool any_of(const simd_mask<_Tp, _Abi>& __m) noexcept { + return __mask_traits<_Tp, _Abi>::__any_of(__m.__s_); +} + template -bool none_of(const simd_mask<_Tp, _Abi>&) noexcept; +bool none_of(const simd_mask<_Tp, _Abi>& __m) noexcept { + return __mask_traits<_Tp, _Abi>::__none_of(__m.__s_); +} + template -bool some_of(const simd_mask<_Tp, _Abi>&) noexcept; +bool some_of(const simd_mask<_Tp, _Abi>& __m) noexcept { + return __mask_traits<_Tp, _Abi>::__some_of(__m.__s_); +} + template -int popcount(const simd_mask<_Tp, _Abi>&) noexcept; +int popcount(const simd_mask<_Tp, _Abi>& __m) noexcept { + return __mask_traits<_Tp, _Abi>::__popcount(__m.__s_); +} + template -int find_first_set(const simd_mask<_Tp, _Abi>&); +int find_first_set(const simd_mask<_Tp, _Abi>& __m) { + return __mask_traits<_Tp, _Abi>::__find_first_set(__m.__s_); +} + template -int find_last_set(const simd_mask<_Tp, _Abi>&); -bool all_of(bool) noexcept; -bool any_of(bool) noexcept; -bool none_of(bool) noexcept; -bool some_of(bool) noexcept; -int popcount(bool) noexcept; -int find_first_set(bool) noexcept; -int find_last_set(bool) noexcept; - -// masked assignment [simd.whereexpr] -template +int find_last_set(const simd_mask<_Tp, _Abi>& __m) { + return __mask_traits<_Tp, _Abi>::__find_last_set(__m.__s_); +} + +bool all_of(_Bool __v) noexcept { return __v; } + +bool any_of(_Bool __v) noexcept { return __v; } + +bool none_of(_Bool __v) noexcept { return !__v; } + +bool some_of(_Bool) noexcept { return false; } + +int popcount(_Bool __v) noexcept { return __v; } + +int find_first_set(_Bool) noexcept { return 0; } + +int find_last_set(_Bool) noexcept { return 0; } + +// Where expression class templates [parallel.simd.whereexpr] +template class const_where_expression; -template + +template class where_expression; -// masked assignment [simd.mask.where] +// where functions [parallel.simd.mask.where] template where_expression, simd<_Tp, _Abi>> -where(const typename simd<_Tp, _Abi>::mask_type&, simd<_Tp, _Abi>&) noexcept; +where(const typename simd<_Tp, _Abi>::mask_type& __m, simd<_Tp, _Abi>& __s) noexcept { + return {__m, __s}; +} template -const_where_expression, const simd<_Tp, _Abi>> -where(const typename simd<_Tp, _Abi>::mask_type&, - const simd<_Tp, _Abi>&) noexcept; +const_where_expression, simd<_Tp, _Abi>> +where(const typename simd<_Tp, _Abi>::mask_type& __m, const simd<_Tp, _Abi>& __s) noexcept { + return {__m, __s}; +} template where_expression, simd_mask<_Tp, _Abi>> -where(const typename __nodeduce>::type&, - simd_mask<_Tp, _Abi>&) noexcept; +where(const type_identity_t>& __m, simd_mask<_Tp, _Abi>& __s) noexcept { + return {__m, __s}; +} template -const_where_expression, const simd_mask<_Tp, _Abi>> -where(const typename __nodeduce>::type&, - const simd_mask<_Tp, _Abi>&) noexcept; +const_where_expression, simd_mask<_Tp, _Abi>> +where(const type_identity_t>& __m, const simd_mask<_Tp, _Abi>& __s) noexcept { + return {__m, __s}; +} template -where_expression where(bool, _Tp&) noexcept; +enable_if_t || is_simd_mask_v<_Tp>), where_expression> where(_Bool __m, _Tp& __s) noexcept { + return {__m, __s}; +} template -const_where_expression where(bool, const _Tp&) noexcept; +enable_if_t || is_simd_mask_v<_Tp>), const_where_expression> +where(_Bool __m, const _Tp& __s) noexcept { + return {__m, __s}; +} + +template +void where(bool __m, simd<_Tp, _Abi>& __v) = delete; -// reductions [simd.reductions] -template > -_Tp reduce(const simd<_Tp, _Abi>&, _BinaryOp = _BinaryOp()); +template +void where(bool __m, const simd<_Tp, _Abi>& __v) = delete; + +// Reductions [parallel.simd.reductions] +template > +_Tp reduce(const simd<_Tp, _Abi>& __s, _BinaryOp __binary_op = {}) { + return __simd_traits<_Tp, _Abi>::__reduce(__s.__s_, __binary_op); +} template typename _SimdType::value_type -reduce(const const_where_expression<_MaskType, _SimdType>&, - typename _SimdType::value_type __neutral_element, _BinaryOp); +reduce(const const_where_expression<_MaskType, _SimdType>& __w, + typename _SimdType::value_type __identity, + _BinaryOp __binary_op) { + _SimdType __tmp(__w.__s_); + return _SimdType::_Impl::__reduce( + _SimdType::_Impl::__masked_assign( + __tmp.__s_, _MaskType::_Impl::__negate(__w.__m_.__s_), _SimdType::_Impl::__broadcast(__identity)), + __binary_op); +} template typename _SimdType::value_type -reduce(const const_where_expression<_MaskType, _SimdType>&, - plus = {}); +reduce(const const_where_expression<_MaskType, _SimdType>& __w, plus<> __binary_op = {}) noexcept { + return reduce(__w, 0, __binary_op); +}; template typename _SimdType::value_type -reduce(const const_where_expression<_MaskType, _SimdType>&, - multiplies); +reduce(const const_where_expression<_MaskType, _SimdType>& __w, multiplies<> __binary_op) noexcept { + return reduce(__w, 1, __binary_op); +}; template typename _SimdType::value_type -reduce(const const_where_expression<_MaskType, _SimdType>&, - bit_and); +reduce(const const_where_expression<_MaskType, _SimdType>& __w, bit_and<> __binary_op) noexcept { + return reduce(__w, ~typename _SimdType::value_type(), __binary_op); +}; template typename _SimdType::value_type -reduce(const const_where_expression<_MaskType, _SimdType>&, - bit_or); +reduce(const const_where_expression<_MaskType, _SimdType>& __w, bit_or<> __binary_op) noexcept { + return reduce(__w, 0, __binary_op); +}; template typename _SimdType::value_type -reduce(const const_where_expression<_MaskType, _SimdType>&, - bit_xor); +reduce(const const_where_expression<_MaskType, _SimdType>& __w, bit_xor<> __binary_op) noexcept { + return reduce(__w, 0, __binary_op); +}; template -_Tp hmin(const simd<_Tp, _Abi>&); +_Tp hmin(const simd<_Tp, _Abi>& __s) noexcept { + return __simd_traits<_Tp, _Abi>::__hmin(__s.__s_); +} + template -typename _SimdType::value_type -hmin(const const_where_expression<_MaskType, _SimdType>&); +typename _SimdType::value_type hmin(const const_where_expression<_MaskType, _SimdType>& __w) noexcept { + _SimdType __tmp(__w.__s_); + return _SimdType::_Impl::__hmin(_SimdType::_Impl::__masked_assign( + __tmp.__s_, + _MaskType::_Impl::__negate(__w.__m_.__s_), + _SimdType::_Impl::__broadcast(numeric_limits::max()))); +}; + template -_Tp hmax(const simd<_Tp, _Abi>&); +_Tp hmax(const simd<_Tp, _Abi>& __s) noexcept { + return __simd_traits<_Tp, _Abi>::__hmax(__s.__s_); +} + template -typename _SimdType::value_type -hmax(const const_where_expression<_MaskType, _SimdType>&); +typename _SimdType::value_type hmax(const const_where_expression<_MaskType, _SimdType>& __w) noexcept { + _SimdType __tmp(__w.__s_); + return _SimdType::_Impl::__hmax(_SimdType::_Impl::__masked_assign( + __tmp.__s_, + _MaskType::_Impl::__negate(__w.__m_.__s_), + _SimdType::_Impl::__broadcast(numeric_limits::lowest()))); +}; -// algorithms [simd.alg] +// Algorithms [parallel.simd.alg] template -simd<_Tp, _Abi> min(const simd<_Tp, _Abi>&, const simd<_Tp, _Abi>&) noexcept; +simd<_Tp, _Abi> min(const simd<_Tp, _Abi>& __a, const simd<_Tp, _Abi>& __b) noexcept { + return __simd_traits<_Tp, _Abi>::__min(__a.__s_, __b.__s_); +} template -simd<_Tp, _Abi> max(const simd<_Tp, _Abi>&, const simd<_Tp, _Abi>&) noexcept; +simd<_Tp, _Abi> max(const simd<_Tp, _Abi>& __a, const simd<_Tp, _Abi>& __b) noexcept { + return __simd_traits<_Tp, _Abi>::__max(__a.__s_, __b.__s_); +} template -std::pair, simd<_Tp, _Abi>> -minmax(const simd<_Tp, _Abi>&, const simd<_Tp, _Abi>&) noexcept; +pair, simd<_Tp, _Abi>> minmax(const simd<_Tp, _Abi>& __a, const simd<_Tp, _Abi>& __b) noexcept { + return __simd_traits<_Tp, _Abi>::__minmax(__a.__s_, __b.__s_); +} template -simd<_Tp, _Abi> clamp(const simd<_Tp, _Abi>&, const simd<_Tp, _Abi>&, - const simd<_Tp, _Abi>&); +simd<_Tp, _Abi> clamp(const simd<_Tp, _Abi>& __v, const simd<_Tp, _Abi>& __lo, const simd<_Tp, _Abi>& __hi) noexcept { + return __simd_traits<_Tp, _Abi>::__clamp(__v.__s_, __lo.__s_, __hi.__s_); +} -// [simd.whereexpr] -// TODO implement where expressions. -template +// Where expression class templates [parallel.simd.whereexpr] +template class const_where_expression { + // TODO: check type of _MaskType and _SimdType + template + friend typename _Vp::value_type + reduce(const const_where_expression<_Mp, _Vp>& __w, typename _Vp::value_type __identity, _BinaryOp __op); + + template + friend typename _Vp::value_type hmin(const const_where_expression<_Mp, _Vp>& __w) noexcept; + + template + friend typename _Vp::value_type hmax(const const_where_expression<_Mp, _Vp>& __w) noexcept; + +protected: + using _Impl = typename _SimdType::_Impl; + const _MaskType __m_; + _SimdType& __s_; + public: const_where_expression(const const_where_expression&) = delete; const_where_expression& operator=(const const_where_expression&) = delete; - __remove_const_t<_Tp> operator-() const&&; + const_where_expression(const _MaskType& __m, const _SimdType& __s) : __m_(__m), __s_(const_cast<_SimdType&>(__s)) {} + + _SimdType operator-() const&& noexcept { + return _Impl::__masked_assign(__s_.__s_, __m_.__s_, _Impl::__unary_minus(__s_.__s_)); + } + _SimdType operator+() const&& noexcept { return __s_; } + _SimdType operator~() const&& noexcept { + return _Impl::__masked_assign(__s_.__s_, __m_.__s_, _Impl::__bitwise_not(__s_.__s_)); + } + template - void copy_to(_Up*, _Flags) const&&; + enable_if_t && + ((is_same_v<_Up, bool> && is_same_v) || + (__is_vectorizable<_Up>() && !is_same_v))> + copy_to(_Up* __mem, _Flags) const&& { + _SimdType __tmp; + _Impl::__load(__tmp.__s_, _Flags::template __apply<_SimdType>(__mem)); + _Impl::__store( + _Impl::__masked_assign(__tmp.__s_, __m_.__s_, __s_.__s_), _Flags::template __apply<_SimdType>(__mem)); + } }; -template -class where_expression : public const_where_expression<_MaskType, _Tp> { +template +class const_where_expression { + // TODO: check type of _Tp + template + friend typename _Vp::value_type + reduce(const const_where_expression<_Mp, _Vp>& __w, typename _Vp::value_type __identity, _BinaryOp __op); + + template + friend typename _Vp::value_type hmin(const const_where_expression<_Mp, _Vp>& __w) noexcept; + + template + friend typename _Vp::value_type hmax(const const_where_expression<_Mp, _Vp>& __w) noexcept; + +protected: + const bool __m_; + _Tp& __s_; + public: - where_expression(const where_expression&) = delete; - where_expression& operator=(const where_expression&) = delete; - template - void operator=(_Up&&); - template - void operator+=(_Up&&); - template - void operator-=(_Up&&); - template - void operator*=(_Up&&); - template - void operator/=(_Up&&); - template - void operator%=(_Up&&); - template - void operator&=(_Up&&); - template - void operator|=(_Up&&); - template - void operator^=(_Up&&); - template - void operator<<=(_Up&&); - template - void operator>>=(_Up&&); - void operator++(); - void operator++(int); - void operator--(); - void operator--(int); + const_where_expression(const const_where_expression&) = delete; + const_where_expression& operator=(const const_where_expression&) = delete; + const_where_expression(const bool __m, const _Tp& __s) : __m_(__m), __s_(const_cast<_Tp&>(__s)) {} + + _Tp operator-() const&& noexcept { return __m_ ? -__s_ : __s_; } + _Tp operator+() const&& noexcept { return __s_; } + _Tp operator~() const&& noexcept { return __m_ ? !__s_ : __s_; } + template - void copy_from(const _Up*, _Flags); + enable_if_t && + ((is_same_v<_Up, bool> && is_same_v<_Tp, bool>) || (__is_vectorizable<_Up>() && !is_same_v<_Tp, bool>))> + copy_to(_Up* __mem, _Flags) const&& { + if (__m_) + __mem[0] = __m_ ? __s_ : __mem[0]; + }; }; -// [simd.class] -// TODO: implement simd -template -class simd { +template +class where_expression : public const_where_expression<_MaskType, _SimdType> { + using _Impl = typename const_where_expression<_MaskType, _SimdType>::_Impl; + using const_where_expression<_MaskType, _SimdType>::__m_; + using const_where_expression<_MaskType, _SimdType>::__s_; + public: - using value_type = _Tp; - using reference = __simd_reference<_Tp, _Tp, _Abi>; - using mask_type = simd_mask<_Tp, _Abi>; - using abi_type = _Abi; + where_expression(const _MaskType& __m, _SimdType& __s) : const_where_expression<_MaskType, _SimdType>(__m, __s) {} - simd() = default; - simd(const simd&) = default; - simd& operator=(const simd&) = default; + template + enable_if_t> operator=(_Up&& __v) && noexcept { + if constexpr (is_arithmetic_v<_Up>) + _Impl::__masked_assign(__s_.__s_, __m_.__s_, _Impl::__broadcast(__v)); + else + _Impl::__masked_assign(__s_.__s_, __m_.__s_, __v.__s_); + } - static constexpr size_t size() noexcept { - return simd_size<_Tp, _Abi>::value; +# define _LIBCXX_SIMD_MASK_OP_(__op, __name) \ + template \ + enable_if_t() __op declval<_Up>()), _SimdType>> operator __op##=( \ + _Up&& __v)&& noexcept { \ + if constexpr (is_arithmetic_v<_Up>) \ + _Impl::__masked_assign(__s_.__s_, __m_.__s_, _Impl::__name(__s_.__s_, _Impl::__broadcast(__v))); \ + else \ + _Impl::__masked_assign(__s_.__s_, __m_.__s_, _Impl::__name(__s_.__s_, __v.__s_)); \ + } + + _LIBCXX_SIMD_MASK_OP_(+, __plus) + _LIBCXX_SIMD_MASK_OP_(-, __minus) + _LIBCXX_SIMD_MASK_OP_(*, __multiplies) + _LIBCXX_SIMD_MASK_OP_(/, __divides) + _LIBCXX_SIMD_MASK_OP_(%, __modulus) + _LIBCXX_SIMD_MASK_OP_(&, __bitwise_and) + _LIBCXX_SIMD_MASK_OP_(|, __bitwise_or) + _LIBCXX_SIMD_MASK_OP_(^, __bitwise_xor) + _LIBCXX_SIMD_MASK_OP_(<<, __shift_left) + _LIBCXX_SIMD_MASK_OP_(>>, __shift_right) +# undef _LIBCXX_SIMD_MASK_OP_ + + template &>())> + void operator++() && noexcept { + _SimdType __tmp = __s_; + _Impl::__increment(__tmp.__s_); + _Impl::__masked_assign(__s_.__s_, __m_.__s_, __tmp.__s_); } -private: - __simd_storage<_Tp, _Abi> __s_; + template &>()++)> + void operator++(int) && noexcept { + _SimdType __tmp = __s_; + _Impl::__increment(__tmp.__s_); + _Impl::__masked_assign(__s_.__s_, __m_.__s_, __tmp.__s_); + } + + template &>())> + void operator--() && noexcept { + _SimdType __tmp = __s_; + _Impl::__decrement(__tmp.__s_); + _Impl::__masked_assign(__s_.__s_, __m_.__s_, __tmp.__s_); + } + + template &>()--)> + void operator--(int) && noexcept { + _SimdType __tmp = __s_; + _Impl::__decrement(__tmp.__s_); + _Impl::__masked_assign(__s_.__s_, __m_.__s_, __tmp.__s_); + } + + template + enable_if_t && + ((is_same_v<_Up, bool> && is_same_v) || + (__is_vectorizable<_Up>() && !is_same_v))> + copy_from(const _Up* __mem, _Flags) && { + _SimdType __tmp; + _Impl::__load(__tmp.__s_, _Flags::template __apply<_SimdType>(__mem)); + _Impl::__masked_assign(__s_.__s_, __m_.__s_, __tmp.__s_); + } +}; + +template +class where_expression : public const_where_expression { + using const_where_expression::__m_; + using const_where_expression::__s_; + +public: + where_expression(const bool __m, _Tp& __s) : const_where_expression(__m, __s) {} template - static constexpr bool __can_broadcast() { - return (std::is_arithmetic<_Up>::value && - __is_non_narrowing_arithmetic_convertible<_Up, _Tp>()) || - (!std::is_arithmetic<_Up>::value && - std::is_convertible<_Up, _Tp>::value) || - std::is_same<__remove_const_t<_Up>, int>::value || - (std::is_same<__remove_const_t<_Up>, - unsigned int>::value && - std::is_unsigned<_Tp>::value); + enable_if_t> operator=(_Up&& __v) && noexcept { + __s_ = __m_ ? static_cast<_Up&&>(__v) : __s_; + } + +# define _LIBCXX_SIMD_MASK_OP_(__op) \ + template \ + enable_if_t() __op declval<_Up>()), _Tp>> operator __op##=( \ + _Up&& __v)&& noexcept { \ + __s_ = __m_ ? __s_ __op static_cast<_Up&&>(__v) : __s_; \ + } + _LIBCXX_SIMD_MASK_OP_(+) + _LIBCXX_SIMD_MASK_OP_(-) + _LIBCXX_SIMD_MASK_OP_(*) + _LIBCXX_SIMD_MASK_OP_(/) + _LIBCXX_SIMD_MASK_OP_(%) + _LIBCXX_SIMD_MASK_OP_(&) + _LIBCXX_SIMD_MASK_OP_(|) + _LIBCXX_SIMD_MASK_OP_(^) + _LIBCXX_SIMD_MASK_OP_(<<) + _LIBCXX_SIMD_MASK_OP_(>>) +# undef _LIBCXX_SIMD_MASK_OP_ + + template &>())> + void operator++() && noexcept { + if (__m_) + __s_++; } - template - static constexpr decltype( - std::forward_as_tuple(std::declval<_Generator>()( - std::integral_constant())...), - bool()) - __can_generate(std::index_sequence<__indicies...>) { - return !__variadic_sum( - !__can_broadcast()( - std::integral_constant()))>()...); + template &>()++)> + void operator++(int) && noexcept { + if (__m_) + ++__s_; } - template - static bool __can_generate(...) { - return false; + template &>())> + void operator--() && noexcept { + if (__m_) + __s_--; } - template - void __generator_init(_Generator&& __g, std::index_sequence<__indicies...>) { - int __not_used[]{((*this)[__indicies] = - __g(std::integral_constant()), - 0)...}; - (void)__not_used; + template &>()--)> + void operator--(int) && noexcept { + if (__m_) + --__s_; } + template + enable_if_t && + ((is_same_v<_Up, bool> && is_same_v<_Tp, bool>) || (__is_vectorizable<_Up>() && !is_same_v<_Tp, bool>))> + copy_from(const _Up* __mem, _Flags) && { + __s_ = __m_ ? __mem[0] : __s_; + } +}; + +// Class template simd [parallel.simd.class] +template +class __simd_int_operators {}; + +template +class __simd_int_operators<_Simd, _Impl, true> { public: - // implicit type conversion constructor - template >::value && - __is_non_narrowing_arithmetic_convertible<_Up, _Tp>()>::type> - simd(const simd<_Up, simd_abi::fixed_size>& __v) { - for (size_t __i = 0; __i < size(); __i++) { - (*this)[__i] = static_cast<_Tp>(__v[__i]); - } + // unary operators for integral _Tp [simd.unary] + _Simd operator~() const noexcept { return _Impl::__bitwise_not((*static_cast(this)).__s_); } + + // binary operators for integral _Tp [simd.binary] + friend _Simd operator%(const _Simd& __lhs, const _Simd& __rhs) noexcept { + return _Impl::__modulus(__lhs.__s_, __rhs.__s_); } + friend _Simd operator&(const _Simd& __lhs, const _Simd& __rhs) noexcept { + return _Impl::__bitwise_and(__lhs.__s_, __rhs.__s_); + } + + friend _Simd operator|(const _Simd& __lhs, const _Simd& __rhs) noexcept { + return _Impl::__bitwise_or(__lhs.__s_, __rhs.__s_); + } + + friend _Simd operator^(const _Simd& __lhs, const _Simd& __rhs) noexcept { + return _Impl::__bitwise_xor(__lhs.__s_, __rhs.__s_); + } + + friend _Simd operator<<(const _Simd& __lhs, const _Simd& __rhs) noexcept { + return _Impl::__shift_left(__lhs.__s_, __rhs.__s_); + } + + friend _Simd operator>>(const _Simd& __lhs, const _Simd& __rhs) noexcept { + return _Impl::__shift_right(__lhs.__s_, __rhs.__s_); + } + + friend _Simd operator<<(const _Simd& __lhs, int __rhs) noexcept { return _Impl::__shift_left(__lhs.__s_, __rhs); } + + friend _Simd operator>>(const _Simd& __lhs, int __rhs) noexcept { return _Impl::__shift_right(__lhs.__s_, __rhs); } + + // compound assignment for integral _Tp [simd.cassign] + + friend _Simd& operator%=(_Simd& __lhs, const _Simd& __rhs) noexcept { return __lhs = __lhs % __rhs; } + + friend _Simd& operator&=(_Simd& __lhs, const _Simd& __rhs) noexcept { return __lhs = __lhs & __rhs; } + + friend _Simd& operator|=(_Simd& __lhs, const _Simd& __rhs) noexcept { return __lhs = __lhs | __rhs; } + + friend _Simd& operator^=(_Simd& __lhs, const _Simd& __rhs) noexcept { return __lhs = __lhs ^ __rhs; } + + friend _Simd& operator<<=(_Simd& __lhs, const _Simd& __rhs) noexcept { return __lhs = __lhs << __rhs; } + + friend _Simd& operator>>=(_Simd& __lhs, const _Simd& __rhs) noexcept { return __lhs = __lhs >> __rhs; } + + friend _Simd& operator<<=(_Simd& __lhs, int __rhs) noexcept { return __lhs = __lhs << __rhs; } + + friend _Simd& operator>>=(_Simd& __lhs, int __rhs) noexcept { return __lhs = __lhs >> __rhs; } +}; + +template +class simd : public __simd_int_operators, __simd_traits<_Tp, _Abi>, is_integral_v<_Tp>> { + using _Impl = __simd_traits<_Tp, _Abi>; + using _Storage = typename _Impl::_Simd; + + _Storage __s_; + + friend class simd_mask<_Tp, _Abi>; + friend class __simd_int_operators; + + template + friend class const_where_expression; + template + friend class where_expression; + + friend simd min<>(const simd&, const simd&) noexcept; + friend simd max<>(const simd&, const simd&) noexcept; + friend pair minmax<>(const simd&, const simd&) noexcept; + friend simd clamp<>(const simd&, const simd&, const simd&) noexcept; + friend _Tp hmin<>(const simd&) noexcept; + friend _Tp hmax<>(const simd&) noexcept; + + template + friend __Tp reduce(const simd<__Tp, __Abi>&, _BinaryOp); + + template + friend typename _Vp::value_type + reduce(const const_where_expression<_Mp, _Vp>& __w, typename _Vp::value_type __identity, _BinaryOp __op); + + template + friend typename _Vp::value_type hmin(const const_where_expression<_Mp, _Vp>& __w) noexcept; + + template + friend typename _Vp::value_type hmax(const const_where_expression<_Mp, _Vp>& __w) noexcept; + +public: + using value_type = _Tp; + using reference = __simd_reference<_Tp, _Storage, value_type>; + using mask_type = simd_mask<_Tp, _Abi>; + using abi_type = _Abi; + + simd() = default; + simd(const simd&) = default; + simd& operator=(const simd&) = default; + + simd(_Storage __s) : __s_(__s) {} + + static constexpr size_t size() noexcept { return simd_size_v; } + // implicit broadcast constructor - template ()>::type> - simd(_Up&& __rv) { - auto __v = static_cast<_Tp>(__rv); + template (), int> = 0> + simd(_Up&& __v) : __s_(_Impl::__broadcast(static_cast(__v))) {} + + // implicit type conversion constructor + template > && + __is_non_narrowing_arithmetic_convertible<_Up, value_type>()>> + simd(const simd<_Up, simd_abi::fixed_size>& __v) { for (size_t __i = 0; __i < size(); __i++) { - (*this)[__i] = __v; + (*this)[__i] = static_cast<_Tp>(__v[__i]); } } // generator constructor template (std::make_index_sequence()), - int>::type()> - explicit simd(_Generator&& __g) { - __generator_init(std::forward<_Generator>(__g), - std::make_index_sequence()); - } + enable_if_t<__can_generate(make_index_sequence()), int> = 0> + explicit simd(_Generator&& __g) : __s_(_Impl::__generate(std::forward<_Generator>(__g))) {} // load constructor - template < - class _Up, class _Flags, - class = typename std::enable_if<__vectorizable<_Up>()>::type, - class = typename std::enable_if::value>::type> - simd(const _Up* __buffer, _Flags) { - // TODO: optimize for overaligned flags - for (size_t __i = 0; __i < size(); __i++) { - (*this)[__i] = static_cast<_Tp>(__buffer[__i]); - } + template () && is_simd_flag_type_v<_Flags>>> + simd(const _Up* __mem, _Flags) { + _Impl::__load(__s_, _Flags::template __apply(__mem)); } // loads [simd.load] template - typename std::enable_if<__vectorizable<_Up>() && - is_simd_flag_type<_Flags>::value>::type - copy_from(const _Up* __buffer, _Flags) { - *this = simd(__buffer, _Flags()); + enable_if_t<__is_vectorizable<_Up>() && is_simd_flag_type_v<_Flags>> copy_from(const _Up* __mem, _Flags) { + _Impl::__load(__s_, _Flags::template __apply(__mem)); } // stores [simd.store] template - typename std::enable_if<__vectorizable<_Up>() && - is_simd_flag_type<_Flags>::value>::type - copy_to(_Up* __buffer, _Flags) const { - // TODO: optimize for overaligned flags - for (size_t __i = 0; __i < size(); __i++) { - __buffer[__i] = static_cast<_Up>((*this)[__i]); - } + enable_if_t<__is_vectorizable<_Up>() && is_simd_flag_type_v<_Flags>> copy_to(_Up* __mem, _Flags) const { + _Impl::__store(__s_, _Flags::template __apply(__mem)); } // scalar access [simd.subscr] - reference operator[](size_t __i) { return reference(&__s_, __i); } + reference operator[](size_t __i) { return reference(__s_, __i); } value_type operator[](size_t __i) const { return __s_.__get(__i); } // unary operators [simd.unary] - simd& operator++(); - simd operator++(int); - simd& operator--(); - simd operator--(int); - mask_type operator!() const; - simd operator~() const; - simd operator+() const; - simd operator-() const; - -#if 0 + simd& operator++() noexcept { + _Impl::__increment(__s_); + return *this; + } + + simd operator++(int) noexcept { + simd __r = *this; + _Impl::__increment(__s_); + return __r; + } + + simd& operator--() noexcept { + _Impl::__decrement(__s_); + return *this; + } + + simd operator--(int) noexcept { + simd __r = *this; + _Impl::__decrement(__s_); + return __r; + } + + mask_type operator!() const noexcept { return _Impl::__negate(__s_); } + + simd operator+() const noexcept { return *this; } + + simd operator-() const noexcept { return _Impl::__unary_minus(__s_); } + // binary operators [simd.binary] - friend simd operator+(const simd&, const simd&); - friend simd operator-(const simd&, const simd&); - friend simd operator*(const simd&, const simd&); - friend simd operator/(const simd&, const simd&); - friend simd operator%(const simd&, const simd&); - friend simd operator&(const simd&, const simd&); - friend simd operator|(const simd&, const simd&); - friend simd operator^(const simd&, const simd&); - friend simd operator<<(const simd&, const simd&); - friend simd operator>>(const simd&, const simd&); - friend simd operator<<(const simd&, int); - friend simd operator>>(const simd&, int); + friend simd operator+(const simd& __lhs, const simd& __rhs) noexcept { return _Impl::__plus(__lhs.__s_, __rhs.__s_); } + + friend simd operator-(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__minus(__lhs.__s_, __rhs.__s_); + } + + friend simd operator*(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__multiplies(__lhs.__s_, __rhs.__s_); + } + + friend simd operator/(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__divides(__lhs.__s_, __rhs.__s_); + } // compound assignment [simd.cassign] - friend simd& operator+=(simd&, const simd&); - friend simd& operator-=(simd&, const simd&); - friend simd& operator*=(simd&, const simd&); - friend simd& operator/=(simd&, const simd&); - friend simd& operator%=(simd&, const simd&); - - friend simd& operator&=(simd&, const simd&); - friend simd& operator|=(simd&, const simd&); - friend simd& operator^=(simd&, const simd&); - friend simd& operator<<=(simd&, const simd&); - friend simd& operator>>=(simd&, const simd&); - friend simd& operator<<=(simd&, int); - friend simd& operator>>=(simd&, int); + friend simd& operator+=(simd& __lhs, const simd& __rhs) noexcept { return __lhs = __lhs + __rhs; } + + friend simd& operator-=(simd& __lhs, const simd& __rhs) noexcept { return __lhs = __lhs - __rhs; } + + friend simd& operator*=(simd& __lhs, const simd& __rhs) noexcept { return __lhs = __lhs * __rhs; } + + friend simd& operator/=(simd& __lhs, const simd& __rhs) noexcept { return __lhs = __lhs / __rhs; } // compares [simd.comparison] - friend mask_type operator==(const simd&, const simd&); - friend mask_type operator!=(const simd&, const simd&); - friend mask_type operator>=(const simd&, const simd&); - friend mask_type operator<=(const simd&, const simd&); - friend mask_type operator>(const simd&, const simd&); - friend mask_type operator<(const simd&, const simd&); -#endif + friend mask_type operator==(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__equal_to(__lhs.__s_, __rhs.__s_); + } + + friend mask_type operator!=(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__not_equal_to(__lhs.__s_, __rhs.__s_); + } + + friend mask_type operator>=(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__less_equal(__rhs.__s_, __lhs.__s_); + } + + friend mask_type operator<=(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__less_equal(__lhs.__s_, __rhs.__s_); + } + + friend mask_type operator>(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__less(__rhs.__s_, __lhs.__s_); + } + + friend mask_type operator<(const simd& __lhs, const simd& __rhs) noexcept { + return _Impl::__less(__lhs.__s_, __rhs.__s_); + } }; -// [simd.mask.class] +// Class template simd_mask [parallel.simd.mask.class] template -// TODO: implement simd_mask class simd_mask { + using _Impl = __mask_traits<_Tp, _Abi>; + using _Storage = typename _Impl::_Mask; + + _Storage __s_; + + friend class simd<_Tp, _Abi>; + + template + friend class const_where_expression; + template + friend class where_expression; + + template + friend __Tp reduce(const simd<__Tp, __Abi>&, _BinaryOp); + + template + friend typename _Vp::value_type + reduce(const const_where_expression<_Mp, _Vp>& __w, typename _Vp::value_type __identity, _BinaryOp __op); + + friend bool all_of<>(const simd_mask&) noexcept; + friend bool any_of<>(const simd_mask&) noexcept; + friend bool none_of<>(const simd_mask&) noexcept; + friend bool some_of<>(const simd_mask&) noexcept; + friend int popcount<>(const simd_mask&) noexcept; + friend int find_first_set<>(const simd_mask&); + friend int find_last_set<>(const simd_mask&); + + template + friend typename _Vp::value_type hmin(const const_where_expression<_Mp, _Vp>& __w) noexcept; + + template + friend typename _Vp::value_type hmax(const const_where_expression<_Mp, _Vp>& __w) noexcept; + public: using value_type = bool; - // TODO: this is strawman implementation. Turn it into a proxy type. - using reference = bool&; - using simd_type = simd<_Tp, _Abi>; - using abi_type = _Abi; - static constexpr size_t size() noexcept; + using reference = __simd_reference<_Tp, _Storage, value_type>; + using simd_type = simd<_Tp, _Abi>; + using abi_type = _Abi; + + static constexpr size_t size() noexcept { return simd_type::size(); } + simd_mask() = default; + simd_mask(_Storage __s) : __s_(__s) {} + // broadcast constructor - explicit simd_mask(value_type) noexcept; + explicit simd_mask(value_type __v) noexcept : __s_(_Impl::__broadcast(__v)) {} // implicit type conversion constructor - template - simd_mask(const simd_mask<_Up, simd_abi::fixed_size>&) noexcept; + template > && + __is_non_narrowing_arithmetic_convertible<_Up, value_type>()>> + simd_mask(const simd_mask<_Up, simd_abi::fixed_size>& __v) noexcept { + for (size_t __i = 0; __i < size(); __i++) { + (*this)[__i] = static_cast<_Tp>(__v[__i]); + } + } // load constructor - template - simd_mask(const value_type*, _Flags); + template >> + simd_mask(const value_type* __mem, _Flags) { + _Impl::__load(__s_, _Flags::template __apply(__mem)); + } // loads [simd.mask.copy] template - void copy_from(const value_type*, _Flags); + enable_if_t> copy_from(const value_type* __mem, _Flags) { + _Impl::__load(__s_, _Flags::template __apply(__mem)); + } + template - void copy_to(value_type*, _Flags) const; + enable_if_t> copy_to(value_type* __mem, _Flags) const { + _Impl::__store(__s_, _Flags::template __apply(__mem)); + } // scalar access [simd.mask.subscr] - 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); } // unary operators [simd.mask.unary] - simd_mask operator!() const noexcept; + simd_mask operator!() const noexcept { return _Impl::__negate(__s_); } -#if 0 // simd_mask binary operators [simd.mask.binary] - friend simd_mask operator&&(const simd_mask&, const simd_mask&) noexcept; - friend simd_mask operator||(const simd_mask&, const simd_mask&) noexcept; - friend simd_mask operator&(const simd_mask&, const simd_mask&)noexcept; - friend simd_mask operator|(const simd_mask&, const simd_mask&) noexcept; - friend simd_mask operator^(const simd_mask&, const simd_mask&) noexcept; + friend simd_mask operator&&(const simd_mask& __lhs, const simd_mask& __rhs) noexcept { + return _Impl::__logical_and(__lhs.__s_, __rhs.__s_); + } + + friend simd_mask operator||(const simd_mask& __lhs, const simd_mask& __rhs) noexcept { + return _Impl::__logical_or(__lhs.__s_, __rhs.__s_); + } + + friend simd_mask operator&(const simd_mask& __lhs, const simd_mask& __rhs) noexcept { + return _Impl::__bitwise_and(__lhs.__s_, __rhs.__s_); + } + + friend simd_mask operator|(const simd_mask& __lhs, const simd_mask& __rhs) noexcept { + return _Impl::__bitwise_or(__lhs.__s_, __rhs.__s_); + } + + friend simd_mask operator^(const simd_mask& __lhs, const simd_mask& __rhs) noexcept { + return _Impl::__bitwise_xor(__lhs.__s_, __rhs.__s_); + } // simd_mask compound assignment [simd.mask.cassign] - friend simd_mask& operator&=(simd_mask&, const simd_mask&) noexcept; - friend simd_mask& operator|=(simd_mask&, const simd_mask&) noexcept; - friend simd_mask& operator^=(simd_mask&, const simd_mask&) noexcept; + friend simd_mask& operator&=(simd_mask& __lhs, const simd_mask& __rhs) noexcept { return __lhs = __lhs & __rhs; } + + friend simd_mask& operator|=(simd_mask& __lhs, const simd_mask& __rhs) noexcept { return __lhs = __lhs | __rhs; } + + friend simd_mask& operator^=(simd_mask& __lhs, const simd_mask& __rhs) noexcept { return __lhs = __lhs ^ __rhs; } // simd_mask compares [simd.mask.comparison] - friend simd_mask operator==(const simd_mask&, const simd_mask&) noexcept; - friend simd_mask operator!=(const simd_mask&, const simd_mask&) noexcept; -#endif + friend simd_mask operator==(const simd_mask& __lhs, const simd_mask& __rhs) noexcept { + return _Impl::__negate(_Impl::__bitwise_xor(__lhs.__s_, __rhs.__s_)); + } + + friend simd_mask operator!=(const simd_mask& __lhs, const simd_mask& __rhs) noexcept { + return _Impl::__bitwise_xor(__lhs.__s_, __rhs.__s_); + } }; #endif // _LIBCPP_STD_VER >= 17 @@ -1577,9 +2063,4 @@ _LIBCPP_POP_MACROS -#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20 -# include -# include -#endif - -#endif /* _LIBCPP_EXPERIMENTAL_SIMD */ +#endif // _LIBCPP_EXPERIMENTAL_SIMD diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/simd/abi.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/simd/abi.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/simd/abi.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__simd/abi/scalar.h'}} +#include <__simd/abi/scalar.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/simd/arch.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/simd/arch.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/simd/arch.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__simd/arch/config.h'}} +#include <__simd/arch/config.h> diff --git a/libcxx/test/std/experimental/simd/simd.abi/vector_extension.pass.cpp b/libcxx/test/std/experimental/simd/simd.abi/vector_extension.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.abi/vector_extension.pass.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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.abi] - -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -constexpr inline int reg_width() { -#if defined(__AVX__) - return 32; -#else - return 16; -#endif -} - -#ifndef _LIBCPP_HAS_NO_VECTOR_EXTENSION - -static_assert( - sizeof(ex::simd>) == 1, - ""); -static_assert( - sizeof(ex::simd>) == 2, - ""); -static_assert( - sizeof(ex::simd>) == 4, - ""); -static_assert( - sizeof(ex::simd>) == 16, - ""); -static_assert( - sizeof(ex::simd>) == - 16, - ""); -static_assert( - sizeof(ex::simd>) == - 32, - ""); -static_assert( - std::is_same, - ex::__simd_abi>::value, - ""); -#else -static_assert( - std::is_same, - ex::__simd_abi>::value, - ""); - -#endif - -static_assert(std::is_same, - ex::__simd_abi>::value, - ""); - -int main(int, char**) { - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.access/default.pass.cpp b/libcxx/test/std/experimental/simd/simd.access/default.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.access/default.pass.cpp +++ /dev/null @@ -1,220 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 - -// -// -// scalar access [simd.subscr] -// reference operator[](size_t); -// value_type operator[](size_t) const; - -#include -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -template -void test_access() { - { - SimdType a(42), b(4); - static_assert(std::is_convertible::value, ""); - - assert(a[0] == 42); - assert(!a[0] == !42); - assert(~a[0] == ~42); - assert(+a[0] == +42); - assert(-a[0] == -42); - assert(a[0] + b[0] == 42 + 4); - assert(a[0] - b[0] == 42 - 4); - assert(a[0] * b[0] == 42 * 4); - assert(a[0] / b[0] == 42 / 4); - assert(a[0] % b[0] == 42 % 4); - assert(a[0] << b[0] == (42 << 4)); - assert(a[0] >> b[0] == (42 >> 4)); - assert((a[0] < b[0]) == false); - assert((a[0] <= b[0]) == false); - assert((a[0] > b[0]) == true); - assert((a[0] >= b[0]) == true); - assert((a[0] == b[0]) == false); - assert((a[0] != b[0]) == true); - assert((a[0] & b[0]) == (42 & 4)); - assert((a[0] | b[0]) == (42 | 4)); - assert((a[0] ^ b[0]) == (42 ^ 4)); - assert((a[0] && b[0]) == true); - assert((a[0] || b[0]) == true); - - { - auto c = a; - ++c[0]; - assert(c[0] == 42 + 1); - assert(c[1] == 42); - } - { - auto c = a; - auto ret = c[0]++; - assert(ret == 42); - assert(c[0] == 42 + 1); - assert(c[1] == 42); - } - { - auto c = a; - --c[0]; - assert(c[0] == 42 - 1); - assert(c[1] == 42); - } - { - auto c = a; - auto ret = c[0]--; - assert(ret == 42); - assert(c[0] == 42 - 1); - assert(c[1] == 42); - } - - { - auto c = a; - c[0] += b[0]; - assert(c[0] == 42 + 4); - assert(c[1] == 42); - } - { - auto c = a; - c[0] -= b[0]; - assert(c[0] == 42 - 4); - assert(c[1] == 42); - } - { - auto c = a; - c[0] *= b[0]; - assert(c[0] == 42 * 4); - assert(c[1] == 42); - } - { - auto c = a; - c[0] /= b[0]; - assert(c[0] == 42 / 4); - assert(c[1] == 42); - } - { - auto c = a; - c[0] %= b[0]; - assert(c[0] == 42 % 4); - assert(c[1] == 42); - } - { - auto c = a; - c[0] >>= b[0]; - assert(c[0] == (42 >> 4)); - assert(c[1] == 42); - } - { - auto c = a; - c[0] <<= b[0]; - assert(c[0] == (42 << 4)); - assert(c[1] == 42); - } - { - auto c = a; - c[0] &= b[0]; - assert(c[0] == (42 & 4)); - assert(c[1] == 42); - } - { - auto c = a; - c[0] |= b[0]; - assert(c[0] == (42 | 4)); - assert(c[1] == 42); - } - { - auto c = a; - c[0] ^= b[0]; - assert(c[0] == (42 ^ 4)); - assert(c[1] == 42); - } - - { - auto c = a; - (void)(a[0] + (c[0] += a[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] -= a[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] *= a[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] /= a[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] %= a[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] >>= b[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] <<= b[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] &= a[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] |= a[0])); - } - { - auto c = a; - (void)(a[0] + (c[0] ^= a[0])); - } - } - { - const SimdType a(42); - const SimdType b(4); - static_assert(std::is_same::value, ""); - - assert(a[0] == 42); - assert(!a[0] == !42); - assert(~a[0] == ~42); - assert(+a[0] == +42); - assert(-a[0] == -42); - assert(a[0] + b[0] == 42 + 4); - assert(a[0] - b[0] == 42 - 4); - assert(a[0] * b[0] == 42 * 4); - assert(a[0] / b[0] == 42 / 4); - assert(a[0] % b[0] == 42 % 4); - assert(a[0] << b[0] == (42 << 4)); - assert(a[0] >> b[0] == (42 >> 4)); - assert((a[0] < b[0]) == false); - assert((a[0] <= b[0]) == false); - assert((a[0] > b[0]) == true); - assert((a[0] >= b[0]) == true); - assert((a[0] == b[0]) == false); - assert((a[0] != b[0]) == true); - assert((a[0] & b[0]) == (42 & 4)); - assert((a[0] | b[0]) == (42 | 4)); - assert((a[0] ^ b[0]) == (42 ^ 4)); - assert((a[0] && b[0]) == true); - assert((a[0] || b[0]) == true); - } -} - -int main(int, char**) { - test_access>(); - test_access>(); - - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.casts/concat.pass.cpp b/libcxx/test/std/experimental/simd/simd.casts/concat.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.casts/concat.pass.cpp @@ -0,0 +1,152 @@ +//===----------------------------------------------------------------------===// +// +// 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.casts] +// template +// simd + ...)>> concat(const simd&...) noexcept; +// template +// simd_mask + ...)>> concat(const simd_mask&...) noexcept; +// template +// resize_simd * N, simd> concat(const array, N>& arr) noexcept; +// template +// resize_simd * N, simd_mask> concat(const array, N>& arr) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckConcatSimd { + template + void operator()() { + constexpr size_t concat_length = + ex::simd_size_v<_Tp, ex::simd_abi::native<_Tp>> + ex::simd_size_v<_Tp, ex::simd_abi::fixed_size<_Np>> + + ex::simd_size_v<_Tp, SimdAbi>; + if constexpr (concat_length <= max_simd_size) { + ex::native_simd<_Tp> native_simd_([](_Tp i) { return i; }); + ex::fixed_size_simd<_Tp, _Np> fixed_size_simd_([](_Tp i) { return static_cast<_Tp>(_Np - i); }); + + ex::simd<_Tp, SimdAbi> full_type_simd_([](_Tp i) { return i; }); + + using concat_result_type = ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, concat_length>>; + + auto concat_result = ex::concat<_Tp, ex::simd_abi::native<_Tp>, ex::simd_abi::fixed_size<_Np>, SimdAbi>( + native_simd_, fixed_size_simd_, full_type_simd_); + static_assert(ex::is_simd_v); + static_assert(std::is_same_v); + + size_t k = 0; + for (size_t i = 0; i < native_simd_.size(); ++i, ++k) + assert(concat_result[k] == native_simd_[i]); + + for (size_t i = 0; i < fixed_size_simd_.size(); ++i, ++k) + assert(concat_result[k] == fixed_size_simd_[i]); + + for (size_t i = 0; i < full_type_simd_.size(); ++i, ++k) + assert(concat_result[k] == full_type_simd_[i]); + } + } +}; + +struct CheckConcatSimdMask { + template + void operator()() { + constexpr size_t concat_length = + ex::simd_size_v<_Tp, ex::simd_abi::native<_Tp>> + ex::simd_size_v<_Tp, ex::simd_abi::fixed_size<_Np>> + + ex::simd_size_v<_Tp, SimdAbi>; + if constexpr (concat_length <= max_simd_size) { + ex::native_simd_mask<_Tp> native_simd_mask_([](_Tp i) { return i; }); + ex::fixed_size_simd_mask<_Tp, _Np> fixed_size_simd_mask_([](_Tp i) { return static_cast<_Tp>(_Np - i); }); + + ex::simd_mask<_Tp, SimdAbi> full_type_simd_mask_([](_Tp i) { return i; }); + using concat_result_type = ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, concat_length>>; + auto concat_result = ex::concat<_Tp, ex::simd_abi::native<_Tp>, ex::simd_abi::fixed_size<_Np>, SimdAbi>( + native_simd_mask_, fixed_size_simd_mask_, full_type_simd_mask_); + static_assert(ex::is_simd_mask_v); + static_assert(std::is_same_v); + + size_t k = 0; + for (size_t i = 0; i < native_simd_mask_.size(); ++i, ++k) + assert(concat_result[k] == native_simd_mask_[i]); + + for (size_t i = 0; i < fixed_size_simd_mask_.size(); ++i, ++k) + assert(concat_result[k] == fixed_size_simd_mask_[i]); + + for (size_t i = 0; i < full_type_simd_mask_.size(); ++i, ++k) + assert(concat_result[k] == full_type_simd_mask_[i]); + } + } +}; + +struct CheckConcatResizeSimd { + template + void operator()() { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> * _Np <= max_simd_size) { + std::array, _Np> arr; + for (auto& sub_simd : arr) { + for (size_t i = 0; i < sub_simd.size(); ++i) { + sub_simd[i] = static_cast<_Tp>(i); + } + } + + using concat_result_type = ex::resize_simd_t * _Np, ex::simd<_Tp, SimdAbi>>; + auto concat_result = ex::concat<_Tp, SimdAbi, _Np>(arr); + + static_assert(ex::is_simd_v); + static_assert(std::is_same_v); + + constexpr size_t prev_length = ex::simd_size_v<_Tp, SimdAbi>; + + for (size_t i = 0; i < concat_result.size(); ++i) + assert(concat_result[i] == arr[i / prev_length][i % prev_length]); + } + } +}; + +struct CheckConcatResizeSimdMask { + template + void operator()() { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> * _Np <= max_simd_size) { + std::array, _Np> arr; + for (auto& sub_simd_mask : arr) { + for (size_t i = 0; i < sub_simd_mask.size(); ++i) { + sub_simd_mask[i] = static_cast(i); + } + } + + using concat_result_type = ex::resize_simd_t * _Np, ex::simd_mask<_Tp, SimdAbi>>; + auto concat_result = ex::concat<_Tp, SimdAbi, _Np>(arr); + + static_assert(ex::is_simd_mask_v); + static_assert(std::is_same_v); + + constexpr size_t prev_length = ex::simd_size_v<_Tp, SimdAbi>; + + for (size_t i = 0; i < concat_result.size(); ++i) + assert(concat_result[i] == arr[i / prev_length][i % prev_length]); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi, _Np>(); + test_simd_abi(); +} +int main() { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.casts/simd_cast.pass.cpp b/libcxx/test/std/experimental/simd/simd.casts/simd_cast.pass.cpp --- a/libcxx/test/std/experimental/simd/simd.casts/simd_cast.pass.cpp +++ b/libcxx/test/std/experimental/simd/simd.casts/simd_cast.pass.cpp @@ -11,39 +11,170 @@ // // // [simd.casts] -// template see below ex::simd_cast<(const -// ex::simd&); +// template see below ex::simd_cast<(const ex::simd&); +#include "../test_utils.h" #include -#include - -#include "test_macros.h" namespace ex = std::experimental::parallelism_v2; -static_assert( - std::is_same(ex::native_simd())), - ex::native_simd>::value, - ""); - -static_assert(std::is_same( - ex::fixed_size_simd())), - ex::fixed_size_simd>::value, - ""); - -static_assert( - std::is_same>( - ex::simd())), - ex::fixed_size_simd>::value, - ""); - -static_assert( - std::is_same< - decltype(ex::simd_cast>( - ex::fixed_size_simd())), - ex::simd>::value, - ""); - -int main(int, char**) { - return 0; +// 3. test for the final case +#define GNERATOR_FULL_TYPE(TYPE) \ + if constexpr (!std::is_same_v<_Tp, TYPE>) { \ + ex::simd<_Tp, SimdAbi> origin([](_Tp i) { return i; }); \ + auto after_cast = ex::simd_cast(origin); \ + static_assert(std::is_same_v::size()>>>); \ + std::array<_Tp, ex::simd_size_v<_Tp, SimdAbi>> expected_values; \ + for (size_t i = 0; i < origin.size(); ++i) \ + expected_values[i] = static_cast<_Tp>(i); \ + assert_simd_value_correct(after_cast, expected_values); \ + } +struct CheckSimdCast { + template + void operator()() { + GNERATOR_FULL_TYPE(long double) + GNERATOR_FULL_TYPE(double) + GNERATOR_FULL_TYPE(float) + GNERATOR_FULL_TYPE(long long) + GNERATOR_FULL_TYPE(unsigned long long) + GNERATOR_FULL_TYPE(long) + GNERATOR_FULL_TYPE(unsigned long) + GNERATOR_FULL_TYPE(int) + GNERATOR_FULL_TYPE(unsigned int) + GNERATOR_FULL_TYPE(short) + GNERATOR_FULL_TYPE(unsigned short) + GNERATOR_FULL_TYPE(wchar_t) + GNERATOR_FULL_TYPE(signed char) + GNERATOR_FULL_TYPE(unsigned char) + GNERATOR_FULL_TYPE(char32_t) + GNERATOR_FULL_TYPE(char16_t) + + // 2. test for the `std::is_same_v` + { + ex::simd<_Tp, SimdAbi> origin([](_Tp i) { return i; }); + auto after_cast = ex::simd_cast<_Tp>(origin); + if constexpr (std::is_same_v) { + static_assert(std::is_same_v>); + } else { + static_assert(std::is_same_v::size()>>>); + } + std::array<_Tp, ex::simd_size_v<_Tp, SimdAbi>> expected_values; + for (size_t i = 0; i < origin.size(); ++i) + expected_values[i] = static_cast<_Tp>(i); + assert_simd_value_correct(after_cast, expected_values); + } + + constexpr std::size_t origin_full_abi_simd_size = ex::simd<_Tp, SimdAbi>::size(); + static auto InitializeSimd = [](auto& origin_simd) { + const size_t simd_size = origin_simd.size(); + for (size_t i = 0; i < simd_size; ++i) + origin_simd[i] = static_cast<_Tp>(i); + }; + std::array<_Tp, origin_full_abi_simd_size> expected_values; + for (size_t i = 0; i < origin_full_abi_simd_size; ++i) + expected_values[i] = static_cast<_Tp>(i); + + // 1. test for the `ex::is_simd_v` + if constexpr (origin_full_abi_simd_size == ex::native_simd<_Tp>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>(origin); + static_assert(std::is_same_v>); + assert_simd_value_correct(after_cast, expected_values); + } + + { + auto origin = ex::native_simd<_Tp>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>(origin); + static_assert(std::is_same_v>); + assert_simd_value_correct(after_cast, expected_values); + } + } + + if constexpr (origin_full_abi_simd_size == ex::fixed_size_simd<_Tp, _Np>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>(origin); + static_assert(std::is_same_v>); + assert_simd_value_correct(after_cast, expected_values); + } + + { + auto origin = ex::fixed_size_simd<_Tp, _Np>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>(origin); + static_assert(std::is_same_v>); + assert_simd_value_correct(after_cast, expected_values); + } + } + + if constexpr (origin_full_abi_simd_size == ex::simd<_Tp, ex::simd_abi::scalar>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>(origin); + static_assert(std::is_same_v>); + assert_simd_value_correct(after_cast, expected_values); + } + + { + auto origin = ex::simd<_Tp, ex::simd_abi::scalar>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>(origin); + static_assert(std::is_same_v>); + assert_simd_value_correct(after_cast, expected_values); + } + } + + if constexpr (origin_full_abi_simd_size == ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, _Np + 1>>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>>(origin); + static_assert(std::is_same_v>>); + assert_simd_value_correct(after_cast, expected_values); + } + + { + auto origin = ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, _Np + 1>>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>(origin); + static_assert(std::is_same_v>); + assert_simd_value_correct(after_cast, expected_values); + } + } + + if constexpr (origin_full_abi_simd_size == ex::simd<_Tp, ex::simd_abi::compatible<_Tp>>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>>(origin); + static_assert(std::is_same_v>>); + assert_simd_value_correct(after_cast, expected_values); + } + + { + auto origin = ex::simd<_Tp, ex::simd_abi::compatible<_Tp>>(); + InitializeSimd(origin); + auto after_cast = ex::simd_cast>(origin); + static_assert(std::is_same_v>); + assert_simd_value_correct(after_cast, expected_values); + } + } + } +}; + +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(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.casts/split.pass.cpp b/libcxx/test/std/experimental/simd/simd.casts/split.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.casts/split.pass.cpp @@ -0,0 +1,311 @@ +//===----------------------------------------------------------------------===// +// +// 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.casts] +// template +// tuple>...> split(const simd&) noexcept; + +// template +// tuple>...> split(const simd_mask&) noexcept; + +// template +// array / V::size()> split(const simd&) noexcept; + +// template +// array / V::size()> +// split(const simd_mask&) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +// TODO: implement call in utility.h? or replace with std::apply later +template +[[maybe_unused]] void call_tuple(Tuple& t, Func&& func, std::index_sequence) { + static_cast(std::initializer_list{(func(std::get(t)), 0)...}); +} + +template +[[maybe_unused]] void for_tuple(std::tuple& t, Func&& func) { + call_tuple(t, std::forward(func), std::make_index_sequence{}); +} + +struct CheckSplitToArray { + template + void operator()() { + using simd_type_ = ex::simd<_Tp, SimdAbi>; + using simd_mask_type_ = ex::simd_mask<_Tp, SimdAbi>; + using target_simd_abi = ex::simd_abi::fixed_size; + constexpr size_t origin_simd_size = simd_type_::size(); + constexpr size_t origin_simd_mask_size = simd_mask_type_::size(); + + { + if constexpr (ex::simd_size_v<_Tp, target_simd_abi> % origin_simd_size == 0) { + constexpr size_t array_count = ex::simd_size_v<_Tp, target_simd_abi> / origin_simd_size; + using split_result_type = std::array; + + const ex::simd<_Tp, target_simd_abi> origin_simd([](_Tp i) { return i; }); + auto split_result = split(origin_simd); + static_assert(std::is_same_v); + + std::array, array_count> expected_value; + for (size_t i = 0, idx = 0; i < expected_value.size(); ++i) { + for (size_t j = 0; j < expected_value[i].size(); ++j) { + expected_value[i][j] = origin_simd[idx++]; + } + } + + for (size_t i = 0; i < split_result.size(); ++i) { + assert(ex::is_simd_v>); + assert_simd_value_correct(split_result[i], expected_value[i]); + } + } + } + + { + if constexpr (ex::simd_size_v<_Tp, target_simd_abi> % origin_simd_mask_size == 0) { + constexpr size_t array_count = ex::simd_size_v<_Tp, target_simd_abi> / origin_simd_mask_size; + using split_result_type = std::array; + + ex::simd_mask<_Tp, target_simd_abi> origin_simd_mask; + + for (size_t i = 0; i < origin_simd_mask_size;) { + origin_simd_mask[i++] = 0; + if (i + 1 < origin_simd_mask_size) { + origin_simd_mask[i + 1] = 1; + i++; + } + } + auto split_result = split(origin_simd_mask); + static_assert(std::is_same_v); + + std::array, array_count> expected_value; + for (size_t i = 0, idx = 0; i < expected_value.size(); ++i) { + for (size_t j = 0; j < expected_value[i].size(); ++j) { + expected_value[i][j] = origin_simd_mask[idx++]; + } + } + + for (size_t i = 0; i < split_result.size(); ++i) { + assert(ex::is_simd_mask_v>); + assert_simd_mask_value_correct(split_result[i], expected_value[i]); + } + } + } + } +}; + +struct CheckSplitToTuple { + template + void operator()() { + static auto initialize_mask = [](auto& origin_mask) { + origin_mask[0] = 0; + for (std::size_t i = 1; i < origin_mask.size(); ++i) { + origin_mask[i] = 1; + } + }; + if constexpr (_Np == 1) { + { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 1); }); + + auto temp = ex::split<1>(origin_simd); + static_assert(std::is_same_v>>>); + size_t idx = 0; + + std::apply( + [origin_simd, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) { + assert(item[i] == origin_simd[idx]); + ++idx; + } + }, + temp); + } + { + ex::simd_mask<_Tp, SimdAbi> origin_mask; + initialize_mask(origin_mask); + + auto temp = ex::split<1>(origin_mask); + static_assert(std::is_same_v>>>); + size_t idx = 0; + + std::apply( + [origin_mask, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) { + assert(item[i] == origin_mask[idx]); + ++idx; + } + }, + temp); + } + + } else if constexpr (_Np == 4) { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 1); }); + ex::simd_mask<_Tp, SimdAbi> origin_mask; + initialize_mask(origin_mask); + + static const auto split22_to_simd = [](const auto& origin_simd) { + auto temp = ex::split<2, 2>(origin_simd); + + static_assert(std::is_same_v>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, 2>>>>); + size_t idx = 0; + for_tuple(temp, [origin_simd, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) + assert(item[i] == origin_simd[idx++]); + }); + }; + static const auto split22_to_simd_mask = [](const auto& origin_simd_mask) { + auto temp = ex::split<2, 2>(origin_simd_mask); + + static_assert(std::is_same_v>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, 2>>>>); + size_t idx = 0; + for_tuple(temp, [origin_simd_mask, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) + assert(item[i] == origin_simd_mask[idx++]); + }); + }; + static const auto split13_to_simd = [](const auto& origin_simd) { + auto temp = ex::split<1, 3>(origin_simd); + static_assert(std::is_same_v>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, 3>>>>); + size_t idx = 0; + for_tuple(temp, [origin_simd, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) + assert(item[i] == origin_simd[idx++]); + }); + }; + static const auto split13_to_simd_mask = [](const auto& origin_simd_mask) { + auto temp = ex::split<1, 3>(origin_simd_mask); + static_assert(std::is_same_v>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, 3>>>>); + size_t idx = 0; + for_tuple(temp, [origin_simd_mask, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) + assert(item[i] == origin_simd_mask[idx++]); + }); + }; + static const auto split31_to_simd = [](const auto& origin_simd) { + auto temp = ex::split<3, 1>(origin_simd); + static_assert(std::is_same_v>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, 1>>>>); + size_t idx = 0; + for_tuple(temp, [origin_simd, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) + assert(item[i] == origin_simd[idx++]); + }); + }; + static const auto split31_to_simd_mask = [](const auto& origin_simd_mask) { + auto temp = ex::split<3, 1>(origin_simd_mask); + static_assert(std::is_same_v>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, 1>>>>); + size_t idx = 0; + for_tuple(temp, [origin_simd_mask, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) + assert(item[i] == origin_simd_mask[idx++]); + }); + }; + static const auto split112_to_simd = [](const auto& origin_simd) { + auto temp = ex::split<1, 1, 2>(origin_simd); + static_assert(std::is_same_v>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, 1>>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, 2>> >>); + size_t idx = 0; + for_tuple(temp, [origin_simd, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) + assert(item[i] == origin_simd[idx++]); + }); + }; + static const auto split112_to_simd_mask = [](const auto& origin_simd_mask) { + auto temp = ex::split<1, 1, 2>(origin_simd_mask); + static_assert(std::is_same_v>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, 1>>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, 2>> >>); + size_t idx = 0; + for_tuple(temp, [origin_simd_mask, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) + assert(item[i] == origin_simd_mask[idx++]); + }); + }; + static const auto split1111_to_simd = [](const auto& origin_simd) { + auto temp = ex::split<1, 1, 1, 1>(origin_simd); + static_assert(std::is_same_v>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, 1>>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, 1>>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, 1>> >>); + size_t idx = 0; + for_tuple(temp, [origin_simd, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) { + assert(item[i] == origin_simd[idx++]); + } + }); + }; + static const auto split1111_to_simd_mask = [](const auto& origin_simd_mask) { + auto temp = ex::split<1, 1, 1, 1>(origin_simd_mask); + static_assert(std::is_same_v>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, 1>>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, 1>>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, 1>> >>); + size_t idx = 0; + for_tuple(temp, [origin_simd_mask, &idx](auto&& item) { + for (size_t i = 0; i < item.size(); ++i) { + assert(item[i] == origin_simd_mask[idx++]); + } + }); + }; + //split22(origin_mask, false); + split22_to_simd(origin_simd); + split22_to_simd_mask(origin_mask); + split13_to_simd(origin_simd); + split13_to_simd_mask(origin_mask); + split31_to_simd(origin_simd); + split31_to_simd_mask(origin_mask); + split112_to_simd(origin_simd); + split112_to_simd_mask(origin_mask); + split1111_to_simd(origin_simd); + split1111_to_simd_mask(origin_mask); + } else { // pass if simd_size not equal 1 and 4 + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + if constexpr (std::is_same_v) { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> <= 4) { + F{}.template operator()<_Tp, SimdAbi, ex::simd_size_v<_Tp, SimdAbi>>(); + test_simd_abi(); + } + } else { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); + } +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.casts/split_by.pass.cpp b/libcxx/test/std/experimental/simd/simd.casts/split_by.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.casts/split_by.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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.casts] +// template +// array / N, simd>, N> split_by(const simd& x) noexcept; +// template +// array / N, simd_mask>, N> split_by(const simd_mask& x) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSplitBySimd { + template + void operator()() { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> % _Np == 0) { + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + auto arr = ex::split_by<_Np>(origin_simd); + static_assert(std::is_same_v< + decltype(arr), + std::array / _Np, ex::simd<_Tp, SimdAbi>>, _Np> >); + + for (size_t i = 0; i < _Np; ++i) { + for (size_t j = 0; j < arr[i].size(); ++j) { + assert(arr[i][j] == origin_simd[i * (ex::simd_size_v<_Tp, SimdAbi> / _Np) + j]); + } + } + } + } +}; + +struct CheckSplitBySimdMask { + template + void operator()() { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> % _Np == 0) { + const ex::simd_mask<_Tp, SimdAbi> origin_simd([](bool i) { return i; }); + auto arr = ex::split_by<_Np>(origin_simd); + static_assert( + std::is_same_v< + decltype(arr), + std::array / _Np, ex::simd_mask<_Tp, SimdAbi>>, _Np> >); + + for (size_t i = 0; i < _Np; ++i) { + for (size_t j = 0; j < arr[i].size(); ++j) { + assert(arr[i][j] == origin_simd[i * (ex::simd_size_v<_Tp, SimdAbi> / _Np) + j]); + } + } + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi, _Np>(); + test_simd_abi(); +} +int main() { + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.casts/static_simd_cast.pass.cpp b/libcxx/test/std/experimental/simd/simd.casts/static_simd_cast.pass.cpp --- a/libcxx/test/std/experimental/simd/simd.casts/static_simd_cast.pass.cpp +++ b/libcxx/test/std/experimental/simd/simd.casts/static_simd_cast.pass.cpp @@ -14,31 +14,219 @@ // template see below ex::static_simd_cast<(const // ex::simd&); +#include "../test_utils.h" #include -#include - -#include "test_macros.h" namespace ex = std::experimental::parallelism_v2; -static_assert( - std::is_same(ex::native_simd())), - ex::native_simd>::value, - ""); - -static_assert( - std::is_same>( - ex::simd())), - ex::fixed_size_simd>::value, - ""); - -static_assert( - std::is_same< - decltype(ex::static_simd_cast>( - ex::fixed_size_simd())), - ex::simd>::value, - ""); - -int main(int, char**) { - return 0; +#define GNERATOR_FULL_TYPE(TYPE) \ + if constexpr (!std::is_same_v<_Tp, TYPE>) { \ + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); \ + auto after_cast = ex::static_simd_cast(origin_simd); \ + static_assert(std::is_same_v< ex::simd::size()>>, \ + decltype(after_cast)>); \ + std::array<_Tp, origin_simd.size()> expected_value; \ + for (size_t i = 0; i < expected_value.size(); ++i) \ + expected_value[i] = origin_simd[i]; \ + assert_simd_value_correct(after_cast, expected_value); \ + } + +// 2.2. test for the `std::make_signed_t` same as `std::make_signed_t` +#define GNERATOR_FULL_TYPE_SAME_SIGNED(TYPE) \ + \ + if constexpr (!std::is_same_v && std::is_integral_v<_Tp> && \ + std::is_integral_v) { \ + if constexpr (std::is_same_v, std::make_signed_t>) { \ + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); \ + auto after_cast = ex::static_simd_cast(origin_simd); \ + static_assert(std::is_same_v< ex::simd::size()>>, \ + decltype(after_cast)>); \ + std::array<_Tp, origin_simd.size()> expected_value; \ + for (size_t i = 0; i < expected_value.size(); ++i) \ + expected_value[i] = origin_simd[i]; \ + assert_simd_value_correct(after_cast, expected_value); \ + } \ + } + +#define SHORT_TYPE(F) \ + F(long double) \ + F(double) \ + F(float) \ + F(long long) \ + F(unsigned long long) \ + F(long) \ + F(unsigned long) \ + F(int) F(unsigned int) F(short) F(unsigned short) F(wchar_t) F(signed char) F(unsigned char) F(char32_t) F(char16_t) + +// `std::make_signed_t` require `no float`, otherwise fail on `preprocessor stage` see also: https://cppinsights.io/s/aed94d59 +#define SHORT_TYPE_NO_FLOAT(F) \ + F(long long) \ + F(unsigned long long) \ + F(long) \ + F(unsigned long) \ + F(int) F(unsigned int) F(short) F(unsigned short) F(wchar_t) F(signed char) F(unsigned char) F(char32_t) F(char16_t) + +struct CheckStaticSimdCast { + template + void operator()() { + constexpr size_t array_size = ex::simd_size_v<_Tp, SimdAbi>; + SHORT_TYPE(GNERATOR_FULL_TYPE) + SHORT_TYPE_NO_FLOAT(GNERATOR_FULL_TYPE_SAME_SIGNED) + // 2.1. test for the `std::is_same_v` + { + auto after_cast = ex::static_simd_cast<_Tp>(ex::simd<_Tp, SimdAbi>([](_Tp i) { return i; })); + static_assert(std::is_same_v, decltype(after_cast)>); + 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(after_cast, expected_value); + } + + constexpr std::size_t origin_full_abi_simd_size = ex::simd<_Tp, SimdAbi>::size(); + static auto InitializeSimd = [](auto& origin_simd) { + const size_t simd_size = origin_simd.size(); + for (size_t i = 0; i < simd_size; ++i) + origin_simd[i] = static_cast<_Tp>(i + 1); + }; + // 1. test for the `ex::is_simd_v` + if constexpr (origin_full_abi_simd_size == ex::native_simd<_Tp>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>(origin); + + static_assert(std::is_same_v, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + + { + auto origin = ex::native_simd<_Tp>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>(origin); + + static_assert(std::is_same_v, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + } + + if constexpr (origin_full_abi_simd_size == ex::fixed_size_simd<_Tp, _Np>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>(origin); + + static_assert(std::is_same_v, decltype(after_cast)>); + std::array<_Tp, origin.size()> expected_value; + for (size_t i = 0; i < origin.size(); ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + + { + auto origin = ex::fixed_size_simd<_Tp, _Np>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>(origin); + + static_assert(std::is_same_v, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + } + + if constexpr (origin_full_abi_simd_size == ex::simd<_Tp, ex::simd_abi::scalar>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>(origin); + + static_assert(std::is_same_v, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + + { + auto origin = ex::simd<_Tp, ex::simd_abi::scalar>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>(origin); + + static_assert(std::is_same_v, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + } + + if constexpr (origin_full_abi_simd_size == ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, _Np + 1>>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>>(origin); + + static_assert(std::is_same_v>, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + + { + auto origin = ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, _Np + 1>>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>(origin); + + static_assert(std::is_same_v, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + } + + if constexpr (origin_full_abi_simd_size == ex::simd<_Tp, ex::simd_abi::compatible<_Tp>>::size()) { + { + auto origin = ex::simd<_Tp, SimdAbi>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>>(origin); + + static_assert(std::is_same_v>, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, expected_value); + } + + { + auto origin = ex::simd<_Tp, ex::simd_abi::compatible<_Tp>>(); + InitializeSimd(origin); + auto after_cast = ex::static_simd_cast>(origin); + + static_assert(std::is_same_v, decltype(after_cast)>); + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i + 1); + assert_simd_value_correct(after_cast, 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(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.casts/to_compatible.pass.cpp b/libcxx/test/std/experimental/simd/simd.casts/to_compatible.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.casts/to_compatible.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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.casts] +// template simd to_compatible(const fixed_size_simd&) noexcept; +// template simd_mask to_compatible(const fixed_size_simd_mask&) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckToCompatibleSimd { + template + void operator()() { + static_assert( + std::is_same_v(ex::fixed_size_simd<_Tp, _Np>())), ex::simd<_Tp>>, ""); + static auto InitializeSimd = [](auto& origin_simd) { + const size_t simd_size = origin_simd.size(); + for (size_t i = 0; i < simd_size; ++i) + origin_simd[i] = i; + }; + + { + auto origin = ex::fixed_size_simd<_Tp, _Np>(); + InitializeSimd(origin); + auto after_cast = ex::to_compatible<_Tp, _Np>(origin); + std::array<_Tp, origin.size()> expected_values; + for (size_t i = 0; i < origin.size(); ++i) + expected_values[i] = static_cast<_Tp>(i); + assert_simd_value_correct(after_cast, expected_values); + } + } +}; +struct CheckToCompatibleSimdMask { + template + void operator()() { + static_assert( + std::is_same_v(ex::fixed_size_simd<_Tp, _Np>())), ex::simd<_Tp>>, ""); + static auto InitializeSimdMask = [](auto& origin_simd_mask) { + const size_t mask_size = origin_simd_mask.size(); + for (size_t i = 0; i < mask_size; ++i) + origin_simd_mask[i] = static_cast(i); + }; + + { + auto origin = ex::fixed_size_simd_mask<_Tp, _Np>(); + InitializeSimdMask(origin); + auto after_cast = ex::to_compatible<_Tp, _Np>(origin); + std::array<_Tp, origin.size()> expected_values; + for (size_t i = 0; i < origin.size(); ++i) + expected_values[i] = static_cast(i); + assert_simd_mask_value_correct(after_cast, expected_values); + } + } +}; +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi, ex::simd_size_v<_Tp, ex::simd_abi::compatible<_Tp>>>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.casts/to_fixed_size.pass.cpp b/libcxx/test/std/experimental/simd/simd.casts/to_fixed_size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.casts/to_fixed_size.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// 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.casts] +// template +// fixed_size_simd> to_fixed_size(const simd&) noexcept; +// template +// fixed_size_simd_mask> to_fixed_size(const simd_mask&) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckToFixedSizeSimd { + template + void operator()() { + static auto InitializeSimd = [](auto& origin_simd) { + const size_t simd_size = origin_simd.size(); + for (size_t i = 0; i < simd_size; ++i) + origin_simd[i] = static_cast<_Tp>(i); + }; + { + ex::simd<_Tp, SimdAbi> origin; + InitializeSimd(origin); + auto after_cast = ex::to_fixed_size<_Tp, SimdAbi>(origin); + static_assert(std::is_same_v< ex::fixed_size_simd<_Tp, ex::simd_size_v<_Tp, SimdAbi>>, decltype(after_cast)>, ""); + std::array<_Tp, origin.size()> expected_values; + for (size_t i = 0; i < origin.size(); ++i) + expected_values[i] = static_cast<_Tp>(i); + assert_simd_value_correct(after_cast, expected_values); + } + } +}; +struct CheckToFixedSizeSimdMask { + template + void operator()() { + static auto InitializeSimdMask = [](auto& origin_simd_mask) { + const size_t mask_size = origin_simd_mask.size(); + for (size_t i = 0; i < mask_size; ++i) + origin_simd_mask[i] = static_cast(i); + }; + { + ex::simd_mask<_Tp, SimdAbi> origin{}; + InitializeSimdMask(origin); + auto after_cast = ex::to_fixed_size<_Tp, SimdAbi>(origin); + static_assert( + std::is_same_v< ex::fixed_size_simd_mask<_Tp, ex::simd_size_v<_Tp, SimdAbi>>, decltype(after_cast)>, ""); + std::array<_Tp, origin.size()> expected_values; + for (size_t i = 0; i < origin.size(); ++i) + expected_values[i] = static_cast(i); + assert_simd_mask_value_correct(after_cast, expected_values); + } + } +}; +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(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.casts/to_native.pass.cpp b/libcxx/test/std/experimental/simd/simd.casts/to_native.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.casts/to_native.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// 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.casts] +// template +// native_simd to_native(const fixed_size_simd&) noexcept; +// template +// native_simd_mask to_native(const fixed_size_simd_mask&) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckToNativeSimd { + template + void operator()() { + static_assert( + std::is_same_v(ex::fixed_size_simd<_Tp, _Np>())), ex::native_simd<_Tp>>, ""); + static auto InitializeSimd = [](auto& origin_simd) { + const size_t simd_size = origin_simd.size(); + for (size_t i = 0; i < simd_size; ++i) + origin_simd[i] = static_cast<_Tp>(i); + }; + { + auto origin = ex::fixed_size_simd<_Tp, _Np>(); + InitializeSimd(origin); + auto after_cast = ex::to_native<_Tp, _Np>(origin); + std::array<_Tp, origin.size()> expected_values; + for (size_t i = 0; i < origin.size(); ++i) + expected_values[i] = static_cast<_Tp>(i); + assert_simd_value_correct(after_cast, expected_values); + } + } +}; +struct CheckToNativeSimdMask { + template + void operator()() { + static_assert( + std::is_same_v(ex::fixed_size_simd<_Tp, _Np>())), ex::native_simd<_Tp>>, ""); + static auto InitializeSimdMask = [](auto& origin_simd_mask) { + const size_t mask_size = origin_simd_mask.size(); + for (size_t i = 0; i < mask_size; ++i) { + origin_simd_mask[i] = static_cast(i); + } + }; + { + auto origin = ex::fixed_size_simd_mask<_Tp, _Np>(); + InitializeSimdMask(origin); + auto after_cast = ex::to_native<_Tp, _Np>(origin); + std::array<_Tp, origin.size()> expected_values; + for (size_t i = 0; i < origin.size(); ++i) + expected_values[i] = static_cast(i); + assert_simd_mask_value_correct(after_cast, expected_values); + } + } +}; +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi, ex::simd_size_v<_Tp, ex::simd_abi::native<_Tp>>>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_copy.pass.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_copy.pass.cpp @@ -0,0 +1,123 @@ +//===----------------------------------------------------------------------===// +// +// 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 void copy_from(const U* mem, Flags f); +// template void copy_to(U* mem, Flags f); + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdCopyFrom { + template + void operator()() { + { + constexpr auto alignas_size = ex::memory_alignment_v, _Tp>; + ex::simd<_Tp, SimdAbi> origin_simd; + + constexpr auto array_length = origin_simd.size(); + + alignas(alignas_size) _Tp buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast<_Tp>(i + 1); + + origin_simd.copy_from(buffer, ex::vector_aligned_tag()); + + assert_simd_value_correct(origin_simd, buffer); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(_Np)); + ex::simd<_Tp, SimdAbi> origin_simd; + constexpr auto array_length = origin_simd.size(); + + alignas(alignas_size) _Tp buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast<_Tp>(i); + + origin_simd.copy_from(buffer, ex::overaligned_tag()); + + assert_simd_value_correct(origin_simd, buffer); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(alignof(_Tp))); + ex::simd<_Tp, SimdAbi> origin_simd; + constexpr auto array_length = origin_simd.size(); + + alignas(alignas_size) _Tp buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast<_Tp>(i); + + origin_simd.copy_from(buffer, ex::element_aligned_tag()); + + assert_simd_value_correct(origin_simd, buffer); + } + } +}; + +struct CheckSimdCopyTo { + template + void operator()() { + { + constexpr auto alignas_size = ex::memory_alignment_v, _Tp>; + + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 1); }); + constexpr auto array_length = origin_simd.size(); + alignas(alignas_size) _Tp expected_buffer[array_length]{}; + + origin_simd.copy_to(expected_buffer, ex::vector_aligned_tag()); + + assert_simd_value_correct(origin_simd, expected_buffer); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(_Np)); + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 1); }); + + constexpr auto array_length = origin_simd.size(); + + alignas(alignas_size) _Tp expected_buffer[array_length]{}; + + origin_simd.copy_to(expected_buffer, ex::overaligned_tag()); + + assert_simd_value_correct(origin_simd, expected_buffer); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(alignof(_Tp))); + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 1); }); + + constexpr auto array_length = origin_simd.size(); + + alignas(alignas_size) _Tp expected_buffer[array_length]{}; + + origin_simd.copy_to(expected_buffer, ex::element_aligned_tag()); + + assert_simd_value_correct(origin_simd, expected_buffer); + } + } +}; +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(); +} \ No newline at end of file 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,227 @@ +//===----------------------------------------------------------------------===// +// +// 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 zero_init { + T val; + +public: + zero_init() : val(static_cast(0)) {} + zero_init(T val) : val(val) {} + operator T&() { return val; } + operator T() const { return val; } +}; +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); + + ex::simd<_Tp, SimdAbi> expected_simd_from_vectorizable_type(ex::simd<_Tp, SimdAbi>(static_cast<_Tp>(3))); + assert_simd_value_correct(expected_simd_from_vectorizable_type, origin_value); + + zero_init<_Tp> implicit_convert_to_3(3); + ex::simd<_Tp, SimdAbi> expected_simd_from_implicit_convert(std::move(implicit_convert_to_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(std::move(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(std::move(uint_value_3)); + assert_simd_value_correct(expected_simd_from_uint, origin_value); + } + } +}; + +struct CheckFixedSimdCtor { + template + void operator()() { + if constexpr (std::is_same_v>) { + constexpr size_t array_size = ex::simd_size_v<_Tp, SimdAbi>; + if constexpr (std::is_integral_v<_Tp> && std::is_signed_v<_Tp>) { + { + ex::simd char_simd([](char i) { return i; }); + ex::simd<_Tp, SimdAbi> convert_from_char(char_simd); + + std::array expected_value; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast(i); + assert_simd_value_correct(convert_from_char, expected_value); + } + if constexpr (!std::is_same_v<_Tp, signed char>) { + ex::simd short_simd([](short i) { return i; }); + ex::simd<_Tp, SimdAbi> convert_from_short(short_simd); + + std::array expected_value_in_short; + for (size_t i = 0; i < array_size; i++) + expected_value_in_short[i] = static_cast(i); + assert_simd_value_correct(convert_from_short, expected_value_in_short); + } + + if constexpr (!std::is_same_v<_Tp, signed char> && !std::is_same_v<_Tp, short>) { + ex::simd wchar_simd([](wchar_t i) { return i; }); + ex::simd<_Tp, SimdAbi> convert_from_wchar(wchar_simd); + + std::array expected_value_in_wchar; + for (size_t i = 0; i < array_size; i++) + expected_value_in_wchar[i] = static_cast(i); + assert_simd_value_correct(convert_from_wchar, expected_value_in_wchar); + } + if constexpr (!std::is_same_v<_Tp, signed char> && !std::is_same_v<_Tp, short>) { + ex::simd int_simd([](int i) { return i; }); + ex::simd<_Tp, SimdAbi> convert_from_int(int_simd); + + std::array expected_value_in_int; + for (size_t i = 0; i < array_size; i++) + expected_value_in_int[i] = static_cast(i); + assert_simd_value_correct(convert_from_int, expected_value_in_int); + } + if constexpr (!std::is_same_v<_Tp, signed char> && !std::is_same_v<_Tp, short> && + !std::is_same_v<_Tp, wchar_t> && !std::is_same_v<_Tp, int>) { + ex::simd long_simd([](long i) { return i; }); + auto convert_from_long(long_simd); + static_assert(std::is_same_v, decltype(convert_from_long)>); + + std::array expected_value_in_long; + for (size_t i = 0; i < array_size; i++) + expected_value_in_long[i] = static_cast(i); + assert_simd_value_correct(convert_from_long, expected_value_in_long); + } + if constexpr (!std::is_same_v<_Tp, signed char> && !std::is_same_v<_Tp, short> && + !std::is_same_v<_Tp, wchar_t> && !std::is_same_v<_Tp, int>) { + ex::simd long_long_simd([](long long i) { return i; }); + ex::simd<_Tp, SimdAbi> convert_from_long_long(long_long_simd); + + std::array expected_value_in_long_long; + for (size_t i = 0; i < array_size; i++) + expected_value_in_long_long[i] = static_cast(i); + assert_simd_value_correct(convert_from_long_long, expected_value_in_long_long); + } + } else if constexpr (std::is_floating_point_v<_Tp>) { + { + const ex::simd float_simd([](float i) { return i; }); + ex::simd<_Tp, SimdAbi> convert_from_float(float_simd); + + std::array expected_value_in_float; + for (size_t i = 0; i < array_size; i++) + expected_value_in_float[i] = static_cast(i); + assert_simd_value_correct(convert_from_float, expected_value_in_float); + } + if constexpr (!std::is_same_v<_Tp, float>) { + const ex::simd double_simd([](double i) { return i; }); + ex::simd<_Tp, SimdAbi> convert_from_double(double_simd); + + std::array expected_value_in_double; + for (size_t i = 0; i < array_size; i++) + expected_value_in_double[i] = static_cast(i); + assert_simd_value_correct(convert_from_double, expected_value_in_double); + } + if constexpr (!std::is_same_v<_Tp, float> && !std::is_same_v<_Tp, double>) { + const ex::simd long_double_simd([](long double i) { return i; }); + ex::simd<_Tp, SimdAbi> convert_from_long_double(long_double_simd); + + std::array expected_value_in_long_double; + for (size_t i = 0; i < array_size; i++) + expected_value_in_long_double[i] = static_cast(i); + assert_simd_value_correct(convert_from_long_double, expected_value_in_long_double); + } + } else { + } + } + } +}; + +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_length = ex::simd_size_v<_Tp, SimdAbi>; + + alignas(alignas_size) _Tp buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast<_Tp>(i + 1); + + ex::simd<_Tp, SimdAbi> origin_simd(buffer, ex::vector_aligned_tag()); + assert_simd_value_correct(origin_simd, buffer); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(_Np)); + + constexpr auto array_length = ex::simd_size_v<_Tp, SimdAbi>; + + alignas(alignas_size) _Tp buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast<_Tp>(i); + + ex::simd<_Tp, SimdAbi> origin_simd(buffer, ex::overaligned_tag()); + assert_simd_value_correct(origin_simd, buffer); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(alignof(_Tp))); + + constexpr auto array_length = ex::simd_size_v<_Tp, SimdAbi>; + + alignas(alignas_size) _Tp buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast<_Tp>(i); + + ex::simd<_Tp, SimdAbi> origin_simd(buffer, ex::element_aligned_tag()); + assert_simd_value_correct(origin_simd, buffer); + } + } +}; + +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(); +} 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,55 @@ +//===----------------------------------------------------------------------===// +// +// 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, _Np>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file 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 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_unary.pass.cpp @@ -0,0 +1,165 @@ +//===----------------------------------------------------------------------===// +// +// 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; + +struct CheckSimdUnaryOperator { + template + void operator()() { + constexpr size_t array_size = ex::simd_size_v<_Tp, SimdAbi>; + { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + + std::array<_Tp, array_size> origin_value; + for (size_t i = 0; i < array_size; i++) + origin_value[i] = static_cast<_Tp>(i); + + auto after_increment_simd = ++origin_simd; + static_assert(std::is_same_v>); + + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast<_Tp>(origin_value[i] + 1); + + assert_simd_value_correct(after_increment_simd, expected_value); + assert_simd_value_correct(origin_simd, expected_value); + } + { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + + std::array<_Tp, array_size> origin_value; + for (size_t i = 0; i < array_size; i++) + origin_value[i] = static_cast<_Tp>(i); + + auto after_increment_simd = origin_simd++; + static_assert(std::is_same_v>); + + assert_simd_value_correct(after_increment_simd, origin_value); + + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast<_Tp>(origin_value[i] + 1); + + assert_simd_value_correct(origin_simd, expected_value); + } + { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 1); }); + + std::array<_Tp, array_size> origin_value; + for (size_t i = 0; i < array_size; i++) + origin_value[i] = static_cast<_Tp>(i + 1); + + auto after_decrement_simd = --origin_simd; + static_assert(std::is_same_v>); + + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast<_Tp>(origin_value[i] - 1); + + assert_simd_value_correct(after_decrement_simd, expected_value); + assert_simd_value_correct(origin_simd, expected_value); + } + { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 1); }); + + std::array<_Tp, array_size> origin_value; + for (size_t i = 0; i < array_size; i++) + origin_value[i] = static_cast<_Tp>(i + 1); + + auto after_decrement_simd = origin_simd--; + static_assert(std::is_same_v>); + + assert_simd_value_correct(after_decrement_simd, origin_value); + + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast<_Tp>(origin_value[i] - 1); + + assert_simd_value_correct(origin_simd, expected_value); + } + { + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + + auto origin_mask = !origin_simd; + static_assert(std::is_same_v>); + + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = !static_cast<_Tp>(i); + + assert_simd_mask_value_correct(origin_mask, expected_value); + } + { + if constexpr (std::is_integral_v<_Tp> && !std::is_unsigned_v<_Tp>) { + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + + auto after_inverse_simd = ~origin_simd; + static_assert(std::is_same_v>); + + 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(after_inverse_simd, expected_value); + } + } + { + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = static_cast<_Tp>(i); + + auto expected_simd = +origin_simd; + static_assert(std::is_same_v>); + + assert_simd_value_correct(expected_simd, expected_value); + } + { + if constexpr (!std::is_unsigned_v<_Tp>) { + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + + std::array<_Tp, array_size> expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = -static_cast<_Tp>(i); + + auto expected_simd = -origin_simd; + static_assert(std::is_same_v>); + + assert_simd_value_correct(expected_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(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_width.pass.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_width.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_width.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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] +// static constexpr size_t size() noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; +struct CheckSimdWidth { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> origin_simd{}; + static_assert(origin_simd.size() == ex::simd_size_v<_Tp, SimdAbi>); + } +}; +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(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.cons/broadcast.pass.cpp b/libcxx/test/std/experimental/simd/simd.cons/broadcast.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.cons/broadcast.pass.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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); - -#include -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -template -auto not_supported_native_simd_ctor(Args&&... args) - -> decltype(ex::native_simd(std::forward(args)...), - void()) = delete; - -template -void not_supported_native_simd_ctor(...) {} - -template -auto supported_native_simd_ctor(Args&&... args) - -> decltype(ex::native_simd(std::forward(args)...), void()) {} - -template -void supported_native_simd_ctor(...) = delete; - -void compile_narrowing_conversion() { - supported_native_simd_ctor(3); - supported_native_simd_ctor(3); - supported_native_simd_ctor(3); - supported_native_simd_ctor(3); - supported_native_simd_ctor(3); - supported_native_simd_ctor(3); - supported_native_simd_ctor(3); - supported_native_simd_ctor(3); - supported_native_simd_ctor(3.f); - supported_native_simd_ctor(3.); - supported_native_simd_ctor(3.); - - not_supported_native_simd_ctor(3.); - not_supported_native_simd_ctor(long(3)); - not_supported_native_simd_ctor(long(3)); - not_supported_native_simd_ctor(3.); -} - -void compile_convertible() { - struct ConvertibleToInt { - operator int64_t() const; - }; - supported_native_simd_ctor(ConvertibleToInt()); - - struct NotConvertibleToInt {}; - not_supported_native_simd_ctor(NotConvertibleToInt()); -} - -void compile_unsigned() { - not_supported_native_simd_ctor(3u); - supported_native_simd_ctor(3u); -} - -template -void test_broadcast() { - SimdType a(3); - for (size_t i = 0; i < a.size(); i++) { - assert(a[i] == 3); - } -} - -int main(int, char**) { - test_broadcast>(); - test_broadcast>(); - test_broadcast>(); - - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.cons/default.pass.cpp b/libcxx/test/std/experimental/simd/simd.cons/default.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.cons/default.pass.cpp +++ /dev/null @@ -1,31 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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; - -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -int main(int, char**) { - static_assert(ex::native_simd().size() > 0, ""); - static_assert(ex::fixed_size_simd().size() == 4, ""); - static_assert(ex::fixed_size_simd().size() == 5, ""); - static_assert(ex::fixed_size_simd().size() == 1, ""); - static_assert(ex::fixed_size_simd().size() == 32, ""); - - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.cons/generator.pass.cpp b/libcxx/test/std/experimental/simd/simd.cons/generator.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.cons/generator.pass.cpp +++ /dev/null @@ -1,92 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 explicit simd(G&& gen); - -#include -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -template -auto not_supported_simd128_ctor(Args&&... args) -> decltype( - ex::fixed_size_simd(std::forward(args)...), - void()) = delete; - -template -void not_supported_simd128_ctor(...) {} - -template -auto supported_simd128_ctor(Args&&... args) -> decltype( - ex::fixed_size_simd(std::forward(args)...), - void()) {} - -template -void supported_simd128_ctor(...) = delete; - -struct identity { - template - int operator()(std::integral_constant) const { - return value; - } -}; - -void compile_generator() { - supported_simd128_ctor(identity()); - not_supported_simd128_ctor([](int i) { return float(i); }); - not_supported_simd128_ctor([](intptr_t i) { return (int*)(i); }); - not_supported_simd128_ctor([](int* i) { return i; }); -} - -struct limited_identity { - template - typename std::conditional::type - operator()(std::integral_constant) const { - return value; - } -}; - -void compile_limited_identity() { - supported_simd128_ctor(limited_identity()); - not_supported_simd128_ctor(limited_identity()); -} - -template -void test_generator() { - { - SimdType a([](int i) { return i; }); - assert(a[0] == 0); - assert(a[1] == 1); - assert(a[2] == 2); - assert(a[3] == 3); - } - { - SimdType a([](int i) { return 2 * i - 1; }); - assert(a[0] == -1); - assert(a[1] == 1); - assert(a[2] == 3); - assert(a[3] == 5); - } -} - -int main(int, char**) { - // TODO: adjust the tests when this assertion fails. - assert(ex::native_simd::size() >= 4); - test_generator>(); - test_generator>(); - - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.cons/load.pass.cpp b/libcxx/test/std/experimental/simd/simd.cons/load.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.cons/load.pass.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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(const U* mem, Flags f); - -#include -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -template -auto not_supported_native_simd_ctor(Args&&... args) - -> decltype(ex::native_simd(std::forward(args)...), - void()) = delete; - -template -void not_supported_native_simd_ctor(...) {} - -template -auto supported_native_simd_ctor(Args&&... args) - -> decltype(ex::native_simd(std::forward(args)...), void()) {} - -template -void supported_native_simd_ctor(...) = delete; - -void compile_load_ctor() { - supported_native_simd_ctor((int*)nullptr, ex::element_aligned_tag()); - supported_native_simd_ctor((int*)nullptr, - ex::element_aligned_tag()); - supported_native_simd_ctor((float*)nullptr, - ex::element_aligned_tag()); - supported_native_simd_ctor((unsigned int*)nullptr, - ex::element_aligned_tag()); - supported_native_simd_ctor((float*)nullptr, - ex::element_aligned_tag()); - - not_supported_native_simd_ctor((int*)nullptr, int()); -} - -template -void test_load_ctor() { - alignas(32) int32_t buffer[] = {4, 3, 2, 1}; - { - SimdType a(buffer, ex::element_aligned_tag()); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - { - SimdType a(buffer, ex::vector_aligned_tag()); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - { - SimdType a(buffer, ex::overaligned_tag<32>()); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - - { - SimdType a(buffer, ex::element_aligned); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - { - SimdType a(buffer, ex::vector_aligned); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - { - SimdType a(buffer, ex::overaligned<32>); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } -} - -template -void test_converting_load_ctor() { - float buffer[] = {1., 2., 4., 8.}; - SimdType a(buffer, ex::element_aligned_tag()); - assert(a[0] == 1); - assert(a[1] == 2); - assert(a[2] == 4); - assert(a[3] == 8); -} - -int main(int, char**) { - // TODO: adjust the tests when this assertion fails. - assert(ex::native_simd::size() >= 4); - test_load_ctor>(); - test_load_ctor>(); - test_converting_load_ctor>(); - test_converting_load_ctor>(); - - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_copy.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_copy.pass.cpp @@ -0,0 +1,126 @@ +//===----------------------------------------------------------------------===// +// +// 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] +// template void copy_from(const value_type* mem, Flags); +// template void copy_to(value_type* mem, Flags); + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdCopyFrom { + template + void operator()() { + { + constexpr auto alignas_size = ex::memory_alignment_v>; + ex::simd_mask<_Tp, SimdAbi> origin_simd_mask{}; + + constexpr auto array_length = origin_simd_mask.size(); + + alignas(alignas_size) bool buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast(i + 1); + + origin_simd_mask.copy_from(buffer, ex::vector_aligned_tag()); + + assert_simd_mask_value_correct(origin_simd_mask, buffer); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(_Np)); + ex::simd_mask<_Tp, SimdAbi> origin_simd_mask{}; + constexpr auto array_length = origin_simd_mask.size(); + + alignas(alignas_size) bool buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast(i + 1); + + origin_simd_mask.copy_from(buffer, ex::overaligned_tag()); + + assert_simd_mask_value_correct(origin_simd_mask, buffer); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(alignof(_Tp))); + ex::simd_mask<_Tp, SimdAbi> origin_simd_mask{}; + constexpr auto array_length = origin_simd_mask.size(); + + alignas(alignas_size) bool buffer[array_length]; + for (size_t i = 0; i < array_length; i++) + buffer[i] = static_cast(i + 1); + + origin_simd_mask.copy_from(buffer, ex::element_aligned_tag()); + + assert_simd_mask_value_correct(origin_simd_mask, buffer); + } + } +}; + +struct CheckSimdCopyTo { + template + void operator()() { + { + constexpr auto alignas_size = ex::memory_alignment_v>; + + const ex::simd_mask<_Tp, SimdAbi> origin_simd_mask([](bool i) { return static_cast(i + 1); }); + constexpr auto array_length = origin_simd_mask.size(); + alignas(alignas_size) bool expected_buffer[array_length]{}; + + origin_simd_mask.copy_to(expected_buffer, ex::vector_aligned_tag()); + + for (size_t i = 0; i < origin_simd_mask.size(); i++) + assert(expected_buffer[i] == static_cast(i + 1)); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(_Np)); + const ex::simd_mask<_Tp, SimdAbi> origin_simd_mask([](bool i) { return static_cast(i + 1); }); + + constexpr auto array_length = origin_simd_mask.size(); + + alignas(alignas_size) bool expected_buffer[array_length]{}; + + origin_simd_mask.copy_to(expected_buffer, ex::overaligned_tag()); + + for (size_t i = 0; i < array_length; i++) + assert(expected_buffer[i] == static_cast(i + 1)); + } + + { + constexpr auto alignas_size = std::max(next_pow2(sizeof(_Tp)), next_pow2(alignof(_Tp))); + const ex::simd_mask<_Tp, SimdAbi> origin_simd_mask([](bool i) { return static_cast(i + 1); }); + + constexpr auto array_length = origin_simd_mask.size(); + + alignas(alignas_size) bool expected_buffer[array_length]{}; + + origin_simd_mask.copy_to(expected_buffer, ex::element_aligned_tag()); + + for (size_t i = 0; i < array_length; i++) + assert(expected_buffer[i] == static_cast(i + 1)); + } + } +}; +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(); +} \ No newline at end of file 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,81 @@ +//===----------------------------------------------------------------------===// +// +// 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 CheckSimdMaskCtor { + 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); + } + { + if constexpr (std::is_same_v>) { + ex::simd_mask<_Tp, SimdAbi> origin_fixed_size_abi_mask; + for (size_t i = 0; i < array_size; i++) + origin_fixed_size_abi_mask[i] = static_cast(i % 2); + ex::simd_mask<_Tp, SimdAbi> mask_ctor_from_fixed_abi(origin_fixed_size_abi_mask); + + std::array expected_value; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast(i % 2); + + assert_simd_mask_value_correct(mask_ctor_from_fixed_abi, expected_value); + } + } + { + alignas(alignof(bool)) bool expected_value[array_size]; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast(i % 2); + + ex::simd_mask<_Tp, SimdAbi> simd_mask_(expected_value, ex::element_aligned_tag()); + assert_simd_mask_value_correct(simd_mask_, expected_value); + } + { + alignas(ex::memory_alignment_v>) bool expected_value[array_size]; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast(i % 2); + + ex::simd_mask<_Tp, SimdAbi> simd_mask_(expected_value, ex::vector_aligned_tag()); + assert_simd_mask_value_correct(simd_mask_, expected_value); + } + { + alignas(ex::memory_alignment_v>) bool expected_value[array_size]; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = static_cast(i % 2); + + ex::simd_mask<_Tp, SimdAbi> simd_mask_(expected_value, ex::overaligned_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() { test_all_simd_abi(); } \ No newline at end of file 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,58 @@ +//===----------------------------------------------------------------------===// +// +// 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<_Tp, SimdAbi> lhs{}, rhs{}; + ex::simd_mask<_Tp, SimdAbi> origin_simd_mask(lhs == rhs); + 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()() { + ex::simd<_Tp, SimdAbi> lhs{}, rhs{}; + const ex::simd_mask<_Tp, SimdAbi> origin_simd_mask(lhs == rhs); + 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, _Np>(); + test_simd_abi(); +} + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_unary.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_unary.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_unary.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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 operator!() const noexcept; + +#include "../test_utils.h" +#include + +struct CheckSimdMaskUnary { + template + void operator()() { + constexpr size_t array_size = ex::simd_size_v<_Tp, SimdAbi>; + + bool origin_value[array_size]; + for (size_t i = 0; i < array_size; i++) + origin_value[i] = static_cast(i % 2); + + ex::simd_mask<_Tp, SimdAbi> origin_mask(origin_value, ex::element_aligned_tag()); + + ex::simd_mask<_Tp, SimdAbi> after_convert_mask = !origin_mask; + + std::array expected_value; + for (size_t i = 0; i < array_size; i++) + expected_value[i] = !origin_value[i]; + + assert_simd_mask_value_correct(after_convert_mask, 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(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_width.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_width.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_width.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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] +// static constexpr size_t size() noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; +struct CheckSimdMaskWidth { + template + void operator()() { + const ex::simd_mask<_Tp, SimdAbi> origin_simd_mask{}; + static_assert(origin_simd_mask.size() == ex::simd_size_v<_Tp, SimdAbi>); + } +}; +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(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_binary.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_binary.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_binary.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// friend simd_mask operator&&(const simd_mask& lhs, const simd_mask& rhs) noexcept; +// friend simd_mask operator||(const simd_mask& lhs, const simd_mask& rhs) noexcept; +// friend simd_mask operator& (const simd_mask& lhs, const simd_mask& rhs) noexcept; +// friend simd_mask operator| (const simd_mask& lhs, const simd_mask& rhs) noexcept; +// friend simd_mask operator^ (const simd_mask& lhs, const simd_mask& rhs) noexcept; + +#include "../test_utils.h" +#include +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdMaskBinary { + template + void operator()() { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(5)); + ex::simd<_Tp, SimdAbi> r_simd([](_Tp i) { return i; }); + + const ex::simd_mask<_Tp, SimdAbi> lhs(l_simd == r_simd); + const ex::simd_mask<_Tp, SimdAbi> rhs(l_simd > r_simd); + + { + ex::simd_mask<_Tp, SimdAbi> logical_and_mask(lhs && rhs); + for (size_t i = 0; i < logical_and_mask.size(); ++i) { + assert(logical_and_mask[i] == (lhs[i] && rhs[i])); + } + } + { + ex::simd_mask<_Tp, SimdAbi> logical_or_mask(lhs || rhs); + for (size_t i = 0; i < logical_or_mask.size(); ++i) { + assert(logical_or_mask[i] == (lhs[i] || rhs[i])); + } + } + { + ex::simd_mask<_Tp, SimdAbi> bitwise_and_mask(lhs & rhs); + for (size_t i = 0; i < bitwise_and_mask.size(); ++i) { + assert(bitwise_and_mask[i] == (lhs[i] && rhs[i])); + } + } + { + ex::simd_mask<_Tp, SimdAbi> bitwise_or_mask(lhs | rhs); + for (size_t i = 0; i < bitwise_or_mask.size(); ++i) { + assert(bitwise_or_mask[i] == (lhs[i] || rhs[i])); + } + } + { + ex::simd_mask<_Tp, SimdAbi> bitwise_xor_mask(lhs ^ rhs); + for (size_t i = 0; i < bitwise_xor_mask.size(); ++i) { + assert(bitwise_xor_mask[i] == (lhs[i] ^ rhs[i])); + } + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_cassign.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_cassign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_cassign.pass.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// friend simd_mask& operator&=(simd_mask& lhs, const simd_mask& rhs) noexcept; +// friend simd_mask& operator|=(simd_mask& lhs, const simd_mask& rhs) noexcept; +// friend simd_mask& operator^=(simd_mask& lhs, const simd_mask& rhs) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdMaskCassign { + template + void operator()() { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(5)); + ex::simd<_Tp, SimdAbi> r_simd([](_Tp i) { return i; }); + + ex::simd_mask<_Tp, SimdAbi> lhs(l_simd == r_simd); + const ex::simd_mask<_Tp, SimdAbi> rhs(l_simd > r_simd); + constexpr size_t array_size = ex::simd_size_v<_Tp, SimdAbi>; + { + auto origin_lhs = lhs; + auto and_equal_mask(lhs &= rhs); + + static_assert(std::is_same_v, decltype(and_equal_mask)>); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = (origin_lhs[i] && rhs[i]); + + assert_simd_mask_value_correct(lhs, expected_value); + assert_simd_mask_value_correct(and_equal_mask, expected_value); + + lhs = origin_lhs; + } + { + auto origin_lhs = lhs; + auto or_equal_mask(lhs |= rhs); + + static_assert(std::is_same_v, decltype(or_equal_mask)>); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = (origin_lhs[i] || rhs[i]); + + assert_simd_mask_value_correct(lhs, expected_value); + assert_simd_mask_value_correct(or_equal_mask, expected_value); + + lhs = origin_lhs; + } + { + auto origin_lhs = lhs; + auto xor_equal_mask(lhs ^= rhs); + + static_assert(std::is_same_v, decltype(xor_equal_mask)>); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = (origin_lhs[i] ^ rhs[i]); + + assert_simd_mask_value_correct(lhs, expected_value); + assert_simd_mask_value_correct(xor_equal_mask, expected_value); + + lhs = origin_lhs; + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } diff --git a/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_comparison.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_comparison.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_comparison.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// friend simd_mask operator==(const simd_mask&, const simd_mask&) noexcept; +// friend simd_mask operator!=(const simd_mask&, const simd_mask&) noexcept; + +#include "../test_utils.h" +#include +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdMaskComparision { + template + void operator()() { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(5)); + ex::simd<_Tp, SimdAbi> r_simd([](_Tp i) { return i; }); + + const ex::simd_mask<_Tp, SimdAbi> lhs(l_simd == r_simd); + const ex::simd_mask<_Tp, SimdAbi> rhs(l_simd > r_simd); + + { + ex::simd_mask<_Tp, SimdAbi> equal_mask(lhs == rhs); + for (size_t i = 0; i < equal_mask.size(); ++i) { + assert(equal_mask[i] == (lhs[i] == rhs[i])); + } + } + { + ex::simd_mask<_Tp, SimdAbi> not_equal_mask(lhs != rhs); + for (size_t i = 0; i < not_equal_mask.size(); ++i) { + assert(not_equal_mask[i] == (lhs[i] != rhs[i])); + } + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } diff --git a/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_reductions.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_reductions.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_reductions.pass.cpp @@ -0,0 +1,143 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// template bool all_of(const simd_mask&) noexcept; +// template bool any_of(const simd_mask&) noexcept; +// template bool none_of(const simd_mask&) noexcept; +// template bool some_of(const simd_mask&) noexcept; +// template int popcount(const simd_mask&) noexcept; +// template int find_first_set(const simd_mask&); +// template int find_last_set(const simd_mask&); +// bool all_of(T) noexcept; +// bool any_of(T) noexcept; +// bool none_of(T) noexcept; +// bool some_of(T) noexcept; +// int popcount(T) noexcept; +// int find_first_set(T); +// int find_last_set(T); + +#include "../test_utils.h" +#include +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdMaskReduction { + template + void operator()() { + { + const ex::simd_mask<_Tp, SimdAbi> mask_all_true(true), mask_all_false(false); + assert(ex::all_of(mask_all_true) == true); + assert(ex::all_of(mask_all_false) == false); + } + { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(0)); + ex::simd<_Tp, SimdAbi> r_simd([](_Tp i) { return i; }); + const ex::simd_mask<_Tp, SimdAbi> mask_at_least_one_true(l_simd == r_simd); + assert(ex::any_of(mask_at_least_one_true) == true); + const ex::simd_mask<_Tp, SimdAbi> mask_all_false(false); + assert(ex::any_of(mask_all_false) == false); + } + { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(0)); + ex::simd<_Tp, SimdAbi> r_simd([](_Tp i) { return i; }); + const ex::simd_mask<_Tp, SimdAbi> mask_at_least_one_true(l_simd == r_simd); + assert(ex::none_of(mask_at_least_one_true) == false); + const ex::simd_mask<_Tp, SimdAbi> mask_all_false(false); + assert(ex::none_of(mask_all_false) == true); + } + { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(0)); + ex::simd<_Tp, SimdAbi> r_simd([](_Tp i) { return i; }); + const ex::simd_mask<_Tp, SimdAbi> mask_at_least_one_true(l_simd == r_simd); + assert(ex::none_of(mask_at_least_one_true) == false); + const ex::simd_mask<_Tp, SimdAbi> mask_all_false(false); + assert(ex::none_of(mask_all_false) == true); + } + { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(0)); + ex::simd<_Tp, SimdAbi> r_simd([](_Tp i) { return i; }); + const ex::simd_mask<_Tp, SimdAbi> mask_at_least_one_true(l_simd == r_simd); + if (mask_at_least_one_true.size() >= 2) { + assert(ex::some_of(mask_at_least_one_true) == true); + } + const ex::simd_mask<_Tp, SimdAbi> mask_all_false(false); + assert(ex::some_of(mask_all_false) == false); + const ex::simd_mask<_Tp, SimdAbi> mask_all_true(true); + assert(ex::some_of(mask_all_true) == false); + } + { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(0)); + ex::simd<_Tp, SimdAbi> r_simd([](_Tp i) { return i; }); + const ex::simd_mask<_Tp, SimdAbi> mask_at_least_one_true(l_simd == r_simd); + assert(ex::popcount(mask_at_least_one_true) == 1); + const ex::simd_mask<_Tp, SimdAbi> mask_all_false(false); + assert(ex::popcount(mask_all_false) == 0); + const ex::simd_mask<_Tp, SimdAbi> mask_all_true(true); + assert(ex::popcount(mask_all_true) == static_cast(mask_all_true.size())); + } + { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(0)); + ex::simd<_Tp, SimdAbi> r_simd(static_cast<_Tp>(0)); + const ex::simd_mask<_Tp, SimdAbi> mask_all_true(l_simd == r_simd); + assert(ex::any_of(mask_all_true) == true); + assert(ex::find_first_set(mask_all_true) == 0); + } + { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(0)); + ex::simd<_Tp, SimdAbi> r_simd(static_cast<_Tp>(0)); + const ex::simd_mask<_Tp, SimdAbi> mask_all_true(l_simd == r_simd); + assert(ex::any_of(mask_all_true) == true); + assert(ex::find_last_set(mask_all_true) == static_cast(mask_all_true.size() - 1)); + } + { + ex::simd<_Tp, SimdAbi> l_simd(static_cast<_Tp>(0)); + ex::simd<_Tp, SimdAbi> r_simd(static_cast<_Tp>(0)); + const ex::simd_mask<_Tp, SimdAbi> mask_all_true(l_simd == r_simd); + assert(ex::any_of(mask_all_true) == true); + assert(ex::find_last_set(mask_all_true) == static_cast(mask_all_true.size() - 1)); + } + { + constexpr int val = _Np - 1; + if constexpr (val == 0) { + constexpr bool val_false = static_cast<_Tp>(val); + assert(ex::all_of(val_false) == false); + assert(ex::any_of(val_false) == false); + assert(ex::none_of(val_false) == true); + assert(ex::some_of(val_false) == false); + assert(ex::popcount(val_false) == 0); + assert(ex::find_first_set(val_false) == 0); + assert(ex::find_last_set(val_false) == 0); + } else { + constexpr bool val_true = static_cast<_Tp>(val); + assert(ex::all_of(val_true) == true); + assert(ex::any_of(val_true) == true); + assert(ex::none_of(val_true) == false); + assert(ex::some_of(val_true) == false); + assert(ex::popcount(val_true) == 1); + assert(ex::find_first_set(val_true) == 0); + assert(ex::find_last_set(val_true) == 0); + } + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi, _Np>(); + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } diff --git a/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_where.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_where.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.nonmembers/simd_mask_where.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// template +// where_expression, simd> where(const typename simd::mask_type& k, +// simd& v) noexcept; +// template +// const_where_expression, simd> where(const typename simd::mask_type& k, +// const simd& v) noexcept; +// template +// where_expression, simd_mask> where(const type_identity_t>& k, +// simd_mask& v) noexcept; +// template +// const_where_expression, simd_mask> where(const type_identity_t>& k, +// const simd_mask& v) noexcept; +// template +// where_expression where(see below k, T& v) noexcept; +// template +// const_where_expression where(see below k, const T& v) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdMaskWhere { + template + void operator()() { + const typename ex::simd<_Tp, SimdAbi>::mask_type mask_{}; + ex::simd<_Tp, SimdAbi> simd_([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> const_simd_([](_Tp i) { return i; }); + const ex::simd_mask<_Tp, SimdAbi> const_mask_(simd_ == const_simd_); + constexpr size_t array_size = simd_.size(); + { + auto pure_simd = +ex::where(mask_, simd_); + static_assert(std::is_same_v >); + + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; ++i) + expected_values[i] = static_cast<_Tp>(i); + + assert_simd_value_correct(pure_simd, expected_values); + } + { + auto const_simd = +ex::where(mask_, const_simd_); + static_assert(std::is_same_v>); + + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; ++i) + expected_values[i] = static_cast<_Tp>(i); + + assert_simd_value_correct(const_simd, expected_values); + } + { + auto pure_mask = +ex::where(std::type_identity_t>(mask_), mask_); + + static_assert(std::is_same_v, decltype(pure_mask)>); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = mask_[i]; + + assert_simd_mask_value_correct(pure_mask, expected_value); + } + { + auto const_mask = +ex::where(std::type_identity_t>(mask_), const_mask_); + + static_assert(std::is_same_v, decltype(const_mask)>); + std::array expected_value; + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = const_mask_[i]; + + assert_simd_mask_value_correct(const_mask, expected_value); + } + { + _Tp val{1}; + auto data = +ex::where<_Tp>(true, val); + + static_assert(std::is_same_v<_Tp, decltype(data)>); + assert(data == val); + } + { + const _Tp val{1}; + auto data = +ex::where<_Tp>(true, val); + + static_assert(std::is_same_v<_Tp, decltype(data)>); + assert(data == val); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } diff --git a/libcxx/test/std/experimental/simd/simd.mem/load.pass.cpp b/libcxx/test/std/experimental/simd/simd.mem/load.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.mem/load.pass.cpp +++ /dev/null @@ -1,124 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 - -// -// -// loads [simd.load] -// template void copy_from(const U* mem, Flags f); - -#include -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -template -auto not_supported_load(Args&&... args) -> decltype( - std::declval>().copy_from(std::forward(args)...), - void()) = delete; - -template -void not_supported_load(...) {} - -template -auto supported_load(Args&&... args) -> decltype( - std::declval>().copy_from(std::forward(args)...), - void()) {} - -template -void supported_load(...) = delete; - -void compile_load() { - supported_load((int*)nullptr, ex::element_aligned_tag()); - supported_load((int*)nullptr, ex::element_aligned_tag()); - supported_load((float*)nullptr, ex::element_aligned_tag()); - supported_load((unsigned int*)nullptr, ex::element_aligned_tag()); - supported_load((float*)nullptr, ex::element_aligned_tag()); - - not_supported_load((int*)nullptr, int()); -} - -template -void test_load() { - alignas(32) int32_t buffer[] = {4, 3, 2, 1}; - { - SimdType a; - a.copy_from(buffer, ex::element_aligned_tag()); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - { - SimdType a; - a.copy_from(buffer, ex::vector_aligned_tag()); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - { - SimdType a; - a.copy_from(buffer, ex::overaligned_tag<32>()); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - - { - SimdType a; - a.copy_from(buffer, ex::element_aligned); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - { - SimdType a; - a.copy_from(buffer, ex::vector_aligned); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } - { - SimdType a; - a.copy_from(buffer, ex::overaligned<32>); - assert(a[0] == 4); - assert(a[1] == 3); - assert(a[2] == 2); - assert(a[3] == 1); - } -} - -template -void test_converting_load() { - float buffer[] = {1., 2., 4., 8.}; - SimdType a; - a.copy_from(buffer, ex::element_aligned_tag()); - assert(a[0] == 1); - assert(a[1] == 2); - assert(a[2] == 4); - assert(a[3] == 8); -} - -int main(int, char**) { - // TODO: adjust the tests when this assertion fails. - assert(ex::native_simd::size() >= 4); - test_load>(); - test_load>(); - test_converting_load>(); - test_converting_load>(); - - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.mem/store.pass.cpp b/libcxx/test/std/experimental/simd/simd.mem/store.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.mem/store.pass.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 - -// -// -// // stores [simd.store] -// template void copy_to(U* mem, Flags f) const; - -#include -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -template -void test_store() { - SimdType a([](int i) { return 4 - i; }); - { - alignas(32) int32_t buffer[4] = {0}; - a.copy_to(buffer, ex::element_aligned_tag()); - assert(buffer[0] == 4); - assert(buffer[1] == 3); - assert(buffer[2] == 2); - assert(buffer[3] == 1); - } - { - alignas(32) int32_t buffer[4] = {0}; - a.copy_to(buffer, ex::vector_aligned_tag()); - assert(buffer[0] == 4); - assert(buffer[1] == 3); - assert(buffer[2] == 2); - assert(buffer[3] == 1); - } - { - alignas(32) int32_t buffer[4] = {0}; - a.copy_to(buffer, ex::overaligned_tag<32>()); - assert(buffer[0] == 4); - assert(buffer[1] == 3); - assert(buffer[2] == 2); - assert(buffer[3] == 1); - } - - { - alignas(32) int32_t buffer[4] = {0}; - a.copy_to(buffer, ex::element_aligned); - assert(buffer[0] == 4); - assert(buffer[1] == 3); - assert(buffer[2] == 2); - assert(buffer[3] == 1); - } - { - alignas(32) int32_t buffer[4] = {0}; - a.copy_to(buffer, ex::vector_aligned); - assert(buffer[0] == 4); - assert(buffer[1] == 3); - assert(buffer[2] == 2); - assert(buffer[3] == 1); - } - { - alignas(32) int32_t buffer[4] = {0}; - a.copy_to(buffer, ex::overaligned<32>); - assert(buffer[0] == 4); - assert(buffer[1] == 3); - assert(buffer[2] == 2); - assert(buffer[3] == 1); - } -} - -template -void test_converting_store() { - float buffer[4] = {0.}; - SimdType a([](int i) { return 1 << i; }); - a.copy_to(buffer, ex::element_aligned_tag()); - assert(buffer[0] == 1.); - assert(buffer[1] == 2.); - assert(buffer[2] == 4.); - assert(buffer[3] == 8.); -} - -int main(int, char**) { - // TODO: adjust the tests when this assertion fails. - test_store>(); - test_store>(); - test_converting_store>(); - test_converting_store>(); - - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.nonmembers/simd_alg.pass.cpp b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_alg.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_alg.pass.cpp @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// template simd min(const simd& a, const simd& b) noexcept; +// template simd max(const simd& a, const simd& b) noexcept; +// template pair, simd> minmax(const simd& a, const simd& b) noexcept; +// template simd clamp(const simd& v, const simd& lo, const simd& hi); + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdMin { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> lhs([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> rhs(static_cast<_Tp>(5)); + + auto result_min_simd = ex::min(lhs, rhs); + static_assert(std::is_same_v>); + + std::array<_Tp, lhs.size()> expected_values; + + for (size_t i = 0; i < lhs.size(); ++i) + expected_values[i] = std::min(lhs[i], rhs[i]); + + assert_simd_value_correct(result_min_simd, expected_values); + } +}; + +struct CheckSimdMax { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> lhs([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> rhs(static_cast<_Tp>(5)); + + auto result_max_simd = ex::max(lhs, rhs); + static_assert(std::is_same_v>); + + std::array<_Tp, lhs.size()> expected_values; + + for (size_t i = 0; i < lhs.size(); ++i) + expected_values[i] = std::max(lhs[i], rhs[i]); + + assert_simd_value_correct(result_max_simd, expected_values); + } +}; + +struct CheckSimdMinMax { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> lhs([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> rhs(static_cast<_Tp>(5)); + + auto result_min_max_simd = ex::minmax(lhs, rhs); + static_assert( + std::is_same_v, ex::simd<_Tp, SimdAbi>>>); + + std::array<_Tp, lhs.size()> expected_min_values; + + for (size_t i = 0; i < lhs.size(); ++i) + expected_min_values[i] = std::min(lhs[i], rhs[i]); + + assert_simd_value_correct(result_min_max_simd.first, expected_min_values); + + std::array<_Tp, lhs.size()> expected_max_values; + + for (size_t i = 0; i < lhs.size(); ++i) + expected_max_values[i] = std::max(lhs[i], rhs[i]); + + assert_simd_value_correct(result_min_max_simd.second, expected_max_values); + } +}; + +struct CheckSimdClamp { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> lo([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> hi([](_Tp i) { return static_cast<_Tp>(i + 1); }); + const ex::simd<_Tp, SimdAbi> v(static_cast<_Tp>(5)); + + auto result_simd = ex::clamp(v, lo, hi); + static_assert(std::is_same_v>); + + std::array<_Tp, lo.size()> expected_values; + + for (size_t i = 0; i < lo.size(); ++i) + expected_values[i] = std::clamp(v[i], lo[i], hi[i]); + + assert_simd_value_correct(result_simd, expected_values); + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main() { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} diff --git a/libcxx/test/std/experimental/simd/simd.nonmembers/simd_binary.pass.cpp b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_binary.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_binary.pass.cpp @@ -0,0 +1,166 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// friend simd operator+(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator-(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator*(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator/(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator%(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator&(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator|(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator^(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator<<(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator>>(const simd& lhs, const simd& rhs) noexcept; +// friend simd operator<<(const simd& v, int n) noexcept; +// friend simd operator>>(const simd& v, int n) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdBinary { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> lhs([](_Tp i) { return static_cast<_Tp>(i + 1); }); + const ex::simd<_Tp, SimdAbi> rhs([]([[maybe_unused]] _Tp i) { return static_cast<_Tp>(2); }); + static_assert(lhs.size() == rhs.size()); + constexpr size_t array_size = lhs.size(); + { + auto result_plus_simd = lhs + rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] + rhs[i]); + assert_simd_value_correct(result_plus_simd, expected_values); + } + { + auto result_minus_simd = lhs - rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] - rhs[i]); + assert_simd_value_correct(result_minus_simd, expected_values); + } + { + auto result_multiply_simd = lhs * rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] * rhs[i]); + assert_simd_value_correct(result_multiply_simd, expected_values); + } + { + auto result_division_simd = lhs / rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] / rhs[i]); + assert_simd_value_correct(result_division_simd, expected_values); + } + { + if constexpr (std::is_integral_v<_Tp>) { + auto result_mod_simd = lhs % rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] % rhs[i]); + assert_simd_value_correct(result_mod_simd, expected_values); + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + auto result_and_simd = lhs & rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] & rhs[i]); + assert_simd_value_correct(result_and_simd, expected_values); + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + auto result_or_simd = lhs | rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] | rhs[i]); + assert_simd_value_correct(result_or_simd, expected_values); + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + auto result_exor_simd = lhs ^ rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] ^ rhs[i]); + assert_simd_value_correct(result_exor_simd, expected_values); + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + auto result_shift_left_simd = lhs << rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] << rhs[i]); + assert_simd_value_correct(result_shift_left_simd, expected_values); + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + auto result_shift_right_simd = lhs >> rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] >> rhs[i]); + assert_simd_value_correct(result_shift_right_simd, expected_values); + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + constexpr int shift_size = 2; + auto result_shift_left_simd = lhs << shift_size; + static_assert(std::is_same_v>); + + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] << shift_size); + assert_simd_value_correct(result_shift_left_simd, expected_values); + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + constexpr int shift_size = 2; + auto result_shift_right_simd = lhs >> shift_size; + static_assert(std::is_same_v>); + + std::array<_Tp, array_size> expected_values; + for (size_t i = 0; i < array_size; i++) + expected_values[i] = static_cast<_Tp>(lhs[i] >> shift_size); + assert_simd_value_correct(result_shift_right_simd, expected_values); + } + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.nonmembers/simd_cassign.pass.cpp b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_cassign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_cassign.pass.cpp @@ -0,0 +1,220 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// friend simd& operator+=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator-=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator*=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator/=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator%=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator&=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator|=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator^=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator<<=(simd& lhs, const simd& rhs) noexcept; +// friend simd& operator>>=(simd& lhs, const simd& rhs) noexcept; +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdCassign { + template + void operator()() { + ex::simd<_Tp, SimdAbi> lhs([](_Tp i) { return static_cast<_Tp>(i + 2); }); + const ex::simd<_Tp, SimdAbi> rhs([]([[maybe_unused]] _Tp i) { return static_cast<_Tp>(2); }); + constexpr std::size_t array_size = lhs.size(); + { + const auto origin_lhs_backup = lhs; + auto result_plus_simd = lhs += rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_plus_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] + rhs[i]; + assert_simd_value_correct(result_plus_simd, expected_value); + + lhs = origin_lhs_backup; + } + + { + const auto origin_lhs_backup = lhs; + auto result_minus_simd = lhs -= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_minus_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] - rhs[i]; + assert_simd_value_correct(result_minus_simd, expected_value); + + lhs = origin_lhs_backup; + } + { + const auto origin_lhs_backup = lhs; + auto result_multiply_simd = lhs *= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_multiply_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] * rhs[i]; + assert_simd_value_correct(result_multiply_simd, expected_value); + + lhs = origin_lhs_backup; + } + { + const auto origin_lhs_backup = lhs; + auto result_division_simd = lhs /= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_division_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] / rhs[i]; + assert_simd_value_correct(result_division_simd, expected_value); + + lhs = origin_lhs_backup; + } + { + if constexpr (std::is_integral_v<_Tp>) { + const auto origin_lhs_backup = lhs; + auto result_mod_simd = lhs %= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_mod_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] % rhs[i]; + assert_simd_value_correct(result_mod_simd, expected_value); + + lhs = origin_lhs_backup; + } + } + + { + if constexpr (std::is_integral_v<_Tp>) { + const auto origin_lhs_backup = lhs; + auto result_and_simd = lhs &= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_and_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] & rhs[i]; + assert_simd_value_correct(result_and_simd, expected_value); + + lhs = origin_lhs_backup; + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + const auto origin_lhs_backup = lhs; + auto result_or_simd = lhs |= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_or_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] | rhs[i]; + assert_simd_value_correct(result_or_simd, expected_value); + + lhs = origin_lhs_backup; + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + const auto origin_lhs_backup = lhs; + auto result_exor_simd = lhs ^= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_exor_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] ^ rhs[i]; + assert_simd_value_correct(result_exor_simd, expected_value); + + lhs = origin_lhs_backup; + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + const auto origin_lhs_backup = lhs; + auto result_shift_left_simd = lhs <<= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_shift_left_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] << rhs[i]; + assert_simd_value_correct(result_shift_left_simd, expected_value); + + lhs = origin_lhs_backup; + } + } + { + if constexpr (std::is_integral_v<_Tp>) { + const auto origin_lhs_backup = lhs; + auto result_shift_right_simd = lhs >>= rhs; + static_assert(std::is_same_v>); + std::array<_Tp, array_size> expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = lhs[i]; + assert_simd_value_correct(result_shift_right_simd, expected_value); + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = origin_lhs_backup[i] >> rhs[i]; + assert_simd_value_correct(result_shift_right_simd, expected_value); + + lhs = origin_lhs_backup; + } + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } diff --git a/libcxx/test/std/experimental/simd/simd.nonmembers/simd_comparison.pass.cpp b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_comparison.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_comparison.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// friend mask_type operator==(const simd& lhs, const simd& rhs) noexcept; +// friend mask_type operator!=(const simd& lhs, const simd& rhs) noexcept; +// friend mask_type operator>=(const simd& lhs, const simd& rhs) noexcept; +// friend mask_type operator<=(const simd& lhs, const simd& rhs) noexcept; +// friend mask_type operator>(const simd& lhs, const simd& rhs) noexcept; +// friend mask_type operator<(const simd& lhs, const simd& rhs) noexcept; + +#include "../test_utils.h" +#include +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdComparison { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> lhs(static_cast<_Tp>(5)); + const ex::simd<_Tp, SimdAbi> rhs([](_Tp i) { return i; }); + + { + const ex::simd_mask<_Tp, SimdAbi> equal_simd_mask(lhs == rhs); + for (size_t i = 0; i < equal_simd_mask.size(); ++i) { + assert(equal_simd_mask[i] == (lhs[i] == rhs[i])); + } + } + { + const ex::simd_mask<_Tp, SimdAbi> not_equal_simd_mask(lhs != rhs); + for (size_t i = 0; i < not_equal_simd_mask.size(); ++i) { + assert(not_equal_simd_mask[i] == (lhs[i] != rhs[i])); + } + } + { + const ex::simd_mask<_Tp, SimdAbi> great_equal_simd_mask(lhs >= rhs); + for (size_t i = 0; i < great_equal_simd_mask.size(); ++i) { + assert(great_equal_simd_mask[i] == (lhs[i] >= rhs[i])); + } + } + { + const ex::simd_mask<_Tp, SimdAbi> less_equal_simd_mask(lhs <= rhs); + for (size_t i = 0; i < less_equal_simd_mask.size(); ++i) { + assert(less_equal_simd_mask[i] == (lhs[i] <= rhs[i])); + } + } + { + const ex::simd_mask<_Tp, SimdAbi> great_simd_mask(lhs > rhs); + for (size_t i = 0; i < great_simd_mask.size(); ++i) { + assert(great_simd_mask[i] == (lhs[i] > rhs[i])); + } + } + { + const ex::simd_mask<_Tp, SimdAbi> less_simd_mask(lhs < rhs); + for (size_t i = 0; i < less_simd_mask.size(); ++i) { + assert(less_simd_mask[i] == (lhs[i] < rhs[i])); + } + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.nonmembers/simd_reductions.pass.cpp b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_reductions.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.nonmembers/simd_reductions.pass.cpp @@ -0,0 +1,403 @@ +//===----------------------------------------------------------------------===// +// +// 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.nonmembers] +// template > +// T reduce(const simd&, +// BinaryOperation = {}); + +// template +// typename V::value_type reduce(const const_where_expression& x, +// typename V::value_type neutral_element, +// BinaryOperation binary_op); + +// template +// typename V::value_type reduce(const const_where_expression& x, +// plus<> binary_op = {}) noexcept; +// template +// typename V::value_type reduce(const const_where_expression& x, +// multiplies<> binary_op) noexcept; +// template +// typename V::value_type reduce(const const_where_expression& x, +// bit_and<> binary_op) noexcept; +// template +// typename V::value_type reduce(const const_where_expression& x, +// bit_or<> binary_op) noexcept; +// template +// typename V::value_type reduce(const const_where_expression& x, +// bit_xor<> binary_op) noexcept; + +// template +// T hmin(const simd&) noexcept; +// template +// typename V::value_type hmin(const const_where_expression&) noexcept; +// template +// T hmax(const simd&) noexcept; +// template +// typename V::value_type hmax(const const_where_expression&) noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdReductionPart1 { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> simd_zero_start([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> simd_one_start([](_Tp i) { return static_cast<_Tp>(i + 1); }); + { + static const auto reduce_test_plus = [](const ex::simd<_Tp, SimdAbi>& simd_) { + auto reduce_result = ex::reduce(simd_, std::plus<>()); + static_assert(std::is_same_v); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected += simd_[i]; + assert(reduce_result == expected); + }; + reduce_test_plus(simd_zero_start); + reduce_test_plus(simd_one_start); + } + { + static const auto reduce_test_multiplies = [](const ex::simd<_Tp, SimdAbi>& simd_) { + auto reduce_result = ex::reduce(simd_, std::multiplies<>()); + static_assert(std::is_same_v); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected *= simd_[i]; + assert(reduce_result == expected); + }; + reduce_test_multiplies(simd_zero_start); + reduce_test_multiplies(simd_one_start); + } + { + static const auto reduce_test_bit_and = [](const ex::simd<_Tp, SimdAbi>& simd_) { + _Tp reduce_result = ex::reduce(simd_, std::bit_and<>()); + static_assert(std::is_same_v); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected &= simd_[i]; + assert(reduce_result == expected); + }; + reduce_test_bit_and(simd_zero_start); + reduce_test_bit_and(simd_one_start); + } + { + static const auto reduce_test_bit_or = [](const ex::simd<_Tp, SimdAbi>& simd_) { + _Tp reduce_result = ex::reduce(simd_, std::bit_or<>()); + static_assert(std::is_same_v); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected |= simd_[i]; + assert(reduce_result == expected); + }; + reduce_test_bit_or(simd_zero_start); + reduce_test_bit_or(simd_one_start); + } + { + static const auto reduce_test_bit_xor = [](const ex::simd<_Tp, SimdAbi>& simd_) { + _Tp reduce_result = ex::reduce(simd_, std::bit_xor<>()); + static_assert(std::is_same_v); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected ^= simd_[i]; + assert(reduce_result == expected); + }; + reduce_test_bit_xor(simd_zero_start); + reduce_test_bit_xor(simd_one_start); + } + } +}; + +struct CheckSimdReductionPart2 { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> simd_zero_start([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> simd_one_start([](_Tp i) { return static_cast<_Tp>(i + 1); }); + { + static const auto reduce_test_plus = [](const ex::simd<_Tp, SimdAbi>& simd_) { + _Tp identity_element{}; + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), identity_element, std::plus<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(identity_element == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::plus<>()); + _Tp expected{}; + for (size_t i = 0; i < simd_.size(); ++i) + expected += simd_[i]; + assert(result == expected); + } + }; + reduce_test_plus(simd_zero_start); + reduce_test_plus(simd_one_start); + } + + { + static const auto reduce_test_multiplies = [](const ex::simd<_Tp, SimdAbi>& simd_) { + _Tp identity_element{}; + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), identity_element, std::multiplies<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(identity_element == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::multiplies<>()); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected *= simd_[i]; + assert(result == expected); + } + }; + reduce_test_multiplies(simd_zero_start); + reduce_test_multiplies(simd_one_start); + } + + { + static const auto reduce_test_bit_and = [](const ex::simd<_Tp, SimdAbi>& simd_) { + _Tp identity_element{}; + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), identity_element, std::bit_and<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(identity_element == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::bit_and<>()); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected &= simd_[i]; + assert(result == expected); + } + }; + reduce_test_bit_and(simd_zero_start); + reduce_test_bit_and(simd_one_start); + } + + { + static const auto reduce_test_bit_or = [](const ex::simd<_Tp, SimdAbi>& simd_) { + _Tp identity_element{}; + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), identity_element, std::bit_or<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(identity_element == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::bit_or<>()); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected |= simd_[i]; + assert(result == expected); + } + }; + reduce_test_bit_or(simd_zero_start); + reduce_test_bit_or(simd_one_start); + } + + { + static const auto reduce_test_bit_xor = [](const ex::simd<_Tp, SimdAbi>& simd_) { + _Tp identity_element{}; + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), identity_element, std::bit_xor<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(identity_element == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::bit_xor<>()); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected ^= simd_[i]; + assert(result == expected); + } + }; + reduce_test_bit_xor(simd_zero_start); + reduce_test_bit_xor(simd_one_start); + } + } +}; + +struct CheckSimdReductionPart3 { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> simd_zero_start([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> simd_one_start([](_Tp i) { return static_cast<_Tp>(i + 1); }); + + { + static const auto reduce_test_plus = [](const ex::simd<_Tp, SimdAbi>& simd_) { + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), std::plus<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(0 == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::plus<>()); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected += simd_[i]; + assert(result == expected); + } + }; + reduce_test_plus(simd_zero_start); + reduce_test_plus(simd_one_start); + } + { + static const auto reduce_test_multiplies = [](const ex::simd<_Tp, SimdAbi>& simd_) { + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), std::multiplies<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(1 == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::multiplies<>()); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected *= simd_[i]; + assert(result == expected); + } + }; + reduce_test_multiplies(simd_zero_start); + reduce_test_multiplies(simd_one_start); + } + if constexpr (std::is_signed_v<_Tp>) { + static const auto reduce_test_bit_and = [](const ex::simd<_Tp, SimdAbi>& simd_) { + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), std::bit_and<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(~(_Tp()) == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::bit_and<>()); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected &= simd_[i]; + assert(result == expected); + } + }; + reduce_test_bit_and(simd_zero_start); + reduce_test_bit_and(simd_one_start); + } + if constexpr (std::is_signed_v<_Tp>) { + static const auto reduce_test_bit_or = [](const ex::simd<_Tp, SimdAbi>& simd_) { + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), std::bit_or<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(0 == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::bit_or<>()); + _Tp expected{simd_[0]}; + for (size_t i = 1; i < simd_.size(); ++i) + expected |= simd_[i]; + assert(result == expected); + } + }; + reduce_test_bit_or(simd_zero_start); + reduce_test_bit_or(simd_one_start); + } + if constexpr (std::is_signed_v<_Tp>) { + static const auto reduce_test_bit_xor = [](const ex::simd<_Tp, SimdAbi>& simd_) { + auto reduce_result = ex::reduce(ex::where(simd_ > 0, simd_), std::bit_xor<>()); + static_assert(std::is_same_v); + if (ex::none_of(simd_ > 0)) { + assert(0 == reduce_result); + } else { + _Tp result = ex::reduce(simd_, std::bit_xor<>()); + _Tp expected{0}; + for (size_t i = 0; i < simd_.size(); ++i) + expected ^= simd_[i]; + assert(result == expected); + } + }; + reduce_test_bit_xor(simd_zero_start); + reduce_test_bit_xor(simd_one_start); + } + } +}; + +struct CheckSimdReductionPart4 { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> simd_zero_start([](_Tp i) { return i; }); + const ex::simd<_Tp, SimdAbi> simd_one_start([](_Tp i) { return static_cast<_Tp>(i + 1); }); + { + static const auto reduce_hmin_test = [](const ex::simd<_Tp, SimdAbi>& simd_, bool is_start_zero) { + auto hmin_result = ex::hmin(simd_); + static_assert(std::is_same_v<_Tp, decltype(hmin_result)>); + if (is_start_zero) + assert(hmin_result == static_cast<_Tp>(0)); + else + assert(hmin_result == static_cast<_Tp>(1)); + + const ex::simd_mask<_Tp, SimdAbi> mask_(simd_ > 0); + auto where_expr = ex::where(mask_, simd_); + auto result = ex::hmin(where_expr); + static_assert(std::is_same_v<_Tp, decltype(result)>); + + if (ex::none_of(mask_)) { + assert(std::numeric_limits<_Tp>::max() == result); + } else { + _Tp expected(std::numeric_limits<_Tp>::max()); + for (size_t i = 0; i < simd_.size(); ++i) { + if (mask_[i] == true) { + if (expected == std::numeric_limits<_Tp>::max()) + expected = simd_[i]; + else + expected = std::min(expected, simd_[i]); + } + } + assert(result == expected); + } + }; + reduce_hmin_test(simd_zero_start, true); + reduce_hmin_test(simd_one_start, false); + } + { + static const auto reduce_hmax_test = [](const ex::simd<_Tp, SimdAbi>& simd_, bool is_start_zero) { + auto hmax_result = ex::hmax(simd_); + static_assert(std::is_same_v<_Tp, decltype(hmax_result)>); + if (is_start_zero) + assert(hmax_result == static_cast<_Tp>(simd_.size() - 1)); + else + assert(hmax_result == static_cast<_Tp>(simd_.size())); + + const ex::simd_mask<_Tp, SimdAbi> mask_(simd_ > 0); + auto where_expr = ex::where(mask_, simd_); + _Tp result = ex::hmax(where_expr); + + if (ex::none_of(mask_)) { + assert(std::numeric_limits<_Tp>::lowest() == result); + } else { + _Tp expected{0}; + for (size_t i = 0; i < simd_.size(); ++i) { + if (mask_[i] == true) { + if (expected == 0) + expected = simd_[i]; + else + expected = std::max(expected, simd_[i]); + } + } + assert(result == expected); + } + }; + reduce_hmax_test(simd_zero_start, true); + reduce_hmax_test(simd_one_start, false); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + if constexpr (!std::is_floating_point_v<_Tp>) { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); + } +} + +int main() { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.reference/reference_operators.pass.cpp b/libcxx/test/std/experimental/simd/simd.reference/reference_operators.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.reference/reference_operators.pass.cpp @@ -0,0 +1,264 @@ +//===----------------------------------------------------------------------===// +// +// 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] +// template reference=(U&& x) && noexcept; +// template reference+=(U&& x) && noexcept; +// template reference-=(U&& x) && noexcept; +// template reference*=(U&& x) && noexcept; +// template reference/=(U&& x) && noexcept; +// template reference%=(U&& x) && noexcept; +// template reference|=(U&& x) && noexcept; +// template reference&=(U&& x) && noexcept; +// template reference^=(U&& x) && noexcept; +// template reference<<=(U&& x) && noexcept; +// template reference>>=(U&& x) && noexcept; +// reference++() && noexcept; +// value_type++(int) && noexcept; +// reference--() && noexcept; +// value_type--(int) && noexcept; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckReferenceOperators { + template + void operator()() { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 2); }); + + { + assert(origin_simd[0] == static_cast<_Tp>(2)); + + using U = _Tp; + U x = static_cast(3); + + auto res = origin_simd[0] = (x); + assert(origin_simd[0] == static_cast<_Tp>(3)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(3)); + } + + { + assert(origin_simd[0] == static_cast<_Tp>(3)); + + using U = _Tp; + U x = static_cast(4); + + auto res = origin_simd[0] += (x); + assert(origin_simd[0] == static_cast<_Tp>(7)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(7)); + } + { + assert(origin_simd[0] == static_cast<_Tp>(7)); + + using U = _Tp; + U x = static_cast(6); + + auto res = origin_simd[0] -= (x); + assert(origin_simd[0] == static_cast<_Tp>(1)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(1)); + } + { + assert(origin_simd[0] == static_cast<_Tp>(1)); + + using U = _Tp; + U x = static_cast(12); + + auto res = origin_simd[0] *= (x); + assert(origin_simd[0] == static_cast<_Tp>(12)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(12)); + } + + { + assert(origin_simd[0] == static_cast<_Tp>(12)); + + using U = _Tp; + U x = static_cast(2); + + auto res = origin_simd[0] /= (x); + assert(origin_simd[0] == static_cast<_Tp>(6)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(6)); + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(6)); + + using U = _Tp; + U x = static_cast(4); + + auto res = origin_simd[0] %= (x); + assert(origin_simd[0] == static_cast<_Tp>(2)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(2)); + } + } + + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(2)); + + using U = _Tp; + U x = static_cast(5); + + auto res = origin_simd[0] |= (x); + assert(origin_simd[0] == static_cast<_Tp>(7)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(7)); + } + } + + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(7)); + + using U = _Tp; + U x = static_cast(3); + + auto res = origin_simd[0] &= (x); + assert(origin_simd[0] == static_cast<_Tp>(3)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(3)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(3)); + + using U = _Tp; + U x = static_cast(8); + + auto res = origin_simd[0] ^= (x); + assert(origin_simd[0] == static_cast<_Tp>(11)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(11)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(11)); + + using U = _Tp; + U x = static_cast(2); + + auto res = origin_simd[0] <<= (x); + assert(origin_simd[0] == static_cast<_Tp>(44)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(44)); + } + } + + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(44)); + + using U = _Tp; + U x = static_cast(2); + + auto res = origin_simd[0] >>= (x); + assert(origin_simd[0] == static_cast<_Tp>(11)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(11)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(11)); + + auto res = ++origin_simd[0]; + assert(origin_simd[0] == static_cast<_Tp>(12)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(12)); + + } else { + assert(origin_simd[0] == static_cast<_Tp>(6)); + + auto res = ++origin_simd[0]; + assert(origin_simd[0] == static_cast<_Tp>(7)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(7)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(12)); + + auto res = --origin_simd[0]; + assert(origin_simd[0] == static_cast<_Tp>(11)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(11)); + + } else { + assert(origin_simd[0] == static_cast<_Tp>(7)); + + auto res = --origin_simd[0]; + assert(origin_simd[0] == static_cast<_Tp>(6)); + static_assert(is_simd_reference::value); + assert(res == static_cast<_Tp>(6)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(11)); + + auto res = origin_simd[0]++; + + assert(origin_simd[0] == static_cast<_Tp>(12)); + static_assert(!is_simd_reference::value); + assert(res == static_cast<_Tp>(11)); + + } else { + assert(origin_simd[0] == static_cast<_Tp>(6)); + + auto res = origin_simd[0]++; + + assert(origin_simd[0] == static_cast<_Tp>(7)); + static_assert(!is_simd_reference::value); + assert(res == static_cast<_Tp>(6)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + assert(origin_simd[0] == static_cast<_Tp>(12)); + + auto res = origin_simd[0]--; + + assert(origin_simd[0] == static_cast<_Tp>(11)); + static_assert(!is_simd_reference::value); + assert(res == static_cast<_Tp>(12)); + + } else { + assert(origin_simd[0] == static_cast<_Tp>(7)); + + auto res = origin_simd[0]--; + + assert(origin_simd[0] == static_cast<_Tp>(6)); + static_assert(!is_simd_reference::value); + assert(res == static_cast<_Tp>(7)); + } + } + } +}; +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(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.reference/reference_swap.pass.cpp b/libcxx/test/std/experimental/simd/simd.reference/reference_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.reference/reference_swap.pass.cpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// 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] +// friend void swap(reference&& a, reference&& b) noexcept; +// friend void swap(value_type& a, reference&& b) noexcept; +// friend void swap(reference&& a, value_type& b) noexcept; + +#include "../test_utils.h" +#include +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckReferenceSwap { + template + void operator()() { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> >= 2) { + ex::simd<_Tp, SimdAbi> reference_simd([](_Tp i) { return static_cast<_Tp>(i + 2); }); + + { + assert(reference_simd[0] == static_cast<_Tp>(2)); + assert(reference_simd[1] == static_cast<_Tp>(3)); + swap(reference_simd[0], reference_simd[1]); + assert(reference_simd[1] == static_cast<_Tp>(2)); + assert(reference_simd[0] == static_cast<_Tp>(3)); + } + + { + _Tp value_to_swap = static_cast<_Tp>(4); + assert(value_to_swap == static_cast<_Tp>(4)); + + assert(reference_simd[0] == static_cast<_Tp>(3)); + swap(value_to_swap, reference_simd[0]); + assert(value_to_swap == static_cast<_Tp>(3)); + assert(reference_simd[0] == static_cast<_Tp>(4)); + } + { + _Tp value_to_swap = static_cast<_Tp>(6); + assert(value_to_swap == static_cast<_Tp>(6)); + + assert(reference_simd[0] == static_cast<_Tp>(4)); + swap(reference_simd[0], value_to_swap); + assert(value_to_swap == static_cast<_Tp>(4)); + assert(reference_simd[0] == static_cast<_Tp>(6)); + } + } + } +}; +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(); } \ No newline at end of file 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,36 @@ +//===----------------------------------------------------------------------===// +// +// 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 CheckReferenceValueType { + template + void operator()() { + ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return static_cast<_Tp>(i + 2); }); + static_assert(_Tp(2) == static_cast<_Tp>(2)); + static_assert(std::is_same_v); + } +}; +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(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.traits/abi_for_size.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/abi_for_size.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/experimental/simd/simd.traits/abi_for_size.pass.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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.traits] -// template struct abi_for_size { using type = see below ; -// }; template using ex::abi_for_size_t = typename -// ex::abi_for_size::type; - -#include -#include - -#include "test_macros.h" - -namespace ex = std::experimental::parallelism_v2; - -static_assert(std::is_same::type, - ex::simd_abi::fixed_size<4>>::value, - ""); - -static_assert(std::is_same, - ex::simd_abi::fixed_size<4>>::value, - ""); - -int main(int, char**) { - return 0; -} diff --git a/libcxx/test/std/experimental/simd/simd.traits/is_abi_tag.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/is_abi_tag.pass.cpp --- a/libcxx/test/std/experimental/simd/simd.traits/is_abi_tag.pass.cpp +++ b/libcxx/test/std/experimental/simd/simd.traits/is_abi_tag.pass.cpp @@ -12,103 +12,66 @@ // // [simd.traits] // template struct is_abi_tag; -// template inline constexpr bool ex::is_abi_tag_v = -// ex::is_abi_tag::value; +// template inline constexpr bool ex::is_abi_tag_v = ex::is_abi_tag::value; +#include "../test_utils.h" #include #include -#include "test_macros.h" namespace ex = std::experimental::parallelism_v2; -struct UserType {}; - -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); - -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); - -static_assert(ex::is_abi_tag::value, ""); -static_assert( - !std::is_same>::value, - ""); - -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); -static_assert(ex::is_abi_tag>::value, ""); - -static_assert(!ex::is_abi_tag::value, ""); -static_assert(!ex::is_abi_tag::value, ""); -static_assert(!ex::is_abi_tag::value, ""); -static_assert(!ex::is_abi_tag::value, ""); -static_assert(!ex::is_abi_tag>::value, ""); -static_assert(!ex::is_abi_tag>::value, ""); -static_assert(!ex::is_abi_tag>::value, ""); -static_assert(!ex::is_abi_tag>::value, ""); - -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); - -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); - -static_assert(ex::is_abi_tag_v, ""); -static_assert( - !std::is_same>::value, - ""); - -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); -static_assert(ex::is_abi_tag_v>, ""); - -static_assert(!ex::is_abi_tag_v, ""); -static_assert(!ex::is_abi_tag_v, ""); -static_assert(!ex::is_abi_tag_v, ""); -static_assert(!ex::is_abi_tag_v, ""); -static_assert(!ex::is_abi_tag_v>, ""); -static_assert(!ex::is_abi_tag_v>, ""); -static_assert(!ex::is_abi_tag_v>, ""); -static_assert(!ex::is_abi_tag_v>, ""); +struct CheckIsAbiTagTrue { + template + void operator()() { + static_assert(_Tp::value); + } +}; + +struct CheckIsAbiTagFalse { + template + void operator()() { + static_assert(!_Tp::value); + } +}; + +struct CheckIsAbiTagVTrue { + template + void operator()() { + static_assert(_Tp); + } +}; + +struct CheckIsAbiTagVFalse { + template + void operator()() { + static_assert(!_Tp); + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + if constexpr (std::is_same::value) { + F{}.template operator()>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()>(); + F{}.template operator()>>(); + F{}.template operator()>>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()>(); + } else { + F{}.template operator()>(); + F{}.template operator()>>(); + F{}.template operator()>>(); + } + + test_simd_abi(); +} int main(int, char**) { - return 0; -} + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.traits/is_simd.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/is_simd.pass.cpp --- a/libcxx/test/std/experimental/simd/simd.traits/is_simd.pass.cpp +++ b/libcxx/test/std/experimental/simd/simd.traits/is_simd.pass.cpp @@ -15,116 +15,62 @@ // template inline constexpr bool ex::is_simd_v = // ex::is_simd::value; +#include "../test_utils.h" #include #include -#include "test_macros.h" namespace ex = std::experimental::parallelism_v2; -struct UserType {}; - -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); - -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); - -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); - -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); -static_assert(ex::is_simd>::value, ""); - -static_assert(!ex::is_simd::value, ""); -static_assert(!ex::is_simd::value, ""); -static_assert(!ex::is_simd::value, ""); -static_assert(!ex::is_simd>::value, ""); -static_assert(!ex::is_simd>::value, ""); -static_assert(!ex::is_simd::value, ""); - -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); - -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); - -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); - -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); -static_assert(ex::is_simd_v>, ""); - -static_assert(!ex::is_simd_v, ""); -static_assert(!ex::is_simd_v, ""); -static_assert(!ex::is_simd_v, ""); -static_assert(!ex::is_simd_v>, ""); -static_assert(!ex::is_simd_v>, ""); -static_assert(!ex::is_simd_v, ""); +struct CheckIsSimdTrue { + template + void operator()() { + static_assert(_Tp::value); + } +}; + +struct CheckIsSimdFalse { + template + void operator()() { + static_assert(!_Tp::value); + } +}; + +struct CheckIsSimdVTrue { + template + void operator()() { + static_assert(_Tp); + } +}; + +struct CheckIsSimdVFalse { + template + void operator()() { + static_assert(!_Tp); + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + if constexpr (std::is_same::value) { + F{}.template operator()>>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()>(); + F{}.template operator()>>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()>>(); + } else { + F{}.template operator()>(); + F{}.template operator()>>(); + } + + test_simd_abi(); +} int main(int, char**) { - return 0; -} + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.traits/is_simd_flag_type.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/is_simd_flag_type.pass.cpp --- a/libcxx/test/std/experimental/simd/simd.traits/is_simd_flag_type.pass.cpp +++ b/libcxx/test/std/experimental/simd/simd.traits/is_simd_flag_type.pass.cpp @@ -12,41 +12,70 @@ // // [simd.traits] // template struct is_simd_flag_type; -// template inline constexpr bool ex::is_simd_flag_type_v = -// ex::is_simd_flag_type::value; +// template inline constexpr bool ex::is_simd_flag_type_v = ex::is_simd_flag_type::value; +#include "../test_utils.h" #include #include -#include "test_macros.h" namespace ex = std::experimental::parallelism_v2; -struct UserType {}; +struct CheckIsSimdFlagTypeTrue { + template + void operator()() { + static_assert(_Tp::value); + } +}; -static_assert(ex::is_simd_flag_type::value, ""); -static_assert(ex::is_simd_flag_type::value, ""); -static_assert(ex::is_simd_flag_type>::value, ""); -static_assert(ex::is_simd_flag_type>::value, ""); +struct CheckIsSimdFlagTypeFalse { + template + void operator()() { + static_assert(!_Tp::value); + } +}; -static_assert(!ex::is_simd_flag_type::value, ""); -static_assert(!ex::is_simd_flag_type::value, ""); -static_assert(!ex::is_simd_flag_type::value, ""); -static_assert(!ex::is_simd_flag_type::value, ""); -static_assert(!ex::is_simd_flag_type>::value, ""); -static_assert(!ex::is_simd_flag_type>::value, ""); +struct CheckIsSimdFlagTypeVTrue { + template + void operator()() { + static_assert(_Tp); + } +}; -static_assert(ex::is_simd_flag_type_v, ""); -static_assert(ex::is_simd_flag_type_v, ""); -static_assert(ex::is_simd_flag_type_v>, ""); -static_assert(ex::is_simd_flag_type_v>, ""); +struct CheckIsSimdFlagTypeVFalse { + template + void operator()() { + static_assert(!_Tp); + } +}; -static_assert(!ex::is_simd_flag_type_v, ""); -static_assert(!ex::is_simd_flag_type_v, ""); -static_assert(!ex::is_simd_flag_type_v, ""); -static_assert(!ex::is_simd_flag_type_v, ""); -static_assert(!ex::is_simd_flag_type_v>, ""); -static_assert(!ex::is_simd_flag_type_v>, ""); +template +void test_simd_abi() {} +template +void test_simd_abi() { + if constexpr (std::is_same::value) { + F{}.template operator()>(); + F{}.template operator()>(); + F{}.template operator()>>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()>(); + F{}.template operator()>>(); + F{}.template operator()>>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()>(); + F{}.template operator()>(); + F{}.template operator()>>(); + } else { + F{}.template operator()>(); + F{}.template operator()>>(); + F{}.template operator()>>(); + } -int main(int, char**) { - return 0; + test_simd_abi(); } + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.traits/is_simd_mask.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/is_simd_mask.pass.cpp --- a/libcxx/test/std/experimental/simd/simd.traits/is_simd_mask.pass.cpp +++ b/libcxx/test/std/experimental/simd/simd.traits/is_simd_mask.pass.cpp @@ -12,142 +12,64 @@ // // [simd.traits] // template struct ex::is_simd_mask; -// template inline constexpr bool ex::is_simd_mask_v = -// ex::is_simd_mask::value; +// template inline constexpr bool ex::is_simd_mask_v = ex::is_simd_mask::value; +#include "../test_utils.h" #include #include -#include "test_macros.h" namespace ex = std::experimental::parallelism_v2; -struct UserType {}; - -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); - -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); - -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, ""); - -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, - ""); -static_assert(ex::is_simd_mask>::value, ""); -static_assert(ex::is_simd_mask>::value, - ""); - -static_assert(!ex::is_simd_mask::value, ""); -static_assert(!ex::is_simd_mask::value, ""); -static_assert(!ex::is_simd_mask::value, ""); -static_assert(!ex::is_simd_mask>::value, ""); -static_assert(!ex::is_simd_mask>::value, ""); -static_assert(!ex::is_simd_mask::value, ""); - -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); - -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); - -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); - -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); -static_assert(ex::is_simd_mask_v>, ""); - -static_assert(!ex::is_simd_mask_v, ""); -static_assert(!ex::is_simd_mask_v, ""); -static_assert(!ex::is_simd_mask_v, ""); -static_assert(!ex::is_simd_mask_v>, ""); -static_assert(!ex::is_simd_mask_v>, ""); -static_assert(!ex::is_simd_mask_v, ""); +struct CheckIsSimdMaskTrue { + template + void operator()() { + static_assert(_Tp::value); + } +}; + +struct CheckIsSimdMaskFalse { + template + void operator()() { + static_assert(!_Tp::value); + } +}; + +struct CheckIsSimdMaskVTrue { + template + void operator()() { + static_assert(_Tp); + } +}; + +struct CheckIsSimdMaskVFalse { + template + void operator()() { + static_assert(!_Tp); + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + if constexpr (std::is_same::value) { + F{}.template operator()>>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()>(); + F{}.template operator()>>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()>>(); + } else { + F{}.template operator()>(); + F{}.template operator()>>(); + } + + test_simd_abi(); +} int main(int, char**) { - return 0; -} + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.traits/memory_alignment.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/memory_alignment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.traits/memory_alignment.pass.cpp @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// 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.traits] +// template struct memory_alignment; +// template +// inline constexpr size_t memory_alignment_v = memory_alignment::value; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckMemoryAlignmentNative { + template + void operator()() { + static_assert(ex::memory_alignment>{}.value == 16); + static_assert(ex::memory_alignment_v> == 16); + } +}; +struct CheckMemoryAlignmentCompatible { + template + void operator()() { + static_assert(ex::memory_alignment>{}.value == 16); + static_assert(ex::memory_alignment_v> == 16); + } +}; +struct CheckMemoryAlignmentScalar { + template + void operator()() { + if constexpr (std::is_same_v<_Tp, signed char> || std::is_same_v<_Tp, unsigned char>) { + static_assert(ex::memory_alignment>{}.value == 1); + static_assert(ex::memory_alignment_v> == 1); + } else if constexpr (std::is_same_v<_Tp, short> || std::is_same_v<_Tp, unsigned short> || + std::is_same_v<_Tp, char16_t>) { + static_assert(ex::memory_alignment>{}.value == 2); + static_assert(ex::memory_alignment_v> == 2); + } else if constexpr (std::is_same_v<_Tp, float> || std::is_same_v<_Tp, int> || std::is_same_v<_Tp, unsigned int> || + std::is_same_v<_Tp, wchar_t> || std::is_same_v<_Tp, char32_t>) { + static_assert(ex::memory_alignment>{}.value == 4); + static_assert(ex::memory_alignment_v> == 4); + } else if constexpr (std::is_same_v<_Tp, double> || std::is_same_v<_Tp, long long> || + std::is_same_v<_Tp, unsigned long long> || std::is_same_v<_Tp, long> || + std::is_same_v<_Tp, unsigned long>) { + static_assert(ex::memory_alignment>{}.value == 8); + static_assert(ex::memory_alignment_v> == 8); + } else { + static_assert(ex::memory_alignment>{}.value == 16); + static_assert(ex::memory_alignment_v> == 16); + } + } +}; + +struct CheckMemoryAlignmentFixedSize { + template + void operator()() { + if constexpr (std::is_same_v<_Tp, signed char> || std::is_same_v<_Tp, unsigned char>) { + static_assert(ex::memory_alignment>{}.value == next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == next_pow2(_Np)); + } else if constexpr (std::is_same_v<_Tp, short> || std::is_same_v<_Tp, unsigned short> || + std::is_same_v<_Tp, char16_t>) { + static_assert(ex::memory_alignment>{}.value == 2 * next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == 2 * next_pow2(_Np)); + } else if constexpr (std::is_same_v<_Tp, float> || std::is_same_v<_Tp, int> || std::is_same_v<_Tp, unsigned int> || + std::is_same_v<_Tp, wchar_t> || std::is_same_v<_Tp, char32_t>) { + static_assert(ex::memory_alignment>{}.value == 4 * next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == 4 * next_pow2(_Np)); + } else if constexpr (std::is_same_v<_Tp, double> || std::is_same_v<_Tp, long long> || + std::is_same_v<_Tp, unsigned long long> || std::is_same_v<_Tp, long> || + std::is_same_v<_Tp, unsigned long>) { + static_assert(ex::memory_alignment>{}.value == 8 * next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == 8 * next_pow2(_Np)); + } else { + static_assert(ex::memory_alignment>{}.value == 16 * next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == 16 * next_pow2(_Np)); + } + } +}; + +struct CheckMemoryAlignmentDeduce { + template + void operator()() { + if constexpr (std::is_same_v<_Tp, signed char> || std::is_same_v<_Tp, unsigned char>) { + static_assert(ex::memory_alignment>{}.value == next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == next_pow2(_Np)); + } else if constexpr (std::is_same_v<_Tp, short> || std::is_same_v<_Tp, unsigned short> || + std::is_same_v<_Tp, char16_t>) { + static_assert(ex::memory_alignment>{}.value == 2 * next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == 2 * next_pow2(_Np)); + } else if constexpr (std::is_same_v<_Tp, float> || std::is_same_v<_Tp, int> || std::is_same_v<_Tp, unsigned int> || + std::is_same_v<_Tp, wchar_t> || std::is_same_v<_Tp, char32_t>) { + static_assert(ex::memory_alignment>{}.value == 4 * next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == 4 * next_pow2(_Np)); + } else if constexpr (std::is_same_v<_Tp, double> || std::is_same_v<_Tp, long long> || + std::is_same_v<_Tp, unsigned long long> || std::is_same_v<_Tp, long> || + std::is_same_v<_Tp, unsigned long>) { + static_assert(ex::memory_alignment>{}.value == 8 * next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == 8 * next_pow2(_Np)); + } else { + static_assert(ex::memory_alignment>{}.value == 16 * next_pow2(_Np)); + static_assert(ex::memory_alignment_v> == 16 * next_pow2(_Np)); + } + } +}; +template +void test_simd_abi() {} +template +void test_simd_abi() { + if constexpr (std::is_same_v) { + F{}.template operator()<_Tp, ex::simd_abi::native<_Tp>, _Np>(); + } else if constexpr (std::is_same_v) { + F{}.template operator()<_Tp, ex::simd_abi::compatible<_Tp>, _Np>(); + } else if constexpr (std::is_same_v) { + F{}.template operator()<_Tp, ex::simd_abi::scalar, _Np>(); + } else if constexpr (std::is_same_v) { + F{}.template operator()<_Tp, ex::simd_abi::fixed_size<_Np>, _Np>(); + } else { + F{}.template operator()<_Tp, ex::simd_abi::deduce_t<_Tp, _Np>, _Np>(); + } + test_simd_abi(); +} + +int main() { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.traits/rebind_simd.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/rebind_simd.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.traits/rebind_simd.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// 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.traits] +// template struct rebind_simd { using type = see below ; }; +// template using rebind_simd_t = typename rebind_simd::type; + +#include "../test_utils.h" +#include +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckRebindSimdTrue { + template + void operator()() { + static_assert( + std::is_same>::type, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, ex::simd_size_v<_Up, SimdAbi>, SimdAbi>>>::value); + static_assert( + std::is_same>::type, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, ex::simd_size_v<_Up, SimdAbi>, SimdAbi>>>::value); + static_assert( + std::is_same>, + ex::simd<_Tp, ex::simd_abi::deduce_t<_Tp, ex::simd_size_v<_Up, SimdAbi>, SimdAbi>>>::value); + static_assert( + std::is_same>, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, ex::simd_size_v<_Up, SimdAbi>, SimdAbi>>>::value); + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + F{}.template operator()(); + + test_simd_abi(); +} + +int main() { test_all_simd_abi(); } diff --git a/libcxx/test/std/experimental/simd/simd.traits/resize_simd.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/resize_simd.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.traits/resize_simd.pass.cpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// 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.traits] +// template struct resize_simd { using type = see below ; }; +// template using resize_simd_t = typename resize_simd::type; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +class CheckResizeSimdTrue { +public: + template + void operator()(Func func) { + using U1 = ex::resize_simd, _Np), ex::simd_mask<_Tp, SimdAbi>>; + using V1 = ex::simd_mask<_Tp, SimdAbi>; + static_assert(std::is_same_v< + typename U1::type, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, func(ex::simd_size_v<_Tp, SimdAbi>, _Np), SimdAbi>>>); + + using U2 = ex::resize_simd, _Np), ex::simd<_Tp, SimdAbi>>; + using V2 = ex::simd<_Tp, SimdAbi>; + static_assert( + std::is_same_v, _Np), SimdAbi>>>); + + using U3 = ex::resize_simd_t, _Np), ex::simd_mask<_Tp, SimdAbi>>; + using V3 = ex::simd_mask<_Tp, SimdAbi>; + static_assert(std::is_same_v< + U3, + ex::simd_mask<_Tp, ex::simd_abi::deduce_t<_Tp, func(ex::simd_size_v<_Tp, SimdAbi>, _Np), SimdAbi>>>); + + using U4 = ex::resize_simd_t, _Np), ex::simd<_Tp, SimdAbi>>; + using V4 = ex::simd<_Tp, SimdAbi>; + static_assert( + std::is_same_v, _Np), SimdAbi>>>); + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, _Np, SimdAbi, std::plus>(std::plus()); + if constexpr (ex::simd_size_v<_Tp, SimdAbi> >= _Np) { + F{}.template operator()<_Tp, _Np, SimdAbi, std::divides>(std::divides()); + } + F{}.template operator()<_Tp, _Np, SimdAbi, std::multiplies>(std::multiplies()); + if constexpr (ex::simd_size_v < _Tp, SimdAbi >> _Np) { + F{}.template operator()<_Tp, _Np, SimdAbi, std::minus>(std::minus()); + } + + test_simd_abi(); +} +int main() { test_all_simd_abi(); } \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.traits/simd_size.pass.cpp b/libcxx/test/std/experimental/simd/simd.traits/simd_size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.traits/simd_size.pass.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// 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.traits] +//template > struct simd_size; +//template > +//inline constexpr size_t ex::simd_size_v = ex::simd_size::value; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckSimdSizeTrue { + template + void operator()() { + static_assert(_Tp::value == _Np); + } +}; + +struct CheckSimdSizeVTrue { + template + void operator()() { + static_assert(_Tp == _Np); + } +}; + +// the `native abi` will be set to different size(if turn to `AVX` within 256bit or more) +// current we will focus on 128bit +struct CheckSimdSizeExtraTrue { + template + void operator()() { + if constexpr (std::is_same<_Tp, double>::value || std::is_same<_Tp, long long>::value || + std::is_same<_Tp, unsigned long long>::value || std::is_same<_Tp, long>::value || + std::is_same<_Tp, unsigned long>::value) { + static_assert(_Np::value == 2); + } else if constexpr (std::is_same<_Tp, float>::value || std::is_same<_Tp, int>::value || + std::is_same<_Tp, unsigned int>::value || std::is_same<_Tp, wchar_t>::value || + std::is_same<_Tp, char32_t>::value) { + static_assert(_Np::value == 4); + } else if constexpr (std::is_same<_Tp, short>::value || std::is_same<_Tp, unsigned short>::value || + std::is_same<_Tp, char16_t>::value) { + static_assert(_Np::value == 8); + } else if constexpr (std::is_same<_Tp, signed char>::value || std::is_same<_Tp, unsigned char>::value) { + static_assert(_Np::value == 16); + } else { + static_assert(_Np::value == 1); + } + } +}; + +struct CheckSimdSizeVExtraTrue { + template + void operator()() { + if constexpr (std::is_same<_Tp, double>::value || std::is_same<_Tp, long long>::value || + std::is_same<_Tp, unsigned long long>::value || std::is_same<_Tp, long>::value || + std::is_same<_Tp, unsigned long>::value) { + static_assert(_Np == 2); + } else if constexpr (std::is_same<_Tp, float>::value || std::is_same<_Tp, int>::value || + std::is_same<_Tp, unsigned int>::value || std::is_same<_Tp, wchar_t>::value || + std::is_same<_Tp, char32_t>::value) { + static_assert(_Np == 4); + } else if constexpr (std::is_same<_Tp, short>::value || std::is_same<_Tp, unsigned short>::value || + std::is_same<_Tp, char16_t>::value) { + static_assert(_Np == 8); + } else if constexpr (std::is_same<_Tp, signed char>::value || std::is_same<_Tp, unsigned char>::value) { + static_assert(_Np == 16); + } else { + static_assert(_Np == 1); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + if constexpr (std::is_same::value) { + F{}.template operator(), 1>(); + F{}.template operator()>, _Np>(); + F{}.template operator()>, _Np>(); + } else if constexpr (std::is_same::value) { + F{}.template operator(), 1>(); + F{}.template operator()>, _Np>(); + F{}.template operator()>, _Np>(); + } else if constexpr (std::is_same::value) { + F{}.template operator()<_Tp, ex::simd_size<_Tp, ex::simd_abi::native<_Tp>>>(); + F{}.template operator()<_Tp, ex::simd_size<_Tp, ex::simd_abi::compatible<_Tp>>>(); + } else { + F{}.template operator()<_Tp, ex::simd_size_v<_Tp, ex::simd_abi::native<_Tp>>>(); + F{}.template operator()<_Tp, ex::simd_size_v<_Tp, ex::simd_abi::compatible<_Tp>>>(); + } + + test_simd_abi(); +} + +int main() { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} \ No newline at end of file diff --git a/libcxx/test/std/experimental/simd/simd.whereexpr/const_where_expression.pass.cpp b/libcxx/test/std/experimental/simd/simd.whereexpr/const_where_expression.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.whereexpr/const_where_expression.pass.cpp @@ -0,0 +1,160 @@ +//===----------------------------------------------------------------------===// +// +// 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.whereexpr] +// template +// class const_where_expression { +// const M& mask; // exposition only +// T& data; // exposition only +// public: +// const_where_expression(const const_where_expression&) = delete; +// const_where_expression& operator=(const const_where_expression&) = delete; +// T operator-() const && noexcept; +// T operator+() const && noexcept; +// T operator~() const && noexcept; +// template void copy_to(U* mem, Flags f) const &&; +// }; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckConstWhereExprOperatorMinus { + template + void operator()() { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> >= 2 && !std::is_unsigned_v<_Tp>) { + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + auto where_result = -ex::where(origin_simd < static_cast<_Tp>(2), origin_simd); + + 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); + + expected_value[1] = -static_cast<_Tp>(1); + + assert_simd_value_correct(where_result, expected_value); + } + } +}; + +struct CheckConstWhereExprOperatorPositive { + template + void operator()() { + const ex::simd<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + auto where_result = +ex::where(origin_simd < static_cast<_Tp>(2), origin_simd); + + 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(where_result, expected_value); + } +}; + +struct CheckConstWhereExprOperatorNegation { + template + void operator()() { + const ex::simd_mask<_Tp, SimdAbi> origin_simd([](_Tp i) { return i; }); + auto where_result = ~ex::where(true, origin_simd); + + constexpr size_t array_size = origin_simd.size(); + std::array expected_value; + + for (size_t i = 0; i < array_size; ++i) + expected_value[i] = false; + + assert_simd_mask_value_correct(where_result, expected_value); + } +}; + +struct CheckConstWhereExprCopyTo { + template + void operator()() { + { + const ex::simd<_Tp, SimdAbi> origin_simd(static_cast<_Tp>(5)); + constexpr size_t array_size = origin_simd.size(); + alignas(alignof(_Tp)) _Tp arr[array_size]{}; + + ex::where(origin_simd > 0, origin_simd).copy_to(arr, ex::element_aligned_tag()); + for (size_t i = 0; i < array_size; i++) { + assert(arr[i] == static_cast<_Tp>(5)); + } + } + { + const ex::simd<_Tp, SimdAbi> origin_simd(static_cast<_Tp>(5)); + constexpr size_t array_size = origin_simd.size(); + alignas(ex::memory_alignment_v, _Tp>) _Tp arr[array_size]{}; + + ex::where(origin_simd > 0, origin_simd).copy_to(arr, ex::vector_aligned_tag()); + for (size_t i = 0; i < array_size; i++) { + assert(arr[i] == static_cast<_Tp>(5)); + } + } + + { + const ex::simd<_Tp, SimdAbi> origin_simd(static_cast<_Tp>(5)); + constexpr size_t array_size = origin_simd.size(); + alignas(alignof(_Tp)) _Tp arr[array_size]{}; + + ex::where(origin_simd > 0, origin_simd).copy_to(arr, ex::overaligned_tag()); + for (size_t i = 0; i < array_size; i++) { + assert(arr[i] == static_cast<_Tp>(5)); + } + } + { + const ex::simd<_Tp, SimdAbi> origin_simd(static_cast<_Tp>(5)); + constexpr size_t array_size = origin_simd.size(); + alignas(alignof(_Tp)) _Tp arr[array_size]{}; + + ex::where(origin_simd > 0, origin_simd).copy_to(arr, ex::element_aligned_tag()); + for (size_t i = 0; i < array_size; i++) { + assert(arr[i] == static_cast<_Tp>(5)); + } + } + { + ex::simd_mask<_Tp, SimdAbi> origin_mask{}; + + if constexpr (origin_mask.size() == 4) { + bool buffer[] = {true, true, false, false}; + ex::where(origin_mask, origin_mask).copy_to(buffer, ex::element_aligned_tag()); + + assert(buffer[0]); + assert(buffer[1]); + assert(!buffer[2]); + assert(!buffer[3]); + } + } + { + _Tp val = static_cast<_Tp>(1); + ex::where(true, static_cast<_Tp>(3)).copy_to(&val, ex::element_aligned_tag()); + assert(val == static_cast<_Tp>(3)); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi, _Np>(); + test_simd_abi(); +} + +int main() { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); +} diff --git a/libcxx/test/std/experimental/simd/simd.whereexpr/where_expression.pass.cpp b/libcxx/test/std/experimental/simd/simd.whereexpr/where_expression.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.whereexpr/where_expression.pass.cpp @@ -0,0 +1,294 @@ +//===----------------------------------------------------------------------===// +// +// 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.whereexpr] +// template +// class where_expression : public const_where_expression { +// public: +// where_expression(const where_expression&) = delete; +// where_expression& operator=(const where_expression&) = delete; +// template void operator=(U&& x); +// template void operator+=(U&& x); +// template void operator-=(U&& x); +// template void operator*=(U&& x); +// template void operator/=(U&& x); +// template void operator%=(U&& x); +// template void operator&=(U&& x); +// template void operator|=(U&& x); +// template void operator^=(U&& x); +// template void operator<<=(U&& x); +// template void operator>>=(U&& x); +// void operator++(); +// void operator++(int); +// void operator--(); +// void operator--(int); +// template void copy_from(const U* mem, Flags); +// }; + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +struct CheckWhereExprOperators { + template + void operator()() { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> == 4) { + { + ex::simd_mask<_Tp, SimdAbi> lhs(true); + ex::simd_mask<_Tp, SimdAbi> rhs(false); + ex::where(lhs, rhs) = lhs; + assert(rhs[0] == true); + assert(rhs[1] == true); + assert(rhs[2] == true); + assert(rhs[3] == true); + } + { + _Tp buf[]{1, 2, 3, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) += 2; + assert(lhs[0] == static_cast<_Tp>(3)); + assert(lhs[1] == static_cast<_Tp>(4)); + assert(lhs[2] == static_cast<_Tp>(5)); + assert(lhs[3] == static_cast<_Tp>(6)); + } + { + _Tp buf[]{1, 2, 3, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) -= 2; + assert(lhs[0] == static_cast<_Tp>(-1)); + assert(lhs[1] == static_cast<_Tp>(0)); + assert(lhs[2] == static_cast<_Tp>(1)); + assert(lhs[3] == static_cast<_Tp>(2)); + } + { + _Tp buf[]{2, 2, 4, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) /= 2; + assert(lhs[0] == static_cast<_Tp>(1)); + assert(lhs[1] == static_cast<_Tp>(1)); + assert(lhs[2] == static_cast<_Tp>(2)); + assert(lhs[3] == static_cast<_Tp>(2)); + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + _Tp buf[]{3, 3, 4, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) %= 2; + assert(lhs[0] == static_cast<_Tp>(1)); + assert(lhs[1] == static_cast<_Tp>(1)); + assert(lhs[2] == static_cast<_Tp>(0)); + assert(lhs[3] == static_cast<_Tp>(0)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + _Tp buf[]{3, 3, 4, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) &= 2; + assert(lhs[0] == static_cast<_Tp>(2)); + assert(lhs[1] == static_cast<_Tp>(2)); + assert(lhs[2] == static_cast<_Tp>(0)); + assert(lhs[3] == static_cast<_Tp>(0)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + _Tp buf[]{3, 3, 4, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) |= 2; + assert(lhs[0] == static_cast<_Tp>(3)); + assert(lhs[1] == static_cast<_Tp>(3)); + assert(lhs[2] == static_cast<_Tp>(6)); + assert(lhs[3] == static_cast<_Tp>(6)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + _Tp buf[]{3, 3, 4, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) ^= 2; + assert(lhs[0] == static_cast<_Tp>(1)); + assert(lhs[1] == static_cast<_Tp>(1)); + assert(lhs[2] == static_cast<_Tp>(6)); + assert(lhs[3] == static_cast<_Tp>(6)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + _Tp buf[]{3, 3, 4, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) <<= 2; + assert(lhs[0] == static_cast<_Tp>(12)); + assert(lhs[1] == static_cast<_Tp>(12)); + assert(lhs[2] == static_cast<_Tp>(16)); + assert(lhs[3] == static_cast<_Tp>(16)); + } + } + { + if constexpr (!std::is_floating_point_v<_Tp>) { + _Tp buf[]{3, 3, 4, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) >>= 2; + assert(lhs[0] == static_cast<_Tp>(0)); + assert(lhs[1] == static_cast<_Tp>(0)); + assert(lhs[2] == static_cast<_Tp>(1)); + assert(lhs[3] == static_cast<_Tp>(1)); + } + } + { + _Tp buf[]{1, 2, 3, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs) *= 2; + assert(lhs[0] == static_cast<_Tp>(2)); + assert(lhs[1] == static_cast<_Tp>(4)); + assert(lhs[2] == static_cast<_Tp>(6)); + assert(lhs[3] == static_cast<_Tp>(8)); + } + { + _Tp buf[]{1, 2, 3, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs)++; + assert(lhs[0] == static_cast<_Tp>(2)); + assert(lhs[1] == static_cast<_Tp>(3)); + assert(lhs[2] == static_cast<_Tp>(4)); + assert(lhs[3] == static_cast<_Tp>(5)); + } + { + _Tp buf[]{1, 2, 3, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ++ex::where(rhs, lhs); + assert(lhs[0] == static_cast<_Tp>(2)); + assert(lhs[1] == static_cast<_Tp>(3)); + assert(lhs[2] == static_cast<_Tp>(4)); + assert(lhs[3] == static_cast<_Tp>(5)); + } + { + _Tp buf[]{1, 2, 3, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + ex::where(rhs, lhs)--; + assert(lhs[0] == static_cast<_Tp>(0)); + assert(lhs[1] == static_cast<_Tp>(1)); + assert(lhs[2] == static_cast<_Tp>(2)); + assert(lhs[3] == static_cast<_Tp>(3)); + } + { + _Tp buf[]{1, 2, 3, 4}; + ex::simd<_Tp, SimdAbi> lhs; + lhs.copy_from(buf, ex::element_aligned_tag()); + ex::simd_mask<_Tp, SimdAbi> rhs(true); + + --ex::where(rhs, lhs); + assert(lhs[0] == static_cast<_Tp>(0)); + assert(lhs[1] == static_cast<_Tp>(1)); + assert(lhs[2] == static_cast<_Tp>(2)); + assert(lhs[3] == static_cast<_Tp>(3)); + } + } + } +}; + +struct CheckWhereExprCopyFrom { + template + void operator()() { + if constexpr (ex::simd_size_v<_Tp, SimdAbi> == 4) { + { + alignas(alignof(_Tp)) + const _Tp buffer[]{static_cast<_Tp>(-1), static_cast<_Tp>(-2), static_cast<_Tp>(-3), static_cast<_Tp>(-4)}; + ex::simd<_Tp, SimdAbi> lhs([](_Tp i) { return i; }); + ex::where(lhs < 2, lhs).copy_from(buffer, ex::element_aligned_tag()); + assert(lhs[0] == static_cast<_Tp>(-1)); + assert(lhs[1] == static_cast<_Tp>(-2)); + assert(lhs[2] == static_cast<_Tp>(2)); + assert(lhs[3] == static_cast<_Tp>(3)); + } + { + alignas(alignof(_Tp)) + const _Tp buffer[]{static_cast<_Tp>(-1), static_cast<_Tp>(-2), static_cast<_Tp>(-3), static_cast<_Tp>(-4)}; + ex::simd<_Tp, SimdAbi> lhs([](_Tp i) { return i; }); + ex::where(lhs < 2, lhs).copy_from(buffer, ex::overaligned_tag()); + assert(lhs[0] == static_cast<_Tp>(-1)); + assert(lhs[1] == static_cast<_Tp>(-2)); + assert(lhs[2] == static_cast<_Tp>(2)); + assert(lhs[3] == static_cast<_Tp>(3)); + } + { + alignas(ex::memory_alignment_v, _Tp>) + const _Tp buffer[]{static_cast<_Tp>(-1), static_cast<_Tp>(-2), static_cast<_Tp>(-3), static_cast<_Tp>(-4)}; + ex::simd<_Tp, SimdAbi> lhs([](_Tp i) { return i; }); + ex::where(lhs < 2, lhs).copy_from(buffer, ex::vector_aligned_tag()); + assert(lhs[0] == static_cast<_Tp>(-1)); + assert(lhs[1] == static_cast<_Tp>(-2)); + assert(lhs[2] == static_cast<_Tp>(2)); + assert(lhs[3] == static_cast<_Tp>(3)); + } + } + + { + constexpr _Tp rhs = static_cast<_Tp>(1); + _Tp lhs = 3; + ex::where(true, lhs).copy_from(&rhs, ex::element_aligned_tag()); + assert(lhs == 1); + } + } +}; + +template +void test_simd_abi() {} +template +void test_simd_abi() { + F{}.template operator()<_Tp, SimdAbi>(); + test_simd_abi(); +} +int main() { + test_all_simd_abi(); + test_all_simd_abi(); +} diff --git a/libcxx/test/std/experimental/simd/test_utils.h b/libcxx/test/std/experimental/simd/test_utils.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/test_utils.h @@ -0,0 +1,116 @@ +//===----------------------------------------------------------------------===// +// +// 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 TEST_UTIL_H +#define TEST_UTIL_H + +#include +#include + +namespace ex = std::experimental::parallelism_v2; + +template +struct is_simd_reference : std::false_type {}; + +template +struct is_simd_reference> : std::true_type {}; + +template +void assert_simd_value_correct(const ex::simd<_Tp, SimdAbi>& origin_simd, _Tp* 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, bool* expected_value) { + for (size_t i = 0; i < origin_mask.size(); ++i) + assert(origin_mask[i] == expected_value[i]); +} + +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<_Tp, ArraySize>& expected_value) { + for (size_t i = 0; i < origin_mask.size(); ++i) + assert(origin_mask[i] == expected_value[i]); +} + +template +void assert_simd_value_correct(const ex::simd<_Tp, SimdAbi>& origin_simd, + const std::array& expected_value) { + for (size_t i = 0; i < origin_simd.size(); ++i) + assert(origin_simd[i] == static_cast<_Tp>(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] == static_cast<_Tp>(expected_value[i])); +} + +template +void test_simd_abi(); + +template +void test_all_simd_abi(std::integer_sequence) { + using namespace ex; + + (test_simd_abi, + simd_abi::fixed_size<_Np + 1>, + simd_abi::scalar, + simd_abi::compatible<_Tp>, + simd_abi::deduce_t<_Tp, _Np + 1>>(), + ...); +} + +template +void test_all_simd_abi() { + [[maybe_unused]] constexpr auto integer_seq_from_directly_build = + std::integer_sequence(); + [[maybe_unused]] constexpr auto integer_seq_from_make_integer = std::make_integer_sequence(); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); + test_all_simd_abi(integer_seq_from_make_integer); +} +constexpr std::size_t max_simd_size = 32; + +// credit to: https://stackoverflow.com/a/466242 +constexpr size_t next_pow2(size_t v) noexcept { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} +#endif // TEST_UTIL_H \ No newline at end of file