1010//! and the relative position of the access;
1111//! - idempotency properties asserted in `perms.rs` (for optimizations)
1212
13- use std:: fmt;
13+ use std:: { fmt, mem } ;
1414
1515use smallvec:: SmallVec ;
1616
@@ -699,18 +699,24 @@ impl<'tcx> Tree {
699699/// Integration with the BorTag garbage collector
700700impl Tree {
701701 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) ;
704703 // Right after the GC runs is a good moment to check if we can
705704 // merge some adjacent ranges that were made equal by the removal of some
706705 // tags (this does not necessarily mean that they have identical internal representations,
707706 // see the `PartialEq` impl for `UniValMap`)
708707 self . rperms . merge_adjacent_thorough ( ) ;
709708 }
710709
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+
711717 /// 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 .
714720 ///
715721 /// NOTE: This leaves in the middle of the tree tags that are unreachable but have
716722 /// reachable children. There is a potential for compacting the tree by reassigning
@@ -721,42 +727,60 @@ impl Tree {
721727 /// `child: Reserved`. This tree can exist. If we blindly delete `parent` and reassign
722728 /// `child` to be a direct child of `root` then Writes to `child` are now permitted
723729 /// 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 ;
753783 }
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
760784 }
761785 }
762786}
0 commit comments