@@ -119,14 +119,15 @@ mod pointer;
119
119
mod queries;
120
120
mod value;
121
121
122
+ use std:: collections:: hash_map:: Entry ;
122
123
use std:: fmt;
123
124
use std:: io;
124
125
use std:: io:: { Read , Write } ;
125
126
use std:: num:: { NonZeroU32 , NonZeroU64 } ;
126
127
use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
127
128
128
129
use rustc_ast:: LitKind ;
129
- use rustc_data_structures:: fx:: FxHashMap ;
130
+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexSet } ;
130
131
use rustc_data_structures:: sync:: { HashMapExt , Lock } ;
131
132
use rustc_data_structures:: tiny_list:: TinyList ;
132
133
use rustc_errors:: ErrorGuaranteed ;
@@ -204,25 +205,71 @@ pub enum LitToConstError {
204
205
Reported ( ErrorGuaranteed ) ,
205
206
}
206
207
208
+ /// We encode the address space in the top 2 bits of the id.
209
+ #[ rustc_pass_by_value]
207
210
#[ 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
+ }
209
246
210
247
// We want the `Debug` output to be readable as it is used by `derive(Debug)` for
211
248
// all the Miri types.
212
249
impl fmt:: Debug for AllocId {
213
250
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( ) )
215
261
}
216
262
}
217
263
218
264
// No "Display" since AllocIds are not usually user-visible.
219
265
220
- #[ derive( TyDecodable , TyEncodable ) ]
266
+ #[ repr( u8 ) ]
267
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , TyDecodable , TyEncodable ) ]
221
268
enum AllocDiscriminant {
222
- Alloc ,
269
+ Memory = 0 ,
270
+ Static ,
223
271
Fn ,
224
272
VTable ,
225
- Static ,
226
273
}
227
274
228
275
pub 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>>>(
233
280
match tcx. global_alloc ( alloc_id) {
234
281
GlobalAlloc :: Memory ( alloc) => {
235
282
trace ! ( "encoding {:?} with {:#?}" , alloc_id, alloc) ;
236
- AllocDiscriminant :: Alloc . encode ( encoder) ;
283
+ AllocDiscriminant :: Memory . encode ( encoder) ;
237
284
alloc. encode ( encoder) ;
238
285
}
239
286
GlobalAlloc :: Function ( fn_instance) => {
@@ -332,7 +379,7 @@ impl<'s> AllocDecodingSession<'s> {
332
379
ref mut entry @ State :: Empty => {
333
380
// We are allowed to decode.
334
381
match alloc_kind {
335
- AllocDiscriminant :: Alloc => {
382
+ AllocDiscriminant :: Memory => {
336
383
// If this is an allocation, we need to reserve an
337
384
// `AllocId` so we can decode cyclic graphs.
338
385
let alloc_id = decoder. interner ( ) . reserve_alloc_id ( ) ;
@@ -376,7 +423,7 @@ impl<'s> AllocDecodingSession<'s> {
376
423
// Now decode the actual data.
377
424
let alloc_id = decoder. with_position ( pos, |decoder| {
378
425
match alloc_kind {
379
- AllocDiscriminant :: Alloc => {
426
+ AllocDiscriminant :: Memory => {
380
427
let alloc = <ConstAllocation < ' tcx > as Decodable < _ > >:: decode ( decoder) ;
381
428
// We already have a reserved `AllocId`.
382
429
let alloc_id = alloc_id. unwrap ( ) ;
@@ -482,31 +529,49 @@ impl<'tcx> GlobalAlloc<'tcx> {
482
529
483
530
pub ( crate ) struct AllocMap < ' tcx > {
484
531
/// 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 > > ,
492
535
493
536
/// The `AllocId` to assign to the next requested ID.
494
537
/// 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 > ,
496
553
}
497
554
498
555
impl < ' tcx > AllocMap < ' tcx > {
499
556
pub ( crate ) fn new ( ) -> Self {
500
557
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 ) ,
504
564
}
505
565
}
566
+
567
+ /// This allocation may only be used to back memory, ie. a `ConstAllocation`.
506
568
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... \
510
575
You've just earned yourself a free drink if we ever meet. \
511
576
Seriously, how did you do that?!",
512
577
) ;
@@ -518,35 +583,29 @@ impl<'tcx> TyCtxt<'tcx> {
518
583
/// Obtains a new allocation ID that can be referenced but does not
519
584
/// yet have an allocation backing it.
520
585
///
586
+ /// This allocation may only be used to back memory, ie. a `ConstAllocation`.
587
+ ///
521
588
/// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such
522
589
/// an `AllocId` from a query.
523
590
pub fn reserve_alloc_id ( self ) -> AllocId {
524
591
self . alloc_map . lock ( ) . reserve ( )
525
592
}
526
593
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
-
546
594
/// Generates an `AllocId` for a static or return a cached one in case this function has been
547
595
/// called on the same static before.
548
596
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 )
550
609
}
551
610
552
611
/// Generates an `AllocId` for a function. Depending on the function type,
@@ -563,27 +622,30 @@ impl<'tcx> TyCtxt<'tcx> {
563
622
. args
564
623
. into_iter ( )
565
624
. 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
+ }
572
644
} else {
573
- // Deduplicate.
574
- self . reserve_and_set_dedup ( GlobalAlloc :: Function ( instance) )
645
+ insert ( & mut alloc_map. fns )
575
646
}
576
647
}
577
648
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
-
587
649
/// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical
588
650
/// `Allocation` with a different `AllocId`.
589
651
/// Statics with identical content will still point to the same `Allocation`, i.e.,
@@ -602,7 +664,23 @@ impl<'tcx> TyCtxt<'tcx> {
602
664
/// local dangling pointers and allocations in constants/statics.
603
665
#[ inline]
604
666
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
+ }
606
684
}
607
685
608
686
#[ inline]
@@ -621,15 +699,17 @@ impl<'tcx> TyCtxt<'tcx> {
621
699
/// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to
622
700
/// call this function twice, even with the same `Allocation` will ICE the compiler.
623
701
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) {
625
704
bug ! ( "tried to set allocation ID {id:?}, but it was already existing as {old:#?}" ) ;
626
705
}
627
706
}
628
707
629
708
/// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
630
709
/// twice for the same `(AllocId, Allocation)` pair.
631
710
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) ;
633
713
}
634
714
}
635
715
0 commit comments