Auto merge of #3833 - JoJoDeveloping:tb-fix-stack-overflow, r=RalfJung · rust-lang/rust@8b10bda (original) (raw)
`@@ -10,7 +10,7 @@
`
10
10
`//! and the relative position of the access;
`
11
11
`` //! - idempotency properties asserted in perms.rs
(for optimizations)
``
12
12
``
13
``
`-
use std::fmt;
`
``
13
`+
use std::{fmt, mem};
`
14
14
``
15
15
`use smallvec::SmallVec;
`
16
16
``
`@@ -699,18 +699,24 @@ impl<'tcx> Tree {
`
699
699
`/// Integration with the BorTag garbage collector
`
700
700
`impl Tree {
`
701
701
`pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet) {
`
702
``
`-
let root_is_needed = self.keep_only_needed(self.root, live_tags); // root can't be removed
`
703
``
`-
assert!(root_is_needed);
`
``
702
`+
self.remove_useless_children(self.root, live_tags);
`
704
703
`// Right after the GC runs is a good moment to check if we can
`
705
704
`// merge some adjacent ranges that were made equal by the removal of some
`
706
705
`// tags (this does not necessarily mean that they have identical internal representations,
`
707
706
`` // see the PartialEq
impl for UniValMap
)
``
708
707
`self.rperms.merge_adjacent_thorough();
`
709
708
`}
`
710
709
``
``
710
`+
/// Checks if a node is useless and should be GC'ed.
`
``
711
`+
/// A node is useless if it has no children and also the tag is no longer live.
`
``
712
`+
fn is_useless(&self, idx: UniIndex, live: &FxHashSet) -> bool {
`
``
713
`+
let node = self.nodes.get(idx).unwrap();
`
``
714
`+
node.children.is_empty() && !live.contains(&node.tag)
`
``
715
`+
}
`
``
716
+
711
717
`/// Traverses the entire tree looking for useless tags.
`
712
``
`-
/// Returns true iff the tag it was called on is still live or has live children,
`
713
``
`-
/// and removes from the tree all tags that have no live children.
`
``
718
`+
/// Removes from the tree all useless child nodes of root.
`
``
719
`+
/// It will not delete the root itself.
`
714
720
`///
`
715
721
`/// NOTE: This leaves in the middle of the tree tags that are unreachable but have
`
716
722
`/// reachable children. There is a potential for compacting the tree by reassigning
`
`@@ -721,42 +727,60 @@ impl Tree {
`
721
727
`` /// child: Reserved
. This tree can exist. If we blindly delete parent
and reassign
``
722
728
`` /// child
to be a direct child of root
then Writes to child
are now permitted
``
723
729
`` /// whereas they were not when parent
was still there.
``
724
``
`-
fn keep_only_needed(&mut self, idx: UniIndex, live: &FxHashSet) -> bool {
`
725
``
`-
let node = self.nodes.get(idx).unwrap();
`
726
``
`-
// FIXME: this function does a lot of cloning, a 2-pass approach is possibly
`
727
``
`-
// more efficient. It could consist of
`
728
``
`-
// 1. traverse the Tree, collect all useless tags in a Vec
`
729
``
`-
// 2. traverse the Vec, remove all tags previously selected
`
730
``
`-
// Bench it.
`
731
``
`-
let children: SmallVec<_> = node
`
732
``
`-
.children
`
733
``
`-
.clone()
`
734
``
`-
.into_iter()
`
735
``
`-
.filter(|child| self.keep_only_needed(*child, live))
`
736
``
`-
.collect();
`
737
``
`-
let no_children = children.is_empty();
`
738
``
`-
let node = self.nodes.get_mut(idx).unwrap();
`
739
``
`-
node.children = children;
`
740
``
`-
if !live.contains(&node.tag) && no_children {
`
741
``
`-
// All of the children and this node are unreachable, delete this tag
`
742
``
`-
// from the tree (the children have already been deleted by recursive
`
743
``
`-
// calls).
`
744
``
`-
// Due to the API of UniMap we must absolutely call
`
745
``
`` -
// UniValMap::remove
for the key of this tag on all maps that used it
``
746
``
`` -
// (which are self.nodes
and every range of self.rperms
)
``
747
``
`` -
// before we can safely apply UniValMap::forget
to truly remove
``
748
``
`-
// the tag from the mapping.
`
749
``
`-
let tag = node.tag;
`
750
``
`-
self.nodes.remove(idx);
`
751
``
`-
for (_perms_range, perms) in self.rperms.iter_mut_all() {
`
752
``
`-
perms.remove(idx);
`
``
730
`+
fn remove_useless_children(&mut self, root: UniIndex, live: &FxHashSet) {
`
``
731
`+
// To avoid stack overflows, we roll our own stack.
`
``
732
`+
// Each element in the stack consists of the current tag, and the number of the
`
``
733
`+
// next child to be processed.
`
``
734
+
``
735
`` +
// The other functions are written using the TreeVisitorStack
, but that does not work here
``
``
736
`+
// since we need to 1) do a post-traversal and 2) remove nodes from the tree.
`
``
737
`+
// Since we do a post-traversal (by deleting nodes only after handling all children),
`
``
738
`+
// we also need to be a bit smarter than "pop node, push all children."
`
``
739
`+
let mut stack = vec![(root, 0)];
`
``
740
`+
while let Some((tag, nth_child)) = stack.last_mut() {
`
``
741
`+
let node = self.nodes.get(*tag).unwrap();
`
``
742
`+
if *nth_child < node.children.len() {
`
``
743
`+
// Visit the child by pushing it to the stack.
`
``
744
`` +
// Also increase nth_child
so that when we come back to the tag
node, we
``
``
745
`+
// look at the next child.
`
``
746
`+
let next_child = node.children[*nth_child];
`
``
747
`+
*nth_child += 1;
`
``
748
`+
stack.push((next_child, 0));
`
``
749
`+
continue;
`
``
750
`+
} else {
`
``
751
`` +
// We have processed all children of node
, so now it is time to process node
itself.
``
``
752
`` +
// First, get the current children of node
. To appease the borrow checker,
``
``
753
`+
// we have to temporarily move the list out of the node, and then put the
`
``
754
`+
// list of remaining children back in.
`
``
755
`+
let mut children_of_node =
`
``
756
`+
mem::take(&mut self.nodes.get_mut(*tag).unwrap().children);
`
``
757
`+
// Remove all useless children, and save them for later.
`
``
758
`` +
// The closure needs &self
and the loop below needs &mut self
, so we need to collect
``
``
759
`+
// in to a temporary list.
`
``
760
`+
let to_remove: Vec<_> =
`
``
761
`+
children_of_node.drain_filter(|x| self.is_useless(*x, live)).collect();
`
``
762
`+
// Put back the now-filtered vector.
`
``
763
`+
self.nodes.get_mut(*tag).unwrap().children = children_of_node;
`
``
764
`` +
// Now, all that is left is unregistering the children saved in to_remove
.
``
``
765
`+
for idx in to_remove {
`
``
766
`` +
// Note: In the rest of this comment, "this node" refers to idx
.
``
``
767
`+
// This node has no more children (if there were any, they have already been removed).
`
``
768
`+
// It is also unreachable as determined by the GC, so we can remove it everywhere.
`
``
769
`+
// Due to the API of UniMap we must make sure to call
`
``
770
`` +
// UniValMap::remove
for the key of this node on all maps that used it
``
``
771
`` +
// (which are self.nodes
and every range of self.rperms
)
``
``
772
`` +
// before we can safely apply UniKeyMap::remove
to truly remove
``
``
773
`` +
// this tag from the tag_mapping
.
``
``
774
`+
let node = self.nodes.remove(idx).unwrap();
`
``
775
`+
for (_perms_range, perms) in self.rperms.iter_mut_all() {
`
``
776
`+
perms.remove(idx);
`
``
777
`+
}
`
``
778
`+
self.tag_mapping.remove(&node.tag);
`
``
779
`+
}
`
``
780
`+
// We are done, the parent can continue.
`
``
781
`+
stack.pop();
`
``
782
`+
continue;
`
753
783
`}
`
754
``
`-
self.tag_mapping.remove(&tag);
`
755
``
`-
// The tag has been deleted, inform the caller
`
756
``
`-
false
`
757
``
`-
} else {
`
758
``
`-
// The tag is still live or has live children, it must be kept
`
759
``
`-
true
`
760
784
`}
`
761
785
`}
`
762
786
`}
`