Skip to content

Commit

Permalink
unify implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
tarcieri committed May 5, 2021
1 parent d8e9304 commit 59b29d6
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 149 deletions.
105 changes: 30 additions & 75 deletions cpufeatures/src/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,86 +4,41 @@
//! unprivileged userspace code, so this implementation relies on OS-specific
//! APIs for feature detection.

/// Create module with CPU feature detection code.
#[macro_export]
macro_rules! new {
($mod_name:ident, $($tf:tt),+ $(,)? ) => {
mod $mod_name {
use core::sync::atomic::{AtomicU8, Ordering::Relaxed};

const UNINIT: u8 = u8::max_value();
static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);

/// Initialization token
#[derive(Copy, Clone, Debug)]
pub struct InitToken(());

impl InitToken {
/// Get initialized value
#[inline(always)]
pub fn get(&self) -> bool {
#[cfg(not(all($(target_feature=$tf, )*)))]
let res = STORAGE.load(Relaxed) == 1;
#[cfg(all($(target_feature=$tf, )*))]
let res = true;
res
}
}

/// Initialize underlying storage if needed and get
/// stored value and initialization token.
#[inline]
pub fn init_get() -> (InitToken, bool) {
#[cfg(not(all($(target_feature=$tf, )*)))]
let res = {
// Relaxed ordering is fine, as we only have a single atomic variable.
let val = STORAGE.load(Relaxed);
if val == UNINIT {
let res = {
#[cfg(target_os = "linux")]
{
let hwcaps = unsafe { libc::getauxval(libc::AT_HWCAP) };
$(cpufeatures::check!(hwcaps, $tf) & )+ true
}

#[cfg(target_os = "macos")]
{
$(cpufeatures::check!($tf) & )+ true
}
};

STORAGE.store(res as u8, Relaxed);
res
} else {
val == 1
}
};
#[cfg(all($(target_feature=$tf, )*))]
let res = true;

(InitToken(()), res)
}

/// Initialize underlying storage if needed and get
/// initialization token.
#[inline]
pub fn init() -> InitToken {
init_get().0
}

/// Initialize underlying storage if needed and get
/// stored value.
#[inline]
pub fn get() -> bool {
init_get().1
}
macro_rules! __unless_target_features {
($($tf:tt),+ => $body:expr ) => {
{
#[cfg(not(all($(target_feature=$tf,)*)))]
$body

#[cfg(all($(target_feature=$tf,)*))]
true
}
};
}

#[cfg(target_os = "linux")]
#[macro_export]
#[doc(hidden)]
macro_rules! __detect_target_features {
($($tf:tt),+) => {{
let hwcaps = unsafe { libc::getauxval(libc::AT_HWCAP) };
$($crate::check!(hwcaps, $tf) & )+ true
}};
}

#[cfg(target_os = "macos")]
#[macro_export]
#[doc(hidden)]
macro_rules! __detect_target_features {
($($tf:tt),+) => {{
$($crate::check!($tf) & )+ true
}};
}

/// Linux `expand_check_macro`
#[cfg(target_os = "linux")]
macro_rules! expand_check_macro {
macro_rules! __expand_check_macro {
($(($name:tt, $hwcap:expr)),* $(,)?) => {
#[macro_export]
#[doc(hidden)]
Expand All @@ -97,7 +52,7 @@ macro_rules! expand_check_macro {

/// Linux `expand_check_macro`
#[cfg(target_os = "linux")]
expand_check_macro! {
__expand_check_macro! {
("aes", HWCAP_AES), // Enable AES support.
("sha2", HWCAP_SHA2), // Enable SHA1 and SHA256 support.
("sha3", HWCAP_SHA3), // Enable SHA512 and SHA3 support.
Expand Down Expand Up @@ -125,7 +80,7 @@ macro_rules! check {
true
};
("sha3") => {
unsafe { cpufeatures::aarch64::sysctlbyname(b"hw.optional.armv8_2_sha3\0") }
unsafe { $crate::aarch64::sysctlbyname(b"hw.optional.armv8_2_sha3\0") }
};
}

Expand Down
65 changes: 65 additions & 0 deletions cpufeatures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,68 @@ mod x86;
target_arch = "x86_64"
)))]
compile_error!("This crate works only on `aarch64` (Linux/Mac), `x86`, and `x86-64 targets.");

/// Create module with CPU feature detection code.
#[macro_export]
macro_rules! new {
($mod_name:ident, $($tf:tt),+) => {
mod $mod_name {
use core::sync::atomic::{AtomicU8, Ordering::Relaxed};

const UNINIT: u8 = u8::max_value();
static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);

/// Initialization token
#[derive(Copy, Clone, Debug)]
pub struct InitToken(());

impl InitToken {
/// Get initialized value
#[inline(always)]
pub fn get(&self) -> bool {
$crate::__unless_target_features! {
$($tf),+ => {
STORAGE.load(Relaxed) == 1
}
}
}
}

/// Initialize underlying storage if needed and get
/// stored value and initialization token.
#[inline]
pub fn init_get() -> (InitToken, bool) {
let res = $crate::__unless_target_features! {
$($tf),+ => {
// Relaxed ordering is fine, as we only have a single atomic variable.
let val = STORAGE.load(Relaxed);

if val == UNINIT {
let res = $crate::__detect_target_features!($($tf),+);
STORAGE.store(res as u8, Relaxed);
res
} else {
val == 1
}
}
};

(InitToken(()), res)
}

/// Initialize underlying storage if needed and get
/// initialization token.
#[inline]
pub fn init() -> InitToken {
init_get().0
}

/// Initialize underlying storage if needed and get
/// stored value.
#[inline]
pub fn get() -> bool {
init_get().1
}
}
};
}
104 changes: 30 additions & 74 deletions cpufeatures/src/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,87 +3,43 @@
//! Portable, `no_std`-friendly implementation that relies on the x86 `CPUID`
//! instruction for feature detection.

/// Create module with CPU feature detection code.
#[macro_export]
macro_rules! new {
($mod_name:ident, $($tf:tt),+ $(,)? ) => {
mod $mod_name {
use core::sync::atomic::{AtomicU8, Ordering::Relaxed};
#[doc(hidden)]
macro_rules! __unless_target_features {
($($tf:tt),+ => $body:expr ) => {{
#[cfg(not(all($(target_feature=$tf,)*)))]
{
#[cfg(not(target_env = "sgx"))]
$body

const UNINIT: u8 = u8::max_value();
static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);

/// Initialization token
#[derive(Copy, Clone, Debug)]
pub struct InitToken(());

impl InitToken {
/// Get initialized value
#[inline(always)]
pub fn get(&self) -> bool {
// CPUID is not available on SGX targets
#[cfg(all(not(target_env = "sgx"), not(all($(target_feature=$tf, )*))))]
let res = STORAGE.load(Relaxed) == 1;
#[cfg(all(target_env = "sgx", not(all($(target_feature=$tf, )*))))]
let res = false;
#[cfg(all($(target_feature=$tf, )*))]
let res = true;
res
}
}

/// Initialize underlying storage if needed and get
/// stored value and initialization token.
#[inline]
pub fn init_get() -> (InitToken, bool) {
// CPUID is not available on SGX targets
#[cfg(all(not(target_env = "sgx"), not(all($(target_feature=$tf, )*))))]
let res = {
#[cfg(target_arch = "x86")]
use core::arch::x86::{__cpuid, __cpuid_count};
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{__cpuid, __cpuid_count};
// CPUID is not available on SGX targets
#[cfg(target_env = "sgx")]
false
}

// Relaxed ordering is fine, as we only have a single atomic variable.
let val = STORAGE.load(Relaxed);
if val == UNINIT {
#[allow(unused_variables)]
let cr = unsafe {
[__cpuid(1), __cpuid_count(7, 0)]
};
let res = $(cpufeatures::check!(cr, $tf) & )+ true;
STORAGE.store(res as u8, Relaxed);
res
} else {
val == 1
}
};
#[cfg(all(target_env = "sgx", not(all($(target_feature=$tf, )*))))]
let res = false;
#[cfg(all($(target_feature=$tf, )*))]
let res = true;
#[cfg(all($(target_feature=$tf,)*))]
true
}};
}

(InitToken(()), res)
}
#[macro_export]
#[doc(hidden)]
macro_rules! __detect_target_features {
($($tf:tt),+) => {{
#[cfg(target_arch = "x86")]
use core::arch::x86::{__cpuid, __cpuid_count};
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{__cpuid, __cpuid_count};

/// Initialize underlying storage if needed and get
/// initialization token.
#[inline]
pub fn init() -> InitToken {
init_get().0
}
let cr = unsafe {
[__cpuid(1), __cpuid_count(7, 0)]
};

/// Initialize underlying storage if needed and get
/// stored value.
#[inline]
pub fn get() -> bool {
init_get().1
}
}
};
$($crate::check!(cr, $tf) & )+ true
}};
}

macro_rules! expand_check_macro {
macro_rules! __expand_check_macro {
($(($name:tt, $i:expr, $reg:ident, $offset:expr)),* $(,)?) => {
#[macro_export]
#[doc(hidden)]
Expand All @@ -95,7 +51,7 @@ macro_rules! expand_check_macro {
};
}

expand_check_macro! {
__expand_check_macro! {
("mmx", 0, edx, 23),
("sse", 0, edx, 25),
("sse2", 0, edx, 26),
Expand Down

0 comments on commit 59b29d6

Please sign in to comment.