Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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) const
Unexecuted 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