Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8520226

Browse files
committedNov 23, 2023
Miri: Skip over GlobalAllocs when collecting
1 parent 237339f commit 8520226

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> {
@@ -706,10 +734,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
706734
/// Check whether an allocation is live. This is faster than calling
707735
/// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is
708736
/// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics.
709-
pub fn is_alloc_live(&self, id: AllocId) -> bool {
710-
self.tcx.try_get_global_alloc(id).is_some()
711-
|| self.memory.alloc_map.contains_key_ref(&id)
712-
|| self.memory.extra_fn_ptr_map.contains_key(&id)
737+
pub fn is_alloc_live(&self, id: AllocId) -> Liveness {
738+
if self.memory.alloc_map.contains_key_ref(&id) {
739+
return Liveness::Live;
740+
}
741+
if self.tcx.try_get_global_alloc(id).is_some() {
742+
return Liveness::Global;
743+
}
744+
if self.memory.extra_fn_ptr_map.contains_key(&id) {
745+
return Liveness::Live;
746+
}
747+
Liveness::Dead
713748
}
714749

715750
/// Obtain the size and alignment of an allocation, even if that allocation has
@@ -744,13 +779,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
744779
.expect("statics should not have generic parameters");
745780
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
746781
assert!(layout.is_sized());
747-
(layout.size, layout.align.abi, AllocKind::LiveData)
782+
(layout.size, layout.align.abi, AllocKind::GlobalLiveData)
748783
}
749784
Some(GlobalAlloc::Memory(alloc)) => {
750785
// Need to duplicate the logic here, because the global allocations have
751786
// different associated types than the interpreter-local ones.
752787
let alloc = alloc.inner();
753-
(alloc.size(), alloc.align, AllocKind::LiveData)
788+
(alloc.size(), alloc.align, AllocKind::GlobalLiveData)
754789
}
755790
Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"),
756791
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
}

‎src/tools/miri/src/provenance_gc.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,21 @@ pub struct LiveAllocs<'a, 'mir, 'tcx> {
156156
}
157157

158158
impl LiveAllocs<'_, '_, '_> {
159-
pub fn is_live(&self, id: AllocId) -> bool {
160-
self.collected.contains(&id) ||
161-
self.ecx.is_alloc_live(id)
159+
pub fn is_live(&self, id: AllocId, is_global: &mut bool) -> bool {
160+
if *is_global {
161+
return true;
162+
}
163+
if self.collected.contains(&id) {
164+
return true;
165+
}
166+
match self.ecx.is_alloc_live(id) {
167+
Liveness::Dead => false,
168+
Liveness::Live => true,
169+
Liveness::Global => {
170+
*is_global = true;
171+
true
172+
}
173+
}
162174
}
163175
}
164176

@@ -200,7 +212,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
200212
ecx: this,
201213
collected: allocs,
202214
};
203-
this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
215+
this.machine.allocation_spans.borrow_mut().retain(|id, (.., is_live)| allocs.is_live(*id, is_live));
204216
this.machine.intptrcast.borrow_mut().remove_unreachable_allocs(&allocs);
205217
if let Some(borrow_tracker) = &this.machine.borrow_tracker {
206218
borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);

0 commit comments

Comments
 (0)
Please sign in to comment.