1//! Helpers for runtime target feature detection that are shared across architectures.
23// `AtomicU32` is preferred for a consistent size across targets.
4#[cfg(all(target_has_atomic = "ptr", not(target_has_atomic = "32")))]
5compile_error!("currently all targets that support `AtomicPtr` also support `AtomicU32`");
67use core::sync::atomic::{AtomicU32, Ordering};
89/// Given a list of identifiers, assign each one a unique sequential single-bit mask.
10#[allow(unused_macros)]
11macro_rules! unique_masks {
12 ($ty:ty, $($name:ident,)+) => {
13#[cfg(test)]
14pub const ALL: &[$ty] = &[$($name),+];
15#[cfg(test)]
16pub const NAMES: &[&str] = &[$(stringify!($name)),+];
1718unique_masks!(@one; $ty; 0; $($name,)+);
19 };
20// Matcher for a single value
21(@one; $_ty:ty; $_idx:expr;) => {};
22 (@one; $ty:ty; $shift:expr; $name:ident, $($tail:tt)*) => {
23pub const $name: $ty = 1 << $shift;
24// Ensure the top bit is not used since it stores initialized state.
25const _: () = assert!($name != (1 << (<$ty>::BITS - 1)));
26// Increment the shift and invoke the next
27unique_masks!(@one; $ty; $shift + 1; $($tail)*);
28 };
29}
3031/// Call `init` once to choose an implementation, then use it for the rest of the program.
32///
33/// - `sig` is the function type.
34/// - `init` is an expression called at startup that chooses an implementation and returns a
35/// function pointer.
36/// - `call` is an expression to call a function returned by `init`, encapsulating any safety
37/// preconditions.
38///
39/// The type `Func` is available in `init` and `call`.
40///
41/// This is effectively our version of an ifunc without linker support. Note that `init` may be
42/// called more than once until one completes.
43#[allow(unused_macros)] // only used on some architectures
44macro_rules! select_once {
45 (
46 sig: fn($($arg:ident: $ArgTy:ty),*) -> $RetTy:ty,
47 init: $init:expr,
48 call: $call:expr,
49 ) => {{
50use core::mem;
51use core::sync::atomic::{AtomicPtr, Ordering};
5253type Func = unsafe fn($($arg: $ArgTy),*) -> $RetTy;
5455/// Stores a pointer that is immediately jumped to. By default it is an init function
56 /// that sets FUNC to something else.
57static FUNC: AtomicPtr<()> = AtomicPtr::new((initializer as Func) as *mut ());
5859/// Run once to set the function that will be used for all subsequent calls.
60fn initializer($($arg: $ArgTy),*) -> $RetTy {
61// Select an implementation, ensuring a 'static lifetime.
62let fn_ptr: Func = $init();
63 FUNC.store(fn_ptr as *mut (), Ordering::Relaxed);
6465// Forward the call to the selected function.
66$call(fn_ptr)
67 }
6869let raw: *mut () = FUNC.load(Ordering::Relaxed);
7071// SAFETY: will only ever be `initializer` or another function pointer that has the
72 // 'static lifetime.
73let fn_ptr: Func = unsafe { mem::transmute::<*mut (), Func>(raw) };
7475$call(fn_ptr)
76 }}
77}
7879#[allow(unused_imports)]
80pub(crate) use {select_once, unique_masks};
8182use crate::support::cold_path;
8384/// Helper for working with bit flags, based on `bitflags`.
85#[derive(Clone, Copy, Debug, PartialEq)]
86pub struct Flags(u32);
8788#[allow(dead_code)] // only used on some architectures
89impl Flags {
90/// No bits set.
91pub const fn empty() -> Self {
92Self(0)
93 }
9495/// Create with bits already set.
96pub const fn from_bits(val: u32) -> Self {
97Self(val)
98 }
99100/// Get the integer representation.
101pub fn bits(&self) -> u32 {
102self.0
103}
104105/// Set any bits in `mask`.
106pub fn insert(&mut self, mask: u32) {
107self.0 |= mask;
108 }
109110/// Check whether the mask is set.
111pub fn contains(&self, mask: u32) -> bool {
112self.0 & mask == mask
113 }
114115/// Check whether the nth bit is set.
116pub fn test_nth(&self, bit: u32) -> bool {
117debug_assert!(bit < u32::BITS, "bit index out-of-bounds");
118self.0 & (1 << bit) != 0
119}
120}
121122/// Load flags from an atomic value. If the flags have not yet been initialized, call `init`
123/// to do so.
124///
125/// Note that `init` may run more than once.
126#[allow(dead_code)] // only used on some architectures
127pub fn get_or_init_flags_cache(cache: &AtomicU32, init: impl FnOnce() -> Flags) -> Flags {
128// The top bit is used to indicate that the values have already been set once.
129const INITIALIZED: u32 = 1 << 31;
130131// Relaxed ops are sufficient since the result should always be the same.
132let mut flags = Flags::from_bits(cache.load(Ordering::Relaxed));
133134if !flags.contains(INITIALIZED) {
135// Without this, `init` is inlined and the bit check gets wrapped in `init`'s lengthy
136 // prologue/epilogue. Cold pathing gives a preferable load->test->?jmp->ret.
137cold_path();
138139 flags = init();
140debug_assert!(
141 !flags.contains(INITIALIZED),
142"initialized bit shouldn't be set"
143);
144 flags.insert(INITIALIZED);
145 cache.store(flags.bits(), Ordering::Relaxed);
146 }
147148 flags
149}
150151#[cfg(test)]
152mod tests {
153use super::*;
154155#[test]
156fn unique_masks() {
157unique_masks! {
158 u32,
159 V0,
160 V1,
161 V2,
162 }
163assert_eq!(V0, 1u32 << 0);
164assert_eq!(V1, 1u32 << 1);
165assert_eq!(V2, 1u32 << 2);
166assert_eq!(ALL, [V0, V1, V2]);
167assert_eq!(NAMES, ["V0", "V1", "V2"]);
168 }
169170#[test]
171fn flag_cache_is_used() {
172// Sanity check that flags are only ever set once
173static CACHE: AtomicU32 = AtomicU32::new(0);
174175let mut f1 = Flags::from_bits(0x1);
176let f2 = Flags::from_bits(0x2);
177178let r1 = get_or_init_flags_cache(&CACHE, || f1);
179let r2 = get_or_init_flags_cache(&CACHE, || f2);
180181 f1.insert(1 << 31); // init bit
182183assert_eq!(r1, f1);
184assert_eq!(r2, f1);
185 }
186187#[test]
188fn select_cache_is_used() {
189// Sanity check that cache is used
190static CALLED: AtomicU32 = AtomicU32::new(0);
191192fn inner() {
193fn nop() {}
194195select_once! {
196 sig: fn() -> (),
197 init: || {
198 CALLED.fetch_add(1, Ordering::Relaxed);
199 nop
200 },
201 call: |fn_ptr: Func| unsafe { fn_ptr() },
202 }
203 }
204205// `init` should only have been called once.
206inner();
207assert_eq!(CALLED.load(Ordering::Relaxed), 1);
208 inner();
209assert_eq!(CALLED.load(Ordering::Relaxed), 1);
210 }
211}