diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index dabab667ee968..2dbf7d1ca89a9 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -317,6 +317,7 @@ #![feature(panic_info_message)] #![feature(panic_internals)] #![feature(panic_unwind)] +#![feature(platform_intrinsics)] #![feature(pin_static_ref)] #![feature(portable_simd)] #![feature(prelude_import)] @@ -471,8 +472,6 @@ pub use core::pin; pub use core::ptr; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; -#[unstable(feature = "portable_simd", issue = "86656")] -pub use core::simd; #[unstable(feature = "async_stream", issue = "79024")] pub use core::stream; #[stable(feature = "i128", since = "1.26.0")] @@ -519,6 +518,10 @@ pub mod time; #[unstable(feature = "once_cell", issue = "74465")] pub mod lazy; +#[cfg(not(all(miri, doctest)))] // Miri does not support all SIMD intrinsics +#[unstable(feature = "portable_simd", issue = "86656")] +pub mod simd; + #[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. diff --git a/library/std/src/simd.rs b/library/std/src/simd.rs new file mode 100644 index 0000000000000..c2452638d9d67 --- /dev/null +++ b/library/std/src/simd.rs @@ -0,0 +1,128 @@ +//! This module primarily re-exports [`core::simd`], except for the [`Float`] +//! trait, which exists to allow usage of certain functions requiring +//! runtime support. +//! +use crate::sealed::Sealed; +pub use core::simd::*; + +// "platform intrinsics" are essentially "codegen intrinsics" +extern "platform-intrinsic" { + // ceil + fn simd_ceil(x: T) -> T; + + // floor + fn simd_floor(x: T) -> T; + + // round + fn simd_round(x: T) -> T; + + // trunc + fn simd_trunc(x: T) -> T; + + // fsqrt + fn simd_fsqrt(x: T) -> T; + + // fma + fn simd_fma(x: T, y: T, z: T) -> T; +} + +/// This trait provides a possibly-temporary implementation of float functions +/// that may, in the absence of hardware support, canonicalize to calling an +/// operating system's `math.h` dynamically-loaded library (also known as a +/// shared object). As these require runtime support, they should only appear +/// in binaries built assuming OS support: `std`. +/// +/// However, there is no reason SIMD types, in general, need OS support, +/// as for many architectures an embedded binary may simply configure that +/// support itself. This means these types must be visible in `core` +/// but have these functions available in `std`. +/// +/// [`f32`] and [`f64`] achieve a similar trick by using "lang items", but +/// due to compiler limitations, it is harder to implement this approach for +/// abstract data types like [`Simd`]. From that need, this trait is born. +/// +/// It is possible this trait will be replaced in some manner in the future, +/// when either the compiler or its supporting runtime functions are improved. +/// For now this trait is available to permit experimentation with SIMD float +/// operations that may lack hardware support, such as `mul_add`. +pub trait Float: Sealed + Sized { + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, + /// yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target + /// architecture has a dedicated `fma` CPU instruction. However, this is not always + /// true, and will be heavily dependent on designing algorithms with specific target + /// hardware in mind. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn mul_add(self, a: Self, b: Self) -> Self { + unsafe { simd_fma(self, a, b) } + } + + /// Produces a vector where every lane has the square root value + /// of the equivalently-indexed lane in `self` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn sqrt(self) -> Self { + unsafe { simd_fsqrt(self) } + } + + /// Returns the smallest integer greater than or equal to each lane. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn ceil(self) -> Self { + unsafe { simd_ceil(self) } + } + + /// Returns the largest integer value less than or equal to each lane. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn floor(self) -> Self { + unsafe { simd_floor(self) } + } + + /// Rounds to the nearest integer value. Ties round toward zero. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn round(self) -> Self { + unsafe { simd_round(self) } + } + + /// Returns the floating point's integer value, with its fractional part removed. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn trunc(self) -> Self { + unsafe { simd_trunc(self) } + } + + /// Returns the floating point's fractional value, with its integer part removed. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn fract(self) -> Self; +} + +impl Sealed for Simd where LaneCount: SupportedLaneCount {} +impl Sealed for Simd where LaneCount: SupportedLaneCount {} + +// We can safely just use all the defaults. +impl Float for Simd +where + LaneCount: SupportedLaneCount, +{ + /// Returns the floating point's fractional value, with its integer part removed. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } +} +impl Float for Simd +where + LaneCount: SupportedLaneCount, +{ + /// Returns the floating point's fractional value, with its integer part removed. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } +} diff --git a/src/test/ui/simd/libm_std_can_float.rs b/src/test/ui/simd/libm_std_can_float.rs new file mode 100644 index 0000000000000..560f9022feb32 --- /dev/null +++ b/src/test/ui/simd/libm_std_can_float.rs @@ -0,0 +1,23 @@ +// run-pass + +// This is the converse of the other libm test. +#![feature(portable_simd)] +use std::simd::f32x4; +use std::simd::Float; + +// For SIMD float ops, the LLIR version which is used to implement the portable +// forms of them may become calls to math.h AKA libm. So, we can't guarantee +// we can compile them for #![no_std] crates. +// +// However, we can expose some of these ops via an extension trait. +fn main() { + let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); + let x2 = x + x; + let _xc = x.ceil(); + let _xf = x.floor(); + let _xr = x.round(); + let _xt = x.trunc(); + let _xfma = x.mul_add(x, x); + let _xsqrt = x.sqrt(); + let _ = x2.abs() * x2; +} diff --git a/src/test/ui/simd/portable-intrinsics-arent-exposed.rs b/src/test/ui/simd/portable-intrinsics-arent-exposed.rs index 4d7590323550c..667c8b67b1d46 100644 --- a/src/test/ui/simd/portable-intrinsics-arent-exposed.rs +++ b/src/test/ui/simd/portable-intrinsics-arent-exposed.rs @@ -1,7 +1,8 @@ // May not matter, since people can use them with a nightly feature. // However this tests to guarantee they don't leak out via portable_simd, // and thus don't accidentally get stabilized. -use std::simd::intrinsics; //~ERROR E0603 +use core::simd::intrinsics; //~ERROR E0433 +use std::simd::intrinsics; //~ERROR E0432 fn main() { () diff --git a/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr b/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr index 9ac73eca19345..f568aa0429525 100644 --- a/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr +++ b/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr @@ -1,15 +1,16 @@ -error[E0603]: module `intrinsics` is private - --> $DIR/portable-intrinsics-arent-exposed.rs:4:16 +error[E0433]: failed to resolve: maybe a missing crate `core`? + --> $DIR/portable-intrinsics-arent-exposed.rs:4:5 | -LL | use std::simd::intrinsics; - | ^^^^^^^^^^ private module - | -note: the module `intrinsics` is defined here - --> $SRC_DIR/core/src/lib.rs:LL:COL +LL | use core::simd::intrinsics; + | ^^^^ maybe a missing crate `core`? + +error[E0432]: unresolved import `std::simd::intrinsics` + --> $DIR/portable-intrinsics-arent-exposed.rs:5:5 | -LL | pub use crate::core_simd::simd::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | use std::simd::intrinsics; + | ^^^^^^^^^^^^^^^^^^^^^ no `intrinsics` in `simd` -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0603`. +Some errors have detailed explanations: E0432, E0433. +For more information about an error, try `rustc --explain E0432`.