55
66use crate :: sync:: atomic:: Ordering ;
77
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 ;
139
1410/// Sets the `bit` of `x`.
1511#[ inline]
@@ -30,7 +26,7 @@ const fn unset_bit(x: u64, bit: u32) -> u64 {
3026}
3127
3228/// Maximum number of features that can be cached.
33- const CACHE_CAPACITY : u32 = 63 ;
29+ const CACHE_CAPACITY : u32 = 62 ;
3430
3531/// This type is used to initialize the cache
3632#[ derive( Copy , Clone ) ]
@@ -81,83 +77,48 @@ impl Initializer {
8177}
8278
8379/// 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 ( ) ] ;
8582
86- /// Feature cache with capacity for `CACHE_CAPACITY ` features.
83+ /// Feature cache with capacity for `usize::max_value() - 1 ` features.
8784///
8885/// Note: the last feature bit is used to represent an
8986/// 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 ) ;
9293
93- #[ cfg( target_pointer_width = "64" ) ]
94- #[ allow( clippy:: use_self) ]
9594impl Cache {
95+ const CAPACITY : u32 = ( core:: mem:: size_of :: < usize > ( ) * 8 - 1 ) as u32 ;
96+ const MASK : usize = ( 1 << Cache :: CAPACITY ) - 1 ;
97+
9698 /// Creates an uninitialized cache.
9799 #[ allow( clippy:: declare_interior_mutable_const) ]
98100 const fn uninitialized ( ) -> Self {
99- Cache ( AtomicU64 :: new ( u64 :: max_value ( ) ) )
101+ Cache ( AtomicUsize :: new ( usize :: max_value ( ) ) )
100102 }
101103 /// Is the cache uninitialized?
102104 #[ inline]
103105 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 ( )
105107 }
106108
107109 /// Is the `bit` in the cache set?
108110 #[ inline]
109111 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)
111113 }
112114
113115 /// Initializes the cache.
114116 #[ 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 ) ;
117119 }
118120}
119121
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- }
161122cfg_if:: cfg_if! {
162123 if #[ cfg( feature = "std_detect_env_override" ) ] {
163124 #[ inline( never) ]
@@ -167,16 +128,22 @@ cfg_if::cfg_if! {
167128 let _ = super :: Feature :: from_str( v) . map( |v| value. unset( v as u32 ) ) ;
168129 }
169130 }
170- CACHE . initialize ( value) ;
131+ do_initialize ( value) ;
171132 }
172133 } else {
173134 #[ inline]
174135 fn initialize( value: Initializer ) {
175- CACHE . initialize ( value) ;
136+ do_initialize ( value) ;
176137 }
177138 }
178139}
179140
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+
180147/// Tests the `bit` of the storage. If the storage has not been initialized,
181148/// initializes it with the result of `f()`.
182149///
@@ -194,8 +161,14 @@ pub(crate) fn test<F>(bit: u32, f: F) -> bool
194161where
195162 F : FnOnce ( ) -> Initializer ,
196163{
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 ( ) )
199172 }
200- CACHE . test ( bit)
173+ CACHE [ idx ] . test ( bit)
201174}
0 commit comments