about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/miri/src/concurrency/data_race.rs12
-rw-r--r--src/tools/miri/src/concurrency/thread.rs41
-rw-r--r--src/tools/miri/src/concurrency/weak_memory.rs10
-rw-r--r--src/tools/miri/src/intptrcast.rs6
-rw-r--r--src/tools/miri/src/lib.rs2
-rw-r--r--src/tools/miri/src/machine.rs93
-rw-r--r--src/tools/miri/src/shims/env.rs10
-rw-r--r--src/tools/miri/src/shims/panic.rs11
-rw-r--r--src/tools/miri/src/shims/time.rs16
-rw-r--r--src/tools/miri/src/shims/tls.rs8
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs12
-rw-r--r--src/tools/miri/src/shims/unix/linux/sync.rs52
-rw-r--r--src/tools/miri/src/shims/unix/sync.rs64
-rw-r--r--src/tools/miri/src/stacked_borrows/mod.rs19
-rw-r--r--src/tools/miri/src/tag_gc.rs172
15 files changed, 306 insertions, 222 deletions
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index 2e54ddaaba1..d0fc349f1ac 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -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.
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index 5e6fcbde69a..ec1da4138d4 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -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);
         }
     }
 }
diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs
index 15c6c8e9c0e..9d7a49c0b43 100644
--- a/src/tools/miri/src/concurrency/weak_memory.rs
+++ b/src/tools/miri/src/concurrency/weak_memory.rs
@@ -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);
         }
     }
 }
diff --git a/src/tools/miri/src/intptrcast.rs b/src/tools/miri/src/intptrcast.rs
index b9e5def8fa7..9722b7643e4 100644
--- a/src/tools/miri/src/intptrcast.rs
+++ b/src/tools/miri/src/intptrcast.rs
@@ -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 {
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 245bdc51a8a..463feb4dcc8 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -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.
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 523aad22aa5..20ae908fce8 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -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;
-
-        if let Some(stacked_borrows) = stacked_borrows {
-            stacked_borrows.borrow().visit_machine_values(visit);
-        }
+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(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);
         }
     }
 }
diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs
index d922014c383..076d3878de2 100644
--- a/src/tools/miri/src/shims/env.rs
+++ b/src/tools/miri/src/shims/env.rs
@@ -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);
         }
     }
 }
diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs
index 0d681d3e09b..698e025961d 100644
--- a/src/tools/miri/src/shims/panic.rs
+++ b/src/tools/miri/src/shims/panic.rs
@@ -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);
     }
 }
 
diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs
index 05eff3dfd59..9f04034e1a1 100644
--- a/src/tools/miri/src/shims/time.rs
+++ b/src/tools/miri/src/shims/time.rs
@@ -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(())
     }
 }
diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs
index 568eb6fa910..430dedbc170 100644
--- a/src/tools/miri/src/shims/tls.rs
+++ b/src/tools/miri/src/shims/tls.rs
@@ -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);
         }
     }
 }
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 5024b2ab45f..9713cd9265e 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -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);
         }
     }
 }
diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs
index bbfb1c34db7..5762ee27b84 100644
--- a/src/tools/miri/src/shims/unix/linux/sync.rs
+++ b/src/tools/miri/src/shims/unix/linux/sync.rs
@@ -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(())
-    }
-}
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index 72b71ada8e0..5aafe76ade1 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -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()]);
diff --git a/src/tools/miri/src/stacked_borrows/mod.rs b/src/tools/miri/src/stacked_borrows/mod.rs
index b40358e2c15..829703d6562 100644
--- a/src/tools/miri/src/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/stacked_borrows/mod.rs
@@ -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);
         }
     }
 }
diff --git a/src/tools/miri/src/tag_gc.rs b/src/tools/miri/src/tag_gc.rs
index e2273f055dd..5aa653632f3 100644
--- a/src/tools/miri/src/tag_gc.rs
+++ b/src/tools/miri/src/tag_gc.rs
@@ -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>);
+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);
+        }
+    }
 }
 
-pub struct ProvenanceVisitor {
-    tags: FxHashSet<SbTag>,
+impl<T: VisitTags> VisitTags for std::cell::RefCell<T> {
+    fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+        self.borrow().visit_tags(visit)
+    }
 }
 
-impl ProvenanceVisitor {
-    pub fn visit<V>(&mut self, v: V)
-    where
-        V: MachineValue,
-    {
-        v.visit_provenance(&mut self.tags);
+impl VisitTags for SbTag {
+    fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+        visit(*self)
     }
 }
 
-impl<T: MachineValue> MachineValue for &T {
-    fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
-        (**self).visit_provenance(tags);
+impl VisitTags for Provenance {
+    fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+        if let Provenance::Concrete { sb, .. } = self {
+            visit(*sb);
+        }
     }
 }
 
-impl MachineValue for Operand<Provenance> {
-    fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
+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);
-            }
-            Operand::Immediate(Immediate::ScalarPair(s1, s2)) => {
-                s1.visit_provenance(tags);
-                s2.visit_provenance(tags);
-            }
-            Operand::Immediate(Immediate::Uninit) => {}
-            Operand::Indirect(p) => {
-                p.visit_provenance(tags);
-            }
+            Scalar::Ptr(ptr, _) => ptr.visit_tags(visit),
+            Scalar::Int(_) => (),
         }
     }
 }
 
-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 VisitTags for Immediate<Provenance> {
+    fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+        match self {
+            Immediate::Scalar(s) => {
+                s.visit_tags(visit);
+            }
+            Immediate::ScalarPair(s1, s2) => {
+                s1.visit_tags(visit);
+                s2.visit_tags(visit);
             }
+            Immediate::Uninit => {}
         }
     }
 }
 
-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 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 MachineValue for SbTag {
-    fn visit_provenance(&self, tags: &mut FxHashSet<SbTag>) {
-        tags.insert(*self);
+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 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 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 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 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::Indirect(p) => {
+                p.visit_tags(visit);
+            }
         }
     }
 }
 
-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<'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();
-
+impl VisitTags for crate::MiriInterpCx<'_, '_> {
+    fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
         // Memory.
-        this.memory.alloc_map().iter(|it| {
+        self.memory.alloc_map().iter(|it| {
             for (_id, (_kind, alloc)) in it {
-                alloc.visit_machine_values(acc);
+                alloc.visit_tags(visit);
             }
         });
 
         // And all the other machine values.
-        this.machine.visit_machine_values(acc);
+        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.
@@ -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(())
     }