#![allow(clippy::identity_op, unsafe_op_in_unsafe_fn, clippy::missing_safety_doc)]
pub use super::arch::*;
pub mod bits;
pub mod casts;
pub mod cmp;
pub mod math;
pub use bits::*;
pub use casts::*;
pub use cmp::*;
pub use math::*;
pub use crate::backend::generic::polyfills::*;
pub use crate::backend::prefetch::{HAS_PREFETCH, prefetch};
#[inline(always)]
pub const fn identity<T>(value: T) -> T {
value
}
macro_rules! decl_const_ctors {
($($name:ident: [$e:ty; $n:literal] => $ty:ty),* $(,)?) => {$(
#[inline(always)]
pub const fn $name(values: [$e; $n]) -> $ty {
unsafe { crate::generic_array::const_transmute(values) }
}
)*};
}
decl_const_ctors! {
cu8x16: [u8; 16] => uint8x16_t,
cu16x8: [u16; 8] => uint16x8_t,
cu32x4: [u32; 4] => uint32x4_t,
cu64x2: [u64; 2] => uint64x2_t,
}
#[inline(always)]
pub const fn neon_lane_table<const N: usize>(elem_size: usize, idxs: [u32; N]) -> uint8x16_t {
let mut out = [0xFFu8; 16];
let mut i = 0;
while i < N {
let base = idxs[i] as usize * elem_size;
let mut b = 0;
while b < elem_size {
out[i * elem_size + b] = if base + b > 255 { 0xFF } else { (base + b) as u8 };
b += 1;
}
i += 1;
}
cu8x16(out)
}
const fn lane_expand_pattern(elem: usize) -> [u8; 16] {
let mut o = [0u8; 16];
let mut j = 0;
while j < 16 {
o[j] = (j / elem) as u8;
j += 1;
}
o
}
const fn lane_offset_pattern(elem: usize) -> [u8; 16] {
let mut o = [0u8; 16];
let mut j = 0;
while j < 16 {
o[j] = (j % elem) as u8;
j += 1;
}
o
}
#[inline(always)]
pub unsafe fn neon_lane_table_dyn<const N: usize, const AVAIL: usize>(idxs: [u32; N]) -> uint8x16_t {
unsafe {
if const { !(N == 2 || N == 4 || N == 8 || N == 16) } {
return neon_lane_table::<N>(16 / N, idxs);
}
let p = idxs.as_ptr();
let bytes: uint8x16_t = if const { N == 16 } {
let lo = vqmovn_u16(vcombine_u16(vqmovn_u32(vld1q_u32(p)), vqmovn_u32(vld1q_u32(p.add(4)))));
let hi = vqmovn_u16(vcombine_u16(
vqmovn_u32(vld1q_u32(p.add(8))),
vqmovn_u32(vld1q_u32(p.add(12))),
));
vcombine_u8(lo, hi)
} else if const { N == 8 } {
let b = vqmovn_u16(vcombine_u16(vqmovn_u32(vld1q_u32(p)), vqmovn_u32(vld1q_u32(p.add(4)))));
vcombine_u8(b, b)
} else if const { N == 4 } {
let b = vqmovn_u16(vcombine_u16(vqmovn_u32(vld1q_u32(p)), vdup_n_u16(0)));
vcombine_u8(b, b)
} else {
let v = vcombine_u32(vld1_u32(p), vdup_n_u32(0));
let b = vqmovn_u16(vcombine_u16(vqmovn_u32(v), vdup_n_u16(0)));
vcombine_u8(b, b)
};
let clamped = vminq_u8(bytes, vdupq_n_u8(const { AVAIL as u8 }));
if const { N == 16 } {
return clamped;
}
neon_lane_expand::<N>(clamped)
}
}
#[inline(always)]
unsafe fn neon_lane_expand<const N: usize>(clamped: uint8x16_t) -> uint8x16_t {
unsafe {
if const { N == 16 } {
return clamped;
}
let expanded = vqtbl1q_u8(clamped, const { cu8x16(lane_expand_pattern(16 / N)) });
let scaled = match const { 16 / N } {
2 => vshlq_n_u8::<1>(expanded),
4 => vshlq_n_u8::<2>(expanded),
_ => vshlq_n_u8::<3>(expanded),
};
vaddq_u8(scaled, const { cu8x16(lane_offset_pattern(16 / N)) })
}
}
#[inline(always)]
pub unsafe fn neon_lane_table_row<const N: usize>(row: *const u8) -> uint8x16_t {
unsafe {
let b = vld1_u8(row);
neon_lane_expand::<N>(vcombine_u8(b, b))
}
}
#[inline(always)]
pub const fn neon_imm8x4_to_table<const IMM8: i32>() -> uint8x16_t {
neon_lane_table::<4>(
4,
[
((IMM8 >> 0) & 0b11) as u32,
((IMM8 >> 2) & 0b11) as u32,
((IMM8 >> 4) & 0b11) as u32,
((IMM8 >> 6) & 0b11) as u32,
],
)
}
#[inline(always)]
pub const fn neon_imm8x2_to_table<const IMM8: i32>() -> uint8x16_t {
neon_lane_table::<2>(8, [((IMM8 >> 0) & 0b1) as u32, ((IMM8 >> 1) & 0b1) as u32])
}
const fn imm_bit(imm8: i32, b: i32) -> u64 {
if (imm8 >> b) & 1 != 0 { !0 } else { 0 }
}
#[inline(always)]
pub const fn neon_imm8x4_to_mask<const IMM8: i32>() -> uint32x4_t {
cu32x4([
imm_bit(IMM8, 0) as u32,
imm_bit(IMM8, 1) as u32,
imm_bit(IMM8, 2) as u32,
imm_bit(IMM8, 3) as u32,
])
}
#[inline(always)]
pub const fn neon_imm8x2_to_mask<const IMM8: i32>() -> uint64x2_t {
cu64x2([imm_bit(IMM8, 0), imm_bit(IMM8, 1)])
}