diff --git a/libcxx/docs/Status/ParallelismProjects.csv b/libcxx/docs/Status/ParallelismProjects.csv --- a/libcxx/docs/Status/ParallelismProjects.csv +++ b/libcxx/docs/Status/ParallelismProjects.csv @@ -16,9 +16,11 @@ | `[parallel.simd.whereexpr] `_, "Where expression class templates", None, Yin Zhang, |In Progress| | `[parallel.simd.class] `_, "`Class template simd declaration and alias `_", [parallel.simd.abi], Yin Zhang, |Complete| | `[parallel.simd.class] `_, "`simd<>::size() `_", [parallel.simd.traits] simd_size[_v], Yin Zhang, |Complete| +| `[parallel.simd.class] `_, "`simd broadcast constructor `_", None, Yin Zhang, |Complete| | `[parallel.simd.class] `_, "Class template simd implementation", None, Yin Zhang, |In Progress| | `[parallel.simd.nonmembers] `_, "simd non-member operations", None, Yin Zhang, |In Progress| | `[parallel.simd.mask.class] `_, "`Class template simd_mask declaration and alias `_", [parallel.simd.abi], Yin Zhang, |Complete| | `[parallel.simd.mask.class] `_, "`simd_mask<>::size() `_", [parallel.simd.class] simd<>::size(), Yin Zhang, |Complete| +| `[parallel.simd.mask.class] `_, "`simd_mask broadcast constructor `_", None, Yin Zhang, |Complete| | `[parallel.simd.mask.class] `_, "Class template simd_mask implementation", None, Yin Zhang, |In Progress| | `[parallel.simd.mask.nonmembers] `_, "simd_mask non-member operations", None, Yin Zhang, |In Progress| diff --git a/libcxx/include/experimental/__simd/scalar.h b/libcxx/include/experimental/__simd/scalar.h --- a/libcxx/include/experimental/__simd/scalar.h +++ b/libcxx/include/experimental/__simd/scalar.h @@ -44,11 +44,15 @@ struct __simd_operations<_Tp, simd_abi::__scalar> { using _SimdStorage = __simd_storage<_Tp, simd_abi::__scalar>; using _MaskStorage = __mask_storage<_Tp, simd_abi::__scalar>; + + static _SimdStorage __broadcast(_Tp __v) noexcept { return {__v}; } }; template struct __mask_operations<_Tp, simd_abi::__scalar> { using _MaskStorage = __mask_storage<_Tp, simd_abi::__scalar>; + + static _MaskStorage __broadcast(bool __v) noexcept { return {__v}; } }; } // namespace parallelism_v2 diff --git a/libcxx/include/experimental/__simd/simd.h b/libcxx/include/experimental/__simd/simd.h --- a/libcxx/include/experimental/__simd/simd.h +++ b/libcxx/include/experimental/__simd/simd.h @@ -10,6 +10,7 @@ #ifndef _LIBCPP_EXPERIMENTAL___SIMD_SIMD_H #define _LIBCPP_EXPERIMENTAL___SIMD_SIMD_H +#include <__type_traits/remove_cvref.h> #include #include #include @@ -38,6 +39,14 @@ using abi_type = _Abi; static _LIBCPP_HIDE_FROM_ABI constexpr size_t size() noexcept { return simd_size_v; } + + // broadcast constructor + template >, int> = 0> + _LIBCPP_HIDE_FROM_ABI simd(_Up&& __v) noexcept : __s_(_Impl::__broadcast(static_cast(__v))) {} + + // scalar access [simd.subscr] + // Add operator[] temporarily to test braodcast. Add test for it in later patch. + _LIBCPP_HIDE_FROM_ABI value_type operator[](size_t __i) const { return __s_.__get(__i); } }; template diff --git a/libcxx/include/experimental/__simd/simd_mask.h b/libcxx/include/experimental/__simd/simd_mask.h --- a/libcxx/include/experimental/__simd/simd_mask.h +++ b/libcxx/include/experimental/__simd/simd_mask.h @@ -37,6 +37,13 @@ using abi_type = _Abi; static _LIBCPP_HIDE_FROM_ABI constexpr size_t size() noexcept { return simd_type::size(); } + + // broadcast constructor + _LIBCPP_HIDE_FROM_ABI explicit simd_mask(value_type __v) noexcept : __s_(_Impl::__broadcast(__v)) {} + + // scalar access [simd.mask.subscr] + // Add operator[] temporarily to test braodcast. Add test for it in later patch. + _LIBCPP_HIDE_FROM_ABI value_type operator[](size_t __i) const { return __s_.__get(__i); } }; template diff --git a/libcxx/include/experimental/__simd/utility.h b/libcxx/include/experimental/__simd/utility.h --- a/libcxx/include/experimental/__simd/utility.h +++ b/libcxx/include/experimental/__simd/utility.h @@ -13,8 +13,12 @@ #include <__type_traits/is_arithmetic.h> #include <__type_traits/is_const.h> #include <__type_traits/is_constant_evaluated.h> +#include <__type_traits/is_convertible.h> #include <__type_traits/is_same.h> +#include <__type_traits/is_unsigned.h> #include <__type_traits/is_volatile.h> +#include <__type_traits/void_t.h> +#include <__utility/declval.h> #include #include @@ -26,7 +30,7 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL inline namespace parallelism_v2 { template -constexpr bool __is_vectorizable_v = +inline constexpr bool __is_vectorizable_v = is_arithmetic_v<_Tp> && !is_const_v<_Tp> && !is_volatile_v<_Tp> && !is_same_v<_Tp, bool>; template @@ -54,6 +58,19 @@ return __v ? (numeric_limits())>::max()) : 0; } +template +inline constexpr bool __is_non_narrowing_convertible_v = false; + +template +inline constexpr bool __is_non_narrowing_convertible_v<_From, _To, std::void_t()})>> = + true; + +template +inline constexpr bool __can_broadcast_v = + (__is_vectorizable_v<_Up> && __is_non_narrowing_convertible_v<_Up, _Tp>) || + (!__is_vectorizable_v<_Up> && is_convertible_v<_Up, _Tp>) || is_same_v<_Up, int> || + (is_same_v<_Up, unsigned int> && is_unsigned_v<_Tp>); + } // namespace parallelism_v2 _LIBCPP_END_NAMESPACE_EXPERIMENTAL diff --git a/libcxx/include/experimental/__simd/vec_ext.h b/libcxx/include/experimental/__simd/vec_ext.h --- a/libcxx/include/experimental/__simd/vec_ext.h +++ b/libcxx/include/experimental/__simd/vec_ext.h @@ -48,11 +48,28 @@ struct __simd_operations<_Tp, simd_abi::__vec_ext<_Np>> { using _SimdStorage = __simd_storage<_Tp, simd_abi::__vec_ext<_Np>>; using _MaskStorage = __mask_storage<_Tp, simd_abi::__vec_ext<_Np>>; + + static _SimdStorage __broadcast(_Tp __v) noexcept { + _SimdStorage __result; + for (int __i = 0; __i < _Np; ++__i) { + __result.__set(__i, __v); + } + return __result; + } }; template struct __mask_operations<_Tp, simd_abi::__vec_ext<_Np>> { using _MaskStorage = __mask_storage<_Tp, simd_abi::__vec_ext<_Np>>; + + static _MaskStorage __broadcast(bool __v) noexcept { + _MaskStorage __result; + auto __all_bits_v = experimental::__set_all_bits<_Tp>(__v); + for (int __i = 0; __i < _Np; ++__i) { + __result.__set(__i, __all_bits_v); + } + return __result; + } }; } // namespace parallelism_v2 diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_ctor_broadcast.pass.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_ctor_broadcast.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_ctor_broadcast.pass.cpp @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// +// 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; + +// GCC returns __int128 unsigned with garbled data in higher 64 bits. +// This is likely a bug in GCC implementation. Investigation needed. +// XFAIL: gcc-13 + +#include "../test_utils.h" + +namespace ex = std::experimental::parallelism_v2; + +template +struct BroadCastHelper { + const std::array& expected_value; + + BroadCastHelper(const std::array& value) : expected_value(value) {} + + template + void operator()() const { + if constexpr (is_non_narrowing_convertible_v) { + ex::simd simd_broadcast_from_vectorizable_type(static_cast(3)); + assert_simd_values_equal(simd_broadcast_from_vectorizable_type, expected_value); + } + } +}; + +template +struct CheckSimdBroadcastCtorFromVectorizedType { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + std::array expected_value; + std::fill(expected_value.begin(), expected_value.end(), 3); + + types::for_each(arithmetic_no_bool_types(), BroadCastHelper(expected_value)); + } +}; + +template +class implicit_type { + T val; + +public: + implicit_type(T v) : val(v) {} + operator T() const { return val; } +}; + +template +struct CheckSimdBroadcastCtor { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + std::array expected_value; + std::fill(expected_value.begin(), expected_value.end(), 3); + + implicit_type implicit_convert_to_3(3); + ex::simd simd_broadcast_from_implicit_type(std::move(implicit_convert_to_3)); + assert_simd_values_equal(simd_broadcast_from_implicit_type, expected_value); + + ex::simd simd_broadcast_from_int(3); + assert_simd_values_equal(simd_broadcast_from_int, expected_value); + + if constexpr (std::is_unsigned_v) { + ex::simd simd_broadcast_from_uint(3u); + assert_simd_values_equal(simd_broadcast_from_uint, expected_value); + } + } +}; + +template +class no_implicit_type { + T val; + +public: + no_implicit_type(T v) : val(v) {} +}; + +template , class = void> +struct has_broadcast_ctor : std::false_type {}; + +template +struct has_broadcast_ctor(std::declval()))>> + : std::true_type {}; + +template +struct CheckBroadcastCtorTraitsHelper { + template + void operator()() const { + if constexpr (std::is_same_v) + static_assert(has_broadcast_ctor::value); + else if constexpr (std::is_same_v && std::is_unsigned_v) + static_assert(has_broadcast_ctor::value); + else if constexpr (is_non_narrowing_convertible_v) + static_assert(has_broadcast_ctor::value); + else + static_assert(!has_broadcast_ctor::value); + } +}; + +template +struct CheckBroadcastCtorTraits { + template + void operator()() { + types::for_each(arithmetic_no_bool_types(), CheckBroadcastCtorTraitsHelper()); + + static_assert(!has_broadcast_ctor, T, SimdAbi>::value); + static_assert(has_broadcast_ctor, T, SimdAbi>::value); + } +}; + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_ctor_broadcast.pass.cpp b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_ctor_broadcast.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.mask.class/simd_mask_ctor_broadcast.pass.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// 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; + +// GCC returns __int128 unsigned with garbled data in higher 64 bits. +// This is likely a bug in GCC implementation. Investigation needed. +// XFAIL: gcc-13 + +#include "../test_utils.h" +#include + +namespace ex = std::experimental::parallelism_v2; + +template +struct CheckMaskBroadcastCtor { + template + void operator()() { + constexpr size_t array_size = ex::simd_size_v; + const ex::simd_mask mask_ctor_from_broadcast(false); + const std::array expected_value{}; + assert_simd_mask_values_equal(mask_ctor_from_broadcast, expected_value); + } +}; + +int main(int, char**) { + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/test_utils.h b/libcxx/test/std/experimental/simd/test_utils.h --- a/libcxx/test/std/experimental/simd/test_utils.h +++ b/libcxx/test/std/experimental/simd/test_utils.h @@ -9,6 +9,10 @@ #ifndef TEST_UTIL_H #define TEST_UTIL_H +#include +#include +#include +#include #include #include #include "type_algorithms.h" @@ -55,4 +59,23 @@ return pow; } +template +inline constexpr bool is_non_narrowing_convertible_v = false; + +template +inline constexpr bool is_non_narrowing_convertible_v()})>> = true; + +template +void assert_simd_values_equal(const ex::simd& origin_simd, const std::array& expected_value) { + for (std::size_t i = 0; i < origin_simd.size(); ++i) + assert(origin_simd[i] == static_cast(expected_value[i])); +} + +template +void assert_simd_mask_values_equal(const ex::simd_mask& origin_mask, + const std::array& expected_value) { + for (std::size_t i = 0; i < origin_mask.size(); ++i) + assert(origin_mask[i] == expected_value[i]); +} + #endif // TEST_UTIL_H