11//! This module is responsible for managing the absolute addresses that allocations are located at, 
22//! and for casting between pointers and integers based on those addresses. 
33
4+ mod  reuse_pool; 
5+ 
46use  std:: cell:: RefCell ; 
57use  std:: cmp:: max; 
68use  std:: collections:: hash_map:: Entry ; 
@@ -9,9 +11,10 @@ use rand::Rng;
911
1012use  rustc_data_structures:: fx:: { FxHashMap ,  FxHashSet } ; 
1113use  rustc_span:: Span ; 
12- use  rustc_target:: abi:: { HasDataLayout ,  Size } ; 
14+ use  rustc_target:: abi:: { Align ,   HasDataLayout ,  Size } ; 
1315
1416use  crate :: * ; 
17+ use  reuse_pool:: ReusePool ; 
1518
1619#[ derive( Copy ,  Clone ,  Debug ,  PartialEq ,  Eq ) ]  
1720pub  enum  ProvenanceMode  { 
@@ -26,7 +29,7 @@ pub enum ProvenanceMode {
2629
2730pub  type  GlobalState  = RefCell < GlobalStateInner > ; 
2831
29- #[ derive( Clone ,   Debug ) ]  
32+ #[ derive( Debug ) ]  
3033pub  struct  GlobalStateInner  { 
3134    /// This is used as a map between the address of each allocation and its `AllocId`. It is always 
3235     /// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset 
@@ -38,6 +41,8 @@ pub struct GlobalStateInner {
3841     /// they do not have an `AllocExtra`. 
3942     /// This is the inverse of `int_to_ptr_map`. 
4043     base_addr :  FxHashMap < AllocId ,  u64 > , 
44+     /// A pool of addresses we can reuse for future allocations. 
45+      reuse :  ReusePool , 
4146    /// Whether an allocation has been exposed or not. This cannot be put 
4247     /// into `AllocExtra` for the same reason as `base_addr`. 
4348     exposed :  FxHashSet < AllocId > , 
@@ -53,6 +58,7 @@ impl VisitProvenance for GlobalStateInner {
5358        let  GlobalStateInner  { 
5459            int_to_ptr_map :  _, 
5560            base_addr :  _, 
61+             reuse :  _, 
5662            exposed :  _, 
5763            next_base_addr :  _, 
5864            provenance_mode :  _, 
@@ -71,6 +77,7 @@ impl GlobalStateInner {
7177        GlobalStateInner  { 
7278            int_to_ptr_map :  Vec :: default ( ) , 
7379            base_addr :  FxHashMap :: default ( ) , 
80+             reuse :  ReusePool :: new ( ) , 
7481            exposed :  FxHashSet :: default ( ) , 
7582            next_base_addr :  stack_addr, 
7683            provenance_mode :  config. provenance_mode , 
@@ -142,6 +149,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
142149        Ok ( match  global_state. base_addr . entry ( alloc_id)  { 
143150            Entry :: Occupied ( entry)  => * entry. get ( ) , 
144151            Entry :: Vacant ( entry)  => { 
152+                 let  mut  rng = ecx. machine . rng . borrow_mut ( ) ; 
145153                let  ( size,  align,  kind)  = ecx. get_alloc_info ( alloc_id) ; 
146154                // This is either called immediately after allocation (and then cached), or when 
147155                // adjusting `tcx` pointers (which never get freed). So assert that we are looking 
@@ -150,44 +158,63 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
150158                // information was removed. 
151159                assert ! ( !matches!( kind,  AllocKind :: Dead ) ) ; 
152160
153-                 // This allocation does not have a base address yet, pick one. 
154-                 // Leave some space to the previous allocation, to give it some chance to be less aligned. 
155-                 let  slack = { 
156-                     let  mut  rng = ecx. machine . rng . borrow_mut ( ) ; 
157-                     // This means that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. 
158-                     rng. gen_range ( 0 ..16 ) 
161+                 // This allocation does not have a base address yet, pick or reuse one. 
162+                 let  base_addr = if  let  Some ( reuse_addr)  =
163+                     global_state. reuse . take_addr ( & mut  * rng,  size,  align) 
164+                 { 
165+                     reuse_addr
166+                 }  else  { 
167+                     // We have to pick a fresh address. 
168+                     // Leave some space to the previous allocation, to give it some chance to be less aligned. 
169+                     // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. 
170+                     let  slack = rng. gen_range ( 0 ..16 ) ; 
171+                     // From next_base_addr + slack, round up to adjust for alignment. 
172+                     let  base_addr = global_state
173+                         . next_base_addr 
174+                         . checked_add ( slack) 
175+                         . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?; 
176+                     let  base_addr = align_addr ( base_addr,  align. bytes ( ) ) ; 
177+ 
178+                     // Remember next base address.  If this allocation is zero-sized, leave a gap 
179+                     // of at least 1 to avoid two allocations having the same base address. 
180+                     // (The logic in `alloc_id_from_addr` assumes unique addresses, and different 
181+                     // function/vtable pointers need to be distinguishable!) 
182+                     global_state. next_base_addr  = base_addr
183+                         . checked_add ( max ( size. bytes ( ) ,  1 ) ) 
184+                         . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?; 
185+                     // Even if `Size` didn't overflow, we might still have filled up the address space. 
186+                     if  global_state. next_base_addr  > ecx. target_usize_max ( )  { 
187+                         throw_exhaust ! ( AddressSpaceFull ) ; 
188+                     } 
189+ 
190+                     base_addr
159191                } ; 
160-                 // From next_base_addr + slack, round up to adjust for alignment. 
161-                 let  base_addr = global_state
162-                     . next_base_addr 
163-                     . checked_add ( slack) 
164-                     . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?; 
165-                 let  base_addr = align_addr ( base_addr,  align. bytes ( ) ) ; 
166-                 entry. insert ( base_addr) ; 
167192                trace ! ( 
168-                     "Assigning base address {:#x} to allocation {:?} (size: {}, align: {}, slack: {} )" , 
193+                     "Assigning base address {:#x} to allocation {:?} (size: {}, align: {})" , 
169194                    base_addr, 
170195                    alloc_id, 
171196                    size. bytes( ) , 
172197                    align. bytes( ) , 
173-                     slack, 
174198                ) ; 
175199
176-                 // Remember next base address.  If this allocation is zero-sized, leave a gap 
177-                 // of at least 1 to avoid two allocations having the same base address. 
178-                 // (The logic in `alloc_id_from_addr` assumes unique addresses, and different 
179-                 // function/vtable pointers need to be distinguishable!) 
180-                 global_state. next_base_addr  = base_addr
181-                     . checked_add ( max ( size. bytes ( ) ,  1 ) ) 
182-                     . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?; 
183-                 // Even if `Size` didn't overflow, we might still have filled up the address space. 
184-                 if  global_state. next_base_addr  > ecx. target_usize_max ( )  { 
185-                     throw_exhaust ! ( AddressSpaceFull ) ; 
186-                 } 
187-                 // Also maintain the opposite mapping in `int_to_ptr_map`. 
188-                 // Given that `next_base_addr` increases in each allocation, pushing the 
189-                 // corresponding tuple keeps `int_to_ptr_map` sorted 
190-                 global_state. int_to_ptr_map . push ( ( base_addr,  alloc_id) ) ; 
200+                 // Store address in cache. 
201+                 entry. insert ( base_addr) ; 
202+ 
203+                 // Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it sorted. 
204+                 // We have a fast-path for the common case that this address is bigger than all previous ones. 
205+                 let  pos = if  global_state
206+                     . int_to_ptr_map 
207+                     . last ( ) 
208+                     . is_some_and ( |( last_addr,  _) | * last_addr < base_addr) 
209+                 { 
210+                     global_state. int_to_ptr_map . len ( ) 
211+                 }  else  { 
212+                     global_state
213+                         . int_to_ptr_map 
214+                         . binary_search_by_key ( & base_addr,  |( addr,  _) | * addr) 
215+                         . unwrap_err ( ) 
216+                 } ; 
217+                 global_state. int_to_ptr_map . insert ( pos,  ( base_addr,  alloc_id) ) ; 
191218
192219                base_addr
193220            } 
@@ -302,7 +329,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
302329} 
303330
304331impl  GlobalStateInner  { 
305-     pub  fn  free_alloc_id ( & mut  self ,  dead_id :  AllocId )  { 
332+     pub  fn  free_alloc_id ( 
333+         & mut  self , 
334+         rng :  & mut  impl  Rng , 
335+         dead_id :  AllocId , 
336+         size :  Size , 
337+         align :  Align , 
338+     )  { 
306339        // We can *not* remove this from `base_addr`, since the interpreter design requires that we 
307340        // be able to retrieve an AllocId + offset for any memory access *before* we check if the 
308341        // access is valid. Specifically, `ptr_get_alloc` is called on each attempt at a memory 
@@ -322,6 +355,8 @@ impl GlobalStateInner {
322355        // We can also remove it from `exposed`, since this allocation can anyway not be returned by 
323356        // `alloc_id_from_addr` any more. 
324357        self . exposed . remove ( & dead_id) ; 
358+         // Also remember this address for future reuse. 
359+         self . reuse . add_addr ( rng,  addr,  size,  align) 
325360    } 
326361} 
327362
0 commit comments