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 @@ -38,6 +38,16 @@ using abi_type = _Abi; static _LIBCPP_HIDE_FROM_ABI constexpr size_t size() noexcept { return simd_size_v; } + + explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s) : __s_(__s) {} + + // 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,15 @@ using abi_type = _Abi; static _LIBCPP_HIDE_FROM_ABI constexpr size_t size() noexcept { return simd_type::size(); } + + explicit _LIBCPP_HIDE_FROM_ABI simd_mask(const _Storage& __s) : __s_(__s) {} + + // 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,7 +13,9 @@ #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 #include @@ -54,6 +56,19 @@ return __v ? (std::numeric_limits())>::max()) : 0; } +template +struct __is_value_preserving_convertible : std::false_type {}; + +template +struct __is_value_preserving_convertible<__From, __To, decltype(static_cast<__To>(std::declval<__From>()))> + : std::true_type {}; + +template +constexpr bool __can_broadcast_v = + (__is_vectorizable_v<_Up> && __is_value_preserving_convertible<_Up, _Tp>::value) || + (!__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 = std::experimental::parallelism_v2::__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,86 @@ +//===----------------------------------------------------------------------===// +// +// 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; + +#include "../test_utils.h" + +namespace ex = std::experimental::parallelism_v2; + +template +struct BroadCastHelper { + const std::array& origin_value; + + BroadCastHelper(const std::array& origin_value) : origin_value(origin_value) {} + + template + void operator()() const { + if constexpr (is_value_preserving_convertible::value) { + ex::simd expected_simd_from_vectorizable_type(static_cast(3)); + assert_simd_value_correct(expected_simd_from_vectorizable_type, origin_value); + } + } +}; + +template +struct CheckSimdBroadcastCtorFromVectorizedType { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + std::array origin_value; + for (std::size_t i = 0; i < array_size; ++i) + origin_value[i] = static_cast(3); + + types::for_each(arithmetic_no_bool_types(), BroadCastHelper(origin_value)); + } +}; + +template +class implicit_val { + T val; + +public: + implicit_val(T val) : val(val) {} + operator T() const { return val; } +}; + +template +struct CheckSimdBroadcastCtor { + template + void operator()() { + constexpr std::size_t array_size = ex::simd_size_v; + std::array origin_value; + for (std::size_t i = 0; i < array_size; ++i) + origin_value[i] = static_cast(3); + + implicit_val implicit_convert_to_3(3); + ex::simd 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 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) { + unsigned int uint_value_3 = static_cast(3); + ex::simd expected_simd_from_uint(std::move(uint_value_3)); + assert_simd_value_correct(expected_simd_from_uint, origin_value); + } + } +}; + +int main(int, char**) { + test_all_simd_abi(); + test_all_simd_abi(); + return 0; +} diff --git a/libcxx/test/std/experimental/simd/simd.class/simd_ctor_broadcast.verify.cpp b/libcxx/test/std/experimental/simd/simd.class/simd_ctor_broadcast.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/experimental/simd/simd.class/simd_ctor_broadcast.verify.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// 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; + +#include "../test_utils.h" + +namespace ex = std::experimental::parallelism_v2; + +template +class no_implicit_val { + T val; + +public: + no_implicit_val(T val) : val(val) {} +}; + +int main(int, char**) { + ex::native_simd s1(2.0f); + // expected-error-re@* {{no matching constructor for initialization of {{.*}}}} + + no_implicit_val value(3); + ex::native_simd s2(std::move(value)); + // expected-error-re@* {{no matching constructor for initialization of {{.*}}}} + + return 0; +} \ No newline at end of file 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,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] +// explicit simd_mask(value_type) noexcept; + +#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_value_correct(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,8 @@ #ifndef TEST_UTIL_H #define TEST_UTIL_H +#include +#include #include #include #include "type_algorithms.h" @@ -50,4 +52,24 @@ return pow; } +template +struct is_value_preserving_convertible : std::false_type {}; + +template +struct is_value_preserving_convertible(std::declval()))> : std::true_type {}; + +template +void assert_simd_value_correct(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_value_correct(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