From f1452ba247fbfe24d509cbaf9b31362b621ee40a Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 10 Feb 2023 05:33:19 +0900 Subject: [PATCH] detect: Support run-time detection on aarch64 macOS (test-only) This module is currently only enabled on tests because aarch64 macOS always supports FEAT_LSE and FEAT_LSE2. If macOS supporting Armv9.4-a becomes popular in the future, this module will be used to support outline atomics for FEAT_LSE128/FEAT_LRCPC3. --- .github/.cspell/project-dictionary.txt | 3 + src/imp/atomic128/aarch64.rs | 4 + src/imp/atomic128/detect/aarch64_macos.rs | 154 ++++++++++++++++++++ src/imp/atomic128/detect/aarch64_windows.rs | 2 +- src/lib.rs | 1 + 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 src/imp/atomic128/detect/aarch64_macos.rs diff --git a/.github/.cspell/project-dictionary.txt b/.github/.cspell/project-dictionary.txt index 5b35a6b2..a85d5373 100644 --- a/.github/.cspell/project-dictionary.txt +++ b/.github/.cspell/project-dictionary.txt @@ -9,6 +9,7 @@ auxvec backoff binutils bitops +bytecount callthru casp cbnz @@ -56,6 +57,7 @@ libcalls libtcl libtest lqarx +lrcpc lwsync machdep mclass @@ -101,6 +103,7 @@ subc subcmd subfe syscall +sysctlbyname systemsim tagme Tlink diff --git a/src/imp/atomic128/aarch64.rs b/src/imp/atomic128/aarch64.rs index 1bece1cd..0dd45c21 100644 --- a/src/imp/atomic128/aarch64.rs +++ b/src/imp/atomic128/aarch64.rs @@ -73,6 +73,10 @@ mod detect; #[cfg(any(target_os = "linux", target_os = "android"))] #[path = "detect/aarch64_aa64reg.rs"] mod detect_aa64reg; +#[cfg(test)] +#[cfg(target_os = "macos")] +#[path = "detect/aarch64_macos.rs"] +mod detect_macos; #[cfg(not(portable_atomic_no_asm))] use core::arch::asm; diff --git a/src/imp/atomic128/detect/aarch64_macos.rs b/src/imp/atomic128/detect/aarch64_macos.rs new file mode 100644 index 00000000..385cbe19 --- /dev/null +++ b/src/imp/atomic128/detect/aarch64_macos.rs @@ -0,0 +1,154 @@ +// Run-time feature detection on aarch64 macOS by using sysctl. +// +// This module is currently only enabled on tests because aarch64 macOS always supports FEAT_LSE and FEAT_LSE2. +// https://github.com/rust-lang/rust/blob/1.67.0/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs#L7 +// +// If macOS supporting Armv9.4-a becomes popular in the future, this module will +// be used to support outline atomics for FEAT_LSE128/FEAT_LRCPC3. +// +// Refs: +// - https://developer.apple.com/documentation/apple-silicon/addressing-architectural-differences-in-your-macos-code +// - https://github.com/golang/go/commit/c15593197453b8bf90fc3a9080ba2afeaf7934ea +// +// Note that iOS doesn't support sysctl: +// - https://developer.apple.com/forums/thread/9440 +// - https://nabla-c0d3.github.io/blog/2015/06/16/ios9-security-privacy + +#![cfg_attr( + any( + portable_atomic_no_aarch64_target_feature, + any(target_feature = "lse", portable_atomic_target_feature = "lse") + ), + allow(dead_code) +)] + +include!("common.rs"); + +use core::{mem::MaybeUninit, ptr}; + +// core::ffi::c_* (except c_void) requires Rust 1.64 +#[allow(non_camel_case_types)] +mod ffi { + pub(crate) use core::ffi::c_void; + // c_char is {i,u}32 on darwin + // https://github.com/rust-lang/rust/blob/1.67.0/library/core/src/ffi/mod.rs#L104-L157 + // https://github.com/rust-lang/libc/blob/0.2.139/src/unix/bsd/apple/mod.rs#L4 + pub(crate) type c_char = i8; + // c_{,u}int is {i,u}32 on non-16-bit architectures + // https://github.com/rust-lang/rust/blob/1.67.0/library/core/src/ffi/mod.rs#L159-L173 + pub(crate) type c_int = i32; + // c_size_t is usize + // https://github.com/rust-lang/rust/blob/1.67.0/library/core/src/ffi/mod.rs#L83-L88 + pub(crate) type c_size_t = usize; + + extern "C" { + // https://developer.apple.com/documentation/kernel/1387446-sysctlbyname + // https://github.com/rust-lang/libc/blob/0.2.139/src/unix/bsd/apple/mod.rs#L5167-L5173 + pub(crate) fn sysctlbyname( + name: *const c_char, + old_p: *mut c_void, + old_len_p: *mut c_size_t, + new_p: *mut c_void, + new_len: c_size_t, + ) -> c_int; + } +} + +#[inline] +unsafe fn sysctlbyname32(name: &[u8]) -> Option { + const OUT_LEN: ffi::c_size_t = core::mem::size_of::() as ffi::c_size_t; + + debug_assert_eq!(name.last(), Some(&0), "{:?}", name); + debug_assert_eq!(name.iter().filter(|&&v| v == 0).count(), 1, "{:?}", name); + + let mut out = MaybeUninit::::uninit(); + let mut out_len = OUT_LEN; + // SAFETY: + // - the caller must guarantee that `name` a valid C string. + // - `out_len` does not exceed the size of `out`. + // - `sysctlbyname` is thread-safe. + let res = unsafe { + ffi::sysctlbyname( + name.as_ptr() as *const ffi::c_char, + out.as_mut_ptr() as *mut ffi::c_void, + &mut out_len, + ptr::null_mut(), + 0, + ) + }; + if res != 0 || out_len != OUT_LEN { + return None; + } + // SAFETY: we've checked that sysctl was successful and `out` was filled. + Some(unsafe { out.assume_init() }) +} + +#[inline] +fn _detect(info: &mut CpuInfo) { + // SAFETY: we passed a valid C string. + if unsafe { sysctlbyname32(b"hw.optional.armv8_1_atomics\0").unwrap_or(0) != 0 } { + info.set(CpuInfo::HAS_LSE); + } + + #[cfg(test)] + { + // SAFETY: we passed a valid C string. + if unsafe { sysctlbyname32(b"hw.optional.arm.FEAT_LSE2\0").unwrap_or(0) != 0 } { + info.set(CpuInfo::HAS_LSE2); + } + } +} + +#[allow( + clippy::alloc_instead_of_core, + clippy::std_instead_of_alloc, + clippy::std_instead_of_core, + clippy::undocumented_unsafe_blocks, + clippy::wildcard_imports +)] +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_detect() { + unsafe { + assert_eq!(sysctlbyname32(b"hw.optional.armv8_1_atomics\0"), Some(1)); + assert_eq!(sysctlbyname32(b"hw.optional.arm.FEAT_LSE\0"), Some(1)); + assert_eq!(sysctlbyname32(b"hw.optional.arm.FEAT_LSE2\0"), Some(1)); + assert_eq!(sysctlbyname32(b"hw.optional.arm.FEAT_LSE128\0"), None); + assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::NotFound); + assert_eq!(sysctlbyname32(b"hw.optional.arm.FEAT_LRCPC\0"), Some(1)); + assert_eq!(sysctlbyname32(b"hw.optional.arm.FEAT_LRCPC2\0"), Some(1)); + assert_eq!(sysctlbyname32(b"hw.optional.arm.FEAT_LRCPC3\0"), None); + assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::NotFound); + } + } + + // Static assertions for FFI bindings. + // This checks that FFI bindings defined in this crate and FFI bindings defined + // in libc have compatible signatures (or the same values if constants). + // Since this is static assertion, we can detect problems with + // `cargo check --tests --target ` run in CI (via TESTS=1 build.sh) + // without actually running tests on these platforms. + // See also tools/codegen/src/ffi.rs. + // TODO: auto-generate this test + #[allow( + clippy::cast_possible_wrap, + clippy::cast_sign_loss, + clippy::no_effect_underscore_binding + )] + const _: fn() = || { + let _: ffi::c_int = 0 as std::os::raw::c_int; + let _: ffi::c_int = 0 as libc::c_int; + let _: ffi::c_size_t = 0 as libc::size_t; + let mut _sysctlbyname: unsafe extern "C" fn( + *const ffi::c_char, + *mut ffi::c_void, + *mut ffi::c_size_t, + *mut ffi::c_void, + ffi::c_size_t, + ) -> ffi::c_int = ffi::sysctlbyname; + _sysctlbyname = libc::sysctlbyname; + }; +} diff --git a/src/imp/atomic128/detect/aarch64_windows.rs b/src/imp/atomic128/detect/aarch64_windows.rs index f40c8274..eb27096e 100644 --- a/src/imp/atomic128/detect/aarch64_windows.rs +++ b/src/imp/atomic128/detect/aarch64_windows.rs @@ -1,4 +1,4 @@ -// Run-time feature detection on aarch64 Windows. +// Run-time feature detection on aarch64 Windows by using IsProcessorFeaturePresent. // // As of nightly-2023-01-23, is_aarch64_feature_detected doesn't support run-time detection of FEAT_LSE on Windows. // https://github.com/rust-lang/stdarch/blob/a0c30f3e3c75adcd6ee7efc94014ebcead61c507/crates/std_detect/src/detect/os/windows/aarch64.rs diff --git a/src/lib.rs b/src/lib.rs index ff0b2f45..2231709f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,6 +179,7 @@ See also [the `atomic128` module's readme](https://github.com/taiki-e/portable-a clippy::inline_always, clippy::missing_errors_doc, clippy::module_inception, + clippy::naive_bytecount, clippy::similar_names, clippy::single_match, clippy::type_complexity