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 < BorTag > ) {
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 < BorTag > ) -> 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 < BorTag > ) -> 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 < BorTag > ) {
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
}
0 commit comments