vsimd/
isa.rs

1use crate::{SIMD128, SIMD256, SIMD64};
2
3pub unsafe trait InstructionSet: Copy + 'static {
4    const ID: InstructionSetTypeId;
5    const ARCH: bool;
6
7    unsafe fn new() -> Self;
8
9    fn is_enabled() -> bool;
10}
11
12#[inline(always)]
13#[must_use]
14pub fn detect<S: InstructionSet>() -> Option<S> {
15    S::is_enabled().then(|| unsafe { S::new() })
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum InstructionSetTypeId {
20    Fallback,
21    SSE2,
22    SSSE3,
23    SSE41,
24    AVX2,
25    NEON,
26    WASM128,
27}
28
29#[doc(hidden)]
30#[inline]
31#[must_use]
32pub const fn matches_isa_impl<S, U>() -> bool
33where
34    S: InstructionSet,
35    U: InstructionSet,
36{
37    #[allow(clippy::enum_glob_use)]
38    use InstructionSetTypeId::*;
39
40    let (self_ty, super_ty) = (S::ID, U::ID);
41    let inherits = match self_ty {
42        Fallback => matches!(super_ty, Fallback),
43        SSE2 => matches!(super_ty, Fallback | SSE2),
44        SSSE3 => matches!(super_ty, Fallback | SSE2 | SSSE3),
45        SSE41 => matches!(super_ty, Fallback | SSE2 | SSSE3 | SSE41),
46        AVX2 => matches!(super_ty, Fallback | SSE2 | SSSE3 | SSE41 | AVX2),
47        NEON => matches!(super_ty, Fallback | NEON),
48        WASM128 => matches!(super_ty, Fallback | WASM128),
49    };
50
51    S::ARCH && U::ARCH && inherits
52}
53
54#[macro_export]
55macro_rules! is_isa_type {
56    ($self:ident, $isa:ident) => {{
57        matches!(
58            <$self as $crate::isa::InstructionSet>::ID,
59            <$isa as $crate::isa::InstructionSet>::ID
60        )
61    }};
62}
63
64#[macro_export]
65macro_rules! matches_isa {
66    ($self:ident, $super:ident $(| $other:ident)*) => {{
67        // TODO: inline const
68        use $crate::isa::InstructionSet;
69        struct MatchesISA<S>(S);
70        impl<S: InstructionSet> MatchesISA<S> {
71            const VALUE: bool = { $crate::isa::matches_isa_impl::<S, $super>() $(||$crate::isa::matches_isa_impl::<S, $other>())* };
72        }
73        MatchesISA::<$self>::VALUE
74    }};
75}
76
77#[derive(Debug, Clone, Copy)]
78pub struct Fallback(());
79
80unsafe impl InstructionSet for Fallback {
81    const ID: InstructionSetTypeId = InstructionSetTypeId::Fallback;
82    const ARCH: bool = true;
83
84    #[inline(always)]
85    unsafe fn new() -> Self {
86        Self(())
87    }
88
89    #[inline(always)]
90    fn is_enabled() -> bool {
91        true
92    }
93}
94
95#[allow(unused_macros)]
96macro_rules! is_feature_detected {
97    ($feature:tt) => {{
98        #[cfg(target_feature = $feature)]
99        {
100            true
101        }
102        #[cfg(not(target_feature = $feature))]
103        {
104            #[cfg(feature = "detect")]
105            {
106                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
107                {
108                    std::arch::is_x86_feature_detected!($feature)
109                }
110                #[cfg(target_arch = "arm")]
111                {
112                    std::arch::is_arm_feature_detected!($feature)
113                }
114                #[cfg(target_arch = "aarch64")]
115                {
116                    std::arch::is_aarch64_feature_detected!($feature)
117                }
118                #[cfg(not(any(
119                    target_arch = "x86",
120                    target_arch = "x86_64",
121                    target_arch = "arm",
122                    target_arch = "aarch64"
123                )))]
124                {
125                    false
126                }
127            }
128            #[cfg(not(feature = "detect"))]
129            {
130                false
131            }
132        }
133    }};
134}
135
136macro_rules! x86_is_enabled {
137    ($feature:tt) => {{
138        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
139        {
140            is_feature_detected!($feature)
141        }
142        #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
143        {
144            false
145        }
146    }};
147}
148
149#[derive(Debug, Clone, Copy)]
150pub struct SSE2(());
151
152unsafe impl InstructionSet for SSE2 {
153    const ID: InstructionSetTypeId = InstructionSetTypeId::SSE2;
154    const ARCH: bool = cfg!(any(target_arch = "x86", target_arch = "x86_64"));
155
156    #[inline(always)]
157    unsafe fn new() -> Self {
158        Self(())
159    }
160
161    #[inline(always)]
162    fn is_enabled() -> bool {
163        x86_is_enabled!("sse2")
164    }
165}
166
167unsafe impl SIMD64 for SSE2 {}
168unsafe impl SIMD128 for SSE2 {}
169unsafe impl SIMD256 for SSE2 {}
170
171#[derive(Debug, Clone, Copy)]
172pub struct SSSE3(());
173
174unsafe impl InstructionSet for SSSE3 {
175    const ID: InstructionSetTypeId = InstructionSetTypeId::SSSE3;
176    const ARCH: bool = cfg!(any(target_arch = "x86", target_arch = "x86_64"));
177
178    #[inline(always)]
179    unsafe fn new() -> Self {
180        Self(())
181    }
182
183    #[inline(always)]
184    fn is_enabled() -> bool {
185        x86_is_enabled!("ssse3")
186    }
187}
188
189unsafe impl SIMD64 for SSSE3 {}
190unsafe impl SIMD128 for SSSE3 {}
191unsafe impl SIMD256 for SSSE3 {}
192
193#[derive(Debug, Clone, Copy)]
194pub struct SSE41(());
195
196unsafe impl InstructionSet for SSE41 {
197    const ID: InstructionSetTypeId = InstructionSetTypeId::SSE41;
198    const ARCH: bool = cfg!(any(target_arch = "x86", target_arch = "x86_64"));
199
200    #[inline(always)]
201    unsafe fn new() -> Self {
202        Self(())
203    }
204
205    #[inline(always)]
206    fn is_enabled() -> bool {
207        x86_is_enabled!("sse4.1")
208    }
209}
210
211unsafe impl SIMD64 for SSE41 {}
212unsafe impl SIMD128 for SSE41 {}
213unsafe impl SIMD256 for SSE41 {}
214
215#[derive(Debug, Clone, Copy)]
216pub struct AVX2(());
217
218unsafe impl InstructionSet for AVX2 {
219    const ID: InstructionSetTypeId = InstructionSetTypeId::AVX2;
220    const ARCH: bool = cfg!(any(target_arch = "x86", target_arch = "x86_64"));
221
222    #[inline(always)]
223    unsafe fn new() -> Self {
224        Self(())
225    }
226
227    #[inline(always)]
228    fn is_enabled() -> bool {
229        x86_is_enabled!("avx2")
230    }
231}
232
233unsafe impl SIMD64 for AVX2 {}
234unsafe impl SIMD128 for AVX2 {}
235unsafe impl SIMD256 for AVX2 {}
236
237#[allow(clippy::upper_case_acronyms)]
238#[derive(Debug, Clone, Copy)]
239pub struct NEON(());
240
241unsafe impl InstructionSet for NEON {
242    const ID: InstructionSetTypeId = InstructionSetTypeId::NEON;
243    const ARCH: bool = cfg!(any(target_arch = "arm", target_arch = "aarch64"));
244
245    #[inline(always)]
246    unsafe fn new() -> Self {
247        Self(())
248    }
249
250    #[inline(always)]
251    fn is_enabled() -> bool {
252        #[cfg(target_arch = "arm")]
253        {
254            #[cfg(feature = "unstable")]
255            {
256                is_feature_detected!("neon")
257            }
258            #[cfg(not(feature = "unstable"))]
259            {
260                false
261            }
262        }
263        #[cfg(target_arch = "aarch64")]
264        {
265            is_feature_detected!("neon")
266        }
267        #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
268        {
269            false
270        }
271    }
272}
273
274unsafe impl SIMD64 for NEON {}
275unsafe impl SIMD128 for NEON {}
276unsafe impl SIMD256 for NEON {}
277
278#[derive(Debug, Clone, Copy)]
279pub struct WASM128(());
280
281unsafe impl InstructionSet for WASM128 {
282    const ID: InstructionSetTypeId = InstructionSetTypeId::WASM128;
283    const ARCH: bool = cfg!(target_arch = "wasm32");
284
285    #[inline(always)]
286    unsafe fn new() -> Self {
287        Self(())
288    }
289
290    #[inline(always)]
291    fn is_enabled() -> bool {
292        #[cfg(target_arch = "wasm32")]
293        {
294            is_feature_detected!("simd128")
295        }
296        #[cfg(not(target_arch = "wasm32"))]
297        {
298            false
299        }
300    }
301}
302
303unsafe impl SIMD64 for WASM128 {}
304unsafe impl SIMD128 for WASM128 {}
305unsafe impl SIMD256 for WASM128 {}