use crate::neon::util::NeonStoreF;
use crate::transpose::{Transposition, validate_transpose_buffers};
use std::arch::aarch64::*;
#[inline(always)]
pub(crate) fn vtrnq_f64_to_f32(a0: float32x4_t, a1: float32x4_t) -> float32x4x2_t {
unsafe {
let b0 = vreinterpretq_f32_f64(vtrn1q_f64(
vreinterpretq_f64_f32(a0),
vreinterpretq_f64_f32(a1),
));
let b1 = vreinterpretq_f32_f64(vtrn2q_f64(
vreinterpretq_f64_f32(a0),
vreinterpretq_f64_f32(a1),
));
float32x4x2_t(b0, b1)
}
}
#[inline(always)]
pub(crate) fn transpose_4x4(v0: float32x4x4_t) -> float32x4x4_t {
unsafe {
let b0 = vtrnq_f32(v0.0, v0.1);
let b1 = vtrnq_f32(v0.2, v0.3);
let c0 = vtrnq_f64_to_f32(b0.0, b1.0);
let c1 = vtrnq_f64_to_f32(b0.1, b1.1);
float32x4x4_t(c0.0, c1.0, c0.1, c1.1)
}
}
#[inline(always)]
fn transpose_4x4_f32(store: [NeonStoreF; 4]) -> [NeonStoreF; 4] {
let q = transpose_4x4(float32x4x4_t(
store[0].v, store[1].v, store[2].v, store[3].v,
));
[
NeonStoreF::raw(q.0),
NeonStoreF::raw(q.1),
NeonStoreF::raw(q.2),
NeonStoreF::raw(q.3),
]
}
pub(crate) struct NeonTranspose4x4 {
pub(crate) width: usize,
pub(crate) height: usize,
}
impl NeonTranspose4x4 {
fn transpose_y<const REMAINDER_Y: usize>(&self, src: &[f32], dst: &mut [f32], y: usize) {
const BLOCK_SIZE_X: usize = 4;
let input_y = y;
let input_stride = self.width;
let output_stride = self.height;
let src = unsafe { src.get_unchecked(input_stride * input_y..) };
let mut x = 0usize;
while x + BLOCK_SIZE_X <= self.width {
let output_x = x;
let src = unsafe { src.get_unchecked(x..) };
let dst = unsafe { dst.get_unchecked_mut(y + output_stride * output_x..) };
let mut zbuffer: [NeonStoreF; 4] = std::array::from_fn(|x| {
if x < REMAINDER_Y {
NeonStoreF::load(unsafe { src.get_unchecked(x * input_stride..) })
} else {
NeonStoreF::default()
}
});
zbuffer = transpose_4x4_f32(zbuffer);
for i in 0..4 {
if REMAINDER_Y == 4 {
zbuffer[i].write(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 3 {
zbuffer[i].write3(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 2 {
zbuffer[i].write2(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 1 {
zbuffer[i].write1(unsafe { dst.get_unchecked_mut(output_stride * i..) });
}
}
x += BLOCK_SIZE_X;
}
let rem = self.width - x;
match rem {
1 => {
let output_x = x;
let src = unsafe { src.get_unchecked(x..) };
let dst = unsafe { dst.get_unchecked_mut(y + output_stride * output_x..) };
let mut zbuffer: [NeonStoreF; 4] = std::array::from_fn(|x| {
if x < REMAINDER_Y {
NeonStoreF::load1(unsafe { src.get_unchecked(x * input_stride..) })
} else {
NeonStoreF::default()
}
});
zbuffer = transpose_4x4_f32(zbuffer);
if REMAINDER_Y == 4 {
zbuffer[0].write(dst);
} else if REMAINDER_Y == 3 {
zbuffer[0].write3(dst);
} else if REMAINDER_Y == 2 {
zbuffer[0].write2(dst);
} else if REMAINDER_Y == 1 {
zbuffer[0].write1(dst);
}
}
2 => {
let output_x = x;
let src = unsafe { src.get_unchecked(x..) };
let dst = unsafe { dst.get_unchecked_mut(y + output_stride * output_x..) };
let zbuffer: [NeonStoreF; 4] = std::array::from_fn(|x| {
if x < REMAINDER_Y {
NeonStoreF::load2(unsafe { src.get_unchecked(x * input_stride..) })
} else {
NeonStoreF::default()
}
});
let buffer = transpose_4x4_f32(zbuffer);
for i in 0..2 {
if REMAINDER_Y == 4 {
buffer[i].write(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 3 {
buffer[i].write3(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 2 {
buffer[i].write2(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 1 {
buffer[i].write1(unsafe { dst.get_unchecked_mut(output_stride * i..) });
}
}
}
3 => {
let output_x = x;
let src = unsafe { src.get_unchecked(x..) };
let dst = unsafe { dst.get_unchecked_mut(y + output_stride * output_x..) };
let zbuffer: [NeonStoreF; 4] = std::array::from_fn(|x| {
if x < REMAINDER_Y {
NeonStoreF::load3(unsafe { src.get_unchecked(x * input_stride..) })
} else {
NeonStoreF::default()
}
});
let buffer = transpose_4x4_f32(zbuffer);
for i in 0..3 {
if REMAINDER_Y == 4 {
buffer[i].write(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 3 {
buffer[i].write3(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 2 {
buffer[i].write2(unsafe { dst.get_unchecked_mut(output_stride * i..) });
} else if REMAINDER_Y == 1 {
buffer[i].write1(unsafe { dst.get_unchecked_mut(output_stride * i..) });
}
}
}
_ => {}
}
}
}
impl Transposition<f32> for NeonTranspose4x4 {
fn transpose(&self, src: &[f32], dst: &mut [f32]) {
validate_transpose_buffers(src, dst, self.width, self.height);
const BLOCK_SIZE_Y: usize = 4;
let mut y = 0usize;
while y + BLOCK_SIZE_Y <= self.height {
self.transpose_y::<4>(src, dst, y);
y += BLOCK_SIZE_Y;
}
let rem_y = self.height - y;
if rem_y > 0 {
match rem_y {
1 => self.transpose_y::<1>(src, dst, y),
2 => self.transpose_y::<2>(src, dst, y),
3 => self.transpose_y::<3>(src, dst, y),
_ => {}
}
}
}
}