Rust
CPUID
Invoke special instructions like CPUID.
CPUID
x86's cpuid instruction gives information about the processor: the vendor name, supported instruction sets (SSE, AVX, AVX-512...). A "leaf" number is written to eax, and the result comes back in eax, ebx, ecx, edx. Instead of hand-written assembly, the standard library offers ready tools:
use std::arch::x86_64::__cpuid;
let r = unsafe { __cpuid(0) }; // leaf 0: vendor
// r.ebx, r.edx, r.ecx → "GenuineIntel" or "AuthenticAMD"
if is_x86_feature_detected!("avx2") {
// the AVX2 path
}
is_x86_feature_detected! caches the result; it's ideal for picking the best implementation at runtime.
Runtime selection
Several versions of the same function are written for different instruction sets and the best one is chosen at runtime:
fn toplam(v: &[f32]) -> f32 {
#[cfg(target_arch = "x86_64")]
if is_x86_feature_detected!("avx2") {
return unsafe { toplam_avx2(v) };
}
v.iter().sum()
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn toplam_avx2(v: &[f32]) -> f32 { v.iter().sum() }
#[target_feature] recompiles the function with AVX2 enabled; the compiler can use wide vectors in it.
Exercise Processor features
On x86-64, print the results of __cpuid(0).eax > 0 (the highest supported leaf) and is_x86_feature_detected!("sse2") separated by a space; on other architectures print true true. (Every x86-64 processor has SSE2.)
Show example solution
#[cfg(target_arch = "x86_64")]
fn islemci_bilgisi() -> (bool, bool) {
// SAFETY: cpuid her x86-64 işlemcide vardır.
let r = unsafe { std::arch::x86_64::__cpuid(0) };
(r.eax > 0, is_x86_feature_detected!("sse2"))
}
#[cfg(not(target_arch = "x86_64"))]
fn islemci_bilgisi() -> (bool, bool) {
(true, true)
}
fn main() {
let (a, b) = islemci_bilgisi();
println!("{} {}", a, b);
}
Test yourself
-
Why is writing <code>cpuid</code> by hand with <code>asm!</code> tricky?
- It's very slow
- It isn't tricky
- LLVM reserves rbx, so it can't be an operand; it must be saved and restored
- cpuid doesn't exist
Show answer
LLVM reserves rbx, so it can't be an operand; it must be saved and restored — std::arch::__cpuid handles this detail correctly; in most cases intrinsics are safer than raw assembly.
-
Feature check
if ____!("avx2") {
// AVX2'li yol
}
Show answer
if is_x86_feature_detected!("avx2") {
// AVX2'li yol
}
-
Tool and timing
- #[cfg(target_feature)]
- Compile time
- is_x86_feature_detected!
- Runtime
- __cpuid(0)
- Vendor info
- #[target_feature(enable)]
- Per-function feature
Next lesson: Summary - Inline assembly