use crate::constants::*;
use crate::sse::_mm_move_ss;
use crate::sse2::*;
use crate::types::*;
use core::arch::aarch64::*;
#[inline]
pub fn _mm_blend_epi16<const IMM: i32>(a: __m128i, b: __m128i) -> __m128i {
const { assert!(IMM >= 0 && IMM < 256, "IMM must be in 0..256") };
let av = to_i16_array(a);
let bv = to_i16_array(b);
let mut out = [0i16; 8];
for (i, o) in out.iter_mut().enumerate() {
*o = if (IMM >> i) & 1 == 1 { bv[i] } else { av[i] };
}
__m128i::from_s16(unsafe { vld1q_s16(out.as_ptr()) })
}
#[inline]
pub fn _mm_blend_pd<const IMM: i32>(a: __m128d, b: __m128d) -> __m128d {
const { assert!(IMM >= 0 && IMM < 4, "IMM must be in 0..4") };
unsafe {
let a0 = vgetq_lane_f64(a.f64(), 0);
let a1 = vgetq_lane_f64(a.f64(), 1);
let b0 = vgetq_lane_f64(b.f64(), 0);
let b1 = vgetq_lane_f64(b.f64(), 1);
let e0 = if IMM & 1 == 1 { b0 } else { a0 };
let e1 = if IMM & 2 == 2 { b1 } else { a1 };
_mm_set_pd(e1, e0)
}
}
#[inline]
pub fn _mm_blend_ps<const IMM: i32>(a: __m128, b: __m128) -> __m128 {
const { assert!(IMM >= 0 && IMM < 16, "IMM must be in 0..16") };
let mut av = [0.0f32; 4];
let mut bv = [0.0f32; 4];
unsafe {
vst1q_f32(av.as_mut_ptr(), a.f32());
vst1q_f32(bv.as_mut_ptr(), b.f32());
}
let mut out = [0.0f32; 4];
for (i, o) in out.iter_mut().enumerate() {
*o = if (IMM >> i) & 1 == 1 { bv[i] } else { av[i] };
}
__m128::from_f32(unsafe { vld1q_f32(out.as_ptr()) })
}
#[inline]
pub fn _mm_blendv_epi8(a: __m128i, b: __m128i, mask: __m128i) -> __m128i {
unsafe {
let sel = vcltq_s8(mask.s8(), vdupq_n_s8(0));
__m128i::from_s8(vbslq_s8(sel, b.s8(), a.s8()))
}
}
#[inline]
pub fn _mm_blendv_ps(a: __m128, b: __m128, mask: __m128) -> __m128 {
unsafe {
let sel = vcltq_s32(vreinterpretq_s32_f32(mask.f32()), vdupq_n_s32(0));
__m128::from_f32(vbslq_f32(sel, b.f32(), a.f32()))
}
}
#[inline]
pub fn _mm_blendv_pd(a: __m128d, b: __m128d, mask: __m128d) -> __m128d {
unsafe {
let sel = vcltq_s64(vreinterpretq_s64_f64(mask.f64()), vdupq_n_s64(0));
__m128d::from_f64(vbslq_f64(sel, b.f64(), a.f64()))
}
}
#[inline]
pub fn _mm_round_ps<const ROUNDING: i32>(a: __m128) -> __m128 {
const { assert!(ROUNDING >= 0 && ROUNDING < 16, "ROUNDING must be in 0..16") };
let mode = ROUNDING & !(_MM_FROUND_RAISE_EXC | _MM_FROUND_NO_EXC);
unsafe {
let f = a.f32();
let r = match mode {
_MM_FROUND_TO_NEAREST_INT => vrndnq_f32(f),
_MM_FROUND_TO_NEG_INF => vrndmq_f32(f),
_MM_FROUND_TO_POS_INF => vrndpq_f32(f),
_MM_FROUND_TO_ZERO => vrndq_f32(f),
_ => vrndiq_f32(f),
};
__m128::from_f32(r)
}
}
#[inline]
pub fn _mm_round_pd<const ROUNDING: i32>(a: __m128d) -> __m128d {
const { assert!(ROUNDING >= 0 && ROUNDING < 16, "ROUNDING must be in 0..16") };
let mode = ROUNDING & !(_MM_FROUND_RAISE_EXC | _MM_FROUND_NO_EXC);
unsafe {
let f = a.f64();
let r = match mode {
_MM_FROUND_TO_NEAREST_INT => vrndnq_f64(f),
_MM_FROUND_TO_NEG_INF => vrndmq_f64(f),
_MM_FROUND_TO_POS_INF => vrndpq_f64(f),
_MM_FROUND_TO_ZERO => vrndq_f64(f),
_ => vrndiq_f64(f),
};
__m128d::from_f64(r)
}
}
#[inline]
pub fn _mm_floor_ps(a: __m128) -> __m128 {
_mm_round_ps::<{ _MM_FROUND_TO_NEG_INF }>(a)
}
#[inline]
pub fn _mm_ceil_ps(a: __m128) -> __m128 {
_mm_round_ps::<{ _MM_FROUND_TO_POS_INF }>(a)
}
#[inline]
pub fn _mm_floor_pd(a: __m128d) -> __m128d {
_mm_round_pd::<{ _MM_FROUND_TO_NEG_INF }>(a)
}
#[inline]
pub fn _mm_ceil_pd(a: __m128d) -> __m128d {
_mm_round_pd::<{ _MM_FROUND_TO_POS_INF }>(a)
}
#[inline]
pub fn _mm_round_ss<const ROUNDING: i32>(a: __m128, b: __m128) -> __m128 {
_mm_move_ss(a, _mm_round_ps::<ROUNDING>(b))
}
#[inline]
pub fn _mm_floor_ss(a: __m128, b: __m128) -> __m128 {
_mm_move_ss(a, _mm_floor_ps(b))
}
#[inline]
pub fn _mm_ceil_ss(a: __m128, b: __m128) -> __m128 {
_mm_move_ss(a, _mm_ceil_ps(b))
}
#[inline]
pub fn _mm_round_sd<const ROUNDING: i32>(a: __m128d, b: __m128d) -> __m128d {
_mm_move_sd(a, _mm_round_pd::<ROUNDING>(b))
}
#[inline]
pub fn _mm_floor_sd(a: __m128d, b: __m128d) -> __m128d {
_mm_move_sd(a, _mm_floor_pd(b))
}
#[inline]
pub fn _mm_ceil_sd(a: __m128d, b: __m128d) -> __m128d {
_mm_move_sd(a, _mm_ceil_pd(b))
}
#[inline]
pub fn _mm_cvtepi8_epi16(a: __m128i) -> __m128i {
__m128i::from_s16(unsafe { vmovl_s8(vget_low_s8(a.s8())) })
}
#[inline]
pub fn _mm_cvtepi8_epi32(a: __m128i) -> __m128i {
unsafe {
let w = vmovl_s8(vget_low_s8(a.s8()));
__m128i::from_s32(vmovl_s16(vget_low_s16(w)))
}
}
#[inline]
pub fn _mm_cvtepi16_epi32(a: __m128i) -> __m128i {
__m128i::from_s32(unsafe { vmovl_s16(vget_low_s16(a.s16())) })
}
#[inline]
pub fn _mm_cvtepi32_epi64(a: __m128i) -> __m128i {
__m128i::from_s64(unsafe { vmovl_s32(vget_low_s32(a.s32())) })
}
#[inline]
pub fn _mm_cvtepu8_epi16(a: __m128i) -> __m128i {
__m128i::from_u16(unsafe { vmovl_u8(vget_low_u8(a.u8())) })
}
#[inline]
pub fn _mm_cvtepu8_epi32(a: __m128i) -> __m128i {
unsafe {
let w = vmovl_u8(vget_low_u8(a.u8()));
__m128i::from_u32(vmovl_u16(vget_low_u16(w)))
}
}
#[inline]
pub fn _mm_cvtepu16_epi32(a: __m128i) -> __m128i {
__m128i::from_u32(unsafe { vmovl_u16(vget_low_u16(a.u16())) })
}
#[inline]
pub fn _mm_cvtepu32_epi64(a: __m128i) -> __m128i {
__m128i::from_u64(unsafe { vmovl_u32(vget_low_u32(a.u32())) })
}
#[inline]
pub fn _mm_min_epi8(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_s8(unsafe { vminq_s8(a.s8(), b.s8()) })
}
#[inline]
pub fn _mm_max_epi8(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_s8(unsafe { vmaxq_s8(a.s8(), b.s8()) })
}
#[inline]
pub fn _mm_min_epi32(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_s32(unsafe { vminq_s32(a.s32(), b.s32()) })
}
#[inline]
pub fn _mm_max_epi32(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_s32(unsafe { vmaxq_s32(a.s32(), b.s32()) })
}
#[inline]
pub fn _mm_min_epu16(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_u16(unsafe { vminq_u16(a.u16(), b.u16()) })
}
#[inline]
pub fn _mm_max_epu16(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_u16(unsafe { vmaxq_u16(a.u16(), b.u16()) })
}
#[inline]
pub fn _mm_min_epu32(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_u32(unsafe { vminq_u32(a.u32(), b.u32()) })
}
#[inline]
pub fn _mm_max_epu32(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_u32(unsafe { vmaxq_u32(a.u32(), b.u32()) })
}
#[inline]
pub fn _mm_mullo_epi32(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_s32(unsafe { vmulq_s32(a.s32(), b.s32()) })
}
#[inline]
pub fn _mm_mul_epi32(a: __m128i, b: __m128i) -> __m128i {
unsafe {
let al = vmovn_s64(a.s64());
let bl = vmovn_s64(b.s64());
__m128i::from_s64(vmull_s32(al, bl))
}
}
#[inline]
pub fn _mm_packus_epi32(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_u16(unsafe { vcombine_u16(vqmovun_s32(a.s32()), vqmovun_s32(b.s32())) })
}
#[inline]
pub fn _mm_cmpeq_epi64(a: __m128i, b: __m128i) -> __m128i {
__m128i::from_u64(unsafe { vceqq_s64(a.s64(), b.s64()) })
}
#[inline]
pub fn _mm_minpos_epu16(a: __m128i) -> __m128i {
let mut vals = [0u16; 8];
unsafe { vst1q_u16(vals.as_mut_ptr(), a.u16()) };
let mut min = vals[0];
let mut idx = 0u16;
for (i, &v) in vals.iter().enumerate().skip(1) {
if v < min {
min = v;
idx = i as u16;
}
}
_mm_set_epi16(0, 0, 0, 0, 0, 0, idx as i16, min as i16)
}
#[inline]
pub fn _mm_dp_ps<const IMM: i32>(a: __m128, b: __m128) -> __m128 {
const { assert!(IMM >= 0 && IMM < 256, "IMM must be in 0..256") };
let mut av = [0.0f32; 4];
let mut bv = [0.0f32; 4];
unsafe {
vst1q_f32(av.as_mut_ptr(), a.f32());
vst1q_f32(bv.as_mut_ptr(), b.f32());
}
let mut sum = 0.0f32;
for i in 0..4 {
if (IMM >> (4 + i)) & 1 == 1 {
sum += av[i] * bv[i];
}
}
let mut out = [0.0f32; 4];
for (i, o) in out.iter_mut().enumerate() {
if (IMM >> i) & 1 == 1 {
*o = sum;
}
}
__m128::from_f32(unsafe { vld1q_f32(out.as_ptr()) })
}
#[inline]
pub fn _mm_dp_pd<const IMM: i32>(a: __m128d, b: __m128d) -> __m128d {
const { assert!(IMM >= 0 && IMM < 256, "IMM must be in 0..256") };
unsafe {
let a0 = vgetq_lane_f64(a.f64(), 0);
let a1 = vgetq_lane_f64(a.f64(), 1);
let b0 = vgetq_lane_f64(b.f64(), 0);
let b1 = vgetq_lane_f64(b.f64(), 1);
let mut sum = 0.0f64;
if (IMM >> 4) & 1 == 1 {
sum += a0 * b0;
}
if (IMM >> 5) & 1 == 1 {
sum += a1 * b1;
}
let e0 = if IMM & 1 == 1 { sum } else { 0.0 };
let e1 = if IMM & 2 == 2 { sum } else { 0.0 };
_mm_set_pd(e1, e0)
}
}
#[inline]
pub fn _mm_extract_epi8<const IMM: i32>(a: __m128i) -> i32 {
const { assert!(IMM >= 0 && IMM < 16, "IMM must be in 0..16") };
to_u8_array(a)[(IMM & 0xf) as usize] as i32
}
#[inline]
pub fn _mm_extract_epi32<const IMM: i32>(a: __m128i) -> i32 {
const { assert!(IMM >= 0 && IMM < 4, "IMM must be in 0..4") };
to_i32_array(a)[(IMM & 0x3) as usize]
}
#[inline]
pub fn _mm_extract_epi64<const IMM: i32>(a: __m128i) -> i64 {
const { assert!(IMM >= 0 && IMM < 2, "IMM must be in 0..2") };
let mut vals = [0i64; 2];
unsafe { vst1q_s64(vals.as_mut_ptr(), a.s64()) };
vals[(IMM & 0x1) as usize]
}
#[inline]
pub fn _mm_extract_epi16<const IMM: i32>(a: __m128i) -> i32 {
const { assert!(IMM >= 0 && IMM < 8, "IMM must be in 0..8") };
to_i16_array(a)[(IMM & 0x7) as usize] as u16 as i32
}
#[inline]
pub fn _mm_insert_epi8<const IMM: i32>(a: __m128i, i: i32) -> __m128i {
const { assert!(IMM >= 0 && IMM < 16, "IMM must be in 0..16") };
let mut vals = to_u8_array(a);
vals[(IMM & 0xf) as usize] = i as u8;
__m128i::from_u8(unsafe { vld1q_u8(vals.as_ptr()) })
}
#[inline]
pub fn _mm_insert_epi32<const IMM: i32>(a: __m128i, i: i32) -> __m128i {
const { assert!(IMM >= 0 && IMM < 4, "IMM must be in 0..4") };
let mut vals = to_i32_array(a);
vals[(IMM & 0x3) as usize] = i;
__m128i::from_s32(unsafe { vld1q_s32(vals.as_ptr()) })
}
#[inline]
pub fn _mm_insert_epi64<const IMM: i32>(a: __m128i, i: i64) -> __m128i {
const { assert!(IMM >= 0 && IMM < 2, "IMM must be in 0..2") };
let mut vals = [0i64; 2];
unsafe { vst1q_s64(vals.as_mut_ptr(), a.s64()) };
vals[(IMM & 0x1) as usize] = i;
__m128i::from_s64(unsafe { vld1q_s64(vals.as_ptr()) })
}
#[inline]
pub fn _mm_insert_epi16<const IMM: i32>(a: __m128i, i: i32) -> __m128i {
const { assert!(IMM >= 0 && IMM < 8, "IMM must be in 0..8") };
let mut vals = to_i16_array(a);
vals[(IMM & 0x7) as usize] = i as i16;
__m128i::from_s16(unsafe { vld1q_s16(vals.as_ptr()) })
}
#[inline]
pub fn _mm_testz_si128(a: __m128i, b: __m128i) -> i32 {
let r = _mm_and_si128(a, b);
i32::from(is_all_zero(r))
}
#[inline]
pub fn _mm_testc_si128(a: __m128i, b: __m128i) -> i32 {
let r = _mm_andnot_si128(a, b);
i32::from(is_all_zero(r))
}
#[inline]
pub fn _mm_testnzc_si128(a: __m128i, b: __m128i) -> i32 {
let zf = _mm_testz_si128(a, b);
let cf = _mm_testc_si128(a, b);
i32::from(zf == 0 && cf == 0)
}
#[inline]
pub fn _mm_test_all_ones(a: __m128i) -> i32 {
i32::from(is_all_ones(a))
}
#[inline]
pub fn _mm_test_all_zeros(a: __m128i, mask: __m128i) -> i32 {
_mm_testz_si128(a, mask)
}
#[inline]
pub fn _mm_test_mix_ones_zeros(a: __m128i, mask: __m128i) -> i32 {
_mm_testnzc_si128(a, mask)
}
fn is_all_zero(a: __m128i) -> bool {
unsafe { vmaxvq_u32(a.u32()) == 0 }
}
fn is_all_ones(a: __m128i) -> bool {
unsafe { vminvq_u32(a.u32()) == u32::MAX }
}