#[target_feature(enable = "neon")]
pub(super) unsafe fn dot_f32_neon(q: &[f32], row_bytes: &[u8]) -> f32 {
unsafe {
use std::arch::aarch64::*;
let dim = q.len();
let row_ptr = row_bytes.as_ptr() as *const f32;
let mut acc = vdupq_n_f32(0.0);
let chunks = dim / 4;
for i in 0..chunks {
let qv = vld1q_f32(q.as_ptr().add(i * 4));
let rv = vld1q_f32(row_ptr.add(i * 4));
acc = vfmaq_f32(acc, qv, rv);
}
let mut tail = 0.0f32;
for (i, &qv) in q.iter().enumerate().skip(chunks * 4) {
let off = i * 4;
let v = f32::from_le_bytes([
row_bytes[off],
row_bytes[off + 1],
row_bytes[off + 2],
row_bytes[off + 3],
]);
tail += qv * v;
}
let lane_sum = vaddvq_f32(acc);
lane_sum + tail
}
}
#[cfg(neon_f16)]
#[clippy::msrv = "1.94"]
#[target_feature(enable = "neon")]
pub(super) unsafe fn dot_f32_f16_neon(q: &[f32], row_bytes: &[u8]) -> f32 {
unsafe {
use std::arch::aarch64::*;
let dim = q.len();
let row_ptr = row_bytes.as_ptr() as *const u16;
let mut acc = vdupq_n_f32(0.0);
let chunks = dim / 4;
for i in 0..chunks {
let halfs = vld1_u16(row_ptr.add(i * 4));
let f16x4: float16x4_t = std::mem::transmute(halfs);
let widened: float32x4_t = vcvt_f32_f16(f16x4);
let qv = vld1q_f32(q.as_ptr().add(i * 4));
acc = vfmaq_f32(acc, qv, widened);
}
let mut tail = 0.0f32;
for (i, &qv) in q.iter().enumerate().skip(chunks * 4) {
let off = i * 2;
let h = half::f16::from_le_bytes([row_bytes[off], row_bytes[off + 1]]);
tail += qv * h.to_f32();
}
let lane_sum = vaddvq_f32(acc);
lane_sum + tail
}
}
#[target_feature(enable = "neon")]
pub(super) unsafe fn dot_f32_i4_neon(
q: &[f32],
codes: &[u8],
group_scales: &[f32],
dim: usize,
block: usize,
scratch: &mut [f32],
) -> f32 {
unsafe {
use std::arch::aarch64::*;
let nbytes = dim / 2;
let chunks = nbytes / 16;
let lo_mask = vdupq_n_s8(0x0F);
for c in 0..chunks {
let packed = vld1q_u8(codes.as_ptr().add(c * 16));
let packed_s = vreinterpretq_s8_u8(packed);
let lo = vshrq_n_s8::<4>(vshlq_n_s8::<4>(vandq_s8(packed_s, lo_mask)));
let hi = vshrq_n_s8::<4>(packed_s);
let zipped = vzipq_s8(lo, hi); store_s8x16_as_f32(zipped.0, &mut scratch[c * 32..]);
store_s8x16_as_f32(zipped.1, &mut scratch[c * 32 + 16..]);
}
for idx in (chunks * 32)..dim {
let byte = codes[idx / 2];
let nib = if idx % 2 == 0 { byte } else { byte >> 4 };
let n = nib & 0x0F;
let s = if n & 0x08 != 0 {
(n | 0xF0) as i8
} else {
n as i8
};
scratch[idx] = s as f32;
}
dot_scratch_blocked(q, scratch, group_scales, dim, block)
}
}
#[target_feature(enable = "neon")]
unsafe fn store_s8x16_as_f32(v: std::arch::aarch64::int8x16_t, out: &mut [f32]) {
assert!(out.len() >= 16, "store_s8x16_as_f32: need 16 lanes");
unsafe {
use std::arch::aarch64::*;
let lo16 = vmovl_s8(vget_low_s8(v));
let hi16 = vmovl_s8(vget_high_s8(v));
let q0 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(lo16)));
let q1 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(lo16)));
let q2 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(hi16)));
let q3 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(hi16)));
vst1q_f32(out.as_mut_ptr(), q0);
vst1q_f32(out.as_mut_ptr().add(4), q1);
vst1q_f32(out.as_mut_ptr().add(8), q2);
vst1q_f32(out.as_mut_ptr().add(12), q3);
}
}
#[inline]
fn dot_scratch_blocked(
q: &[f32],
scratch: &[f32],
group_scales: &[f32],
dim: usize,
block: usize,
) -> f32 {
let mut acc = 0.0f32;
let _ = dim;
for (g, &scale) in group_scales.iter().enumerate() {
let mut part = 0.0f32;
let base = g * block;
for j in 0..block {
part += q[base + j] * scratch[base + j];
}
acc += part * scale;
}
acc
}
#[target_feature(enable = "neon")]
pub(super) unsafe fn dot_f32_i8_neon(q: &[f32], row: &[i8]) -> f32 {
unsafe {
use std::arch::aarch64::*;
let dim = q.len();
let mut acc = vdupq_n_f32(0.0);
let chunks = dim / 8;
for i in 0..chunks {
let i8x8 = vld1_s8(row.as_ptr().add(i * 8));
let i16x8 = vmovl_s8(i8x8);
let i32_lo = vmovl_s16(vget_low_s16(i16x8));
let i32_hi = vmovl_s16(vget_high_s16(i16x8));
let f_lo = vcvtq_f32_s32(i32_lo);
let f_hi = vcvtq_f32_s32(i32_hi);
let q_lo = vld1q_f32(q.as_ptr().add(i * 8));
let q_hi = vld1q_f32(q.as_ptr().add(i * 8 + 4));
acc = vfmaq_f32(acc, q_lo, f_lo);
acc = vfmaq_f32(acc, q_hi, f_hi);
}
let mut tail = 0.0f32;
for i in (chunks * 8)..dim {
tail += q[i] * (row[i] as f32);
}
vaddvq_f32(acc) + tail
}
}