re-architect the tag visitor traits

This commit is contained in:
Ralf Jung 2022-10-02 16:22:53 +02:00
parent 841d1b24ed
commit e212af2f65
15 changed files with 332 additions and 248 deletions

View File

@ -696,6 +696,12 @@ pub struct VClockAlloc {
alloc_ranges: RefCell<RangeMap<MemoryCellClocks>>,
}
impl VisitTags for VClockAlloc {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
// No tags here.
}
}
impl VClockAlloc {
/// Create a new data-race detector for newly allocated memory.
pub fn new_allocation(
@ -1239,6 +1245,12 @@ pub struct GlobalState {
pub track_outdated_loads: bool,
}
impl VisitTags for GlobalState {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
// We don't have any tags.
}
}
impl GlobalState {
/// Create a new global state, setup with just thread-id=0
/// advanced to timestamp = 1.

View File

@ -32,7 +32,7 @@ pub enum SchedulingAction {
/// Timeout callbacks can be created by synchronization primitives to tell the
/// scheduler that they should be called once some period of time passes.
pub trait MachineCallback<'mir, 'tcx>: VisitMachineValues {
pub trait MachineCallback<'mir, 'tcx>: VisitTags {
fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>;
}
@ -183,25 +183,21 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
}
}
impl VisitMachineValues for Thread<'_, '_> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for Thread<'_, '_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let Thread { panic_payload, last_error, stack, state: _, thread_name: _, join_status: _ } =
self;
if let Some(payload) = panic_payload {
visit.visit(*payload);
}
if let Some(error) = last_error {
visit.visit(**error);
}
panic_payload.visit_tags(visit);
last_error.visit_tags(visit);
for frame in stack {
frame.visit_machine_values(visit)
frame.visit_tags(visit)
}
}
}
impl VisitMachineValues for Frame<'_, '_, Provenance, FrameData<'_>> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for Frame<'_, '_, Provenance, FrameData<'_>> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let Frame {
return_place,
locals,
@ -210,21 +206,20 @@ impl VisitMachineValues for Frame<'_, '_, Provenance, FrameData<'_>> {
instance: _,
return_to_block: _,
loc: _,
// There are some private fields we cannot access; they contain no tags.
..
} = self;
// Return place.
if let Place::Ptr(mplace) = **return_place {
visit.visit(mplace);
}
return_place.visit_tags(visit);
// Locals.
for local in locals.iter() {
if let LocalValue::Live(value) = &local.value {
visit.visit(value);
value.visit_tags(visit);
}
}
extra.visit_machine_values(visit);
extra.visit_tags(visit);
}
}
@ -300,8 +295,8 @@ impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
}
}
impl VisitMachineValues for ThreadManager<'_, '_> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for ThreadManager<'_, '_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let ThreadManager {
threads,
thread_local_alloc_ids,
@ -312,13 +307,13 @@ impl VisitMachineValues for ThreadManager<'_, '_> {
} = self;
for thread in threads {
thread.visit_machine_values(visit);
thread.visit_tags(visit);
}
for ptr in thread_local_alloc_ids.borrow().values().copied() {
visit.visit(ptr);
for ptr in thread_local_alloc_ids.borrow().values() {
ptr.visit_tags(visit);
}
for callback in timeout_callbacks.values() {
callback.callback.visit_machine_values(visit);
callback.callback.visit_tags(visit);
}
}
}

View File

@ -108,15 +108,15 @@ pub struct StoreBufferAlloc {
store_buffers: RefCell<RangeObjectMap<StoreBuffer>>,
}
impl VisitMachineValues for StoreBufferAlloc {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
for val in self
.store_buffers
impl VisitTags for StoreBufferAlloc {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let Self { store_buffers } = self;
for val in store_buffers
.borrow()
.iter()
.flat_map(|buf| buf.buffer.iter().map(|element| &element.val))
{
visit.visit(val);
val.visit_tags(visit);
}
}
}

View File

@ -44,6 +44,12 @@ pub struct GlobalStateInner {
provenance_mode: ProvenanceMode,
}
impl VisitTags for GlobalStateInner {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
// Nothing to visit here.
}
}
impl GlobalStateInner {
pub fn new(config: &MiriConfig) -> Self {
GlobalStateInner {

View File

@ -112,7 +112,7 @@ pub use crate::range_map::RangeMap;
pub use crate::stacked_borrows::{
CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, SbTag, Stack, Stacks,
};
pub use crate::tag_gc::{EvalContextExt as _, ProvenanceVisitor, VisitMachineValues};
pub use crate::tag_gc::{EvalContextExt as _, VisitTags};
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
/// set per default, for maximal validation power.

View File

@ -63,13 +63,12 @@ impl<'tcx> std::fmt::Debug for FrameData<'tcx> {
}
}
impl VisitMachineValues for FrameData<'_> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
let FrameData { catch_unwind, stacked_borrows: _, timing: _ } = self;
impl VisitTags for FrameData<'_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let FrameData { catch_unwind, stacked_borrows, timing: _ } = self;
if let Some(catch_unwind) = catch_unwind {
catch_unwind.visit_machine_values(visit);
}
catch_unwind.visit_tags(visit);
stacked_borrows.visit_tags(visit);
}
}
@ -261,17 +260,13 @@ pub struct AllocExtra {
pub weak_memory: Option<weak_memory::AllocExtra>,
}
impl VisitMachineValues for AllocExtra {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
let AllocExtra { stacked_borrows, data_race: _, weak_memory } = self;
impl VisitTags for AllocExtra {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let AllocExtra { stacked_borrows, data_race, weak_memory } = self;
if let Some(stacked_borrows) = stacked_borrows {
stacked_borrows.borrow().visit_machine_values(visit);
}
if let Some(weak_memory) = weak_memory {
weak_memory.visit_machine_values(visit);
}
stacked_borrows.visit_tags(visit);
data_race.visit_tags(visit);
weak_memory.visit_tags(visit);
}
}
@ -615,8 +610,9 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
}
}
impl VisitMachineValues for MiriMachine<'_, '_> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for MiriMachine<'_, '_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
#[rustfmt::skip]
let MiriMachine {
threads,
tls,
@ -626,25 +622,52 @@ impl VisitMachineValues for MiriMachine<'_, '_> {
cmd_line,
extern_statics,
dir_handler,
..
stacked_borrows,
data_race,
intptrcast,
file_handler,
tcx: _,
isolated_op: _,
validate: _,
enforce_abi: _,
clock: _,
layouts: _,
static_roots: _,
profiler: _,
string_cache: _,
exported_symbols_cache: _,
panic_on_unsupported: _,
backtrace_style: _,
local_crates: _,
rng: _,
tracked_alloc_ids: _,
check_alignment: _,
cmpxchg_weak_failure_rate: _,
mute_stdout_stderr: _,
weak_memory: _,
preemption_rate: _,
report_progress: _,
basic_block_count: _,
#[cfg(unix)]
external_so_lib: _,
gc_interval: _,
since_gc: _,
num_cpus: _,
} = self;
threads.visit_machine_values(visit);
tls.visit_machine_values(visit);
env_vars.visit_machine_values(visit);
dir_handler.visit_machine_values(visit);
if let Some(argc) = argc {
visit.visit(argc);
}
if let Some(argv) = argv {
visit.visit(argv);
}
if let Some(cmd_line) = cmd_line {
visit.visit(cmd_line);
}
for ptr in extern_statics.values().copied() {
visit.visit(ptr);
threads.visit_tags(visit);
tls.visit_tags(visit);
env_vars.visit_tags(visit);
dir_handler.visit_tags(visit);
file_handler.visit_tags(visit);
data_race.visit_tags(visit);
stacked_borrows.visit_tags(visit);
intptrcast.visit_tags(visit);
argc.visit_tags(visit);
argv.visit_tags(visit);
cmd_line.visit_tags(visit);
for ptr in extern_statics.values() {
ptr.visit_tags(visit);
}
}
}

View File

@ -36,15 +36,13 @@ pub struct EnvVars<'tcx> {
pub(crate) environ: Option<MPlaceTy<'tcx, Provenance>>,
}
impl VisitMachineValues for EnvVars<'_> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for EnvVars<'_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let EnvVars { map, environ } = self;
environ.visit_tags(visit);
for ptr in map.values() {
visit.visit(*ptr);
}
if let Some(env) = environ {
visit.visit(**env);
ptr.visit_tags(visit);
}
}
}

View File

@ -35,11 +35,12 @@ pub struct CatchUnwindData<'tcx> {
ret: mir::BasicBlock,
}
impl VisitMachineValues for CatchUnwindData<'_> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
let CatchUnwindData { catch_fn, data, dest: _, ret: _ } = self;
visit.visit(catch_fn);
visit.visit(data);
impl VisitTags for CatchUnwindData<'_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let CatchUnwindData { catch_fn, data, dest, ret: _ } = self;
catch_fn.visit_tags(visit);
data.visit_tags(visit);
dest.visit_tags(visit);
}
}

View File

@ -219,7 +219,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.register_timeout_callback(
active_thread,
Time::Monotonic(timeout_time),
Box::new(Callback { active_thread }),
Box::new(UnblockCallback { thread_to_unblock: active_thread }),
);
Ok(0)
@ -242,24 +242,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.register_timeout_callback(
active_thread,
Time::Monotonic(timeout_time),
Box::new(Callback { active_thread }),
Box::new(UnblockCallback { thread_to_unblock: active_thread }),
);
Ok(())
}
}
struct Callback {
active_thread: ThreadId,
struct UnblockCallback {
thread_to_unblock: ThreadId,
}
impl VisitMachineValues for Callback {
fn visit_machine_values(&self, _visit: &mut ProvenanceVisitor) {}
impl VisitTags for UnblockCallback {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {}
}
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback {
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback {
fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
ecx.unblock_thread(self.active_thread);
ecx.unblock_thread(self.thread_to_unblock);
Ok(())
}
}

View File

@ -235,15 +235,15 @@ impl<'tcx> TlsData<'tcx> {
}
}
impl VisitMachineValues for TlsData<'_> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for TlsData<'_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let TlsData { keys, macos_thread_dtors, next_key: _, dtors_running: _ } = self;
for scalar in keys.values().flat_map(|v| v.data.values()) {
visit.visit(scalar);
scalar.visit_tags(visit);
}
for (_, scalar) in macos_thread_dtors.values() {
visit.visit(scalar);
scalar.visit_tags(visit);
}
}
}

View File

@ -256,6 +256,12 @@ pub struct FileHandler {
handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
}
impl VisitTags for FileHandler {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
// All our FileDescriptor do not have any tags.
}
}
impl FileHandler {
pub(crate) fn new(mute_stdout_stderr: bool) -> FileHandler {
let mut handles: BTreeMap<_, Box<dyn FileDescriptor>> = BTreeMap::new();
@ -462,12 +468,12 @@ impl Default for DirHandler {
}
}
impl VisitMachineValues for DirHandler {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for DirHandler {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let DirHandler { streams, next_id: _ } = self;
for dir in streams.values() {
visit.visit(dir.entry);
dir.entry.visit_tags(visit);
}
}
}

View File

@ -189,6 +189,31 @@ pub fn futex<'tcx>(
// Register a timeout callback if a timeout was specified.
// This callback will override the return value when the timeout triggers.
if let Some(timeout_time) = timeout_time {
struct Callback<'tcx> {
thread: ThreadId,
addr_usize: u64,
dest: PlaceTy<'tcx, Provenance>,
}
impl<'tcx> VisitTags for Callback<'tcx> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let Callback { thread: _, addr_usize: _, dest } = self;
dest.visit_tags(visit);
}
}
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> {
fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
this.unblock_thread(self.thread);
this.futex_remove_waiter(self.addr_usize, self.thread);
let etimedout = this.eval_libc("ETIMEDOUT")?;
this.set_last_error(etimedout)?;
this.write_scalar(Scalar::from_machine_isize(-1, this), &self.dest)?;
Ok(())
}
}
let dest = dest.clone();
this.register_timeout_callback(
thread,
@ -252,30 +277,3 @@ pub fn futex<'tcx>(
Ok(())
}
struct Callback<'tcx> {
thread: ThreadId,
addr_usize: u64,
dest: PlaceTy<'tcx, Provenance>,
}
impl<'tcx> VisitMachineValues for Callback<'tcx> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
let Callback { thread: _, addr_usize: _, dest } = self;
if let Place::Ptr(place) = **dest {
visit.visit(place);
}
}
}
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> {
fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
this.unblock_thread(self.thread);
this.futex_remove_waiter(self.addr_usize, self.thread);
let etimedout = this.eval_libc("ETIMEDOUT")?;
this.set_last_error(etimedout)?;
this.write_scalar(Scalar::from_machine_isize(-1, this), &self.dest)?;
Ok(())
}
}

View File

@ -851,6 +851,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// We return success for now and override it in the timeout callback.
this.write_scalar(Scalar::from_i32(0), dest)?;
struct Callback<'tcx> {
active_thread: ThreadId,
mutex_id: MutexId,
id: CondvarId,
dest: PlaceTy<'tcx, Provenance>,
}
impl<'tcx> VisitTags for Callback<'tcx> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let Callback { active_thread: _, mutex_id: _, id: _, dest } = self;
dest.visit_tags(visit);
}
}
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> {
fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
// We are not waiting for the condvar any more, wait for the
// mutex instead.
reacquire_cond_mutex(ecx, self.active_thread, self.mutex_id)?;
// Remove the thread from the conditional variable.
ecx.condvar_remove_waiter(self.id, self.active_thread);
// Set the return value: we timed out.
let etimedout = ecx.eval_libc("ETIMEDOUT")?;
ecx.write_scalar(etimedout, &self.dest)?;
Ok(())
}
}
// Register the timeout callback.
let dest = dest.clone();
this.register_timeout_callback(
@ -885,39 +916,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
struct Callback<'tcx> {
active_thread: ThreadId,
mutex_id: MutexId,
id: CondvarId,
dest: PlaceTy<'tcx, Provenance>,
}
impl<'tcx> VisitMachineValues for Callback<'tcx> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
let Callback { active_thread: _, mutex_id: _, id: _, dest } = self;
if let Place::Ptr(place) = **dest {
visit.visit(place);
}
}
}
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> {
fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
// We are not waiting for the condvar any more, wait for the
// mutex instead.
reacquire_cond_mutex(ecx, self.active_thread, self.mutex_id)?;
// Remove the thread from the conditional variable.
ecx.condvar_remove_waiter(self.id, self.active_thread);
// Set the return value: we timed out.
let etimedout = ecx.eval_libc("ETIMEDOUT")?;
ecx.write_scalar(etimedout, &self.dest)?;
Ok(())
}
}
fn layout_of_maybe_uninit<'tcx>(tcx: TyCtxtAt<'tcx>, param: Ty<'tcx>) -> TyAndLayout<'tcx> {
let def_id = tcx.require_lang_item(LangItem::MaybeUninit, None);
let ty = tcx.bound_type_of(def_id).subst(*tcx, &[param.into()]);

View File

@ -71,6 +71,12 @@ pub struct FrameExtra {
protected_tags: SmallVec<[SbTag; 2]>,
}
impl VisitTags for FrameExtra {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
// `protected_tags` are fine to GC.
}
}
/// Extra per-allocation state.
#[derive(Clone, Debug)]
pub struct Stacks {
@ -109,6 +115,13 @@ pub struct GlobalStateInner {
retag_fields: bool,
}
impl VisitTags for GlobalStateInner {
fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
// The only candidate is base_ptr_tags, and that does not need visiting since we don't ever
// GC the bottommost tag.
}
}
/// We need interior mutable access to the global state.
pub type GlobalState = RefCell<GlobalStateInner>;
@ -513,10 +526,10 @@ impl Stacks {
}
}
impl VisitMachineValues for Stacks {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for Stacks {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
for tag in self.exposed_tags.iter().copied() {
visit.visit(tag);
visit(tag);
}
}
}

View File

@ -2,123 +2,155 @@ use rustc_data_structures::fx::FxHashSet;
use crate::*;
pub trait VisitMachineValues {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor);
pub trait VisitTags {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag));
}
pub trait MachineValue {
fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>);
}
pub struct ProvenanceVisitor {
tags: FxHashSet<SbTag>,
}
impl ProvenanceVisitor {
pub fn visit<V>(&mut self, v: V)
where
V: MachineValue,
{
v.visit_provenance(&mut self.tags);
impl<T: VisitTags> VisitTags for Option<T> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
if let Some(x) = self {
x.visit_tags(visit);
}
}
}
impl<T: MachineValue> MachineValue for &T {
fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
(**self).visit_provenance(tags);
impl<T: VisitTags> VisitTags for std::cell::RefCell<T> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
self.borrow().visit_tags(visit)
}
}
impl MachineValue for Operand<Provenance> {
fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
impl VisitTags for SbTag {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
visit(*self)
}
}
impl VisitTags for Provenance {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
if let Provenance::Concrete { sb, .. } = self {
visit(*sb);
}
}
}
impl VisitTags for Pointer<Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let (prov, _offset) = self.into_parts();
prov.visit_tags(visit);
}
}
impl VisitTags for Pointer<Option<Provenance>> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let (prov, _offset) = self.into_parts();
prov.visit_tags(visit);
}
}
impl VisitTags for Scalar<Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
match self {
Operand::Immediate(Immediate::Scalar(s)) => {
s.visit_provenance(tags);
Scalar::Ptr(ptr, _) => ptr.visit_tags(visit),
Scalar::Int(_) => (),
}
}
}
impl VisitTags for Immediate<Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
match self {
Immediate::Scalar(s) => {
s.visit_tags(visit);
}
Operand::Immediate(Immediate::ScalarPair(s1, s2)) => {
s1.visit_provenance(tags);
s2.visit_provenance(tags);
Immediate::ScalarPair(s1, s2) => {
s1.visit_tags(visit);
s2.visit_tags(visit);
}
Immediate::Uninit => {}
}
}
}
impl VisitTags for MemPlaceMeta<Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
match self {
MemPlaceMeta::Meta(m) => m.visit_tags(visit),
MemPlaceMeta::None => {}
}
}
}
impl VisitTags for MemPlace<Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let MemPlace { ptr, meta } = self;
ptr.visit_tags(visit);
meta.visit_tags(visit);
}
}
impl VisitTags for MPlaceTy<'_, Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
(**self).visit_tags(visit)
}
}
impl VisitTags for Place<Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
match self {
Place::Ptr(p) => p.visit_tags(visit),
Place::Local { .. } => {
// Will be visited as part of the stack frame.
}
}
}
}
impl VisitTags for PlaceTy<'_, Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
(**self).visit_tags(visit)
}
}
impl VisitTags for Operand<Provenance> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
match self {
Operand::Immediate(imm) => {
imm.visit_tags(visit);
}
Operand::Immediate(Immediate::Uninit) => {}
Operand::Indirect(p) => {
p.visit_provenance(tags);
p.visit_tags(visit);
}
}
}
}
impl MachineValue for Scalar<Provenance> {
fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
if let Scalar::Ptr(ptr, _) = self {
if let Provenance::Concrete { sb, .. } = ptr.provenance {
tags.insert(sb);
}
}
}
}
impl MachineValue for MemPlace<Provenance> {
fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
if let Some(Provenance::Concrete { sb, .. }) = self.ptr.provenance {
tags.insert(sb);
}
}
}
impl MachineValue for SbTag {
fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
tags.insert(*self);
}
}
impl MachineValue for Pointer<Provenance> {
fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
let (prov, _offset) = self.into_parts();
if let Provenance::Concrete { sb, .. } = prov {
tags.insert(sb);
}
}
}
impl MachineValue for Pointer<Option<Provenance>> {
fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
let (prov, _offset) = self.into_parts();
if let Some(Provenance::Concrete { sb, .. }) = prov {
tags.insert(sb);
}
}
}
impl VisitMachineValues for Allocation<Provenance, AllocExtra> {
fn visit_machine_values(&self, visit: &mut ProvenanceVisitor) {
impl VisitTags for Allocation<Provenance, AllocExtra> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
for (_size, prov) in self.provenance().iter() {
if let Provenance::Concrete { sb, .. } = prov {
visit.visit(*sb);
}
prov.visit_tags(visit);
}
self.extra.visit_machine_values(visit);
self.extra.visit_tags(visit);
}
}
impl VisitTags for crate::MiriInterpCx<'_, '_> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
// Memory.
self.memory.alloc_map().iter(|it| {
for (_id, (_kind, alloc)) in it {
alloc.visit_tags(visit);
}
});
// And all the other machine values.
self.machine.visit_tags(visit);
}
}
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
/// GC helper to visit everything that can store provenance. The `ProvenanceVisitor` knows how
/// to extract provenance from the interpreter data types.
fn visit_all_machine_values(&self, acc: &mut ProvenanceVisitor) {
let this = self.eval_context_ref();
// Memory.
this.memory.alloc_map().iter(|it| {
for (_id, (_kind, alloc)) in it {
alloc.visit_machine_values(acc);
}
});
// And all the other machine values.
this.machine.visit_machine_values(acc);
}
fn garbage_collect_tags(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
// No reason to do anything at all if stacked borrows is off.
@ -126,9 +158,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
return Ok(());
}
let mut visitor = ProvenanceVisitor { tags: FxHashSet::default() };
this.visit_all_machine_values(&mut visitor);
self.remove_unreachable_tags(visitor.tags);
let mut tags = FxHashSet::default();
this.visit_tags(&mut |tag| {
tags.insert(tag);
});
self.remove_unreachable_tags(tags);
Ok(())
}