Skip to main content

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 }