Skip to content

Commit

Permalink
Merge #74
Browse files Browse the repository at this point in the history
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
bors[bot] and taiki-e authored Feb 9, 2023
2 parents d9ba46f + f1452ba commit cdb7020
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .github/.cspell/project-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ auxvec
backoff
binutils
bitops
bytecount
callthru
casp
cbnz
Expand Down Expand Up @@ -56,6 +57,7 @@ libcalls
libtcl
libtest
lqarx
lrcpc
lwsync
machdep
mclass
Expand Down Expand Up @@ -101,6 +103,7 @@ subc
subcmd
subfe
syscall
sysctlbyname
systemsim
tagme
Tlink
Expand Down
4 changes: 4 additions & 0 deletions src/imp/atomic128/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
154 changes: 154 additions & 0 deletions src/imp/atomic128/detect/aarch64_macos.rs
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;
};
}
2 changes: 1 addition & 1 deletion src/imp/atomic128/detect/aarch64_windows.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit cdb7020

Please sign in to comment.