Skip to content

Commit 58e7838

Browse files
authored
Add true support for no_std libraries (#70)
Fixes #66
1 parent 1ead1b4 commit 58e7838

File tree

5 files changed

+71
-52
lines changed

5 files changed

+71
-52
lines changed

fearless_simd/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ all-features = true
2020

2121
[features]
2222
default = ["std"]
23-
# Get floating point functions from the standard library (likely using your targets libc)
23+
# Get floating point functions from the standard library (likely using your targets libc).
24+
# Also allows using `Level::new` on all platforms, to detect which target features are enabled
2425
std = []
2526
# Use floating point implementations from libm
2627
libm = ["dep:libm"]

fearless_simd/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ It benefited from conversations with Luca Versari, though he is not responsible
116116
The following crate [feature flags](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features) are available:
117117

118118
- `std` (enabled by default): Get floating point functions from the standard library (likely using your target's libc).
119+
Also allows using [`Level::new`] on all platforms, to detect which target features are enabled.
119120
- `libm`: Use floating point implementations from [libm].
120121
- `safe_wrappers`: Include safe wrappers for (some) target feature specific intrinsics,
121122
beyond the basic SIMD operations abstracted on all platforms.

fearless_simd/src/generated.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,27 @@
5050
//!
5151
//! All files in this subdirectory are autogenerated by the `fearless_simd_gen` crate.
5252
53-
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
53+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
5454
mod avx2;
5555
mod fallback;
56-
#[cfg(all(feature = "std", target_arch = "aarch64"))]
56+
#[cfg(target_arch = "aarch64")]
5757
mod neon;
5858
mod ops;
5959
mod simd_trait;
6060
mod simd_types;
61-
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
61+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
6262
mod sse4_2;
6363
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
6464
mod wasm;
6565

66-
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
66+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
6767
pub use avx2::*;
6868
pub use fallback::*;
69-
#[cfg(all(feature = "std", target_arch = "aarch64"))]
69+
#[cfg(target_arch = "aarch64")]
7070
pub use neon::*;
7171
pub use simd_trait::*;
7272
pub use simd_types::*;
73-
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
73+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
7474
pub use sse4_2::*;
7575
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
7676
pub use wasm::*;

fearless_simd/src/lib.rs

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
//! The following crate [feature flags](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features) are available:
7979
//!
8080
//! - `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.
8182
//! - `libm`: Use floating point implementations from [libm].
8283
//! - `safe_wrappers`: Include safe wrappers for (some) target feature specific intrinsics,
8384
//! beyond the basic SIMD operations abstracted on all platforms.
@@ -125,7 +126,7 @@ pub use generated::*;
125126
pub use traits::*;
126127

127128
/// Implementations of [`Simd`] for 64 bit ARM.
128-
#[cfg(all(feature = "std", target_arch = "aarch64"))]
129+
#[cfg(target_arch = "aarch64")]
129130
pub mod aarch64 {
130131
pub use crate::generated::Neon;
131132
}
@@ -137,7 +138,7 @@ pub mod wasm32 {
137138
}
138139

139140
/// 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"))]
141142
pub mod x86 {
142143
pub use crate::generated::Avx2;
143144
pub use crate::generated::Sse4_2;
@@ -153,18 +154,19 @@ pub enum Level {
153154
/// Scalar fallback level, i.e. no supported SIMD features are to be used.
154155
///
155156
/// This can be created with [`Level::fallback`].
157+
// TODO: Allow not compiling this in (probably only on web, but maybe elsewhere?)
156158
Fallback(Fallback),
157159
/// The Neon instruction set on 64 bit ARM.
158-
#[cfg(all(feature = "std", target_arch = "aarch64"))]
160+
#[cfg(target_arch = "aarch64")]
159161
Neon(Neon),
160162
/// The SIMD 128 instructions on 32-bit WebAssembly.
161163
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
162164
WasmSimd128(WasmSimd128),
163165
/// 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"))]
165167
Sse4_2(Sse4_2),
166168
/// 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"))]
168170
Avx2(Avx2),
169171
// If new variants are added, make sure to handle them in `Level::dispatch`
170172
// and `simd_dispatch`
@@ -175,15 +177,42 @@ impl Level {
175177
///
176178
/// If no SIMD instruction set is available, a scalar fallback will be used instead.
177179
///
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+
///
178199
/// This value will be passed to functions generated using [`simd_dispatch`].
200+
#[cfg(any(feature = "std", target_arch = "wasm32"))]
201+
#[must_use]
179202
pub fn new() -> Self {
180-
#[cfg(all(feature = "std", target_arch = "aarch64"))]
203+
#[cfg(target_arch = "aarch64")]
181204
if std::arch::is_aarch64_feature_detected!("neon") {
182205
return unsafe { Level::Neon(Neon::new_unchecked()) };
183206
}
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"))]
187216
{
188217
if std::arch::is_x86_feature_detected!("avx2")
189218
&& std::arch::is_x86_feature_detected!("fma")
@@ -193,10 +222,24 @@ impl Level {
193222
return unsafe { Level::Sse4_2(Sse4_2::new_unchecked()) };
194223
}
195224
}
196-
#[cfg(not(all(target_arch = "wasm32", target_feature = "simd128")))]
225+
#[cfg(not(target_arch = "wasm32"))]
197226
Self::fallback()
198227
}
199228

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+
200243
/// If this is a proof that Neon (or better) is available, access that instruction set.
201244
///
202245
/// This method should be preferred over matching against the `Neon` variant of self,
@@ -205,7 +248,7 @@ impl Level {
205248
///
206249
/// This can be used in combination with the `safe_wrappers` feature to gain checked access to
207250
/// the level-specific SIMD capabilities.
208-
#[cfg(all(feature = "std", target_arch = "aarch64"))]
251+
#[cfg(target_arch = "aarch64")]
209252
#[inline]
210253
pub fn as_neon(self) -> Option<Neon> {
211254
match self {
@@ -239,7 +282,7 @@ impl Level {
239282
///
240283
/// This can be used in combination with the `safe_wrappers` feature to gain checked access to
241284
/// 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"))]
243286
#[inline]
244287
pub fn as_sse4_2(self) -> Option<Sse4_2> {
245288
match self {
@@ -256,7 +299,7 @@ impl Level {
256299
///
257300
/// This can be used in combination with the `safe_wrappers` feature to gain checked access to
258301
/// 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"))]
260303
#[inline]
261304
pub fn as_avx2(self) -> Option<Avx2> {
262305
match self {
@@ -291,7 +334,7 @@ impl Level {
291334
/// [enabled]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-target_feature-attribute
292335
#[inline]
293336
pub fn dispatch<W: WithSimd>(self, f: W) -> W::Output {
294-
#[cfg(all(feature = "std", target_arch = "aarch64"))]
337+
#[cfg(target_arch = "aarch64")]
295338
#[target_feature(enable = "neon")]
296339
#[inline]
297340
// unsafe not needed here with tf11, but can be justified
@@ -305,14 +348,14 @@ impl Level {
305348
f.with_simd(simd128)
306349
}
307350

308-
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
351+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
309352
#[target_feature(enable = "sse4.2")]
310353
#[inline]
311354
unsafe fn dispatch_sse4_2<W: WithSimd>(f: W, sse4_2: Sse4_2) -> W::Output {
312355
f.with_simd(sse4_2)
313356
}
314357

315-
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
358+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
316359
#[target_feature(enable = "avx2,fma")]
317360
#[inline]
318361
unsafe fn dispatch_avx2<W: WithSimd>(f: W, avx2: Avx2) -> W::Output {
@@ -325,13 +368,13 @@ impl Level {
325368
}
326369

327370
match self {
328-
#[cfg(all(feature = "std", target_arch = "aarch64"))]
371+
#[cfg(target_arch = "aarch64")]
329372
Level::Neon(neon) => unsafe { dispatch_neon(f, neon) },
330373
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
331374
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"))]
333376
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"))]
335378
Level::Avx2(avx2) => unsafe { dispatch_avx2(f, avx2) },
336379
Level::Fallback(fallback) => dispatch_fallback(f, fallback),
337380
}

fearless_simd/src/macros.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
reason = "TODO: https://github.com/linebender/fearless_simd/issues/40"
99
)]
1010

11-
#[cfg(feature = "std")]
1211
#[macro_export]
1312
macro_rules! simd_dispatch {
1413
(
@@ -58,28 +57,3 @@ macro_rules! simd_dispatch {
5857
}
5958
};
6059
}
61-
62-
#[cfg(not(feature = "std"))]
63-
#[macro_export]
64-
macro_rules! simd_dispatch {
65-
(
66-
$( #[$meta:meta] )* $vis:vis
67-
$func:ident ( level $( , $arg:ident : $ty:ty $(,)? )* ) $( -> $ret:ty )?
68-
= $inner:ident
69-
) => {
70-
$( #[$meta] )* $vis
71-
fn $func(level: $crate::Level $(, $arg: $ty )*) $( -> $ret )? {
72-
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
73-
#[inline]
74-
unsafe fn inner_wasm_simd128(simd128: $crate::wasm32::WasmSimd128 $( , $arg: $ty )* ) $( -> $ret )? {
75-
$inner( simd128 $( , $arg )* )
76-
}
77-
match level {
78-
Level::Fallback(fb) => $inner(fb $( , $arg )* ),
79-
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
80-
Level::WasmSimd128(wasm) => unsafe { inner_wasm_simd128 (wasm $( , $arg )* ) }
81-
_ => unreachable!()
82-
}
83-
}
84-
};
85-
}

0 commit comments

Comments
 (0)