#[derive(Clone, Copy)]
pub(crate) struct CpuInfo(u32);
impl CpuInfo {
const INIT: u32 = 0;
#[inline]
fn set(&mut self, bit: u32) {
self.0 = set(self.0, bit);
}
#[inline]
fn test(self, bit: u32) -> bool {
test(self.0, bit)
}
}
#[inline]
fn set(x: u32, bit: u32) -> u32 {
x | 1 << bit
}
#[inline]
fn test(x: u32, bit: u32) -> bool {
x & (1 << bit) != 0
}
#[inline]
pub(crate) fn detect() -> CpuInfo {
use core::sync::atomic::{AtomicU32, Ordering};
static CACHE: AtomicU32 = AtomicU32::new(0);
let mut info = CpuInfo(CACHE.load(Ordering::Relaxed));
if info.0 != 0 {
return info;
}
info.set(CpuInfo::INIT);
if !cfg!(portable_atomic_test_outline_atomics_detect_false) {
_detect(&mut info);
}
CACHE.store(info.0, Ordering::Relaxed);
info
}
#[cfg(target_arch = "aarch64")]
impl CpuInfo {
const HAS_LSE: u32 = 1;
#[cfg_attr(not(test), allow(dead_code))]
const HAS_LSE2: u32 = 2;
#[cfg(test)]
const HAS_LSE128: u32 = 3;
#[cfg(test)]
const HAS_RCPC3: u32 = 4;
#[cfg(any(test, not(any(target_feature = "lse", portable_atomic_target_feature = "lse"))))]
#[inline]
pub(crate) fn has_lse(self) -> bool {
self.test(CpuInfo::HAS_LSE)
}
#[cfg_attr(not(test), allow(dead_code))]
#[cfg(any(test, not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))))]
#[inline]
pub(crate) fn has_lse2(self) -> bool {
self.test(CpuInfo::HAS_LSE2)
}
#[cfg(test)]
#[inline]
pub(crate) fn has_lse128(self) -> bool {
self.test(CpuInfo::HAS_LSE128)
}
#[cfg(test)]
#[inline]
pub(crate) fn has_rcpc3(self) -> bool {
self.test(CpuInfo::HAS_RCPC3)
}
}
#[cfg(target_arch = "x86_64")]
impl CpuInfo {
const HAS_CMPXCHG16B: u32 = 1;
const HAS_VMOVDQA_ATOMIC: u32 = 2;
#[cfg(any(
test,
not(any(target_feature = "cmpxchg16b", portable_atomic_target_feature = "cmpxchg16b")),
))]
#[inline]
pub(crate) fn has_cmpxchg16b(self) -> bool {
self.test(CpuInfo::HAS_CMPXCHG16B)
}
#[inline]
pub(crate) fn has_vmovdqa_atomic(self) -> bool {
self.test(CpuInfo::HAS_VMOVDQA_ATOMIC)
}
}
#[cfg(target_arch = "powerpc64")]
impl CpuInfo {
const HAS_QUADWORD_ATOMICS: u32 = 1;
#[cfg(any(
test,
not(any(
target_feature = "quadword-atomics",
portable_atomic_target_feature = "quadword-atomics",
)),
))]
#[inline]
pub(crate) fn has_quadword_atomics(self) -> bool {
self.test(CpuInfo::HAS_QUADWORD_ATOMICS)
}
}
#[cfg(any(target_arch = "aarch64", target_arch = "powerpc64"))]
#[cfg(not(windows))]
#[allow(dead_code, non_camel_case_types)]
mod c_types {
pub(crate) type c_void = core::ffi::c_void;
pub(crate) type c_int = i32;
pub(crate) type c_uint = u32;
#[cfg(target_pointer_width = "64")]
pub(crate) type c_long = i64;
#[cfg(not(target_pointer_width = "64"))]
pub(crate) type c_long = i32;
#[cfg(target_pointer_width = "64")]
pub(crate) type c_ulong = u64;
#[cfg(not(target_pointer_width = "64"))]
pub(crate) type c_ulong = u32;
pub(crate) type c_size_t = usize;
#[cfg(not(target_os = "macos"))]
pub(crate) type c_char = u8;
#[cfg(target_os = "macos")]
pub(crate) type c_char = i8;
#[cfg(test)]
const _: fn() = || {
use test_helper::{libc, sys};
let _: c_int = 0 as std::os::raw::c_int;
let _: c_uint = 0 as std::os::raw::c_uint;
let _: c_long = 0 as std::os::raw::c_long;
let _: c_ulong = 0 as std::os::raw::c_ulong;
let _: c_size_t = 0 as libc::size_t; let _: c_char = 0 as std::os::raw::c_char;
let _: c_char = 0 as sys::c_char;
};
}
#[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_common {
use super::*;
#[test]
fn test_bit_flags() {
let mut x = CpuInfo(0);
#[cfg(target_arch = "aarch64")]
{
assert!(!x.test(CpuInfo::INIT));
assert!(!x.test(CpuInfo::HAS_LSE));
assert!(!x.test(CpuInfo::HAS_LSE2));
assert!(!x.test(CpuInfo::HAS_LSE128));
assert!(!x.test(CpuInfo::HAS_RCPC3));
x.set(CpuInfo::INIT);
assert!(x.test(CpuInfo::INIT));
assert!(!x.test(CpuInfo::HAS_LSE));
assert!(!x.test(CpuInfo::HAS_LSE2));
assert!(!x.test(CpuInfo::HAS_LSE128));
assert!(!x.test(CpuInfo::HAS_RCPC3));
x.set(CpuInfo::HAS_LSE);
assert!(x.test(CpuInfo::INIT));
assert!(x.test(CpuInfo::HAS_LSE));
assert!(!x.test(CpuInfo::HAS_LSE2));
assert!(!x.test(CpuInfo::HAS_LSE128));
assert!(!x.test(CpuInfo::HAS_RCPC3));
x.set(CpuInfo::HAS_LSE2);
assert!(x.test(CpuInfo::INIT));
assert!(x.test(CpuInfo::HAS_LSE));
assert!(x.test(CpuInfo::HAS_LSE2));
assert!(!x.test(CpuInfo::HAS_LSE128));
assert!(!x.test(CpuInfo::HAS_RCPC3));
x.set(CpuInfo::HAS_LSE128);
assert!(x.test(CpuInfo::INIT));
assert!(x.test(CpuInfo::HAS_LSE));
assert!(x.test(CpuInfo::HAS_LSE2));
assert!(x.test(CpuInfo::HAS_LSE128));
assert!(!x.test(CpuInfo::HAS_RCPC3));
x.set(CpuInfo::HAS_RCPC3);
assert!(x.test(CpuInfo::INIT));
assert!(x.test(CpuInfo::HAS_LSE));
assert!(x.test(CpuInfo::HAS_LSE2));
assert!(x.test(CpuInfo::HAS_LSE128));
assert!(x.test(CpuInfo::HAS_RCPC3));
}
#[cfg(target_arch = "x86_64")]
{
assert!(!x.test(CpuInfo::INIT));
assert!(!x.test(CpuInfo::HAS_CMPXCHG16B));
assert!(!x.test(CpuInfo::HAS_VMOVDQA_ATOMIC));
x.set(CpuInfo::INIT);
assert!(x.test(CpuInfo::INIT));
assert!(!x.test(CpuInfo::HAS_CMPXCHG16B));
assert!(!x.test(CpuInfo::HAS_VMOVDQA_ATOMIC));
x.set(CpuInfo::HAS_CMPXCHG16B);
assert!(x.test(CpuInfo::INIT));
assert!(x.test(CpuInfo::HAS_CMPXCHG16B));
assert!(!x.test(CpuInfo::HAS_VMOVDQA_ATOMIC));
x.set(CpuInfo::HAS_VMOVDQA_ATOMIC);
assert!(x.test(CpuInfo::INIT));
assert!(x.test(CpuInfo::HAS_CMPXCHG16B));
assert!(x.test(CpuInfo::HAS_VMOVDQA_ATOMIC));
}
#[cfg(target_arch = "powerpc64")]
{
assert!(!x.test(CpuInfo::INIT));
assert!(!x.test(CpuInfo::HAS_QUADWORD_ATOMICS));
x.set(CpuInfo::INIT);
assert!(x.test(CpuInfo::INIT));
assert!(!x.test(CpuInfo::HAS_QUADWORD_ATOMICS));
x.set(CpuInfo::HAS_QUADWORD_ATOMICS);
assert!(x.test(CpuInfo::INIT));
assert!(x.test(CpuInfo::HAS_QUADWORD_ATOMICS));
}
}
#[test]
fn print_features() {
use std::{fmt::Write as _, io::Write, string::String};
let mut features = String::new();
macro_rules! print_feature {
($name:expr, $enabled:expr $(,)?) => {{
let _ = writeln!(features, " {}: {}", $name, $enabled);
}};
}
#[cfg(target_arch = "aarch64")]
{
features.push_str("run-time:\n");
print_feature!("lse", detect().test(CpuInfo::HAS_LSE));
print_feature!("lse2", detect().test(CpuInfo::HAS_LSE2));
print_feature!("lse128", detect().test(CpuInfo::HAS_LSE128));
print_feature!("rcpc3", detect().test(CpuInfo::HAS_RCPC3));
features.push_str("compile-time:\n");
print_feature!(
"lse",
cfg!(any(target_feature = "lse", portable_atomic_target_feature = "lse")),
);
print_feature!(
"lse2",
cfg!(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
);
}
#[cfg(target_arch = "x86_64")]
{
features.push_str("run-time:\n");
print_feature!("cmpxchg16b", detect().test(CpuInfo::HAS_CMPXCHG16B));
print_feature!("vmovdqa-atomic", detect().test(CpuInfo::HAS_VMOVDQA_ATOMIC));
features.push_str("compile-time:\n");
print_feature!(
"cmpxchg16b",
cfg!(any(
target_feature = "cmpxchg16b",
portable_atomic_target_feature = "cmpxchg16b",
)),
);
}
#[cfg(target_arch = "powerpc64")]
{
features.push_str("run-time:\n");
print_feature!("quadword-atomics", detect().test(CpuInfo::HAS_QUADWORD_ATOMICS));
features.push_str("compile-time:\n");
print_feature!(
"quadword-atomics",
cfg!(any(
target_feature = "quadword-atomics",
portable_atomic_target_feature = "quadword-atomics",
)),
);
}
let stdout = std::io::stderr();
let mut stdout = stdout.lock();
let _ = stdout.write_all(features.as_bytes());
}
#[cfg(target_arch = "x86_64")]
#[test]
#[cfg_attr(portable_atomic_test_outline_atomics_detect_false, ignore)]
fn test_detect() {
if detect().has_cmpxchg16b() {
assert!(detect().test(CpuInfo::HAS_CMPXCHG16B));
} else {
assert!(!detect().test(CpuInfo::HAS_CMPXCHG16B));
}
if detect().has_vmovdqa_atomic() {
assert!(detect().test(CpuInfo::HAS_VMOVDQA_ATOMIC));
} else {
assert!(!detect().test(CpuInfo::HAS_VMOVDQA_ATOMIC));
}
}
#[cfg(target_arch = "aarch64")]
#[test]
#[cfg_attr(portable_atomic_test_outline_atomics_detect_false, ignore)]
fn test_detect() {
let proc_cpuinfo = test_helper::cpuinfo::ProcCpuinfo::new();
if detect().has_lse() {
assert!(detect().test(CpuInfo::HAS_LSE));
if let Ok(proc_cpuinfo) = proc_cpuinfo {
assert!(proc_cpuinfo.lse);
}
} else {
assert!(!detect().test(CpuInfo::HAS_LSE));
if let Ok(proc_cpuinfo) = proc_cpuinfo {
assert!(!proc_cpuinfo.lse);
}
}
if detect().has_lse2() {
assert!(detect().test(CpuInfo::HAS_LSE));
assert!(detect().test(CpuInfo::HAS_LSE2));
if let Ok(test_helper::cpuinfo::ProcCpuinfo { lse2: Some(lse2), .. }) = proc_cpuinfo {
assert!(lse2);
}
} else {
assert!(!detect().test(CpuInfo::HAS_LSE2));
if let Ok(test_helper::cpuinfo::ProcCpuinfo { lse2: Some(lse2), .. }) = proc_cpuinfo {
assert!(!lse2);
}
}
if detect().has_lse128() {
assert!(detect().test(CpuInfo::HAS_LSE));
assert!(detect().test(CpuInfo::HAS_LSE2));
assert!(detect().test(CpuInfo::HAS_LSE128));
} else {
assert!(!detect().test(CpuInfo::HAS_LSE128));
}
if detect().has_rcpc3() {
assert!(detect().test(CpuInfo::HAS_RCPC3));
} else {
assert!(!detect().test(CpuInfo::HAS_RCPC3));
}
}
#[cfg(target_arch = "powerpc64")]
#[test]
#[cfg_attr(portable_atomic_test_outline_atomics_detect_false, ignore)]
fn test_detect() {
let proc_cpuinfo = test_helper::cpuinfo::ProcCpuinfo::new();
if detect().has_quadword_atomics() {
assert!(detect().test(CpuInfo::HAS_QUADWORD_ATOMICS));
if let Ok(proc_cpuinfo) = proc_cpuinfo {
assert!(proc_cpuinfo.power8);
}
} else {
assert!(!detect().test(CpuInfo::HAS_QUADWORD_ATOMICS));
if let Ok(proc_cpuinfo) = proc_cpuinfo {
assert!(!proc_cpuinfo.power8);
}
}
}
}