| 1 | //! Helpers for runtime target feature detection that are shared across architectures. |
| 2 | |
| 3 | // `AtomicU32` is preferred for a consistent size across targets. |
| 4 | #[cfg (all(target_has_atomic = "ptr" , not(target_has_atomic = "32" )))] |
| 5 | compile_error!("currently all targets that support `AtomicPtr` also support `AtomicU32`" ); |
| 6 | |
| 7 | use core::sync::atomic::{AtomicU32, Ordering}; |
| 8 | |
| 9 | /// Given a list of identifiers, assign each one a unique sequential single-bit mask. |
| 10 | #[allow (unused_macros)] |
| 11 | macro_rules! unique_masks { |
| 12 | ($ty:ty, $($name:ident,)+) => { |
| 13 | #[cfg(test)] |
| 14 | pub const ALL: &[$ty] = &[$($name),+]; |
| 15 | #[cfg(test)] |
| 16 | pub const NAMES: &[&str] = &[$(stringify!($name)),+]; |
| 17 | |
| 18 | unique_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)*) => { |
| 23 | pub const $name: $ty = 1 << $shift; |
| 24 | // Ensure the top bit is not used since it stores initialized state. |
| 25 | const _: () = assert!($name != (1 << (<$ty>::BITS - 1))); |
| 26 | // Increment the shift and invoke the next |
| 27 | unique_masks!(@one; $ty; $shift + 1; $($tail)*); |
| 28 | }; |
| 29 | } |
| 30 | |
| 31 | /// 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 |
| 44 | macro_rules! select_once { |
| 45 | ( |
| 46 | sig: fn($($arg:ident: $ArgTy:ty),*) -> $RetTy:ty, |
| 47 | init: $init:expr, |
| 48 | call: $call:expr, |
| 49 | ) => {{ |
| 50 | use core::mem; |
| 51 | use core::sync::atomic::{AtomicPtr, Ordering}; |
| 52 | |
| 53 | type Func = unsafe fn($($arg: $ArgTy),*) -> $RetTy; |
| 54 | |
| 55 | /// Stores a pointer that is immediately jumped to. By default it is an init function |
| 56 | /// that sets FUNC to something else. |
| 57 | static FUNC: AtomicPtr<()> = AtomicPtr::new((initializer as Func) as *mut ()); |
| 58 | |
| 59 | /// Run once to set the function that will be used for all subsequent calls. |
| 60 | fn initializer($($arg: $ArgTy),*) -> $RetTy { |
| 61 | // Select an implementation, ensuring a 'static lifetime. |
| 62 | let fn_ptr: Func = $init(); |
| 63 | FUNC.store(fn_ptr as *mut (), Ordering::Relaxed); |
| 64 | |
| 65 | // Forward the call to the selected function. |
| 66 | $call(fn_ptr) |
| 67 | } |
| 68 | |
| 69 | let raw: *mut () = FUNC.load(Ordering::Relaxed); |
| 70 | |
| 71 | // SAFETY: will only ever be `initializer` or another function pointer that has the |
| 72 | // 'static lifetime. |
| 73 | let fn_ptr: Func = unsafe { mem::transmute::<*mut (), Func>(raw) }; |
| 74 | |
| 75 | $call(fn_ptr) |
| 76 | }} |
| 77 | } |
| 78 | |
| 79 | #[allow (unused_imports)] |
| 80 | pub(crate) use {select_once, unique_masks}; |
| 81 | |
| 82 | use crate::support::cold_path; |
| 83 | |
| 84 | /// Helper for working with bit flags, based on `bitflags`. |
| 85 | #[derive (Clone, Copy, Debug, PartialEq)] |
| 86 | pub struct Flags(u32); |
| 87 | |
| 88 | #[allow (dead_code)] // only used on some architectures |
| 89 | impl Flags { |
| 90 | /// No bits set. |
| 91 | pub const fn empty() -> Self { |
| 92 | Self(0) |
| 93 | } |
| 94 | |
| 95 | /// Create with bits already set. |
| 96 | pub const fn from_bits(val: u32) -> Self { |
| 97 | Self(val) |
| 98 | } |
| 99 | |
| 100 | /// Get the integer representation. |
| 101 | pub fn bits(&self) -> u32 { |
| 102 | self.0 |
| 103 | } |
| 104 | |
| 105 | /// Set any bits in `mask`. |
| 106 | pub fn insert(&mut self, mask: u32) { |
| 107 | self.0 |= mask; |
| 108 | } |
| 109 | |
| 110 | /// Check whether the mask is set. |
| 111 | pub fn contains(&self, mask: u32) -> bool { |
| 112 | self.0 & mask == mask |
| 113 | } |
| 114 | |
| 115 | /// Check whether the nth bit is set. |
| 116 | pub fn test_nth(&self, bit: u32) -> bool { |
| 117 | debug_assert!(bit < u32::BITS, "bit index out-of-bounds" ); |
| 118 | self.0 & (1 << bit) != 0 |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | /// 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 |
| 127 | pub 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. |
| 129 | const INITIALIZED: u32 = 1 << 31; |
| 130 | |
| 131 | // Relaxed ops are sufficient since the result should always be the same. |
| 132 | let mut flags: Flags = Flags::from_bits(val:cache.load(order:Ordering::Relaxed)); |
| 133 | |
| 134 | if !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. |
| 137 | cold_path(); |
| 138 | |
| 139 | flags = init(); |
| 140 | debug_assert!( |
| 141 | !flags.contains(INITIALIZED), |
| 142 | "initialized bit shouldn't be set" |
| 143 | ); |
| 144 | flags.insert(INITIALIZED); |
| 145 | cache.store(val:flags.bits(), order:Ordering::Relaxed); |
| 146 | } |
| 147 | |
| 148 | flags |
| 149 | } |
| 150 | |
| 151 | #[cfg (test)] |
| 152 | mod tests { |
| 153 | use super::*; |
| 154 | |
| 155 | #[test ] |
| 156 | fn unique_masks() { |
| 157 | unique_masks! { |
| 158 | u32, |
| 159 | V0, |
| 160 | V1, |
| 161 | V2, |
| 162 | } |
| 163 | assert_eq!(V0, 1u32 << 0); |
| 164 | assert_eq!(V1, 1u32 << 1); |
| 165 | assert_eq!(V2, 1u32 << 2); |
| 166 | assert_eq!(ALL, [V0, V1, V2]); |
| 167 | assert_eq!(NAMES, ["V0" , "V1" , "V2" ]); |
| 168 | } |
| 169 | |
| 170 | #[test ] |
| 171 | fn flag_cache_is_used() { |
| 172 | // Sanity check that flags are only ever set once |
| 173 | static CACHE: AtomicU32 = AtomicU32::new(0); |
| 174 | |
| 175 | let mut f1 = Flags::from_bits(0x1); |
| 176 | let f2 = Flags::from_bits(0x2); |
| 177 | |
| 178 | let r1 = get_or_init_flags_cache(&CACHE, || f1); |
| 179 | let r2 = get_or_init_flags_cache(&CACHE, || f2); |
| 180 | |
| 181 | f1.insert(1 << 31); // init bit |
| 182 | |
| 183 | assert_eq!(r1, f1); |
| 184 | assert_eq!(r2, f1); |
| 185 | } |
| 186 | |
| 187 | #[test ] |
| 188 | fn select_cache_is_used() { |
| 189 | // Sanity check that cache is used |
| 190 | static CALLED: AtomicU32 = AtomicU32::new(0); |
| 191 | |
| 192 | fn inner() { |
| 193 | fn nop() {} |
| 194 | |
| 195 | select_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 | } |
| 204 | |
| 205 | // `init` should only have been called once. |
| 206 | inner(); |
| 207 | assert_eq!(CALLED.load(Ordering::Relaxed), 1); |
| 208 | inner(); |
| 209 | assert_eq!(CALLED.load(Ordering::Relaxed), 1); |
| 210 | } |
| 211 | } |
| 212 | |