1use std::mem::MaybeUninit;
2
3use crate::Isa;
4use crate::functional::simd_map;
5use crate::ops::{GetNumOps, GetSimd};
6use crate::span::SrcDest;
7
8pub trait SimdOp {
11 type Output;
13
14 fn eval<I: Isa>(self, isa: I) -> Self::Output;
16
17 fn dispatch(self) -> Self::Output
19 where
20 Self: Sized,
21 {
22 dispatch(self)
23 }
24}
25
26pub fn dispatch<Op: SimdOp>(op: Op) -> Op::Output {
31 #[cfg(target_arch = "aarch64")]
32 if let Some(isa) = super::arch::aarch64::ArmNeonIsa::new() {
33 return op.eval(isa);
34 }
35
36 #[cfg(target_arch = "x86_64")]
37 {
38 {
39 #[target_feature(enable = "avx512f")]
41 #[target_feature(enable = "avx512vl")]
42 #[target_feature(enable = "avx512bw")]
43 #[target_feature(enable = "avx512dq")]
44 #[target_feature(enable = "f16c")]
45 unsafe fn dispatch_avx512<Op: SimdOp>(isa: impl Isa, op: Op) -> Op::Output {
46 op.eval(isa)
47 }
48
49 if let Some(isa) = super::arch::x86_64::Avx512Isa::new() {
50 unsafe {
52 return dispatch_avx512(isa, op);
53 }
54 }
55 }
56
57 #[target_feature(enable = "avx2")]
59 #[target_feature(enable = "avx")]
60 #[target_feature(enable = "fma")]
61 #[target_feature(enable = "f16c")]
62 unsafe fn dispatch_avx2<Op: SimdOp>(isa: impl Isa, op: Op) -> Op::Output {
63 op.eval(isa)
64 }
65
66 if let Some(isa) = super::arch::x86_64::Avx2Isa::new() {
67 unsafe {
69 return dispatch_avx2(isa, op);
70 }
71 }
72 }
73
74 #[cfg(target_arch = "wasm32")]
75 #[cfg(target_feature = "simd128")]
76 {
77 if let Some(isa) = super::arch::wasm32::Wasm32Isa::new() {
78 return op.eval(isa);
79 }
80 }
81
82 let isa = super::arch::generic::GenericIsa::new();
83 op.eval(isa)
84}
85
86pub trait SimdUnaryOp<T: GetSimd> {
88 fn eval<I: Isa>(&self, isa: I, x: T::Simd<I>) -> T::Simd<I>;
104
105 #[inline(always)]
111 fn apply<I: Isa>(isa: I, x: T::Simd<I>) -> T::Simd<I>
112 where
113 Self: Default,
114 {
115 Self::default().eval(isa, x)
116 }
117
118 fn map<'dst>(&self, input: &[T], output: &'dst mut [MaybeUninit<T>]) -> &'dst mut [T]
123 where
124 Self: Sized,
125 T: GetNumOps,
126 {
127 let wrapped_op = SimdMapOp::wrap((input, output).into(), self);
128 dispatch(wrapped_op)
129 }
130
131 #[allow(private_bounds)]
136 fn map_mut(&self, input: &mut [T])
137 where
138 Self: Sized,
139 T: GetNumOps,
140 {
141 let wrapped_op = SimdMapOp::wrap(input.into(), self);
142 dispatch(wrapped_op);
143 }
144
145 fn scalar_eval(&self, x: T) -> T
147 where
148 Self: Sized,
149 T: GetNumOps,
150 {
151 let mut array = [x];
152 self.map_mut(&mut array);
153 array[0]
154 }
155}
156
157struct SimdMapOp<'src, 'dst, 'op, T: GetSimd, Op: SimdUnaryOp<T>> {
160 src_dest: SrcDest<'src, 'dst, T>,
161 op: &'op Op,
162}
163
164impl<'src, 'dst, 'op, T: GetSimd, Op: SimdUnaryOp<T>> SimdMapOp<'src, 'dst, 'op, T, Op> {
165 pub fn wrap(src_dest: SrcDest<'src, 'dst, T>, op: &'op Op) -> Self {
166 SimdMapOp { src_dest, op }
167 }
168}
169
170impl<'dst, T: GetNumOps + GetSimd, Op: SimdUnaryOp<T>> SimdOp for SimdMapOp<'_, 'dst, '_, T, Op> {
171 type Output = &'dst mut [T];
172
173 #[inline(always)]
174 fn eval<I: Isa>(self, isa: I) -> Self::Output {
175 simd_map(
176 T::num_ops(isa),
177 self.src_dest,
178 #[inline(always)]
179 |x| self.op.eval(isa, x),
180 )
181 }
182}
183
184#[cfg(test)]
186macro_rules! test_simd_op {
187 ($isa:ident, $op:block) => {{
188 struct TestOp {}
189
190 impl SimdOp for TestOp {
191 type Output = ();
192
193 fn eval<I: Isa>(self, $isa: I) {
194 $op
195 }
196 }
197
198 TestOp {}.dispatch()
199 }};
200}
201
202#[cfg(test)]
203pub(crate) use test_simd_op;
204
205#[cfg(test)]
206mod tests {
207 use super::SimdUnaryOp;
208 use crate::Isa;
209 use crate::ops::{FloatOps, GetNumOps, GetSimd, NumOps};
210
211 #[test]
212 fn test_unary_float_op() {
213 struct Reciprocal {}
214
215 impl SimdUnaryOp<f32> for Reciprocal {
216 fn eval<I: Isa>(&self, isa: I, x: I::F32) -> I::F32 {
217 let ops = isa.f32();
218 ops.div(ops.one(), x)
219 }
220 }
221
222 let mut buf = [1., 2., 3., 4.];
223 Reciprocal {}.map_mut(&mut buf);
224
225 assert_eq!(buf, [1., 1. / 2., 1. / 3., 1. / 4.]);
226 }
227
228 #[test]
229 fn test_unary_generic_op() {
230 struct Double {}
231
232 impl<T> SimdUnaryOp<T> for Double
233 where
234 T: GetSimd + GetNumOps,
235 {
236 fn eval<I: Isa>(&self, isa: I, x: T::Simd<I>) -> T::Simd<I> {
237 let ops = T::num_ops(isa);
238 ops.add(x, x)
239 }
240 }
241
242 let mut buf = [1i32, 2, 3, 4];
243 Double {}.map_mut(&mut buf);
244 assert_eq!(buf, [2, 4, 6, 8]);
245
246 let mut buf = [1.0f32, 2., 3., 4.];
247 Double {}.map_mut(&mut buf);
248 assert_eq!(buf, [2., 4., 6., 8.]);
249 }
250}