Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Prev Previous commit
Next Next commit
Add DFS iterator for Memory
  • Loading branch information
ecstatic-morse committed Dec 9, 2019
commit 840e323965be8c269278f76c1d1fb434df75f89a
53 changes: 53 additions & 0 deletions src/librustc_mir/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -950,3 +950,56 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
}
}
}

/// A depth-first search over the allocation graph.
///
/// This is based on the DFS in `rustc_data_structures`, which we cannot use directly because
/// `AllocId` does not implement `Idx`.
pub struct DepthFirstSearch<'mem, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
memory: &'mem Memory<'mir, 'tcx, M>,
visited: FxHashSet<AllocId>,
stack: Vec<AllocId>,
}

impl<M: Machine<'mir, 'tcx>> DepthFirstSearch<'mem, 'mir, 'tcx, M> {
/// Returns a new DFS iterator that will traverse all allocations reachable from the given
/// `AllocId`s.
///
/// The first node in `roots` will be the first node visited by the DFS.
pub fn with_roots(
memory: &'mem Memory<'mir, 'tcx, M>,
roots: impl IntoIterator<Item = AllocId>,
) -> Self {
let mut stack: Vec<_> = roots.into_iter().collect();
stack.reverse();

DepthFirstSearch {
memory,
visited: stack.iter().copied().collect(),
stack,
}
}
}

impl<M: Machine<'mir, 'tcx>> Iterator for DepthFirstSearch<'mem, 'mir, 'tcx, M> {
type Item = (AllocId, InterpResult<'tcx, &'mem Allocation<M::PointerTag, M::AllocExtra>>);

fn next(&mut self) -> Option<Self::Item> {
let DepthFirstSearch { stack, visited, memory } = self;

let id = stack.pop()?;
let alloc = memory.get_raw(id);

if let Ok(alloc) = alloc {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this where you are ignoring InterpError? This seems reasonable.

We should make sure that we don't catch errors requiring allocation though, similar to this. Maybe create a new helper method at the error type for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean by "errors requiring allocation". Are there certain errors that we should bug on instead of ignoring? Is Ub one of these?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any error type that has a String or other heap types in their fields will allocate when being thrown, and the catching will then destroy the allocated values. If this allocate->throw->catch->destroy happens in a high frequency we're wasting a lot of cycles with all the allocating/deallocating.

So, the idea is to make sure that any error that is caught and acted upon (and not just rethrown) does not allocate

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idea: Should we make the set of errors that Memory can emit a separate type (with appropriate Into impls) so we know the set of errors that Memory methods can emit and can make sure all of them don't allocate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I'll probably wait for someone else to do this? It feels like y'all have a decent idea of what you want here.

let new_pointers = alloc
.relocations()
.values()
.map(|&(_, id)| id)
.filter(|id| visited.insert(*id));

stack.extend(new_pointers);
}

Some((id, alloc))
}
}