Skip to content

Commit 393f193

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

File tree

5 files changed

+261
-5
lines changed

5 files changed

+261
-5
lines changed

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: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,29 @@
2525
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL
2626
inline namespace parallelism_v2 {
2727

28+
template <class _Simd, class _Impl, bool>
29+
class __simd_int_operators {};
30+
31+
template <class _Simd, class _Impl>
32+
class __simd_int_operators<_Simd, _Impl, true> {
33+
public:
34+
// unary operators for integral _Tp
35+
_LIBCPP_HIDE_FROM_ABI _Simd operator~() const noexcept {
36+
return _Simd(_Impl::__bitwise_not((*static_cast<const _Simd*>(this)).__s_), _Simd::storage_tag);
37+
}
38+
};
39+
2840
// class template simd [simd.class]
2941
// TODO: implement simd class
3042
template <class _Tp, class _Abi>
31-
class simd {
43+
class simd : public __simd_int_operators<simd<_Tp, _Abi>, __simd_operations<_Tp, _Abi>, is_integral_v<_Tp>> {
3244
using _Impl = __simd_operations<_Tp, _Abi>;
3345
using _Storage = typename _Impl::_SimdStorage;
3446

3547
_Storage __s_;
3648

49+
friend class __simd_int_operators<simd, _Impl, true>;
50+
3751
public:
3852
using value_type = _Tp;
3953
using reference = __simd_reference<_Tp, _Storage, value_type>;
@@ -45,8 +59,10 @@ class simd {
4559
_LIBCPP_HIDE_FROM_ABI simd() noexcept = default;
4660

4761
// 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) {}
62+
struct storage_tag_t {};
63+
static constexpr storage_tag_t storage_tag{};
64+
explicit _LIBCPP_HIDE_FROM_ABI operator _Storage() const { return __s_; }
65+
explicit _LIBCPP_HIDE_FROM_ABI simd(const _Storage& __s, storage_tag_t) : __s_(__s) {}
5066

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

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

0 commit comments

Comments
 (0)