File indexing completed on 2026-11-07 10:37:05
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #ifndef XSIMD_AVX512VL_128_HPP
0014 #define XSIMD_AVX512VL_128_HPP
0015
0016 #include "../types/xsimd_avx512vl_register.hpp"
0017 #include "../types/xsimd_batch_constant.hpp"
0018
0019 #include <type_traits>
0020
0021 namespace xsimd
0022 {
0023 namespace kernel
0024 {
0025 using namespace types;
0026
0027 namespace detail
0028 {
0029
0030
0031
0032
0033 XSIMD_INLINE uint32_t morton(uint16_t x, uint16_t y) noexcept;
0034 template <size_t N>
0035 XSIMD_INLINE unsigned char tobitset(unsigned char unpacked[N]);
0036
0037 template <class A, class T, int Cmp>
0038 XSIMD_INLINE batch_bool<T, A> compare_int_avx512vl_128(batch<T, A> const& self, batch<T, A> const& other) noexcept
0039 {
0040 using register_type = typename batch_bool<T, A>::register_type;
0041 if (std::is_signed<T>::value)
0042 {
0043 XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
0044 {
0045
0046 uint64_t mask_low0 = _mm_cmp_epi32_mask((batch<int32_t, A>(self.data) & batch<int32_t, A>(0x000000FF)) << 24,
0047 (batch<int32_t, A>(other.data) & batch<int32_t, A>(0x000000FF)) << 24,
0048 Cmp);
0049 uint64_t mask_low1 = _mm_cmp_epi32_mask((batch<int32_t, A>(self.data) & batch<int32_t, A>(0x0000FF00)) << 16,
0050 (batch<int32_t, A>(other.data) & batch<int32_t, A>(0x0000FF00)) << 16,
0051 Cmp);
0052 uint64_t mask_high0 = _mm_cmp_epi32_mask((batch<int32_t, A>(self.data) & batch<int32_t, A>(0x00FF0000)) << 8,
0053 (batch<int32_t, A>(other.data) & batch<int32_t, A>(0x00FF0000)) << 8,
0054 Cmp);
0055 uint64_t mask_high1 = _mm_cmp_epi32_mask((batch<int32_t, A>(self.data) & batch<int32_t, A>(0xFF000000)),
0056 (batch<int32_t, A>(other.data) & batch<int32_t, A>(0xFF000000)),
0057 Cmp);
0058 uint64_t mask = 0;
0059 for (unsigned i = 0; i < 8; ++i)
0060 {
0061 mask |= (mask_low0 & (uint64_t(1) << i)) << (3 * i + 0);
0062 mask |= (mask_low1 & (uint64_t(1) << i)) << (3 * i + 1);
0063 mask |= (mask_high0 & (uint64_t(1) << i)) << (3 * i + 2);
0064 mask |= (mask_high1 & (uint64_t(1) << i)) << (3 * i + 3);
0065 }
0066 return (register_type)mask;
0067 }
0068 else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
0069 {
0070
0071 uint16_t mask_low = _mm_cmp_epi32_mask((batch<int32_t, A>(self.data) & batch<int32_t, A>(0x0000FFFF)) << 16,
0072 (batch<int32_t, A>(other.data) & batch<int32_t, A>(0x0000FFFF)) << 16,
0073 Cmp);
0074 uint16_t mask_high = _mm_cmp_epi32_mask((batch<int32_t, A>(self.data) & batch<int32_t, A>(0xFFFF0000)),
0075 (batch<int32_t, A>(other.data) & batch<int32_t, A>(0xFFFF0000)),
0076 Cmp);
0077 return static_cast<register_type>(morton(mask_low, mask_high));
0078 }
0079 else XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
0080 {
0081 return (register_type)_mm_cmp_epi32_mask(self, other, Cmp);
0082 }
0083 else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
0084 {
0085 return (register_type)_mm_cmp_epi64_mask(self, other, Cmp);
0086 }
0087 }
0088 else
0089 {
0090 XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
0091 {
0092 uint64_t mask_low0 = _mm_cmp_epu32_mask((batch<uint32_t, A>(self.data) & batch<uint32_t, A>(0x000000FF)), (batch<uint32_t, A>(other.data) & batch<uint32_t, A>(0x000000FF)), Cmp);
0093 uint64_t mask_low1 = _mm_cmp_epu32_mask((batch<uint32_t, A>(self.data) & batch<uint32_t, A>(0x0000FF00)), (batch<uint32_t, A>(other.data) & batch<uint32_t, A>(0x0000FF00)), Cmp);
0094 uint64_t mask_high0 = _mm_cmp_epu32_mask((batch<uint32_t, A>(self.data) & batch<uint32_t, A>(0x00FF0000)), (batch<uint32_t, A>(other.data) & batch<uint32_t, A>(0x00FF0000)), Cmp);
0095 uint64_t mask_high1 = _mm_cmp_epu32_mask((batch<uint32_t, A>(self.data) & batch<uint32_t, A>(0xFF000000)), (batch<uint32_t, A>(other.data) & batch<uint32_t, A>(0xFF000000)), Cmp);
0096 uint64_t mask = 0;
0097 for (unsigned i = 0; i < 8; ++i)
0098 {
0099 mask |= (mask_low0 & (uint64_t(1) << i)) << (3 * i + 0);
0100 mask |= (mask_low1 & (uint64_t(1) << i)) << (3 * i + 1);
0101 mask |= (mask_high0 & (uint64_t(1) << i)) << (3 * i + 2);
0102 mask |= (mask_high1 & (uint64_t(1) << i)) << (3 * i + 3);
0103 }
0104 return (register_type)mask;
0105 }
0106 else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
0107 {
0108 uint16_t mask_low = _mm_cmp_epu32_mask((batch<uint32_t, A>(self.data) & batch<uint32_t, A>(0x0000FFFF)), (batch<uint32_t, A>(other.data) & batch<uint32_t, A>(0x0000FFFF)), Cmp);
0109 uint16_t mask_high = _mm_cmp_epu32_mask((batch<uint32_t, A>(self.data) & batch<uint32_t, A>(0xFFFF0000)), (batch<uint32_t, A>(other.data) & batch<uint32_t, A>(0xFFFF0000)), Cmp);
0110 return static_cast<register_type>(morton(mask_low, mask_high));
0111 }
0112 else XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
0113 {
0114 return (register_type)_mm_cmp_epu32_mask(self, other, Cmp);
0115 }
0116 else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
0117 {
0118 return (register_type)_mm_cmp_epu64_mask(self, other, Cmp);
0119 }
0120 }
0121 }
0122 }
0123
0124
0125 template <class A, class T>
0126 XSIMD_INLINE batch_bool<T, A> load_unaligned(bool const* mem, batch_bool<T, A>, requires_arch<avx512vl_128>) noexcept
0127 {
0128 using register_type = typename batch_bool<T, A>::register_type;
0129 constexpr auto size = batch_bool<T, A>::size;
0130 constexpr auto chunk_size = size >= 8 ? 8 : (size >= 4 ? 4 : 2);
0131 constexpr auto iter = size / chunk_size;
0132 static_assert((size % chunk_size) == 0, "incorrect size of bool batch");
0133 register_type mask = 0;
0134 for (std::size_t i = 0; i < iter; ++i)
0135 {
0136 unsigned char block = detail::tobitset<chunk_size>((unsigned char*)mem + i * chunk_size);
0137 mask |= (register_type(block) << (i * chunk_size));
0138 }
0139 return mask;
0140 }
0141
0142
0143 template <class A, class T>
0144 XSIMD_INLINE batch<T, A> from_bool(batch_bool<T, A> const& self, requires_arch<avx512vl_128>) noexcept
0145 {
0146 return select(self, batch<T, A>(1), batch<T, A>(0));
0147 }
0148
0149
0150 template <class T, class A>
0151 XSIMD_INLINE batch_bool<T, A> from_mask(batch_bool<T, A> const&, uint64_t mask, requires_arch<avx512vl_128>) noexcept
0152 {
0153 assert(mask == (mask & ((uint64_t(1) << batch_bool<T, A>::size) - 1)) && "inbound mask");
0154 return static_cast<typename batch_bool<T, A>::register_type>(mask & ((uint64_t(1) << batch_bool<T, A>::size) - 1));
0155 }
0156
0157
0158 template <class A, class T>
0159 XSIMD_INLINE uint64_t mask(batch_bool<T, A> const& self, requires_arch<avx512vl_128>) noexcept
0160 {
0161 return self.data & ((uint64_t(1) << batch_bool<T, A>::size) - 1);
0162 }
0163
0164
0165 template <class A, class T_out, class T_in>
0166 XSIMD_INLINE batch_bool<T_out, A> batch_bool_cast(batch_bool<T_in, A> const& self, batch_bool<T_out, A> const&, requires_arch<avx512vl_128>) noexcept
0167 {
0168 return self.data;
0169 }
0170
0171
0172 template <class A, class T, class... Values>
0173 XSIMD_INLINE batch_bool<T, A> set(batch_bool<T, A> const&, requires_arch<avx512vl_128>, Values... values) noexcept
0174 {
0175 static_assert(sizeof...(Values) == batch_bool<T, A>::size, "consistent init");
0176 using register_type = typename batch_bool<T, A>::register_type;
0177 register_type r = 0;
0178 unsigned shift = 0;
0179 (void)std::initializer_list<register_type> { (r |= register_type(values ? 1 : 0) << (shift++))... };
0180 return r;
0181 }
0182
0183
0184 template <class T, class A>
0185 XSIMD_INLINE void store(batch_bool<T, A> const& self, bool* mem, requires_arch<avx512vl_128>) noexcept
0186 {
0187 constexpr auto size = batch_bool<T, A>::size;
0188 for (std::size_t i = 0; i < size; ++i)
0189 mem[i] = (self.data >> i) & 0x1;
0190 }
0191
0192
0193 template <class A>
0194 XSIMD_INLINE batch<int64_t, A> abs(batch<int64_t, A> const& self, requires_arch<avx512vl_128>) noexcept
0195 {
0196 return _mm_abs_epi64(self);
0197 }
0198
0199
0200
0201
0202
0203
0204
0205
0206 namespace detail
0207 {
0208
0209
0210
0211 template <class T, class Mode, enable_sized_integral_t<T, 4> = 0>
0212 XSIMD_INLINE __m128i maskload128(T const* mem, uint64_t m, Mode) noexcept
0213 {
0214 XSIMD_IF_CONSTEXPR(std::is_same<Mode, aligned_mode>::value)
0215 {
0216 return _mm_maskz_load_epi32((__mmask8)m, mem);
0217 }
0218 else
0219 {
0220 return _mm_maskz_loadu_epi32((__mmask8)m, mem);
0221 }
0222 }
0223 template <class T, class Mode, enable_sized_integral_t<T, 8> = 0>
0224 XSIMD_INLINE __m128i maskload128(T const* mem, uint64_t m, Mode) noexcept
0225 {
0226 XSIMD_IF_CONSTEXPR(std::is_same<Mode, aligned_mode>::value)
0227 {
0228 return _mm_maskz_load_epi64((__mmask8)m, mem);
0229 }
0230 else
0231 {
0232 return _mm_maskz_loadu_epi64((__mmask8)m, mem);
0233 }
0234 }
0235 template <class Mode>
0236 XSIMD_INLINE __m128 maskload128(float const* mem, uint64_t m, Mode) noexcept
0237 {
0238 XSIMD_IF_CONSTEXPR(std::is_same<Mode, aligned_mode>::value)
0239 {
0240 return _mm_maskz_load_ps((__mmask8)m, mem);
0241 }
0242 else
0243 {
0244 return _mm_maskz_loadu_ps((__mmask8)m, mem);
0245 }
0246 }
0247 template <class Mode>
0248 XSIMD_INLINE __m128d maskload128(double const* mem, uint64_t m, Mode) noexcept
0249 {
0250 XSIMD_IF_CONSTEXPR(std::is_same<Mode, aligned_mode>::value)
0251 {
0252 return _mm_maskz_load_pd((__mmask8)m, mem);
0253 }
0254 else
0255 {
0256 return _mm_maskz_loadu_pd((__mmask8)m, mem);
0257 }
0258 }
0259
0260 template <class T, class Mode, enable_sized_integral_t<T, 4> = 0>
0261 XSIMD_INLINE void maskstore128(T* mem, __m128i src, uint64_t m, Mode) noexcept
0262 {
0263 XSIMD_IF_CONSTEXPR(std::is_same<Mode, aligned_mode>::value)
0264 {
0265 _mm_mask_store_epi32(mem, (__mmask8)m, src);
0266 }
0267 else
0268 {
0269 _mm_mask_storeu_epi32(mem, (__mmask8)m, src);
0270 }
0271 }
0272 template <class T, class Mode, enable_sized_integral_t<T, 8> = 0>
0273 XSIMD_INLINE void maskstore128(T* mem, __m128i src, uint64_t m, Mode) noexcept
0274 {
0275 XSIMD_IF_CONSTEXPR(std::is_same<Mode, aligned_mode>::value)
0276 {
0277 _mm_mask_store_epi64(mem, (__mmask8)m, src);
0278 }
0279 else
0280 {
0281 _mm_mask_storeu_epi64(mem, (__mmask8)m, src);
0282 }
0283 }
0284 template <class Mode>
0285 XSIMD_INLINE void maskstore128(float* mem, __m128 src, uint64_t m, Mode) noexcept
0286 {
0287 XSIMD_IF_CONSTEXPR(std::is_same<Mode, aligned_mode>::value)
0288 {
0289 _mm_mask_store_ps(mem, (__mmask8)m, src);
0290 }
0291 else
0292 {
0293 _mm_mask_storeu_ps(mem, (__mmask8)m, src);
0294 }
0295 }
0296 template <class Mode>
0297 XSIMD_INLINE void maskstore128(double* mem, __m128d src, uint64_t m, Mode) noexcept
0298 {
0299 XSIMD_IF_CONSTEXPR(std::is_same<Mode, aligned_mode>::value)
0300 {
0301 _mm_mask_store_pd(mem, (__mmask8)m, src);
0302 }
0303 else
0304 {
0305 _mm_mask_storeu_pd(mem, (__mmask8)m, src);
0306 }
0307 }
0308 }
0309
0310
0311
0312 template <class A, class T, bool... V, class Mode,
0313 typename>
0314 XSIMD_INLINE batch<T, A> load_masked(T const* mem, batch_bool_constant<T, A, V...> mask, convert<T>, Mode, requires_arch<avx512vl_128>) noexcept
0315 {
0316 XSIMD_IF_CONSTEXPR(detail::lowers_to_plain_moves(mask))
0317 {
0318 return detail::plain_move_load<sse2>(mem, mask, convert<T> {}, Mode {});
0319 }
0320 else
0321 {
0322 return detail::maskload128(mem, mask.mask(), Mode {});
0323 }
0324 }
0325
0326 template <class A, class T, class Mode,
0327 typename>
0328 XSIMD_INLINE batch<T, A> load_masked(T const* mem, batch_bool<T, A> mask, convert<T>, Mode, requires_arch<avx512vl_128>) noexcept
0329 {
0330 return detail::maskload128(mem, mask.mask(), Mode {});
0331 }
0332
0333 template <class A, class T, bool... V, class Mode,
0334 typename>
0335 XSIMD_INLINE void store_masked(T* mem, batch<T, A> const& src, batch_bool_constant<T, A, V...> mask, Mode, requires_arch<avx512vl_128>) noexcept
0336 {
0337 XSIMD_IF_CONSTEXPR(detail::lowers_to_plain_moves(mask))
0338 {
0339 detail::plain_move_store<sse2>(mem, src, mask, Mode {});
0340 }
0341 else
0342 {
0343 detail::maskstore128(mem, src, mask.mask(), Mode {});
0344 }
0345 }
0346
0347 template <class A, class T, class Mode,
0348 typename>
0349 XSIMD_INLINE void store_masked(T* mem, batch<T, A> const& src, batch_bool<T, A> mask, Mode, requires_arch<avx512vl_128>) noexcept
0350 {
0351 detail::maskstore128(mem, src, mask.mask(), Mode {});
0352 }
0353
0354
0355 template <class T, class A, class U, detail::enable_sized_integral_t<T, 4> = 0, detail::enable_sized_integral_t<U, 4> = 0>
0356 XSIMD_INLINE batch<T, A> gather(batch<T, A> const&, T const* src, batch<U, A> const& index,
0357 kernel::requires_arch<avx512vl_128>) noexcept
0358 {
0359 return _mm_mmask_i32gather_epi32(_mm_setzero_si128(), (__mmask8)0xF, index, static_cast<const void*>(src), sizeof(T));
0360 }
0361
0362 template <class T, class A, class U, detail::enable_sized_integral_t<T, 8> = 0, detail::enable_sized_integral_t<U, 8> = 0>
0363 XSIMD_INLINE batch<T, A> gather(batch<T, A> const&, T const* src, batch<U, A> const& index,
0364 kernel::requires_arch<avx512vl_128>) noexcept
0365 {
0366 return _mm_mmask_i64gather_epi64(_mm_setzero_si128(), (__mmask8)0x3, index, static_cast<const void*>(src), sizeof(T));
0367 }
0368
0369 template <class A, class U, detail::enable_sized_integral_t<U, 4> = 0>
0370 XSIMD_INLINE batch<float, A> gather(batch<float, A> const&, float const* src,
0371 batch<U, A> const& index,
0372 kernel::requires_arch<avx512vl_128>) noexcept
0373 {
0374 return _mm_mmask_i32gather_ps(_mm_setzero_ps(), (__mmask8)0xF, index, src, sizeof(float));
0375 }
0376
0377 template <class A, class U, detail::enable_sized_integral_t<U, 8> = 0>
0378 XSIMD_INLINE batch<double, A> gather(batch<double, A> const&, double const* src,
0379 batch<U, A> const& index,
0380 requires_arch<avx512vl_128>) noexcept
0381 {
0382 return _mm_mmask_i64gather_pd(_mm_setzero_pd(), (__mmask8)0x3, index, src, sizeof(double));
0383 }
0384
0385
0386 template <class A, class T,
0387 class = std::enable_if_t<std::is_same<uint32_t, T>::value || std::is_same<int32_t, T>::value>>
0388 XSIMD_INLINE void scatter(batch<T, A> const& src, T* dst,
0389 batch<int32_t, A> const& index,
0390 kernel::requires_arch<avx512vl_128>) noexcept
0391 {
0392 _mm_i32scatter_epi32(dst, index, src, sizeof(T));
0393 }
0394
0395 template <class A, class T,
0396 class = std::enable_if_t<std::is_same<uint64_t, T>::value || std::is_same<int64_t, T>::value>>
0397 XSIMD_INLINE void scatter(batch<T, A> const& src, T* dst,
0398 batch<int64_t, A> const& index,
0399 kernel::requires_arch<avx512vl_128>) noexcept
0400 {
0401 _mm_i64scatter_epi64(dst, index, src, sizeof(T));
0402 }
0403
0404 template <class A>
0405 XSIMD_INLINE void scatter(batch<float, A> const& src, float* dst,
0406 batch<int32_t, A> const& index,
0407 kernel::requires_arch<avx512vl_128>) noexcept
0408 {
0409 _mm_i32scatter_ps(dst, index, src, sizeof(float));
0410 }
0411
0412 template <class A>
0413 XSIMD_INLINE void scatter(batch<double, A> const& src, double* dst,
0414 batch<int64_t, A> const& index,
0415 kernel::requires_arch<avx512vl_128>) noexcept
0416 {
0417 _mm_i64scatter_pd(dst, index, src, sizeof(double));
0418 }
0419
0420
0421 template <class A>
0422 XSIMD_INLINE batch<int64_t, A> max(batch<int64_t, A> const& self, batch<int64_t, A> const& other, requires_arch<avx512vl_128>) noexcept
0423 {
0424 return _mm_max_epi64(self, other);
0425 }
0426 template <class A>
0427 XSIMD_INLINE batch<uint64_t, A> max(batch<uint64_t, A> const& self, batch<uint64_t, A> const& other, requires_arch<avx512vl_128>) noexcept
0428 {
0429 return _mm_max_epu64(self, other);
0430 }
0431
0432
0433 template <class A>
0434 XSIMD_INLINE batch<int64_t, A> min(batch<int64_t, A> const& self, batch<int64_t, A> const& other, requires_arch<avx512vl_128>) noexcept
0435 {
0436 return _mm_min_epi64(self, other);
0437 }
0438 template <class A>
0439 XSIMD_INLINE batch<uint64_t, A> min(batch<uint64_t, A> const& self, batch<uint64_t, A> const& other, requires_arch<avx512vl_128>) noexcept
0440 {
0441 return _mm_min_epu64(self, other);
0442 }
0443
0444
0445 template <class A, size_t I>
0446 XSIMD_INLINE batch<float, A> insert(batch<float, A> const& self, float val, index<I>, requires_arch<avx512vl_128>) noexcept
0447 {
0448
0449 int32_t tmp = bit_cast<int32_t>(val);
0450 return _mm_castsi128_ps(_mm_mask_set1_epi32(_mm_castps_si128(self), __mmask8(1 << (I & 7)), tmp));
0451 }
0452
0453 template <class A, size_t I>
0454 XSIMD_INLINE batch<double, A> insert(batch<double, A> const& self, double val, index<I>, requires_arch<avx512vl_128>) noexcept
0455 {
0456 int64_t tmp = bit_cast<int64_t>(val);
0457 return _mm_castsi128_pd(_mm_mask_set1_epi64(_mm_castpd_si128(self), __mmask8(1 << (I & 3)), tmp));
0458 }
0459
0460 template <class A, class T, size_t I, class = std::enable_if_t<std::is_integral<T>::value>>
0461 XSIMD_INLINE batch<T, A> insert(batch<T, A> const& self, T val, index<I> pos, requires_arch<avx512vl_128>) noexcept
0462 {
0463 XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
0464 {
0465 return _mm_mask_set1_epi32(self, __mmask8(1 << (I & 7)), val);
0466 }
0467 else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
0468 {
0469 return _mm_mask_set1_epi64(self, __mmask8(1 << (I & 3)), val);
0470 }
0471 else
0472 {
0473 return insert(self, val, pos, common {});
0474 }
0475 }
0476
0477
0478 template <class A>
0479 XSIMD_INLINE batch_bool<float, A> isnan(batch<float, A> const& self, requires_arch<avx512vl_128>) noexcept
0480 {
0481 return (typename batch_bool<float, A>::register_type)_mm_cmp_ps_mask(self, self, _CMP_UNORD_Q);
0482 }
0483 template <class A>
0484 XSIMD_INLINE batch_bool<double, A> isnan(batch<double, A> const& self, requires_arch<avx512vl_128>) noexcept
0485 {
0486 return (typename batch_bool<double, A>::register_type)_mm_cmp_pd_mask(self, self, _CMP_UNORD_Q);
0487 }
0488
0489
0490
0491
0492 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0493 XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, int32_t other, requires_arch<avx512vl_128>) noexcept
0494 {
0495 XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
0496 {
0497 return _mm_srai_epi64(self, other);
0498 }
0499 else
0500 {
0501 return bitwise_rshift(self, other, avx2_128 {});
0502 }
0503 }
0504 template <size_t shift, class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0505 XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, requires_arch<avx512vl_128>) noexcept
0506 {
0507 constexpr auto bits = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
0508 static_assert(shift < bits, "Shift amount must be less than the number of bits in T");
0509 XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
0510 {
0511 return _mm_srai_epi64(self, shift);
0512 }
0513 else
0514 {
0515 return bitwise_rshift<shift>(self, avx2_128 {});
0516 }
0517 }
0518 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0519 XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0520 {
0521 XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
0522 {
0523 return _mm_srav_epi64(self, other);
0524 }
0525 else
0526 {
0527 return bitwise_rshift(self, other, avx2_128 {});
0528 }
0529 }
0530
0531
0532 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0533 XSIMD_INLINE batch<T, A> rotl(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0534 {
0535 XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
0536 {
0537 return _mm_rolv_epi32(self, other);
0538 }
0539 else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
0540 {
0541 return _mm_rolv_epi64(self, other);
0542 }
0543 else
0544 {
0545 return rotl(self, other, avx2_128 {});
0546 }
0547 }
0548 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0549 XSIMD_INLINE batch<T, A> rotl(batch<T, A> const& self, int32_t other, requires_arch<avx512vl_128>) noexcept
0550 {
0551 return rotl(self, batch<T, A>(other), A {});
0552 }
0553 template <size_t count, class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0554 XSIMD_INLINE batch<T, A> rotl(batch<T, A> const& self, requires_arch<avx512vl_128>) noexcept
0555 {
0556 constexpr auto bits = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
0557 static_assert(count < bits, "Count must be less than the number of bits in T");
0558 XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
0559 {
0560 return _mm_rol_epi32(self, count);
0561 }
0562 else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
0563 {
0564 return _mm_rol_epi64(self, count);
0565 }
0566 else
0567 {
0568 return rotl<count>(self, avx2_128 {});
0569 }
0570 }
0571
0572
0573 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0574 XSIMD_INLINE batch<T, A> rotr(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0575 {
0576 XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
0577 {
0578 return _mm_rorv_epi32(self, other);
0579 }
0580 else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
0581 {
0582 return _mm_rorv_epi64(self, other);
0583 }
0584 else
0585 {
0586 return rotr(self, other, avx2_128 {});
0587 }
0588 }
0589 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0590 XSIMD_INLINE batch<T, A> rotr(batch<T, A> const& self, int32_t other, requires_arch<avx512vl_128>) noexcept
0591 {
0592 return rotr(self, batch<T, A>(other), A {});
0593 }
0594
0595 template <size_t count, class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0596 XSIMD_INLINE batch<T, A> rotr(batch<T, A> const& self, requires_arch<avx512vl_128>) noexcept
0597 {
0598 constexpr auto bits = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
0599 static_assert(count < bits, "Count must be less than the number of bits in T");
0600 XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
0601 {
0602 return _mm_ror_epi32(self, count);
0603 }
0604 else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
0605 {
0606 return _mm_ror_epi64(self, count);
0607 }
0608 else
0609 {
0610 return rotr<count>(self, avx2_128 {});
0611 }
0612 }
0613
0614
0615
0616 template <class A>
0617 XSIMD_INLINE batch<float, A> compress(batch<float, A> const& self, batch_bool<float, A> const& mask, requires_arch<avx512vl_128>) noexcept
0618 {
0619 return _mm_maskz_compress_ps(mask.mask(), self);
0620 }
0621 template <class A>
0622 XSIMD_INLINE batch<double, A> compress(batch<double, A> const& self, batch_bool<double, A> const& mask, requires_arch<avx512vl_128>) noexcept
0623 {
0624 return _mm_maskz_compress_pd(mask.mask(), self);
0625 }
0626 template <class A>
0627 XSIMD_INLINE batch<int32_t, A> compress(batch<int32_t, A> const& self, batch_bool<int32_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
0628 {
0629 return _mm_maskz_compress_epi32(mask.mask(), self);
0630 }
0631 template <class A>
0632 XSIMD_INLINE batch<uint32_t, A> compress(batch<uint32_t, A> const& self, batch_bool<uint32_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
0633 {
0634 return _mm_maskz_compress_epi32(mask.mask(), self);
0635 }
0636 template <class A>
0637 XSIMD_INLINE batch<int64_t, A> compress(batch<int64_t, A> const& self, batch_bool<int64_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
0638 {
0639 return _mm_maskz_compress_epi64(mask.mask(), self);
0640 }
0641 template <class A>
0642 XSIMD_INLINE batch<uint64_t, A> compress(batch<uint64_t, A> const& self, batch_bool<uint64_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
0643 {
0644 return _mm_maskz_compress_epi64(mask.mask(), self);
0645 }
0646
0647
0648 template <class A>
0649 XSIMD_INLINE batch<float, A> expand(batch<float, A> const& self, batch_bool<float, A> const& mask, requires_arch<avx512vl_128>) noexcept
0650 {
0651 return _mm_maskz_expand_ps(mask.mask(), self);
0652 }
0653 template <class A>
0654 XSIMD_INLINE batch<double, A> expand(batch<double, A> const& self, batch_bool<double, A> const& mask, requires_arch<avx512vl_128>) noexcept
0655 {
0656 return _mm_maskz_expand_pd(mask.mask(), self);
0657 }
0658 template <class A>
0659 XSIMD_INLINE batch<int32_t, A> expand(batch<int32_t, A> const& self, batch_bool<int32_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
0660 {
0661 return _mm_maskz_expand_epi32(mask.mask(), self);
0662 }
0663 template <class A>
0664 XSIMD_INLINE batch<uint32_t, A> expand(batch<uint32_t, A> const& self, batch_bool<uint32_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
0665 {
0666 return _mm_maskz_expand_epi32(mask.mask(), self);
0667 }
0668 template <class A>
0669 XSIMD_INLINE batch<int64_t, A> expand(batch<int64_t, A> const& self, batch_bool<int64_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
0670 {
0671 return _mm_maskz_expand_epi64(mask.mask(), self);
0672 }
0673 template <class A>
0674 XSIMD_INLINE batch<uint64_t, A> expand(batch<uint64_t, A> const& self, batch_bool<uint64_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
0675 {
0676 return _mm_maskz_expand_epi64(mask.mask(), self);
0677 }
0678
0679
0680 template <class A, class T>
0681 XSIMD_INLINE bool all(batch_bool<T, A> const& self, requires_arch<avx512vl_128>) noexcept
0682 {
0683 using register_type = typename batch_bool<T, A>::register_type;
0684 constexpr register_type bitmask = (register_type(1) << batch_bool<T, A>::size) - 1;
0685 return (self.data & bitmask) == bitmask;
0686 }
0687
0688
0689 template <class A, class T>
0690 XSIMD_INLINE bool any(batch_bool<T, A> const& self, requires_arch<avx512vl_128>) noexcept
0691 {
0692 using register_type = typename batch_bool<T, A>::register_type;
0693 constexpr register_type bitmask = (register_type(1) << batch_bool<T, A>::size) - 1;
0694 return (self.data & bitmask) != 0;
0695 }
0696
0697
0698 template <class A>
0699 XSIMD_INLINE batch_bool<float, A> eq(batch<float, A> const& self, batch<float, A> const& other, requires_arch<avx512vl_128>) noexcept
0700 {
0701 return (typename batch_bool<float, A>::register_type)_mm_cmp_ps_mask(self, other, _CMP_EQ_OQ);
0702 }
0703 template <class A>
0704 XSIMD_INLINE batch_bool<double, A> eq(batch<double, A> const& self, batch<double, A> const& other, requires_arch<avx512vl_128>) noexcept
0705 {
0706 return (typename batch_bool<double, A>::register_type)_mm_cmp_pd_mask(self, other, _CMP_EQ_OQ);
0707 }
0708
0709 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0710 XSIMD_INLINE batch_bool<T, A> eq(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0711 {
0712 return detail::compare_int_avx512vl_128<A, T, _MM_CMPINT_EQ>(self, other);
0713 }
0714 template <class A, class T>
0715 XSIMD_INLINE batch_bool<T, A> eq(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0716 {
0717 using register_type = typename batch_bool<T, A>::register_type;
0718 return register_type(~self.data ^ other.data);
0719 }
0720
0721
0722 template <class A>
0723 XSIMD_INLINE batch_bool<float, A> neq(batch<float, A> const& self, batch<float, A> const& other, requires_arch<avx512vl_128>) noexcept
0724 {
0725 return (typename batch_bool<float, A>::register_type)_mm_cmp_ps_mask(self, other, _CMP_NEQ_UQ);
0726 }
0727 template <class A>
0728 XSIMD_INLINE batch_bool<double, A> neq(batch<double, A> const& self, batch<double, A> const& other, requires_arch<avx512vl_128>) noexcept
0729 {
0730 return (typename batch_bool<double, A>::register_type)_mm_cmp_pd_mask(self, other, _CMP_NEQ_UQ);
0731 }
0732
0733 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0734 XSIMD_INLINE batch_bool<T, A> neq(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0735 {
0736 return (~(self == other));
0737 }
0738 template <class A, class T>
0739 XSIMD_INLINE batch_bool<T, A> neq(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0740 {
0741 using register_type = typename batch_bool<T, A>::register_type;
0742 return register_type(self.data ^ other.data);
0743 }
0744
0745
0746 template <class A>
0747 XSIMD_INLINE batch_bool<float, A> gt(batch<float, A> const& self, batch<float, A> const& other, requires_arch<avx512vl_128>) noexcept
0748 {
0749 return (typename batch_bool<float, A>::register_type)_mm_cmp_ps_mask(self, other, _CMP_GT_OQ);
0750 }
0751 template <class A>
0752 XSIMD_INLINE batch_bool<double, A> gt(batch<double, A> const& self, batch<double, A> const& other, requires_arch<avx512vl_128>) noexcept
0753 {
0754 return (typename batch_bool<double, A>::register_type)_mm_cmp_pd_mask(self, other, _CMP_GT_OQ);
0755 }
0756 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0757 XSIMD_INLINE batch_bool<T, A> gt(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0758 {
0759 return detail::compare_int_avx512vl_128<A, T, _MM_CMPINT_GT>(self, other);
0760 }
0761
0762
0763 template <class A>
0764 XSIMD_INLINE batch_bool<float, A> ge(batch<float, A> const& self, batch<float, A> const& other, requires_arch<avx512vl_128>) noexcept
0765 {
0766 return (typename batch_bool<float, A>::register_type)_mm_cmp_ps_mask(self, other, _CMP_GE_OQ);
0767 }
0768 template <class A>
0769 XSIMD_INLINE batch_bool<double, A> ge(batch<double, A> const& self, batch<double, A> const& other, requires_arch<avx512vl_128>) noexcept
0770 {
0771 return (typename batch_bool<double, A>::register_type)_mm_cmp_pd_mask(self, other, _CMP_GE_OQ);
0772 }
0773
0774 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0775 XSIMD_INLINE batch_bool<T, A> ge(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0776 {
0777 return detail::compare_int_avx512vl_128<A, T, _MM_CMPINT_GE>(self, other);
0778 }
0779
0780
0781 template <class A>
0782 XSIMD_INLINE batch_bool<float, A> lt(batch<float, A> const& self, batch<float, A> const& other, requires_arch<avx512vl_128>) noexcept
0783 {
0784 return (typename batch_bool<float, A>::register_type)_mm_cmp_ps_mask(self, other, _CMP_LT_OQ);
0785 }
0786 template <class A>
0787 XSIMD_INLINE batch_bool<double, A> lt(batch<double, A> const& self, batch<double, A> const& other, requires_arch<avx512vl_128>) noexcept
0788 {
0789 return (typename batch_bool<double, A>::register_type)_mm_cmp_pd_mask(self, other, _CMP_LT_OQ);
0790 }
0791
0792 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0793 XSIMD_INLINE batch_bool<T, A> lt(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0794 {
0795 return detail::compare_int_avx512vl_128<A, T, _MM_CMPINT_LT>(self, other);
0796 }
0797
0798
0799 template <class A>
0800 XSIMD_INLINE batch_bool<float, A> le(batch<float, A> const& self, batch<float, A> const& other, requires_arch<avx512vl_128>) noexcept
0801 {
0802 return (typename batch_bool<float, A>::register_type)_mm_cmp_ps_mask(self, other, _CMP_LE_OQ);
0803 }
0804 template <class A>
0805 XSIMD_INLINE batch_bool<double, A> le(batch<double, A> const& self, batch<double, A> const& other, requires_arch<avx512vl_128>) noexcept
0806 {
0807 return (typename batch_bool<double, A>::register_type)_mm_cmp_pd_mask(self, other, _CMP_LE_OQ);
0808 }
0809
0810 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0811 XSIMD_INLINE batch_bool<T, A> le(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0812 {
0813 return detail::compare_int_avx512vl_128<A, T, _MM_CMPINT_LE>(self, other);
0814 }
0815
0816
0817 template <class A>
0818 XSIMD_INLINE batch<float, A> select(batch_bool<float, A> const& cond, batch<float, A> const& true_br, batch<float, A> const& false_br, requires_arch<avx512vl_128>) noexcept
0819 {
0820 return _mm_mask_blend_ps(cond, false_br, true_br);
0821 }
0822 template <class A>
0823 XSIMD_INLINE batch<double, A> select(batch_bool<double, A> const& cond, batch<double, A> const& true_br, batch<double, A> const& false_br, requires_arch<avx512vl_128>) noexcept
0824 {
0825 return _mm_mask_blend_pd(cond, false_br, true_br);
0826 }
0827 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0828 XSIMD_INLINE batch<T, A> select(batch_bool<T, A> const& cond, batch<T, A> const& true_br, batch<T, A> const& false_br, requires_arch<avx512vl_128>) noexcept
0829 {
0830 XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
0831 {
0832 batch_bool<T, avx2_128> batch_cond = batch_bool<T, avx2_128>::from_mask(cond.mask());
0833 return _mm_blendv_epi8(false_br, true_br, batch_cond);
0834 }
0835 else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
0836 {
0837 batch_bool<T, avx2_128> batch_cond = batch_bool<T, avx2_128>::from_mask(cond.mask());
0838 return _mm_blendv_epi8(false_br, true_br, batch_cond);
0839 }
0840 else XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
0841 {
0842 return _mm_mask_blend_epi32(cond, false_br, true_br);
0843 }
0844 else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
0845 {
0846 return _mm_mask_blend_epi64(cond, false_br, true_br);
0847 }
0848 }
0849 template <class A, class T, bool... Values>
0850 XSIMD_INLINE batch<T, A> select(batch_bool_constant<T, A, Values...> const&, batch<T, A> const& true_br, batch<T, A> const& false_br, requires_arch<avx512vl_128>) noexcept
0851 {
0852 return select(batch_bool<T, A> { Values... }, true_br, false_br, avx512vl_128 {});
0853 }
0854
0855
0856
0857
0858
0859
0860 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0861 XSIMD_INLINE batch<T, A> decr_if(batch<T, A> const& self, batch_bool<T, A> const& mask, requires_arch<avx512vl_128>) noexcept
0862 {
0863 return decr_if(self, mask, common {});
0864 }
0865 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0866 XSIMD_INLINE batch<T, A> incr_if(batch<T, A> const& self, batch_bool<T, A> const& mask, requires_arch<avx512vl_128>) noexcept
0867 {
0868 return incr_if(self, mask, common {});
0869 }
0870
0871
0872 template <class A>
0873 XSIMD_INLINE batch<float, A>
0874 reciprocal(batch<float, A> const& self,
0875 kernel::requires_arch<avx512vl_128>) noexcept
0876 {
0877 return _mm_rcp14_ps(self);
0878 }
0879
0880 template <class A>
0881 XSIMD_INLINE batch<double, A>
0882 reciprocal(batch<double, A> const& self,
0883 kernel::requires_arch<avx512vl_128>) noexcept
0884 {
0885 return _mm_rcp14_pd(self);
0886 }
0887
0888
0889 template <class A, class T>
0890 XSIMD_INLINE batch_bool<T, A> bitwise_and(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0891 {
0892 using register_type = typename batch_bool<T, A>::register_type;
0893 return register_type(self.data & other.data);
0894 }
0895
0896
0897 template <class A, class T>
0898 XSIMD_INLINE batch_bool<T, A> bitwise_andnot(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0899 {
0900 using register_type = typename batch_bool<T, A>::register_type;
0901 return register_type(self.data & ~other.data);
0902 }
0903
0904
0905 template <class A, class T>
0906 XSIMD_INLINE batch_bool<T, A> bitwise_not(batch_bool<T, A> const& self, requires_arch<avx512vl_128>) noexcept
0907 {
0908 using register_type = typename batch_bool<T, A>::register_type;
0909 return register_type(~self.data);
0910 }
0911
0912
0913 template <class A, class T>
0914 XSIMD_INLINE batch_bool<T, A> bitwise_or(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0915 {
0916 using register_type = typename batch_bool<T, A>::register_type;
0917 return register_type(self.data | other.data);
0918 }
0919
0920
0921 template <class A, class T>
0922 XSIMD_INLINE batch_bool<T, A> bitwise_xor(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0923 {
0924 using register_type = typename batch_bool<T, A>::register_type;
0925 return register_type(self.data ^ other.data);
0926 }
0927
0928
0929 template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0930 XSIMD_INLINE batch<T, A> sadd(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
0931 {
0932 if (std::is_signed<T>::value)
0933 {
0934 auto mask = other < 0;
0935 auto self_pos_branch = min(std::numeric_limits<T>::max() - other, self);
0936 auto self_neg_branch = max(std::numeric_limits<T>::min() - other, self);
0937 return other + select(mask, self_neg_branch, self_pos_branch);
0938 }
0939 else
0940 {
0941 const auto diffmax = std::numeric_limits<T>::max() - self;
0942 const auto mindiff = min(diffmax, other);
0943 return self + mindiff;
0944 }
0945 }
0946
0947 }
0948 }
0949
0950 #endif