78
78
//! The following crate [feature flags](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features) are available:
79
79
//!
80
80
//! - `std` (enabled by default): Get floating point functions from the standard library (likely using your target's libc).
81
+ //! Also allows using [`Level::new`] on all platforms, to detect which target features are enabled.
81
82
//! - `libm`: Use floating point implementations from [libm].
82
83
//! - `safe_wrappers`: Include safe wrappers for (some) target feature specific intrinsics,
83
84
//! beyond the basic SIMD operations abstracted on all platforms.
@@ -125,7 +126,7 @@ pub use generated::*;
125
126
pub use traits:: * ;
126
127
127
128
/// Implementations of [`Simd`] for 64 bit ARM.
128
- #[ cfg( all ( feature = "std" , target_arch = "aarch64" ) ) ]
129
+ #[ cfg( target_arch = "aarch64" ) ]
129
130
pub mod aarch64 {
130
131
pub use crate :: generated:: Neon ;
131
132
}
@@ -137,7 +138,7 @@ pub mod wasm32 {
137
138
}
138
139
139
140
/// Implementations of [`Simd`] on x86 architectures (both 32 and 64 bit).
140
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
141
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
141
142
pub mod x86 {
142
143
pub use crate :: generated:: Avx2 ;
143
144
pub use crate :: generated:: Sse4_2 ;
@@ -153,18 +154,19 @@ pub enum Level {
153
154
/// Scalar fallback level, i.e. no supported SIMD features are to be used.
154
155
///
155
156
/// This can be created with [`Level::fallback`].
157
+ // TODO: Allow not compiling this in (probably only on web, but maybe elsewhere?)
156
158
Fallback ( Fallback ) ,
157
159
/// The Neon instruction set on 64 bit ARM.
158
- #[ cfg( all ( feature = "std" , target_arch = "aarch64" ) ) ]
160
+ #[ cfg( target_arch = "aarch64" ) ]
159
161
Neon ( Neon ) ,
160
162
/// The SIMD 128 instructions on 32-bit WebAssembly.
161
163
#[ cfg( all( target_arch = "wasm32" , target_feature = "simd128" ) ) ]
162
164
WasmSimd128 ( WasmSimd128 ) ,
163
165
/// The SSE4.2 instruction set on (32 and 64 bit) x86.
164
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
166
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
165
167
Sse4_2 ( Sse4_2 ) ,
166
168
/// The AVX2 and FMA instruction set on (32 and 64 bit) x86.
167
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
169
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
168
170
Avx2 ( Avx2 ) ,
169
171
// If new variants are added, make sure to handle them in `Level::dispatch`
170
172
// and `simd_dispatch`
@@ -175,15 +177,42 @@ impl Level {
175
177
///
176
178
/// If no SIMD instruction set is available, a scalar fallback will be used instead.
177
179
///
180
+ /// This function requires the standard library, to use the
181
+ /// [`is_x86_feature_detected`](std::arch::is_x86_feature_detected)
182
+ /// or [`is_aarch64_feature_detected`](std::arch::is_aarch64_feature_detected).
183
+ /// If you are on wasm32, you can use `Level::new_wasm` instead.
184
+ ///
185
+ /// Note that in most cases, this function should only be called by end-user applications.
186
+ /// Libraries should instead accept a `Level` argument, probably as they are
187
+ /// creating their data structures, then storing the level for any computations.
188
+ /// Libraries which wish to abstract away SIMD usage for their common-case clients,
189
+ /// should make their non-`Level` entrypoint match this function's `cfg`; to instead
190
+ /// handle this at runtime, they can use [`try_detect`](Self::try_detect),
191
+ /// handling the `None` case as they deem fit (probably panicking).
192
+ /// This strategy avoids users of the library inadvertently using the fallback level,
193
+ /// even if the requisite target features are available.
194
+ ///
195
+ /// If you are on an embedded device where these macros are not supported,
196
+ /// you should construct the relevant variants yourself, using whatever
197
+ /// way your specific chip supports accessing the current level.
198
+ ///
178
199
/// This value will be passed to functions generated using [`simd_dispatch`].
200
+ #[ cfg( any( feature = "std" , target_arch = "wasm32" ) ) ]
201
+ #[ must_use]
179
202
pub fn new ( ) -> Self {
180
- #[ cfg( all ( feature = "std" , target_arch = "aarch64" ) ) ]
203
+ #[ cfg( target_arch = "aarch64" ) ]
181
204
if std:: arch:: is_aarch64_feature_detected!( "neon" ) {
182
205
return unsafe { Level :: Neon ( Neon :: new_unchecked ( ) ) } ;
183
206
}
184
- #[ cfg( all( target_arch = "wasm32" , target_feature = "simd128" ) ) ]
185
- return Level :: WasmSimd128 ( WasmSimd128 :: new_unchecked ( ) ) ;
186
- #[ cfg( all( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
207
+ #[ cfg( target_arch = "wasm32" ) ]
208
+ {
209
+ // WASM always either has the SIMD feature compiled in or not.
210
+ #[ cfg( target_feature = "simd128" ) ]
211
+ return Level :: WasmSimd128 ( WasmSimd128 :: new_unchecked ( ) ) ;
212
+ #[ cfg( not( target_feature = "simd128" ) ) ]
213
+ return Level :: fallback ( ) ;
214
+ }
215
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
187
216
{
188
217
if std:: arch:: is_x86_feature_detected!( "avx2" )
189
218
&& std:: arch:: is_x86_feature_detected!( "fma" )
@@ -193,10 +222,24 @@ impl Level {
193
222
return unsafe { Level :: Sse4_2 ( Sse4_2 :: new_unchecked ( ) ) } ;
194
223
}
195
224
}
196
- #[ cfg( not( all ( target_arch = "wasm32" , target_feature = "simd128" ) ) ) ]
225
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
197
226
Self :: fallback ( )
198
227
}
199
228
229
+ /// Get the target feature level suitable for this run.
230
+ ///
231
+ /// Should be used in libraries if they wish to handle the case where
232
+ /// target features cannot be detected at runtime.
233
+ /// Most users should prefer [`new`](Self::new).
234
+ /// This is discussed in more detail in `new`'s documentation.
235
+ #[ allow( clippy:: allow_attributes, reason = "Only needed in some cfgs." ) ]
236
+ #[ allow( unreachable_code, reason = "Fallback unreachable in some cfgs." ) ]
237
+ pub fn try_detect ( ) -> Option < Self > {
238
+ #[ cfg( any( feature = "std" , target_arch = "wasm32" ) ) ]
239
+ return Some ( Self :: new ( ) ) ;
240
+ None
241
+ }
242
+
200
243
/// If this is a proof that Neon (or better) is available, access that instruction set.
201
244
///
202
245
/// This method should be preferred over matching against the `Neon` variant of self,
@@ -205,7 +248,7 @@ impl Level {
205
248
///
206
249
/// This can be used in combination with the `safe_wrappers` feature to gain checked access to
207
250
/// the level-specific SIMD capabilities.
208
- #[ cfg( all ( feature = "std" , target_arch = "aarch64" ) ) ]
251
+ #[ cfg( target_arch = "aarch64" ) ]
209
252
#[ inline]
210
253
pub fn as_neon ( self ) -> Option < Neon > {
211
254
match self {
@@ -239,7 +282,7 @@ impl Level {
239
282
///
240
283
/// This can be used in combination with the `safe_wrappers` feature to gain checked access to
241
284
/// the level-specific SIMD capabilities.
242
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
285
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
243
286
#[ inline]
244
287
pub fn as_sse4_2 ( self ) -> Option < Sse4_2 > {
245
288
match self {
@@ -256,7 +299,7 @@ impl Level {
256
299
///
257
300
/// This can be used in combination with the `safe_wrappers` feature to gain checked access to
258
301
/// the level-specific SIMD capabilities.
259
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
302
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
260
303
#[ inline]
261
304
pub fn as_avx2 ( self ) -> Option < Avx2 > {
262
305
match self {
@@ -291,7 +334,7 @@ impl Level {
291
334
/// [enabled]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-target_feature-attribute
292
335
#[ inline]
293
336
pub fn dispatch < W : WithSimd > ( self , f : W ) -> W :: Output {
294
- #[ cfg( all ( feature = "std" , target_arch = "aarch64" ) ) ]
337
+ #[ cfg( target_arch = "aarch64" ) ]
295
338
#[ target_feature( enable = "neon" ) ]
296
339
#[ inline]
297
340
// unsafe not needed here with tf11, but can be justified
@@ -305,14 +348,14 @@ impl Level {
305
348
f. with_simd ( simd128)
306
349
}
307
350
308
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
351
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
309
352
#[ target_feature( enable = "sse4.2" ) ]
310
353
#[ inline]
311
354
unsafe fn dispatch_sse4_2 < W : WithSimd > ( f : W , sse4_2 : Sse4_2 ) -> W :: Output {
312
355
f. with_simd ( sse4_2)
313
356
}
314
357
315
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
358
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
316
359
#[ target_feature( enable = "avx2,fma" ) ]
317
360
#[ inline]
318
361
unsafe fn dispatch_avx2 < W : WithSimd > ( f : W , avx2 : Avx2 ) -> W :: Output {
@@ -325,13 +368,13 @@ impl Level {
325
368
}
326
369
327
370
match self {
328
- #[ cfg( all ( feature = "std" , target_arch = "aarch64" ) ) ]
371
+ #[ cfg( target_arch = "aarch64" ) ]
329
372
Level :: Neon ( neon) => unsafe { dispatch_neon ( f, neon) } ,
330
373
#[ cfg( all( target_arch = "wasm32" , target_feature = "simd128" ) ) ]
331
374
Level :: WasmSimd128 ( simd128) => dispatch_simd128 ( f, simd128) ,
332
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
375
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
333
376
Level :: Sse4_2 ( sse4_2) => unsafe { dispatch_sse4_2 ( f, sse4_2) } ,
334
- #[ cfg( all ( feature = "std" , any( target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
377
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
335
378
Level :: Avx2 ( avx2) => unsafe { dispatch_avx2 ( f, avx2) } ,
336
379
Level :: Fallback ( fallback) => dispatch_fallback ( f, fallback) ,
337
380
}
0 commit comments