|
1 |
| -//! Cache of run-time feature detection |
| 1 | +//! Caches run-time feature detection so that it only needs to be computed |
| 2 | +//! once. |
2 | 3 |
|
3 |
| -use core::sync::atomic::{AtomicUsize, Ordering}; |
| 4 | +use core::sync::atomic::{AtomicU64, Ordering}; |
4 | 5 |
|
5 |
| -use super::bit; |
| 6 | +/// Sets the `bit` of `x`. |
| 7 | +pub const fn set_bit(x: u64, bit: u32) -> u64 { |
| 8 | + x | 1 << bit |
| 9 | +} |
| 10 | + |
| 11 | +/// Tests the `bit` of `x`. |
| 12 | +pub const fn test_bit(x: u64, bit: u32) -> bool { |
| 13 | + x & (1 << bit) != 0 |
| 14 | +} |
| 15 | + |
| 16 | +/// Maximum number of features that can be cached. |
| 17 | +const CACHE_CAPACITY: u32 = 63; |
| 18 | + |
| 19 | +/// This type is used to initialize the cache |
| 20 | +pub struct Initializer(u64); |
| 21 | + |
| 22 | +impl Initializer { |
| 23 | + /// Creates a cleared cache. |
| 24 | + pub fn new() -> Self { |
| 25 | + Initializer(0) |
| 26 | + } |
| 27 | + /// Tests the `bit` of the cache. |
| 28 | + pub fn test(&self, bit: u32) -> bool { |
| 29 | + // FIXME: this way of making sure that the cache is large enough is |
| 30 | + // brittle. |
| 31 | + debug_assert!( |
| 32 | + bit < CACHE_CAPACITY, |
| 33 | + "too many features, time to increase the cache size!" |
| 34 | + ); |
| 35 | + test_bit(self.0, bit) |
| 36 | + } |
| 37 | + /// Sets the `bit` of the cache. |
| 38 | + pub fn set(&mut self, bit: u32) { |
| 39 | + // FIXME: this way of making sure that the cache is large enough is |
| 40 | + // brittle. |
| 41 | + debug_assert!( |
| 42 | + bit < CACHE_CAPACITY, |
| 43 | + "too many features, time to increase the cache size!" |
| 44 | + ); |
| 45 | + let v = self.0; |
| 46 | + self.0 = set_bit(v, bit); |
| 47 | + } |
| 48 | +} |
6 | 49 |
|
7 |
| -/// This global variable is a bitset used to cache the features supported by |
8 |
| -/// the |
9 |
| -/// CPU. |
10 |
| -static CACHE: AtomicUsize = AtomicUsize::new(usize::max_value()); |
| 50 | +/// Feature cache with capacity for `CACHE_CAPACITY` features. |
| 51 | +/// |
| 52 | +/// Note: the last feature bit is used to represent an |
| 53 | +/// uninitialized cache. |
| 54 | +struct Cache(AtomicU64); |
| 55 | + |
| 56 | +/// This global variable is a cache of the features supported by the CPU. |
| 57 | +static CACHE: Cache = Cache::uninitialized(); |
| 58 | + |
| 59 | +impl Cache { |
| 60 | + /// Creates an uninitialized cache. |
| 61 | + const fn uninitialized() -> Self { |
| 62 | + Cache(AtomicU64::new(u64::max_value())) |
| 63 | + } |
| 64 | + /// Is the cache uninitialized? |
| 65 | + pub fn is_uninitialized(&self) -> bool { |
| 66 | + self.0.load(Ordering::Relaxed) == u64::max_value() |
| 67 | + } |
| 68 | + |
| 69 | + /// Is the `bit` in the cache set? |
| 70 | + pub fn test(&self, bit: u32) -> bool { |
| 71 | + test_bit(CACHE.0.load(Ordering::Relaxed), bit) |
| 72 | + } |
| 73 | + |
| 74 | + pub fn set(&self, value: Initializer) { |
| 75 | + self.0.store(value.0, Ordering::Relaxed); |
| 76 | + } |
| 77 | +} |
11 | 78 |
|
12 | 79 | /// Test the `bit` of the storage. If the storage has not been initialized,
|
13 | 80 | /// initializes it with the result of `f()`.
|
14 | 81 | ///
|
15 | 82 | /// On its first invocation, it detects the CPU features and caches them in the
|
16 |
| -/// `FEATURES` global variable as an `AtomicUsize`. |
| 83 | +/// `FEATURES` global variable as an `AtomicU64`. |
17 | 84 | ///
|
18 | 85 | /// It uses the `__Feature` variant to index into this variable as a bitset. If
|
19 | 86 | /// the bit is set, the feature is enabled, and otherwise it is disabled.
|
20 | 87 | ///
|
21 | 88 | /// PLEASE: do not use this, it is an implementation detail subject to change.
|
22 | 89 | pub fn test<F>(bit: u32, f: F) -> bool
|
23 | 90 | where
|
24 |
| - F: FnOnce() -> usize, |
| 91 | + F: FnOnce() -> Initializer, |
25 | 92 | {
|
26 |
| - if CACHE.load(Ordering::Relaxed) == usize::max_value() { |
27 |
| - CACHE.store(f(), Ordering::Relaxed); |
| 93 | + if CACHE.is_uninitialized() { |
| 94 | + CACHE.set(f()); |
28 | 95 | }
|
29 |
| - bit::test(CACHE.load(Ordering::Relaxed), bit) |
| 96 | + CACHE.test(bit) |
30 | 97 | }
|
0 commit comments