#![allow(non_camel_case_types)]
#![allow(dead_code)]
mod ffi {
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/detect_capabilities_bindings.rs"));
pub use ::core::ffi::c_int;
}
use ffi::*;
const SUPPORTED: c_int = 1;
const NOT_SUPPORTED: c_int = 0;
#[derive(Debug, Clone, Copy)]
pub struct RuntimeCapabilities {
pub x86_64_avx2: bool,
pub aarch64_sha3: bool,
pub armv81m_mve: bool,
}
impl RuntimeCapabilities {
pub fn probe() -> Self {
Self {
x86_64_avx2: check_capability(mld_sys_cap::X86_64_AVX2),
aarch64_sha3: check_capability(mld_sys_cap::AARCH64_SHA3),
armv81m_mve: check_capability(mld_sys_cap::ARMV81M_MVE),
}
}
}
pub fn mld_sys_cap_to_str(cap: mld_sys_cap::Type) -> &'static str {
match cap {
mld_sys_cap::X86_64_AVX2 => stringify!(mld_sys_cap::X86_64_AVX2),
mld_sys_cap::AARCH64_SHA3 => stringify!(mld_sys_cap::AARCH64_SHA3),
mld_sys_cap::ARMV81M_MVE => stringify!(mld_sys_cap::ARMV81M_MVE),
_ => unreachable!("Unknown mld_sys_cap value: {cap:?}"),
}
}
#[unsafe(no_mangle)]
#[inline(always)]
pub extern "C" fn mldrs_sys_check_capability(cap: mld_sys_cap::Type) -> c_int {
let is_supported: bool = check_capability(cap);
bool_to_c_int(is_supported)
}
#[inline(always)]
pub fn check_capability(cap: mld_sys_cap::Type) -> bool {
cfg_if::cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
x86_backend::check_capability(cap)
} else if #[cfg(any(target_arch = "aarch64"))] {
aarch64_backend::check_capability(cap)
} else {
let _ = cap;
false
}
}
}
#[inline(always)]
fn bool_to_c_int(v: bool) -> c_int {
if v { SUPPORTED } else { NOT_SUPPORTED }
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub(super) mod x86_backend {
use super::*;
cpufeatures::new!(cpufeats_avx2_flags, "avx2", "bmi2", "popcnt");
pub fn check_capability(cap: mld_sys_cap::Type) -> bool {
let token: cpufeats_avx2_flags::InitToken = cpufeats_avx2_flags::init();
match cap {
mld_sys_cap::X86_64_AVX2 => token.get() == true,
_ => false,
}
}
}
#[cfg(any(target_arch = "aarch64"))]
pub(super) mod aarch64_backend {
use super::*;
cpufeatures::new!(cpufeats_sha3_flags, "sha3");
pub fn check_capability(cap: mld_sys_cap::Type) -> bool {
let token: cpufeats_sha3_flags::InitToken = cpufeats_sha3_flags::init();
match cap {
mld_sys_cap::AARCH64_SHA3 => token.get() == true,
_ => false,
}
}
}
mod init_once {
pub use ::core::ffi::c_int;
#[derive(Copy, Clone, Debug)]
struct InitError();
type InitResult<T> = core::result::Result<T, InitError>;
#[inline(always)]
fn mldrs_init_sys_check_capability_wrapper() -> InitResult<()> {
unsafe extern "C" {
fn mldrs_init_sys_check_capability() -> c_int;
}
let result = unsafe { mldrs_init_sys_check_capability() };
if result != 1 {
Err(InitError())
} else {
Ok(())
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mldrs_call_once_init_sys_check_capability() {
use std::sync::OnceLock;
static MLDRS_INIT_SYS_CHECK_CAPABILITY_RESULT: OnceLock<InitResult<()>> = OnceLock::new();
let result = *MLDRS_INIT_SYS_CHECK_CAPABILITY_RESULT
.get_or_init(|| mldrs_init_sys_check_capability_wrapper());
if result.is_err() {
unreachable! {"mldrs_sys_check_capability() failed!"}
}
}
#[cfg(test)]
mod test {
use super::super::ffi;
use super::super::*;
use super::*;
use ffi::mldrs_test_mld_sys_check_capability;
#[test]
fn mldrs_init_sys_check_capability_fails_if_called_twice() {
let _ = mldrs_init_sys_check_capability_wrapper();
assert!(
mldrs_init_sys_check_capability_wrapper().is_err(),
"Should fail the second time"
);
}
fn assert_capability_result(cap: mld_sys_cap::Type, cap_name: &'static str) {
let got: c_int = unsafe { mldrs_test_mld_sys_check_capability(cap) };
println!("Testing `mld_sys_check_capability({cap_name})` returned {got:?}");
assert!(
got == SUPPORTED || got == NOT_SUPPORTED,
"unexpected capability result for {cap_name}: {got}"
);
let rs_got: c_int = mldrs_sys_check_capability(cap);
assert_eq!(got, rs_got, "mismatched capability result for {cap_name}");
}
macro_rules! capability_tests {
(
$(
$test_name:ident => $cap:path
),+ $(,)?
) => {
$(
#[test]
fn $test_name() {
println!("");
assert_capability_result($cap, stringify!($cap));
print!("... ");
}
)+
};
}
capability_tests! {
test_mld_sys_cap_x86_64_avx2 => mld_sys_cap::X86_64_AVX2,
test_mld_sys_cap_aarch64_sha3 => mld_sys_cap::AARCH64_SHA3,
test_mld_sys_cap_armv81m_mve => mld_sys_cap::ARMV81M_MVE,
}
#[test]
fn test_all_mld_sys_cap() {
use ffi::MLDRS_SYS_CAP_MAX_VALUE;
println!("\nTesting all capabilities");
let max = MLDRS_SYS_CAP_MAX_VALUE;
for cap in 0..=max {
let cap_name = mld_sys_cap_to_str(cap);
assert_capability_result(cap, cap_name);
}
print!("DONE\t... ");
}
}
}