use fearless_simd::{Neon, i8x16, i16x8, i32x4, prelude::*};
pub(in crate::kernels::prefill) struct Block {
neon: Neon,
weights: [i16x8<Neon>; 16],
}
impl Block {
pub(super) fn new(weights: &[i16; 128]) -> Option<Self> {
let neon = fearless_simd::Level::baseline().as_neon()?;
Some(Self {
neon,
weights: std::array::from_fn(|index| {
i16x8::from_slice(neon, &weights[index * 8..index * 8 + 8])
}),
})
}
#[inline]
fn products16(&self, start: usize, values: &[i8; 16]) -> [i32x4<Neon>; 4] {
let (low, high) = i8x16::from_slice(self.neon, values).widen();
let (a, b) = (self.weights[start] * low).widen();
let (c, d) = (self.weights[start + 1] * high).widen();
[a, b, c, d]
}
#[inline]
pub(super) fn dot128(&self, values: &[i8; 128]) -> i32 {
let mut even = i32x4::splat(self.neon, 0);
let mut odd = i32x4::splat(self.neon, 0);
for (index, values) in values.as_chunks::<16>().0.iter().enumerate() {
let [a, b, c, d] = self.products16(index * 2, values);
even += a + c;
odd += b + d;
}
(even + odd).reduce_sum()
}
#[inline]
pub(super) fn dot32(&self, group: usize, values: &[i8; 32]) -> i32 {
let halves = values.as_chunks::<16>().0;
let low = self.products16(group * 4, &halves[0]);
let high = self.products16(group * 4 + 2, &halves[1]);
((low[0] + low[1]) + (low[2] + low[3]) + (high[0] + high[1]) + (high[2] + high[3]))
.reduce_sum()
}
#[inline]
pub(super) fn lanes32(&self, group: usize, values: &[i8; 32]) -> [i32; 4] {
let halves = values.as_chunks::<16>().0;
let low = self.products16(group * 4, &halves[0]);
let high = self.products16(group * 4 + 2, &halves[1]);
std::array::from_fn(|lane| (low[lane] + high[lane]).reduce_sum())
}
}