#[cfg(target_arch = "x86_64")]
fn detect_x86_64() -> Detected {
let caps_static = caps_static();
#[cfg(feature = "std")]
let (runtime_caps, is_amd, family, model) = {
let batch = cpuid_batch_x86_64();
(batch.caps, batch.is_amd, batch.family, batch.model)
};
#[cfg(feature = "std")]
let mut caps = caps_static.union(runtime_caps);
#[cfg(not(feature = "std"))]
let caps = caps_static;
#[cfg(feature = "std")]
{
use crate::platform::caps::x86;
if is_intel_hybrid(is_amd, family, model) && !hybrid_avx512_override() {
caps = caps
.difference(x86::AVX512F)
.difference(x86::AVX512DQ)
.difference(x86::AVX512IFMA)
.difference(x86::AVX512CD)
.difference(x86::AVX512BW)
.difference(x86::AVX512VL)
.difference(x86::AVX512VBMI)
.difference(x86::AVX512VBMI2)
.difference(x86::AVX512VNNI)
.difference(x86::AVX512BITALG)
.difference(x86::AVX512VPOPCNTDQ)
.difference(x86::AVX512BF16)
.difference(x86::AVX512FP16)
.difference(x86::VPCLMULQDQ)
.difference(x86::VAES)
.difference(x86::GFNI)
.difference(x86::AVX10_1)
.difference(x86::AVX10_2);
}
}
Detected {
caps,
arch: Arch::X86_64,
}
}
#[cfg(target_arch = "x86")]
fn detect_x86() -> Detected {
let mut caps = caps_static();
#[cfg(feature = "std")]
{
use crate::platform::caps::x86;
if std::arch::is_x86_feature_detected!("sse2") {
caps |= x86::SSE2;
}
caps |= runtime_x86_32();
}
Detected {
caps,
arch: Arch::X86,
}
}
#[cfg(all(target_arch = "x86_64", feature = "std"))]
struct CpuidBatch {
caps: Caps,
is_amd: bool,
family: u32,
model: u32,
}
#[cfg(all(target_arch = "x86_64", feature = "std"))]
#[allow(unsafe_code)]
fn cpuid_batch_x86_64() -> CpuidBatch {
use core::arch::x86_64::{__cpuid, __cpuid_count, _xgetbv};
use crate::platform::caps::x86;
const XCR0_AVX_MASK: u64 = 0x6;
const XCR0_AVX512_MASK: u64 = 0xE0;
let mut caps = Caps::NONE;
let cpuid0 = __cpuid(0);
let is_amd = cpuid0.ebx == 0x6874_7541;
let cpuid1 = __cpuid(1);
let base_family = (cpuid1.eax >> 8) & 0xF;
let ext_family = (cpuid1.eax >> 20) & 0xFF;
let family = base_family + ext_family;
let base_model = (cpuid1.eax >> 4) & 0xF;
let ext_model = (cpuid1.eax >> 16) & 0xF;
let model = if base_family == 6 || base_family == 15 {
base_model + (ext_model << 4)
} else {
base_model
};
let osxsave = cpuid1.ecx & (1 << 27) != 0;
let xcr0 = if osxsave {
unsafe { _xgetbv(0) }
} else {
0
};
let os_avx = (xcr0 & XCR0_AVX_MASK) == XCR0_AVX_MASK;
let os_avx512 = os_avx && (xcr0 & XCR0_AVX512_MASK) == XCR0_AVX512_MASK;
if cpuid1.ecx & (1 << 0) != 0 {
caps |= x86::SSE3;
}
if cpuid1.ecx & (1 << 9) != 0 {
caps |= x86::SSSE3;
}
if cpuid1.ecx & (1 << 19) != 0 {
caps |= x86::SSE41;
}
if cpuid1.ecx & (1 << 20) != 0 {
caps |= x86::SSE42;
}
if cpuid1.ecx & (1 << 23) != 0 {
caps |= x86::POPCNT;
}
if cpuid1.ecx & (1 << 25) != 0 {
caps |= x86::AESNI;
}
if cpuid1.ecx & (1 << 1) != 0 {
caps |= x86::PCLMULQDQ;
}
if cpuid1.ecx & (1 << 30) != 0 {
caps |= x86::RDRAND;
}
if os_avx {
if cpuid1.ecx & (1 << 28) != 0 {
caps |= x86::AVX;
}
if cpuid1.ecx & (1 << 12) != 0 {
caps |= x86::FMA;
}
if cpuid1.ecx & (1 << 29) != 0 {
caps |= x86::F16C;
}
}
let cpuid7 = __cpuid_count(7, 0);
if cpuid7.ebx & (1 << 3) != 0 {
caps |= x86::BMI1;
}
if cpuid7.ebx & (1 << 8) != 0 {
caps |= x86::BMI2;
}
if cpuid7.ebx & (1 << 19) != 0 {
caps |= x86::ADX;
}
if cpuid7.ebx & (1 << 29) != 0 {
caps |= x86::SHA;
}
if os_avx && cpuid7.ebx & (1 << 5) != 0 {
caps |= x86::AVX2;
}
if os_avx512 {
if cpuid7.ebx & (1 << 16) != 0 {
caps |= x86::AVX512F;
}
if cpuid7.ebx & (1 << 17) != 0 {
caps |= x86::AVX512DQ;
}
if cpuid7.ebx & (1 << 21) != 0 {
caps |= x86::AVX512IFMA;
}
if cpuid7.ebx & (1 << 28) != 0 {
caps |= x86::AVX512CD;
}
if cpuid7.ebx & (1 << 30) != 0 {
caps |= x86::AVX512BW;
}
if cpuid7.ebx & (1 << 31) != 0 {
caps |= x86::AVX512VL;
}
if cpuid7.ecx & (1 << 1) != 0 {
caps |= x86::AVX512VBMI;
}
if cpuid7.ecx & (1 << 6) != 0 {
caps |= x86::AVX512VBMI2;
}
if cpuid7.ecx & (1 << 11) != 0 {
caps |= x86::AVX512VNNI;
}
if cpuid7.ecx & (1 << 12) != 0 {
caps |= x86::AVX512BITALG;
}
if cpuid7.ecx & (1 << 14) != 0 {
caps |= x86::AVX512VPOPCNTDQ;
}
if cpuid7.ecx & (1 << 8) != 0 {
caps |= x86::GFNI;
}
if cpuid7.ecx & (1 << 9) != 0 {
caps |= x86::VAES;
}
if cpuid7.ecx & (1 << 10) != 0 {
caps |= x86::VPCLMULQDQ;
}
}
if cpuid7.edx & (1 << 18) != 0 {
caps |= x86::RDSEED;
}
if cpuid7.edx & (1 << 24) != 0 {
caps |= x86::AMX_TILE;
}
if cpuid7.edx & (1 << 22) != 0 {
caps |= x86::AMX_BF16;
}
if cpuid7.edx & (1 << 25) != 0 {
caps |= x86::AMX_INT8;
}
let cpuid7_1 = __cpuid_count(7, 1);
if cpuid7_1.eax & (1 << 0) != 0 {
caps |= x86::SHA512;
}
if os_avx512 {
if cpuid7_1.eax & (1 << 4) != 0 {
caps |= x86::AVX512BF16;
}
if cpuid7_1.eax & (1 << 5) != 0 {
caps |= x86::AVX512FP16;
}
}
if cpuid7_1.eax & (1 << 21) != 0 {
caps |= x86::AMX_FP16;
}
if cpuid7_1.eax & (1 << 8) != 0 {
caps |= x86::AMX_COMPLEX;
}
if os_avx512 && cpuid0.eax >= 0x24 {
let cpuid24 = __cpuid_count(0x24, 0);
let avx10_version = cpuid24.ebx & 0xFF;
if avx10_version >= 1 {
caps |= x86::AVX10_1;
}
if avx10_version >= 2 {
caps |= x86::AVX10_2;
}
}
if cpuid0.eax >= 0x29 {
let cpuid29 = __cpuid_count(0x29, 0);
if cpuid29.ebx & 1 != 0 {
caps |= x86::APX;
}
}
let cpuid_ext = __cpuid(0x8000_0001);
if cpuid_ext.ecx & (1 << 5) != 0 {
caps |= x86::LZCNT;
}
if cpuid_ext.ecx & (1 << 6) != 0 {
caps |= x86::SSE4A;
}
if is_amd {
caps |= x86::AMD;
if family >= 0x1A {
caps |= x86::AMD_ZEN5;
}
}
CpuidBatch {
caps,
is_amd,
family,
model,
}
}
#[cfg(all(target_arch = "x86", feature = "std"))]
#[allow(unsafe_code)]
fn runtime_x86_32() -> Caps {
use core::arch::x86::{__cpuid, __cpuid_count};
use crate::platform::caps::x86;
let mut caps = Caps::NONE;
let cpuid1 = unsafe { __cpuid(1) };
if cpuid1.ecx & (1 << 0) != 0 {
caps |= x86::SSE3;
}
if cpuid1.ecx & (1 << 9) != 0 {
caps |= x86::SSSE3;
}
if cpuid1.ecx & (1 << 19) != 0 {
caps |= x86::SSE41;
}
if cpuid1.ecx & (1 << 20) != 0 {
caps |= x86::SSE42;
}
if cpuid1.ecx & (1 << 1) != 0 {
caps |= x86::PCLMULQDQ;
}
if cpuid1.ecx & (1 << 25) != 0 {
caps |= x86::AESNI;
}
caps
}
#[cfg(all(any(target_arch = "x86_64", target_arch = "x86"), feature = "std"))]
fn hybrid_avx512_override() -> bool {
std::env::var("RSCRYPTO_FORCE_AVX512")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
#[cfg(all(any(target_arch = "x86_64", target_arch = "x86"), feature = "std"))]
fn is_intel_hybrid(is_amd: bool, family: u32, model: u32) -> bool {
if is_amd {
return false;
}
if family != 6 {
return false;
}
matches!(
model,
0x97 | 0x9A | 0x9C | 0xB7 | 0xBA | 0xBF | 0xAA | 0xAC | 0xBD | 0xC5 | 0xC6 )
}