/src/WasmEdge/include/experimental/simd/backend_m128i.hpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: CC0-1.0 |
2 | | /// |
3 | | // simd - A C++17 implementation of a subset of the C++26 std::simd library |
4 | | // |
5 | | // To the extent possible under law, the author(s) have dedicated all |
6 | | // copyright and related and neighboring rights to this software to the |
7 | | // public domain worldwide. This software is distributed without any warranty. |
8 | | // |
9 | | // You should have received a copy of the CC0 Public Domain Dedication |
10 | | // along with this software. If not, see |
11 | | // <http://creativecommons.org/publicdomain/zero/1.0/>. |
12 | | /// |
13 | | |
14 | | #pragma once |
15 | | |
16 | | #include "experimental/simd/backend_array.hpp" |
17 | | #include "experimental/simd/detail.hpp" |
18 | | |
19 | | #if defined(_M_X64) || defined(__SSE2__) |
20 | | |
21 | | #include <emmintrin.h> |
22 | | #include <limits> |
23 | | #if defined(__SSE4_1__) || defined(__AVX__) |
24 | | #include <smmintrin.h> |
25 | | #endif |
26 | | |
27 | | namespace cxx26 { |
28 | | namespace simd { |
29 | | namespace detail { |
30 | | |
31 | | template <class T, simd_size_type N> struct simd_backend<T, m128i_abi<N>> { |
32 | | static_assert(N * sizeof(T) == 16); |
33 | | |
34 | | using value_type = T; |
35 | | using storage_type = __m128i; |
36 | | using mask_storage = __m128i; |
37 | | |
38 | | using array_backend = simd_backend<T, array_abi<N>>; |
39 | | using array_storage_type = typename array_backend::storage_type; |
40 | | |
41 | | static constexpr bool is_float = std::is_same_v<T, float>; |
42 | | static constexpr bool is_double = std::is_same_v<T, double>; |
43 | | static constexpr bool is_signed_int = |
44 | | std::is_integral_v<T> && std::is_signed_v<T>; |
45 | | |
46 | | using signed_backend = simd_backend<integer_from_t<sizeof(T)>, m128i_abi<N>>; |
47 | | |
48 | 0 | static __m128 as_ps(const storage_type &S) noexcept { |
49 | 0 | return _mm_castsi128_ps(S); |
50 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<int, cxx26::simd::detail::m128i_abi<4> >::as_ps(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::as_ps(long long __vector(2) const&) |
51 | 0 | static __m128d as_pd(const storage_type &S) noexcept { |
52 | 0 | return _mm_castsi128_pd(S); |
53 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<long, cxx26::simd::detail::m128i_abi<2> >::as_pd(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::as_pd(long long __vector(2) const&) |
54 | | |
55 | | static array_storage_type to_array(const storage_type &S) noexcept { |
56 | | return cxx20::bit_cast<array_storage_type>(S); |
57 | | } |
58 | | template <class A> static storage_type from_array(const A &S) noexcept { |
59 | | return cxx20::bit_cast<storage_type>(S); |
60 | | } |
61 | | |
62 | | template <class G> static storage_type generate(G &&Gen) { |
63 | | return from_array(array_backend::generate(std::forward<G>(Gen))); |
64 | | } |
65 | 0 | static storage_type broadcast(T Value) noexcept { |
66 | | if constexpr (is_float) { |
67 | | return _mm_castps_si128(_mm_set1_ps(Value)); |
68 | | } else if constexpr (is_double) { |
69 | | return _mm_castpd_si128(_mm_set1_pd(Value)); |
70 | | } else if constexpr (sizeof(T) == 1) { |
71 | | return _mm_set1_epi8(static_cast<char>(Value)); |
72 | | } else if constexpr (sizeof(T) == 2) { |
73 | | return _mm_set1_epi16(static_cast<short>(Value)); |
74 | 0 | } else if constexpr (sizeof(T) == 4) { |
75 | 0 | return _mm_set1_epi32(static_cast<int>(Value)); |
76 | 0 | } else { |
77 | 0 | return _mm_set1_epi64x(static_cast<long long>(Value)); |
78 | 0 | } |
79 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<int, cxx26::simd::detail::m128i_abi<4> >::broadcast(int) Unexecuted instantiation: cxx26::simd::detail::simd_backend<long, cxx26::simd::detail::m128i_abi<2> >::broadcast(long) |
80 | | static T get(const storage_type &S, simd_size_type I) noexcept { |
81 | | return array_backend::get(to_array(S), I); |
82 | | } |
83 | | static void set_lane(storage_type &S, simd_size_type I, T Value) noexcept { |
84 | | array_storage_type A = to_array(S); |
85 | | array_backend::set_lane(A, I, Value); |
86 | | S = from_array(A); |
87 | | } |
88 | | |
89 | | template <class U> |
90 | | static storage_type convert(const storage_type &S) noexcept { |
91 | | if constexpr (std::is_integral_v<T> && std::is_integral_v<U>) { |
92 | | return S; |
93 | | } else if constexpr (is_float && std::is_same_v<U, std::int32_t>) { |
94 | | return _mm_castps_si128(_mm_cvtepi32_ps(S)); |
95 | | } else if constexpr (std::is_same_v<T, std::int32_t> && |
96 | | std::is_same_v<U, float>) { |
97 | | return _mm_cvttps_epi32(_mm_castsi128_ps(S)); |
98 | | } else { |
99 | | using from_backend = simd_backend<U, m128i_abi<N>>; |
100 | | return from_array( |
101 | | array_backend::template convert<U>(from_backend::to_array(S))); |
102 | | } |
103 | | } |
104 | | |
105 | | #define CXX26_SIMD_M128I_ADD_SUB(NAME) \ |
106 | | static storage_type NAME(const storage_type &A, \ |
107 | 0 | const storage_type &B) noexcept { \ |
108 | 0 | if constexpr (is_float) { \ |
109 | 0 | return _mm_castps_si128(_mm_##NAME##_ps(as_ps(A), as_ps(B))); \ |
110 | 0 | } else if constexpr (is_double) { \ |
111 | 0 | return _mm_castpd_si128(_mm_##NAME##_pd(as_pd(A), as_pd(B))); \ |
112 | 0 | } else if constexpr (sizeof(T) == 1) { \ |
113 | 0 | return _mm_##NAME##_epi8(A, B); \ |
114 | 0 | } else if constexpr (sizeof(T) == 2) { \ |
115 | 0 | return _mm_##NAME##_epi16(A, B); \ |
116 | 0 | } else if constexpr (sizeof(T) == 4) { \ |
117 | 0 | return _mm_##NAME##_epi32(A, B); \ |
118 | 0 | } else { \ |
119 | 0 | return _mm_##NAME##_epi64(A, B); \ |
120 | 0 | } \ |
121 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<int, cxx26::simd::detail::m128i_abi<4> >::sub(long long __vector(2) const&, long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<int, cxx26::simd::detail::m128i_abi<4> >::add(long long __vector(2) const&, long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<long, cxx26::simd::detail::m128i_abi<2> >::sub(long long __vector(2) const&, long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<long, cxx26::simd::detail::m128i_abi<2> >::add(long long __vector(2) const&, long long __vector(2) const&) |
122 | | CXX26_SIMD_M128I_ADD_SUB(add) |
123 | | CXX26_SIMD_M128I_ADD_SUB(sub) |
124 | | #undef CXX26_SIMD_M128I_ADD_SUB |
125 | | static storage_type mul(const storage_type &A, |
126 | | const storage_type &B) noexcept { |
127 | | if constexpr (is_float) { |
128 | | return _mm_castps_si128(_mm_mul_ps(as_ps(A), as_ps(B))); |
129 | | } else if constexpr (is_double) { |
130 | | return _mm_castpd_si128(_mm_mul_pd(as_pd(A), as_pd(B))); |
131 | | } else if constexpr (sizeof(T) == 1) { |
132 | | const __m128i Even = _mm_mullo_epi16(A, B); |
133 | | const __m128i Odd = |
134 | | _mm_mullo_epi16(_mm_srli_epi16(A, 8), _mm_srli_epi16(B, 8)); |
135 | | return _mm_or_si128( |
136 | | _mm_and_si128(Even, _mm_set1_epi16(static_cast<short>(0x00ff))), |
137 | | _mm_slli_epi16(Odd, 8)); |
138 | | } else if constexpr (sizeof(T) == 2) { |
139 | | return _mm_mullo_epi16(A, B); |
140 | | } else if constexpr (sizeof(T) == 4) { |
141 | | const __m128i Even = _mm_mul_epu32(A, B); |
142 | | const __m128i Odd = |
143 | | _mm_mul_epu32(_mm_srli_epi64(A, 32), _mm_srli_epi64(B, 32)); |
144 | | return _mm_unpacklo_epi32( |
145 | | _mm_shuffle_epi32(Even, _MM_SHUFFLE(0, 0, 2, 0)), |
146 | | _mm_shuffle_epi32(Odd, _MM_SHUFFLE(0, 0, 2, 0))); |
147 | | } else { |
148 | | const __m128i Cross = |
149 | | _mm_add_epi64(_mm_mul_epu32(_mm_srli_epi64(A, 32), B), |
150 | | _mm_mul_epu32(A, _mm_srli_epi64(B, 32))); |
151 | | return _mm_add_epi64(_mm_mul_epu32(A, B), _mm_slli_epi64(Cross, 32)); |
152 | | } |
153 | | } |
154 | | static storage_type div(const storage_type &A, |
155 | | const storage_type &B) noexcept { |
156 | | if constexpr (is_float) { |
157 | | return _mm_castps_si128(_mm_div_ps(as_ps(A), as_ps(B))); |
158 | | } else if constexpr (is_double) { |
159 | | return _mm_castpd_si128(_mm_div_pd(as_pd(A), as_pd(B))); |
160 | | } else { |
161 | | return from_array(array_backend::div(to_array(A), to_array(B))); |
162 | | } |
163 | | } |
164 | | static storage_type bit_and(const storage_type &A, |
165 | 0 | const storage_type &B) noexcept { |
166 | 0 | return _mm_and_si128(A, B); |
167 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::bit_and(long long __vector(2) const&, long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::bit_and(long long __vector(2) const&, long long __vector(2) const&) |
168 | | static storage_type bit_or(const storage_type &A, |
169 | 0 | const storage_type &B) noexcept { |
170 | 0 | return _mm_or_si128(A, B); |
171 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::bit_or(long long __vector(2) const&, long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::bit_or(long long __vector(2) const&, long long __vector(2) const&) |
172 | | static storage_type bit_xor(const storage_type &A, |
173 | | const storage_type &B) noexcept { |
174 | | return _mm_xor_si128(A, B); |
175 | | } |
176 | 0 | static storage_type bit_not(const storage_type &A) noexcept { |
177 | 0 | return _mm_xor_si128(A, _mm_set1_epi32(-1)); |
178 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::bit_not(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::bit_not(long long __vector(2) const&) |
179 | | |
180 | | #define CXX26_SIMD_M128I_VIA_ARRAY(NAME) \ |
181 | | static storage_type NAME(const storage_type &A, \ |
182 | | const storage_type &B) noexcept { \ |
183 | | return from_array(array_backend::NAME(to_array(A), to_array(B))); \ |
184 | | } |
185 | | CXX26_SIMD_M128I_VIA_ARRAY(shl) |
186 | | CXX26_SIMD_M128I_VIA_ARRAY(shr) |
187 | | CXX26_SIMD_M128I_VIA_ARRAY(permute) |
188 | | #undef CXX26_SIMD_M128I_VIA_ARRAY |
189 | | |
190 | | static storage_type shl(const storage_type &A, simd_size_type S) noexcept { |
191 | | const __m128i Count = _mm_cvtsi32_si128(S); |
192 | | if constexpr (sizeof(T) == 1) { |
193 | | return _mm_and_si128(_mm_sll_epi16(A, Count), |
194 | | _mm_set1_epi8(static_cast<char>(0xff << S))); |
195 | | } else if constexpr (sizeof(T) == 2) { |
196 | | return _mm_sll_epi16(A, Count); |
197 | | } else if constexpr (sizeof(T) == 4) { |
198 | | return _mm_sll_epi32(A, Count); |
199 | | } else { |
200 | | return _mm_sll_epi64(A, Count); |
201 | | } |
202 | | } |
203 | 0 | static storage_type shr(const storage_type &A, simd_size_type S) noexcept { |
204 | 0 | const __m128i Count = _mm_cvtsi32_si128(S); |
205 | | if constexpr (sizeof(T) == 1 && is_signed_int) { |
206 | | const __m128i Wide = _mm_cvtsi32_si128(S + 8); |
207 | | return _mm_packs_epi16(_mm_sra_epi16(_mm_unpacklo_epi8(A, A), Wide), |
208 | | _mm_sra_epi16(_mm_unpackhi_epi8(A, A), Wide)); |
209 | | } else if constexpr (sizeof(T) == 1) { |
210 | | return _mm_and_si128(_mm_srl_epi16(A, Count), |
211 | | _mm_set1_epi8(static_cast<char>(0xff >> S))); |
212 | | } else if constexpr (sizeof(T) == 2 && is_signed_int) { |
213 | | return _mm_sra_epi16(A, Count); |
214 | | } else if constexpr (sizeof(T) == 2) { |
215 | | return _mm_srl_epi16(A, Count); |
216 | 0 | } else if constexpr (sizeof(T) == 4 && is_signed_int) { |
217 | 0 | return _mm_sra_epi32(A, Count); |
218 | | } else if constexpr (sizeof(T) == 4) { |
219 | | return _mm_srl_epi32(A, Count); |
220 | 0 | } else if constexpr (is_signed_int) { |
221 | 0 | const __m128i Sign = _mm_srl_epi64( |
222 | 0 | _mm_set1_epi64x(std::numeric_limits<long long>::min()), Count); |
223 | 0 | return _mm_sub_epi64(_mm_xor_si128(_mm_srl_epi64(A, Count), Sign), Sign); |
224 | | } else { |
225 | | return _mm_srl_epi64(A, Count); |
226 | | } |
227 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<int, cxx26::simd::detail::m128i_abi<4> >::shr(long long __vector(2) const&, int) Unexecuted instantiation: cxx26::simd::detail::simd_backend<long, cxx26::simd::detail::m128i_abi<2> >::shr(long long __vector(2) const&, int) |
228 | | static storage_type neg(const storage_type &A) noexcept { |
229 | | if constexpr (std::is_floating_point_v<T>) { |
230 | | return bit_xor(A, broadcast(static_cast<T>(-0.0))); |
231 | | } else { |
232 | | return sub(_mm_setzero_si128(), A); |
233 | | } |
234 | | } |
235 | | |
236 | 0 | static storage_type sqrt(const storage_type &A) noexcept { |
237 | 0 | if constexpr (is_float) { |
238 | 0 | return _mm_castps_si128(_mm_sqrt_ps(as_ps(A))); |
239 | 0 | } else { |
240 | 0 | return _mm_castpd_si128(_mm_sqrt_pd(as_pd(A))); |
241 | 0 | } |
242 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::sqrt(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::sqrt(long long __vector(2) const&) |
243 | | |
244 | | // std::round_to_nearest rounds ties to even, the IEEE default. |
245 | | #if defined(__SSE4_1__) || defined(__AVX__) |
246 | | template <std::float_round_style R> |
247 | | static storage_type round(const storage_type &A) noexcept { |
248 | | constexpr int Mode = |
249 | | (R == std::round_to_nearest ? _MM_FROUND_TO_NEAREST_INT |
250 | | : R == std::round_toward_infinity ? _MM_FROUND_TO_POS_INF |
251 | | : R == std::round_toward_neg_infinity ? _MM_FROUND_TO_NEG_INF |
252 | | : _MM_FROUND_TO_ZERO) | |
253 | | _MM_FROUND_NO_EXC; |
254 | | if constexpr (is_float) { |
255 | | return _mm_castps_si128(_mm_round_ps(as_ps(A), Mode)); |
256 | | } else { |
257 | | return _mm_castpd_si128(_mm_round_pd(as_pd(A), Mode)); |
258 | | } |
259 | | } |
260 | | #else |
261 | | // x86 has no rounding instruction before SSE4.1. The rounding works on the |
262 | | // bit pattern with integer instructions, so it does not depend on the MXCSR |
263 | | // rounding mode or on denormals-are-zero. |
264 | | using int_type = integer_from_t<sizeof(T)>; |
265 | | static constexpr int mantissa_bits = std::numeric_limits<T>::digits - 1; |
266 | | static constexpr int_type exponent_bias = |
267 | | std::numeric_limits<T>::max_exponent - 1; |
268 | | |
269 | | // For a biased exponent E with 0 <= E - bias < mantissa_bits, returns the |
270 | | // integer 2^(mantissa_bits - (E - bias)), which is one unit in the place of |
271 | | // the lowest integer bit of the significand. Other lanes are unspecified. |
272 | 0 | static storage_type integer_unit(const storage_type &Exponent) noexcept { |
273 | 0 | if constexpr (is_float) { |
274 | | // The float 2^K converts exactly to the integer 2^K. |
275 | 0 | const __m128i Pow = _mm_slli_epi32( |
276 | 0 | _mm_sub_epi32(_mm_set1_epi32(2 * exponent_bias + mantissa_bits), |
277 | 0 | Exponent), |
278 | 0 | mantissa_bits); |
279 | 0 | return _mm_cvttps_epi32(_mm_castsi128_ps(Pow)); |
280 | 0 | } else { |
281 | 0 | const __m128i Shift = _mm_sub_epi64( |
282 | 0 | _mm_set1_epi64x(exponent_bias + mantissa_bits), Exponent); |
283 | 0 | const __m128i One = _mm_set1_epi64x(1); |
284 | 0 | const __m128i Low = _mm_sll_epi64(One, Shift); |
285 | 0 | const __m128i High = _mm_sll_epi64(One, _mm_unpackhi_epi64(Shift, Shift)); |
286 | 0 | return _mm_castpd_si128( |
287 | 0 | _mm_move_sd(_mm_castsi128_pd(High), _mm_castsi128_pd(Low))); |
288 | 0 | } |
289 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::integer_unit(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::integer_unit(long long __vector(2) const&) |
290 | | |
291 | | template <std::float_round_style R> |
292 | 0 | static storage_type round(const storage_type &A) noexcept { |
293 | 0 | using ib = signed_backend; |
294 | 0 | const auto Splat = [](auto Value) { |
295 | 0 | return ib::broadcast(static_cast<int_type>(Value)); |
296 | 0 | }; Unexecuted instantiation: auto cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::round<(std::__1::float_round_style)0>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<int>(int) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)0>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<long>(long) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)0>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<int>(int) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::round<(std::__1::float_round_style)2>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<int>(int) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::round<(std::__1::float_round_style)3>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<int>(int) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::round<(std::__1::float_round_style)1>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<int>(int) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)2>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<long>(long) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)2>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<int>(int) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)3>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<long>(long) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)3>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<int>(int) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)1>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<long>(long) constUnexecuted instantiation: auto cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)1>(long long __vector(2) const&)::{lambda(auto:1)#1}::operator()<int>(int) const |
297 | 0 | const storage_type Zero = _mm_setzero_si128(); |
298 | 0 | const storage_type SignMask = Splat(std::numeric_limits<int_type>::min()); |
299 | 0 | const storage_type OneBits = Splat(exponent_bias << mantissa_bits); |
300 | 0 | const storage_type Sign = bit_and(A, SignMask); |
301 | 0 | const storage_type Abs = _mm_andnot_si128(SignMask, A); |
302 | 0 | [[maybe_unused]] const storage_type Positive = ib::eq(Sign, Zero); |
303 | 0 | [[maybe_unused]] const storage_type NonZero = bit_not(ib::eq(Abs, Zero)); |
304 | | |
305 | | // |A| < 1 rounds to zero or to one with the sign of A. |
306 | 0 | storage_type Small = Sign; |
307 | 0 | if constexpr (R == std::round_toward_infinity) { |
308 | 0 | Small = bit_or(Small, bit_and(bit_and(Positive, NonZero), OneBits)); |
309 | 0 | } else if constexpr (R == std::round_toward_neg_infinity) { |
310 | 0 | Small = |
311 | 0 | bit_or(Small, bit_and(_mm_andnot_si128(Positive, NonZero), OneBits)); |
312 | 0 | } else if constexpr (R == std::round_to_nearest) { |
313 | 0 | const storage_type HalfBits = Splat((exponent_bias - 1) << mantissa_bits); |
314 | 0 | Small = bit_or(Small, bit_and(ib::gt(Abs, HalfBits), OneBits)); |
315 | 0 | } |
316 | | |
317 | | // 1 <= |A| < 2^mantissa_bits clears the fraction bits, then adds one unit |
318 | | // when the rounding goes away from zero. |
319 | 0 | const storage_type Unit = integer_unit(ib::shr(Abs, mantissa_bits)); |
320 | 0 | const storage_type FractionMask = ib::sub(Unit, Splat(1)); |
321 | 0 | const storage_type Fraction = bit_and(Abs, FractionMask); |
322 | 0 | const storage_type Truncated = _mm_andnot_si128(FractionMask, A); |
323 | 0 | [[maybe_unused]] const storage_type HasFraction = |
324 | 0 | bit_not(ib::eq(Fraction, Zero)); |
325 | 0 | storage_type Up = Zero; |
326 | 0 | if constexpr (R == std::round_toward_infinity) { |
327 | 0 | Up = bit_and(HasFraction, Positive); |
328 | 0 | } else if constexpr (R == std::round_toward_neg_infinity) { |
329 | 0 | Up = _mm_andnot_si128(Positive, HasFraction); |
330 | 0 | } else if constexpr (R == std::round_to_nearest) { |
331 | 0 | const storage_type Half = ib::shr(Unit, 1); |
332 | 0 | const storage_type Odd = bit_not(ib::eq(bit_and(Truncated, Unit), Zero)); |
333 | 0 | Up = bit_or(ib::gt(Fraction, Half), bit_and(ib::eq(Fraction, Half), Odd)); |
334 | 0 | } |
335 | 0 | const storage_type Middle = ib::add(Truncated, bit_and(Up, Unit)); |
336 | | |
337 | | // |A| >= 2^mantissa_bits is already integral, infinite or NaN. A NaN gets |
338 | | // its quiet bit set. |
339 | 0 | const storage_type InfBits = |
340 | 0 | Splat((2 * exponent_bias + 1) << mantissa_bits); |
341 | 0 | const storage_type QuietBit = Splat(int_type{1} << (mantissa_bits - 1)); |
342 | 0 | const storage_type Large = |
343 | 0 | bit_or(A, bit_and(ib::gt(Abs, InfBits), QuietBit)); |
344 | |
|
345 | 0 | const storage_type IntegralBits = |
346 | 0 | Splat((exponent_bias + mantissa_bits) << mantissa_bits); |
347 | 0 | return select(ib::gt(OneBits, Abs), Small, |
348 | 0 | select(ib::gt(IntegralBits, Abs), Middle, Large)); |
349 | 0 | } Unexecuted instantiation: long long __vector(2) cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::round<(std::__1::float_round_style)0>(long long __vector(2) const&) Unexecuted instantiation: long long __vector(2) cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)0>(long long __vector(2) const&) Unexecuted instantiation: long long __vector(2) cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::round<(std::__1::float_round_style)2>(long long __vector(2) const&) Unexecuted instantiation: long long __vector(2) cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::round<(std::__1::float_round_style)3>(long long __vector(2) const&) Unexecuted instantiation: long long __vector(2) cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::round<(std::__1::float_round_style)1>(long long __vector(2) const&) Unexecuted instantiation: long long __vector(2) cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)2>(long long __vector(2) const&) Unexecuted instantiation: long long __vector(2) cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)3>(long long __vector(2) const&) Unexecuted instantiation: long long __vector(2) cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::round<(std::__1::float_round_style)1>(long long __vector(2) const&) |
350 | | #endif |
351 | 0 | static storage_type ceil(const storage_type &A) noexcept { |
352 | 0 | return round<std::round_toward_infinity>(A); |
353 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::ceil(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::ceil(long long __vector(2) const&) |
354 | 0 | static storage_type floor(const storage_type &A) noexcept { |
355 | 0 | return round<std::round_toward_neg_infinity>(A); |
356 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::floor(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::floor(long long __vector(2) const&) |
357 | 0 | static storage_type trunc(const storage_type &A) noexcept { |
358 | 0 | return round<std::round_toward_zero>(A); |
359 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::trunc(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::trunc(long long __vector(2) const&) |
360 | 0 | static storage_type roundeven(const storage_type &A) noexcept { |
361 | 0 | return round<std::round_to_nearest>(A); |
362 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::roundeven(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::roundeven(long long __vector(2) const&) |
363 | | |
364 | | static mask_storage eq(const storage_type &A, |
365 | 0 | const storage_type &B) noexcept { |
366 | | if constexpr (is_float) { |
367 | | return _mm_castps_si128(_mm_cmpeq_ps(as_ps(A), as_ps(B))); |
368 | | } else if constexpr (is_double) { |
369 | | return _mm_castpd_si128(_mm_cmpeq_pd(as_pd(A), as_pd(B))); |
370 | | } else if constexpr (sizeof(T) == 1) { |
371 | | return _mm_cmpeq_epi8(A, B); |
372 | | } else if constexpr (sizeof(T) == 2) { |
373 | | return _mm_cmpeq_epi16(A, B); |
374 | 0 | } else if constexpr (sizeof(T) == 4) { |
375 | 0 | return _mm_cmpeq_epi32(A, B); |
376 | 0 | } else { |
377 | 0 | const __m128i Half = _mm_cmpeq_epi32(A, B); |
378 | 0 | return _mm_and_si128(Half, |
379 | 0 | _mm_shuffle_epi32(Half, _MM_SHUFFLE(2, 3, 0, 1))); |
380 | 0 | } |
381 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<int, cxx26::simd::detail::m128i_abi<4> >::eq(long long __vector(2) const&, long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<long, cxx26::simd::detail::m128i_abi<2> >::eq(long long __vector(2) const&, long long __vector(2) const&) |
382 | | static mask_storage gt(const storage_type &A, |
383 | 0 | const storage_type &B) noexcept { |
384 | | if constexpr (is_float) { |
385 | | return _mm_castps_si128(_mm_cmpgt_ps(as_ps(A), as_ps(B))); |
386 | | } else if constexpr (is_double) { |
387 | | return _mm_castpd_si128(_mm_cmpgt_pd(as_pd(A), as_pd(B))); |
388 | | } else if constexpr (is_signed_int && sizeof(T) == 1) { |
389 | | return _mm_cmpgt_epi8(A, B); |
390 | | } else if constexpr (is_signed_int && sizeof(T) == 2) { |
391 | | return _mm_cmpgt_epi16(A, B); |
392 | 0 | } else if constexpr (is_signed_int && sizeof(T) == 4) { |
393 | 0 | return _mm_cmpgt_epi32(A, B); |
394 | | } else if constexpr (sizeof(T) < 8) { |
395 | | const __m128i Bias = signed_backend::broadcast( |
396 | | std::numeric_limits<integer_from_t<sizeof(T)>>::min()); |
397 | | return signed_backend::gt(_mm_xor_si128(A, Bias), _mm_xor_si128(B, Bias)); |
398 | 0 | } else { |
399 | 0 | constexpr int Min = std::numeric_limits<std::int32_t>::min(); |
400 | 0 | const __m128i Bias = |
401 | 0 | is_signed_int ? _mm_set_epi32(0, Min, 0, Min) : _mm_set1_epi32(Min); |
402 | 0 | const __m128i X = _mm_xor_si128(A, Bias); |
403 | 0 | const __m128i Y = _mm_xor_si128(B, Bias); |
404 | 0 | const __m128i Gt = _mm_cmpgt_epi32(X, Y); |
405 | 0 | const __m128i HighEq = |
406 | 0 | _mm_shuffle_epi32(_mm_cmpeq_epi32(X, Y), _MM_SHUFFLE(3, 3, 1, 1)); |
407 | 0 | return _mm_or_si128( |
408 | 0 | _mm_shuffle_epi32(Gt, _MM_SHUFFLE(3, 3, 1, 1)), |
409 | 0 | _mm_and_si128(HighEq, |
410 | 0 | _mm_shuffle_epi32(Gt, _MM_SHUFFLE(2, 2, 0, 0)))); |
411 | 0 | } |
412 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<int, cxx26::simd::detail::m128i_abi<4> >::gt(long long __vector(2) const&, long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<long, cxx26::simd::detail::m128i_abi<2> >::gt(long long __vector(2) const&, long long __vector(2) const&) |
413 | | static mask_storage lt(const storage_type &A, |
414 | | const storage_type &B) noexcept { |
415 | | return gt(B, A); |
416 | | } |
417 | | #define CXX26_SIMD_M128I_COMPARE(NAME, CMP, INTEGER) \ |
418 | | static mask_storage NAME(const storage_type &A, \ |
419 | | const storage_type &B) noexcept { \ |
420 | | if constexpr (is_float) { \ |
421 | | return _mm_castps_si128(_mm_cmp##CMP##_ps(as_ps(A), as_ps(B))); \ |
422 | | } else if constexpr (is_double) { \ |
423 | | return _mm_castpd_si128(_mm_cmp##CMP##_pd(as_pd(A), as_pd(B))); \ |
424 | | } else { \ |
425 | | return INTEGER; \ |
426 | | } \ |
427 | | } |
428 | | CXX26_SIMD_M128I_COMPARE(ne, neq, bit_not(eq(A, B))) |
429 | | CXX26_SIMD_M128I_COMPARE(le, le, bit_not(gt(A, B))) |
430 | | CXX26_SIMD_M128I_COMPARE(ge, ge, bit_not(gt(B, A))) |
431 | | #undef CXX26_SIMD_M128I_COMPARE |
432 | | |
433 | | static storage_type select(const mask_storage &M, const storage_type &A, |
434 | 0 | const storage_type &B) noexcept { |
435 | 0 | return _mm_or_si128(_mm_and_si128(M, A), _mm_andnot_si128(M, B)); |
436 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<float, cxx26::simd::detail::m128i_abi<4> >::select(long long __vector(2) const&, long long __vector(2) const&, long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<double, cxx26::simd::detail::m128i_abi<2> >::select(long long __vector(2) const&, long long __vector(2) const&, long long __vector(2) const&) |
437 | | |
438 | 0 | static unsigned long long to_ullong(const storage_type &S) noexcept { |
439 | 0 | if constexpr (sizeof(T) == 1) { |
440 | 0 | return static_cast<unsigned long long>(_mm_movemask_epi8(S)); |
441 | 0 | } else if constexpr (sizeof(T) == 2) { |
442 | 0 | return static_cast<unsigned long long>( |
443 | 0 | _mm_movemask_epi8(_mm_packs_epi16(S, _mm_setzero_si128()))); |
444 | 0 | } else if constexpr (sizeof(T) == 4) { |
445 | 0 | return static_cast<unsigned long long>(_mm_movemask_ps(as_ps(S))); |
446 | 0 | } else { |
447 | 0 | return static_cast<unsigned long long>(_mm_movemask_pd(as_pd(S))); |
448 | 0 | } |
449 | 0 | } Unexecuted instantiation: cxx26::simd::detail::simd_backend<signed char, cxx26::simd::detail::m128i_abi<16> >::to_ullong(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<short, cxx26::simd::detail::m128i_abi<8> >::to_ullong(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<int, cxx26::simd::detail::m128i_abi<4> >::to_ullong(long long __vector(2) const&) Unexecuted instantiation: cxx26::simd::detail::simd_backend<long, cxx26::simd::detail::m128i_abi<2> >::to_ullong(long long __vector(2) const&) |
450 | | }; |
451 | | |
452 | | } // namespace detail |
453 | | } // namespace simd |
454 | | } // namespace cxx26 |
455 | | |
456 | | #endif |