From 1ece49d0d3e02265c1a9ee5d526de512dc5bb130 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 31 Mar 2025 10:35:30 +0900 Subject: [PATCH 1/6] std_detect: Use riscv_hwprobe on RISC-V Linux/Android --- crates/std_detect/README.md | 5 +- crates/std_detect/src/detect/arch/riscv.rs | 83 ++++++ .../std_detect/src/detect/os/linux/riscv.rs | 280 +++++++++++++++++- crates/std_detect/tests/cpu-detection.rs | 97 ++++++ 4 files changed, 457 insertions(+), 8 deletions(-) diff --git a/crates/std_detect/README.md b/crates/std_detect/README.md index 5191b2f665..0053d777aa 100644 --- a/crates/std_detect/README.md +++ b/crates/std_detect/README.md @@ -56,11 +56,14 @@ 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`, `s390x`: + * `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `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. + * `riscv{32,64}`: + `std_detect` supports these on Linux by querying `riscv_hwprobe`, and + by querying ELF auxiliary vectors (using `getauxval` when available). * FreeBSD: * `arm32`, `powerpc64`: `std_detect` supports these on FreeBSD by querying ELF diff --git a/crates/std_detect/src/detect/arch/riscv.rs b/crates/std_detect/src/detect/arch/riscv.rs index 2368131fea..546c9ceedb 100644 --- a/crates/std_detect/src/detect/arch/riscv.rs +++ b/crates/std_detect/src/detect/arch/riscv.rs @@ -125,6 +125,10 @@ features! { @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] a: "a"; /// "A" Extension for Atomic Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zacas: "zacas"; + /// Atomic Compare-And-Swap Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zawrs: "zawrs"; + /// Wait on Reservation Set @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zam: "zam"; without cfg check: true; /// "Zam" Extension for Misaligned Atomics @@ -194,6 +198,85 @@ features! { @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] v: "v"; /// "V" Extension for Vector Operations + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfh: "zvfh"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Extension for Half-Precision Floating-Point + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfhmin: "zvfhmin"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Extension for Minimal Half-Precision Floating-Point + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32x: "zve32x"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Extensions for Embedded Processors + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32f: "zve32f"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Extensions for Embedded Processors + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64x: "zve64x"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Extensions for Embedded Processors + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64f: "zve64f"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Extensions for Embedded Processors + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64d: "zve64d"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Extensions for Embedded Processors + + // FIXME: we can implement this by getting the current vlen + // zvl*b: Minimum Vector Length Standard Extensions + + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkb: "zvkb"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Bit-manipulation used in Cryptography + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbb: "zvbb"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector basic bit-manipulation instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbc: "zvbc"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Carryless Multiplication + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkg: "zvkg"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector GCM instructions for Cryptography + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkned: "zvkned"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector AES Encryption & Decryption (Single Round) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknha: "zvknha"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector SHA-2 (SHA-256 only)) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknhb: "zvknhb"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector SHA-2 (SHA-256 and SHA-512) + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksed: "zvksed"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// SM4 Block Cipher Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksh: "zvksh"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// SM3 Hash Function Instructions + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkn: "zvkn"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Shorthand for 'Zvkned', 'Zvknhb', 'Zvkb', and 'Zvkt' + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknc: "zvknc"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Shorthand for 'Zvkn' and 'Zvbc' + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkng: "zvkng"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Shorthand for 'Zvkn' and 'Zvkg' + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvks: "zvks"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Shorthand for 'Zvksed', 'Zvksh', 'Zvkb', and 'Zvkt' + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksc: "zvksc"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Shorthand for 'Zvks' and 'Zvbc' + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksg: "zvksg"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Shorthand for 'Zvks' and 'Zvkg' + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkt: "zvkt"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Vector Data-Independent Execution Latency + + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_scalar_mem: "unaligned-scalar-mem"; + /// Has reasonably performant unaligned scalar + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_vector_mem: "unaligned-vector-mem"; + without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 + /// Has reasonably performant unaligned vector @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svnapot: "svnapot"; without cfg check: true; diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 2a9671d75d..8a62be980b 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -1,11 +1,268 @@ //! Run-time feature detection for RISC-V on Linux. +//! +//! On RISC-V, detection using auxv only supports single-letter extensions. +//! So, we use riscv_hwprobe that supports multi-letter extensions if available. +//! + +use core::ptr; use super::auxvec; use crate::detect::{Feature, bit, cache}; -/// Read list of supported features from the auxiliary vector. +// See +// for riscv_hwprobe struct and RISCV_HWPROBE_* constants. + +#[repr(C)] +struct riscv_hwprobe { + key: i64, + value: u64, +} + +#[allow(non_upper_case_globals)] +const __NR_riscv_hwprobe: libc::c_long = 258; + +const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3; +const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0; + +const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4; +const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0; +const RISCV_HWPROBE_IMA_C: u64 = 1 << 1; +const RISCV_HWPROBE_IMA_V: u64 = 1 << 2; +const RISCV_HWPROBE_EXT_ZBA: u64 = 1 << 3; +const RISCV_HWPROBE_EXT_ZBB: u64 = 1 << 4; +const RISCV_HWPROBE_EXT_ZBS: u64 = 1 << 5; +// const RISCV_HWPROBE_EXT_ZICBOZ: u64 = 1 << 6; +const RISCV_HWPROBE_EXT_ZBC: u64 = 1 << 7; +const RISCV_HWPROBE_EXT_ZBKB: u64 = 1 << 8; +const RISCV_HWPROBE_EXT_ZBKC: u64 = 1 << 9; +const RISCV_HWPROBE_EXT_ZBKX: u64 = 1 << 10; +const RISCV_HWPROBE_EXT_ZKND: u64 = 1 << 11; +const RISCV_HWPROBE_EXT_ZKNE: u64 = 1 << 12; +const RISCV_HWPROBE_EXT_ZKNH: u64 = 1 << 13; +const RISCV_HWPROBE_EXT_ZKSED: u64 = 1 << 14; +const RISCV_HWPROBE_EXT_ZKSH: u64 = 1 << 15; +const RISCV_HWPROBE_EXT_ZKT: u64 = 1 << 16; +const RISCV_HWPROBE_EXT_ZVBB: u64 = 1 << 17; +const RISCV_HWPROBE_EXT_ZVBC: u64 = 1 << 18; +const RISCV_HWPROBE_EXT_ZVKB: u64 = 1 << 19; +const RISCV_HWPROBE_EXT_ZVKG: u64 = 1 << 20; +const RISCV_HWPROBE_EXT_ZVKNED: u64 = 1 << 21; +const RISCV_HWPROBE_EXT_ZVKNHA: u64 = 1 << 22; +const RISCV_HWPROBE_EXT_ZVKNHB: u64 = 1 << 23; +const RISCV_HWPROBE_EXT_ZVKSED: u64 = 1 << 24; +const RISCV_HWPROBE_EXT_ZVKSH: u64 = 1 << 25; +const RISCV_HWPROBE_EXT_ZVKT: u64 = 1 << 26; +const RISCV_HWPROBE_EXT_ZFH: u64 = 1 << 27; +const RISCV_HWPROBE_EXT_ZFHMIN: u64 = 1 << 28; +// const RISCV_HWPROBE_EXT_ZIHINTNTL: u64 = 1 << 29; +const RISCV_HWPROBE_EXT_ZVFH: u64 = 1 << 30; +const RISCV_HWPROBE_EXT_ZVFHMIN: u64 = 1 << 31; +// const RISCV_HWPROBE_EXT_ZFA: u64 = 1 << 32; +const RISCV_HWPROBE_EXT_ZTSO: u64 = 1 << 33; +const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34; +// const RISCV_HWPROBE_EXT_ZICOND: u64 = 1 << 35; +const RISCV_HWPROBE_EXT_ZIHINTPAUSE: u64 = 1 << 36; +const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37; +const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38; +const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39; +const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40; +const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41; +// const RISCV_HWPROBE_EXT_ZIMOP: u64 = 1 << 42; +// const RISCV_HWPROBE_EXT_ZCA: u64 = 1 << 43; +// const RISCV_HWPROBE_EXT_ZCB: u64 = 1 << 44; +// const RISCV_HWPROBE_EXT_ZCD: u64 = 1 << 45; +// const RISCV_HWPROBE_EXT_ZCF: u64 = 1 << 46; +// const RISCV_HWPROBE_EXT_ZCMOP: u64 = 1 << 47; +const RISCV_HWPROBE_EXT_ZAWRS: u64 = 1 << 48; +// const RISCV_HWPROBE_EXT_SUPM: u64 = 1 << 49; + +const RISCV_HWPROBE_KEY_CPUPERF_0: i64 = 5; +const RISCV_HWPROBE_MISALIGNED_FAST: u64 = 3; +const RISCV_HWPROBE_MISALIGNED_MASK: u64 = 7; + +const RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF: i64 = 10; +const RISCV_HWPROBE_MISALIGNED_VECTOR_FAST: u64 = 3; + +// syscall returns an unsupported error if riscv_hwprobe is not supported, +// so we can safely use this function on older versions of Linux. +fn _riscv_hwprobe(out: &mut [riscv_hwprobe]) -> bool { + unsafe fn __riscv_hwprobe( + pairs: *mut riscv_hwprobe, + pair_count: libc::size_t, + cpu_set_size: libc::size_t, + cpus: *mut libc::c_ulong, + flags: libc::c_uint, + ) -> libc::c_long { + unsafe { + libc::syscall( + __NR_riscv_hwprobe, + pairs, + pair_count, + cpu_set_size, + cpus, + flags, + ) + } + } + + let len = out.len(); + unsafe { __riscv_hwprobe(out.as_mut_ptr(), len, 0, ptr::null_mut(), 0) == 0 } +} + +/// Read list of supported features from riscv_hwprobe or the auxiliary vector. pub(crate) fn detect_features() -> cache::Initializer { let mut value = cache::Initializer::default(); + + let mut out = [ + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_BASE_BEHAVIOR, + value: 0, + }, + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_IMA_EXT_0, + value: 0, + }, + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_CPUPERF_0, + value: 0, + }, + riscv_hwprobe { + key: RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF, + value: 0, + }, + ]; + if _riscv_hwprobe(&mut out) { + let mut enable_feature = |feature, enable| { + if enable { + value.set(feature as u32); + } + }; + if out[0].key != -1 { + let base_behavior = out[0].value; + let ima = base_behavior & RISCV_HWPROBE_BASE_BEHAVIOR_IMA != 0; + // If future RV128I is supported, implement with `enable_feature` here + #[cfg(target_arch = "riscv32")] + enable_feature(Feature::rv32i, ima); + #[cfg(target_arch = "riscv64")] + enable_feature(Feature::rv64i, ima); + enable_feature(Feature::m, ima); + enable_feature(Feature::a, ima); + } + if out[1].key != -1 { + let ima_ext_0 = out[1].value; + let fd = ima_ext_0 & RISCV_HWPROBE_IMA_FD != 0; + enable_feature(Feature::f, fd); + enable_feature(Feature::d, fd); + enable_feature(Feature::zicsr, fd); // implied by f + enable_feature(Feature::c, ima_ext_0 & RISCV_HWPROBE_IMA_C != 0); + // enable_feature(Feature::zicboz, ima_ext_0 & RISCV_HWPROBE_EXT_ZICBOZ != 0); + enable_feature(Feature::zfh, ima_ext_0 & RISCV_HWPROBE_EXT_ZFH != 0); + enable_feature(Feature::zfhmin, ima_ext_0 & RISCV_HWPROBE_EXT_ZFHMIN != 0); + // enable_feature(Feature::zihintntl, ima_ext_0 & RISCV_HWPROBE_EXT_ZIHINTNTL != 0); + // enable_feature(Feature::zfa, ima_ext_0 & RISCV_HWPROBE_EXT_ZFA != 0); + enable_feature(Feature::ztso, ima_ext_0 & RISCV_HWPROBE_EXT_ZTSO != 0); + enable_feature(Feature::zacas, ima_ext_0 & RISCV_HWPROBE_EXT_ZACAS != 0); + // enable_feature(Feature::zicond, ima_ext_0 & RISCV_HWPROBE_EXT_ZICOND != 0); + enable_feature( + Feature::zihintpause, + ima_ext_0 & RISCV_HWPROBE_EXT_ZIHINTPAUSE != 0, + ); + // enable_feature(Feature::zimop, ima_ext_0 & RISCV_HWPROBE_EXT_ZIMOP != 0); + // enable_feature(Feature::zca, ima_ext_0 & RISCV_HWPROBE_EXT_ZCA != 0); + // enable_feature(Feature::zcb, ima_ext_0 & RISCV_HWPROBE_EXT_ZCB != 0); + // enable_feature(Feature::zcd, ima_ext_0 & RISCV_HWPROBE_EXT_ZCD != 0); + // enable_feature(Feature::zcf, ima_ext_0 & RISCV_HWPROBE_EXT_ZCF != 0); + // enable_feature(Feature::zcmop, ima_ext_0 & RISCV_HWPROBE_EXT_ZCMOP != 0); + enable_feature(Feature::zawrs, ima_ext_0 & RISCV_HWPROBE_EXT_ZAWRS != 0); + // enable_feature(Feature::supm, ima_ext_0 & RISCV_HWPROBE_EXT_SUPM != 0); + // Bit-Manipulation ISA extensions + enable_feature(Feature::zba, ima_ext_0 & RISCV_HWPROBE_EXT_ZBA != 0); + enable_feature(Feature::zbb, ima_ext_0 & RISCV_HWPROBE_EXT_ZBB != 0); + enable_feature(Feature::zbs, ima_ext_0 & RISCV_HWPROBE_EXT_ZBS != 0); + enable_feature(Feature::zbc, ima_ext_0 & RISCV_HWPROBE_EXT_ZBC != 0); + // Scalar Crypto ISA extensions + let zbkb = ima_ext_0 & RISCV_HWPROBE_EXT_ZBKB != 0; + enable_feature(Feature::zbkb, zbkb); + let zbkc = ima_ext_0 & RISCV_HWPROBE_EXT_ZBKC != 0; + enable_feature(Feature::zbkc, zbkc); + let zbkx = ima_ext_0 & RISCV_HWPROBE_EXT_ZBKX != 0; + enable_feature(Feature::zbkx, zbkx); + let zknd = ima_ext_0 & RISCV_HWPROBE_EXT_ZKND != 0; + enable_feature(Feature::zknd, zknd); + let zkne = ima_ext_0 & RISCV_HWPROBE_EXT_ZKNE != 0; + enable_feature(Feature::zkne, zkne); + let zknh = ima_ext_0 & RISCV_HWPROBE_EXT_ZKNH != 0; + enable_feature(Feature::zknh, zknh); + let zksed = ima_ext_0 & RISCV_HWPROBE_EXT_ZKSED != 0; + enable_feature(Feature::zksed, zksed); + let zksh = ima_ext_0 & RISCV_HWPROBE_EXT_ZKSH != 0; + enable_feature(Feature::zksh, zksh); + let zkt = ima_ext_0 & RISCV_HWPROBE_EXT_ZKT != 0; + enable_feature(Feature::zkt, zkt); + let zkn = zbkb & zbkc & zbkx & zkne & zknd & zknh; + enable_feature(Feature::zkn, zkn); + // enable_feature(Feature::zk, zkn & zkr & zkt); + enable_feature(Feature::zks, zbkb & zbkc & zbkx & zksed & zksh); + // Standard Vector Extensions + enable_feature(Feature::v, ima_ext_0 & RISCV_HWPROBE_IMA_V != 0); + enable_feature(Feature::zvfh, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFH != 0); + enable_feature(Feature::zvfhmin, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFHMIN != 0); + enable_feature(Feature::zve32x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32X != 0); + enable_feature(Feature::zve32f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32F != 0); + enable_feature(Feature::zve64x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64X != 0); + enable_feature(Feature::zve64f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64F != 0); + enable_feature(Feature::zve64d, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64D != 0); + // Vector Cryptography and Bit-manipulation Extensions + let zvbb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBB != 0; + enable_feature(Feature::zvbb, zvbb); + let zvbc = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBC != 0; + enable_feature(Feature::zvbc, zvbc); + let zvkb = zvbb || ima_ext_0 & RISCV_HWPROBE_EXT_ZVKB != 0; + enable_feature(Feature::zvkb, zvkb); + let zvkg = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKG != 0; + enable_feature(Feature::zvkg, zvkg); + let zvkned = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNED != 0; + enable_feature(Feature::zvkned, zvkned); + enable_feature(Feature::zvknha, ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHA != 0); + let zvknhb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHB != 0; + enable_feature(Feature::zvknhb, zvknhb); + let zvksed = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSED != 0; + enable_feature(Feature::zvksed, zvksed); + let zvksh = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSH != 0; + enable_feature(Feature::zvksh, zvksh); + let zvkt = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKT != 0; + enable_feature(Feature::zvkt, zvkt); + let zvkn = zvkned & zvknhb & zvkb & zvkt; + enable_feature(Feature::zvkn, zvkn); + enable_feature(Feature::zvknc, zvkn & zvbc); + enable_feature(Feature::zvkng, zvkn & zvkg); + let zvks = zvksed & zvksh & zvkb & zvkt; + enable_feature(Feature::zvks, zvks); + enable_feature(Feature::zvksc, zvks & zvbc); + enable_feature(Feature::zvksg, zvks & zvkg); + } + if out[2].key != -1 { + enable_feature( + Feature::unaligned_scalar_mem, + out[2].value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST, + ); + } + if out[3].key != -1 { + enable_feature( + Feature::unaligned_vector_mem, + out[3].value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST, + ); + } + // FIXME: should be enough with hwprobe only, but our code below checks h and e + // unavailable in neither uapi/asm/hwprobe.h nor uapi/asm/hwcap.h. + // https://github.com/torvalds/linux/blob/master/arch/riscv/include/uapi/asm/hwcap.h + // return value; + } + + // FIXME: As said in the above FIXME, we currently alway checks auxv too. + // // riscv_hwprobe requires Linux 6.4, so we fallback to auxv-based detection on + // // old Linux kernel. + let enable_feature = |value: &mut cache::Initializer, feature, enable| { if enable { value.set(feature as u32); @@ -22,6 +279,11 @@ pub(crate) fn detect_features() -> cache::Initializer { // The values are part of the platform-specific [asm/hwcap.h][hwcap] // // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/hwcap.h + // + // Note that there is no need to check b'v' - b'a' here for the case where riscv_hwprobe is unsupported, + // since both RISCV_HWPROBE_IMA_V and COMPAT_HWCAP_ISA_V are only supported on Linux 6.5+. + // https://github.com/torvalds/linux/commit/162e4df137c1fea6557fda3e4cdf5dc6ca6d5510 + // https://github.com/torvalds/linux/commit/dc6667a4e7e36f283bcd0264a0be55adae4d6f86 let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform #[allow(clippy::eq_op)] enable_feature( @@ -46,16 +308,20 @@ pub(crate) fn detect_features() -> cache::Initializer { ); let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); // If future RV128I is supported, implement with `enable_feature` here - #[cfg(target_pointer_width = "64")] + // Checking target_pointer_width instead of target_arch is incorrect since + // there are RV64ILP32* ABIs. + #[cfg(target_arch = "riscv64")] enable_feature(&mut value, Feature::rv64i, has_i); - #[cfg(target_pointer_width = "32")] + #[cfg(target_arch = "riscv32")] enable_feature(&mut value, Feature::rv32i, has_i); - #[cfg(target_pointer_width = "32")] + // FIXME: e is not exposed in any of asm/hwcap.h, uapi/asm/hwcap.h, uapi/asm/hwprobe.h + #[cfg(target_arch = "riscv32")] enable_feature( &mut value, Feature::rv32e, bit::test(auxv.hwcap, (b'e' - b'a').into()), ); + // FIXME: h is not exposed in uapi/asm/hwcap.h and uapi/asm/hwprobe.h enable_feature( &mut value, Feature::h, @@ -66,9 +332,9 @@ pub(crate) fn detect_features() -> cache::Initializer { Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into()), ); - // FIXME: Auxvec does not show supervisor feature support, but this mode may be useful - // to detect when Rust is used to write Linux kernel modules. - // These should be more than Auxvec way to detect supervisor features. + + // Neither hwprobe nor auxv supports detection of supervisor feature. + // Since target_os = "linux" is for user mode, their detection is not useful. value } diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index 2c9cd95d29..550cbb410b 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -5,6 +5,10 @@ any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_aarch64_feature_detection) )] +#![cfg_attr( + any(target_arch = "riscv32", target_arch = "riscv64"), + feature(stdarch_riscv_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))] @@ -15,6 +19,8 @@ target_arch = "arm", target_arch = "aarch64", target_arch = "arm64ec", + target_arch = "riscv32", + target_arch = "riscv64", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "s390x", @@ -220,6 +226,97 @@ fn aarch64_darwin() { println!("sha3: {:?}", is_aarch64_feature_detected!("sha3")); } +#[test] +#[cfg(all( + any(target_arch = "riscv32", target_arch = "riscv64"), + target_os = "linux" +))] +fn riscv_linux() { + println!("rv32i: {}", is_riscv_feature_detected!("rv32i")); + println!("rv32e: {}", is_riscv_feature_detected!("rv32e")); + println!("rv64i: {}", is_riscv_feature_detected!("rv64i")); + println!("rv128i: {}", is_riscv_feature_detected!("rv128i")); + println!("zicsr: {}", is_riscv_feature_detected!("zicsr")); + println!("zicntr: {}", is_riscv_feature_detected!("zicntr")); + println!("zihpm: {}", is_riscv_feature_detected!("zihpm")); + println!("zifencei: {}", is_riscv_feature_detected!("zifencei")); + println!("zihintpause: {}", is_riscv_feature_detected!("zihintpause")); + println!("m: {}", is_riscv_feature_detected!("m")); + println!("a: {}", is_riscv_feature_detected!("a")); + println!("zacas: {}", is_riscv_feature_detected!("zacas")); + println!("zawrs: {}", is_riscv_feature_detected!("zawrs")); + println!("zam: {}", is_riscv_feature_detected!("zam")); + println!("ztso: {}", is_riscv_feature_detected!("ztso")); + println!("f: {}", is_riscv_feature_detected!("f")); + println!("d: {}", is_riscv_feature_detected!("d")); + println!("q: {}", is_riscv_feature_detected!("q")); + println!("zfh: {}", is_riscv_feature_detected!("zfh")); + println!("zfhmin: {}", is_riscv_feature_detected!("zfhmin")); + println!("zfinx: {}", is_riscv_feature_detected!("zfinx")); + println!("zdinx: {}", is_riscv_feature_detected!("zdinx")); + println!("zhinx: {}", is_riscv_feature_detected!("zhinx")); + println!("zhinxmin: {}", is_riscv_feature_detected!("zhinxmin")); + println!("c: {}", is_riscv_feature_detected!("c")); + println!("zba: {}", is_riscv_feature_detected!("zba")); + println!("zbb: {}", is_riscv_feature_detected!("zbb")); + println!("zbc: {}", is_riscv_feature_detected!("zbc")); + println!("zbs: {}", is_riscv_feature_detected!("zbs")); + println!("zbkb: {}", is_riscv_feature_detected!("zbkb")); + println!("zbkc: {}", is_riscv_feature_detected!("zbkc")); + println!("zbkx: {}", is_riscv_feature_detected!("zbkx")); + println!("zknd: {}", is_riscv_feature_detected!("zknd")); + println!("zkne: {}", is_riscv_feature_detected!("zkne")); + println!("zknh: {}", is_riscv_feature_detected!("zknh")); + println!("zksed: {}", is_riscv_feature_detected!("zksed")); + println!("zksh: {}", is_riscv_feature_detected!("zksh")); + println!("zkr: {}", is_riscv_feature_detected!("zkr")); + println!("zksed: {}", is_riscv_feature_detected!("zksed")); + println!("zksh: {}", is_riscv_feature_detected!("zksh")); + println!("zkr: {}", is_riscv_feature_detected!("zkr")); + println!("zkn: {}", is_riscv_feature_detected!("zkn")); + println!("zks: {}", is_riscv_feature_detected!("zks")); + println!("zkt: {}", is_riscv_feature_detected!("zkt")); + println!("v: {}", is_riscv_feature_detected!("v")); + println!("zvfh: {}", is_riscv_feature_detected!("zvfh")); + println!("zvfhmin: {}", is_riscv_feature_detected!("zvfhmin")); + println!("zve32x: {}", is_riscv_feature_detected!("zve32x")); + println!("zve32f: {}", is_riscv_feature_detected!("zve32f")); + println!("zve64x: {}", is_riscv_feature_detected!("zve64x")); + println!("zve64f: {}", is_riscv_feature_detected!("zve64f")); + println!("zve64d: {}", is_riscv_feature_detected!("zve64d")); + println!("zvkb: {}", is_riscv_feature_detected!("zvkb")); + println!("zvbb: {}", is_riscv_feature_detected!("zvbb")); + println!("zvbc: {}", is_riscv_feature_detected!("zvbc")); + println!("zvkg: {}", is_riscv_feature_detected!("zvkg")); + println!("zvkned: {}", is_riscv_feature_detected!("zvkned")); + println!("zvknha: {}", is_riscv_feature_detected!("zvknha")); + println!("zvknhb: {}", is_riscv_feature_detected!("zvknhb")); + println!("zvksed: {}", is_riscv_feature_detected!("zvksed")); + println!("zvksh: {}", is_riscv_feature_detected!("zvksh")); + println!("zvkn: {}", is_riscv_feature_detected!("zvkn")); + println!("zvknc: {}", is_riscv_feature_detected!("zvknc")); + println!("zvkng: {}", is_riscv_feature_detected!("zvkng")); + println!("zvks: {}", is_riscv_feature_detected!("zvks")); + println!("zvksc: {}", is_riscv_feature_detected!("zvksc")); + println!("zvksg: {}", is_riscv_feature_detected!("zvksg")); + println!("zvkt: {}", is_riscv_feature_detected!("zvkt")); + println!( + "unaligned-scalar-mem: {}", + is_riscv_feature_detected!("unaligned-scalar-mem") + ); + println!( + "unaligned-vector-mem: {}", + is_riscv_feature_detected!("unaligned-vector-mem") + ); + println!("svnapot: {}", is_riscv_feature_detected!("svnapot")); + println!("svpbmt: {}", is_riscv_feature_detected!("svpbmt")); + println!("svinval: {}", is_riscv_feature_detected!("svinval")); + println!("h: {}", is_riscv_feature_detected!("h")); + println!("s: {}", is_riscv_feature_detected!("s")); + println!("j: {}", is_riscv_feature_detected!("j")); + println!("p: {}", is_riscv_feature_detected!("p")); +} + #[test] #[cfg(all(target_arch = "powerpc", target_os = "linux"))] fn powerpc_linux() { From 507193a5d74d44e8f9710fc89b38a6f89c3dfe12 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 2 Apr 2025 02:12:07 +0900 Subject: [PATCH 2/6] std_detect: Enable cfg check for RISC-V vector-related target features --- crates/std_detect/src/detect/arch/riscv.rs | 24 ---------------------- 1 file changed, 24 deletions(-) diff --git a/crates/std_detect/src/detect/arch/riscv.rs b/crates/std_detect/src/detect/arch/riscv.rs index 546c9ceedb..4d5a922629 100644 --- a/crates/std_detect/src/detect/arch/riscv.rs +++ b/crates/std_detect/src/detect/arch/riscv.rs @@ -199,83 +199,59 @@ features! { @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] v: "v"; /// "V" Extension for Vector Operations @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfh: "zvfh"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Extension for Half-Precision Floating-Point @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfhmin: "zvfhmin"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Extension for Minimal Half-Precision Floating-Point @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32x: "zve32x"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Extensions for Embedded Processors @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32f: "zve32f"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Extensions for Embedded Processors @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64x: "zve64x"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Extensions for Embedded Processors @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64f: "zve64f"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Extensions for Embedded Processors @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64d: "zve64d"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Extensions for Embedded Processors // FIXME: we can implement this by getting the current vlen // zvl*b: Minimum Vector Length Standard Extensions @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkb: "zvkb"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Bit-manipulation used in Cryptography @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbb: "zvbb"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector basic bit-manipulation instructions @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbc: "zvbc"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Carryless Multiplication @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkg: "zvkg"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector GCM instructions for Cryptography @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkned: "zvkned"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector AES Encryption & Decryption (Single Round) @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknha: "zvknha"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector SHA-2 (SHA-256 only)) @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknhb: "zvknhb"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector SHA-2 (SHA-256 and SHA-512) @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksed: "zvksed"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// SM4 Block Cipher Instructions @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksh: "zvksh"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// SM3 Hash Function Instructions @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkn: "zvkn"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Shorthand for 'Zvkned', 'Zvknhb', 'Zvkb', and 'Zvkt' @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknc: "zvknc"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Shorthand for 'Zvkn' and 'Zvbc' @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkng: "zvkng"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Shorthand for 'Zvkn' and 'Zvkg' @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvks: "zvks"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Shorthand for 'Zvksed', 'Zvksh', 'Zvkb', and 'Zvkt' @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksc: "zvksc"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Shorthand for 'Zvks' and 'Zvbc' @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksg: "zvksg"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Shorthand for 'Zvks' and 'Zvkg' @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkt: "zvkt"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Vector Data-Independent Execution Latency @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_scalar_mem: "unaligned-scalar-mem"; /// Has reasonably performant unaligned scalar @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_vector_mem: "unaligned-vector-mem"; - without cfg check: true; // FIXME: added in https://github.com/rust-lang/rust/pull/138742 /// Has reasonably performant unaligned vector @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svnapot: "svnapot"; From eeb0002bc622b20e0df3b9126b42b1d2b129fe0a Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 2 Apr 2025 02:42:17 +0900 Subject: [PATCH 3/6] std_detect: Remove RISC-V privileged features --- crates/std_detect/src/detect/arch/riscv.rs | 24 ------------------- .../std_detect/src/detect/os/linux/riscv.rs | 11 +-------- crates/std_detect/tests/cpu-detection.rs | 5 ---- 3 files changed, 1 insertion(+), 39 deletions(-) diff --git a/crates/std_detect/src/detect/arch/riscv.rs b/crates/std_detect/src/detect/arch/riscv.rs index 4d5a922629..b1d82f9b45 100644 --- a/crates/std_detect/src/detect/arch/riscv.rs +++ b/crates/std_detect/src/detect/arch/riscv.rs @@ -79,14 +79,6 @@ features! { /// * P: `"p"` /// * Zam: `"zam"` /// - /// Defined by Privileged Specification: - /// - /// * Supervisor: `"s"` - /// * Svnapot: `"svnapot"` - /// * Svpbmt: `"svpbmt"` - /// * Svinval: `"svinval"` - /// * Hypervisor: `"h"` - /// /// [ISA manual]: https://github.com/riscv/riscv-isa-manual/ #[stable(feature = "riscv_ratified", since = "1.78.0")] @@ -254,22 +246,6 @@ features! { @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_vector_mem: "unaligned-vector-mem"; /// Has reasonably performant unaligned vector - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svnapot: "svnapot"; - without cfg check: true; - /// "Svnapot" Extension for NAPOT Translation Contiguity - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svpbmt: "svpbmt"; - without cfg check: true; - /// "Svpbmt" Extension for Page-Based Memory Types - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] svinval: "svinval"; - without cfg check: true; - /// "Svinval" Extension for Fine-Grained Address-Translation Cache Invalidation - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] h: "h"; - without cfg check: true; - /// "H" Extension for Hypervisor Support - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] s: "s"; - without cfg check: true; - /// Supervisor-Level ISA @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] j: "j"; without cfg check: true; /// "J" Extension for Dynamically Translated Languages diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 8a62be980b..6848d69183 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -253,7 +253,7 @@ pub(crate) fn detect_features() -> cache::Initializer { out[3].value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST, ); } - // FIXME: should be enough with hwprobe only, but our code below checks h and e + // FIXME: should be enough with hwprobe only, but our code below checks e // unavailable in neither uapi/asm/hwprobe.h nor uapi/asm/hwcap.h. // https://github.com/torvalds/linux/blob/master/arch/riscv/include/uapi/asm/hwcap.h // return value; @@ -321,20 +321,11 @@ pub(crate) fn detect_features() -> cache::Initializer { Feature::rv32e, bit::test(auxv.hwcap, (b'e' - b'a').into()), ); - // FIXME: h is not exposed in uapi/asm/hwcap.h and uapi/asm/hwprobe.h - enable_feature( - &mut value, - Feature::h, - bit::test(auxv.hwcap, (b'h' - b'a').into()), - ); enable_feature( &mut value, Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into()), ); - // Neither hwprobe nor auxv supports detection of supervisor feature. - // Since target_os = "linux" is for user mode, their detection is not useful. - value } diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index 550cbb410b..6059be4781 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -308,11 +308,6 @@ fn riscv_linux() { "unaligned-vector-mem: {}", is_riscv_feature_detected!("unaligned-vector-mem") ); - println!("svnapot: {}", is_riscv_feature_detected!("svnapot")); - println!("svpbmt: {}", is_riscv_feature_detected!("svpbmt")); - println!("svinval: {}", is_riscv_feature_detected!("svinval")); - println!("h: {}", is_riscv_feature_detected!("h")); - println!("s: {}", is_riscv_feature_detected!("s")); println!("j: {}", is_riscv_feature_detected!("j")); println!("p: {}", is_riscv_feature_detected!("p")); } From 1474c1d9c0b567c2d304c7bb09e6c1a87e313db0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 2 Apr 2025 04:01:41 +0900 Subject: [PATCH 4/6] std_detect: Check hwcap to detect RISC-V V extension It reflects Vector enablement status, unlike hwprobe. --- .../std_detect/src/detect/os/linux/riscv.rs | 281 +++++++++--------- 1 file changed, 137 insertions(+), 144 deletions(-) diff --git a/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std_detect/src/detect/os/linux/riscv.rs index 6848d69183..ae364e8804 100644 --- a/crates/std_detect/src/detect/os/linux/riscv.rs +++ b/crates/std_detect/src/detect/os/linux/riscv.rs @@ -21,13 +21,13 @@ struct riscv_hwprobe { #[allow(non_upper_case_globals)] const __NR_riscv_hwprobe: libc::c_long = 258; -const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3; -const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0; +// const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3; +// const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0; const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4; -const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0; -const RISCV_HWPROBE_IMA_C: u64 = 1 << 1; -const RISCV_HWPROBE_IMA_V: u64 = 1 << 2; +// const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0; +// const RISCV_HWPROBE_IMA_C: u64 = 1 << 1; +// const RISCV_HWPROBE_IMA_V: u64 = 1 << 2; const RISCV_HWPROBE_EXT_ZBA: u64 = 1 << 3; const RISCV_HWPROBE_EXT_ZBB: u64 = 1 << 4; const RISCV_HWPROBE_EXT_ZBS: u64 = 1 << 5; @@ -62,11 +62,11 @@ const RISCV_HWPROBE_EXT_ZTSO: u64 = 1 << 33; const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34; // const RISCV_HWPROBE_EXT_ZICOND: u64 = 1 << 35; const RISCV_HWPROBE_EXT_ZIHINTPAUSE: u64 = 1 << 36; -const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37; -const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38; -const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39; -const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40; -const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41; +// const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37; +// const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38; +// const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39; +// const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40; +// const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41; // const RISCV_HWPROBE_EXT_ZIMOP: u64 = 1 << 42; // const RISCV_HWPROBE_EXT_ZCA: u64 = 1 << 43; // const RISCV_HWPROBE_EXT_ZCB: u64 = 1 << 44; @@ -113,11 +113,79 @@ fn _riscv_hwprobe(out: &mut [riscv_hwprobe]) -> bool { pub(crate) fn detect_features() -> cache::Initializer { let mut value = cache::Initializer::default(); + let enable_feature = |value: &mut cache::Initializer, feature, enable| { + if enable { + value.set(feature as u32); + } + }; + let enable_features = |value: &mut cache::Initializer, feature_slice: &[Feature], enable| { + if enable { + for feature in feature_slice { + value.set(*feature as u32); + } + } + }; + + // The values are part of the platform-specific [asm/hwcap.h][hwcap] + // + // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/hwcap.h + let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform + #[allow(clippy::eq_op)] + enable_feature( + &mut value, + Feature::a, + bit::test(auxv.hwcap, (b'a' - b'a').into()), + ); + enable_feature( + &mut value, + Feature::c, + bit::test(auxv.hwcap, (b'c' - b'a').into()), + ); + enable_features( + &mut value, + &[Feature::d, Feature::f, Feature::zicsr], + bit::test(auxv.hwcap, (b'd' - b'a').into()), + ); + enable_features( + &mut value, + &[Feature::f, Feature::zicsr], + bit::test(auxv.hwcap, (b'f' - b'a').into()), + ); + let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); + // If future RV128I is supported, implement with `enable_feature` here + // Checking target_pointer_width instead of target_arch is incorrect since + // there are RV64ILP32* ABIs. + #[cfg(target_arch = "riscv64")] + enable_feature(&mut value, Feature::rv64i, has_i); + #[cfg(target_arch = "riscv32")] + enable_feature(&mut value, Feature::rv32i, has_i); + // FIXME: e is not exposed in any of asm/hwcap.h, uapi/asm/hwcap.h, uapi/asm/hwprobe.h + #[cfg(target_arch = "riscv32")] + enable_feature( + &mut value, + Feature::rv32e, + bit::test(auxv.hwcap, (b'e' - b'a').into()), + ); + enable_feature( + &mut value, + Feature::m, + bit::test(auxv.hwcap, (b'm' - b'a').into()), + ); + let has_v = bit::test(auxv.hwcap, (b'v' - b'a').into()); + enable_features( + &mut value, + &[ + Feature::v, + Feature::zve32f, + Feature::zve32x, + Feature::zve64d, + Feature::zve64f, + Feature::zve64x, + ], + has_v, + ); + let mut out = [ - riscv_hwprobe { - key: RISCV_HWPROBE_KEY_BASE_BEHAVIOR, - value: 0, - }, riscv_hwprobe { key: RISCV_HWPROBE_KEY_IMA_EXT_0, value: 0, @@ -138,23 +206,13 @@ pub(crate) fn detect_features() -> cache::Initializer { } }; if out[0].key != -1 { - let base_behavior = out[0].value; - let ima = base_behavior & RISCV_HWPROBE_BASE_BEHAVIOR_IMA != 0; - // If future RV128I is supported, implement with `enable_feature` here - #[cfg(target_arch = "riscv32")] - enable_feature(Feature::rv32i, ima); - #[cfg(target_arch = "riscv64")] - enable_feature(Feature::rv64i, ima); - enable_feature(Feature::m, ima); - enable_feature(Feature::a, ima); - } - if out[1].key != -1 { - let ima_ext_0 = out[1].value; - let fd = ima_ext_0 & RISCV_HWPROBE_IMA_FD != 0; - enable_feature(Feature::f, fd); - enable_feature(Feature::d, fd); - enable_feature(Feature::zicsr, fd); // implied by f - enable_feature(Feature::c, ima_ext_0 & RISCV_HWPROBE_IMA_C != 0); + let ima_ext_0 = out[0].value; + // i, m, a, f, d, zicsr, and c extensions are detected by hwcap. + // let fd = ima_ext_0 & RISCV_HWPROBE_IMA_FD != 0; + // enable_feature(Feature::f, fd); + // enable_feature(Feature::d, fd); + // enable_feature(Feature::zicsr, fd); // implied by f + // enable_feature(Feature::c, ima_ext_0 & RISCV_HWPROBE_IMA_C != 0); // enable_feature(Feature::zicboz, ima_ext_0 & RISCV_HWPROBE_EXT_ZICBOZ != 0); enable_feature(Feature::zfh, ima_ext_0 & RISCV_HWPROBE_EXT_ZFH != 0); enable_feature(Feature::zfhmin, ima_ext_0 & RISCV_HWPROBE_EXT_ZFHMIN != 0); @@ -203,129 +261,64 @@ pub(crate) fn detect_features() -> cache::Initializer { enable_feature(Feature::zkn, zkn); // enable_feature(Feature::zk, zkn & zkr & zkt); enable_feature(Feature::zks, zbkb & zbkc & zbkx & zksed & zksh); - // Standard Vector Extensions - enable_feature(Feature::v, ima_ext_0 & RISCV_HWPROBE_IMA_V != 0); - enable_feature(Feature::zvfh, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFH != 0); - enable_feature(Feature::zvfhmin, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFHMIN != 0); - enable_feature(Feature::zve32x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32X != 0); - enable_feature(Feature::zve32f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32F != 0); - enable_feature(Feature::zve64x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64X != 0); - enable_feature(Feature::zve64f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64F != 0); - enable_feature(Feature::zve64d, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64D != 0); - // Vector Cryptography and Bit-manipulation Extensions - let zvbb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBB != 0; - enable_feature(Feature::zvbb, zvbb); - let zvbc = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBC != 0; - enable_feature(Feature::zvbc, zvbc); - let zvkb = zvbb || ima_ext_0 & RISCV_HWPROBE_EXT_ZVKB != 0; - enable_feature(Feature::zvkb, zvkb); - let zvkg = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKG != 0; - enable_feature(Feature::zvkg, zvkg); - let zvkned = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNED != 0; - enable_feature(Feature::zvkned, zvkned); - enable_feature(Feature::zvknha, ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHA != 0); - let zvknhb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHB != 0; - enable_feature(Feature::zvknhb, zvknhb); - let zvksed = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSED != 0; - enable_feature(Feature::zvksed, zvksed); - let zvksh = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSH != 0; - enable_feature(Feature::zvksh, zvksh); - let zvkt = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKT != 0; - enable_feature(Feature::zvkt, zvkt); - let zvkn = zvkned & zvknhb & zvkb & zvkt; - enable_feature(Feature::zvkn, zvkn); - enable_feature(Feature::zvknc, zvkn & zvbc); - enable_feature(Feature::zvkng, zvkn & zvkg); - let zvks = zvksed & zvksh & zvkb & zvkt; - enable_feature(Feature::zvks, zvks); - enable_feature(Feature::zvksc, zvks & zvbc); - enable_feature(Feature::zvksg, zvks & zvkg); + // Refer result from hwcap because it reflects Vector enablement status, unlike hwprobe. + // prctl(PR_RISCV_V_GET_CONTROL) is another way to check this but it doesn't work with + // qemu-user (as of 9.2.1). + // See https://docs.kernel.org/arch/riscv/vector.html for more. + if has_v { + // Standard Vector Extensions + // v and zve{32,64}* extensions are detected by hwcap. + // enable_feature(Feature::v, ima_ext_0 & RISCV_HWPROBE_IMA_V != 0); + enable_feature(Feature::zvfh, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFH != 0); + enable_feature(Feature::zvfhmin, ima_ext_0 & RISCV_HWPROBE_EXT_ZVFHMIN != 0); + // enable_feature(Feature::zve32x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32X != 0); + // enable_feature(Feature::zve32f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE32F != 0); + // enable_feature(Feature::zve64x, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64X != 0); + // enable_feature(Feature::zve64f, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64F != 0); + // enable_feature(Feature::zve64d, ima_ext_0 & RISCV_HWPROBE_EXT_ZVE64D != 0); + // Vector Cryptography and Bit-manipulation Extensions + let zvbb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBB != 0; + enable_feature(Feature::zvbb, zvbb); + let zvbc = ima_ext_0 & RISCV_HWPROBE_EXT_ZVBC != 0; + enable_feature(Feature::zvbc, zvbc); + let zvkb = zvbb || ima_ext_0 & RISCV_HWPROBE_EXT_ZVKB != 0; + enable_feature(Feature::zvkb, zvkb); + let zvkg = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKG != 0; + enable_feature(Feature::zvkg, zvkg); + let zvkned = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNED != 0; + enable_feature(Feature::zvkned, zvkned); + enable_feature(Feature::zvknha, ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHA != 0); + let zvknhb = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKNHB != 0; + enable_feature(Feature::zvknhb, zvknhb); + let zvksed = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSED != 0; + enable_feature(Feature::zvksed, zvksed); + let zvksh = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKSH != 0; + enable_feature(Feature::zvksh, zvksh); + let zvkt = ima_ext_0 & RISCV_HWPROBE_EXT_ZVKT != 0; + enable_feature(Feature::zvkt, zvkt); + let zvkn = zvkned & zvknhb & zvkb & zvkt; + enable_feature(Feature::zvkn, zvkn); + enable_feature(Feature::zvknc, zvkn & zvbc); + enable_feature(Feature::zvkng, zvkn & zvkg); + let zvks = zvksed & zvksh & zvkb & zvkt; + enable_feature(Feature::zvks, zvks); + enable_feature(Feature::zvksc, zvks & zvbc); + enable_feature(Feature::zvksg, zvks & zvkg); + } } - if out[2].key != -1 { + if out[1].key != -1 { enable_feature( Feature::unaligned_scalar_mem, - out[2].value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST, + out[1].value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST, ); } - if out[3].key != -1 { + if out[2].key != -1 { enable_feature( Feature::unaligned_vector_mem, - out[3].value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST, + out[2].value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST, ); } - // FIXME: should be enough with hwprobe only, but our code below checks e - // unavailable in neither uapi/asm/hwprobe.h nor uapi/asm/hwcap.h. - // https://github.com/torvalds/linux/blob/master/arch/riscv/include/uapi/asm/hwcap.h - // return value; } - // FIXME: As said in the above FIXME, we currently alway checks auxv too. - // // riscv_hwprobe requires Linux 6.4, so we fallback to auxv-based detection on - // // old Linux kernel. - - let enable_feature = |value: &mut cache::Initializer, feature, enable| { - if enable { - value.set(feature as u32); - } - }; - let enable_features = |value: &mut cache::Initializer, feature_slice: &[Feature], enable| { - if enable { - for feature in feature_slice { - value.set(*feature as u32); - } - } - }; - - // The values are part of the platform-specific [asm/hwcap.h][hwcap] - // - // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/hwcap.h - // - // Note that there is no need to check b'v' - b'a' here for the case where riscv_hwprobe is unsupported, - // since both RISCV_HWPROBE_IMA_V and COMPAT_HWCAP_ISA_V are only supported on Linux 6.5+. - // https://github.com/torvalds/linux/commit/162e4df137c1fea6557fda3e4cdf5dc6ca6d5510 - // https://github.com/torvalds/linux/commit/dc6667a4e7e36f283bcd0264a0be55adae4d6f86 - let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform - #[allow(clippy::eq_op)] - enable_feature( - &mut value, - Feature::a, - bit::test(auxv.hwcap, (b'a' - b'a').into()), - ); - enable_feature( - &mut value, - Feature::c, - bit::test(auxv.hwcap, (b'c' - b'a').into()), - ); - enable_features( - &mut value, - &[Feature::d, Feature::f, Feature::zicsr], - bit::test(auxv.hwcap, (b'd' - b'a').into()), - ); - enable_features( - &mut value, - &[Feature::f, Feature::zicsr], - bit::test(auxv.hwcap, (b'f' - b'a').into()), - ); - let has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); - // If future RV128I is supported, implement with `enable_feature` here - // Checking target_pointer_width instead of target_arch is incorrect since - // there are RV64ILP32* ABIs. - #[cfg(target_arch = "riscv64")] - enable_feature(&mut value, Feature::rv64i, has_i); - #[cfg(target_arch = "riscv32")] - enable_feature(&mut value, Feature::rv32i, has_i); - // FIXME: e is not exposed in any of asm/hwcap.h, uapi/asm/hwcap.h, uapi/asm/hwprobe.h - #[cfg(target_arch = "riscv32")] - enable_feature( - &mut value, - Feature::rv32e, - bit::test(auxv.hwcap, (b'e' - b'a').into()), - ); - enable_feature( - &mut value, - Feature::m, - bit::test(auxv.hwcap, (b'm' - b'a').into()), - ); - value } From 5bee27c7e2e7c26d41a1f62e23c07d63126f7460 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 10 Apr 2025 17:46:05 +0900 Subject: [PATCH 5/6] std_detect: Remove comment about zvl*b target features --- crates/std_detect/src/detect/arch/riscv.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/std_detect/src/detect/arch/riscv.rs b/crates/std_detect/src/detect/arch/riscv.rs index b1d82f9b45..cab1b60705 100644 --- a/crates/std_detect/src/detect/arch/riscv.rs +++ b/crates/std_detect/src/detect/arch/riscv.rs @@ -205,9 +205,6 @@ features! { @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64d: "zve64d"; /// Vector Extensions for Embedded Processors - // FIXME: we can implement this by getting the current vlen - // zvl*b: Minimum Vector Length Standard Extensions - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkb: "zvkb"; /// Vector Bit-manipulation used in Cryptography @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbb: "zvbb"; From 669e55348d533bbc30a22463643d4dd8a0f54c5c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 10 Apr 2025 17:46:24 +0900 Subject: [PATCH 6/6] std_detect: Enable riscv_linux test also on Android --- crates/std_detect/tests/cpu-detection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index 6059be4781..9176f8f5b7 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -229,7 +229,7 @@ fn aarch64_darwin() { #[test] #[cfg(all( any(target_arch = "riscv32", target_arch = "riscv64"), - target_os = "linux" + any(target_os = "linux", target_os = "android") ))] fn riscv_linux() { println!("rv32i: {}", is_riscv_feature_detected!("rv32i"));