diff --git a/crates/std_detect/README.md b/crates/std_detect/README.md index 04bc6a665e..b9146d42c9 100644 --- a/crates/std_detect/README.md +++ b/crates/std_detect/README.md @@ -5,7 +5,7 @@ The private `std::detect` module implements run-time feature detection in Rust's standard library. This allows detecting whether the CPU the binary runs on supports certain features, like SIMD instructions. -# Usage +# Usage `std::detect` APIs are available as part of `libstd`. Prefer using it via the standard library than through this crate. Unstable features of `std::detect` are @@ -19,7 +19,7 @@ from the platform. You can then manually include `std_detect` as a dependency to get similar run-time feature detection support than the one offered by Rust's standard library. We intend to make `std_detect` more flexible and configurable in this -regard to better serve the needs of `#[no_std]` targets. +regard to better serve the needs of `#[no_std]` targets. # Features @@ -53,8 +53,8 @@ crate from working on applications in which `std` is not available. [`cupid`](https://crates.io/crates/cupid) crate. * Linux/Android: - * `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `riscv{32,64}`, `loongarch64`: `std_detect` - supports these on Linux by querying ELF auxiliary vectors (using `getauxval` + * `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `riscv{32,64}`, `loongarch64`, `s390x`: + `std_detect` supports these on Linux by querying ELF auxiliary vectors (using `getauxval` when available), and if that fails, by querying `/proc/cpuinfo`. * `arm64`: partial support for doing run-time feature detection by directly querying `mrs` is implemented for Linux >= 4.11, but not enabled by default. diff --git a/crates/std_detect/src/detect/arch/mod.rs b/crates/std_detect/src/detect/arch/mod.rs index d0f5fab749..ff00c202de 100644 --- a/crates/std_detect/src/detect/arch/mod.rs +++ b/crates/std_detect/src/detect/arch/mod.rs @@ -21,6 +21,8 @@ mod mips; mod mips64; #[macro_use] mod loongarch; +#[macro_use] +mod s390x; cfg_if! { if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { @@ -50,6 +52,9 @@ cfg_if! { } else if #[cfg(target_arch = "loongarch64")] { #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] pub use loongarch::*; + } else if #[cfg(target_arch = "s390x")] { + #[unstable(feature = "stdarch_s390x_feature_detection", issue = "135413")] + pub use s390x::*; } else { // Unimplemented architecture: #[doc(hidden)] diff --git a/crates/std_detect/src/detect/arch/s390x.rs b/crates/std_detect/src/detect/arch/s390x.rs new file mode 100644 index 0000000000..be62aeb96a --- /dev/null +++ b/crates/std_detect/src/detect/arch/s390x.rs @@ -0,0 +1,12 @@ +//! Run-time feature detection on s390x. + +features! { + @TARGET: s390x; + @CFG: target_arch = "s390x"; + @MACRO_NAME: is_s390x_feature_detected; + @MACRO_ATTRS: + /// Checks if `s390x` feature is enabled. + #[unstable(feature = "stdarch_s390x_feature_detection", issue = "135413")] + @FEATURE: #[unstable(feature = "stdarch_s390x_feature_detection", issue = "135413")] vector: "vector"; + /// s390x vector facility +} diff --git a/crates/std_detect/src/detect/mod.rs b/crates/std_detect/src/detect/mod.rs index ab247a303e..a61400a555 100644 --- a/crates/std_detect/src/detect/mod.rs +++ b/crates/std_detect/src/detect/mod.rs @@ -101,6 +101,7 @@ pub fn features() -> impl Iterator { target_arch = "mips", target_arch = "mips64", target_arch = "loongarch64", + target_arch = "s390x", ))] { (0_u8..Feature::_last as u8).map(|discriminant: u8| { #[allow(bindings_with_variant_name)] // RISC-V has Feature::f diff --git a/crates/std_detect/src/detect/os/linux/auxvec.rs b/crates/std_detect/src/detect/os/linux/auxvec.rs index 61b6e2df1a..79720a0dee 100644 --- a/crates/std_detect/src/detect/os/linux/auxvec.rs +++ b/crates/std_detect/src/detect/os/linux/auxvec.rs @@ -10,7 +10,8 @@ pub(crate) const AT_HWCAP: usize = 16; target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] pub(crate) const AT_HWCAP2: usize = 26; @@ -26,7 +27,8 @@ pub(crate) struct AuxVec { target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] pub hwcap2: usize, } @@ -98,7 +100,8 @@ pub(crate) fn auxv() -> Result { target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] { if let Ok(hwcap2) = getauxval(AT_HWCAP2) { @@ -146,7 +149,8 @@ pub(crate) fn auxv() -> Result { target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] { let hwcap = unsafe { libc::getauxval(AT_HWCAP as libc::c_ulong) as usize }; @@ -242,7 +246,8 @@ fn auxv_from_buf(buf: &[usize]) -> Result { target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] { let mut hwcap = None; @@ -275,7 +280,8 @@ mod tests { #[cfg(any( target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] #[test] fn auxv_crate() { @@ -290,7 +296,8 @@ mod tests { target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] { if let Ok(hwcap2) = getauxval(AT_HWCAP2) { @@ -365,7 +372,8 @@ mod tests { target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] #[test] #[cfg(feature = "std_detect_file_io")] diff --git a/crates/std_detect/src/detect/os/linux/mod.rs b/crates/std_detect/src/detect/os/linux/mod.rs index 3057738520..fbe6bea93c 100644 --- a/crates/std_detect/src/detect/os/linux/mod.rs +++ b/crates/std_detect/src/detect/os/linux/mod.rs @@ -57,6 +57,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "loongarch64")] { mod loongarch; pub(crate) use self::loongarch::detect_features; + } else if #[cfg(target_arch = "s390x")] { + mod s390x; + pub(crate) use self::s390x::detect_features; } else { use crate::detect::cache; /// Performs run-time feature detection. diff --git a/crates/std_detect/src/detect/os/linux/s390x.rs b/crates/std_detect/src/detect/os/linux/s390x.rs new file mode 100644 index 0000000000..91ce55e94e --- /dev/null +++ b/crates/std_detect/src/detect/os/linux/s390x.rs @@ -0,0 +1,95 @@ +//! Run-time feature detection for s390x on Linux. + +use super::auxvec; +use crate::detect::{bit, cache, Feature}; + +/// Try to read the features from the auxiliary vector +pub(crate) fn detect_features() -> cache::Initializer { + if let Ok(auxv) = auxvec::auxv() { + let hwcap: AtHwcap = auxv.into(); + return hwcap.cache(); + } + + cache::Initializer::default() +} + +/// These values are part of the platform-specific [asm/elf.h][kernel], and are a selection of the +/// fields found in the [Facility Indications]. +/// +/// [Facility Indications]: https://www.ibm.com/support/pages/sites/default/files/2021-05/SA22-7871-10.pdf#page=63 +/// [kernel]: https://github.com/torvalds/linux/blob/b62cef9a5c673f1b8083159f5dc03c1c5daced2f/arch/s390/include/asm/elf.h#L129 +#[derive(Debug, Default, PartialEq)] +struct AtHwcap { + esan3: bool, + zarch: bool, + stfle: bool, + msa: bool, + ldisp: bool, + eimm: bool, + dfp: bool, + hpage: bool, + etf3eh: bool, + high_gprs: bool, + te: bool, + vxrs: bool, + vxrs_bcd: bool, + vxrs_ext: bool, + gs: bool, + vxrs_ext2: bool, + vxrs_pde: bool, + sort: bool, + dflt: bool, + vxrs_pde2: bool, + nnpa: bool, + pci_mio: bool, + sie: bool, +} + +impl From for AtHwcap { + /// Reads AtHwcap from the auxiliary vector. + fn from(auxv: auxvec::AuxVec) -> Self { + AtHwcap { + esan3: bit::test(auxv.hwcap, 0), + zarch: bit::test(auxv.hwcap, 1), + stfle: bit::test(auxv.hwcap, 2), + msa: bit::test(auxv.hwcap, 3), + ldisp: bit::test(auxv.hwcap, 4), + eimm: bit::test(auxv.hwcap, 5), + dfp: bit::test(auxv.hwcap, 6), + hpage: bit::test(auxv.hwcap, 7), + etf3eh: bit::test(auxv.hwcap, 8), + high_gprs: bit::test(auxv.hwcap, 9), + te: bit::test(auxv.hwcap, 10), + vxrs: bit::test(auxv.hwcap, 11), + vxrs_bcd: bit::test(auxv.hwcap, 12), + vxrs_ext: bit::test(auxv.hwcap, 13), + gs: bit::test(auxv.hwcap, 14), + vxrs_ext2: bit::test(auxv.hwcap, 15), + vxrs_pde: bit::test(auxv.hwcap, 16), + sort: bit::test(auxv.hwcap, 17), + dflt: bit::test(auxv.hwcap, 18), + vxrs_pde2: bit::test(auxv.hwcap, 19), + nnpa: bit::test(auxv.hwcap, 20), + pci_mio: bit::test(auxv.hwcap, 21), + sie: bit::test(auxv.hwcap, 22), + } + } +} + +impl AtHwcap { + /// Initializes the cache from the feature bits. + fn cache(self) -> cache::Initializer { + let mut value = cache::Initializer::default(); + { + let mut enable_feature = |f, enable| { + if enable { + value.set(f as u32); + } + }; + + // bit 129 of the extended facility list + enable_feature(Feature::vector, self.vxrs); + } + value + } +} diff --git a/crates/std_detect/src/lib.rs b/crates/std_detect/src/lib.rs index 881e12cf9c..ab1b77bad5 100644 --- a/crates/std_detect/src/lib.rs +++ b/crates/std_detect/src/lib.rs @@ -12,6 +12,7 @@ //! * `powerpc`: [`is_powerpc_feature_detected`] //! * `powerpc64`: [`is_powerpc64_feature_detected`] //! * `loongarch`: [`is_loongarch_feature_detected`] +//! * `s390x`: [`is_s390x_feature_detected`] #![unstable(feature = "stdarch_internal", issue = "none")] #![feature(staged_api, doc_cfg, allow_internal_unstable)] diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index 6ed612a596..69b7075bb1 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -4,6 +4,7 @@ #![cfg_attr(target_arch = "aarch64", feature(stdarch_aarch64_feature_detection))] #![cfg_attr(target_arch = "powerpc", feature(stdarch_powerpc_feature_detection))] #![cfg_attr(target_arch = "powerpc64", feature(stdarch_powerpc_feature_detection))] +#![cfg_attr(target_arch = "s390x", feature(stdarch_s390x_feature_detection))] #![cfg_attr( any(target_arch = "x86", target_arch = "x86_64"), feature(sha512_sm_x86, x86_amx_intrinsics, xop_target_feature) @@ -18,7 +19,8 @@ target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ), macro_use )] @@ -240,6 +242,12 @@ fn powerpc64_linux_or_freebsd() { println!("power8: {}", is_powerpc64_feature_detected!("power8")); } +#[test] +#[cfg(all(target_arch = "s390x", target_os = "linux",))] +fn s390x_linux() { + println!("vector: {}", is_s390x_feature_detected!("vector")); +} + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn x86_all() { diff --git a/crates/std_detect/tests/macro_trailing_commas.rs b/crates/std_detect/tests/macro_trailing_commas.rs index 9f6ef074d0..4769a5e318 100644 --- a/crates/std_detect/tests/macro_trailing_commas.rs +++ b/crates/std_detect/tests/macro_trailing_commas.rs @@ -7,7 +7,8 @@ target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ), feature(stdarch_internal) )] @@ -15,6 +16,7 @@ #![cfg_attr(target_arch = "aarch64", feature(stdarch_aarch64_feature_detection))] #![cfg_attr(target_arch = "powerpc", feature(stdarch_powerpc_feature_detection))] #![cfg_attr(target_arch = "powerpc64", feature(stdarch_powerpc_feature_detection))] +#![cfg_attr(target_arch = "s390x", feature(stdarch_s390x_feature_detection))] #![allow(clippy::unwrap_used, clippy::use_debug, clippy::print_stdout)] #[cfg(any( @@ -24,7 +26,8 @@ target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "s390x", ))] #[macro_use] extern crate std_detect; @@ -60,6 +63,13 @@ fn powerpc64_linux() { let _ = is_powerpc64_feature_detected!("altivec",); } +#[test] +#[cfg(all(target_arch = "s390x", target_os = "linux"))] +fn s390x_linux() { + let _ = is_s390x_feature_detected!("vector"); + let _ = is_s390x_feature_detected!("vector",); +} + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn x86_all() {