plat/cpu.rs
1// cpu -- CPU feature detection for SIMD dispatch [src/core/cpu.s]
2//
3// cpu_features() -> CPU_* bits cached; SSE2 is the baseline and has no bit.
4// `--simd sse2` on the command line forces 0 (baseline
5// kernels only, to compare results)
6// cpu_brand() -> String the processor name ("" when unknown)
7//
8// The detection itself lives in rt::cpu (the simd crate dispatches on it); the process entry
9// applies `--simd sse2` before the app starts.
10
11use alloc::string::String;
12
13pub use rt::cpu::{CPU_AVX2, CPU_BMI2, CPU_FMA, CPU_POPCNT, CPU_SSE41};
14use rt::cpu::cpuid;
15
16/// CPU_* bits (cached; rt::cpu) [cpu_features]
17pub fn cpu_features() -> u32 {
18 rt::cpu::features()
19}
20
21/// `--simd sse2` on the command line: no optional features (called by the process entry)
22pub(crate) fn simd_override() {
23 if crate::arg_value("--simd").as_deref() == Some("sse2") {
24 rt::cpu::force_baseline();
25 }
26}
27
28/// the processor name from CPUID 0x80000002..4, blanks trimmed [cpu_brand]
29pub fn cpu_brand() -> String {
30 if cpuid(0x8000_0000).eax < 0x8000_0004 {
31 return String::new();
32 }
33 let mut b = [0u8; 48];
34 for (i, leaf) in (0x8000_0002u32..=0x8000_0004).enumerate() {
35 let r = cpuid(leaf);
36 for (j, v) in [r.eax, r.ebx, r.ecx, r.edx].iter().enumerate() {
37 b[i * 16 + j * 4..i * 16 + j * 4 + 4].copy_from_slice(&v.to_le_bytes());
38 }
39 }
40 let end = b.iter().position(|&c| c == 0).unwrap_or(48);
41 String::from_utf8_lossy(&b[..end]).trim_matches(' ').into()
42}
43
44/// run two 256-bit AVX2 instructions (they fault without OS support); only call it when
45/// cpu_features() reports CPU_AVX2 [plattest t_cpu]
46#[cfg(target_arch = "x86_64")]
47pub fn avx2_probe() -> bool {
48 if cpu_features() & CPU_AVX2 == 0 {
49 return false;
50 }
51 // SAFETY: AVX2 and the YMM state are enabled (checked above); xmm0 is declared clobbered
52 // (the upper half is zeroed again by vzeroupper, which ends the 256-bit code).
53 unsafe {
54 core::arch::asm!("vpxor ymm0, ymm0, ymm0", "vpaddd ymm0, ymm0, ymm0", "vzeroupper",
55 out("xmm0") _, options(nomem, nostack));
56 }
57 true
58}
59
60/// the stack pointer is 16-byte aligned where a call could happen (the compiler relies on
61/// it: a thread started with a misaligned stack shows here) [plattest th_raw]
62#[cfg(target_arch = "x86_64")]
63pub fn stack_aligned() -> bool {
64 let sp: usize;
65 // SAFETY: reads rsp only; without `nostack` the compiler aligns the stack for a call.
66 unsafe { core::arch::asm!("mov {}, rsp", out(reg) sp, options(nomem, preserves_flags)) };
67 sp & 15 == 0
68}
69
70/// The browser does not advertise or execute native AVX2 instructions.
71#[cfg(any(target_arch = "wasm32", all(target_os = "android", not(target_arch = "x86_64"))))]
72pub fn avx2_probe() -> bool { false }
73
74/// WebAssembly's compiler-managed value/call stacks satisfy their platform ABI.
75#[cfg(any(target_arch = "wasm32", all(target_os = "android", not(target_arch = "x86_64"))))]
76pub fn stack_aligned() -> bool { true }