Skip to content

Commit 2efe18a

Browse files
committed
[x86] implement cpuid intrinsics
1 parent 5db64a7 commit 2efe18a

File tree

2 files changed

+122
-1
lines changed

2 files changed

+122
-1
lines changed

src/x86/cpuid.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//! `cpuid` intrinsics
2+
3+
#![cfg_attr(feature = "cargo-clippy", allow(stutter))]
4+
5+
#[cfg(test)]
6+
use stdsimd_test::assert_instr;
7+
8+
/// Result of the `cpuid` instruction.
9+
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
10+
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
11+
pub struct CpuidResult {
12+
/// EAX register.
13+
pub eax: u32,
14+
/// EBX register.
15+
pub ebx: u32,
16+
/// ECX register.
17+
pub ecx: u32,
18+
/// EDX register.
19+
pub edx: u32,
20+
}
21+
22+
/// `cpuid` instruction.
23+
///
24+
/// The [CPUID Wikipedia page][wiki_cpuid] contains how to query which
25+
/// information using the `eax` and `ecx` registers, and the format in
26+
/// which this information is returned in `eax...edx`.
27+
///
28+
/// The `has_cpuid()` intrinsics can be used to query whether the `cpuid`
29+
/// instruction is available.
30+
///
31+
/// The definitive references are:
32+
/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
33+
/// Instruction Set Reference, A-Z][intel64_ref].
34+
/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
35+
/// System Instructions][amd64_ref].
36+
///
37+
/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
38+
/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
39+
/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
40+
#[inline(always)]
41+
#[cfg_attr(test, assert_instr(cpuid))]
42+
pub unsafe fn __cpuid_count(eax: u32, ecx: u32) -> CpuidResult {
43+
let mut r = ::std::mem::uninitialized::<CpuidResult>();
44+
asm!("cpuid"
45+
: "={eax}"(r.eax), "={ebx}"(r.ebx), "={ecx}"(r.ecx), "={edx}"(r.edx)
46+
: "{eax}"(eax), "{ecx}"(ecx)
47+
: :);
48+
r
49+
}
50+
51+
/// `cpuid` instruction.
52+
///
53+
/// See `__cpuid_count`.
54+
#[inline(always)]
55+
#[cfg_attr(test, assert_instr(cpuid))]
56+
pub unsafe fn __cpuid(eax: u32) -> CpuidResult {
57+
__cpuid_count(eax, 0)
58+
}
59+
60+
/// Does the host support the `cpuid` instruction?
61+
#[inline(always)]
62+
pub fn has_cpuid() -> bool {
63+
#[cfg(target_arch = "x86_64")]
64+
{
65+
true
66+
}
67+
#[cfg(target_arch = "x86")]
68+
{
69+
use super::ia32::{__readeflags, __writeeflags};
70+
71+
// On `x86` the `cpuid` instruction is not always available.
72+
// This follows the approach indicated in:
73+
// http://wiki.osdev.org/CPUID#Checking_CPUID_availability
74+
unsafe {
75+
// Read EFLAGS:
76+
let eflags: u32 = __readeflags();
77+
78+
// Invert the ID bit in EFLAGS:
79+
let eflags_mod: u32 = eflags | 0x0020_0000;
80+
81+
// Store the modified EFLAGS (ID bit may or may not be inverted)
82+
__writeeflags(eflags_mod);
83+
84+
// Read EFLAGS again:
85+
let eflags_after: u32 = __readeflags();
86+
87+
// Check if the ID bit changed:
88+
eflags_after != eflags
89+
}
90+
}
91+
}
92+
93+
#[cfg(test)]
94+
mod tests {
95+
use super::*;
96+
97+
#[test]
98+
fn test_always_has_cpuid() {
99+
// all currently-tested targets have the instruction
100+
// FIXME: add targets without `cpuid` to CI
101+
assert!(has_cpuid());
102+
}
103+
104+
#[cfg(target_arch = "x86")]
105+
#[test]
106+
fn test_has_cpuid() {
107+
use vendor::__readeflags;
108+
unsafe {
109+
let before = __readeflags();
110+
111+
if has_cpuid() {
112+
assert!(before != __readeflags());
113+
} else {
114+
assert!(before == __readeflags());
115+
}
116+
}
117+
}
118+
119+
}

src/x86/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! `x86` and `x86_64` intrinsics.
22
33
pub use self::ia32::*;
4+
pub use self::cpuid::*;
45
pub use self::xsave::*;
56

67
pub use self::sse::*;
@@ -31,8 +32,9 @@ mod macros;
3132
#[macro_use]
3233
mod runtime;
3334

34-
mod xsave;
3535
mod ia32;
36+
mod cpuid;
37+
mod xsave;
3638

3739
mod sse;
3840
mod sse2;

0 commit comments

Comments
 (0)