portable_atomic/imp/detect/
x86_64.rs
1#![cfg_attr(portable_atomic_sanitize_thread, allow(dead_code))]
10
11#[cfg(any(target_env = "sgx", miri))]
14compile_error!("internal error: this module is not supported on this environment");
15
16include!("common.rs");
17
18#[cfg(not(portable_atomic_no_asm))]
19use core::arch::asm;
20use core::arch::x86_64::CpuidResult;
21
22#[cfg(not(target_env = "sgx"))]
30fn __cpuid(leaf: u32) -> CpuidResult {
31 let eax;
32 let mut ebx;
33 let ecx;
34 let edx;
35 unsafe {
39 asm!(
40 "mov {ebx_tmp:r}, rbx", "cpuid",
42 "xchg {ebx_tmp:r}, rbx", ebx_tmp = out(reg) ebx,
44 inout("eax") leaf => eax,
45 inout("ecx") 0 => ecx,
46 out("edx") edx,
47 options(nostack, preserves_flags),
48 );
49 }
50 CpuidResult { eax, ebx, ecx, edx }
51}
52
53const _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] {
60 [
61 u32::from_ne_bytes([b[0], b[1], b[2], b[3]]),
62 u32::from_ne_bytes([b[4], b[5], b[6], b[7]]),
63 u32::from_ne_bytes([b[8], b[9], b[10], b[11]]),
64 ]
65}
66fn _vendor_id() -> [u32; 3] {
67 let CpuidResult { ebx, ecx, edx, .. } = __cpuid(0);
68 [ebx, edx, ecx]
69}
70fn _vendor_has_vmovdqa_atomic(vendor_id: [u32; 3], family: u32) -> bool {
71 vendor_id == _VENDOR_ID_INTEL
74 || vendor_id == _VENDOR_ID_INTEL2
75 || vendor_id == _VENDOR_ID_AMD
76 || vendor_id == _VENDOR_ID_ZHAOXIN
77 || vendor_id == _VENDOR_ID_CENTAUR && family > 6
78}
79
80#[cold]
81fn _detect(info: &mut CpuInfo) {
82 let CpuidResult {
83 #[cfg(target_feature = "sse")]
84 eax: proc_info_eax,
85 ecx: proc_info_ecx,
86 ..
87 } = __cpuid(1);
88
89 if test(proc_info_ecx, 13) {
91 info.set(CpuInfo::HAS_CMPXCHG16B);
92 }
93
94 #[cfg(target_feature = "sse")]
96 {
97 use core::arch::x86_64::_xgetbv;
98
99 let cpu_xsave = test(proc_info_ecx, 26);
101 if cpu_xsave {
102 let cpu_osxsave = test(proc_info_ecx, 27);
103 if cpu_osxsave {
104 let xcr0 = unsafe { _xgetbv(0) };
107 let os_avx_support = xcr0 & 6 == 6;
108 if os_avx_support && test(proc_info_ecx, 28) {
109 let vendor_id = _vendor_id();
110 let family = (proc_info_eax >> 8) & 0x0F;
111 if _vendor_has_vmovdqa_atomic(vendor_id, family) {
112 info.set(CpuInfo::HAS_VMOVDQA_ATOMIC);
113 }
114 }
115 }
116 }
117 }
118}
119
120#[allow(
121 clippy::alloc_instead_of_core,
122 clippy::std_instead_of_alloc,
123 clippy::std_instead_of_core,
124 clippy::undocumented_unsafe_blocks,
125 clippy::wildcard_imports
126)]
127#[cfg(test)]
128mod tests {
129 use std::{
130 io::{self, Write as _},
131 mem, str,
132 };
133
134 use super::*;
135
136 #[test]
137 #[cfg_attr(portable_atomic_test_outline_atomics_detect_false, ignore)]
138 fn test_cpuid() {
139 assert_eq!(std::is_x86_feature_detected!("cmpxchg16b"), detect().has_cmpxchg16b());
140 let vendor_id = _vendor_id();
141 {
142 let stdout = io::stderr();
143 let mut stdout = stdout.lock();
144 let _ = writeln!(
145 stdout,
146 "\n vendor_id: {} (ebx: {:x}, edx: {:x}, ecx: {:x})",
147 str::from_utf8(&unsafe { mem::transmute::<[u32; 3], [u8; 12]>(vendor_id) })
148 .unwrap(),
149 vendor_id[0],
150 vendor_id[1],
151 vendor_id[2],
152 );
153 }
154 let CpuidResult { eax: proc_info_eax, .. } = __cpuid(1);
155 let family = (proc_info_eax >> 8) & 0x0F;
156 if _vendor_has_vmovdqa_atomic(vendor_id, family) {
157 assert_eq!(std::is_x86_feature_detected!("avx"), detect().has_vmovdqa_atomic());
158 } else {
159 assert!(!detect().has_vmovdqa_atomic());
160 }
161 assert_eq!(
162 unsafe { mem::transmute::<[u32; 3], [u8; 12]>(_VENDOR_ID_INTEL) },
163 *b"GenuineIntel"
164 );
165 }
166}