@@ -119,14 +119,15 @@ mod pointer;
119119mod queries;
120120mod value;
121121
122+ use std:: collections:: hash_map:: Entry ;
122123use std:: fmt;
123124use std:: io;
124125use std:: io:: { Read , Write } ;
125126use std:: num:: { NonZeroU32 , NonZeroU64 } ;
126127use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
127128
128129use rustc_ast:: LitKind ;
129- use rustc_data_structures:: fx:: FxHashMap ;
130+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexSet } ;
130131use rustc_data_structures:: sync:: { HashMapExt , Lock } ;
131132use rustc_data_structures:: tiny_list:: TinyList ;
132133use rustc_errors:: ErrorGuaranteed ;
@@ -204,25 +205,71 @@ pub enum LitToConstError {
204205 Reported ( ErrorGuaranteed ) ,
205206}
206207
208+ /// We encode the address space in the top 2 bits of the id.
209+ #[ rustc_pass_by_value]
207210#[ derive( Copy , Clone , Eq , Hash , Ord , PartialEq , PartialOrd ) ]
208- pub struct AllocId ( pub NonZeroU64 ) ;
211+ pub struct AllocId ( NonZeroU64 ) ;
212+
213+ impl AllocId {
214+ const TAG_MASK : u64 = 0b11 << 62 ;
215+ const ID_MASK : u64 = !Self :: TAG_MASK ;
216+
217+ #[ inline]
218+ fn new ( index : usize , tag : AllocDiscriminant ) -> AllocId {
219+ let index: u64 = index. try_into ( ) . unwrap ( ) ;
220+ // SAFETY: `tag` is only 2 bits, so is in range for `u8`.
221+ let tag = unsafe { std:: mem:: transmute :: < AllocDiscriminant , u8 > ( tag) } ;
222+ let tag = ( tag as u64 ) << 62 ;
223+ AllocId ( NonZeroU64 :: new ( index | tag) . unwrap ( ) )
224+ }
225+
226+ #[ inline]
227+ fn alloc_discriminant ( self ) -> AllocDiscriminant {
228+ let tag = ( self . 0 . get ( ) >> 62 ) as u8 ;
229+ // SAFETY: `tag` is only 2 bits, so is in range for `AllocDiscriminant`.
230+ unsafe { std:: mem:: transmute :: < u8 , AllocDiscriminant > ( tag) }
231+ }
232+
233+ #[ inline]
234+ fn get ( self ) -> usize {
235+ ( self . 0 . get ( ) & Self :: ID_MASK ) . try_into ( ) . unwrap ( )
236+ }
237+
238+ // This is used by miri debugging.
239+ pub fn unchecked_new ( idx : NonZeroU64 ) -> AllocId {
240+ AllocId ( idx)
241+ }
242+ pub fn unchecked_get ( self ) -> NonZeroU64 {
243+ self . 0
244+ }
245+ }
209246
210247// We want the `Debug` output to be readable as it is used by `derive(Debug)` for
211248// all the Miri types.
212249impl fmt:: Debug for AllocId {
213250 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
214- if f. alternate ( ) { write ! ( f, "a{}" , self . 0 ) } else { write ! ( f, "alloc{}" , self . 0 ) }
251+ let mut prefix = match self . alloc_discriminant ( ) {
252+ AllocDiscriminant :: Memory => "alloc" ,
253+ AllocDiscriminant :: Static => "static" ,
254+ AllocDiscriminant :: Fn => "fn" ,
255+ AllocDiscriminant :: VTable => "vtable" ,
256+ } ;
257+ if f. alternate ( ) {
258+ prefix = & prefix[ ..1 ] ;
259+ }
260+ write ! ( f, "{}{}" , prefix, self . get( ) )
215261 }
216262}
217263
218264// No "Display" since AllocIds are not usually user-visible.
219265
220- #[ derive( TyDecodable , TyEncodable ) ]
266+ #[ repr( u8 ) ]
267+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , TyDecodable , TyEncodable ) ]
221268enum AllocDiscriminant {
222- Alloc ,
269+ Memory = 0 ,
270+ Static ,
223271 Fn ,
224272 VTable ,
225- Static ,
226273}
227274
228275pub fn specialized_encode_alloc_id < ' tcx , E : TyEncoder < I = TyCtxt < ' tcx > > > (
@@ -233,7 +280,7 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
233280 match tcx. global_alloc ( alloc_id) {
234281 GlobalAlloc :: Memory ( alloc) => {
235282 trace ! ( "encoding {:?} with {:#?}" , alloc_id, alloc) ;
236- AllocDiscriminant :: Alloc . encode ( encoder) ;
283+ AllocDiscriminant :: Memory . encode ( encoder) ;
237284 alloc. encode ( encoder) ;
238285 }
239286 GlobalAlloc :: Function ( fn_instance) => {
@@ -332,7 +379,7 @@ impl<'s> AllocDecodingSession<'s> {
332379 ref mut entry @ State :: Empty => {
333380 // We are allowed to decode.
334381 match alloc_kind {
335- AllocDiscriminant :: Alloc => {
382+ AllocDiscriminant :: Memory => {
336383 // If this is an allocation, we need to reserve an
337384 // `AllocId` so we can decode cyclic graphs.
338385 let alloc_id = decoder. interner ( ) . reserve_alloc_id ( ) ;
@@ -376,7 +423,7 @@ impl<'s> AllocDecodingSession<'s> {
376423 // Now decode the actual data.
377424 let alloc_id = decoder. with_position ( pos, |decoder| {
378425 match alloc_kind {
379- AllocDiscriminant :: Alloc => {
426+ AllocDiscriminant :: Memory => {
380427 let alloc = <ConstAllocation < ' tcx > as Decodable < _ > >:: decode ( decoder) ;
381428 // We already have a reserved `AllocId`.
382429 let alloc_id = alloc_id. unwrap ( ) ;
@@ -482,31 +529,49 @@ impl<'tcx> GlobalAlloc<'tcx> {
482529
483530pub ( crate ) struct AllocMap < ' tcx > {
484531 /// Maps `AllocId`s to their corresponding allocations.
485- alloc_map : FxHashMap < AllocId , GlobalAlloc < ' tcx > > ,
486-
487- /// Used to ensure that statics and functions only get one associated `AllocId`.
488- /// Should never contain a `GlobalAlloc::Memory`!
489- //
490- // FIXME: Should we just have two separate dedup maps for statics and functions each?
491- dedup : FxHashMap < GlobalAlloc < ' tcx > , AllocId > ,
532+ /// We expect this map to be sparse, as the interpreter may create local allocations that do
533+ /// not make it here.
534+ memory : FxHashMap < AllocId , ConstAllocation < ' tcx > > ,
492535
493536 /// The `AllocId` to assign to the next requested ID.
494537 /// Always incremented; never gets smaller.
495- next_id : AllocId ,
538+ next_memory_id : AllocId ,
539+
540+ /// AllocId for statics have a tag in their AllocId.
541+ /// The index in the IndexSet is the untagged id.
542+ statics : FxIndexSet < DefId > ,
543+
544+ /// AllocId for vtables have a tag in their AllocId.
545+ /// The index in the IndexSet is the untagged id.
546+ vtables : FxIndexSet < ( Ty < ' tcx > , Option < ty:: PolyExistentialTraitRef < ' tcx > > ) > ,
547+
548+ /// Fns have a specific tag. The index in the vec is the untagged id.
549+ /// Monomorphic functions should only get one `AllocId`. To ensure this, we use a
550+ /// deduplication map.
551+ fns : Vec < Instance < ' tcx > > ,
552+ fns_dedup : FxHashMap < Instance < ' tcx > , AllocId > ,
496553}
497554
498555impl < ' tcx > AllocMap < ' tcx > {
499556 pub ( crate ) fn new ( ) -> Self {
500557 AllocMap {
501- alloc_map : Default :: default ( ) ,
502- dedup : Default :: default ( ) ,
503- next_id : AllocId ( NonZeroU64 :: new ( 1 ) . unwrap ( ) ) ,
558+ memory : Default :: default ( ) ,
559+ statics : Default :: default ( ) ,
560+ vtables : Default :: default ( ) ,
561+ fns : Default :: default ( ) ,
562+ fns_dedup : Default :: default ( ) ,
563+ next_memory_id : AllocId :: new ( 1 , AllocDiscriminant :: Memory ) ,
504564 }
505565 }
566+
567+ /// This allocation may only be used to back memory, ie. a `ConstAllocation`.
506568 fn reserve ( & mut self ) -> AllocId {
507- let next = self . next_id ;
508- self . next_id . 0 = self . next_id . 0 . checked_add ( 1 ) . expect (
509- "You overflowed a u64 by incrementing by 1... \
569+ let next = self . next_memory_id ;
570+ self . next_memory_id . 0 = self . next_memory_id . 0 . checked_add ( 1 ) . unwrap ( ) ;
571+ assert_eq ! (
572+ self . next_memory_id. alloc_discriminant( ) ,
573+ AllocDiscriminant :: Memory ,
574+ "You overflowed a u62 by incrementing by 1... \
510575 You've just earned yourself a free drink if we ever meet. \
511576 Seriously, how did you do that?!",
512577 ) ;
@@ -518,35 +583,29 @@ impl<'tcx> TyCtxt<'tcx> {
518583 /// Obtains a new allocation ID that can be referenced but does not
519584 /// yet have an allocation backing it.
520585 ///
586+ /// This allocation may only be used to back memory, ie. a `ConstAllocation`.
587+ ///
521588 /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such
522589 /// an `AllocId` from a query.
523590 pub fn reserve_alloc_id ( self ) -> AllocId {
524591 self . alloc_map . lock ( ) . reserve ( )
525592 }
526593
527- /// Reserves a new ID *if* this allocation has not been dedup-reserved before.
528- /// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we
529- /// don't want to dedup IDs for "real" memory!
530- fn reserve_and_set_dedup ( self , alloc : GlobalAlloc < ' tcx > ) -> AllocId {
531- let mut alloc_map = self . alloc_map . lock ( ) ;
532- match alloc {
533- GlobalAlloc :: Function ( ..) | GlobalAlloc :: Static ( ..) | GlobalAlloc :: VTable ( ..) => { }
534- GlobalAlloc :: Memory ( ..) => bug ! ( "Trying to dedup-reserve memory with real data!" ) ,
535- }
536- if let Some ( & alloc_id) = alloc_map. dedup . get ( & alloc) {
537- return alloc_id;
538- }
539- let id = alloc_map. reserve ( ) ;
540- debug ! ( "creating alloc {alloc:?} with id {id:?}" ) ;
541- alloc_map. alloc_map . insert ( id, alloc. clone ( ) ) ;
542- alloc_map. dedup . insert ( alloc, id) ;
543- id
544- }
545-
546594 /// Generates an `AllocId` for a static or return a cached one in case this function has been
547595 /// called on the same static before.
548596 pub fn reserve_and_set_static_alloc ( self , static_id : DefId ) -> AllocId {
549- self . reserve_and_set_dedup ( GlobalAlloc :: Static ( static_id) )
597+ let ( index, _) = self . alloc_map . lock ( ) . statics . insert_full ( static_id) ;
598+ AllocId :: new ( index, AllocDiscriminant :: Static )
599+ }
600+
601+ /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
602+ pub fn reserve_and_set_vtable_alloc (
603+ self ,
604+ ty : Ty < ' tcx > ,
605+ poly_trait_ref : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
606+ ) -> AllocId {
607+ let ( index, _) = self . alloc_map . lock ( ) . vtables . insert_full ( ( ty, poly_trait_ref) ) ;
608+ AllocId :: new ( index, AllocDiscriminant :: VTable )
550609 }
551610
552611 /// Generates an `AllocId` for a function. Depending on the function type,
@@ -563,27 +622,30 @@ impl<'tcx> TyCtxt<'tcx> {
563622 . args
564623 . into_iter ( )
565624 . any ( |kind| !matches ! ( kind. unpack( ) , GenericArgKind :: Lifetime ( _) ) ) ;
566- if is_generic {
567- // Get a fresh ID.
568- let mut alloc_map = self . alloc_map . lock ( ) ;
569- let id = alloc_map. reserve ( ) ;
570- alloc_map. alloc_map . insert ( id, GlobalAlloc :: Function ( instance) ) ;
571- id
625+
626+ let insert = |fns : & mut Vec < _ > | {
627+ let len = fns. len ( ) ;
628+ fns. push ( instance) ;
629+ AllocId :: new ( len, AllocDiscriminant :: Fn )
630+ } ;
631+
632+ let mut alloc_map = self . alloc_map . lock ( ) ;
633+ let alloc_map = & mut * alloc_map;
634+ if !is_generic {
635+ // The function is not generic, attempt to deduplicate it.
636+ match alloc_map. fns_dedup . entry ( instance) {
637+ Entry :: Occupied ( o) => * o. get ( ) ,
638+ Entry :: Vacant ( v) => {
639+ let id = insert ( & mut alloc_map. fns ) ;
640+ v. insert ( id) ;
641+ id
642+ }
643+ }
572644 } else {
573- // Deduplicate.
574- self . reserve_and_set_dedup ( GlobalAlloc :: Function ( instance) )
645+ insert ( & mut alloc_map. fns )
575646 }
576647 }
577648
578- /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
579- pub fn reserve_and_set_vtable_alloc (
580- self ,
581- ty : Ty < ' tcx > ,
582- poly_trait_ref : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
583- ) -> AllocId {
584- self . reserve_and_set_dedup ( GlobalAlloc :: VTable ( ty, poly_trait_ref) )
585- }
586-
587649 /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical
588650 /// `Allocation` with a different `AllocId`.
589651 /// Statics with identical content will still point to the same `Allocation`, i.e.,
@@ -602,7 +664,23 @@ impl<'tcx> TyCtxt<'tcx> {
602664 /// local dangling pointers and allocations in constants/statics.
603665 #[ inline]
604666 pub fn try_get_global_alloc ( self , id : AllocId ) -> Option < GlobalAlloc < ' tcx > > {
605- self . alloc_map . lock ( ) . alloc_map . get ( & id) . cloned ( )
667+ match id. alloc_discriminant ( ) {
668+ AllocDiscriminant :: Memory => {
669+ self . alloc_map . lock ( ) . memory . get ( & id) . copied ( ) . map ( GlobalAlloc :: Memory )
670+ }
671+ AllocDiscriminant :: Static => {
672+ self . alloc_map . lock ( ) . statics . get_index ( id. get ( ) ) . copied ( ) . map ( GlobalAlloc :: Static )
673+ }
674+ AllocDiscriminant :: Fn => {
675+ self . alloc_map . lock ( ) . fns . get ( id. get ( ) ) . copied ( ) . map ( GlobalAlloc :: Function )
676+ }
677+ AllocDiscriminant :: VTable => self
678+ . alloc_map
679+ . lock ( )
680+ . vtables
681+ . get_index ( id. get ( ) )
682+ . map ( |& ( ty, pred) | GlobalAlloc :: VTable ( ty, pred) ) ,
683+ }
606684 }
607685
608686 #[ inline]
@@ -621,15 +699,17 @@ impl<'tcx> TyCtxt<'tcx> {
621699 /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to
622700 /// call this function twice, even with the same `Allocation` will ICE the compiler.
623701 pub fn set_alloc_id_memory ( self , id : AllocId , mem : ConstAllocation < ' tcx > ) {
624- if let Some ( old) = self . alloc_map . lock ( ) . alloc_map . insert ( id, GlobalAlloc :: Memory ( mem) ) {
702+ debug_assert_eq ! ( id. alloc_discriminant( ) , AllocDiscriminant :: Memory ) ;
703+ if let Some ( old) = self . alloc_map . lock ( ) . memory . insert ( id, mem) {
625704 bug ! ( "tried to set allocation ID {id:?}, but it was already existing as {old:#?}" ) ;
626705 }
627706 }
628707
629708 /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
630709 /// twice for the same `(AllocId, Allocation)` pair.
631710 fn set_alloc_id_same_memory ( self , id : AllocId , mem : ConstAllocation < ' tcx > ) {
632- self . alloc_map . lock ( ) . alloc_map . insert_same ( id, GlobalAlloc :: Memory ( mem) ) ;
711+ debug_assert_eq ! ( id. alloc_discriminant( ) , AllocDiscriminant :: Memory ) ;
712+ self . alloc_map . lock ( ) . memory . insert_same ( id, mem) ;
633713 }
634714}
635715
0 commit comments