#![allow(unsafe_op_in_unsafe_fn, clippy::missing_safety_doc)]
use crate::math::common::AlignedVec;
use crate::math::common::hsum_avx2;
use core::arch::x86_64::*;
#[derive(Clone)]
pub struct A2HeadConv {
pub head_w: AlignedVec<f32>,
pub head_b: f32,
pub head_scale: f32,
pub num_channels: usize,
pub kernel_size: usize,
}
impl A2HeadConv {
pub const HEAD_KERNEL_SIZE: usize = 16;
pub fn new(head_w: AlignedVec<f32>, head_b: f32, head_scale: f32, num_channels: usize) -> Self {
let k = Self::HEAD_KERNEL_SIZE;
assert_eq!(
head_w.len(),
k * num_channels,
"head_w must have HEAD_KERNEL_SIZE * num_channels elements"
);
Self {
head_w,
head_b,
head_scale,
num_channels,
kernel_size: k,
}
}
#[inline(always)]
pub fn process(
&self,
head_history: &[f32],
head_write_pos: usize,
ring_mask: usize,
num_frames: usize,
output: &mut [f32],
) {
debug_assert!(output.len() >= num_frames);
debug_assert!(head_history.len() >= (ring_mask + 1) * self.num_channels);
match self.num_channels {
8 => {
unsafe {
head_process_ch8_avx2(
&self.head_w,
self.head_b,
self.head_scale,
head_history,
head_write_pos,
ring_mask,
num_frames,
output,
);
}
return;
}
3 => {
unsafe {
head_process_ch3_sse(
&self.head_w,
self.head_b,
self.head_scale,
head_history,
head_write_pos,
ring_mask,
num_frames,
output,
);
}
return;
}
_ => {}
}
let k = self.kernel_size;
let ch = self.num_channels;
let base = head_write_pos.wrapping_sub(num_frames);
for (f, out_val) in output.iter_mut().take(num_frames).enumerate() {
let col_base = base.wrapping_add(f);
let mut y = self.head_b;
for t in 0..k {
let col = col_base.wrapping_sub(k - 1 - t) & ring_mask;
let src_off = col * ch;
let w_off = t * ch;
for c in 0..ch {
unsafe {
y += *self.head_w.get_unchecked(w_off + c)
* *head_history.get_unchecked(src_off + c);
}
}
}
*out_val = y * self.head_scale;
}
}
}
#[expect(
clippy::too_many_arguments,
reason = "FFI design or complex DSP kernel signature required by construction"
)]
#[target_feature(enable = "avx2,fma")]
pub unsafe fn head_process_ch8_avx2(
head_w: &[f32],
head_b: f32,
head_scale: f32,
head_history: &[f32],
head_write_pos: usize,
ring_mask: usize,
num_frames: usize,
output: &mut [f32],
) {
let k = A2HeadConv::HEAD_KERNEL_SIZE;
const T: usize = 4;
let n_tiled = (num_frames / T) * T;
let base = head_write_pos.wrapping_sub(num_frames);
for f in (0..n_tiled).step_by(T) {
let mut a0 = _mm256_setzero_ps();
let mut a1 = _mm256_setzero_ps();
let mut a2 = _mm256_setzero_ps();
let mut a3 = _mm256_setzero_ps();
let col_base_f = base.wrapping_add(f);
for t in 0..k {
let w_v = _mm256_loadu_ps(head_w.as_ptr().add(t * 8));
let col0 = col_base_f.wrapping_sub(k - 1 - t) & ring_mask;
let h0 = _mm256_loadu_ps(head_history.as_ptr().add(col0 * 8));
a0 = _mm256_fmadd_ps(w_v, h0, a0);
let col1 = col_base_f.wrapping_add(1).wrapping_sub(k - 1 - t) & ring_mask;
let h1 = _mm256_loadu_ps(head_history.as_ptr().add(col1 * 8));
a1 = _mm256_fmadd_ps(w_v, h1, a1);
let col2 = col_base_f.wrapping_add(2).wrapping_sub(k - 1 - t) & ring_mask;
let h2 = _mm256_loadu_ps(head_history.as_ptr().add(col2 * 8));
a2 = _mm256_fmadd_ps(w_v, h2, a2);
let col3 = col_base_f.wrapping_add(3).wrapping_sub(k - 1 - t) & ring_mask;
let h3 = _mm256_loadu_ps(head_history.as_ptr().add(col3 * 8));
a3 = _mm256_fmadd_ps(w_v, h3, a3);
}
output[f] = (hsum_avx2(a0) + head_b) * head_scale;
output[f + 1] = (hsum_avx2(a1) + head_b) * head_scale;
output[f + 2] = (hsum_avx2(a2) + head_b) * head_scale;
output[f + 3] = (hsum_avx2(a3) + head_b) * head_scale;
}
for (f, out_val) in output.iter_mut().take(num_frames).enumerate().skip(n_tiled) {
*out_val = a2_head_single_frame_scalar_ref(
head_w,
head_b,
head_scale,
8,
head_history,
head_write_pos,
ring_mask,
num_frames,
f,
);
}
}
#[expect(
clippy::too_many_arguments,
reason = "FFI design or complex DSP kernel signature required by construction"
)]
#[target_feature(enable = "fma")]
pub unsafe fn head_process_ch3_sse(
head_w: &[f32],
head_b: f32,
head_scale: f32,
head_history: &[f32],
head_write_pos: usize,
ring_mask: usize,
num_frames: usize,
output: &mut [f32],
) {
let k = A2HeadConv::HEAD_KERNEL_SIZE;
let base = head_write_pos.wrapping_sub(num_frames);
let w_ptr = head_w.as_ptr();
let h_ptr = head_history.as_ptr();
for (f, out_val) in output.iter_mut().take(num_frames).enumerate() {
let mut acc = _mm_setzero_ps();
let col_base = base.wrapping_add(f);
for t in 0..k {
let col = col_base.wrapping_sub(k - 1 - t) & ring_mask;
let src_off = col * 3;
let w_off = t * 3;
let w_v = _mm_setr_ps(
*w_ptr.add(w_off),
*w_ptr.add(w_off + 1),
*w_ptr.add(w_off + 2),
0.0,
);
let h_v = _mm_setr_ps(
*h_ptr.add(src_off),
*h_ptr.add(src_off + 1),
*h_ptr.add(src_off + 2),
0.0,
);
acc = _mm_fmadd_ps(w_v, h_v, acc);
}
let hadd1 = _mm_hadd_ps(acc, acc);
let hadd2 = _mm_hadd_ps(hadd1, hadd1);
let y = _mm_cvtss_f32(hadd2);
*out_val = (y + head_b) * head_scale;
}
}
#[expect(
clippy::too_many_arguments,
reason = "FFI design or complex DSP kernel signature required by construction"
)]
pub fn a2_head_single_frame_scalar_ref(
head_w: &[f32],
head_b: f32,
head_scale: f32,
num_channels: usize,
head_history: &[f32],
head_write_pos: usize,
ring_mask: usize,
num_frames: usize,
frame: usize,
) -> f32 {
let k = A2HeadConv::HEAD_KERNEL_SIZE;
let base = head_write_pos.wrapping_sub(num_frames);
let col_base = base.wrapping_add(frame);
let mut y = head_b;
for t in 0..k {
let col = col_base.wrapping_sub(k - 1 - t) & ring_mask;
let src_off = col * num_channels;
let w_off = t * num_channels;
for c in 0..num_channels {
y += head_w[w_off + c] * head_history[src_off + c];
}
}
y * head_scale
}
#[expect(
clippy::too_many_arguments,
reason = "FFI design or complex DSP kernel signature required by construction"
)]
pub fn a2_head_block_scalar_ref(
head_w: &[f32],
head_b: f32,
head_scale: f32,
num_channels: usize,
head_history: &[f32],
head_write_pos: usize,
ring_mask: usize,
num_frames: usize,
output: &mut [f32],
) {
for (f, out_val) in output.iter_mut().take(num_frames).enumerate() {
*out_val = a2_head_single_frame_scalar_ref(
head_w,
head_b,
head_scale,
num_channels,
head_history,
head_write_pos,
ring_mask,
num_frames,
f,
);
}
}
#[cfg(test)]
#[path = "head_test.rs"]
mod tests;