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