Skip to content

Commit 9fc2c00

Browse files
committed
Miri: Skip over GlobalAllocs when collecting
1 parent d7661d5 commit 9fc2c00

File tree

8 files changed

+112
-80
lines changed

8 files changed

+112
-80
lines changed

compiler/rustc_const_eval/src/interpret/memory.rs

+41-6
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,20 @@ impl<T: fmt::Display> fmt::Display for MemoryKind<T> {
5757
}
5858
}
5959

60+
#[derive(Clone, Copy)]
61+
pub enum Liveness {
62+
Live,
63+
Dead,
64+
Global,
65+
}
66+
6067
/// The return value of `get_alloc_info` indicates the "kind" of the allocation.
68+
#[derive(Clone, Copy)]
6169
pub enum AllocKind {
6270
/// A regular live data allocation.
6371
LiveData,
72+
/// A regular live data allocation that will never be deallocated.
73+
GlobalLiveData,
6474
/// A function allocation (that fn ptrs point to).
6575
Function,
6676
/// A (symbolic) vtable allocation.
@@ -69,6 +79,24 @@ pub enum AllocKind {
6979
Dead,
7080
}
7181

82+
impl AllocKind {
83+
pub fn is_global(self) -> bool {
84+
use AllocKind::*;
85+
match self {
86+
GlobalLiveData | Function | VTable => true,
87+
LiveData | Dead => false,
88+
}
89+
}
90+
91+
pub fn is_live_data(self) -> bool {
92+
use AllocKind::*;
93+
match self {
94+
GlobalLiveData | LiveData => true,
95+
Function | VTable | Dead => false,
96+
}
97+
}
98+
}
99+
72100
/// The value of a function pointer.
73101
#[derive(Debug, Copy, Clone)]
74102
pub enum FnVal<'tcx, Other> {
@@ -695,10 +723,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
695723
/// Check whether an allocation is live. This is faster than calling
696724
/// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is
697725
/// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics.
698-
pub fn is_alloc_live(&self, id: AllocId) -> bool {
699-
self.tcx.try_get_global_alloc(id).is_some()
700-
|| self.memory.alloc_map.contains_key_ref(&id)
701-
|| self.memory.extra_fn_ptr_map.contains_key(&id)
726+
pub fn is_alloc_live(&self, id: AllocId) -> Liveness {
727+
if self.memory.alloc_map.contains_key_ref(&id) {
728+
return Liveness::Live;
729+
}
730+
if self.tcx.try_get_global_alloc(id).is_some() {
731+
return Liveness::Global;
732+
}
733+
if self.memory.extra_fn_ptr_map.contains_key(&id) {
734+
return Liveness::Live;
735+
}
736+
Liveness::Dead
702737
}
703738

704739
/// Obtain the size and alignment of an allocation, even if that allocation has
@@ -733,13 +768,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
733768
.expect("statics should not have generic parameters");
734769
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
735770
assert!(layout.is_sized());
736-
(layout.size, layout.align.abi, AllocKind::LiveData)
771+
(layout.size, layout.align.abi, AllocKind::GlobalLiveData)
737772
}
738773
Some(GlobalAlloc::Memory(alloc)) => {
739774
// Need to duplicate the logic here, because the global allocations have
740775
// different associated types than the interpreter-local ones.
741776
let alloc = alloc.inner();
742-
(alloc.size(), alloc.align, AllocKind::LiveData)
777+
(alloc.size(), alloc.align, AllocKind::GlobalLiveData)
743778
}
744779
Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"),
745780
Some(GlobalAlloc::VTable(..)) => {

compiler/rustc_const_eval/src/interpret/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub use self::intern::{
2525
intern_const_alloc_for_constprop, intern_const_alloc_recursive, InternKind,
2626
};
2727
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
28-
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
28+
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Liveness, Memory, MemoryKind};
2929
pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
3030
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
3131
pub use self::projection::{OffsetMode, Projectable};

src/tools/miri/src/borrow_tracker/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub struct GlobalStateInner {
9191
/// Table storing the "base" tag for each allocation.
9292
/// The base tag is the one used for the initial pointer.
9393
/// We need this in a separate table to handle cyclic statics.
94-
base_ptr_tags: FxHashMap<AllocId, BorTag>,
94+
base_ptr_tags: FxHashMap<AllocId, (BorTag, bool)>,
9595
/// Next unused call ID (for protectors).
9696
next_call_id: CallId,
9797
/// All currently protected tags.
@@ -222,7 +222,7 @@ impl GlobalStateInner {
222222
}
223223

224224
pub fn base_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> BorTag {
225-
self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| {
225+
self.base_ptr_tags.get(&id).map(|(id, _is_global)| *id).unwrap_or_else(|| {
226226
let tag = self.new_ptr();
227227
if self.tracked_pointer_tags.contains(&tag) {
228228
machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
@@ -232,13 +232,13 @@ impl GlobalStateInner {
232232
));
233233
}
234234
trace!("New allocation {:?} has base tag {:?}", id, tag);
235-
self.base_ptr_tags.try_insert(id, tag).unwrap();
235+
self.base_ptr_tags.try_insert(id, (tag, false)).unwrap();
236236
tag
237237
})
238238
}
239239

240240
pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) {
241-
self.base_ptr_tags.retain(|id, _| allocs.is_live(*id));
241+
self.base_ptr_tags.retain(|id, (_, is_global)| allocs.is_live(*id, is_global));
242242
}
243243
}
244244

@@ -382,7 +382,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
382382
// pointer is readable, and the implicit read access inserted
383383
// will never cause UB on the pointer itself.
384384
let (_, _, kind) = this.get_alloc_info(*alloc_id);
385-
if matches!(kind, AllocKind::LiveData) {
385+
if kind.is_live_data() {
386386
let alloc_extra = this.get_alloc_extra(*alloc_id).unwrap();
387387
let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
388388
alloc_borrow_tracker.release_protector(&this.machine, borrow_tracker, *tag)?;

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

+28-38
Original file line numberDiff line numberDiff line change
@@ -648,32 +648,27 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
648648
};
649649

650650
let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
651-
match alloc_kind {
652-
AllocKind::LiveData => {
653-
// This should have alloc_extra data, but `get_alloc_extra` can still fail
654-
// if converting this alloc_id from a global to a local one
655-
// uncovers a non-supported `extern static`.
656-
let extra = this.get_alloc_extra(alloc_id)?;
657-
let mut stacked_borrows = extra
658-
.borrow_tracker_sb()
659-
.borrow_mut();
660-
// Note that we create a *second* `DiagnosticCxBuilder` below for the actual retag.
661-
// FIXME: can this be done cleaner?
662-
let dcx = DiagnosticCxBuilder::retag(
663-
&this.machine,
664-
retag_info,
665-
new_tag,
666-
orig_tag,
667-
alloc_range(base_offset, size),
668-
);
669-
let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
670-
dcx.log_creation();
671-
if new_perm.protector().is_some() {
672-
dcx.log_protector();
673-
}
674-
},
675-
AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
676-
// No stacked borrows on these allocations.
651+
if alloc_kind.is_live_data() {
652+
// This should have alloc_extra data, but `get_alloc_extra` can still fail
653+
// if converting this alloc_id from a global to a local one
654+
// uncovers a non-supported `extern static`.
655+
let extra = this.get_alloc_extra(alloc_id)?;
656+
let mut stacked_borrows = extra
657+
.borrow_tracker_sb()
658+
.borrow_mut();
659+
// Note that we create a *second* `DiagnosticCxBuilder` below for the actual retag.
660+
// FIXME: can this be done cleaner?
661+
let dcx = DiagnosticCxBuilder::retag(
662+
&this.machine,
663+
retag_info,
664+
new_tag,
665+
orig_tag,
666+
alloc_range(base_offset, size),
667+
);
668+
let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
669+
dcx.log_creation();
670+
if new_perm.protector().is_some() {
671+
dcx.log_protector();
677672
}
678673
}
679674
Ok(())
@@ -1012,18 +1007,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10121007
// This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.
10131008
// NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
10141009
let (_size, _align, kind) = this.get_alloc_info(alloc_id);
1015-
match kind {
1016-
AllocKind::LiveData => {
1017-
// This should have alloc_extra data, but `get_alloc_extra` can still fail
1018-
// if converting this alloc_id from a global to a local one
1019-
// uncovers a non-supported `extern static`.
1020-
let alloc_extra = this.get_alloc_extra(alloc_id)?;
1021-
trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
1022-
alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag);
1023-
}
1024-
AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
1025-
// No stacked borrows on these allocations.
1026-
}
1010+
if kind.is_live_data() {
1011+
// This should have alloc_extra data, but `get_alloc_extra` can still fail
1012+
// if converting this alloc_id from a global to a local one
1013+
// uncovers a non-supported `extern static`.
1014+
let alloc_extra = this.get_alloc_extra(alloc_id)?;
1015+
trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
1016+
alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag);
10271017
}
10281018
Ok(())
10291019
}

src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs

+8-13
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
285285
}
286286

287287
let alloc_kind = this.get_alloc_info(alloc_id).2;
288-
if !matches!(alloc_kind, AllocKind::LiveData) {
288+
if !alloc_kind.is_live_data() {
289289
assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
290290
// There's not actually any bytes here where accesses could even be tracked.
291291
// Just produce the new provenance, nothing else to do.
@@ -538,18 +538,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
538538
// This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.
539539
// NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
540540
let (_size, _align, kind) = this.get_alloc_info(alloc_id);
541-
match kind {
542-
AllocKind::LiveData => {
543-
// This should have alloc_extra data, but `get_alloc_extra` can still fail
544-
// if converting this alloc_id from a global to a local one
545-
// uncovers a non-supported `extern static`.
546-
let alloc_extra = this.get_alloc_extra(alloc_id)?;
547-
trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
548-
alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
549-
}
550-
AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
551-
// No tree borrows on these allocations.
552-
}
541+
if kind.is_live_data() {
542+
// This should have alloc_extra data, but `get_alloc_extra` can still fail
543+
// if converting this alloc_id from a global to a local one
544+
// uncovers a non-supported `extern static`.
545+
let alloc_extra = this.get_alloc_extra(alloc_id)?;
546+
trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
547+
alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
553548
}
554549
Ok(())
555550
}

src/tools/miri/src/intptrcast.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub struct GlobalStateInner {
3535
/// `AllocExtra` because function pointers also have a base address, and
3636
/// they do not have an `AllocExtra`.
3737
/// This is the inverse of `int_to_ptr_map`.
38-
base_addr: FxHashMap<AllocId, u64>,
38+
base_addr: FxHashMap<AllocId, (u64, bool)>,
3939
/// Whether an allocation has been exposed or not. This cannot be put
4040
/// into `AllocExtra` for the same reason as `base_addr`.
4141
exposed: FxHashSet<AllocId>,
@@ -78,7 +78,7 @@ impl GlobalStateInner {
7878
pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) {
7979
// `exposed` and `int_to_ptr_map` are cleared immediately when an allocation
8080
// is freed, so `base_addr` is the only one we have to clean up based on the GC.
81-
self.base_addr.retain(|id, _| allocs.is_live(*id));
81+
self.base_addr.retain(|id, (_, is_global)| allocs.is_live(*id, is_global));
8282
}
8383
}
8484

@@ -125,7 +125,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
125125
// We only use this provenance if it has been exposed.
126126
if global_state.exposed.contains(&alloc_id) {
127127
// This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed.
128-
debug_assert!(ecx.is_alloc_live(alloc_id));
128+
debug_assert!(!matches!(ecx.is_alloc_live(alloc_id), Liveness::Dead));
129129
Some(alloc_id)
130130
} else {
131131
None
@@ -138,7 +138,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
138138
let global_state = &mut *global_state;
139139

140140
Ok(match global_state.base_addr.entry(alloc_id) {
141-
Entry::Occupied(entry) => *entry.get(),
141+
Entry::Occupied(entry) => entry.get().0,
142142
Entry::Vacant(entry) => {
143143
let (size, align, kind) = ecx.get_alloc_info(alloc_id);
144144
// This is either called immediately after allocation (and then cached), or when
@@ -161,7 +161,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
161161
.checked_add(slack)
162162
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
163163
let base_addr = align_addr(base_addr, align.bytes());
164-
entry.insert(base_addr);
164+
entry.insert((base_addr, kind.is_global()));
165165
trace!(
166166
"Assigning base address {:#x} to allocation {:?} (size: {}, align: {}, slack: {})",
167167
base_addr,
@@ -204,7 +204,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
204204
}
205205
// Exposing a dead alloc is a no-op, because it's not possible to get a dead allocation
206206
// via int2ptr.
207-
if !ecx.is_alloc_live(alloc_id) {
207+
if matches!(ecx.is_alloc_live(alloc_id), Liveness::Dead) {
208208
return Ok(());
209209
}
210210
trace!("Exposing allocation id {alloc_id:?}");
@@ -312,7 +312,7 @@ impl GlobalStateInner {
312312
// returns a dead allocation.
313313
// To avoid a linear scan we first look up the address in `base_addr`, and then find it in
314314
// `int_to_ptr_map`.
315-
let addr = *self.base_addr.get(&dead_id).unwrap();
315+
let addr = self.base_addr.get(&dead_id).unwrap().0;
316316
let pos = self.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap();
317317
let removed = self.int_to_ptr_map.remove(pos);
318318
assert_eq!(removed, (addr, dead_id)); // double-check that we removed the right thing

src/tools/miri/src/machine.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ pub struct MiriMachine<'mir, 'tcx> {
530530

531531
/// The spans we will use to report where an allocation was created and deallocated in
532532
/// diagnostics.
533-
pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
533+
pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>, bool)>>,
534534
}
535535

536536
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
@@ -781,14 +781,14 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
781781
self.allocation_spans
782782
.borrow()
783783
.get(&alloc_id)
784-
.map(|(allocated, _deallocated)| allocated.data())
784+
.map(|(allocated, _deallocated, _)| allocated.data())
785785
}
786786

787787
pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
788788
self.allocation_spans
789789
.borrow()
790790
.get(&alloc_id)
791-
.and_then(|(_allocated, deallocated)| *deallocated)
791+
.and_then(|(_allocated, deallocated, _)| *deallocated)
792792
.map(Span::data)
793793
}
794794
}
@@ -1120,7 +1120,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
11201120
ecx.machine
11211121
.allocation_spans
11221122
.borrow_mut()
1123-
.insert(id, (ecx.machine.current_span(), None));
1123+
.insert(id, (ecx.machine.current_span(), None, false));
11241124
}
11251125

11261126
Ok(Cow::Owned(alloc))
@@ -1258,7 +1258,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
12581258
if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
12591259
borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, range, machine)?;
12601260
}
1261-
if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1261+
if let Some((_, deallocated_at, _)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
12621262
{
12631263
*deallocated_at = Some(machine.current_span());
12641264
}
@@ -1447,7 +1447,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
14471447
};
14481448
let local_decl = &ecx.active_thread_stack()[frame].body.local_decls[local];
14491449
let span = local_decl.source_info.span;
1450-
ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
1450+
ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None, false));
14511451
Ok(())
14521452
}
14531453
}

0 commit comments

Comments
 (0)