@@ -54,6 +54,26 @@ pub enum AllocCheck {
5454 MaybeDead ,
5555}
5656
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+
5777// `Memory` has to depend on the `Machine` because some of its operations
5878// (e.g., `get`) call a `Machine` hook.
5979pub struct Memory < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > {
@@ -69,16 +89,20 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
6989 // FIXME: this should not be public, but interning currently needs access to it
7090 pub ( super ) alloc_map : M :: MemoryMap ,
7191
92+ /// Map for "extra" function pointers.
93+ extra_fn_ptr_map : FxHashMap < AllocId , M :: ExtraFnVal > ,
94+
7295 /// To be able to compare pointers with NULL, and to check alignment for accesses
7396 /// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
7497 /// that do not exist any more.
98+ // FIXME: this should not be public, but interning currently needs access to it
7599 pub ( super ) dead_alloc_map : FxHashMap < AllocId , ( Size , Align ) > ,
76100
77101 /// Extra data added by the machine.
78102 pub extra : M :: MemoryExtra ,
79103
80104 /// Lets us implement `HasDataLayout`, which is awfully convenient.
81- pub ( super ) tcx : TyCtxtAt < ' tcx > ,
105+ pub tcx : TyCtxtAt < ' tcx > ,
82106}
83107
84108impl < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > HasDataLayout for Memory < ' mir , ' tcx , M > {
98122 fn clone ( & self ) -> Self {
99123 Memory {
100124 alloc_map : self . alloc_map . clone ( ) ,
125+ extra_fn_ptr_map : self . extra_fn_ptr_map . clone ( ) ,
101126 dead_alloc_map : self . dead_alloc_map . clone ( ) ,
102127 extra : ( ) ,
103128 tcx : self . tcx ,
@@ -109,6 +134,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
109134 pub fn new ( tcx : TyCtxtAt < ' tcx > , extra : M :: MemoryExtra ) -> Self {
110135 Memory {
111136 alloc_map : M :: MemoryMap :: default ( ) ,
137+ extra_fn_ptr_map : FxHashMap :: default ( ) ,
112138 dead_alloc_map : FxHashMap :: default ( ) ,
113139 extra,
114140 tcx,
@@ -120,8 +146,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
120146 ptr. with_tag ( M :: tag_static_base_pointer ( ptr. alloc_id , & self ) )
121147 }
122148
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+ // FIXME(RalfJung): 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+ } ;
125164 self . tag_static_base_pointer ( Pointer :: from ( id) )
126165 }
127166
@@ -495,56 +534,65 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
495534 id : AllocId ,
496535 liveness : AllocCheck ,
497536 ) -> InterpResult < ' static , ( Size , Align ) > {
537+ // Regular allocations.
498538 if let Ok ( alloc) = self . get ( id) {
499539 return Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) ) ;
500540 }
501- // can't do this in the match argument, we may get cycle errors since the lock would get
502- // dropped after the match.
541+ // Function pointers.
542+ if let Ok ( _) = self . get_fn_alloc ( id) {
543+ return 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 ( ) ) )
548+ } ;
549+ }
550+ // Foreign statics.
551+ // Can't do this in the match argument, we may get cycle errors since the lock would
552+ // be held throughout the match.
503553 let alloc = self . tcx . alloc_map . lock ( ) . get ( id) ;
504- // Could also be a fn ptr or extern static
505554 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- }
513- }
514- // `self.get` would also work, but can cause cycles if a static refers to itself
515555 Some ( GlobalAlloc :: Static ( did) ) => {
516- // The only way `get` couldn't have worked here is if this is an extern static
517556 assert ! ( self . tcx. is_foreign_item( did) ) ;
518557 // Use size and align of the type
519558 let ty = self . tcx . type_of ( did) ;
520559 let layout = self . tcx . layout_of ( ParamEnv :: empty ( ) . and ( ty) ) . unwrap ( ) ;
521- Ok ( ( layout. size , layout. align . abi ) )
560+ return Ok ( ( layout. size , layout. align . abi ) ) ;
522561 }
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- } ,
562+ _ => { }
563+ }
564+ // The rest must be dead.
565+ if let AllocCheck :: MaybeDead = liveness {
566+ // Deallocated pointers are allowed, we should be able to find
567+ // them in the map.
568+ Ok ( * self . dead_alloc_map . get ( & id)
569+ . expect ( "deallocated pointers should all be recorded in `dead_alloc_map`" ) )
570+ } else {
571+ err ! ( DanglingPointerDeref )
536572 }
537573 }
538574
539- pub fn get_fn ( & self , ptr : Pointer < M :: PointerTag > ) -> InterpResult < ' tcx , Instance < ' tcx > > {
575+ fn get_fn_alloc ( & self , id : AllocId ) -> InterpResult < ' tcx , FnVal < ' tcx , M :: ExtraFnVal > > {
576+ trace ! ( "reading fn ptr: {}" , id) ;
577+ if let Some ( extra) = self . extra_fn_ptr_map . get ( & id) {
578+ Ok ( FnVal :: Other ( * extra) )
579+ } else {
580+ match self . tcx . alloc_map . lock ( ) . get ( id) {
581+ Some ( GlobalAlloc :: Function ( instance) ) => Ok ( FnVal :: Instance ( instance) ) ,
582+ _ => Err ( InterpError :: ExecuteMemory . into ( ) ) ,
583+ }
584+ }
585+ }
586+
587+ pub fn get_fn (
588+ & self ,
589+ ptr : Scalar < M :: PointerTag > ,
590+ ) -> InterpResult < ' tcx , FnVal < ' tcx , M :: ExtraFnVal > > {
591+ let ptr = self . force_ptr ( ptr) ?; // We definitely need a pointer value.
540592 if ptr. offset . bytes ( ) != 0 {
541593 return err ! ( InvalidFunctionPointer ) ;
542594 }
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- }
595+ self . get_fn_alloc ( ptr. alloc_id )
548596 }
549597
550598 pub fn mark_immutable ( & mut self , id : AllocId ) -> InterpResult < ' tcx > {
0 commit comments