Expand Miri's BorTag GC to a Provenance GC
This commit is contained in:
parent
82b804c744
commit
0d0a41789f
27 changed files with 395 additions and 296 deletions
|
@ -107,6 +107,14 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> {
|
||||||
FxIndexMap::contains_key(self, k)
|
FxIndexMap::contains_key(self, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn contains_key_ref<Q: ?Sized + Hash + Eq>(&self, k: &Q) -> bool
|
||||||
|
where
|
||||||
|
K: Borrow<Q>,
|
||||||
|
{
|
||||||
|
FxIndexMap::contains_key(self, k)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn insert(&mut self, k: K, v: V) -> Option<V> {
|
fn insert(&mut self, k: K, v: V) -> Option<V> {
|
||||||
FxIndexMap::insert(self, k, v)
|
FxIndexMap::insert(self, k, v)
|
||||||
|
|
|
@ -49,6 +49,14 @@ pub trait AllocMap<K: Hash + Eq, V> {
|
||||||
where
|
where
|
||||||
K: Borrow<Q>;
|
K: Borrow<Q>;
|
||||||
|
|
||||||
|
/// Callers should prefer [`AllocMap::contains_key`] when it is possible to call because it may
|
||||||
|
/// be more efficient. This function exists for callers that only have a shared reference
|
||||||
|
/// (which might make it slightly less efficient than `contains_key`, e.g. if
|
||||||
|
/// the data is stored inside a `RefCell`).
|
||||||
|
fn contains_key_ref<Q: ?Sized + Hash + Eq>(&self, k: &Q) -> bool
|
||||||
|
where
|
||||||
|
K: Borrow<Q>;
|
||||||
|
|
||||||
/// Inserts a new entry into the map.
|
/// Inserts a new entry into the map.
|
||||||
fn insert(&mut self, k: K, v: V) -> Option<V>;
|
fn insert(&mut self, k: K, v: V) -> Option<V>;
|
||||||
|
|
||||||
|
|
|
@ -692,6 +692,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
Ok((&mut alloc.extra, machine))
|
Ok((&mut alloc.extra, machine))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check whether an allocation is live. This is faster than calling
|
||||||
|
/// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is
|
||||||
|
/// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics.
|
||||||
|
pub fn is_alloc_live(&self, id: AllocId) -> bool {
|
||||||
|
self.tcx.try_get_global_alloc(id).is_some()
|
||||||
|
|| self.memory.alloc_map.contains_key_ref(&id)
|
||||||
|
|| self.memory.extra_fn_ptr_map.contains_key(&id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Obtain the size and alignment of an allocation, even if that allocation has
|
/// Obtain the size and alignment of an allocation, even if that allocation has
|
||||||
/// been deallocated.
|
/// been deallocated.
|
||||||
pub fn get_alloc_info(&self, id: AllocId) -> (Size, Align, AllocKind) {
|
pub fn get_alloc_info(&self, id: AllocId) -> (Size, Align, AllocKind) {
|
||||||
|
|
|
@ -525,13 +525,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
self.alloc_map.lock().reserve()
|
self.alloc_map.lock().reserve()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Miri's provenance GC needs to see all live allocations. The interpreter manages most
|
|
||||||
/// allocations but some are managed by [`TyCtxt`] and without this method the interpreter
|
|
||||||
/// doesn't know their [`AllocId`]s are in use.
|
|
||||||
pub fn iter_allocs<F: FnMut(AllocId)>(self, func: F) {
|
|
||||||
self.alloc_map.lock().alloc_map.keys().copied().for_each(func)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reserves a new ID *if* this allocation has not been dedup-reserved before.
|
/// Reserves a new ID *if* this allocation has not been dedup-reserved before.
|
||||||
/// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we
|
/// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we
|
||||||
/// don't want to dedup IDs for "real" memory!
|
/// don't want to dedup IDs for "real" memory!
|
||||||
|
|
|
@ -75,8 +75,8 @@ pub struct FrameState {
|
||||||
protected_tags: SmallVec<[(AllocId, BorTag); 2]>,
|
protected_tags: SmallVec<[(AllocId, BorTag); 2]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for FrameState {
|
impl VisitProvenance for FrameState {
|
||||||
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||||
// `protected_tags` are already recorded by `GlobalStateInner`.
|
// `protected_tags` are already recorded by `GlobalStateInner`.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,10 +110,10 @@ pub struct GlobalStateInner {
|
||||||
unique_is_unique: bool,
|
unique_is_unique: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for GlobalStateInner {
|
impl VisitProvenance for GlobalStateInner {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
for &tag in self.protected_tags.keys() {
|
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
|
// The only other candidate is base_ptr_tags, and that does not need visiting since we don't ever
|
||||||
// GC the bottommost/root tag.
|
// GC the bottommost/root tag.
|
||||||
|
@ -236,6 +236,10 @@ impl GlobalStateInner {
|
||||||
tag
|
tag
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) {
|
||||||
|
self.base_ptr_tags.retain(|id, _| allocs.is_live(*id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Which borrow tracking method to use
|
/// Which borrow tracking method to use
|
||||||
|
@ -503,11 +507,11 @@ impl AllocState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for AllocState {
|
impl VisitProvenance for AllocState {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
match self {
|
match self {
|
||||||
AllocState::StackedBorrows(sb) => sb.visit_tags(visit),
|
AllocState::StackedBorrows(sb) => sb.visit_provenance(visit),
|
||||||
AllocState::TreeBorrows(tb) => tb.visit_tags(visit),
|
AllocState::TreeBorrows(tb) => tb.visit_provenance(visit),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -462,10 +462,10 @@ impl Stacks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for Stacks {
|
impl VisitProvenance for Stacks {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
for tag in self.exposed_tags.iter().copied() {
|
for tag in self.exposed_tags.iter().copied() {
|
||||||
visit(tag);
|
visit(None, Some(tag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -742,11 +742,11 @@ impl Tree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for Tree {
|
impl VisitProvenance for Tree {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
// To ensure that the root never gets removed, we visit it
|
// To ensure that the root never gets removed, we visit it
|
||||||
// (the `root` node of `Tree` is not an `Option<_>`)
|
// (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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -790,9 +790,9 @@ pub struct VClockAlloc {
|
||||||
alloc_ranges: RefCell<RangeMap<MemoryCellClocks>>,
|
alloc_ranges: RefCell<RangeMap<MemoryCellClocks>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for VClockAlloc {
|
impl VisitProvenance for VClockAlloc {
|
||||||
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||||
// No tags here.
|
// No tags or allocIds here.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1404,8 +1404,8 @@ pub struct GlobalState {
|
||||||
pub track_outdated_loads: bool,
|
pub track_outdated_loads: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for GlobalState {
|
impl VisitProvenance for GlobalState {
|
||||||
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||||
// We don't have any tags.
|
// We don't have any tags.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,10 +45,10 @@ pub(super) struct InitOnce<'mir, 'tcx> {
|
||||||
data_race: VClock,
|
data_race: VClock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> {
|
impl<'mir, 'tcx> VisitProvenance for InitOnce<'mir, 'tcx> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
for waiter in self.waiters.iter() {
|
for waiter in self.waiters.iter() {
|
||||||
waiter.callback.visit_tags(visit);
|
waiter.callback.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,10 +181,10 @@ pub(crate) struct SynchronizationState<'mir, 'tcx> {
|
||||||
pub(super) init_onces: IndexVec<InitOnceId, InitOnce<'mir, 'tcx>>,
|
pub(super) init_onces: IndexVec<InitOnceId, InitOnce<'mir, 'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> {
|
impl<'mir, 'tcx> VisitProvenance for SynchronizationState<'mir, 'tcx> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
for init_once in self.init_onces.iter() {
|
for init_once in self.init_onces.iter() {
|
||||||
init_once.visit_tags(visit);
|
init_once.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub enum TlsAllocAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for callbacks that can be executed when some event happens, such as after a timeout.
|
/// 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>;
|
fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,8 +228,8 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for Thread<'_, '_> {
|
impl VisitProvenance for Thread<'_, '_> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let Thread {
|
let Thread {
|
||||||
panic_payloads: panic_payload,
|
panic_payloads: panic_payload,
|
||||||
last_error,
|
last_error,
|
||||||
|
@ -242,17 +242,17 @@ impl VisitTags for Thread<'_, '_> {
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
for payload in panic_payload {
|
for payload in panic_payload {
|
||||||
payload.visit_tags(visit);
|
payload.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
last_error.visit_tags(visit);
|
last_error.visit_provenance(visit);
|
||||||
for frame in stack {
|
for frame in stack {
|
||||||
frame.visit_tags(visit)
|
frame.visit_provenance(visit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for Frame<'_, '_, Provenance, FrameExtra<'_>> {
|
impl VisitProvenance for Frame<'_, '_, Provenance, FrameExtra<'_>> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let Frame {
|
let Frame {
|
||||||
return_place,
|
return_place,
|
||||||
locals,
|
locals,
|
||||||
|
@ -266,22 +266,22 @@ impl VisitTags for Frame<'_, '_, Provenance, FrameExtra<'_>> {
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Return place.
|
// Return place.
|
||||||
return_place.visit_tags(visit);
|
return_place.visit_provenance(visit);
|
||||||
// Locals.
|
// Locals.
|
||||||
for local in locals.iter() {
|
for local in locals.iter() {
|
||||||
match local.as_mplace_or_imm() {
|
match local.as_mplace_or_imm() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(Either::Left((ptr, meta))) => {
|
Some(Either::Left((ptr, meta))) => {
|
||||||
ptr.visit_tags(visit);
|
ptr.visit_provenance(visit);
|
||||||
meta.visit_tags(visit);
|
meta.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
Some(Either::Right(imm)) => {
|
Some(Either::Right(imm)) => {
|
||||||
imm.visit_tags(visit);
|
imm.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extra.visit_tags(visit);
|
extra.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,8 +341,8 @@ pub struct ThreadManager<'mir, 'tcx> {
|
||||||
timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>,
|
timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for ThreadManager<'_, '_> {
|
impl VisitProvenance for ThreadManager<'_, '_> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let ThreadManager {
|
let ThreadManager {
|
||||||
threads,
|
threads,
|
||||||
thread_local_alloc_ids,
|
thread_local_alloc_ids,
|
||||||
|
@ -353,15 +353,15 @@ impl VisitTags for ThreadManager<'_, '_> {
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
for thread in threads {
|
for thread in threads {
|
||||||
thread.visit_tags(visit);
|
thread.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
for ptr in thread_local_alloc_ids.borrow().values() {
|
for ptr in thread_local_alloc_ids.borrow().values() {
|
||||||
ptr.visit_tags(visit);
|
ptr.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
for callback in timeout_callbacks.values() {
|
for callback in timeout_callbacks.values() {
|
||||||
callback.callback.visit_tags(visit);
|
callback.callback.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
sync.visit_tags(visit);
|
sync.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,15 +108,15 @@ pub struct StoreBufferAlloc {
|
||||||
store_buffers: RefCell<RangeObjectMap<StoreBuffer>>,
|
store_buffers: RefCell<RangeObjectMap<StoreBuffer>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for StoreBufferAlloc {
|
impl VisitProvenance for StoreBufferAlloc {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let Self { store_buffers } = self;
|
let Self { store_buffers } = self;
|
||||||
for val in store_buffers
|
for val in store_buffers
|
||||||
.borrow()
|
.borrow()
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|buf| buf.buffer.iter().map(|element| &element.val))
|
.flat_map(|buf| buf.buffer.iter().map(|element| &element.val))
|
||||||
{
|
{
|
||||||
val.visit_tags(visit);
|
val.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,9 +46,21 @@ pub struct GlobalStateInner {
|
||||||
provenance_mode: ProvenanceMode,
|
provenance_mode: ProvenanceMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for GlobalStateInner {
|
impl VisitProvenance for GlobalStateInner {
|
||||||
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||||
// Nothing to visit here.
|
let GlobalStateInner {
|
||||||
|
int_to_ptr_map: _,
|
||||||
|
base_addr: _,
|
||||||
|
exposed: _,
|
||||||
|
next_base_addr: _,
|
||||||
|
provenance_mode: _,
|
||||||
|
} = self;
|
||||||
|
// Though base_addr, int_to_ptr_map, and exposed contain AllocIds, we do not want to visit them.
|
||||||
|
// int_to_ptr_map and exposed must contain only live allocations, and those
|
||||||
|
// are never garbage collected.
|
||||||
|
// base_addr is only relevant if we have a pointer to an AllocId and need to look up its
|
||||||
|
// base address; so if an AllocId is not reachable from somewhere else we can remove it
|
||||||
|
// here.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +74,12 @@ impl GlobalStateInner {
|
||||||
provenance_mode: config.provenance_mode,
|
provenance_mode: config.provenance_mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) {
|
||||||
|
// `exposed` and `int_to_ptr_map` are cleared immediately when an allocation
|
||||||
|
// is freed, so `base_addr` is the only one we have to clean up based on the GC.
|
||||||
|
self.base_addr.retain(|id, _| allocs.is_live(*id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
|
/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
|
||||||
|
@ -107,7 +125,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
// We only use this provenance if it has been exposed.
|
// We only use this provenance if it has been exposed.
|
||||||
if global_state.exposed.contains(&alloc_id) {
|
if global_state.exposed.contains(&alloc_id) {
|
||||||
// This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed.
|
// This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed.
|
||||||
debug_assert!(!matches!(ecx.get_alloc_info(alloc_id).2, AllocKind::Dead));
|
debug_assert!(ecx.is_alloc_live(alloc_id));
|
||||||
Some(alloc_id)
|
Some(alloc_id)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -181,13 +199,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
let ecx = self.eval_context_mut();
|
let ecx = self.eval_context_mut();
|
||||||
let global_state = ecx.machine.intptrcast.get_mut();
|
let global_state = ecx.machine.intptrcast.get_mut();
|
||||||
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
|
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
|
||||||
if global_state.provenance_mode != ProvenanceMode::Strict {
|
if global_state.provenance_mode == ProvenanceMode::Strict {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// Exposing a dead alloc is a no-op, because it's not possible to get a dead allocation
|
||||||
|
// via int2ptr.
|
||||||
|
if !ecx.is_alloc_live(alloc_id) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
trace!("Exposing allocation id {alloc_id:?}");
|
trace!("Exposing allocation id {alloc_id:?}");
|
||||||
|
let global_state = ecx.machine.intptrcast.get_mut();
|
||||||
global_state.exposed.insert(alloc_id);
|
global_state.exposed.insert(alloc_id);
|
||||||
if ecx.machine.borrow_tracker.is_some() {
|
if ecx.machine.borrow_tracker.is_some() {
|
||||||
ecx.expose_tag(alloc_id, tag)?;
|
ecx.expose_tag(alloc_id, tag)?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,9 +77,9 @@ mod intptrcast;
|
||||||
mod machine;
|
mod machine;
|
||||||
mod mono_hash_map;
|
mod mono_hash_map;
|
||||||
mod operator;
|
mod operator;
|
||||||
|
mod provenance_gc;
|
||||||
mod range_map;
|
mod range_map;
|
||||||
mod shims;
|
mod shims;
|
||||||
mod tag_gc;
|
|
||||||
|
|
||||||
// Establish a "crate-wide prelude": we often import `crate::*`.
|
// Establish a "crate-wide prelude": we often import `crate::*`.
|
||||||
|
|
||||||
|
@ -125,8 +125,8 @@ pub use crate::machine::{
|
||||||
};
|
};
|
||||||
pub use crate::mono_hash_map::MonoHashMap;
|
pub use crate::mono_hash_map::MonoHashMap;
|
||||||
pub use crate::operator::EvalContextExt as _;
|
pub use crate::operator::EvalContextExt as _;
|
||||||
|
pub use crate::provenance_gc::{EvalContextExt as _, VisitProvenance, VisitWith, LiveAllocs};
|
||||||
pub use crate::range_map::RangeMap;
|
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
|
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
|
||||||
/// set per default, for maximal validation power.
|
/// set per default, for maximal validation power.
|
||||||
|
|
|
@ -77,12 +77,12 @@ impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for FrameExtra<'_> {
|
impl VisitProvenance for FrameExtra<'_> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _ } = self;
|
let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _ } = self;
|
||||||
|
|
||||||
catch_unwind.visit_tags(visit);
|
catch_unwind.visit_provenance(visit);
|
||||||
borrow_tracker.visit_tags(visit);
|
borrow_tracker.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,13 +311,13 @@ pub struct AllocExtra<'tcx> {
|
||||||
pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
|
pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for AllocExtra<'_> {
|
impl VisitProvenance for AllocExtra<'_> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self;
|
let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self;
|
||||||
|
|
||||||
borrow_tracker.visit_tags(visit);
|
borrow_tracker.visit_provenance(visit);
|
||||||
data_race.visit_tags(visit);
|
data_race.visit_provenance(visit);
|
||||||
weak_memory.visit_tags(visit);
|
weak_memory.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -793,8 +793,8 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for MiriMachine<'_, '_> {
|
impl VisitProvenance for MiriMachine<'_, '_> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let MiriMachine {
|
let MiriMachine {
|
||||||
threads,
|
threads,
|
||||||
|
@ -843,20 +843,20 @@ impl VisitTags for MiriMachine<'_, '_> {
|
||||||
allocation_spans: _,
|
allocation_spans: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
threads.visit_tags(visit);
|
threads.visit_provenance(visit);
|
||||||
tls.visit_tags(visit);
|
tls.visit_provenance(visit);
|
||||||
env_vars.visit_tags(visit);
|
env_vars.visit_provenance(visit);
|
||||||
dir_handler.visit_tags(visit);
|
dir_handler.visit_provenance(visit);
|
||||||
file_handler.visit_tags(visit);
|
file_handler.visit_provenance(visit);
|
||||||
data_race.visit_tags(visit);
|
data_race.visit_provenance(visit);
|
||||||
borrow_tracker.visit_tags(visit);
|
borrow_tracker.visit_provenance(visit);
|
||||||
intptrcast.visit_tags(visit);
|
intptrcast.visit_provenance(visit);
|
||||||
main_fn_ret_place.visit_tags(visit);
|
main_fn_ret_place.visit_provenance(visit);
|
||||||
argc.visit_tags(visit);
|
argc.visit_provenance(visit);
|
||||||
argv.visit_tags(visit);
|
argv.visit_provenance(visit);
|
||||||
cmd_line.visit_tags(visit);
|
cmd_line.visit_provenance(visit);
|
||||||
for ptr in extern_statics.values() {
|
for ptr in extern_statics.values() {
|
||||||
ptr.visit_tags(visit);
|
ptr.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1380,7 +1380,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
|
||||||
// where it mistakenly removes an important tag become visible.
|
// where it mistakenly removes an important tag become visible.
|
||||||
if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
|
if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
|
||||||
ecx.machine.since_gc = 0;
|
ecx.machine.since_gc = 0;
|
||||||
ecx.garbage_collect_tags()?;
|
ecx.run_provenance_gc();
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are our preemption points.
|
// These are our preemption points.
|
||||||
|
|
|
@ -46,6 +46,14 @@ impl<K: Hash + Eq, V> AllocMap<K, V> for MonoHashMap<K, V> {
|
||||||
self.0.get_mut().contains_key(k)
|
self.0.get_mut().contains_key(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn contains_key_ref<Q: ?Sized + Hash + Eq>(&self, k: &Q) -> bool
|
||||||
|
where
|
||||||
|
K: Borrow<Q>,
|
||||||
|
{
|
||||||
|
self.0.borrow().contains_key(k)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn insert(&mut self, k: K, v: V) -> Option<V> {
|
fn insert(&mut self, k: K, v: V) -> Option<V> {
|
||||||
self.0.get_mut().insert(k, Box::new(v)).map(|x| *x)
|
self.0.get_mut().insert(k, Box::new(v)).map(|x| *x)
|
||||||
|
|
209
src/tools/miri/src/provenance_gc.rs
Normal file
209
src/tools/miri/src/provenance_gc.rs
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
use either::Either;
|
||||||
|
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub type VisitWith<'a> = dyn FnMut(Option<AllocId>, Option<BorTag>) + 'a;
|
||||||
|
|
||||||
|
pub trait VisitProvenance {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VisitProvenance> VisitProvenance for Option<T> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
if let Some(x) = self {
|
||||||
|
x.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VisitProvenance> VisitProvenance for std::cell::RefCell<T> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
self.borrow().visit_provenance(visit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for BorTag {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
visit(None, Some(*self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for AllocId {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
visit(Some(*self), None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for Provenance {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
if let Provenance::Concrete { alloc_id, tag, .. } = self {
|
||||||
|
visit(Some(*alloc_id), Some(*tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for Pointer<Provenance> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
let (prov, _offset) = self.into_parts();
|
||||||
|
prov.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for Pointer<Option<Provenance>> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
let (prov, _offset) = self.into_parts();
|
||||||
|
prov.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for Scalar<Provenance> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
match self {
|
||||||
|
Scalar::Ptr(ptr, _) => ptr.visit_provenance(visit),
|
||||||
|
Scalar::Int(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for Immediate<Provenance> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
match self {
|
||||||
|
Immediate::Scalar(s) => {
|
||||||
|
s.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
Immediate::ScalarPair(s1, s2) => {
|
||||||
|
s1.visit_provenance(visit);
|
||||||
|
s2.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
Immediate::Uninit => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for MemPlaceMeta<Provenance> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
match self {
|
||||||
|
MemPlaceMeta::Meta(m) => m.visit_provenance(visit),
|
||||||
|
MemPlaceMeta::None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for ImmTy<'_, Provenance> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
(**self).visit_provenance(visit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for MPlaceTy<'_, Provenance> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
self.ptr().visit_provenance(visit);
|
||||||
|
self.meta().visit_provenance(visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for PlaceTy<'_, Provenance> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
match self.as_mplace_or_local() {
|
||||||
|
Either::Left(mplace) => mplace.visit_provenance(visit),
|
||||||
|
Either::Right(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for OpTy<'_, Provenance> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
match self.as_mplace_or_imm() {
|
||||||
|
Either::Left(mplace) => mplace.visit_provenance(visit),
|
||||||
|
Either::Right(imm) => imm.visit_provenance(visit),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for Allocation<Provenance, AllocExtra<'_>> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
for prov in self.provenance().provenances() {
|
||||||
|
prov.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.extra.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitProvenance for crate::MiriInterpCx<'_, '_> {
|
||||||
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
|
// Visit the contents of the allocations and the IDs themselves, to account for all
|
||||||
|
// live allocation IDs and all provenance in the allocation bytes, even if they are leaked.
|
||||||
|
// We do *not* visit all the `AllocId` of the live allocations; we tried that and adding
|
||||||
|
// them all to the live set is too expensive. Instead we later do liveness check by
|
||||||
|
// checking both "is this alloc id live" and "is it mentioned anywhere else in
|
||||||
|
// the interpreter state".
|
||||||
|
self.memory.alloc_map().iter(|it| {
|
||||||
|
for (_id, (_kind, alloc)) in it {
|
||||||
|
alloc.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// And all the other machine values.
|
||||||
|
self.machine.visit_provenance(visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LiveAllocs<'a, 'mir, 'tcx> {
|
||||||
|
collected: FxHashSet<AllocId>,
|
||||||
|
ecx: &'a MiriInterpCx<'mir, 'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LiveAllocs<'_, '_, '_> {
|
||||||
|
pub fn is_live(&self, id: AllocId) -> bool {
|
||||||
|
self.collected.contains(&id) ||
|
||||||
|
self.ecx.is_alloc_live(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
// We collect all tags from various parts of the interpreter, but also
|
||||||
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
|
let mut tags = FxHashSet::default();
|
||||||
|
let mut alloc_ids = FxHashSet::default();
|
||||||
|
this.visit_provenance(&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<BorTag>) {
|
||||||
|
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<AllocId>) {
|
||||||
|
let this = self.eval_context_ref();
|
||||||
|
let allocs = LiveAllocs {
|
||||||
|
ecx: this,
|
||||||
|
collected: allocs,
|
||||||
|
};
|
||||||
|
this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
|
||||||
|
this.machine.intptrcast.borrow_mut().remove_unreachable_allocs(&allocs);
|
||||||
|
if let Some(borrow_tracker) = &this.machine.borrow_tracker {
|
||||||
|
borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,13 +37,13 @@ pub struct EnvVars<'tcx> {
|
||||||
pub(crate) environ: Option<MPlaceTy<'tcx, Provenance>>,
|
pub(crate) environ: Option<MPlaceTy<'tcx, Provenance>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for EnvVars<'_> {
|
impl VisitProvenance for EnvVars<'_> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let EnvVars { map, environ } = self;
|
let EnvVars { map, environ } = self;
|
||||||
|
|
||||||
environ.visit_tags(visit);
|
environ.visit_provenance(visit);
|
||||||
for ptr in map.values() {
|
for ptr in map.values() {
|
||||||
ptr.visit_tags(visit);
|
ptr.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -459,6 +459,10 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
// shim, add it to the corresponding submodule.
|
// shim, add it to the corresponding submodule.
|
||||||
match link_name.as_str() {
|
match link_name.as_str() {
|
||||||
// Miri-specific extern functions
|
// 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" => {
|
"miri_get_alloc_id" => {
|
||||||
let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
|
let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
|
|
|
@ -35,12 +35,12 @@ pub struct CatchUnwindData<'tcx> {
|
||||||
ret: mir::BasicBlock,
|
ret: mir::BasicBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for CatchUnwindData<'_> {
|
impl VisitProvenance for CatchUnwindData<'_> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let CatchUnwindData { catch_fn, data, dest, ret: _ } = self;
|
let CatchUnwindData { catch_fn, data, dest, ret: _ } = self;
|
||||||
catch_fn.visit_tags(visit);
|
catch_fn.visit_provenance(visit);
|
||||||
data.visit_tags(visit);
|
data.visit_provenance(visit);
|
||||||
dest.visit_tags(visit);
|
dest.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -274,8 +274,8 @@ struct UnblockCallback {
|
||||||
thread_to_unblock: ThreadId,
|
thread_to_unblock: ThreadId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for UnblockCallback {
|
impl VisitProvenance for UnblockCallback {
|
||||||
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {}
|
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback {
|
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback {
|
||||||
|
|
|
@ -207,15 +207,15 @@ impl<'tcx> TlsData<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for TlsData<'_> {
|
impl VisitProvenance for TlsData<'_> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let TlsData { keys, macos_thread_dtors, next_key: _ } = self;
|
let TlsData { keys, macos_thread_dtors, next_key: _ } = self;
|
||||||
|
|
||||||
for scalar in keys.values().flat_map(|v| v.data.values()) {
|
for scalar in keys.values().flat_map(|v| v.data.values()) {
|
||||||
scalar.visit_tags(visit);
|
scalar.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
for (_, scalar) in macos_thread_dtors.values() {
|
for (_, scalar) in macos_thread_dtors.values() {
|
||||||
scalar.visit_tags(visit);
|
scalar.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,8 +288,8 @@ pub struct FileHandler {
|
||||||
pub handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
|
pub handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for FileHandler {
|
impl VisitProvenance for FileHandler {
|
||||||
fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||||
// All our FileDescriptor do not have any tags.
|
// All our FileDescriptor do not have any tags.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -490,12 +490,12 @@ impl Default for DirHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for DirHandler {
|
impl VisitProvenance for DirHandler {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let DirHandler { streams, next_id: _ } = self;
|
let DirHandler { streams, next_id: _ } = self;
|
||||||
|
|
||||||
for dir in streams.values() {
|
for dir in streams.values() {
|
||||||
dir.entry.visit_tags(visit);
|
dir.entry.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,10 +182,10 @@ pub fn futex<'tcx>(
|
||||||
dest: PlaceTy<'tcx, Provenance>,
|
dest: PlaceTy<'tcx, Provenance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> VisitTags for Callback<'tcx> {
|
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let Callback { thread: _, addr_usize: _, dest } = self;
|
let Callback { thread: _, addr_usize: _, dest } = self;
|
||||||
dest.visit_tags(visit);
|
dest.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -886,10 +886,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
dest: PlaceTy<'tcx, Provenance>,
|
dest: PlaceTy<'tcx, Provenance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> VisitTags for Callback<'tcx> {
|
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let Callback { active_thread: _, mutex_id: _, id: _, dest } = self;
|
let Callback { active_thread: _, mutex_id: _, id: _, dest } = self;
|
||||||
dest.visit_tags(visit);
|
dest.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,10 +204,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
pending_place: PlaceTy<'tcx, Provenance>,
|
pending_place: PlaceTy<'tcx, Provenance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> VisitTags for Callback<'tcx> {
|
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let Callback { init_once_id: _, pending_place } = self;
|
let Callback { init_once_id: _, pending_place } = self;
|
||||||
pending_place.visit_tags(visit);
|
pending_place.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,10 +337,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
dest: PlaceTy<'tcx, Provenance>,
|
dest: PlaceTy<'tcx, Provenance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> VisitTags for Callback<'tcx> {
|
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let Callback { thread: _, addr: _, dest } = self;
|
let Callback { thread: _, addr: _, dest } = self;
|
||||||
dest.visit_tags(visit);
|
dest.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,10 +441,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
dest: PlaceTy<'tcx, Provenance>,
|
dest: PlaceTy<'tcx, Provenance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> VisitTags for Callback<'tcx> {
|
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||||
let Callback { thread: _, condvar_id: _, lock_id: _, mode: _, dest } = self;
|
let Callback { thread: _, condvar_id: _, lock_id: _, mode: _, dest } = self;
|
||||||
dest.visit_tags(visit);
|
dest.visit_provenance(visit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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<T: VisitTags> VisitTags for Option<T> {
|
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
|
||||||
if let Some(x) = self {
|
|
||||||
x.visit_tags(visit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: VisitTags> VisitTags for std::cell::RefCell<T> {
|
|
||||||
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<Provenance> {
|
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
|
||||||
let (prov, _offset) = self.into_parts();
|
|
||||||
prov.visit_tags(visit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VisitTags for Pointer<Option<Provenance>> {
|
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
|
||||||
let (prov, _offset) = self.into_parts();
|
|
||||||
prov.visit_tags(visit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VisitTags for Scalar<Provenance> {
|
|
||||||
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
|
|
||||||
match self {
|
|
||||||
Scalar::Ptr(ptr, _) => ptr.visit_tags(visit),
|
|
||||||
Scalar::Int(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VisitTags for Immediate<Provenance> {
|
|
||||||
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<Provenance> {
|
|
||||||
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<Provenance, AllocExtra<'_>> {
|
|
||||||
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<BorTag>) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue