#[derive(Copy, Clone)]
pub(crate) struct Features(());
#[inline(always)]
pub(crate) fn features() -> Features {
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
all(
any(target_arch = "aarch64", target_arch = "arm"),
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
)
))]
{
static INIT: spin::Once<()> = spin::Once::new();
let () = INIT.call_once(|| {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
extern "C" {
fn GFp_cpuid_setup();
}
unsafe {
GFp_cpuid_setup();
}
}
#[cfg(all(
any(target_arch = "aarch64", target_arch = "arm"),
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
))]
{
arm::setup();
}
});
}
Features(())
}
pub(crate) mod arm {
#[cfg(all(
any(target_os = "android", target_os = "linux"),
any(target_arch = "aarch64", target_arch = "arm")
))]
pub fn setup() {
use libc::c_ulong;
extern "C" {
fn getauxval(type_: c_ulong) -> c_ulong;
}
const AT_HWCAP: c_ulong = 16;
#[cfg(target_arch = "aarch64")]
const HWCAP_NEON: c_ulong = 1 << 1;
#[cfg(target_arch = "arm")]
const HWCAP_NEON: c_ulong = 1 << 12;
let caps = unsafe { getauxval(AT_HWCAP) };
#[cfg(target_arch = "aarch64")]
debug_assert!(caps & HWCAP_NEON == HWCAP_NEON);
if caps & HWCAP_NEON == HWCAP_NEON {
let mut features = NEON.mask;
#[cfg(target_arch = "aarch64")]
const OFFSET: c_ulong = 3;
#[cfg(target_arch = "arm")]
const OFFSET: c_ulong = 0;
#[cfg(target_arch = "arm")]
let caps = {
const AT_HWCAP2: c_ulong = 26;
unsafe { getauxval(AT_HWCAP2) }
};
const HWCAP_AES: c_ulong = 1 << 0 + OFFSET;
const HWCAP_PMULL: c_ulong = 1 << 1 + OFFSET;
const HWCAP_SHA2: c_ulong = 1 << 3 + OFFSET;
if caps & HWCAP_AES == HWCAP_AES {
features |= AES.mask;
}
if caps & HWCAP_PMULL == HWCAP_PMULL {
features |= PMULL.mask;
}
if caps & HWCAP_SHA2 == HWCAP_SHA2 {
features |= SHA256.mask;
}
unsafe { GFp_armcap_P = features };
}
}
#[cfg(all(target_os = "fuchsia", target_arch = "aarch64"))]
pub fn setup() {
type zx_status_t = i32;
#[link(name = "zircon")]
extern "C" {
fn zx_system_get_features(kind: u32, features: *mut u32) -> zx_status_t;
}
const ZX_OK: i32 = 0;
const ZX_FEATURE_KIND_CPU: u32 = 0;
const ZX_ARM64_FEATURE_ISA_ASIMD: u32 = 1 << 2;
const ZX_ARM64_FEATURE_ISA_AES: u32 = 1 << 3;
const ZX_ARM64_FEATURE_ISA_PMULL: u32 = 1 << 4;
const ZX_ARM64_FEATURE_ISA_SHA2: u32 = 1 << 6;
let mut caps = 0;
let rc = unsafe { zx_system_get_features(ZX_FEATURE_KIND_CPU, &mut caps) };
if rc == ZX_OK && (caps & ZX_ARM64_FEATURE_ISA_ASIMD == ZX_ARM64_FEATURE_ISA_ASIMD) {
let mut features = NEON.mask;
if caps & ZX_ARM64_FEATURE_ISA_AES == ZX_ARM64_FEATURE_ISA_AES {
features |= AES.mask;
}
if caps & ZX_ARM64_FEATURE_ISA_PMULL == ZX_ARM64_FEATURE_ISA_PMULL {
features |= PMULL.mask;
}
if caps & ZX_ARM64_FEATURE_ISA_SHA2 == ZX_ARM64_FEATURE_ISA_SHA2 {
features |= 1 << 4;
}
unsafe { GFp_armcap_P = features };
}
}
macro_rules! features {
{
$(
$name:ident {
mask: $mask:expr,
/// Should we assume that the feature is always available
/// for aarch64-apple-* targets? The first AArch64 iOS
/// device used the Apple A7 chip.
// TODO: When we can use `if` in const expressions:
// ```
// aarch64_apple: $aarch64_apple,
// ```
aarch64_apple: true,
}
),+
, } => {
$(
#[allow(dead_code)]
pub(crate) const $name: Feature = Feature {
mask: $mask,
};
)+
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
const ARMCAP_STATIC: u32 = 0
$( | $name.mask
)+;
#[cfg(not(all(target_arch = "aarch64", target_vendor = "apple")))]
const ARMCAP_STATIC: u32 = 0;
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
#[test]
fn test_armcap_static_available() {
let features = crate::cpu::features();
$(
assert!($name.available(features));
)+
}
}
}
#[allow(dead_code)]
pub(crate) struct Feature {
mask: u32,
}
impl Feature {
#[allow(dead_code)]
#[inline(always)]
pub fn available(&self, _: super::Features) -> bool {
if self.mask == self.mask & ARMCAP_STATIC {
return true;
}
#[cfg(all(
any(target_os = "android", target_os = "fuchsia", target_os = "linux"),
any(target_arch = "arm", target_arch = "aarch64")
))]
{
if self.mask == self.mask & unsafe { GFp_armcap_P } {
return true;
}
}
false
}
}
features! {
NEON {
mask: 1 << 0,
aarch64_apple: true,
},
AES {
mask: 1 << 2,
aarch64_apple: true,
},
SHA256 {
mask: 1 << 4,
aarch64_apple: true,
},
PMULL {
mask: 1 << 5,
aarch64_apple: true,
},
}
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
#[no_mangle]
static mut GFp_armcap_P: u32 = ARMCAP_STATIC;
#[cfg(all(
any(target_arch = "arm", target_arch = "aarch64"),
target_vendor = "apple"
))]
#[test]
fn test_armcap_static_matches_armcap_dynamic() {
assert_eq!(ARMCAP_STATIC, 1 | 4 | 16 | 32);
assert_eq!(ARMCAP_STATIC, unsafe { GFp_armcap_P });
}
}
#[cfg_attr(
not(any(target_arch = "x86", target_arch = "x86_64")),
allow(dead_code)
)]
pub(crate) mod intel {
pub(crate) struct Feature {
word: usize,
mask: u32,
}
impl Feature {
#[allow(clippy::needless_return)]
#[inline(always)]
pub fn available(&self, _: super::Features) -> bool {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
extern "C" {
static mut GFp_ia32cap_P: [u32; 4];
}
return self.mask == self.mask & unsafe { GFp_ia32cap_P[self.word] };
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
{
return false;
}
}
}
pub(crate) const FXSR: Feature = Feature {
word: 0,
mask: 1 << 24,
};
pub(crate) const PCLMULQDQ: Feature = Feature {
word: 1,
mask: 1 << 1,
};
pub(crate) const SSSE3: Feature = Feature {
word: 1,
mask: 1 << 9,
};
#[cfg(target_arch = "x86_64")]
pub(crate) const MOVBE: Feature = Feature {
word: 1,
mask: 1 << 22,
};
pub(crate) const AES: Feature = Feature {
word: 1,
mask: 1 << 25,
};
#[cfg(target_arch = "x86_64")]
pub(crate) const AVX: Feature = Feature {
word: 1,
mask: 1 << 28,
};
#[cfg(all(target_arch = "x86_64", test))]
mod x86_64_tests {
use super::*;
#[test]
fn test_avx_movbe_mask() {
assert_eq!((AVX.mask | MOVBE.mask) >> 22, 0x41);
}
}
}