use std::cell::Cell;
use std::sync::atomic::{AtomicU8, Ordering};
static BMI2: AtomicU8 = AtomicU8::new(0);
thread_local! {
static SCALAR_ONLY: Cell<bool> = const { Cell::new(false) };
}
#[inline]
pub fn bmi2() -> bool {
if SCALAR_ONLY.with(Cell::get) {
return false;
}
match BMI2.load(Ordering::Relaxed) {
2 => true,
1 => false,
_ => {
let yes = detect();
BMI2.store(if yes { 2 } else { 1 }, Ordering::Relaxed);
yes
}
}
}
#[cfg(target_arch = "x86_64")]
fn detect() -> bool {
std::arch::is_x86_feature_detected!("bmi1") && std::arch::is_x86_feature_detected!("bmi2") && std::arch::is_x86_feature_detected!("lzcnt")
}
#[cfg(not(target_arch = "x86_64"))]
fn detect() -> bool {
false
}
pub fn set_scalar_only(on: bool) {
SCALAR_ONLY.with(|c| c.set(on));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scalar_switch() {
set_scalar_only(true);
assert!(!bmi2());
set_scalar_only(false);
}
}