template <class T, class A>
XSIMD_INLINE void maskstore(int64_t* mem, __m256i mask, __m256i src) noexcept
{
_mm256_maskstore_epi64(reinterpret_cast<longlong*>(mem), mask, src);
}
}
template <class A, class T, bool... Values, class Mode>
XSIMD_INLINE void store_masked(T* mem, batch<T, A> const& src, batch_bool_constant<T, A, Values...> mask, Mode, requires_arch<avx2>) noexcept
{
constexpr size_t lanes_per_half = batch<T, A>::size / 2;
// confined to lower 128-bit half → forward to SSE
XSIMD_IF_CONSTEXPR(mask.countl_zero() >= lanes_per_half)
{
constexpr auto mlo = ::xsimd::detail::lower_half<sse4_2>(mask); constauto lo = detail::lower_half(src);
store_masked<sse4_2>(mem, lo, mlo, Mode {}, sse4_2 {});
} // confined to upper 128-bit half → forward to SSE else XSIMD_IF_CONSTEXPR(mask.countr_zero() >= lanes_per_half)
{
constexpr auto mhi = ::xsimd::detail::upper_half<sse4_2>(mask); constauto hi = detail::upper_half(src);
store_masked<sse4_2>(mem + lanes_per_half, hi, mhi, Mode {}, sse4_2 {});
} else
{
detail::maskstore<T, A>(mem, mask.as_batch(), src);
}
}
template <class A, bool... Values, class Mode>
XSIMD_INLINE void store_masked(uint32_t* mem, batch<uint32_t, A> const& src, batch_bool_constant<uint32_t, A, Values...> mask, Mode, requires_arch<avx2>) noexcept
{ constauto s32 = bitwise_cast<int32_t>(src);
store_masked<A>(reinterpret_cast<int32_t*>(mem), s32, mask, Mode {}, avx2 {});
}
template <class A, bool... Values, class Mode>
XSIMD_INLINE void store_masked(uint64_t* mem, batch<uint64_t, A> const& src, batch_bool_constant<uint64_t, A, Values...>, Mode, requires_arch<avx2>) noexcept
{ constauto s64 = bitwise_cast<int64_t>(src);
store_masked<A>(reinterpret_cast<int64_t*>(mem), s64, batch_bool_constant<int64_t, A, Values...> {}, Mode {}, avx2 {});
}
// load_stream template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value, void>>
XSIMD_INLINE batch<T, A> load_stream(T const* mem, convert<T>, requires_arch<avx2>) noexcept
{ return _mm256_stream_load_si256((__m256i const*)mem);
} template <class A>
XSIMD_INLINE batch<float, A> load_stream(floatconst* mem, convert<float>, requires_arch<avx2>) noexcept
{ return _mm256_castsi256_ps(_mm256_stream_load_si256((__m256i const*)mem));
} template <class A>
XSIMD_INLINE batch<double, A> load_stream(doubleconst* mem, convert<double>, requires_arch<avx2>) noexcept
{ return _mm256_castsi256_pd(_mm256_stream_load_si256((__m256i const*)mem));
}
// bitwise_and template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch<T, A> bitwise_and(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_and_si256(self, other);
} template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch_bool<T, A> bitwise_and(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_and_si256(self, other);
}
// bitwise_andnot template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch<T, A> bitwise_andnot(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_andnot_si256(other, self);
} template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch_bool<T, A> bitwise_andnot(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_andnot_si256(other, self);
}
// bitwise_not template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch<T, A> bitwise_not(batch<T, A> const& self, requires_arch<avx2>) noexcept
{ return _mm256_xor_si256(self, _mm256_set1_epi32(-1));
} template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch_bool<T, A> bitwise_not(batch_bool<T, A> const& self, requires_arch<avx2>) noexcept
{ return _mm256_xor_si256(self, _mm256_set1_epi32(-1));
}
template <size_t shift, class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch<T, A> bitwise_lshift(batch<T, A> const& self, requires_arch<avx2>) noexcept
{
constexpr auto bits = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
static_assert(shift < bits, "Shift must be less than the number of bits in T");
XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
{ // 8-bit left shift via 16-bit shift + mask
__m256i shifted = _mm256_slli_epi16(self, shift); // TODO(C++17): without `if constexpr` we must ensure the compile-time shift does not overflow
constexpr uint8_t mask8 = static_cast<uint8_t>(sizeof(T) == 1 ? (~0u << shift) : 0); const __m256i mask = _mm256_set1_epi8(mask8); return _mm256_and_si256(shifted, mask);
}
XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
{ return _mm256_slli_epi16(self, shift);
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
{ return _mm256_slli_epi32(self, shift);
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
{ return _mm256_slli_epi64(self, shift);
}
}
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch<T, A> bitwise_lshift(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx2>) noexcept
{
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
{ return _mm256_sllv_epi32(self, other);
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
{ return _mm256_sllv_epi64(self, other);
} else
{ return bitwise_lshift(self, other, avx {});
}
}
// bitwise_lshift multiple (constant) specific implementations. // Missing implementations are dispatched to the `batch` overload in xsimd_api. // The 1 byte constant implementation calls the 2 bytes constant version, the 2 bytes // constant version calls into the 4 bytes version which resolves to the dynamic one above. template <class A, class T, T... Vs,
std::enable_if_t<std::is_integral<T>::value && (sizeof(T) <= 2), int> = 0>
XSIMD_INLINE batch<T, A> bitwise_lshift(
batch<T, A> const& self, batch_constant<T, A, Vs...> shifts, requires_arch<avx2> req) noexcept
{ using uint_t = std::make_unsigned_t<T>;
// AVX2 only supports 16-bit shifts with a uniform bitshift value, // otherwise emulate using 32-bit shifts.
XSIMD_IF_CONSTEXPR(utils::all_equals(shifts))
{ return bitwise_lshift<shifts.get(0), A>(self, req);
} return bitwise_cast<T>(
utils::bitwise_lshift_as_twice_larger<uint_t>(
bitwise_cast<uint_t>(self),
batch_constant<uint_t, A, static_cast<uint_t>(Vs)...> {}));
}
// bitwise_or template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch<T, A> bitwise_or(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_or_si256(self, other);
} template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch_bool<T, A> bitwise_or(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_or_si256(self, other);
}
// bitwise_xor template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch<T, A> bitwise_xor(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_xor_si256(self, other);
} template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
XSIMD_INLINE batch<T, A> bitwise_xor(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_xor_si256(self, other);
}
// gather template <class T, class A, class U, detail::enable_sized_integral_t<T, 4> = 0, detail::enable_sized_integral_t<U, 4> = 0>
XSIMD_INLINE batch<T, A> gather(batch<T, A> const&, T const* src, batch<U, A> const& index,
kernel::requires_arch<avx2>) noexcept
{ // scatter for this one is AVX512F+AVX512VL return _mm256_i32gather_epi32(reinterpret_cast<constint*>(src), index, sizeof(T));
}
template <class T, class A, class U, detail::enable_sized_integral_t<T, 8> = 0, detail::enable_sized_integral_t<U, 8> = 0>
XSIMD_INLINE batch<T, A> gather(batch<T, A> const&, T const* src, batch<U, A> const& index,
kernel::requires_arch<avx2>) noexcept
{ // scatter for this one is AVX512F+AVX512VL return _mm256_i64gather_epi64(reinterpret_cast<constlonglongint*>(src), index, sizeof(T));
}
template <class A, class U,
detail::enable_sized_integral_t<U, 4> = 0>
XSIMD_INLINE batch<float, A> gather(batch<float, A> const&, floatconst* src,
batch<U, A> const& index,
kernel::requires_arch<avx2>) noexcept
{ // scatter for this one is AVX512F+AVX512VL return _mm256_i32gather_ps(src, index, sizeof(float));
}
template <class A, class U, detail::enable_sized_integral_t<U, 8> = 0>
XSIMD_INLINE batch<double, A> gather(batch<double, A> const&, doubleconst* src,
batch<U, A> const& index,
requires_arch<avx2>) noexcept
{ // scatter for this one is AVX512F+AVX512VL return _mm256_i64gather_pd(src, index, sizeof(double));
}
// gather: handmade conversions template <class A, class V, detail::enable_sized_integral_t<V, 4> = 0>
XSIMD_INLINE batch<float, A> gather(batch<float, A> const&, doubleconst* src,
batch<V, A> const& index,
requires_arch<avx2>) noexcept
{ const batch<double, A> low(_mm256_i32gather_pd(src, _mm256_castsi256_si128(index.data), sizeof(double))); const batch<double, A> high(_mm256_i32gather_pd(src, _mm256_extractf128_si256(index.data, 1), sizeof(double))); return detail::merge_sse(_mm256_cvtpd_ps(low.data), _mm256_cvtpd_ps(high.data));
}
template <class A, class V, detail::enable_sized_integral_t<V, 4> = 0>
XSIMD_INLINE batch<int32_t, A> gather(batch<int32_t, A> const&, doubleconst* src,
batch<V, A> const& index,
requires_arch<avx2>) noexcept
{ const batch<double, A> low(_mm256_i32gather_pd(src, _mm256_castsi256_si128(index.data), sizeof(double))); const batch<double, A> high(_mm256_i32gather_pd(src, _mm256_extractf128_si256(index.data, 1), sizeof(double))); return detail::merge_sse(_mm256_cvtpd_epi32(low.data), _mm256_cvtpd_epi32(high.data));
}
// mul_hi template <class A>
XSIMD_INLINE batch<int8_t, A> mul_hi(batch<int8_t, A> const& self, batch<int8_t, A> const& other, requires_arch<avx2>) noexcept
{ // Sign-extend bytes to 16-bit (unpack-with-self followed by srai 8 // duplicates the byte then arithmetic-shifts the sign in), do the // 16x16->16 multiply, then take the high byte of each product. // unpacklo/unpackhi and packs are all per-128-bit-lane, so the // round trip preserves byte ordering and no vpermq is needed.
__m256i a_lo = _mm256_srai_epi16(_mm256_unpacklo_epi8(self, self), 8);
__m256i a_hi = _mm256_srai_epi16(_mm256_unpackhi_epi8(self, self), 8);
__m256i b_lo = _mm256_srai_epi16(_mm256_unpacklo_epi8(other, other), 8);
__m256i b_hi = _mm256_srai_epi16(_mm256_unpackhi_epi8(other, other), 8);
__m256i p_lo = _mm256_srai_epi16(_mm256_mullo_epi16(a_lo, b_lo), 8);
__m256i p_hi = _mm256_srai_epi16(_mm256_mullo_epi16(a_hi, b_hi), 8); // results already lie in [-128, 127], so packs is exact (no saturation kicks in). return _mm256_packs_epi16(p_lo, p_hi);
} template <class A>
XSIMD_INLINE batch<uint8_t, A> mul_hi(batch<uint8_t, A> const& self, batch<uint8_t, A> const& other, requires_arch<avx2>) noexcept
{
__m256i zero = _mm256_setzero_si256();
__m256i a_lo = _mm256_unpacklo_epi8(self, zero);
__m256i a_hi = _mm256_unpackhi_epi8(self, zero);
__m256i b_lo = _mm256_unpacklo_epi8(other, zero);
__m256i b_hi = _mm256_unpackhi_epi8(other, zero);
__m256i p_lo = _mm256_srli_epi16(_mm256_mullo_epi16(a_lo, b_lo), 8);
__m256i p_hi = _mm256_srli_epi16(_mm256_mullo_epi16(a_hi, b_hi), 8); return _mm256_packus_epi16(p_lo, p_hi);
} template <class A>
XSIMD_INLINE batch<int16_t, A> mul_hi(batch<int16_t, A> const& self, batch<int16_t, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_mulhi_epi16(self, other);
} template <class A>
XSIMD_INLINE batch<uint16_t, A> mul_hi(batch<uint16_t, A> const& self, batch<uint16_t, A> const& other, requires_arch<avx2>) noexcept
{ return _mm256_mulhi_epu16(self, other);
} template <class A>
XSIMD_INLINE batch<int32_t, A> mul_hi(batch<int32_t, A> const& self, batch<int32_t, A> const& other, requires_arch<avx2>) noexcept
{
__m256i even = _mm256_mul_epi32(self, other);
__m256i odd = _mm256_mul_epi32(_mm256_shuffle_epi32(self, _MM_SHUFFLE(3, 3, 1, 1)),
_mm256_shuffle_epi32(other, _MM_SHUFFLE(3, 3, 1, 1)));
__m256i even_hi = _mm256_srli_epi64(even, 32); return _mm256_blend_epi16(even_hi, odd, 0xCC);
} template <class A>
XSIMD_INLINE batch<uint32_t, A> mul_hi(batch<uint32_t, A> const& self, batch<uint32_t, A> const& other, requires_arch<avx2>) noexcept
{
__m256i even = _mm256_mul_epu32(self, other);
__m256i odd = _mm256_mul_epu32(_mm256_srli_epi64(self, 32), _mm256_srli_epi64(other, 32));
__m256i even_hi = _mm256_srli_epi64(even, 32); return _mm256_blend_epi16(even_hi, odd, 0xCC);
}
template <class A>
XSIMD_INLINE batch<uint64_t, A> mul_hi(batch<uint64_t, A> const& self, batch<uint64_t, A> const& other, requires_arch<avx2>) noexcept
{ return detail::mulhi_u64_core<A>(self, other,
[](batch<uint64_t, A> a, batch<uint64_t, A> b)
{ return batch<uint64_t, A>(_mm256_mul_epu32(a, b)); });
} template <class A>
XSIMD_INLINE batch<int64_t, A> mul_hi(batch<int64_t, A> const& self, batch<int64_t, A> const& other, requires_arch<avx2>) noexcept
{ return detail::mulhi_i64_core<A>(self, other,
[](batch<uint64_t, A> a, batch<uint64_t, A> b)
{ return batch<uint64_t, A>(_mm256_mul_epu32(a, b)); });
}
// rotate_left template <size_t N, class A>
XSIMD_INLINE batch<uint8_t, A> rotate_left(batch<uint8_t, A> const& self, requires_arch<avx2>) noexcept
{ auto other = _mm256_permute2x128_si256(self, self, 0x1); if (N < 16)
{ return _mm256_alignr_epi8(other, self, N);
} else
{ return _mm256_alignr_epi8(self, other, N - 16);
}
} template <size_t N, class A>
XSIMD_INLINE batch<int8_t, A> rotate_left(batch<int8_t, A> const& self, requires_arch<avx2>) noexcept
{ return bitwise_cast<int8_t>(rotate_left<N, A>(bitwise_cast<uint8_t>(self), avx2 {}));
} template <size_t N, class A>
XSIMD_INLINE batch<uint16_t, A> rotate_left(batch<uint16_t, A> const& self, requires_arch<avx2>) noexcept
{ auto other = _mm256_permute2x128_si256(self, self, 0x1); if (N < 8)
{ return _mm256_alignr_epi8(other, self, 2 * N);
} else
{ return _mm256_alignr_epi8(self, other, 2 * (N - 8));
}
} template <size_t N, class A>
XSIMD_INLINE batch<int16_t, A> rotate_left(batch<int16_t, A> const& self, requires_arch<avx2>) noexcept
{ return bitwise_cast<int16_t>(rotate_left<N, A>(bitwise_cast<uint16_t>(self), avx2 {}));
}
// select template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
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<avx2>) noexcept
{
XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
{ return _mm256_blendv_epi8(false_br, true_br, cond);
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
{ return _mm256_blendv_epi8(false_br, true_br, cond);
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
{ return _mm256_blendv_epi8(false_br, true_br, cond);
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
{ return _mm256_blendv_epi8(false_br, true_br, cond);
} else
{ return select(cond, true_br, false_br, avx {});
}
} template <class A, class T, bool... Values, class = std::enable_if_t<std::is_integral<T>::value>>
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<avx2>) noexcept
{ // FIXME: for some reason mask here is not considered as an immediate, // but it's okay for _mm256_blend_epi32 // case 2: return _mm256_blend_epi16(false_br, true_br, mask);
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
{
constexpr int mask = batch_bool_constant<T, A, Values...>::mask(); return _mm256_blend_epi32(false_br, true_br, mask);
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
{
constexpr int mask = batch_bool_constant<T, A, Values...>::mask();
constexpr int imask = detail::interleave(mask); return _mm256_blend_epi32(false_br, true_br, imask);
} else
{ return select(batch_bool<T, A> { Values... }, true_br, false_br, avx2 {});
}
}
// slide_left template <size_t N, class A, class T>
XSIMD_INLINE batch<T, A> slide_left(batch<T, A> const& x, requires_arch<avx2>) noexcept
{
constexpr unsigned BitCount = N * 8; if (BitCount == 0)
{ return x;
} if (BitCount >= 256)
{ return batch<T, A>(T(0));
} if (BitCount > 128)
{
constexpr unsigned M = (BitCount - 128) / 8; auto y = _mm256_bslli_epi128(x, M); return _mm256_permute2x128_si256(y, y, 0x28);
} if (BitCount == 128)
{ return _mm256_permute2x128_si256(x, x, 0x28);
} // shifting by [0, 128[ bits
constexpr unsigned M = BitCount / 8; auto y = _mm256_bslli_epi128(x, M); auto z = _mm256_bsrli_epi128(x, 16 - M); auto w = _mm256_permute2x128_si256(z, z, 0x28); return _mm256_or_si256(y, w);
}
// slide_right template <size_t N, class A, class T>
XSIMD_INLINE batch<T, A> slide_right(batch<T, A> const& x, requires_arch<avx2>) noexcept
{
constexpr unsigned BitCount = N * 8; if (BitCount == 0)
{ return x;
} if (BitCount >= 256)
{ return batch<T, A>(T(0));
} if (BitCount > 128)
{
constexpr unsigned M = (BitCount - 128) / 8; auto y = _mm256_bsrli_epi128(x, M); return _mm256_permute2x128_si256(y, y, 0x81);
} if (BitCount == 128)
{ return _mm256_permute2x128_si256(x, x, 0x81);
} // shifting by [0, 128[ bits
constexpr unsigned M = BitCount / 8; auto y = _mm256_bsrli_epi128(x, M); auto z = _mm256_bslli_epi128(x, 16 - M); auto w = _mm256_permute2x128_si256(z, z, 0x81); return _mm256_or_si256(y, w);
}
// store<batch_bool> namespace detail
{ template <class T>
XSIMD_INLINE void store_bool_avx2(__m256i b, bool* mem, T) noexcept
{ // GCC <12 have missing or buggy unaligned store intrinsics; use memcpy to work around this. // GCC/Clang/MSVC will turn it into the correct store.
XSIMD_IF_CONSTEXPR(sizeof(T) == 1)
{ // negate mask to convert to 0 or 1 auto val = _mm256_sub_epi8(_mm256_set1_epi8(0), b);
memcpy(mem, &val, sizeof(val)); return;
}
auto b_hi = _mm256_extractf128_si256(b, 1); auto b_lo = _mm256_castsi256_si128(b);
XSIMD_IF_CONSTEXPR(sizeof(T) == 2)
{ auto val = _mm_sub_epi8(_mm_set1_epi8(0), _mm_packs_epi16(b_lo, b_hi));
memcpy(mem, &val, sizeof(val));
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
{ auto pack_16 = _mm_packs_epi32(b_lo, b_hi); auto val = _mm_sub_epi8(_mm_set1_epi8(0), _mm_packs_epi16(pack_16, pack_16)); #ifdefined(__x86_64__) auto val_lo = _mm_cvtsi128_si64(val);
memcpy(mem, &val_lo, sizeof(val_lo)); #else
memcpy(mem, &val, sizeof(uint64_t)); #endif
} else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
{
uint32_t mask = _mm256_movemask_epi8(_mm256_srli_epi64(b, 56));
memcpy(mem, &mask, sizeof(mask));
} else
{
assert(false && "unsupported arch/op combination");
}
}
template <class T, class A>
XSIMD_INLINE void store(batch_bool<T, A> b, bool* mem, requires_arch<avx2>) noexcept
{
detail::store_bool_avx2(detail::avx_to_i(b), mem, T {});
}
// permute bytes within each lane (AVX2 only)
__m256i r0 = _mm256_shuffle_epi8(self, half_mask);
__m256i r1 = _mm256_shuffle_epi8(swapped, half_mask);
// select lane by the mask index divided by 16, first lane is 0, second is 16.
constexpr auto lane_size = make_batch_constant<uint8_t, 16, A>();
constexpr auto lane = (make_iota_batch_constant<uint8_t, A>() / lane_size) * lane_size;
batch_bool<uint8_t, A> blend_mask = (mask & 0b10000u) != lane; return _mm256_blendv_epi8(r0, r1, blend_mask);
}
template <class A, typename T, detail::enable_sized_t<T, 1> = 0>
XSIMD_INLINE batch<T, A> swizzle(batch<T, A> const& self, batch<uint8_t, A> const& mask, requires_arch<avx2> req) noexcept
{ return bitwise_cast<T>(swizzle(bitwise_cast<uint8_t>(self), mask, req));
}
template <class A>
XSIMD_INLINE batch<uint16_t, A> swizzle(
batch<uint16_t, A> const& self, batch<uint16_t, A> mask, requires_arch<avx2> req) noexcept
{ // No blend/shuffle for 16 bits, we need to use the 8 bits version constauto self_bytes = bitwise_cast<uint8_t>(self); // If a mask entry is k, we want 2k in low byte and 2k+1 in high byte constauto mask_2k_2kp1 = bitwise_cast<uint8_t>((mask << 1) | (mask << 9) | 0x100); return bitwise_cast<uint16_t>(swizzle(self_bytes, mask_2k_2kp1, req));
}
template <class A, typename T, detail::enable_sized_t<T, 2> = 0>
XSIMD_INLINE batch<T, A> swizzle(batch<T, A> const& self, batch<uint16_t, A> const& mask, requires_arch<avx2> req) noexcept
{ return bitwise_cast<T>(swizzle(bitwise_cast<uint16_t>(self), mask, req));
}
static constexpr T get(std::size_t idx_, std::size_t size_) noexcept
{ const T size = static_cast<T>(size_); const T idx = static_cast<T>(idx_); const T val = values[idx_];
// Check if value in bounds if ((T(0) <= val) && (val < size))
{ // Whether we need to access the value from the other lane constbool val_is_cross_lane = (idx < (size / 2)) != (val < (size / 2)); if (val_is_cross_lane == cross_batch)
{ return val % (size / 2);
}
}
// Out of bounds with most significant bit set to 1 will set the swizzle target to 0 return ~T {};
}
};
}
// swizzle (constant mask) template <class A, uint8_t... Vals>
XSIMD_INLINE batch<uint8_t, A> swizzle(batch<uint8_t, A> const& self, batch_constant<uint8_t, A, Vals...> mask, requires_arch<avx2>) noexcept
{
static_assert(sizeof...(Vals) == 32, "Must contain as many uint8_t as can fit in avx register");
// We can outsmart the dynamic version by creating a compile-time mask that leaves zeros // where it does not need to select data, resulting in a simple OR merge of the two batches.
constexpr auto self_mask = make_batch_constant<uint8_t, detail::swizzle_mask<false, uint8_t, Vals...>, A>();
constexpr auto cross_mask = make_batch_constant<uint8_t, detail::swizzle_mask<true, uint8_t, Vals...>, A>();
// permute bytes within each lane (AVX2 only)
__m256i r0 = _mm256_shuffle_epi8(self, self_mask.as_batch());
__m256i r1 = _mm256_shuffle_epi8(swapped, cross_mask.as_batch());
return _mm256_or_si256(r0, r1);
}
template <class A, typename T, uint8_t... Vals, detail::enable_sized_t<T, 1> = 0>
XSIMD_INLINE batch<T, A> swizzle(batch<T, A> const& self, batch_constant<uint8_t, A, Vals...> const& mask, requires_arch<avx2> req) noexcept
{
static_assert(sizeof...(Vals) == 32, "Must contain as many uint8_t as can fit in avx register"); return bitwise_cast<T>(swizzle(bitwise_cast<uint8_t>(self), mask, req));
}
template <class A, typename T, uint16_t... Vals, detail::enable_sized_t<T, 2> = 0>
XSIMD_INLINE batch<T, A> swizzle(batch<T, A> const& self, batch_constant<uint16_t, A, Vals...> const& mask, requires_arch<avx2> req) noexcept
{
static_assert(sizeof...(Vals) == 16, "Must contain as many uint16_t as can fit in avx register"); return bitwise_cast<T>(swizzle(bitwise_cast<uint16_t>(self), mask, req));
}
template <class A, uint32_t V0, uint32_t V1, uint32_t V2, uint32_t V3, uint32_t V4, uint32_t V5, uint32_t V6, uint32_t V7>
XSIMD_INLINE batch<uint32_t, A> swizzle(batch<uint32_t, A> const& self, batch_constant<uint32_t, A, V0, V1, V2, V3, V4, V5, V6, V7> mask, requires_arch<avx2>) noexcept
{
XSIMD_IF_CONSTEXPR(detail::is_identity(mask))
{ return self;
}
XSIMD_IF_CONSTEXPR(!detail::is_cross_lane(mask))
{
constexpr auto lane_mask = mask % std::integral_constant<uint32_t, (mask.size / 2)>(); // Cheaper intrinsics when not crossing lanes // Contrary to the uint64_t version, the limits of 8 bits for the immediate constant // cannot make different permutations across lanes
batch<float, A> permuted = _mm256_permutevar_ps(bitwise_cast<float>(self), lane_mask.as_batch()); return bitwise_cast<uint32_t>(permuted);
} return _mm256_permutevar8x32_epi32(self, mask.as_batch());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.