Skip to content

Commit 75262c7

Browse files
committed
[libc++] <experimental/simd> Add unary operators for class simd
1 parent e55dee3 commit 75262c7

File tree

6 files changed

+267
-5
lines changed

6 files changed

+267
-5
lines changed

libcxx/docs/Status/ParallelismProjects.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Section,Description,Dependencies,Assignee,Complete
2828
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd load constructor <https://github.com/llvm/llvm-project/pull/76610>`_", None, Yin Zhang, |Complete|
2929
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd subscript operators <https://github.com/llvm/llvm-project/pull/68960>`_", None, Yin Zhang, |Complete|
3030
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd copy functions <https://github.com/llvm/llvm-project/pull/78935>`_", None, Yin Zhang, |Complete|
31+
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "`simd unary operators <https://github.com/llvm/llvm-project/pull/104764>`_", None, Yin Zhang, |Complete|
3132
| `[parallel.simd.class] <https://wg21.link/N4808>`_, "Class template simd implementation", None, Yin Zhang, |In Progress|
3233
| `[parallel.simd.nonmembers] <https://wg21.link/N4808>`_, "simd non-member operations", None, Yin Zhang, |In Progress|
3334
| `[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|

libcxx/include/experimental/__simd/scalar.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,20 @@ struct __simd_operations<_Tp, simd_abi::__scalar> {
6767
static _LIBCPP_HIDE_FROM_ABI void __store(_SimdStorage __s, _Up* __mem) noexcept {
6868
*__mem = static_cast<_Up>(__s.__data);
6969
}
70+
71+
static _LIBCPP_HIDE_FROM_ABI void __increment(_SimdStorage& __s) noexcept { ++__s.__data; }
72+
73+
static _LIBCPP_HIDE_FROM_ABI void __decrement(_SimdStorage& __s) noexcept { --__s.__data; }
74+
75+
static _LIBCPP_HIDE_FROM_ABI _MaskStorage __negate(_SimdStorage __s) noexcept { return {!__s.__data}; }
76+
77+
static _LIBCPP_HIDE_FROM_ABI _SimdStorage __bitwise_not(_SimdStorage __s) noexcept {
78+
return {static_cast<_Tp>(~__s.__data)};
79+
}
80+
81+
static _LIBCPP_HIDE_FROM_ABI _SimdStorage __unary_minus(_SimdStorage __s) noexcept {
82+
return {static_cast<_Tp>(-__s.__data)};
83+
}
7084
};
7185

7286
template <class _Tp>

libcxx/include/experimental/__simd/simd.h

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef _LIBCPP_EXPERIMENTAL___SIMD_SIMD_H
1111
#define _LIBCPP_EXPERIMENTAL___SIMD_SIMD_H
1212

13+
#include <__type_traits/is_integral.h>
1314
#include <__type_traits/is_same.h>
1415
#include <__type_traits/remove_cvref.h>
1516
#include <__utility/forward.h>
@@ -25,15 +26,29 @@
2526
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL
2627
inline namespace parallelism_v2 {
2728

29+
template <class _Simd, class _Impl, bool>
30+
class __simd_int_operators {};
31+
32+
template <class _Simd, class _Impl>
33+
class __simd_int_operators<_Simd, _Impl, true> {
34+
public:
35+
// unary operators for integral _Tp
36+
_LIBCPP_HIDE_FROM_ABI _Simd operator~() const noexcept {
37+
return _Simd(_Impl::__bitwise_not((*static_cast<const _Simd*>(this)).__s_), _Simd::__storage_tag);
38+
}
39+
};
40+
2841
// class template simd [simd.class]
2942
// TODO: implement simd class
3043
template <class _Tp, class _Abi>
31-
class simd {
44+
class simd : public __simd_int_operators<simd<_Tp, _Abi>, __simd_operations<_Tp, _Abi>, is_integral_v<_Tp>> {
3245
using _Impl = __simd_operations<_Tp, _Abi>;
3346
using _Storage = typename _Impl::_SimdStorage;
3447

3548
_Storage __s_;
3649

50+
friend class __simd_int_operators<simd, _Impl, true>;
51+
3752
public:
3853
using value_type = _Tp;
3954
using reference = __simd_reference<_Tp, _Storage, value_type>;
@@ -45,8 +60,10 @@ class simd {
4560
_LIBCPP_HIDE_FROM_ABI simd() noexcept = default;
4661

4762
// explicit conversion from and to implementation-defined types
48-
explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const;
49-
explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s) : __s_(__s) {}
63+
struct __storage_tag_t {};
64+
static constexpr __storage_tag_t __storage_tag{};
65+
explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const { return __s_; }
66+
explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s, __storage_tag_t) : __s_(__s) {}
5067

5168
// broadcast constructor
5269
template <class _Up, enable_if_t<__can_broadcast_v<value_type, __remove_cvref_t<_Up>>, int> = 0>
@@ -88,6 +105,37 @@ class simd {
88105
// scalar access [simd.subscr]
89106
_LIBCPP_HIDE_FROM_ABI reference operator[](size_t __i) noexcept { return reference(__s_, __i); }
90107
_LIBCPP_HIDE_FROM_ABI value_type operator[](size_t __i) const noexcept { return __s_.__get(__i); }
108+
109+
// simd unary operators
110+
_LIBCPP_HIDE_FROM_ABI simd& operator++() noexcept {
111+
_Impl::__increment(__s_);
112+
return *this;
113+
}
114+
115+
_LIBCPP_HIDE_FROM_ABI simd operator++(int) noexcept {
116+
simd __r = *this;
117+
_Impl::__increment(__s_);
118+
return __r;
119+
}
120+
121+
_LIBCPP_HIDE_FROM_ABI simd& operator--() noexcept {
122+
_Impl::__decrement(__s_);
123+
return *this;
124+
}
125+
126+
_LIBCPP_HIDE_FROM_ABI simd operator--(int) noexcept {
127+
simd __r = *this;
128+
_Impl::__decrement(__s_);
129+
return __r;
130+
}
131+
132+
_LIBCPP_HIDE_FROM_ABI mask_type operator!() const noexcept {
133+
return mask_type(_Impl::__negate(__s_), mask_type::__storage_tag);
134+
}
135+
136+
_LIBCPP_HIDE_FROM_ABI simd operator+() const noexcept { return *this; }
137+
138+
_LIBCPP_HIDE_FROM_ABI simd operator-() const noexcept { return simd(_Impl::__unary_minus(__s_), __storage_tag); }
91139
};
92140

93141
template <class _Tp, class _Abi>

libcxx/include/experimental/__simd/simd_mask.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ class simd_mask {
4242
_LIBCPP_HIDE_FROM_ABI simd_mask() noexcept = default;
4343

4444
// explicit conversion from and to implementation-defined types
45-
explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const;
46-
explicit _LIBCPP_HIDE_FROM_ABI simd_mask(const _Storage& __s) : __s_(__s) {}
45+
struct __storage_tag_t {};
46+
static constexpr __storage_tag_t __storage_tag{};
47+
explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const { return __s_; }
48+
explicit _LIBCPP_HIDE_FROM_ABI simd_mask(const _Storage& __s, __storage_tag_t) : __s_(__s) {}
4749

4850
// broadcast constructor
4951
_LIBCPP_HIDE_FROM_ABI explicit simd_mask(value_type __v) noexcept : __s_(_Impl::__broadcast(__v)) {}

libcxx/include/experimental/__simd/vec_ext.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ struct __simd_operations<_Tp, simd_abi::__vec_ext<_Np>> {
8686
for (size_t __i = 0; __i < _Np; __i++)
8787
__mem[__i] = static_cast<_Up>(__s.__data[__i]);
8888
}
89+
90+
static _LIBCPP_HIDE_FROM_ABI void __increment(_SimdStorage& __s) noexcept { __s.__data = __s.__data + 1; }
91+
92+
static _LIBCPP_HIDE_FROM_ABI void __decrement(_SimdStorage& __s) noexcept { __s.__data = __s.__data - 1; }
93+
94+
static _LIBCPP_HIDE_FROM_ABI _MaskStorage __negate(_SimdStorage __s) noexcept { return {!__s.__data}; }
95+
96+
static _LIBCPP_HIDE_FROM_ABI _SimdStorage __bitwise_not(_SimdStorage __s) noexcept { return {~__s.__data}; }
97+
98+
static _LIBCPP_HIDE_FROM_ABI _SimdStorage __unary_minus(_SimdStorage __s) noexcept { return {-__s.__data}; }
8999
};
90100

91101
template <class _Tp, int _Np>
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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+
// Older versions of clang may encounter a backend error (see 0295c2ad):
12+
// Pass-by-value arguments with alignment greater than register width are not supported.
13+
// XFAIL: target=powerpc{{.*}}-ibm-{{.*}} && (clang-17 || clang-18)
14+
15+
// <experimental/simd>
16+
//
17+
// [simd.class]
18+
// simd& operator++() noexcept;
19+
// simd operator++(int) noexcept;
20+
// simd& operator--() noexcept;
21+
// simd operator--(int) noexcept;
22+
// mask_type operator!() const noexcept;
23+
// simd operator~() const noexcept;
24+
// simd operator+() const noexcept;
25+
// simd operator-() const noexcept;
26+
27+
#include "../test_utils.h"
28+
#include <experimental/simd>
29+
30+
namespace ex = std::experimental::parallelism_v2;
31+
32+
template <class T, std::size_t>
33+
struct CheckSimdPrefixIncrementOperator {
34+
template <class SimdAbi>
35+
void operator()() {
36+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
37+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
38+
static_assert(noexcept(++origin_simd));
39+
std::array<T, array_size> expected_return_value, expected_value;
40+
for (size_t i = 0; i < array_size; ++i) {
41+
expected_return_value[i] = static_cast<T>(i) + 1;
42+
expected_value[i] = static_cast<T>(i) + 1;
43+
}
44+
assert_simd_values_equal<array_size>(++origin_simd, expected_return_value);
45+
assert_simd_values_equal<array_size>(origin_simd, expected_value);
46+
}
47+
};
48+
49+
template <class T, std::size_t>
50+
struct CheckSimdPostfixIncrementOperator {
51+
template <class SimdAbi>
52+
void operator()() {
53+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
54+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
55+
static_assert(noexcept(origin_simd++));
56+
std::array<T, array_size> expected_return_value, expected_value;
57+
for (size_t i = 0; i < array_size; ++i) {
58+
expected_return_value[i] = static_cast<T>(i);
59+
expected_value[i] = static_cast<T>(i) + 1;
60+
}
61+
assert_simd_values_equal<array_size>(origin_simd++, expected_return_value);
62+
assert_simd_values_equal<array_size>(origin_simd, expected_value);
63+
}
64+
};
65+
66+
template <class T, std::size_t>
67+
struct CheckSimdPrefixDecrementOperator {
68+
template <class SimdAbi>
69+
void operator()() {
70+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
71+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
72+
static_assert(noexcept(--origin_simd));
73+
std::array<T, array_size> expected_return_value, expected_value;
74+
for (size_t i = 0; i < array_size; ++i) {
75+
expected_return_value[i] = static_cast<T>(i) - 1;
76+
expected_value[i] = static_cast<T>(i) - 1;
77+
}
78+
assert_simd_values_equal<array_size>(--origin_simd, expected_return_value);
79+
assert_simd_values_equal<array_size>(origin_simd, expected_value);
80+
}
81+
};
82+
83+
template <class T, std::size_t>
84+
struct CheckSimdPostfixDecrementOperator {
85+
template <class SimdAbi>
86+
void operator()() {
87+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
88+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
89+
static_assert(noexcept(origin_simd--));
90+
std::array<T, array_size> expected_return_value, expected_value;
91+
for (size_t i = 0; i < array_size; ++i) {
92+
expected_return_value[i] = static_cast<T>(i);
93+
expected_value[i] = static_cast<T>(i) - 1;
94+
}
95+
assert_simd_values_equal<array_size>(origin_simd--, expected_return_value);
96+
assert_simd_values_equal<array_size>(origin_simd, expected_value);
97+
}
98+
};
99+
100+
template <class T, std::size_t>
101+
struct CheckSimdNegationOperator {
102+
template <class SimdAbi>
103+
void operator()() {
104+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
105+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
106+
static_assert(noexcept(!origin_simd));
107+
std::array<bool, array_size> expected_value;
108+
for (size_t i = 0; i < array_size; ++i)
109+
expected_value[i] = !static_cast<bool>(i);
110+
assert_simd_mask_values_equal<array_size>(!origin_simd, expected_value);
111+
}
112+
};
113+
114+
template <class T, std::size_t>
115+
struct CheckSimdBitwiseNotOperator {
116+
template <class SimdAbi>
117+
void operator()() {
118+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
119+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
120+
static_assert(noexcept(~origin_simd));
121+
std::array<T, array_size> expected_value;
122+
for (size_t i = 0; i < array_size; ++i)
123+
expected_value[i] = ~static_cast<T>(i);
124+
assert_simd_values_equal<array_size>(~origin_simd, expected_value);
125+
}
126+
};
127+
128+
template <class T, std::size_t>
129+
struct CheckSimdPositiveSignOperator {
130+
template <class SimdAbi>
131+
void operator()() {
132+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
133+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
134+
static_assert(noexcept(+origin_simd));
135+
std::array<T, array_size> expected_value;
136+
for (size_t i = 0; i < array_size; ++i)
137+
expected_value[i] = +static_cast<T>(i);
138+
assert_simd_values_equal<array_size>(+origin_simd, expected_value);
139+
}
140+
};
141+
142+
template <class T, std::size_t>
143+
struct CheckSimdNegativeSignOperator {
144+
template <class SimdAbi>
145+
void operator()() {
146+
constexpr std::size_t array_size = ex::simd_size_v<T, SimdAbi>;
147+
ex::simd<T, SimdAbi> origin_simd([](T i) { return i; });
148+
static_assert(noexcept(-origin_simd));
149+
std::array<T, array_size> expected_value;
150+
for (size_t i = 0; i < array_size; ++i)
151+
expected_value[i] = -static_cast<T>(i);
152+
assert_simd_values_equal<array_size>(-origin_simd, expected_value);
153+
}
154+
};
155+
156+
template <class T, class SimdAbi = ex::simd_abi::compatible<T>, class = void>
157+
struct has_bitwise_not_op : std::false_type {};
158+
159+
template <class T, class SimdAbi>
160+
struct has_bitwise_not_op<T, SimdAbi, std::void_t<decltype(~std::declval<ex::simd<T, SimdAbi>>())>> : std::true_type {};
161+
162+
template <class T, std::size_t>
163+
struct CheckSimdBitwiseNotTraits {
164+
template <class SimdAbi>
165+
void operator()() {
166+
// This function shall not participate in overload resolution unless
167+
// T is an integral type.
168+
if constexpr (std::is_integral_v<T>)
169+
static_assert(has_bitwise_not_op<T, SimdAbi>::value);
170+
// T is not an integral type.
171+
else
172+
static_assert(!has_bitwise_not_op<T, SimdAbi>::value);
173+
}
174+
};
175+
176+
int main(int, char**) {
177+
test_all_simd_abi<CheckSimdPrefixIncrementOperator>();
178+
test_all_simd_abi<CheckSimdPostfixIncrementOperator>();
179+
test_all_simd_abi<CheckSimdPrefixDecrementOperator>();
180+
test_all_simd_abi<CheckSimdPostfixDecrementOperator>();
181+
test_all_simd_abi<CheckSimdNegationOperator>();
182+
types::for_each(types::integer_types(), TestAllSimdAbiFunctor<CheckSimdBitwiseNotOperator>());
183+
test_all_simd_abi<CheckSimdPositiveSignOperator>();
184+
test_all_simd_abi<CheckSimdNegativeSignOperator>();
185+
test_all_simd_abi<CheckSimdBitwiseNotTraits>();
186+
return 0;
187+
}

0 commit comments

Comments
 (0)