#![cfg_attr(portable_atomic_sanitize_thread, allow(dead_code))]
#[cfg(any(target_env = "sgx", miri))]
compile_error!("internal error: this module is not supported on this environment");
include!("common.rs");
#[cfg(not(portable_atomic_no_asm))]
use core::arch::asm;
use core::arch::x86_64::CpuidResult;
#[cfg(not(target_env = "sgx"))]
fn __cpuid(leaf: u32) -> CpuidResult {
let eax;
let mut ebx;
let ecx;
let edx;
unsafe {
asm!(
"mov {ebx_tmp:r}, rbx", "cpuid",
"xchg {ebx_tmp:r}, rbx", ebx_tmp = out(reg) ebx,
inout("eax") leaf => eax,
inout("ecx") 0 => ecx,
out("edx") edx,
options(nostack, preserves_flags),
);
}
CpuidResult { eax, ebx, ecx, edx }
}
const _VENDOR_ID_INTEL: [u32; 3] = _vender(b"GenuineIntel"); const _VENDOR_ID_INTEL2: [u32; 3] = _vender(b"GenuineIotel"); const _VENDOR_ID_AMD: [u32; 3] = _vender(b"AuthenticAMD"); const _VENDOR_ID_CENTAUR: [u32; 3] = _vender(b"CentaurHauls"); const _VENDOR_ID_ZHAOXIN: [u32; 3] = _vender(b" Shanghai "); const fn _vender(b: &[u8; 12]) -> [u32; 3] {
[
u32::from_ne_bytes([b[0], b[1], b[2], b[3]]),
u32::from_ne_bytes([b[4], b[5], b[6], b[7]]),
u32::from_ne_bytes([b[8], b[9], b[10], b[11]]),
]
}
fn _vendor_id() -> [u32; 3] {
let CpuidResult { ebx, ecx, edx, .. } = __cpuid(0);
[ebx, edx, ecx]
}
fn _vendor_has_vmovdqa_atomic(vendor_id: [u32; 3], family: u32) -> bool {
vendor_id == _VENDOR_ID_INTEL
|| vendor_id == _VENDOR_ID_INTEL2
|| vendor_id == _VENDOR_ID_AMD
|| vendor_id == _VENDOR_ID_ZHAOXIN
|| vendor_id == _VENDOR_ID_CENTAUR && family > 6
}
#[cold]
fn _detect(info: &mut CpuInfo) {
let CpuidResult {
#[cfg(target_feature = "sse")]
eax: proc_info_eax,
ecx: proc_info_ecx,
..
} = __cpuid(1);
if test(proc_info_ecx, 13) {
info.set(CpuInfo::HAS_CMPXCHG16B);
}
#[cfg(target_feature = "sse")]
{
use core::arch::x86_64::_xgetbv;
let cpu_xsave = test(proc_info_ecx, 26);
if cpu_xsave {
let cpu_osxsave = test(proc_info_ecx, 27);
if cpu_osxsave {
let xcr0 = unsafe { _xgetbv(0) };
let os_avx_support = xcr0 & 6 == 6;
if os_avx_support && test(proc_info_ecx, 28) {
let vendor_id = _vendor_id();
let family = (proc_info_eax >> 8) & 0x0F;
if _vendor_has_vmovdqa_atomic(vendor_id, family) {
info.set(CpuInfo::HAS_VMOVDQA_ATOMIC);
}
}
}
}
}
}
#[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 std::{
io::{self, Write as _},
mem, str,
};
use super::*;
#[test]
#[cfg_attr(portable_atomic_test_outline_atomics_detect_false, ignore)]
fn test_cpuid() {
assert_eq!(std::is_x86_feature_detected!("cmpxchg16b"), detect().has_cmpxchg16b());
let vendor_id = _vendor_id();
{
let stdout = io::stderr();
let mut stdout = stdout.lock();
let _ = writeln!(
stdout,
"\n vendor_id: {} (ebx: {:x}, edx: {:x}, ecx: {:x})",
str::from_utf8(&unsafe { mem::transmute::<[u32; 3], [u8; 12]>(vendor_id) })
.unwrap(),
vendor_id[0],
vendor_id[1],
vendor_id[2],
);
}
let CpuidResult { eax: proc_info_eax, .. } = __cpuid(1);
let family = (proc_info_eax >> 8) & 0x0F;
if _vendor_has_vmovdqa_atomic(vendor_id, family) {
assert_eq!(std::is_x86_feature_detected!("avx"), detect().has_vmovdqa_atomic());
} else {
assert!(!detect().has_vmovdqa_atomic());
}
assert_eq!(
unsafe { mem::transmute::<[u32; 3], [u8; 12]>(_VENDOR_ID_INTEL) },
*b"GenuineIntel"
);
}
}