Skip to content

Commit 9a9bbc9

Browse files
committed
Auto merge of rust-lang#2559 - RalfJung:gc-refactor, r=saethlin
GC: factor out visiting all machine values `@saethlin` that is roughly what I had in mind. I think some parts of the state are skipped by the visitor. I listed the ones that I found in FIXMEs but I am not sure if that list is complete.
2 parents 439f58b + 8dc4893 commit 9a9bbc9

File tree

5 files changed

+104
-81
lines changed

5 files changed

+104
-81
lines changed

src/concurrency/thread.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,6 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
290290
&mut self.threads[self.active_thread].stack
291291
}
292292

293-
pub fn iter(&self) -> impl Iterator<Item = &Thread<'mir, 'tcx>> {
294-
self.threads.iter()
295-
}
296-
297293
pub fn all_stacks(
298294
&self,
299295
) -> impl Iterator<Item = &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>]> {
@@ -628,6 +624,33 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
628624
}
629625
}
630626

627+
impl VisitMachineValues for ThreadManager<'_, '_> {
628+
fn visit_machine_values(&self, visit: &mut impl FnMut(&Operand<Provenance>)) {
629+
// FIXME some other fields also contain machine values
630+
let ThreadManager { threads, .. } = self;
631+
632+
for thread in threads {
633+
// FIXME: implement VisitMachineValues for `Thread` and `Frame` instead.
634+
// In particular we need to visit the `last_error` and `catch_unwind` fields.
635+
if let Some(payload) = thread.panic_payload {
636+
visit(&Operand::Immediate(Immediate::Scalar(payload)))
637+
}
638+
for frame in &thread.stack {
639+
// Return place.
640+
if let Place::Ptr(mplace) = *frame.return_place {
641+
visit(&Operand::Indirect(mplace));
642+
}
643+
// Locals.
644+
for local in frame.locals.iter() {
645+
if let LocalValue::Live(value) = &local.value {
646+
visit(value);
647+
}
648+
}
649+
}
650+
}
651+
}
652+
}
653+
631654
// Public interface to thread management.
632655
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
633656
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ pub use crate::range_map::RangeMap;
112112
pub use crate::stacked_borrows::{
113113
CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, SbTag, Stack, Stacks,
114114
};
115-
pub use crate::tag_gc::EvalContextExt as _;
115+
pub use crate::tag_gc::{EvalContextExt as _, VisitMachineValues};
116116

117117
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
118118
/// set per default, for maximal validation power.

src/machine.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
291291
}
292292

293293
/// The machine itself.
294+
///
295+
/// If you add anything here that stores machine values, remember to update
296+
/// `visit_all_machine_values`!
294297
pub struct MiriMachine<'mir, 'tcx> {
295298
// We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access.
296299
pub tcx: TyCtxt<'tcx>,
@@ -586,6 +589,17 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
586589
}
587590
}
588591

592+
impl VisitMachineValues for MiriMachine<'_, '_> {
593+
fn visit_machine_values(&self, visit: &mut impl FnMut(&Operand<Provenance>)) {
594+
// FIXME: visit the missing fields: env vars, weak mem, the MemPlace fields in the machine,
595+
// DirHandler, extern_statics, the Stacked Borrows base pointers; maybe more.
596+
let MiriMachine { threads, tls, .. } = self;
597+
598+
threads.visit_machine_values(visit);
599+
tls.visit_machine_values(visit);
600+
}
601+
}
602+
589603
/// A rustc InterpCx for Miri.
590604
pub type MiriInterpCx<'mir, 'tcx> = InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>;
591605

src/shims/tls.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,17 @@ impl<'tcx> TlsData<'tcx> {
233233
data.remove(&thread_id);
234234
}
235235
}
236+
}
237+
238+
impl VisitMachineValues for TlsData<'_> {
239+
fn visit_machine_values(&self, visit: &mut impl FnMut(&Operand<Provenance>)) {
240+
let TlsData { keys, macos_thread_dtors, next_key: _, dtors_running: _ } = self;
236241

237-
pub fn iter(&self, mut visitor: impl FnMut(&Scalar<Provenance>)) {
238-
for scalar in self.keys.values().flat_map(|v| v.data.values()) {
239-
visitor(scalar);
242+
for scalar in keys.values().flat_map(|v| v.data.values()) {
243+
visit(&Operand::Immediate(Immediate::Scalar(*scalar)));
244+
}
245+
for (_, scalar) in macos_thread_dtors.values() {
246+
visit(&Operand::Immediate(Immediate::Scalar(*scalar)));
240247
}
241248
}
242249
}

src/tag_gc.rs

Lines changed: 52 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
1-
use crate::*;
21
use rustc_data_structures::fx::FxHashSet;
32

3+
use crate::*;
4+
5+
pub trait VisitMachineValues {
6+
fn visit_machine_values(&self, visit: &mut impl FnMut(&Operand<Provenance>));
7+
}
8+
49
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
510
pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
11+
/// Generic GC helper to visit everything that can store a value. The `acc` offers some chance to
12+
/// accumulate everything.
13+
fn visit_all_machine_values<T>(
14+
&self,
15+
acc: &mut T,
16+
mut visit_operand: impl FnMut(&mut T, &Operand<Provenance>),
17+
mut visit_alloc: impl FnMut(&mut T, &Allocation<Provenance, AllocExtra>),
18+
) {
19+
let this = self.eval_context_ref();
20+
21+
// Memory.
22+
this.memory.alloc_map().iter(|it| {
23+
for (_id, (_kind, alloc)) in it {
24+
visit_alloc(acc, alloc);
25+
}
26+
});
27+
28+
// And all the other machine values.
29+
this.machine.visit_machine_values(&mut |op| visit_operand(acc, op));
30+
}
31+
632
fn garbage_collect_tags(&mut self) -> InterpResult<'tcx> {
733
let this = self.eval_context_mut();
834
// No reason to do anything at all if stacked borrows is off.
@@ -12,90 +38,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
1238

1339
let mut tags = FxHashSet::default();
1440

15-
for thread in this.machine.threads.iter() {
16-
if let Some(Scalar::Ptr(
17-
Pointer { provenance: Provenance::Concrete { sb, .. }, .. },
18-
_,
19-
)) = thread.panic_payload
20-
{
21-
tags.insert(sb);
22-
}
23-
}
24-
25-
self.find_tags_in_tls(&mut tags);
26-
self.find_tags_in_memory(&mut tags);
27-
self.find_tags_in_locals(&mut tags)?;
28-
29-
self.remove_unreachable_tags(tags);
30-
31-
Ok(())
32-
}
33-
34-
fn find_tags_in_tls(&mut self, tags: &mut FxHashSet<SbTag>) {
35-
let this = self.eval_context_mut();
36-
this.machine.tls.iter(|scalar| {
37-
if let Scalar::Ptr(Pointer { provenance: Provenance::Concrete { sb, .. }, .. }, _) =
38-
scalar
39-
{
40-
tags.insert(*sb);
41-
}
42-
});
43-
}
44-
45-
fn find_tags_in_memory(&mut self, tags: &mut FxHashSet<SbTag>) {
46-
let this = self.eval_context_mut();
47-
this.memory.alloc_map().iter(|it| {
48-
for (_id, (_kind, alloc)) in it {
49-
for (_size, prov) in alloc.provenance().iter() {
50-
if let Provenance::Concrete { sb, .. } = prov {
51-
tags.insert(*sb);
52-
}
53-
}
54-
}
55-
});
56-
}
57-
58-
fn find_tags_in_locals(&mut self, tags: &mut FxHashSet<SbTag>) -> InterpResult<'tcx> {
59-
let this = self.eval_context_mut();
60-
for frame in this.machine.threads.all_stacks().flatten() {
61-
// Handle the return place of each frame
62-
if let Ok(return_place) = frame.return_place.try_as_mplace() {
63-
if let Some(Provenance::Concrete { sb, .. }) = return_place.ptr.provenance {
41+
let visit_scalar = |tags: &mut FxHashSet<SbTag>, s: &Scalar<Provenance>| {
42+
if let Scalar::Ptr(ptr, _) = s {
43+
if let Provenance::Concrete { sb, .. } = ptr.provenance {
6444
tags.insert(sb);
6545
}
6646
}
47+
};
6748

68-
for local in frame.locals.iter() {
69-
let LocalValue::Live(value) = local.value else {
70-
continue;
71-
};
72-
match value {
73-
Operand::Immediate(Immediate::Scalar(Scalar::Ptr(ptr, _))) =>
74-
if let Provenance::Concrete { sb, .. } = ptr.provenance {
75-
tags.insert(sb);
76-
},
49+
this.visit_all_machine_values(
50+
&mut tags,
51+
|tags, op| {
52+
match op {
53+
Operand::Immediate(Immediate::Scalar(s)) => {
54+
visit_scalar(tags, s);
55+
}
7756
Operand::Immediate(Immediate::ScalarPair(s1, s2)) => {
78-
if let Scalar::Ptr(ptr, _) = s1 {
79-
if let Provenance::Concrete { sb, .. } = ptr.provenance {
80-
tags.insert(sb);
81-
}
82-
}
83-
if let Scalar::Ptr(ptr, _) = s2 {
84-
if let Provenance::Concrete { sb, .. } = ptr.provenance {
85-
tags.insert(sb);
86-
}
87-
}
57+
visit_scalar(tags, s1);
58+
visit_scalar(tags, s2);
8859
}
60+
Operand::Immediate(Immediate::Uninit) => {}
8961
Operand::Indirect(MemPlace { ptr, .. }) => {
9062
if let Some(Provenance::Concrete { sb, .. }) = ptr.provenance {
9163
tags.insert(sb);
9264
}
9365
}
94-
Operand::Immediate(Immediate::Uninit)
95-
| Operand::Immediate(Immediate::Scalar(Scalar::Int(_))) => {}
9666
}
97-
}
98-
}
67+
},
68+
|tags, alloc| {
69+
for (_size, prov) in alloc.provenance().iter() {
70+
if let Provenance::Concrete { sb, .. } = prov {
71+
tags.insert(*sb);
72+
}
73+
}
74+
},
75+
);
76+
77+
self.remove_unreachable_tags(tags);
9978

10079
Ok(())
10180
}

0 commit comments

Comments
 (0)