#![allow(unsafe_code)]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn apply(c1: &[f64], c2: &[f64], x: &[f64], y: &[f64], out: &mut [f64]) {
debug_assert!(
c1.len() == 4 && c2.len() == 4 && x.len() == 4 && y.len() == 4 && out.len() == 4,
"coef_merge_f64x4::apply requires exactly 4-element slices \
(got c1={}, c2={}, x={}, y={}, out={})",
c1.len(),
c2.len(),
x.len(),
y.len(),
out.len()
);
if c1.len() != 4 || c2.len() != 4 || x.len() != 4 || y.len() != 4 || out.len() != 4 {
return;
}
#[cfg(target_arch = "x86_64")]
{
if std::is_x86_feature_detected!("avx2") {
unsafe {
use core::arch::asm;
asm!(
"vmovupd ymm0, ymmword ptr [{c1}]",
"vmovupd ymm1, ymmword ptr [{c2}]",
"vmovupd ymm2, ymmword ptr [{x}]",
"vmovupd ymm3, ymmword ptr [{y}]",
"vmulpd ymm0, ymm0, ymm1", "vmulpd ymm2, ymm2, ymm3", "vmulpd ymm0, ymm0, ymm2", "vmovupd ymmword ptr [{out}], ymm0",
c1 = in(reg) c1.as_ptr(),
c2 = in(reg) c2.as_ptr(),
x = in(reg) x.as_ptr(),
y = in(reg) y.as_ptr(),
out = in(reg) out.as_mut_ptr(),
out("ymm0") _,
out("ymm1") _,
out("ymm2") _,
out("ymm3") _,
options(nostack, preserves_flags),
);
}
return;
}
}
#[cfg(target_arch = "aarch64")]
{
unsafe {
use core::arch::asm;
asm!(
"ld1 {{v0.2d}}, [{c1}], #16",
"ld1 {{v1.2d}}, [{c2}], #16",
"ld1 {{v2.2d}}, [{xs}], #16",
"ld1 {{v3.2d}}, [{ys}], #16",
"ld1 {{v4.2d}}, [{c1}]",
"ld1 {{v5.2d}}, [{c2}]",
"ld1 {{v6.2d}}, [{xs}]",
"ld1 {{v7.2d}}, [{ys}]",
"fmul v0.2d, v0.2d, v1.2d",
"fmul v2.2d, v2.2d, v3.2d",
"fmul v4.2d, v4.2d, v5.2d",
"fmul v6.2d, v6.2d, v7.2d",
"fmul v0.2d, v0.2d, v2.2d",
"fmul v4.2d, v4.2d, v6.2d",
"st1 {{v0.2d}}, [{out}], #16",
"st1 {{v4.2d}}, [{out}]",
c1 = inout(reg) c1.as_ptr() => _,
c2 = inout(reg) c2.as_ptr() => _,
xs = inout(reg) x.as_ptr() => _,
ys = inout(reg) y.as_ptr() => _,
out = inout(reg) out.as_mut_ptr() => _,
out("v0") _, out("v1") _, out("v2") _, out("v3") _,
out("v4") _, out("v5") _, out("v6") _, out("v7") _,
options(nostack, preserves_flags),
);
}
return;
}
#[cfg(all(target_arch = "riscv64", target_feature = "v"))]
{
unsafe {
use core::arch::asm;
asm!(
"li t0, 4",
"vsetvli t0, t0, e64, m1, ta, ma",
"vle64.v v0, ({c1})",
"vle64.v v1, ({c2})",
"vle64.v v2, ({xs})",
"vle64.v v3, ({ys})",
"vfmul.vv v0, v0, v1",
"vfmul.vv v2, v2, v3",
"vfmul.vv v0, v0, v2",
"vse64.v v0, ({out})",
c1 = in(reg) c1.as_ptr(),
c2 = in(reg) c2.as_ptr(),
xs = in(reg) x.as_ptr(),
ys = in(reg) y.as_ptr(),
out = in(reg) out.as_mut_ptr(),
out("t0") _,
out("v0") _,
out("v1") _,
out("v2") _,
out("v3") _,
options(nostack),
);
}
return;
}
for i in 0..4 {
out[i] = (c1[i] * c2[i]) * (x[i] * y[i]);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn merge_matches_naive() {
let c1 = [2.0_f64, 3.0, 4.0, 5.0];
let c2 = [1.5_f64, 2.5, 3.5, 4.5];
let x = [10.0_f64, 20.0, 30.0, 40.0];
let y = [0.5_f64, 1.0, 1.5, 2.0];
let mut out = [0.0_f64; 4];
apply(&c1, &c2, &x, &y, &mut out);
for i in 0..4 {
let expected = (c1[i] * c2[i]) * (x[i] * y[i]);
assert!((out[i] - expected).abs() < 1e-12);
}
}
}