#![allow(clippy::missing_safety_doc)]
use core::mem;
#[doc(hidden)]
#[macro_export]
macro_rules! apply_dense {
($op:expr, $l1:ident) => {{
DenseLane {
a: $op($l1.a),
b: $op($l1.b),
c: $op($l1.c),
d: $op($l1.d),
e: $op($l1.e),
f: $op($l1.f),
g: $op($l1.g),
h: $op($l1.h),
}
}};
($op:expr, $l1:ident, $l2:ident) => {{
DenseLane {
a: $op($l1.a, $l2.a),
b: $op($l1.b, $l2.b),
c: $op($l1.c, $l2.c),
d: $op($l1.d, $l2.d),
e: $op($l1.e, $l2.e),
f: $op($l1.f, $l2.f),
g: $op($l1.g, $l2.g),
h: $op($l1.h, $l2.h),
}
}};
($op:expr, $l1:ident, $l2:ident, $l3:ident) => {{
DenseLane {
a: $op($l1.a, $l2.a, $l3.a),
b: $op($l1.b, $l2.b, $l3.b),
c: $op($l1.c, $l2.c, $l3.c),
d: $op($l1.d, $l2.d, $l3.d),
e: $op($l1.e, $l2.e, $l3.e),
f: $op($l1.f, $l2.f, $l3.f),
g: $op($l1.g, $l2.g, $l3.g),
h: $op($l1.h, $l2.h, $l3.h),
}
}};
}
#[derive(Copy, Clone)]
pub struct DenseLane<T> {
pub a: T,
pub b: T,
pub c: T,
pub d: T,
pub e: T,
pub f: T,
pub g: T,
pub h: T,
}
impl<T: Copy> DenseLane<T> {
pub const NUM_LANES: usize = 8;
#[inline(always)]
pub fn copy(value: T) -> Self {
Self {
a: value,
b: value,
c: value,
d: value,
e: value,
f: value,
g: value,
h: value,
}
}
}
pub trait SimdRegister<T: Copy> {
type Register: Copy;
#[inline(always)]
fn elements_per_dense() -> usize {
let num_elements_per_lane = Self::elements_per_lane();
num_elements_per_lane * DenseLane::<Self::Register>::NUM_LANES
}
#[inline(always)]
fn elements_per_lane() -> usize {
mem::size_of::<Self::Register>() / mem::size_of::<T>()
}
unsafe fn load(mem: *const T) -> Self::Register;
unsafe fn filled(value: T) -> Self::Register;
unsafe fn zeroed() -> Self::Register;
#[allow(clippy::identity_op)]
#[allow(clippy::erasing_op)]
#[inline(always)]
unsafe fn load_dense(mem: *const T) -> DenseLane<Self::Register> {
DenseLane {
a: Self::load(mem.add(Self::elements_per_lane() * 0)),
b: Self::load(mem.add(Self::elements_per_lane() * 1)),
c: Self::load(mem.add(Self::elements_per_lane() * 2)),
d: Self::load(mem.add(Self::elements_per_lane() * 3)),
e: Self::load(mem.add(Self::elements_per_lane() * 4)),
f: Self::load(mem.add(Self::elements_per_lane() * 5)),
g: Self::load(mem.add(Self::elements_per_lane() * 6)),
h: Self::load(mem.add(Self::elements_per_lane() * 7)),
}
}
#[inline(always)]
unsafe fn filled_dense(value: T) -> DenseLane<Self::Register> {
DenseLane::copy(Self::filled(value))
}
#[inline(always)]
unsafe fn zeroed_dense() -> DenseLane<Self::Register> {
DenseLane::copy(Self::zeroed())
}
unsafe fn add(l1: Self::Register, l2: Self::Register) -> Self::Register;
unsafe fn sub(l1: Self::Register, l2: Self::Register) -> Self::Register;
unsafe fn mul(l1: Self::Register, l2: Self::Register) -> Self::Register;
unsafe fn div(l1: Self::Register, l2: Self::Register) -> Self::Register;
unsafe fn fmadd(
l1: Self::Register,
l2: Self::Register,
acc: Self::Register,
) -> Self::Register;
unsafe fn max(l1: Self::Register, l2: Self::Register) -> Self::Register;
unsafe fn min(l1: Self::Register, l2: Self::Register) -> Self::Register;
#[inline(always)]
unsafe fn add_dense(
l1: DenseLane<Self::Register>,
l2: DenseLane<Self::Register>,
) -> DenseLane<Self::Register> {
apply_dense!(Self::add, l1, l2)
}
#[inline(always)]
unsafe fn sub_dense(
l1: DenseLane<Self::Register>,
l2: DenseLane<Self::Register>,
) -> DenseLane<Self::Register> {
apply_dense!(Self::sub, l1, l2)
}
#[inline(always)]
unsafe fn mul_dense(
l1: DenseLane<Self::Register>,
l2: DenseLane<Self::Register>,
) -> DenseLane<Self::Register> {
apply_dense!(Self::mul, l1, l2)
}
#[inline(always)]
unsafe fn div_dense(
l1: DenseLane<Self::Register>,
l2: DenseLane<Self::Register>,
) -> DenseLane<Self::Register> {
apply_dense!(Self::div, l1, l2)
}
#[inline(always)]
unsafe fn fmadd_dense(
l1: DenseLane<Self::Register>,
l2: DenseLane<Self::Register>,
acc: DenseLane<Self::Register>,
) -> DenseLane<Self::Register> {
apply_dense!(Self::fmadd, l1, l2, acc)
}
#[inline(always)]
unsafe fn max_dense(
l1: DenseLane<Self::Register>,
l2: DenseLane<Self::Register>,
) -> DenseLane<Self::Register> {
apply_dense!(Self::max, l1, l2)
}
#[inline(always)]
unsafe fn min_dense(
l1: DenseLane<Self::Register>,
l2: DenseLane<Self::Register>,
) -> DenseLane<Self::Register> {
apply_dense!(Self::min, l1, l2)
}
unsafe fn sum_to_value(reg: Self::Register) -> T;
#[inline(always)]
unsafe fn sum_to_register(lane: DenseLane<Self::Register>) -> Self::Register {
let mut acc1 = Self::add(lane.a, lane.b);
let acc2 = Self::add(lane.c, lane.d);
let mut acc3 = Self::add(lane.e, lane.f);
let acc4 = Self::add(lane.g, lane.h);
acc1 = Self::add(acc1, acc2);
acc3 = Self::add(acc3, acc4);
Self::add(acc1, acc3)
}
unsafe fn max_to_value(reg: Self::Register) -> T;
#[inline(always)]
unsafe fn max_to_register(lane: DenseLane<Self::Register>) -> Self::Register {
let mut acc1 = Self::max(lane.a, lane.b);
let acc2 = Self::max(lane.c, lane.d);
let mut acc3 = Self::max(lane.e, lane.f);
let acc4 = Self::max(lane.g, lane.h);
acc1 = Self::max(acc1, acc2);
acc3 = Self::max(acc3, acc4);
Self::max(acc1, acc3)
}
unsafe fn min_to_value(reg: Self::Register) -> T;
#[inline(always)]
unsafe fn min_to_register(lane: DenseLane<Self::Register>) -> Self::Register {
let mut acc1 = Self::min(lane.a, lane.b);
let acc2 = Self::min(lane.c, lane.d);
let mut acc3 = Self::min(lane.e, lane.f);
let acc4 = Self::min(lane.g, lane.h);
acc1 = Self::min(acc1, acc2);
acc3 = Self::min(acc3, acc4);
Self::min(acc1, acc3)
}
unsafe fn write(mem: *mut T, reg: Self::Register);
#[allow(clippy::identity_op)]
#[allow(clippy::erasing_op)]
#[inline(always)]
unsafe fn write_dense(mem: *mut T, lane: DenseLane<Self::Register>) {
Self::write(mem.add(Self::elements_per_lane() * 0), lane.a);
Self::write(mem.add(Self::elements_per_lane() * 1), lane.b);
Self::write(mem.add(Self::elements_per_lane() * 2), lane.c);
Self::write(mem.add(Self::elements_per_lane() * 3), lane.d);
Self::write(mem.add(Self::elements_per_lane() * 4), lane.e);
Self::write(mem.add(Self::elements_per_lane() * 5), lane.f);
Self::write(mem.add(Self::elements_per_lane() * 6), lane.g);
Self::write(mem.add(Self::elements_per_lane() * 7), lane.h);
}
}