@@ -10,7 +10,7 @@ use std::collections::VecDeque;
10
10
use std:: ptr;
11
11
use std:: borrow:: Cow ;
12
12
13
- use rustc:: ty:: { self , Instance , ParamEnv , query:: TyCtxtAt } ;
13
+ use rustc:: ty:: { self , Instance , query:: TyCtxtAt } ;
14
14
use rustc:: ty:: layout:: { Align , TargetDataLayout , Size , HasDataLayout } ;
15
15
use rustc_data_structures:: fx:: { FxHashSet , FxHashMap } ;
16
16
@@ -54,6 +54,26 @@ pub enum AllocCheck {
54
54
MaybeDead ,
55
55
}
56
56
57
+ /// The value of a function pointer.
58
+ #[ derive( Debug , Copy , Clone ) ]
59
+ pub enum FnVal < ' tcx , Other > {
60
+ Instance ( Instance < ' tcx > ) ,
61
+ Other ( Other ) ,
62
+ }
63
+
64
+ impl < ' tcx , Other > FnVal < ' tcx , Other > {
65
+ pub fn as_instance ( self ) -> InterpResult < ' tcx , Instance < ' tcx > > {
66
+ match self {
67
+ FnVal :: Instance ( instance) =>
68
+ Ok ( instance) ,
69
+ FnVal :: Other ( _) =>
70
+ err ! ( MachineError (
71
+ format!( "Expected instance function pointer, got 'other' pointer" )
72
+ ) ) ,
73
+ }
74
+ }
75
+ }
76
+
57
77
// `Memory` has to depend on the `Machine` because some of its operations
58
78
// (e.g., `get`) call a `Machine` hook.
59
79
pub struct Memory < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > {
@@ -69,16 +89,20 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
69
89
// FIXME: this should not be public, but interning currently needs access to it
70
90
pub ( super ) alloc_map : M :: MemoryMap ,
71
91
92
+ /// Map for "extra" function pointers.
93
+ extra_fn_ptr_map : FxHashMap < AllocId , M :: ExtraFnVal > ,
94
+
72
95
/// To be able to compare pointers with NULL, and to check alignment for accesses
73
96
/// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
74
97
/// that do not exist any more.
98
+ // FIXME: this should not be public, but interning currently needs access to it
75
99
pub ( super ) dead_alloc_map : FxHashMap < AllocId , ( Size , Align ) > ,
76
100
77
101
/// Extra data added by the machine.
78
102
pub extra : M :: MemoryExtra ,
79
103
80
104
/// Lets us implement `HasDataLayout`, which is awfully convenient.
81
- pub ( super ) tcx : TyCtxtAt < ' tcx > ,
105
+ pub tcx : TyCtxtAt < ' tcx > ,
82
106
}
83
107
84
108
impl < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > HasDataLayout for Memory < ' mir , ' tcx , M > {
98
122
fn clone ( & self ) -> Self {
99
123
Memory {
100
124
alloc_map : self . alloc_map . clone ( ) ,
125
+ extra_fn_ptr_map : self . extra_fn_ptr_map . clone ( ) ,
101
126
dead_alloc_map : self . dead_alloc_map . clone ( ) ,
102
127
extra : ( ) ,
103
128
tcx : self . tcx ,
@@ -109,6 +134,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
109
134
pub fn new ( tcx : TyCtxtAt < ' tcx > ) -> Self {
110
135
Memory {
111
136
alloc_map : M :: MemoryMap :: default ( ) ,
137
+ extra_fn_ptr_map : FxHashMap :: default ( ) ,
112
138
dead_alloc_map : FxHashMap :: default ( ) ,
113
139
extra : M :: MemoryExtra :: default ( ) ,
114
140
tcx,
@@ -120,8 +146,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
120
146
ptr. with_tag ( M :: tag_static_base_pointer ( ptr. alloc_id , & self ) )
121
147
}
122
148
123
- pub fn create_fn_alloc ( & mut self , instance : Instance < ' tcx > ) -> Pointer < M :: PointerTag > {
124
- let id = self . tcx . alloc_map . lock ( ) . create_fn_alloc ( instance) ;
149
+ pub fn create_fn_alloc (
150
+ & mut self ,
151
+ fn_val : FnVal < ' tcx , M :: ExtraFnVal > ,
152
+ ) -> Pointer < M :: PointerTag >
153
+ {
154
+ let id = match fn_val {
155
+ FnVal :: Instance ( instance) => self . tcx . alloc_map . lock ( ) . create_fn_alloc ( instance) ,
156
+ FnVal :: Other ( extra) => {
157
+ // TODO: Should we have a cache here?
158
+ let id = self . tcx . alloc_map . lock ( ) . reserve ( ) ;
159
+ let old = self . extra_fn_ptr_map . insert ( id, extra) ;
160
+ assert ! ( old. is_none( ) ) ;
161
+ id
162
+ }
163
+ } ;
125
164
self . tag_static_base_pointer ( Pointer :: from ( id) )
126
165
}
127
166
@@ -495,56 +534,50 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
495
534
id : AllocId ,
496
535
liveness : AllocCheck ,
497
536
) -> InterpResult < ' static , ( Size , Align ) > {
537
+ // Regular allocations.
498
538
if let Ok ( alloc) = self . get ( id) {
499
- return Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) ) ;
539
+ Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) )
500
540
}
501
- // can't do this in the match argument, we may get cycle errors since the lock would get
502
- // dropped after the match.
503
- let alloc = self . tcx . alloc_map . lock ( ) . get ( id) ;
504
- // Could also be a fn ptr or extern static
505
- match alloc {
506
- Some ( GlobalAlloc :: Function ( ..) ) => {
507
- if let AllocCheck :: Dereferencable = liveness {
508
- // The caller requested no function pointers.
509
- err ! ( DerefFunctionPointer )
510
- } else {
511
- Ok ( ( Size :: ZERO , Align :: from_bytes ( 1 ) . unwrap ( ) ) )
512
- }
541
+ // Function pointers.
542
+ else if let Ok ( _) = self . get_fn_alloc ( id) {
543
+ if let AllocCheck :: Dereferencable = liveness {
544
+ // The caller requested no function pointers.
545
+ err ! ( DerefFunctionPointer )
546
+ } else {
547
+ Ok ( ( Size :: ZERO , Align :: from_bytes ( 1 ) . unwrap ( ) ) )
513
548
}
514
- // `self.get` would also work, but can cause cycles if a static refers to itself
515
- Some ( GlobalAlloc :: Static ( did) ) => {
516
- // The only way `get` couldn't have worked here is if this is an extern static
517
- assert ! ( self . tcx. is_foreign_item( did) ) ;
518
- // Use size and align of the type
519
- let ty = self . tcx . type_of ( did) ;
520
- let layout = self . tcx . layout_of ( ParamEnv :: empty ( ) . and ( ty) ) . unwrap ( ) ;
521
- Ok ( ( layout. size , layout. align . abi ) )
549
+ }
550
+ // The rest must be dead.
551
+ else if let AllocCheck :: MaybeDead = liveness {
552
+ // Deallocated pointers are allowed, we should be able to find
553
+ // them in the map.
554
+ Ok ( * self . dead_alloc_map . get ( & id)
555
+ . expect ( "deallocated pointers should all be recorded in `dead_alloc_map`" ) )
556
+ } else {
557
+ err ! ( DanglingPointerDeref )
558
+ }
559
+ }
560
+
561
+ fn get_fn_alloc ( & self , id : AllocId ) -> InterpResult < ' tcx , FnVal < ' tcx , M :: ExtraFnVal > > {
562
+ trace ! ( "reading fn ptr: {}" , id) ;
563
+ if let Some ( extra) = self . extra_fn_ptr_map . get ( & id) {
564
+ Ok ( FnVal :: Other ( * extra) )
565
+ } else {
566
+ match self . tcx . alloc_map . lock ( ) . get ( id) {
567
+ Some ( GlobalAlloc :: Function ( instance) ) => Ok ( FnVal :: Instance ( instance) ) ,
568
+ _ => Err ( InterpError :: ExecuteMemory . into ( ) ) ,
522
569
}
523
- _ => {
524
- if let Ok ( alloc) = self . get ( id) {
525
- Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) )
526
- }
527
- else if let AllocCheck :: MaybeDead = liveness {
528
- // Deallocated pointers are allowed, we should be able to find
529
- // them in the map.
530
- Ok ( * self . dead_alloc_map . get ( & id)
531
- . expect ( "deallocated pointers should all be recorded in `dead_alloc_map`" ) )
532
- } else {
533
- err ! ( DanglingPointerDeref )
534
- }
535
- } ,
536
570
}
537
571
}
538
572
539
- pub fn get_fn ( & self , ptr : Pointer < M :: PointerTag > ) -> InterpResult < ' tcx , Instance < ' tcx > > {
573
+ pub fn get_fn (
574
+ & self ,
575
+ ptr : Pointer < M :: PointerTag > ,
576
+ ) -> InterpResult < ' tcx , FnVal < ' tcx , M :: ExtraFnVal > > {
540
577
if ptr. offset . bytes ( ) != 0 {
541
578
return err ! ( InvalidFunctionPointer ) ;
542
579
}
543
- trace ! ( "reading fn ptr: {}" , ptr. alloc_id) ;
544
- match self . tcx . alloc_map . lock ( ) . get ( ptr. alloc_id ) {
545
- Some ( GlobalAlloc :: Function ( instance) ) => Ok ( instance) ,
546
- _ => Err ( InterpError :: ExecuteMemory . into ( ) ) ,
547
- }
580
+ self . get_fn_alloc ( ptr. alloc_id )
548
581
}
549
582
550
583
pub fn mark_immutable ( & mut self , id : AllocId ) -> InterpResult < ' tcx > {
0 commit comments