macro_rules! capability {
(
$(#[$meta:meta])*
$name:ident,
$cache:ident,
module = $module:ident,
arch = $arch:meta,
feature = $feature:literal,
env = $env:literal,
detect = $detect:expr
) => {
#[cfg($arch)]
static $cache: $crate::intrinsics::detect::Cache =
$crate::intrinsics::detect::Cache::new();
$(#[$meta])*
#[doc = concat!(
"\n\nObtain this token with [`Self::detect`]. It is `Copy`, so it can be ",
"passed to multiple backend calls. It does not enable compiler target ",
"features or validate any memory-safety requirements of an intrinsic.\n\n",
"Safe callers cannot construct the token directly:\n\n",
"```compile_fail,E0423\n",
"use tc_runtime::intrinsics::", stringify!($module), "::", stringify!($name), ";\n",
"let token = ", stringify!($name), "(());\n```",
)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct $name(());
impl $name {
#[doc = concat!(
"\n\n# Examples\n\n```\n",
"use tc_runtime::intrinsics::", stringify!($module), "::", stringify!($name), ";\n",
"fn select_backend(_proof: ", stringify!($name), ") -> &'static str {\n",
" // A backend can require the token in its public signature.\n",
" \"optimized\"\n}\n",
"let backend = match ", stringify!($name), "::detect() {\n",
" Some(proof) => select_backend(proof),\n",
" None => \"portable\",\n",
"};\n",
"assert_eq!(backend == \"optimized\", ", stringify!($name), "::is_enabled());\n```",
)]
pub fn detect() -> Option<Self> {
Self::is_enabled().then_some(Self(()))
}
#[cfg($arch)]
#[doc = concat!(
"Reports whether the capability is enabled on this processor.\n\n",
"The `", $feature, "` Cargo feature always returns `false`. With ",
"this crate's `std` feature enabled, the `", $env, "` environment ",
"variable has the same effect when it is present before the first ",
"call. Results are cached; configure overrides before detection starts. ",
"Disabling detection does not prevent the compiler from emitting ",
"instructions enabled by the build's target features.\n\n",
"Use [`Self::detect`] when a backend needs a proof token.\n\n",
"# Examples\n\n```\n",
"use tc_runtime::intrinsics::", stringify!($module), "::", stringify!($name), ";\n",
"assert_eq!(", stringify!($name), "::is_enabled(), ", stringify!($name), "::detect().is_some());\n",
"#[cfg(feature = ", stringify!($feature), ")]\n",
"assert!(!", stringify!($name), "::is_enabled());\n```",
)]
pub fn is_enabled() -> bool {
$cache.get_or_init(|| {
!cfg!(feature = $feature)
&& !$crate::intrinsics::detect::disabled_by_env($env)
&& $detect
})
}
#[doc = concat!(
"\n\n# Examples\n\n```\n",
"use tc_runtime::intrinsics::", stringify!($module), "::", stringify!($name), ";\n",
"const AVAILABLE: bool = ", stringify!($name), "::is_enabled();\n",
"assert!(!AVAILABLE);\n```",
)]
#[cfg(not($arch))]
pub const fn is_enabled() -> bool {
false
}
}
};
}
pub(crate) use capability;
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
pub(crate) use probe::{Cache, disabled_by_env};
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
mod probe {
use core::sync::atomic::{AtomicU8, Ordering};
const UNKNOWN: u8 = 0;
const DISABLED: u8 = 1;
const ENABLED: u8 = 2;
pub(crate) struct Cache(AtomicU8);
impl Cache {
pub(crate) const fn new() -> Self {
Self(AtomicU8::new(UNKNOWN))
}
pub(crate) fn get_or_init(&self, detect: impl FnOnce() -> bool) -> bool {
match self.0.load(Ordering::Relaxed) {
ENABLED => true,
DISABLED => false,
UNKNOWN => {
let enabled = detect();
self.0
.store(if enabled { ENABLED } else { DISABLED }, Ordering::Relaxed);
enabled
}
_ => unreachable!(),
}
}
}
#[cfg(feature = "std")]
pub(crate) fn disabled_by_env(name: &str) -> bool {
std::env::var_os(name).is_some()
}
#[cfg(not(feature = "std"))]
pub(crate) const fn disabled_by_env(_name: &str) -> bool {
false
}
}