Skip to content

Commit 898a99c

Browse files
committed
[libc++] <experimental/simd> Add copy functions for class simd/simd_mask
1 parent 6a6f9bf commit 898a99c

File tree

7 files changed

+347
-0
lines changed

7 files changed

+347
-0
lines changed

libcxx/docs/Status/ParallelismProjects.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Section,Description,Dependencies,Assignee,Complete
2424
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd generate constructor <https://reviews.llvm.org/D159442>`_", None, Yin Zhang, |Complete|
2525
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd load constructor <https://github.com/llvm/llvm-project/pull/76610>`_", None, Yin Zhang, |Complete|
2626
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd subscript operators <https://github.com/llvm/llvm-project/pull/68960>`_", None, Yin Zhang, |Complete|
27+
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd copy functions <https://github.com/llvm/llvm-project/pull/78935>`_", None, Yin Zhang, |Complete|
2728
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "Class template simd implementation", None, Yin Zhang, |In Progress|
2829
| `[parallel.simd.nonmembers] <https://wg21.link/N4808>`_, "simd non-member operations", None, Yin Zhang, |In Progress|
2930
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`Class template simd_mask declaration and alias <https://reviews.llvm.org/D144362>`_", [parallel.simd.abi], Yin Zhang, |Complete|
@@ -33,5 +34,6 @@ Section,Description,Dependencies,Assignee,Complete
3334
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`simd_mask implicit type conversion constructor <https://github.com/llvm/llvm-project/pull/71132>`_", None, Yin Zhang, |Complete|
3435
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`simd_mask load constructor <https://github.com/llvm/llvm-project/pull/76610>`_", None, Yin Zhang, |Complete|
3536
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`simd_mask subscript operators <https://github.com/llvm/llvm-project/pull/68960>`_", None, Yin Zhang, |Complete|
37+
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`simd_mask copy functions <https://github.com/llvm/llvm-project/pull/78935>`_", None, Yin Zhang, |Complete|
3638
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "Class template simd_mask implementation", None, Yin Zhang, |In Progress|
3739
| `[parallel.simd.mask.nonmembers] <https://wg21.link/N4808>`_, "simd_mask non-member operations", None, Yin Zhang, |In Progress|

libcxx/include/experimental/__simd/scalar.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ struct __simd_operations<_Tp, simd_abi::__scalar> {
6262
static _LIBCPP_HIDE_FROM_ABI void __load(_SimdStorage& __s, const _Up* __mem) noexcept {
6363
__s.__data = static_cast<_Tp>(__mem[0]);
6464
}
65+
66+
template <class _Up>
67+
static _LIBCPP_HIDE_FROM_ABI void __store(_SimdStorage __s, _Up* __mem) noexcept {
68+
*__mem = static_cast<_Up>(__s.__data);
69+
}
6570
};
6671

6772
template <class _Tp>
@@ -71,6 +76,8 @@ struct __mask_operations<_Tp, simd_abi::__scalar> {
7176
static _LIBCPP_HIDE_FROM_ABI _MaskStorage __broadcast(bool __v) noexcept { return {__v}; }
7277

7378
static _LIBCPP_HIDE_FROM_ABI void __load(_MaskStorage& __s, const bool* __mem) noexcept { __s.__data = __mem[0]; }
79+
80+
static _LIBCPP_HIDE_FROM_ABI void __store(_MaskStorage __s, bool* __mem) noexcept { __mem[0] = __s.__data; }
7481
};
7582

7683
} // namespace parallelism_v2

libcxx/include/experimental/__simd/simd.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ class simd {
7070
_Impl::__load(__s_, _Flags::template __apply<simd>(__mem));
7171
}
7272

73+
// copy functions
74+
template <class _Up, class _Flags>
75+
_LIBCPP_HIDE_FROM_ABI enable_if_t<__is_vectorizable_v<_Up> && is_simd_flag_type_v<_Flags>>
76+
copy_from(const _Up* __mem, _Flags) {
77+
_Impl::__load(__s_, _Flags::template __apply<simd>(__mem));
78+
}
79+
80+
template <class _Up, class _Flags>
81+
_LIBCPP_HIDE_FROM_ABI enable_if_t<__is_vectorizable_v<_Up> && is_simd_flag_type_v<_Flags>>
82+
copy_to(_Up* __mem, _Flags) const {
83+
_Impl::__store(__s_, _Flags::template __apply<simd>(__mem));
84+
}
85+
7386
// scalar access [simd.subscr]
7487
_LIBCPP_HIDE_FROM_ABI reference operator[](size_t __i) noexcept { return reference(__s_, __i); }
7588
_LIBCPP_HIDE_FROM_ABI value_type operator[](size_t __i) const noexcept { return __s_.__get(__i); }

libcxx/include/experimental/__simd/simd_mask.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@ class simd_mask {
5858
_Impl::__load(__s_, _Flags::template __apply<simd_mask>(__mem));
5959
}
6060

61+
// copy functions
62+
template <class _Flags>
63+
_LIBCPP_HIDE_FROM_ABI enable_if_t<is_simd_flag_type_v<_Flags>> copy_from(const value_type* __mem, _Flags) {
64+
_Impl::__load(__s_, _Flags::template __apply<simd_mask>(__mem));
65+
}
66+
67+
template <class _Flags>
68+
_LIBCPP_HIDE_FROM_ABI enable_if_t<is_simd_flag_type_v<_Flags>> copy_to(value_type* __mem, _Flags) const {
69+
_Impl::__store(__s_, _Flags::template __apply<simd_mask>(__mem));
70+
}
71+
6172
// scalar access [simd.mask.subscr]
6273
_LIBCPP_HIDE_FROM_ABI reference operator[](size_t __i) noexcept { return reference(__s_, __i); }
6374
_LIBCPP_HIDE_FROM_ABI value_type operator[](size_t __i) const noexcept { return __s_.__get(__i); }

libcxx/include/experimental/__simd/vec_ext.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ struct __simd_operations<_Tp, simd_abi::__vec_ext<_Np>> {
8080
for (size_t __i = 0; __i < _Np; __i++)
8181
__s.__data[__i] = static_cast<_Tp>(__mem[__i]);
8282
}
83+
84+
template <class _Up>
85+
static _LIBCPP_HIDE_FROM_ABI void __store(_SimdStorage __s, _Up* __mem) noexcept {
86+
for (size_t __i = 0; __i < _Np; __i++)
87+
__mem[__i] = static_cast<_Up>(__s.__data[__i]);
88+
}
8389
};
8490

8591
template <class _Tp, int _Np>
@@ -99,6 +105,11 @@ struct __mask_operations<_Tp, simd_abi::__vec_ext<_Np>> {
99105
for (size_t __i = 0; __i < _Np; __i++)
100106
__s.__data[__i] = experimental::__set_all_bits<_Tp>(__mem[__i]);
101107
}
108+
109+
static _LIBCPP_HIDE_FROM_ABI void __store(_MaskStorage __s, bool* __mem) noexcept {
110+
for (size_t __i = 0; __i < _Np; __i++)
111+
__mem[__i] = static_cast<bool>(__s.__data[__i]);
112+
}
102113
};
103114

104115
} // namespace parallelism_v2
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14
10+
// XFAIL: target=powerpc{{.*}}le-unknown-linux-gnu
11+
12+
// <experimental/simd>
13+
//
14+
// [simd.class]
15+
// template<class U, class Flags> void copy_from(const U* mem, Flags);
16+
// template<class U, class Flags> void copy_to(U* mem, Flags) const;
17+
18+
#include "../test_utils.h"
19+
20+
namespace ex = std::experimental::parallelism_v2;
21+
22+
template <class T, class SimdAbi, std::size_t array_size>
23+
struct ElementAlignedCopyFromHelper {
24+
template <class U>
25+
void operator()() const {
26+
constexpr std::size_t alignas_size = alignof(U);
27+
alignas(alignas_size) U buffer[array_size];
28+
for (size_t i = 0; i < array_size; ++i)
29+
buffer[i] = static_cast<U>(i);
30+
ex::simd<T, SimdAbi> origin_simd;
31+
origin_simd.copy_from(buffer, ex::element_aligned_tag());
32+
assert_simd_values_equal(origin_simd, buffer);
33+
}
34+
};
35+
36+
template <class T, class SimdAbi, std::size_t array_size>
37+
struct VectorAlignedCopyFromHelper {
38+
template <class U>
39+
void operator()() const {
40+
constexpr std::size_t alignas_size = ex::memory_alignment_v<ex::simd<T, SimdAbi>, U>;
41+
alignas(alignas_size) U buffer[array_size];
42+
for (size_t i = 0; i < array_size; ++i)
43+
buffer[i] = static_cast<U>(i);
44+
ex::simd<T, SimdAbi> origin_simd;
45+
origin_simd.copy_from(buffer, ex::vector_aligned_tag());
46+
assert_simd_values_equal(origin_simd, buffer);
47+
}
48+
};
49+
50+
template <class T, class SimdAbi, std::size_t array_size>
51+
struct OveralignedCopyFromHelper {
52+
template <class U>
53+
void operator()() const {
54+
constexpr std::size_t alignas_size = bit_ceil(sizeof(U) + 1);
55+
alignas(alignas_size) U buffer[array_size];
56+
for (size_t i = 0; i < array_size; ++i)
57+
buffer[i] = static_cast<U>(i);
58+
ex::simd<T, SimdAbi> origin_simd;
59+
origin_simd.copy_from(buffer, ex::overaligned_tag<alignas_size>());
60+
assert_simd_values_equal(origin_simd, buffer);
61+
}
62+
};
63+
64+
template <class T, std::size_t>
65+
struct CheckSimdCopyFrom {
66+
template <class SimdAbi>
67+
void operator()() {
68+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
69+
70+
types::for_each(simd_test_types(), ElementAlignedCopyFromHelper<T, SimdAbi, array_size>());
71+
types::for_each(simd_test_types(), VectorAlignedCopyFromHelper<T, SimdAbi, array_size>());
72+
types::for_each(simd_test_types(), OveralignedCopyFromHelper<T, SimdAbi, array_size>());
73+
}
74+
};
75+
76+
template <class T, class SimdAbi, std::size_t array_size>
77+
struct ElementAlignedCopyToHelper {
78+
template <class U>
79+
void operator()() const {
80+
constexpr std::size_t alignas_size = alignof(U);
81+
alignas(alignas_size) U buffer[array_size];
82+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
83+
origin_simd.copy_to(buffer, ex::element_aligned_tag());
84+
assert_simd_values_equal(origin_simd, buffer);
85+
}
86+
};
87+
88+
template <class T, class SimdAbi, std::size_t array_size>
89+
struct VectorAlignedCopyToHelper {
90+
template <class U>
91+
void operator()() const {
92+
constexpr std::size_t alignas_size = ex::memory_alignment_v<ex::simd<T, SimdAbi>, U>;
93+
alignas(alignas_size) U buffer[array_size];
94+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
95+
origin_simd.copy_to(buffer, ex::vector_aligned_tag());
96+
assert_simd_values_equal(origin_simd, buffer);
97+
}
98+
};
99+
100+
template <class T, class SimdAbi, std::size_t array_size>
101+
struct OveralignedCopyToHelper {
102+
template <class U>
103+
void operator()() const {
104+
constexpr std::size_t alignas_size = bit_ceil(sizeof(U) + 1);
105+
alignas(alignas_size) U buffer[array_size];
106+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
107+
origin_simd.copy_to(buffer, ex::overaligned_tag<alignas_size>());
108+
assert_simd_values_equal(origin_simd, buffer);
109+
}
110+
};
111+
112+
template <class T, std::size_t>
113+
struct CheckSimdCopyTo {
114+
template <class SimdAbi>
115+
void operator()() {
116+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
117+
118+
types::for_each(simd_test_types(), ElementAlignedCopyToHelper<T, SimdAbi, array_size>());
119+
types::for_each(simd_test_types(), VectorAlignedCopyToHelper<T, SimdAbi, array_size>());
120+
types::for_each(simd_test_types(), OveralignedCopyToHelper<T, SimdAbi, array_size>());
121+
}
122+
};
123+
124+
template <class U, class T, class Flags, class SimdAbi = ex::simd_abi::compatible<T>, class = void>
125+
struct has_copy_from : std::false_type {};
126+
127+
template <class U, class T, class Flags, class SimdAbi>
128+
struct has_copy_from<U,
129+
T,
130+
Flags,
131+
SimdAbi,
132+
std::void_t<decltype(std::declval<ex::simd<T, SimdAbi>>().copy_from(
133+
std::declval<const U*>(), std::declval<Flags>()))>> : std::true_type {};
134+
135+
template <class U, class T, class Flags, class SimdAbi = ex::simd_abi::compatible<T>, class = void>
136+
struct has_copy_to : std::false_type {};
137+
138+
template <class U, class T, class Flags, class SimdAbi>
139+
struct has_copy_to<
140+
U,
141+
T,
142+
Flags,
143+
SimdAbi,
144+
std::void_t<decltype(std::declval<ex::simd<T, SimdAbi>>().copy_to(std::declval<U*>(), std::declval<Flags>()))>>
145+
: std::true_type {};
146+
147+
template <class T, std::size_t>
148+
struct CheckSimdCopyTraits {
149+
template <class SimdAbi>
150+
void operator()() {
151+
// These functions shall not participate in overload resolution unless
152+
// is_simd_flag_type_v<Flags> is true, and
153+
// U is a vectorizable type.
154+
static_assert(has_copy_from<int, T, ex::element_aligned_tag, SimdAbi>::value);
155+
static_assert(has_copy_to<int, T, ex::element_aligned_tag, SimdAbi>::value);
156+
157+
// is_simd_flag_type_v<Flags> is false
158+
static_assert(!has_copy_from<int, T, T, SimdAbi>::value);
159+
static_assert(!has_copy_to<int, T, T, SimdAbi>::value);
160+
static_assert(!has_copy_from<int, T, SimdAbi, SimdAbi>::value);
161+
static_assert(!has_copy_to<int, T, SimdAbi, SimdAbi>::value);
162+
163+
// U is not a vectorizable type.
164+
static_assert(!has_copy_from<SimdAbi, T, ex::element_aligned_tag, SimdAbi>::value);
165+
static_assert(!has_copy_to<SimdAbi, T, ex::element_aligned_tag, SimdAbi>::value);
166+
static_assert(!has_copy_from<ex::element_aligned_tag, T, ex::element_aligned_tag, SimdAbi>::value);
167+
static_assert(!has_copy_to<ex::element_aligned_tag, T, ex::element_aligned_tag, SimdAbi>::value);
168+
}
169+
};
170+
171+
int main(int, char**) {
172+
test_all_simd_abi<CheckSimdCopyFrom>();
173+
test_all_simd_abi<CheckSimdCopyTo>();
174+
test_all_simd_abi<CheckSimdCopyTraits>();
175+
return 0;
176+
}

0 commit comments

Comments
 (0)