-
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
74: detect: Support run-time detection on aarch64 macOS (test-only) r=taiki-e a=taiki-e 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. Co-authored-by: Taiki Endo <[email protected]>
- Loading branch information
Showing
5 changed files
with
163 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<u32> { | ||
const OUT_LEN: ffi::c_size_t = core::mem::size_of::<u32>() 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::<u32>::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 <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; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters