use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicU8, Ordering};
use super::InstructionSet;
const UNINITIALIZED: u8 = 0;
const INITIALIZING: u8 = 1;
const INITIALIZED: u8 = 2;
pub struct DetectInstructionSet {
state: AtomicU8,
isa: UnsafeCell<Option<InstructionSet>>,
}
unsafe impl Sync for DetectInstructionSet {}
impl DetectInstructionSet {
pub const fn new() -> Self {
Self {
state: AtomicU8::new(UNINITIALIZED),
isa: UnsafeCell::new(None),
}
}
#[inline(never)] #[rustfmt::skip]
fn initialize(&self) -> InstructionSet {
let previous_state = self.state.compare_exchange(UNINITIALIZED, INITIALIZING, Ordering::AcqRel, Ordering::Acquire);
match previous_state.unwrap_or_else(|s| s) {
INITIALIZING => while self.state.load(Ordering::Acquire) != INITIALIZED {
core::hint::spin_loop(); }
UNINITIALIZED => {
unsafe { *self.isa.get() = Some(Self::detect_internal()) };
self.state.store(INITIALIZED, Ordering::Release);
}
_ => {} }
unsafe { (*self.isa.get()).unwrap_unchecked() }
}
#[inline(always)]
pub fn get_or_init(&self) -> InstructionSet {
if self.state.load(Ordering::Acquire) == INITIALIZED {
return unsafe { (*self.isa.get()).unwrap_unchecked() };
}
self.initialize()
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn detect_internal() -> InstructionSet {
let features = crate::cpu::x86::features();
let mut best = InstructionSet::Scalar;
if features.avx512f {
best = InstructionSet::X86V4; } else if features.avx2 && features.fma && features.popcnt {
best = InstructionSet::X86V3;
} else if features.sse42 && features.popcnt {
best = InstructionSet::X86V2;
} else if features.sse2 {
best = InstructionSet::X86V1;
}
best
}
}