1
1
use std:: cell:: RefCell ;
2
2
use std:: cmp:: max;
3
- use std:: collections:: hash_map:: Entry ;
3
+ use std:: collections:: { hash_map:: Entry , BTreeMap } ;
4
4
5
5
use log:: trace;
6
6
use rand:: Rng ;
@@ -26,9 +26,9 @@ pub type GlobalState = RefCell<GlobalStateInner>;
26
26
27
27
#[ derive( Clone , Debug ) ]
28
28
pub struct GlobalStateInner {
29
- /// This is used as a map between the address of each allocation and its `AllocId`.
30
- /// It is always sorted
31
- int_to_ptr_map : Vec < ( u64 , AllocId ) > ,
29
+ /// This is a map between the address of each allocation and its `AllocId`.
30
+ /// Since it's a `BTreeMap`, it is always sorted, and provides efficient insertion.
31
+ int_to_ptr_map : BTreeMap < u64 , AllocId > ,
32
32
/// The base address for each allocation. We cannot put that into
33
33
/// `AllocExtra` because function pointers also have a base address, and
34
34
/// they do not have an `AllocExtra`.
@@ -44,7 +44,7 @@ pub struct GlobalStateInner {
44
44
impl GlobalStateInner {
45
45
pub fn new ( config : & MiriConfig ) -> Self {
46
46
GlobalStateInner {
47
- int_to_ptr_map : Vec :: default ( ) ,
47
+ int_to_ptr_map : BTreeMap :: default ( ) ,
48
48
base_addr : FxHashMap :: default ( ) ,
49
49
exposed : FxHashSet :: default ( ) ,
50
50
provenance_mode : config. provenance_mode ,
@@ -59,22 +59,26 @@ impl<'mir, 'tcx> GlobalStateInner {
59
59
let global_state = ecx. machine . intptrcast . borrow ( ) ;
60
60
assert ! ( global_state. provenance_mode != ProvenanceMode :: Strict ) ;
61
61
62
- let pos = global_state. int_to_ptr_map . binary_search_by_key ( & addr, |( addr, _) | * addr) ;
63
-
64
62
// Determine the in-bounds provenance for this pointer.
65
63
// (This is only called on an actual access, so in-bounds is the only possible kind of provenance.)
66
- let alloc_id = match pos {
67
- Ok ( pos) => Some ( global_state. int_to_ptr_map [ pos] . 1 ) ,
68
- Err ( 0 ) => None ,
69
- Err ( pos) => {
70
- // This is the largest of the adresses smaller than `int`,
71
- // i.e. the greatest lower bound (glb)
72
- let ( glb, alloc_id) = global_state. int_to_ptr_map [ pos - 1 ] ;
73
- // This never overflows because `addr >= glb`
74
- let offset = addr - glb;
75
- // If the offset exceeds the size of the allocation, don't use this `alloc_id`.
76
- let size = ecx. get_alloc_info ( alloc_id) . 0 ;
77
- if offset <= size. bytes ( ) { Some ( alloc_id) } else { None }
64
+ let alloc_id = match global_state. int_to_ptr_map . get ( & addr) {
65
+ Some ( & id) => Some ( id) ,
66
+ None => {
67
+ // If the address is not in the map, we check the position it should be inserted at.
68
+ // This returns the max key in the map less than `addr`.
69
+ match global_state. int_to_ptr_map . range ( ..addr) . next_back ( ) {
70
+ // Should be inserted at the beginning.
71
+ None => None ,
72
+ // This is the largest of the adresses smaller than `int`,
73
+ // i.e. the greatest lower bound (glb).
74
+ Some ( ( glb, & alloc_id) ) => {
75
+ // This never overflows because `addr >= glb`
76
+ let offset = addr - glb;
77
+ // If the offset exceeds the size of the allocation, don't use this `alloc_id`.
78
+ let size = ecx. get_alloc_info ( alloc_id) . 0 ;
79
+ if offset <= size. bytes ( ) { Some ( alloc_id) } else { None }
80
+ }
81
+ }
78
82
}
79
83
} ?;
80
84
@@ -183,12 +187,9 @@ impl<'mir, 'tcx> GlobalStateInner {
183
187
align. bytes( ) ,
184
188
) ;
185
189
186
- // TODO replace int_to_ptr_map's data structure, since even if we binary search for
187
- // the insert location, the insertion is still linear (due to copies)
188
- // I've done it the dumb obviously correct way for now.
189
- global_state. int_to_ptr_map . retain ( |& ( ref a, _) | a != & base_addr) ;
190
- global_state. int_to_ptr_map . push ( ( base_addr, alloc_id) ) ;
191
- global_state. int_to_ptr_map . sort ( ) ;
190
+ // Map has no duplicates so no need to remove copies.
191
+ // Map is always sorted.
192
+ global_state. int_to_ptr_map . insert ( base_addr, alloc_id) ;
192
193
193
194
base_addr
194
195
}
0 commit comments