Skip to content

Fix has_cpuid implementation #492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 25, 2018
71 changes: 41 additions & 30 deletions coresimd/x86/cpuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,46 @@ pub fn has_cpuid() -> bool {
}
#[cfg(target_arch = "x86")]
{
use coresimd::x86::{__readeflags, __writeeflags};

// On `x86` the `cpuid` instruction is not always available.
// This follows the approach indicated in:
// http://wiki.osdev.org/CPUID#Checking_CPUID_availability
unsafe {
// Read EFLAGS:
let eflags: u32 = __readeflags();

// Invert the ID bit in EFLAGS:
let eflags_mod: u32 = eflags | 0x0020_0000;

// Store the modified EFLAGS (ID bit may or may not be inverted)
__writeeflags(eflags_mod);

// Read EFLAGS again:
let eflags_after: u32 = __readeflags();

// Check if the ID bit changed:
eflags_after != eflags
// On `x86` the `cpuid` instruction is not always available.
// This follows the approach indicated in:
// http://wiki.osdev.org/CPUID#Checking_CPUID_availability
// https://software.intel.com/en-us/articles/using-cpuid-to-detect-the-presence-of-sse-41-and-sse-42-instruction-sets/
// which detects whether `cpuid` is available by checking whether the 21st bit of the EFLAGS register is modifiable or not.
// If it is, then `cpuid` is available.
let result: u32;
let _temp: u32;
unsafe {
asm!(r#"
# Read eflags into $0 and copy into $1:
pushfd
pop $0
mov $1, $0
# Flip 21st bit:
xor $0, 0x200000
# Set eflags:
push $0
popfd
# Read eflags again, if cpuid is available
# the 21st bit will be flipped, otherwise it
# it will have the same value as the original in $1:
pushfd
pop $0
# Xor'ing with the original eflags should have the
# 21st bit set to true if cpuid is available and zero
# otherwise. All other bits have not been modified and
# are zero:
xor $0, $1
"#
: "=r"(result), "=r"(_temp)
:
: "cc", "memory"
: "intel");
}
// Therefore, if result is 0, the bit was not modified and cpuid is
// not available. If cpuid is available, the bit was modified and
// result != 0.
result != 0
}
}
}
Expand Down Expand Up @@ -138,17 +158,8 @@ mod tests {
assert!(cpuid::has_cpuid());
}

#[cfg(target_arch = "x86")]
#[test]
fn test_has_cpuid() {
unsafe {
let before = __readeflags();

if cpuid::has_cpuid() {
assert!(before != __readeflags());
} else {
assert!(before == __readeflags());
}
}
fn test_has_cpuid_idempotent() {
assert_eq!(cpuid::has_cpuid(), cpuid::has_cpuid());
}
}