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 mut best = InstructionSet::Scalar;
if core_detect::is_x86_feature_detected!("avx512f") {
best = InstructionSet::X86V4; } else if core_detect::is_x86_feature_detected!("avx2") && core_detect::is_x86_feature_detected!("fma") {
best = InstructionSet::X86V3;
} else if core_detect::is_x86_feature_detected!("sse4.2") && core_detect::is_x86_feature_detected!("popcnt") {
best = InstructionSet::X86V2;
} else if core_detect::is_x86_feature_detected!("sse2") {
best = InstructionSet::X86V1;
}
best
}
#[cfg(all(feature = "neon", any(target_arch = "arm", target_arch = "aarch64")))]
fn detect_internal() -> InstructionSet {
InstructionSet::NEON }
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
fn detect_internal() -> InstructionSet {
InstructionSet::WASM32 }
#[cfg(all(feature = "wasm", target_arch = "wasm64"))]
fn detect_internal() -> InstructionSet {
InstructionSet::WASM64 }
#[cfg(not(any(
all(feature = "neon", any(target_arch = "arm", target_arch = "aarch64")),
all(feature = "wasm", any(target_arch = "wasm32", target_arch = "wasm64")),
any(target_arch = "x86", target_arch = "x86_64")
)))]
fn detect_internal() -> InstructionSet {
InstructionSet::Scalar }
}