@@ -159,6 +159,8 @@ struct SpantreeBuilder<'a, Node: Idx> {
159159 /// A supernode without a span edge is the root of its component of the
160160 /// spantree. Nodes that aren't supernodes cannot have a spantree edge.
161161 span_edges : IndexVec < Node , Option < SpantreeEdge < Node > > > ,
162+ /// Shared path buffer recycled by all calls to `yank_to_spantree_root`.
163+ yank_buffer : Vec < Node > ,
162164 /// An in-progress counter expression for each node. Each expression is
163165 /// initially empty, and will be filled in as relevant nodes are visited.
164166 counter_exprs : IndexVec < Node , CounterExprVec < Node > > ,
@@ -171,6 +173,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
171173 graph,
172174 is_unvisited : DenseBitSet :: new_filled ( num_nodes) ,
173175 span_edges : IndexVec :: from_fn_n ( |_| None , num_nodes) ,
176+ yank_buffer : vec ! [ ] ,
174177 counter_exprs : IndexVec :: from_fn_n ( |_| SmallVec :: new ( ) , num_nodes) ,
175178 }
176179 }
@@ -192,24 +195,37 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
192195 fn yank_to_spantree_root ( & mut self , this : Node ) {
193196 debug_assert ! ( self . graph. is_supernode( this) ) ;
194197
195- // Temporarily remove this supernode (any any spantree-children) from its
196- // spantree component, by disconnecting the edge to its spantree-parent.
197- let Some ( SpantreeEdge { is_reversed, claiming_node, span_parent } ) =
198- self . span_edges [ this] . take ( )
199- else {
200- // This supernode has no spantree-parent edge, so it is already the
201- // root of its spantree component.
202- return ;
203- } ;
204-
205- // Recursively make our immediate spantree-parent the root of what's
206- // left of its component, so that only one more edge rotation is needed.
207- self . yank_to_spantree_root ( span_parent) ;
208-
209- // Recreate the removed edge, but in the opposite direction.
210- // Now `this` is the root of its spantree component.
211- self . span_edges [ span_parent] =
212- Some ( SpantreeEdge { is_reversed : !is_reversed, claiming_node, span_parent : this } ) ;
198+ // The rotation is done iteratively, by first traversing from `this` to
199+ // its root and storing the path in a buffer, and then traversing the
200+ // path buffer backwards to reverse all the edges.
201+
202+ // Recycle the same path buffer for all calls to this method.
203+ let path_buf = & mut self . yank_buffer ;
204+ path_buf. clear ( ) ;
205+ path_buf. push ( this) ;
206+
207+ // Traverse the spantree until we reach a supernode that has no
208+ // span-parent, which must be the root.
209+ let mut curr = this;
210+ while let & Some ( SpantreeEdge { span_parent, .. } ) = & self . span_edges [ curr] {
211+ path_buf. push ( span_parent) ;
212+ curr = span_parent;
213+ }
214+
215+ // For each spantree edge `a -> b` in the path that was just traversed,
216+ // reverse it to become `a <- b`, while preserving `claiming_node`.
217+ for & [ a, b] in path_buf. array_windows :: < 2 > ( ) . rev ( ) {
218+ let SpantreeEdge { is_reversed, claiming_node, span_parent } = self . span_edges [ a]
219+ . take ( )
220+ . expect ( "all nodes in the path (except the last) have a `span_parent`" ) ;
221+ debug_assert_eq ! ( span_parent, b) ;
222+ debug_assert ! ( self . span_edges[ b] . is_none( ) ) ;
223+ self . span_edges [ b] =
224+ Some ( SpantreeEdge { is_reversed : !is_reversed, claiming_node, span_parent : a } ) ;
225+ }
226+
227+ // The result of the rotation is that `this` is now a spantree root.
228+ debug_assert ! ( self . span_edges[ this] . is_none( ) ) ;
213229 }
214230
215231 /// Must be called exactly once for each node in the balanced-flow graph.
@@ -273,7 +289,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
273289 /// Asserts that all nodes have been visited, and returns the computed
274290 /// counter expressions (made up of physical counters) for each node.
275291 fn finish ( self ) -> IndexVec < Node , CounterExprVec < Node > > {
276- let Self { graph, is_unvisited, span_edges, counter_exprs } = self ;
292+ let Self { graph, is_unvisited, span_edges, yank_buffer : _ , counter_exprs } = self ;
277293 assert ! ( is_unvisited. is_empty( ) , "some nodes were never visited: {is_unvisited:?}" ) ;
278294 debug_assert ! (
279295 span_edges
0 commit comments