Skip to content

Commit 6bb5c98

Browse files
authored
[libc++] <experimental/simd> Add load constructor for class simd/simd_mask (#76610)
1 parent a70d310 commit 6bb5c98

File tree

8 files changed

+203
-0
lines changed

8 files changed

+203
-0
lines changed

libcxx/docs/Status/ParallelismProjects.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Section,Description,Dependencies,Assignee,Complete
2222
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd broadcast constructor <https://reviews.llvm.org/D156225>`_", None, Yin Zhang, |Complete|
2323
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd implicit type conversion constructor <https://github.com/llvm/llvm-project/pull/71132>`_", None, Yin Zhang, |Complete|
2424
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd generate constructor <https://reviews.llvm.org/D159442>`_", None, Yin Zhang, |Complete|
25+
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd load constructor <https://github.com/llvm/llvm-project/pull/76610>`_", None, Yin Zhang, |Complete|
2526
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd subscript operators <https://github.com/llvm/llvm-project/pull/68960>`_", None, Yin Zhang, |Complete|
2627
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "Class template simd implementation", None, Yin Zhang, |In Progress|
2728
| `[parallel.simd.nonmembers] <https://wg21.link/N4808>`_, "simd non-member operations", None, Yin Zhang, |In Progress|
@@ -30,6 +31,7 @@ Section,Description,Dependencies,Assignee,Complete
3031
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`simd_mask default constructor <https://github.com/llvm/llvm-project/pull/70424>`_", None, Yin Zhang, |Complete|
3132
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`simd_mask broadcast constructor <https://reviews.llvm.org/D156225>`_", None, Yin Zhang, |Complete|
3233
| `[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|
34+
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`simd_mask load constructor <https://github.com/llvm/llvm-project/pull/76610>`_", None, Yin Zhang, |Complete|
3335
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "`simd_mask subscript operators <https://github.com/llvm/llvm-project/pull/68960>`_", None, Yin Zhang, |Complete|
3436
| `[parallel.simd.mask.class] <https://wg21.link/N4808>`_, "Class template simd_mask implementation", None, Yin Zhang, |In Progress|
3537
| `[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
@@ -56,13 +56,20 @@ struct __simd_operations<_Tp, simd_abi::__scalar> {
5656
static _LIBCPP_HIDE_FROM_ABI _SimdStorage __generate(_Generator&& __g) noexcept {
5757
return {__g(std::integral_constant<size_t, 0>())};
5858
}
59+
60+
template <class _Up>
61+
static _LIBCPP_HIDE_FROM_ABI void __load(_SimdStorage& __s, const _Up* __mem) noexcept {
62+
__s.__data = static_cast<_Tp>(__mem[0]);
63+
}
5964
};
6065

6166
template <class _Tp>
6267
struct __mask_operations<_Tp, simd_abi::__scalar> {
6368
using _MaskStorage = __mask_storage<_Tp, simd_abi::__scalar>;
6469

6570
static _LIBCPP_HIDE_FROM_ABI _MaskStorage __broadcast(bool __v) noexcept { return {__v}; }
71+
72+
static _LIBCPP_HIDE_FROM_ABI void __load(_MaskStorage& __s, const bool* __mem) noexcept { __s.__data = __mem[0]; }
6673
};
6774

6875
} // namespace parallelism_v2

libcxx/include/experimental/__simd/simd.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ class simd {
6464
explicit _LIBCPP_HIDE_FROM_ABI simd(_Generator&& __g) noexcept
6565
: __s_(_Impl::__generate(std::forward<_Generator>(__g))) {}
6666

67+
// load constructor
68+
template <class _Up, class _Flags, enable_if_t<__is_vectorizable_v<_Up> && is_simd_flag_type_v<_Flags>, int> = 0>
69+
_LIBCPP_HIDE_FROM_ABI simd(const _Up* __mem, _Flags) {
70+
_Impl::__load(__s_, _Flags::template __apply<simd>(__mem));
71+
}
72+
6773
// scalar access [simd.subscr]
6874
_LIBCPP_HIDE_FROM_ABI reference operator[](size_t __i) noexcept { return reference(__s_, __i); }
6975
_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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ class simd_mask {
5252
}
5353
}
5454

55+
// load constructor
56+
template <class _Flags, enable_if_t<is_simd_flag_type_v<_Flags>, int> = 0>
57+
_LIBCPP_HIDE_FROM_ABI simd_mask(const value_type* __mem, _Flags) {
58+
_Impl::__load(__s_, _Flags::template __apply<simd_mask>(__mem));
59+
}
60+
5561
// scalar access [simd.mask.subscr]
5662
_LIBCPP_HIDE_FROM_ABI reference operator[](size_t __i) noexcept { return reference(__s_, __i); }
5763
_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
@@ -73,6 +73,12 @@ struct __simd_operations<_Tp, simd_abi::__vec_ext<_Np>> {
7373
static _LIBCPP_HIDE_FROM_ABI _SimdStorage __generate(_Generator&& __g) noexcept {
7474
return __generate_init(std::forward<_Generator>(__g), std::make_index_sequence<_Np>());
7575
}
76+
77+
template <class _Up>
78+
static _LIBCPP_HIDE_FROM_ABI void __load(_SimdStorage& __s, const _Up* __mem) noexcept {
79+
for (size_t __i = 0; __i < _Np; __i++)
80+
__s.__data[__i] = static_cast<_Tp>(__mem[__i]);
81+
}
7682
};
7783

7884
template <class _Tp, int _Np>
@@ -87,6 +93,11 @@ struct __mask_operations<_Tp, simd_abi::__vec_ext<_Np>> {
8793
}
8894
return __result;
8995
}
96+
97+
static _LIBCPP_HIDE_FROM_ABI void __load(_MaskStorage& __s, const bool* __mem) noexcept {
98+
for (size_t __i = 0; __i < _Np; __i++)
99+
__s.__data[__i] = experimental::__set_all_bits<_Tp>(__mem[__i]);
100+
}
90101
};
91102

92103
} // namespace parallelism_v2
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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+
11+
// <experimental/simd>
12+
//
13+
// [simd.class]
14+
// template<class U, class Flags> simd(const U* mem, Flags);
15+
16+
#include "../test_utils.h"
17+
18+
namespace ex = std::experimental::parallelism_v2;
19+
20+
template <class T, class SimdAbi, std::size_t array_size>
21+
struct ElementAlignedLoadCtorHelper {
22+
template <class U>
23+
void operator()() const {
24+
U buffer[array_size];
25+
for (size_t i = 0; i < array_size; ++i)
26+
buffer[i] = static_cast<U>(i);
27+
ex::simd<T, SimdAbi> origin_simd(buffer, ex::element_aligned_tag());
28+
assert_simd_values_equal(origin_simd, buffer);
29+
}
30+
};
31+
32+
template <class T, class SimdAbi, std::size_t array_size>
33+
struct VectorAlignedLoadCtorHelper {
34+
template <class U>
35+
void operator()() const {
36+
alignas(ex::memory_alignment_v<ex::simd<T, SimdAbi>, U>) U buffer[array_size];
37+
for (size_t i = 0; i < array_size; ++i)
38+
buffer[i] = static_cast<U>(i);
39+
ex::simd<T, SimdAbi> origin_simd(buffer, ex::vector_aligned_tag());
40+
assert_simd_values_equal(origin_simd, buffer);
41+
}
42+
};
43+
44+
template <class T, class SimdAbi, std::size_t array_size>
45+
struct OveralignedLoadCtorHelper {
46+
template <class U>
47+
void operator()() const {
48+
alignas(bit_ceil(sizeof(U) + 1)) U buffer[array_size];
49+
for (size_t i = 0; i < array_size; ++i)
50+
buffer[i] = static_cast<U>(i);
51+
ex::simd<T, SimdAbi> origin_simd(buffer, ex::overaligned_tag<bit_ceil(sizeof(U) + 1)>());
52+
assert_simd_values_equal(origin_simd, buffer);
53+
}
54+
};
55+
56+
template <class T, std::size_t>
57+
struct CheckSimdLoadCtor {
58+
template <class SimdAbi>
59+
void operator()() {
60+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
61+
62+
types::for_each(arithmetic_no_bool_types(), ElementAlignedLoadCtorHelper<T, SimdAbi, array_size>());
63+
types::for_each(arithmetic_no_bool_types(), VectorAlignedLoadCtorHelper<T, SimdAbi, array_size>());
64+
types::for_each(arithmetic_no_bool_types(), OveralignedLoadCtorHelper<T, SimdAbi, array_size>());
65+
}
66+
};
67+
68+
template <class T, std::size_t>
69+
struct CheckLoadCtorTraits {
70+
template <class SimdAbi>
71+
void operator()() {
72+
// This function shall not participate in overload resolution unless
73+
// is_simd_flag_type_v<Flags> is true, and
74+
// U is a vectorizable type.
75+
static_assert(std::is_constructible_v<ex::simd<T, SimdAbi>, const int*, ex::element_aligned_tag>);
76+
77+
// is_simd_flag_type_v<Flags> is false
78+
static_assert(!std::is_constructible_v<ex::simd<T, SimdAbi>, const int*, T>);
79+
static_assert(!std::is_constructible_v<ex::simd<T, SimdAbi>, const int*, SimdAbi>);
80+
81+
// U is not a vectorizable type.
82+
static_assert(!std::is_constructible_v<ex::simd<T, SimdAbi>, const SimdAbi*, ex::element_aligned_tag>);
83+
static_assert(
84+
!std::is_constructible_v<ex::simd<T, SimdAbi>, const ex::element_aligned_tag*, ex::element_aligned_tag>);
85+
}
86+
};
87+
88+
int main(int, char**) {
89+
test_all_simd_abi<CheckSimdLoadCtor>();
90+
test_all_simd_abi<CheckLoadCtorTraits>();
91+
return 0;
92+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
11+
// <experimental/simd>
12+
//
13+
// [simd.class]
14+
// template<class Flags> simd_mask(const value_type* mem, Flags);
15+
16+
#include "../test_utils.h"
17+
18+
namespace ex = std::experimental::parallelism_v2;
19+
20+
template <class T, std::size_t>
21+
struct CheckSimdMaskLoadCtor {
22+
template <class SimdAbi>
23+
void operator()() {
24+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
25+
26+
// element aligned tag
27+
bool element_buffer[array_size];
28+
for (size_t i = 0; i < array_size; ++i)
29+
element_buffer[i] = static_cast<bool>(i % 2);
30+
ex::simd_mask<T, SimdAbi> element_mask(element_buffer, ex::element_aligned_tag());
31+
assert_simd_mask_values_equal(element_mask, element_buffer);
32+
33+
// vector aligned tag
34+
alignas(ex::memory_alignment_v<ex::simd_mask<T, SimdAbi>>) bool vector_buffer[array_size];
35+
for (size_t i = 0; i < array_size; ++i)
36+
vector_buffer[i] = static_cast<bool>(i % 2);
37+
ex::simd_mask<T, SimdAbi> vector_mask(vector_buffer, ex::vector_aligned_tag());
38+
assert_simd_mask_values_equal(vector_mask, vector_buffer);
39+
40+
// overaligned tag
41+
alignas(bit_ceil(sizeof(bool) + 1)) bool overaligned_buffer[array_size];
42+
for (size_t i = 0; i < array_size; ++i)
43+
overaligned_buffer[i] = static_cast<bool>(i % 2);
44+
ex::simd_mask<T, SimdAbi> overaligned_mask(overaligned_buffer, ex::overaligned_tag<bit_ceil(sizeof(bool) + 1)>());
45+
assert_simd_mask_values_equal(overaligned_mask, overaligned_buffer);
46+
}
47+
};
48+
49+
template <class T, std::size_t>
50+
struct CheckMaskLoadCtorTraits {
51+
template <class SimdAbi>
52+
void operator()() {
53+
// This function shall not participate in overload resolution unless
54+
// is_simd_flag_type_v<Flags> is true
55+
static_assert(std::is_constructible_v<ex::simd_mask<T, SimdAbi>, const bool*, ex::element_aligned_tag>);
56+
57+
// is_simd_flag_type_v<Flags> is false
58+
static_assert(!std::is_constructible_v<ex::simd_mask<T, SimdAbi>, const bool*, T>);
59+
static_assert(!std::is_constructible_v<ex::simd_mask<T, SimdAbi>, const bool*, SimdAbi>);
60+
}
61+
};
62+
63+
int main(int, char**) {
64+
test_all_simd_abi<CheckSimdMaskLoadCtor>();
65+
test_all_simd_abi<CheckMaskLoadCtorTraits>();
66+
return 0;
67+
}

libcxx/test/std/experimental/simd/test_utils.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,16 @@ void assert_simd_mask_values_equal(const ex::simd_mask<T, SimdAbi>& origin_mask,
7979
assert(origin_mask[i] == expected_value[i]);
8080
}
8181

82+
template <class SimdAbi, class T, class U = T>
83+
void assert_simd_values_equal(const ex::simd<T, SimdAbi>& origin_simd, U* expected_value) {
84+
for (size_t i = 0; i < origin_simd.size(); ++i)
85+
assert(origin_simd[i] == static_cast<T>(expected_value[i]));
86+
}
87+
88+
template <class SimdAbi, class T>
89+
void assert_simd_mask_values_equal(const ex::simd_mask<T, SimdAbi>& origin_mask, bool* expected_value) {
90+
for (size_t i = 0; i < origin_mask.size(); ++i)
91+
assert(origin_mask[i] == expected_value[i]);
92+
}
93+
8294
#endif // LIBCXX_TEST_STD_EXPERIMENTAL_SIMD_TEST_UTILS_H

0 commit comments

Comments
 (0)