Skip to main content

hermes_simd/
target.rs

1//! Explicit SIMD target tokens and forced view dispatch helpers.
2
3#[cfg(target_arch = "aarch64")]
4use crate::Neon;
5#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
6use crate::{Avx2, Avx512};
7use crate::{DispatchedView, Scalar};
8use hermes_simd_core::{
9    align::Alignment, arch::SimdArch, execution::Unmasked, scalar::FloatElement, view::SimdView,
10};
11
12/// Runtime-selectable SIMD target token for tests and benchmark harnesses.
13///
14/// `TargetId` is a closed identifier for Hermes' public CPU targets. Use
15/// [`TargetId::is_supported`] before entering a target-specific benchmark row,
16/// or call [`dispatch_view_to`] / [`dispatch_view_mut_to`] to construct a typed
17/// view only when the host can execute that target.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum TargetId {
20    /// Portable scalar target; always supported.
21    Scalar,
22    /// x86/x86_64 AVX2 target, requiring AVX2 and FMA.
23    Avx2,
24    /// x86/x86_64 AVX-512F target.
25    Avx512,
26    /// AArch64 NEON target.
27    Neon,
28}
29
30impl TargetId {
31    /// Returns the stable lowercase target name used in reports and benchmarks.
32    #[must_use]
33    pub const fn name(self) -> &'static str {
34        match self {
35            Self::Scalar => "scalar",
36            Self::Avx2 => "avx2",
37            Self::Avx512 => "avx512",
38            Self::Neon => "neon",
39        }
40    }
41
42    /// Returns true when the current host may execute this target.
43    #[must_use]
44    pub fn is_supported(self) -> bool {
45        match self {
46            Self::Scalar => true,
47            Self::Avx2 => avx2_supported(),
48            Self::Avx512 => avx512_supported(),
49            Self::Neon => neon_supported(),
50        }
51    }
52}
53
54#[inline]
55fn avx2_supported() -> bool {
56    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
57    {
58        Avx2::is_runtime_supported()
59    }
60    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
61    {
62        false
63    }
64}
65
66#[inline]
67fn avx512_supported() -> bool {
68    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
69    {
70        Avx512::is_runtime_supported()
71    }
72    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
73    {
74        false
75    }
76}
77
78#[inline]
79fn neon_supported() -> bool {
80    #[cfg(target_arch = "aarch64")]
81    {
82        Neon::is_runtime_supported()
83    }
84    #[cfg(not(target_arch = "aarch64"))]
85    {
86        false
87    }
88}
89
90/// Dispatches a shared slice into an explicitly requested target.
91///
92/// Returns `None` when the target is not supported by the host or when the
93/// requested alignment typestate is not satisfied by `data`.
94#[inline]
95#[allow(unreachable_code)]
96pub fn dispatch_view_to<'a, T, Align>(
97    target: TargetId,
98    data: &'a [T],
99) -> Option<DispatchedView<'a, T, Align, Unmasked, &'a [T]>>
100where
101    T: FloatElement,
102    Align: Alignment,
103{
104    match target {
105        TargetId::Scalar => {
106            SimdView::<T, Scalar, Align, Unmasked, &'a [T]>::new(data).map(DispatchedView::Scalar)
107        }
108        TargetId::Avx2 => {
109            if !target.is_supported() {
110                None
111            } else {
112                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
113                {
114                    SimdView::<T, Avx2, Align, Unmasked, &'a [T]>::new(data)
115                        .map(DispatchedView::Avx2)
116                }
117                #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
118                {
119                    None
120                }
121            }
122        }
123        TargetId::Avx512 => {
124            if !target.is_supported() {
125                None
126            } else {
127                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
128                {
129                    SimdView::<T, Avx512, Align, Unmasked, &'a [T]>::new(data)
130                        .map(DispatchedView::Avx512)
131                }
132                #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
133                {
134                    None
135                }
136            }
137        }
138        TargetId::Neon => {
139            if !target.is_supported() {
140                None
141            } else {
142                #[cfg(target_arch = "aarch64")]
143                {
144                    SimdView::<T, Neon, Align, Unmasked, &'a [T]>::new(data)
145                        .map(DispatchedView::Neon)
146                }
147                #[cfg(not(target_arch = "aarch64"))]
148                {
149                    None
150                }
151            }
152        }
153    }
154}
155
156/// Dispatches a mutable slice into an explicitly requested target.
157///
158/// Returns `None` when the target is not supported by the host or when the
159/// requested alignment typestate is not satisfied by `data`.
160#[inline]
161#[allow(unreachable_code)]
162pub fn dispatch_view_mut_to<'a, T, Align>(
163    target: TargetId,
164    data: &'a mut [T],
165) -> Option<DispatchedView<'a, T, Align, Unmasked, &'a mut [T]>>
166where
167    T: FloatElement,
168    Align: Alignment,
169{
170    match target {
171        TargetId::Scalar => SimdView::<T, Scalar, Align, Unmasked, &'a mut [T]>::new_mut(data)
172            .map(DispatchedView::Scalar),
173        TargetId::Avx2 => {
174            if !target.is_supported() {
175                None
176            } else {
177                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
178                {
179                    SimdView::<T, Avx2, Align, Unmasked, &'a mut [T]>::new_mut(data)
180                        .map(DispatchedView::Avx2)
181                }
182                #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
183                {
184                    None
185                }
186            }
187        }
188        TargetId::Avx512 => {
189            if !target.is_supported() {
190                None
191            } else {
192                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
193                {
194                    SimdView::<T, Avx512, Align, Unmasked, &'a mut [T]>::new_mut(data)
195                        .map(DispatchedView::Avx512)
196                }
197                #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
198                {
199                    None
200                }
201            }
202        }
203        TargetId::Neon => {
204            if !target.is_supported() {
205                None
206            } else {
207                #[cfg(target_arch = "aarch64")]
208                {
209                    SimdView::<T, Neon, Align, Unmasked, &'a mut [T]>::new_mut(data)
210                        .map(DispatchedView::Neon)
211                }
212                #[cfg(not(target_arch = "aarch64"))]
213                {
214                    None
215                }
216            }
217        }
218    }
219}