@@ -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