timshen created this revision. timshen added reviewers: mclow.lists, EricWF. Herald added a subscriber: sanjoy.
This patch implements the extended version (see P0820) of P0214 concat() and split(). https://reviews.llvm.org/D41412 Files: libcxx/include/experimental/simd libcxx/test/std/experimental/simd/simd.horizontal/concat.pass.cpp libcxx/test/std/experimental/simd/simd.horizontal/split.pass.cpp
Index: libcxx/test/std/experimental/simd/simd.horizontal/split.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/experimental/simd/simd.horizontal/split.pass.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <experimental/simd> +// +// template <size_t... Sizes, class T, class Abi> +// tuple<simd<T, abi_for_size_t<Sizes>>...> split(const simd<T, Abi>&); +// +// template <size_t... Sizes, class T, class Abi> +// tuple<simd_mask<T, abi_for_size_t<Sizes>>...> split(const simd_mask<T, Abi>&); +// +// template <class V, class Abi> +// array<V, simd_size_v<typename V::value_type, Abi> / V::size()> split( +// const simd<typename V::value_type, Abi>&); +// +// template <class V, class Abi> +// array<V, simd_size_v<typename V::value_type, Abi> / V::size()> split( +// const simd_mask<typename V::value_type, Abi>&); +// +// template <size_t n, class T, class A> +// array<simd<T, rebind_abi_t<T, simd_size_v<T, A> / n, A>>, n> split_by( +// const simd<T, A>& x); +// +// template <size_t n, class T, class A> +// array<simd_mask<T, rebind_abi_t<T, simd_size_v<T, A> / n, A>>, n> split_by( +// const simd_mask<T, A>& x); + +#include <experimental/simd> +#include <cassert> +#include <cstdint> + +using namespace std::experimental::parallelism_v2; + +void test_split() { + auto t = split<1, 2, 3>(fixed_size_simd<int, 6>([](int i) { return i; })); + static_assert(std::tuple_size<decltype(t)>::value == 3, ""); + + assert(std::get<0>(t).size() == 1); + assert(std::get<0>(t)[0] == 0); + + assert(std::get<1>(t).size() == 2); + assert(std::get<1>(t)[0] == 1); + assert(std::get<1>(t)[1] == 2); + + assert(std::get<2>(t).size() == 3); + assert(std::get<2>(t)[0] == 3); + assert(std::get<2>(t)[1] == 4); + assert(std::get<2>(t)[2] == 5); +} + +void test_split_array() { + { + auto arr = split_by<2>(fixed_size_simd<int, 6>([](int i) { return i; })); + static_assert(arr.size() == 2, ""); + + assert(arr[0].size() == 3); + assert(arr[0][0] == 0); + assert(arr[0][1] == 1); + assert(arr[0][2] == 2); + + assert(arr[1].size() == 3); + assert(arr[1][0] == 3); + assert(arr[1][1] == 4); + assert(arr[1][2] == 5); + } + { + auto arr = split<fixed_size_simd<int, 3>>( + fixed_size_simd<int, 6>([](int i) { return i; })); + static_assert(arr.size() == 2, ""); + + assert(arr[0].size() == 3); + assert(arr[0][0] == 0); + assert(arr[0][1] == 1); + assert(arr[0][2] == 2); + + assert(arr[1].size() == 3); + assert(arr[1][0] == 3); + assert(arr[1][1] == 4); + assert(arr[1][2] == 5); + } +} + +void compile_split_propagate_abi() { + using compatible_simd_half = + simd<int, + rebind_abi_t<int, simd<int>::size() / 2, simd_abi::compatible<int>>>; + using native_simd_half = + simd<int, rebind_abi_t<int, native_simd<int>::size() / 2, + simd_abi::native<int>>>; + + static_assert( + std::is_same< + decltype( + split<simd<int>::size() / 2, simd<int>::size() / 2>(simd<int>())), + std::tuple<compatible_simd_half, compatible_simd_half>>::value, + ""); + + static_assert( + std::is_same<decltype( + split<native_simd<int>::size() / 2, + native_simd<int>::size() / 2>(native_simd<int>())), + std::tuple<native_simd_half, native_simd_half>>::value, + ""); + + static_assert(std::is_same<decltype(split_by<2>(simd<int>())), + std::array<compatible_simd_half, 2>>::value, + ""); + + static_assert(std::is_same<decltype(split_by<2>(native_simd<int>())), + std::array<native_simd_half, 2>>::value, + ""); +} + +int main() { + test_split(); + test_split_array(); +} Index: libcxx/test/std/experimental/simd/simd.horizontal/concat.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/experimental/simd/simd.horizontal/concat.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 + +// <experimental/simd> +// +// template <class T, class... Abis> +// simd<T, abi_for_size_t<T, (simd_size_v<T, Abis> + ...)>> +// concat(const simd<T, Abis>&...); +// +// template <class T, class Abi, size_t N> +// simd<T, rebind_abi_t<T, N * simd_size_v<T, Abi>, Abi>> +// concat(const std::array<simd<T, Abi>, N>& __v); +// +// template <class T, class... Abis> +// simd_mask<T, abi_for_size_t<T, (simd_size_v<T, Abis> + ...)>> +// concat(const simd_mask<T, Abis>&...); +// +// template <class T, class Abi, size_t N> +// simd_mask<T, rebind_abi_t<T, N * simd_size_v<T, Abi>, Abi>> +// concat(const std::array<simd_mask<T, Abi>, N>&); + +#include <experimental/simd> +#include <cassert> +#include <cstdint> + +using namespace std::experimental::parallelism_v2; + +void test_concat() { + auto v = concat(fixed_size_simd<int, 1>([](int i) { return i; }), + fixed_size_simd<int, 2>([](int i) { return i + 1; }), + fixed_size_simd<int, 3>([](int i) { return i + 3; })); + static_assert(v.size() == 6, ""); + assert(v[0] == 0); + assert(v[1] == 1); + assert(v[2] == 2); + assert(v[3] == 3); + assert(v[4] == 4); + assert(v[5] == 5); +} + +void test_concat_array() { + std::array<fixed_size_simd<int, 2>, 2> arr; + arr[0] = fixed_size_simd<int, 2>([](int) { return 0; }); + arr[1] = fixed_size_simd<int, 2>([](int) { return 1; }); + + auto v = concat(arr); + static_assert(v.size() == 4, ""); + assert(v[0] == 0); + assert(v[1] == 0); + assert(v[2] == 1); + assert(v[3] == 1); +} + +void compile_split_propagate_abi() { + static_assert(std::is_same<typename decltype( + concat(simd<int>(), simd<int>()))::abi_type, + rebind_abi_t<int, simd<int>::size() * 2, + simd_abi::compatible<int>>>::value, + ""); + + static_assert( + std::is_same<typename decltype(concat(native_simd<int>(), + native_simd<int>()))::abi_type, + rebind_abi_t<int, native_simd<int>::size() * 2, + simd_abi::native<int>>>::value, + ""); +} + +int main() { + test_concat(); + test_concat_array(); +} Index: libcxx/include/experimental/simd =================================================================== --- libcxx/include/experimental/simd +++ libcxx/include/experimental/simd @@ -932,12 +932,24 @@ template <class _Tp> inline constexpr bool is_simd_flag_type_v = is_simd_flag_type<_Tp>::value; #endif -template <class _Tp, size_t _Np> + +// NOTE: _Abis... is the extension proposed by P0820, allowing the APIs to +// propagate _StorageKind during transforming input type(s) to the output type. +template <class _Tp, size_t _Np, class... _Abis> struct abi_for_size { using type = simd_abi::fixed_size<_Np>; }; -template <class _Tp, size_t _Np> -using abi_for_size_t = typename abi_for_size<_Tp, _Np>::type; + +template <class _Tp, size_t _Np, _StorageKind __kind, int... __old_size> +struct abi_for_size<_Tp, _Np, __simd_abi<__kind, __old_size>...> { + using type = __simd_abi<__kind, _Np>; +}; + +template <class _Tp, size_t _Np, class... _Abis> +using abi_for_size_t = typename abi_for_size<_Tp, _Np, _Abis...>::type; + +template <class _Tp, size_t _Np, class... _Abis> +using rebind_abi_t = abi_for_size_t<_Tp, _Np, _Abis...>; template <class _Tp, class _Abi = simd_abi::compatible<_Tp>> struct simd_size; @@ -1044,32 +1056,137 @@ template <class _Tp, size_t _Np> simd_mask<_Tp> to_compatible(const fixed_size_simd_mask<_Tp, _Np>&) noexcept; +template <class _TupleType, class _Tp, size_t... __indicies> +_TupleType __split_tuple_impl(_Tp** __buffers, + std::index_sequence<__indicies...>) { + return _TupleType(typename std::tuple_element<__indicies, _TupleType>::type( + __buffers[__indicies], element_aligned_tag())...); +} + template <size_t... __sizes, class _Tp, class _Abi> -tuple<simd<_Tp, abi_for_size_t<_Tp, __sizes>>...> split(const simd<_Tp, _Abi>&); +typename std::enable_if< + __variadic_sum<size_t>(__sizes...) == simd<_Tp, _Abi>::size(), + tuple<simd<_Tp, rebind_abi_t<_Tp, __sizes, _Abi>>...>>::type +split(const simd<_Tp, _Abi>& __v) { + _Tp __buffer[__v.size()]; + __v.copy_to(__buffer, element_aligned_tag()); + + _Tp* __buffers[sizeof...(__sizes)]; + { + size_t __offsets[] = {__sizes...}; + size_t __cur_offset = 0; + for (size_t __i = 0; __i < sizeof...(__sizes); __i++) { + __buffers[__i] = __buffer + __cur_offset; + __cur_offset += __offsets[__i]; + } + } + + return __split_tuple_impl< + tuple<simd<_Tp, rebind_abi_t<_Tp, __sizes, _Abi>>...>>( + __buffers, std::make_index_sequence<sizeof...(__sizes)>()); +} template <size_t... __sizes, class _Tp, class _Abi> -tuple<simd_mask<_Tp, abi_for_size_t<_Tp, __sizes>>...> +tuple<simd_mask<_Tp, rebind_abi_t<_Tp, __sizes, _Abi>>...> split(const simd_mask<_Tp, _Abi>&); +// NOTE: P0820 extension +template <size_t __array_size, class _Tp, class _Abi> +typename std::enable_if< + simd_size<_Tp, _Abi>::value % __array_size == 0, + array<simd<_Tp, rebind_abi_t< + _Tp, simd_size<_Tp, _Abi>::value / __array_size, _Abi>>, + __array_size>>::type +split_by(const simd<_Tp, _Abi>& __v) { + array<simd<_Tp, rebind_abi_t<_Tp, simd_size<_Tp, _Abi>::value / __array_size, + _Abi>>, + __array_size> + __ret; + constexpr size_t __element_size = simd_size<_Tp, _Abi>::value / __array_size; + for (size_t __i = 0; __i < __v.size(); __i++) { + __ret[__i / __element_size][__i % __element_size] = __v[__i]; + } + return __ret; +} + template <class _SimdType, class _Abi> -array<_SimdType, simd_size<typename _SimdType::value_type, _Abi>::value / - _SimdType::size()> -split(const simd<typename _SimdType::value_type, _Abi>&); +typename std::enable_if< + is_simd<_SimdType>::value && + simd_size<typename _SimdType::value_type, _Abi>::value % + _SimdType::size() == + 0, + array<_SimdType, simd_size<typename _SimdType::value_type, _Abi>::value / + _SimdType::size()>>::type +split(const simd<typename _SimdType::value_type, _Abi>& __v) { + return split_by<simd_size<typename _SimdType::value_type, _Abi>::value / + _SimdType::size()>(__v); +} + +template <size_t __array_size, class _Tp, class _Abi> +array< + simd_mask<_Tp, rebind_abi_t<_Tp, simd_size<_Tp, _Abi>::value / __array_size, + _Abi>>, + __array_size> +split_by(const simd_mask<_Tp, _Abi>& x); template <class _SimdType, class _Abi> array<_SimdType, simd_size<typename _SimdType::value_type, _Abi>::value / _SimdType::size()> split(const simd_mask<typename _SimdType::value_type, _Abi>&); +template <class _TupleType, class _Tp, size_t... __indicies> +void __concat_tuple_impl(_TupleType __vs, _Tp** __buffers, + std::index_sequence<__indicies...>) { + int __unused[] = {(std::get<__indicies>(__vs).copy_to(__buffers[__indicies], + element_aligned_tag()), + 0)...}; + (void)__unused; +} + template <class _Tp, class... _Abis> -simd<_Tp, abi_for_size_t<_Tp, __variadic_sum(simd_size<_Tp, _Abis>::value...)>> -concat(const simd<_Tp, _Abis>&...); +simd<_Tp, + rebind_abi_t<_Tp, __variadic_sum<size_t>(simd_size<_Tp, _Abis>::value...), + _Abis...>> +concat(const simd<_Tp, _Abis>&... __vs) { + constexpr auto __size = + __variadic_sum<size_t>(simd_size<_Tp, _Abis>::value...); + _Tp __buffer[__size]; + _Tp* __buffers[__size]; + { + size_t __offsets[] = {simd_size<_Tp, _Abis>::value...}; + size_t __cur_offset = 0; + for (size_t __i = 0; __i < __size; __i++) { + __buffers[__i] = __buffer + __cur_offset; + __cur_offset += __offsets[__i]; + } + } + __concat_tuple_impl(std::forward_as_tuple(__vs...), __buffers, + std::make_index_sequence<sizeof...(_Abis)>()); + return simd<_Tp, rebind_abi_t<_Tp, __size, _Abis...>>(__buffer, + element_aligned_tag()); +} + +template <class _Tp, class _Abi, size_t _Np> +simd<_Tp, rebind_abi_t<_Tp, _Np * simd<_Tp, _Abi>::size(), _Abi>> +concat(const std::array<simd<_Tp, _Abi>, _Np>& __arr) { + simd<_Tp, rebind_abi_t<_Tp, _Np * simd<_Tp, _Abi>::size(), _Abi>> __v; + for (size_t __i = 0; __i < __v.size(); __i++) { + __v[__i] = + __arr[__i / simd<_Tp, _Abi>::size()][__i % simd<_Tp, _Abi>::size()]; + } + return __v; +} template <class _Tp, class... _Abis> -simd_mask<_Tp, - abi_for_size_t<_Tp, __variadic_sum(simd_size<_Tp, _Abis>::value...)>> +simd_mask<_Tp, rebind_abi_t< + _Tp, __variadic_sum<size_t>(simd_size<_Tp, _Abis>::value...), + _Abis...>> concat(const simd_mask<_Tp, _Abis>&...); +template <class _Tp, class _Abi, size_t _Np> +simd_mask<_Tp, rebind_abi_t<_Tp, _Np, _Abi>> +concat(const std::array<simd_mask<_Tp, _Abi>, _Np>&); + // reductions [simd.mask.reductions] template <class _Tp, class _Abi> bool all_of(const simd_mask<_Tp, _Abi>&) noexcept;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits