Skip to main content

target_features/
simd.rs

1#[cfg(target_arch = "aarch64")]
2use crate::aarch64 as features;
3#[cfg(target_arch = "arm")]
4use crate::arm as features;
5#[cfg(target_arch = "arm64ec")]
6use crate::arm64ec as features;
7#[cfg(target_arch = "hexagon")]
8use crate::hexagon as features;
9#[cfg(target_arch = "loongarch32")]
10use crate::loongarch32 as features;
11#[cfg(target_arch = "loongarch64")]
12use crate::loongarch64 as features;
13#[cfg(target_arch = "mips")]
14use crate::mips as features;
15#[cfg(target_arch = "mips64")]
16use crate::mips64 as features;
17#[cfg(target_arch = "powerpc")]
18use crate::powerpc as features;
19#[cfg(target_arch = "powerpc64")]
20use crate::powerpc64 as features;
21#[cfg(target_arch = "riscv32")]
22use crate::riscv32 as features;
23#[cfg(target_arch = "riscv64")]
24use crate::riscv64 as features;
25#[cfg(target_arch = "s390x")]
26use crate::s390x as features;
27#[cfg(target_arch = "wasm32")]
28use crate::wasm32 as features;
29#[cfg(target_arch = "wasm64")]
30use crate::wasm64 as features;
31#[cfg(target_arch = "x86")]
32use crate::x86 as features;
33#[cfg(target_arch = "x86_64")]
34use crate::x86_64 as features;
35
36mod sealed {
37    pub trait SealedSimdElement {}
38}
39
40#[doc(hidden)]
41pub enum SimdElementImpl {
42    Float32,
43    Float64,
44    Other,
45}
46
47/// Types that can be elements of SIMD vectors.
48pub trait SimdElement: sealed::SealedSimdElement {
49    #[doc(hidden)]
50    const IMPL: SimdElementImpl;
51}
52
53macro_rules! impl_simd_element {
54    ($impl:ident: $($ty:ty),* $(,)?) => {
55        $(
56            impl sealed::SealedSimdElement for $ty {}
57            impl SimdElement for $ty {
58                const IMPL: SimdElementImpl = SimdElementImpl::$impl;
59            }
60        )*
61    };
62}
63
64impl_simd_element!(Other: u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
65impl_simd_element!(Float32: f32);
66impl_simd_element!(Float64: f64);
67
68impl<T> sealed::SealedSimdElement for *const T {}
69impl<T> SimdElement for *const T {
70    const IMPL: SimdElementImpl = SimdElementImpl::Other;
71}
72
73impl<T> sealed::SealedSimdElement for *mut T {}
74impl<T> SimdElement for *mut T {
75    const IMPL: SimdElementImpl = SimdElementImpl::Other;
76}
77
78impl crate::TargetFeatures {
79    /// Returns a suggested number of elements for a SIMD vector of type `T`.
80    ///
81    /// The returned value is an approximation and not necessarily indicative of the
82    /// optimal vector width.  A few caveats:
83    /// * Every instruction set is different, and this function doesn't take into account any
84    ///   particular operations--it's just a guess, and should be accurate at least for basic arithmetic.
85    /// * Variable-length vector instruction sets (ARM SVE and RISC-V V) only return the minimum
86    ///   vector length.
87    #[allow(unused_variables)]
88    pub const fn suggested_simd_width<T: SimdElement>(&self) -> Option<usize> {
89        let is_f32 = T::IMPL as u8 == SimdElementImpl::Float32 as u8;
90        let is_f64 = T::IMPL as u8 == SimdElementImpl::Float64 as u8;
91        let is_integer = !is_f32 && !is_f64;
92        let element_size = core::mem::size_of::<T>();
93
94        let v128 = 16 / element_size;
95        let v256 = 32 / element_size;
96        let v512 = 64 / element_size;
97        let v1024 = 128 / element_size;
98
99        #[cfg(target_arch = "arm")]
100        {
101            return if (self.contains(features::NEON) && !is_f64)
102                || (is_f32 && self.contains(features::MVE_FP))
103                || (is_integer && element_size <= 4 && self.contains(features::MVE))
104            {
105                Some(v128)
106            } else {
107                None
108            };
109        }
110
111        #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))]
112        {
113            return if self.contains(features::NEON) {
114                Some(v128)
115            } else {
116                None
117            };
118        }
119
120        #[cfg(target_arch = "hexagon")]
121        {
122            return if is_f64 {
123                None
124            } else if is_f32 {
125                if self.contains(features::HVX_LENGTH128B)
126                    && self.contains(features::HVXV68)
127                    && self.contains(features::HVX_IEEE_FP)
128                {
129                    Some(v1024)
130                } else {
131                    None
132                }
133            } else if self.contains(features::HVX_LENGTH128B) {
134                Some(v1024)
135            } else if self.contains(features::HVX) {
136                Some(v512)
137            } else {
138                None
139            };
140        }
141
142        #[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))]
143        {
144            return if self.contains(features::LASX) {
145                Some(v256)
146            } else if self.contains(features::LSX) {
147                Some(v128)
148            } else {
149                None
150            };
151        }
152
153        #[cfg(any(target_arch = "mips", target_arch = "mips64"))]
154        {
155            return if self.contains(features::MSA) {
156                Some(v128)
157            } else {
158                None
159            };
160        }
161
162        #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
163        {
164            return if self.contains(features::VSX) || (self.contains(features::ALTIVEC) && !is_f64)
165            {
166                Some(v128)
167            } else {
168                None
169            };
170        }
171
172        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
173        {
174            let supports_element = if is_f64 {
175                self.contains(features::ZVE64D)
176            } else if is_f32 {
177                self.contains(features::ZVE32F)
178            } else if element_size <= 4 {
179                self.contains(features::ZVE32X)
180            } else if element_size <= 8 {
181                self.contains(features::ZVE64X)
182            } else {
183                false
184            };
185
186            let vector_bytes = if self.contains(features::ZVL65536B) {
187                8192
188            } else if self.contains(features::ZVL32768B) {
189                4096
190            } else if self.contains(features::ZVL16384B) {
191                2048
192            } else if self.contains(features::ZVL8192B) {
193                1024
194            } else if self.contains(features::ZVL4096B) {
195                512
196            } else if self.contains(features::ZVL2048B) {
197                256
198            } else if self.contains(features::ZVL1024B) {
199                128
200            } else if self.contains(features::ZVL512B) {
201                64
202            } else if self.contains(features::ZVL256B) {
203                32
204            } else if self.contains(features::ZVL128B) {
205                16
206            } else if self.contains(features::ZVL64B) {
207                8
208            } else if self.contains(features::ZVL32B) {
209                4
210            } else {
211                0
212            };
213
214            return if supports_element && vector_bytes != 0 {
215                Some(vector_bytes / element_size)
216            } else {
217                None
218            };
219        }
220
221        #[cfg(target_arch = "s390x")]
222        {
223            return if self.contains(features::VECTOR) {
224                Some(v128)
225            } else {
226                None
227            };
228        }
229
230        #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
231        {
232            return if self.contains(features::SIMD128) {
233                Some(v128)
234            } else {
235                None
236            };
237        }
238
239        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
240        {
241            return if self.contains(features::AVX512F)
242                && (!is_integer || element_size >= 4 || self.contains(features::AVX512BW))
243            {
244                Some(v512)
245            } else if self.contains(features::AVX2)
246                || ((is_f32 || is_f64) && self.contains(features::AVX))
247            {
248                Some(v256)
249            } else if self.contains(features::SSE2) || (is_f32 && self.contains(features::SSE)) {
250                Some(v128)
251            } else {
252                None
253            };
254        }
255
256        #[allow(unreachable_code)]
257        None
258    }
259}