Skip to main content

hermes_simd_core/view/
unary.rs

1use crate::align::Alignment;
2use crate::arch::SimdArch;
3use crate::execution::ExecutionMode;
4use crate::kernel::SimdKernel;
5use crate::ops::UnaryOp;
6use crate::scalar::Scalar;
7use crate::view::{SimdError, SimdView};
8
9impl<'a, T: 'a, Arch: SimdArch + SimdKernel<T>, Align: Alignment, Mode: ExecutionMode, Ref: 'a>
10    SimdView<'a, T, Arch, Align, Mode, Ref>
11where
12    T: Scalar,
13{
14    /// Apply a `UnaryOp<T>` to every element, writing results to `out`.
15    ///
16    /// SIMD-vectorized: processes `floor(len / LANE_COUNT) * LANE_COUNT` elements via
17    /// the hardware path, then applies `Op::apply_scalar` to the tail.
18    ///
19    /// # Errors
20    /// Returns `SimdError::InsufficientOutputLength` if `out.len() < self.len()`.
21    #[inline(always)]
22    pub fn map_unary<Op: UnaryOp<T>>(&self, op: Op, out: &mut [T]) -> Result<(), SimdError> {
23        let data = self.as_slice();
24        let len = data.len();
25        if out.len() < len {
26            return Err(SimdError::InsufficientOutputLength);
27        }
28        let lane_count = Arch::LANE_COUNT;
29        let simd_len = (len / lane_count) * lane_count;
30        let ptr_in = data.as_ptr();
31        let ptr_out = out.as_mut_ptr();
32
33        unsafe {
34            let load = |p: *const T| {
35                if crate::align::is_aligned_for_arch::<Arch, Align>() {
36                    Arch::load_aligned(p)
37                } else {
38                    Arch::load_unaligned(p)
39                }
40            };
41            let store = |p: *mut T, v: Arch::Vector| {
42                // Output alignment matches input when writing into the same AlignedVec;
43                // for cross-buffer writes, the output may differ — use Align to govern both.
44                if crate::align::is_aligned_for_arch::<Arch, Align>()
45                    && (p as usize) % Align::ALIGN_BYTES == 0
46                {
47                    Arch::store_aligned(p, v)
48                } else {
49                    Arch::store_unaligned(p, v)
50                }
51            };
52            for i in (0..simd_len).step_by(lane_count) {
53                let v = load(ptr_in.add(i));
54                let r = op.apply::<Arch>(v);
55                store(ptr_out.add(i), r);
56            }
57        }
58
59        for i in simd_len..len {
60            out[i] = op.apply_scalar(data[i]);
61        }
62
63        Ok(())
64    }
65}
66
67impl<'a, T: 'a, Arch: SimdArch + SimdKernel<T>, Align: Alignment, Mode: ExecutionMode>
68    SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
69where
70    T: Scalar,
71{
72    /// Apply a `UnaryOp<T>` in-place: `self[i] = op(self[i])`.
73    ///
74    /// SIMD-vectorized with load-modify-store per lane group.
75    /// Both load and store respect `Align::IS_ALIGNED`.
76    #[inline(always)]
77    pub fn map_unary_in_place<Op: UnaryOp<T>>(&mut self, op: Op) {
78        let slice = self.as_slice_mut();
79        let len = slice.len();
80        let lane_count = Arch::LANE_COUNT;
81        let simd_len = (len / lane_count) * lane_count;
82        let ptr = slice.as_mut_ptr();
83
84        unsafe {
85            let load = |p: *mut T| {
86                if crate::align::is_aligned_for_arch::<Arch, Align>() {
87                    Arch::load_aligned(p)
88                } else {
89                    Arch::load_unaligned(p)
90                }
91            };
92            let store = |p: *mut T, v: Arch::Vector| {
93                if crate::align::is_aligned_for_arch::<Arch, Align>() {
94                    Arch::store_aligned(p, v)
95                } else {
96                    Arch::store_unaligned(p, v)
97                }
98            };
99            for i in (0..simd_len).step_by(lane_count) {
100                let v = load(ptr.add(i));
101                let r = op.apply::<Arch>(v);
102                store(ptr.add(i), r);
103            }
104        }
105
106        for i in simd_len..len {
107            slice[i] = op.apply_scalar(slice[i]);
108        }
109    }
110}