Skip to content

Commit 2bb6d3d

Browse files
committed
Auto merge of #43713 - arielb1:legacy-dataflow, r=eddyb
rustc::middle::dataflow - visit the CFG in RPO We used to propagate bits in node-id order, which sometimes caused an excessive number of iterations, especially when macros were present. As everyone knows, visiting the CFG in RPO bounds the number of iterators by 1 plus the depth of the most deeply nested loop (times the height of the lattice, which is 1). I have no idea how this affects borrowck perf in the non-worst-case, so it's probably a good idea to not roll this up so we can see the effects. Fixes #43704. r? @eddyb
2 parents e8f5585 + 4e3a0b6 commit 2bb6d3d

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed

src/librustc/middle/dataflow.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ use std::mem;
2222
use std::usize;
2323
use syntax::ast;
2424
use syntax::print::pprust::PrintState;
25+
26+
use rustc_data_structures::graph::OUTGOING;
27+
2528
use util::nodemap::NodeMap;
2629
use hir;
2730
use hir::intravisit::{self, IdRange};
@@ -523,12 +526,16 @@ impl<'a, 'tcx, O:DataFlowOperator+Clone+'static> DataFlowContext<'a, 'tcx, O> {
523526
changed: true
524527
};
525528

529+
let nodes_po = cfg.graph.nodes_in_postorder(OUTGOING, cfg.entry);
526530
let mut temp = vec![0; words_per_id];
531+
let mut num_passes = 0;
527532
while propcx.changed {
533+
num_passes += 1;
528534
propcx.changed = false;
529535
propcx.reset(&mut temp);
530-
propcx.walk_cfg(cfg, &mut temp);
536+
propcx.walk_cfg(cfg, &nodes_po, &mut temp);
531537
}
538+
debug!("finished in {} iterations", num_passes);
532539
}
533540

534541
debug!("Dataflow result for {}:", self.analysis_name);
@@ -543,12 +550,15 @@ impl<'a, 'tcx, O:DataFlowOperator+Clone+'static> DataFlowContext<'a, 'tcx, O> {
543550
impl<'a, 'b, 'tcx, O:DataFlowOperator> PropagationContext<'a, 'b, 'tcx, O> {
544551
fn walk_cfg(&mut self,
545552
cfg: &cfg::CFG,
553+
nodes_po: &[CFGIndex],
546554
in_out: &mut [usize]) {
547555
debug!("DataFlowContext::walk_cfg(in_out={}) {}",
548556
bits_to_string(in_out), self.dfcx.analysis_name);
549557
assert!(self.dfcx.bits_per_id > 0);
550558

551-
cfg.graph.each_node(|node_index, node| {
559+
// Iterate over nodes in reverse postorder
560+
for &node_index in nodes_po.iter().rev() {
561+
let node = cfg.graph.node(node_index);
552562
debug!("DataFlowContext::walk_cfg idx={:?} id={} begin in_out={}",
553563
node_index, node.data.id(), bits_to_string(in_out));
554564

@@ -563,8 +573,7 @@ impl<'a, 'b, 'tcx, O:DataFlowOperator> PropagationContext<'a, 'b, 'tcx, O> {
563573

564574
// Propagate state on-exit from node into its successors.
565575
self.propagate_bits_into_graph_successors_of(in_out, cfg, node_index);
566-
true // continue to next node
567-
});
576+
}
568577
}
569578

570579
fn reset(&mut self, bits: &mut [usize]) {

src/librustc_data_structures/graph/mod.rs

+36
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,42 @@ impl<N: Debug, E: Debug> Graph<N, E> {
308308
DepthFirstTraversal::with_start_node(self, start, direction)
309309
}
310310

311+
pub fn nodes_in_postorder<'a>(&'a self,
312+
direction: Direction,
313+
entry_node: NodeIndex)
314+
-> Vec<NodeIndex>
315+
{
316+
let mut visited = BitVector::new(self.len_nodes());
317+
let mut stack = vec![];
318+
let mut result = Vec::with_capacity(self.len_nodes());
319+
let mut push_node = |stack: &mut Vec<_>, node: NodeIndex| {
320+
if visited.insert(node.0) {
321+
stack.push((node, self.adjacent_edges(node, direction)));
322+
}
323+
};
324+
325+
for node in Some(entry_node).into_iter()
326+
.chain(self.enumerated_nodes().map(|(node, _)| node))
327+
{
328+
push_node(&mut stack, node);
329+
while let Some((node, mut iter)) = stack.pop() {
330+
if let Some((_, child)) = iter.next() {
331+
let target = child.source_or_target(direction);
332+
// the current node needs more processing, so
333+
// add it back to the stack
334+
stack.push((node, iter));
335+
// and then push the new node
336+
push_node(&mut stack, target);
337+
} else {
338+
result.push(node);
339+
}
340+
}
341+
}
342+
343+
assert_eq!(result.len(), self.len_nodes());
344+
result
345+
}
346+
311347
/// Whether or not a node can be reached from itself.
312348
pub fn is_node_cyclic(&self, starting_node_index: NodeIndex) -> bool {
313349
// This is similar to depth traversal below, but we

src/librustc_data_structures/graph/tests.rs

+43
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,46 @@ fn is_node_cyclic_b() {
175175
let graph = create_graph_with_cycle();
176176
assert!(graph.is_node_cyclic(NodeIndex(1)));
177177
}
178+
179+
#[test]
180+
fn nodes_in_postorder() {
181+
let expected = vec![
182+
("A", vec!["C", "E", "D", "B", "A", "F"]),
183+
("B", vec!["C", "E", "D", "B", "A", "F"]),
184+
("C", vec!["C", "E", "D", "B", "A", "F"]),
185+
("D", vec!["C", "E", "D", "B", "A", "F"]),
186+
("E", vec!["C", "E", "D", "B", "A", "F"]),
187+
("F", vec!["C", "E", "D", "B", "F", "A"])
188+
];
189+
190+
let graph = create_graph();
191+
192+
for ((idx, node), &(node_name, ref expected))
193+
in graph.enumerated_nodes().zip(&expected)
194+
{
195+
assert_eq!(node.data, node_name);
196+
assert_eq!(expected,
197+
&graph.nodes_in_postorder(OUTGOING, idx)
198+
.into_iter().map(|idx| *graph.node_data(idx))
199+
.collect::<Vec<&str>>());
200+
}
201+
202+
let expected = vec![
203+
("A", vec!["D", "C", "B", "A"]),
204+
("B", vec!["D", "C", "B", "A"]),
205+
("C", vec!["B", "D", "C", "A"]),
206+
("D", vec!["C", "B", "D", "A"]),
207+
];
208+
209+
let graph = create_graph_with_cycle();
210+
211+
for ((idx, node), &(node_name, ref expected))
212+
in graph.enumerated_nodes().zip(&expected)
213+
{
214+
assert_eq!(node.data, node_name);
215+
assert_eq!(expected,
216+
&graph.nodes_in_postorder(OUTGOING, idx)
217+
.into_iter().map(|idx| *graph.node_data(idx))
218+
.collect::<Vec<&str>>());
219+
}
220+
}

0 commit comments

Comments
 (0)