5
5
6
6
use crate :: sync:: atomic:: Ordering ;
7
7
8
- #[ cfg( target_pointer_width = "64" ) ]
9
- use crate :: sync:: atomic:: AtomicU64 ;
10
-
11
- #[ cfg( target_pointer_width = "32" ) ]
12
- use crate :: sync:: atomic:: AtomicU32 ;
8
+ use crate :: sync:: atomic:: AtomicUsize ;
13
9
14
10
/// Sets the `bit` of `x`.
15
11
#[ inline]
@@ -30,7 +26,7 @@ const fn unset_bit(x: u64, bit: u32) -> u64 {
30
26
}
31
27
32
28
/// Maximum number of features that can be cached.
33
- const CACHE_CAPACITY : u32 = 63 ;
29
+ const CACHE_CAPACITY : u32 = 62 ;
34
30
35
31
/// This type is used to initialize the cache
36
32
#[ derive( Copy , Clone ) ]
@@ -81,83 +77,48 @@ impl Initializer {
81
77
}
82
78
83
79
/// This global variable is a cache of the features supported by the CPU.
84
- static CACHE : Cache = Cache :: uninitialized ( ) ;
80
+ // Note: on x64, we only use the first slot
81
+ static CACHE : [ Cache ; 2 ] = [ Cache :: uninitialized ( ) , Cache :: uninitialized ( ) ] ;
85
82
86
- /// Feature cache with capacity for `CACHE_CAPACITY ` features.
83
+ /// Feature cache with capacity for `usize::max_value() - 1 ` features.
87
84
///
88
85
/// Note: the last feature bit is used to represent an
89
86
/// uninitialized cache.
90
- #[ cfg( target_pointer_width = "64" ) ]
91
- struct Cache ( AtomicU64 ) ;
87
+ ///
88
+ /// Note: we can use `Relaxed` atomic operations, because we are only interested
89
+ /// in the effects of operations on a single memory location. That is, we only
90
+ /// need "modification order", and not the full-blown "happens before". However,
91
+ /// we use `SeqCst` just to be on the safe side.
92
+ struct Cache ( AtomicUsize ) ;
92
93
93
- #[ cfg( target_pointer_width = "64" ) ]
94
- #[ allow( clippy:: use_self) ]
95
94
impl Cache {
95
+ const CAPACITY : u32 = ( core:: mem:: size_of :: < usize > ( ) * 8 - 1 ) as u32 ;
96
+ const MASK : usize = ( 1 << Cache :: CAPACITY ) - 1 ;
97
+
96
98
/// Creates an uninitialized cache.
97
99
#[ allow( clippy:: declare_interior_mutable_const) ]
98
100
const fn uninitialized ( ) -> Self {
99
- Cache ( AtomicU64 :: new ( u64 :: max_value ( ) ) )
101
+ Cache ( AtomicUsize :: new ( usize :: max_value ( ) ) )
100
102
}
101
103
/// Is the cache uninitialized?
102
104
#[ inline]
103
105
pub ( crate ) fn is_uninitialized ( & self ) -> bool {
104
- self . 0 . load ( Ordering :: Relaxed ) == u64 :: max_value ( )
106
+ self . 0 . load ( Ordering :: SeqCst ) == usize :: max_value ( )
105
107
}
106
108
107
109
/// Is the `bit` in the cache set?
108
110
#[ inline]
109
111
pub ( crate ) fn test ( & self , bit : u32 ) -> bool {
110
- test_bit ( CACHE . 0 . load ( Ordering :: Relaxed ) , bit)
112
+ test_bit ( self . 0 . load ( Ordering :: SeqCst ) as u64 , bit)
111
113
}
112
114
113
115
/// Initializes the cache.
114
116
#[ inline]
115
- pub ( crate ) fn initialize ( & self , value : Initializer ) {
116
- self . 0 . store ( value. 0 , Ordering :: Relaxed ) ;
117
+ fn initialize ( & self , value : usize ) {
118
+ self . 0 . store ( value, Ordering :: SeqCst ) ;
117
119
}
118
120
}
119
121
120
- /// Feature cache with capacity for `CACHE_CAPACITY` features.
121
- ///
122
- /// Note: the last feature bit is used to represent an
123
- /// uninitialized cache.
124
- #[ cfg( target_pointer_width = "32" ) ]
125
- struct Cache ( AtomicU32 , AtomicU32 ) ;
126
-
127
- #[ cfg( target_pointer_width = "32" ) ]
128
- impl Cache {
129
- /// Creates an uninitialized cache.
130
- const fn uninitialized ( ) -> Self {
131
- Cache (
132
- AtomicU32 :: new ( u32:: max_value ( ) ) ,
133
- AtomicU32 :: new ( u32:: max_value ( ) ) ,
134
- )
135
- }
136
- /// Is the cache uninitialized?
137
- #[ inline]
138
- pub ( crate ) fn is_uninitialized ( & self ) -> bool {
139
- self . 1 . load ( Ordering :: Relaxed ) == u32:: max_value ( )
140
- }
141
-
142
- /// Is the `bit` in the cache set?
143
- #[ inline]
144
- pub ( crate ) fn test ( & self , bit : u32 ) -> bool {
145
- if bit < 32 {
146
- test_bit ( CACHE . 0 . load ( Ordering :: Relaxed ) as u64 , bit)
147
- } else {
148
- test_bit ( CACHE . 1 . load ( Ordering :: Relaxed ) as u64 , bit - 32 )
149
- }
150
- }
151
-
152
- /// Initializes the cache.
153
- #[ inline]
154
- pub ( crate ) fn initialize ( & self , value : Initializer ) {
155
- let lo: u32 = value. 0 as u32 ;
156
- let hi: u32 = ( value. 0 >> 32 ) as u32 ;
157
- self . 0 . store ( lo, Ordering :: Relaxed ) ;
158
- self . 1 . store ( hi, Ordering :: Relaxed ) ;
159
- }
160
- }
161
122
cfg_if:: cfg_if! {
162
123
if #[ cfg( feature = "std_detect_env_override" ) ] {
163
124
#[ inline( never) ]
@@ -167,16 +128,22 @@ cfg_if::cfg_if! {
167
128
let _ = super :: Feature :: from_str( v) . map( |v| value. unset( v as u32 ) ) ;
168
129
}
169
130
}
170
- CACHE . initialize ( value) ;
131
+ do_initialize ( value) ;
171
132
}
172
133
} else {
173
134
#[ inline]
174
135
fn initialize( value: Initializer ) {
175
- CACHE . initialize ( value) ;
136
+ do_initialize ( value) ;
176
137
}
177
138
}
178
139
}
179
140
141
+ #[ inline]
142
+ fn do_initialize ( value : Initializer ) {
143
+ CACHE [ 0 ] . initialize ( ( value. 0 ) as usize & Cache :: MASK ) ;
144
+ CACHE [ 1 ] . initialize ( ( value. 0 >> Cache :: CAPACITY ) as usize & Cache :: MASK ) ;
145
+ }
146
+
180
147
/// Tests the `bit` of the storage. If the storage has not been initialized,
181
148
/// initializes it with the result of `f()`.
182
149
///
@@ -194,8 +161,14 @@ pub(crate) fn test<F>(bit: u32, f: F) -> bool
194
161
where
195
162
F : FnOnce ( ) -> Initializer ,
196
163
{
197
- if CACHE . is_uninitialized ( ) {
198
- initialize ( f ( ) ) ;
164
+ let ( bit, idx) = if bit < Cache :: CAPACITY {
165
+ ( bit, 0 )
166
+ } else {
167
+ ( bit - Cache :: CAPACITY , 1 )
168
+ } ;
169
+
170
+ if CACHE [ idx] . is_uninitialized ( ) {
171
+ initialize ( f ( ) )
199
172
}
200
- CACHE . test ( bit)
173
+ CACHE [ idx ] . test ( bit)
201
174
}
0 commit comments