diff --git a/src/borrow_tracker/mod.rs b/src/borrow_tracker/mod.rs index a95571572d..c0f7ec41b4 100644 --- a/src/borrow_tracker/mod.rs +++ b/src/borrow_tracker/mod.rs @@ -75,8 +75,8 @@ pub struct FrameState { pub protected_tags: SmallVec<[(AllocId, BorTag); 2]>, } -impl VisitTags for FrameState { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for FrameState { + fn visit_tags(&self, _visit: &mut TagVisitor<'_>) { // `protected_tags` are already recorded by `GlobalStateInner`. } } @@ -110,10 +110,10 @@ pub struct GlobalStateInner { pub unique_is_unique: bool, } -impl VisitTags for GlobalStateInner { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for GlobalStateInner { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { for &tag in self.protected_tags.keys() { - visit(tag); + visit(None, Some(tag)); } // The only other candidate is base_ptr_tags, and that does not need visiting since we don't ever // GC the bottommost/root tag. @@ -471,8 +471,8 @@ impl AllocState { } } -impl VisitTags for AllocState { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for AllocState { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { match self { AllocState::StackedBorrows(sb) => sb.visit_tags(visit), AllocState::TreeBorrows(tb) => tb.visit_tags(visit), diff --git a/src/borrow_tracker/stacked_borrows/mod.rs b/src/borrow_tracker/stacked_borrows/mod.rs index a74c69d52f..d77748caa1 100644 --- a/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/borrow_tracker/stacked_borrows/mod.rs @@ -462,10 +462,10 @@ impl Stacks { } } -impl VisitTags for Stacks { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for Stacks { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { for tag in self.exposed_tags.iter().copied() { - visit(tag); + visit(None, Some(tag)); } } } diff --git a/src/borrow_tracker/tree_borrows/tree.rs b/src/borrow_tracker/tree_borrows/tree.rs index b63b0bdff1..4891a572cb 100644 --- a/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/borrow_tracker/tree_borrows/tree.rs @@ -744,11 +744,11 @@ impl Tree { } } -impl VisitTags for Tree { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for Tree { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { // To ensure that the root never gets removed, we visit it // (the `root` node of `Tree` is not an `Option<_>`) - visit(self.nodes.get(self.root).unwrap().tag) + visit(None, Some(self.nodes.get(self.root).unwrap().tag)) } } diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index bec2972c50..64b13fe11a 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -693,9 +693,9 @@ pub struct VClockAlloc { alloc_ranges: RefCell>, } -impl VisitTags for VClockAlloc { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { - // No tags here. +impl VisitProvenance for VClockAlloc { + fn visit_tags(&self, _visit: &mut TagVisitor<'_>) { + // No tags or allocIds here. } } @@ -1269,8 +1269,8 @@ pub struct GlobalState { pub track_outdated_loads: bool, } -impl VisitTags for GlobalState { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for GlobalState { + fn visit_tags(&self, _visit: &mut TagVisitor<'_>) { // We don't have any tags. } } diff --git a/src/concurrency/init_once.rs b/src/concurrency/init_once.rs index 71582c75ea..4d4be029e5 100644 --- a/src/concurrency/init_once.rs +++ b/src/concurrency/init_once.rs @@ -45,8 +45,8 @@ pub(super) struct InitOnce<'mir, 'tcx> { data_race: VClock, } -impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl<'mir, 'tcx> VisitProvenance for InitOnce<'mir, 'tcx> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { for waiter in self.waiters.iter() { waiter.callback.visit_tags(visit); } diff --git a/src/concurrency/sync.rs b/src/concurrency/sync.rs index 62f6d57ef3..3a30dba841 100644 --- a/src/concurrency/sync.rs +++ b/src/concurrency/sync.rs @@ -181,8 +181,8 @@ pub(crate) struct SynchronizationState<'mir, 'tcx> { pub(super) init_onces: IndexVec>, } -impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl<'mir, 'tcx> VisitProvenance for SynchronizationState<'mir, 'tcx> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { for init_once in self.init_onces.iter() { init_once.visit_tags(visit); } diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index 9041683fbc..2eec491609 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -34,7 +34,7 @@ enum SchedulingAction { } /// Trait for callbacks that can be executed when some event happens, such as after a timeout. -pub trait MachineCallback<'mir, 'tcx>: VisitTags { +pub trait MachineCallback<'mir, 'tcx>: VisitProvenance { fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>; } @@ -219,8 +219,8 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> { } } -impl VisitTags for Thread<'_, '_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for Thread<'_, '_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let Thread { panic_payloads: panic_payload, last_error, @@ -242,8 +242,8 @@ impl VisitTags for Thread<'_, '_> { } } -impl VisitTags for Frame<'_, '_, Provenance, FrameExtra<'_>> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for Frame<'_, '_, Provenance, FrameExtra<'_>> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let Frame { return_place, locals, @@ -332,8 +332,8 @@ pub struct ThreadManager<'mir, 'tcx> { timeout_callbacks: FxHashMap>, } -impl VisitTags for ThreadManager<'_, '_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for ThreadManager<'_, '_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let ThreadManager { threads, thread_local_alloc_ids, diff --git a/src/concurrency/weak_memory.rs b/src/concurrency/weak_memory.rs index 6781b1f6dd..2f3037b2b4 100644 --- a/src/concurrency/weak_memory.rs +++ b/src/concurrency/weak_memory.rs @@ -108,8 +108,8 @@ pub struct StoreBufferAlloc { store_buffers: RefCell>, } -impl VisitTags for StoreBufferAlloc { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for StoreBufferAlloc { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let Self { store_buffers } = self; for val in store_buffers .borrow() diff --git a/src/intptrcast.rs b/src/intptrcast.rs index ab6a256f71..64e2a86c37 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -46,9 +46,21 @@ pub struct GlobalStateInner { provenance_mode: ProvenanceMode, } -impl VisitTags for GlobalStateInner { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { - // Nothing to visit here. +impl VisitProvenance for GlobalStateInner { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + let GlobalStateInner { + int_to_ptr_map: _, + base_addr: _, + exposed, + next_base_addr: _, + provenance_mode: _, + } = self; + // Though int_to_ptr_map and base_addr contain AllocIds, we do not want to visit them + // because they are the state accumulation that our GC is supposed to clean out. Every + // allocation produces an entry in int_to_ptr_map and base_addr indefinitely. + for id in exposed { + id.visit_tags(visit) + } } } @@ -62,6 +74,10 @@ impl GlobalStateInner { provenance_mode: config.provenance_mode, } } + + pub fn remove_unreachable_allocs(&mut self, reachable_allocs: &FxHashSet) { + self.base_addr.retain(|id, _| reachable_allocs.contains(id)); + } } /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple diff --git a/src/lib.rs b/src/lib.rs index 68b9164dec..ebce5526e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,9 +76,9 @@ mod intptrcast; mod machine; mod mono_hash_map; mod operator; +mod provenance_gc; mod range_map; mod shims; -mod tag_gc; // Establish a "crate-wide prelude": we often import `crate::*`. @@ -124,8 +124,8 @@ pub use crate::machine::{ }; pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as _; +pub use crate::provenance_gc::{EvalContextExt as _, TagVisitor, VisitProvenance}; pub use crate::range_map::RangeMap; -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. diff --git a/src/machine.rs b/src/machine.rs index d5775912ea..0e671f48c4 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -77,8 +77,8 @@ impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> { } } -impl VisitTags for FrameExtra<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for FrameExtra<'_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _ } = self; catch_unwind.visit_tags(visit); @@ -311,8 +311,8 @@ pub struct AllocExtra<'tcx> { pub backtrace: Option>>, } -impl VisitTags for AllocExtra<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for AllocExtra<'_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self; borrow_tracker.visit_tags(visit); @@ -793,8 +793,8 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { } } -impl VisitTags for MiriMachine<'_, '_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for MiriMachine<'_, '_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { #[rustfmt::skip] let MiriMachine { threads, @@ -1380,7 +1380,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { // where it mistakenly removes an important tag become visible. if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval { ecx.machine.since_gc = 0; - ecx.garbage_collect_tags()?; + ecx.run_provenance_gc(); } // These are our preemption points. diff --git a/src/provenance_gc.rs b/src/provenance_gc.rs new file mode 100644 index 0000000000..d01fee3dad --- /dev/null +++ b/src/provenance_gc.rs @@ -0,0 +1,185 @@ +use either::Either; + +use rustc_data_structures::fx::FxHashSet; + +use crate::*; + +pub type TagVisitor<'a> = dyn FnMut(Option, Option) + 'a; + +pub trait VisitProvenance { + fn visit_tags(&self, visit: &mut TagVisitor<'_>); +} + +impl VisitProvenance for Option { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + if let Some(x) = self { + x.visit_tags(visit); + } + } +} + +impl VisitProvenance for std::cell::RefCell { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + self.borrow().visit_tags(visit) + } +} + +impl VisitProvenance for BorTag { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + visit(None, Some(*self)) + } +} + +impl VisitProvenance for AllocId { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + visit(Some(*self), None) + } +} + +impl VisitProvenance for Provenance { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + if let Provenance::Concrete { alloc_id, tag, .. } = self { + visit(Some(*alloc_id), Some(*tag)); + } + } +} + +impl VisitProvenance for Pointer { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + let (prov, _offset) = self.into_parts(); + prov.visit_tags(visit); + } +} + +impl VisitProvenance for Pointer> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + let (prov, _offset) = self.into_parts(); + prov.visit_tags(visit); + } +} + +impl VisitProvenance for Scalar { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + match self { + Scalar::Ptr(ptr, _) => ptr.visit_tags(visit), + Scalar::Int(_) => (), + } + } +} + +impl VisitProvenance for Immediate { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + match self { + Immediate::Scalar(s) => { + s.visit_tags(visit); + } + Immediate::ScalarPair(s1, s2) => { + s1.visit_tags(visit); + s2.visit_tags(visit); + } + Immediate::Uninit => {} + } + } +} + +impl VisitProvenance for MemPlaceMeta { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + match self { + MemPlaceMeta::Meta(m) => m.visit_tags(visit), + MemPlaceMeta::None => {} + } + } +} + +impl VisitProvenance for ImmTy<'_, Provenance> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + (**self).visit_tags(visit) + } +} + +impl VisitProvenance for MPlaceTy<'_, Provenance> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + self.ptr().visit_tags(visit); + self.meta().visit_tags(visit); + } +} + +impl VisitProvenance for PlaceTy<'_, Provenance> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + match self.as_mplace_or_local() { + Either::Left(mplace) => mplace.visit_tags(visit), + Either::Right(_) => (), + } + } +} + +impl VisitProvenance for OpTy<'_, Provenance> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + match self.as_mplace_or_imm() { + Either::Left(mplace) => mplace.visit_tags(visit), + Either::Right(imm) => imm.visit_tags(visit), + } + } +} + +impl VisitProvenance for Allocation> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + for prov in self.provenance().provenances() { + prov.visit_tags(visit); + } + + self.extra.visit_tags(visit); + } +} + +impl VisitProvenance for crate::MiriInterpCx<'_, '_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { + // Memory. + self.memory.alloc_map().iter(|it| { + for (id, (_kind, alloc)) in it { + id.visit_tags(visit); + 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> { + fn run_provenance_gc(&mut self) { + let this = self.eval_context_mut(); + + let mut tags = FxHashSet::default(); + let mut alloc_ids = FxHashSet::default(); + this.visit_tags(&mut |id, tag| { + if let Some(id) = id { + alloc_ids.insert(id); + } + if let Some(tag) = tag { + tags.insert(tag); + } + }); + self.remove_unreachable_tags(tags); + self.remove_unreachable_allocs(alloc_ids); + } + + fn remove_unreachable_tags(&mut self, tags: FxHashSet) { + let this = self.eval_context_mut(); + this.memory.alloc_map().iter(|it| { + for (_id, (_kind, alloc)) in it { + if let Some(bt) = &alloc.extra.borrow_tracker { + bt.remove_unreachable_tags(&tags); + } + } + }); + } + + fn remove_unreachable_allocs(&mut self, allocs: FxHashSet) { + let this = self.eval_context_mut(); + this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.contains(id)); + this.machine.intptrcast.borrow_mut().remove_unreachable_allocs(&allocs); + } +} diff --git a/src/shims/env.rs b/src/shims/env.rs index 154a7f6983..9a982b1ca8 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -37,8 +37,8 @@ pub struct EnvVars<'tcx> { pub(crate) environ: Option>, } -impl VisitTags for EnvVars<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for EnvVars<'_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let EnvVars { map, environ } = self; environ.visit_tags(visit); diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 2d5df30374..28073d9018 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -459,6 +459,10 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // shim, add it to the corresponding submodule. match link_name.as_str() { // Miri-specific extern functions + "miri_run_provenance_gc" => { + let [] = this.check_shim(abi, Abi::Rust, link_name, args)?; + this.run_provenance_gc(); + } "miri_get_alloc_id" => { let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; diff --git a/src/shims/panic.rs b/src/shims/panic.rs index 5c0f828e4e..fb5b546a9b 100644 --- a/src/shims/panic.rs +++ b/src/shims/panic.rs @@ -35,8 +35,8 @@ pub struct CatchUnwindData<'tcx> { ret: mir::BasicBlock, } -impl VisitTags for CatchUnwindData<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for CatchUnwindData<'_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let CatchUnwindData { catch_fn, data, dest, ret: _ } = self; catch_fn.visit_tags(visit); data.visit_tags(visit); diff --git a/src/shims/time.rs b/src/shims/time.rs index d6d0483f5e..9d7a57f38e 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -278,8 +278,8 @@ struct UnblockCallback { thread_to_unblock: ThreadId, } -impl VisitTags for UnblockCallback { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {} +impl VisitProvenance for UnblockCallback { + fn visit_tags(&self, _visit: &mut TagVisitor<'_>) {} } impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback { diff --git a/src/shims/tls.rs b/src/shims/tls.rs index 62bd087e7e..24d63a32b2 100644 --- a/src/shims/tls.rs +++ b/src/shims/tls.rs @@ -207,8 +207,8 @@ impl<'tcx> TlsData<'tcx> { } } -impl VisitTags for TlsData<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for TlsData<'_> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let TlsData { keys, macos_thread_dtors, next_key: _ } = self; for scalar in keys.values().flat_map(|v| v.data.values()) { diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 062623a7f6..429398ae4e 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -288,8 +288,8 @@ pub struct FileHandler { pub handles: BTreeMap>, } -impl VisitTags for FileHandler { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for FileHandler { + fn visit_tags(&self, _visit: &mut TagVisitor<'_>) { // All our FileDescriptor do not have any tags. } } @@ -490,8 +490,8 @@ impl Default for DirHandler { } } -impl VisitTags for DirHandler { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for DirHandler { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let DirHandler { streams, next_id: _ } = self; for dir in streams.values() { diff --git a/src/shims/unix/linux/sync.rs b/src/shims/unix/linux/sync.rs index ff25b8120b..a64232aa10 100644 --- a/src/shims/unix/linux/sync.rs +++ b/src/shims/unix/linux/sync.rs @@ -182,8 +182,8 @@ pub fn futex<'tcx>( dest: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let Callback { thread: _, addr_usize: _, dest } = self; dest.visit_tags(visit); } diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index 6666ffbd1d..e08716f15b 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -816,8 +816,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let Callback { active_thread: _, mutex_id: _, id: _, dest } = self; dest.visit_tags(visit); } diff --git a/src/shims/windows/sync.rs b/src/shims/windows/sync.rs index 2c9603097c..8d842545df 100644 --- a/src/shims/windows/sync.rs +++ b/src/shims/windows/sync.rs @@ -204,8 +204,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { pending_place: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let Callback { init_once_id: _, pending_place } = self; pending_place.visit_tags(visit); } @@ -337,8 +337,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let Callback { thread: _, addr: _, dest } = self; dest.visit_tags(visit); } @@ -441,8 +441,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_tags(&self, visit: &mut TagVisitor<'_>) { let Callback { thread: _, condvar_id: _, lock_id: _, mode: _, dest } = self; dest.visit_tags(visit); } diff --git a/src/tag_gc.rs b/src/tag_gc.rs deleted file mode 100644 index 3cccdd3635..0000000000 --- a/src/tag_gc.rs +++ /dev/null @@ -1,169 +0,0 @@ -use either::Either; - -use rustc_data_structures::fx::FxHashSet; - -use crate::*; - -pub trait VisitTags { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)); -} - -impl VisitTags for Option { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - if let Some(x) = self { - x.visit_tags(visit); - } - } -} - -impl VisitTags for std::cell::RefCell { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - self.borrow().visit_tags(visit) - } -} - -impl VisitTags for BorTag { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - visit(*self) - } -} - -impl VisitTags for Provenance { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - if let Provenance::Concrete { tag, .. } = self { - visit(*tag); - } - } -} - -impl VisitTags for Pointer { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - let (prov, _offset) = self.into_parts(); - prov.visit_tags(visit); - } -} - -impl VisitTags for Pointer> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - let (prov, _offset) = self.into_parts(); - prov.visit_tags(visit); - } -} - -impl VisitTags for Scalar { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self { - Scalar::Ptr(ptr, _) => ptr.visit_tags(visit), - Scalar::Int(_) => (), - } - } -} - -impl VisitTags for Immediate { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self { - Immediate::Scalar(s) => { - s.visit_tags(visit); - } - Immediate::ScalarPair(s1, s2) => { - s1.visit_tags(visit); - s2.visit_tags(visit); - } - Immediate::Uninit => {} - } - } -} - -impl VisitTags for MemPlaceMeta { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self { - MemPlaceMeta::Meta(m) => m.visit_tags(visit), - MemPlaceMeta::None => {} - } - } -} - -impl VisitTags for ImmTy<'_, Provenance> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - (**self).visit_tags(visit) - } -} - -impl VisitTags for MPlaceTy<'_, Provenance> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - self.ptr().visit_tags(visit); - self.meta().visit_tags(visit); - } -} - -impl VisitTags for PlaceTy<'_, Provenance> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self.as_mplace_or_local() { - Either::Left(mplace) => mplace.visit_tags(visit), - Either::Right(_) => (), - } - } -} - -impl VisitTags for OpTy<'_, Provenance> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self.as_mplace_or_imm() { - Either::Left(mplace) => mplace.visit_tags(visit), - Either::Right(imm) => imm.visit_tags(visit), - } - } -} - -impl VisitTags for Allocation> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - for prov in self.provenance().provenances() { - prov.visit_tags(visit); - } - - self.extra.visit_tags(visit); - } -} - -impl VisitTags for crate::MiriInterpCx<'_, '_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - // 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> { - 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. - if this.machine.borrow_tracker.is_none() { - return Ok(()); - } - - let mut tags = FxHashSet::default(); - this.visit_tags(&mut |tag| { - tags.insert(tag); - }); - self.remove_unreachable_tags(tags); - - Ok(()) - } - - fn remove_unreachable_tags(&mut self, tags: FxHashSet) { - let this = self.eval_context_mut(); - this.memory.alloc_map().iter(|it| { - for (_id, (_kind, alloc)) in it { - if let Some(bt) = &alloc.extra.borrow_tracker { - bt.remove_unreachable_tags(&tags); - } - } - }); - } -} diff --git a/tests/pass/ptr_int_casts.rs b/tests/pass/ptr_int_casts.rs index a2fcd09810..820bd33ea4 100644 --- a/tests/pass/ptr_int_casts.rs +++ b/tests/pass/ptr_int_casts.rs @@ -1,6 +1,10 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-permissive-provenance + +#[path = "../utils/mod.rs"] +mod utils; + use std::mem; use std::ptr; @@ -46,6 +50,20 @@ fn ptr_int_casts() { // involving types other than usize assert_eq!((-1i32) as usize as *const i32 as usize, (-1i32) as usize); + + // Check that the GC doesn't delete context that would prevent us from casting from int + // to pointer correctly after the allocation is dead. + let (ptr, int) = { + let local = 0u8; + let ptr = &local as *const u8; + (ptr, ptr as usize) + }; + // Manually run the GC, instead of just hoping that it runs at the right time. + unsafe { utils::miri_run_provenance_gc() } + let later_int = ptr as usize; + assert_eq!(int, later_int); + let later_ptr = int as *const u8; + assert_eq!(ptr, later_ptr); } fn ptr_int_ops() { diff --git a/tests/utils/miri_extern.rs b/tests/utils/miri_extern.rs index c0ef2c5064..943eebf46a 100644 --- a/tests/utils/miri_extern.rs +++ b/tests/utils/miri_extern.rs @@ -7,8 +7,7 @@ pub struct MiriFrame { // The size of filename of the function being executed, encoded in UTF-8 pub filename_len: usize, // The line number currently being executed in `filename`, starting from '1'. - pub lineno: u32, - // The column number currently being executed in `filename`, starting from '1'. + pub lineno: u32, // The column number currently being executed in `filename`, starting from '1'. pub colno: u32, // The function pointer to the function currently being executed. // This can be compared against function pointers obtained by @@ -137,4 +136,9 @@ extern "Rust" { out: *mut std::ffi::c_char, out_size: usize, ) -> usize; + + /// Run the provenance GC. The GC will run automatically at some cadence, but tests we want to + /// have control of when it runs so that we can run it for sure at certain points to make sure + /// that it doesn't break anything. + pub fn miri_run_provenance_gc(); }