about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNicholas Nethercote <nnethercote@mozilla.com>2019-09-17 12:08:24 +1000
committerNicholas Nethercote <nnethercote@mozilla.com>2019-09-17 15:32:29 +1000
commit476e75ded71ad6f573daabe2ca7a1be9fe3ce3cb (patch)
tree94837a6293297f33ef727e4bd263bc4407504aca /src
parent5670d048c0f88af9976b5505c7853b23dd06770d (diff)
downloadrust-476e75ded71ad6f573daabe2ca7a1be9fe3ce3cb.tar.gz
rust-476e75ded71ad6f573daabe2ca7a1be9fe3ce3cb.zip
Move a `Node`'s parent into the descendents list.
`Node` has an optional parent and a list of other descendents. Most of
the time the parent is treated the same as the other descendents --
error-handling is the exception -- and chaining them together for
iteration has a non-trivial cost.

This commit changes the representation. There is now a single list of
descendants, and a boolean flag that indicates if there is a parent (in
which case it is first descendent). This representation encodes the same
information, in a way that is less idiomatic but cheaper to iterate over
for the common case where the parent doesn't need special treatment.

As a result, some benchmark workloads are up to 2% faster.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_data_structures/obligation_forest/graphviz.rs4
-rw-r--r--src/librustc_data_structures/obligation_forest/mod.rs75
2 files changed, 36 insertions, 43 deletions
diff --git a/src/librustc_data_structures/obligation_forest/graphviz.rs b/src/librustc_data_structures/obligation_forest/graphviz.rs
index b2120b182fa..fe0a3cb0500 100644
--- a/src/librustc_data_structures/obligation_forest/graphviz.rs
+++ b/src/librustc_data_structures/obligation_forest/graphviz.rs
@@ -74,9 +74,7 @@ impl<'a, O: ForestObligation + 'a> dot::GraphWalk<'a> for &'a ObligationForest<O
             .flat_map(|i| {
                 let node = &self.nodes[i];
 
-                node.parent.iter()
-                    .chain(node.dependents.iter())
-                    .map(move |p| (p.index(), i))
+                node.dependents.iter().map(move |p| (p.index(), i))
             })
             .collect()
     }
diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs
index 189506bf8ab..0ae4a04eb50 100644
--- a/src/librustc_data_structures/obligation_forest/mod.rs
+++ b/src/librustc_data_structures/obligation_forest/mod.rs
@@ -177,21 +177,17 @@ struct Node<O> {
     obligation: O,
     state: Cell<NodeState>,
 
-    /// The parent of a node - the original obligation of which it is a
-    /// subobligation. Except for error reporting, it is just like any member
-    /// of `dependents`.
-    ///
-    /// Unlike `ObligationForest::nodes`, this uses `NodeIndex` rather than
-    /// `usize` for the index, because keeping the size down is more important
-    /// than the cost of converting to a `usize` for indexing.
-    parent: Option<NodeIndex>,
-
     /// Obligations that depend on this obligation for their completion. They
     /// must all be in a non-pending state.
-    ///
-    /// This uses `NodeIndex` for the same reason as `parent`.
     dependents: Vec<NodeIndex>,
 
+    /// If true, dependents[0] points to a "parent" node, which requires
+    /// special treatment upon error but is otherwise treated the same.
+    /// (It would be more idiomatic to store the parent node in a separate
+    /// `Option<NodeIndex>` field, but that slows down the common case of
+    /// iterating over the parent and other descendants together.)
+    has_parent: bool,
+
     /// Identifier of the obligation tree to which this node belongs.
     obligation_tree_id: ObligationTreeId,
 }
@@ -205,8 +201,13 @@ impl<O> Node<O> {
         Node {
             obligation,
             state: Cell::new(NodeState::Pending),
-            parent,
-            dependents: vec![],
+            dependents:
+                if let Some(parent_index) = parent {
+                    vec![parent_index]
+                } else {
+                    vec![]
+                },
+            has_parent: parent.is_some(),
             obligation_tree_id,
         }
     }
@@ -315,13 +316,11 @@ impl<O: ForestObligation> ObligationForest<O> {
                        obligation, parent, o.get());
                 let node = &mut self.nodes[o.get().index()];
                 if let Some(parent_index) = parent {
-                    // If the node is already in `waiting_cache`, it's already
-                    // been marked with a parent. (It's possible that parent
-                    // has been cleared by `apply_rewrites`, though.) So just
-                    // dump `parent` into `node.dependents`... unless it's
-                    // already in `node.dependents` or `node.parent`.
-                    if !node.dependents.contains(&parent_index) &&
-                       Some(parent_index) != node.parent {
+                    // If the node is already in `waiting_cache`, it has
+                    // already had its chance to be marked with a parent. So if
+                    // it's not already present, just dump `parent` into the
+                    // dependents as a non-parent.
+                    if !node.dependents.contains(&parent_index) {
                         node.dependents.push(parent_index);
                     }
                 }
@@ -523,7 +522,7 @@ impl<O: ForestObligation> ObligationForest<O> {
             NodeState::Success => {
                 node.state.set(NodeState::OnDfsStack);
                 stack.push(i);
-                for index in node.parent.iter().chain(node.dependents.iter()) {
+                for index in node.dependents.iter() {
                     self.find_cycles_from_node(stack, processor, index.index());
                 }
                 stack.pop();
@@ -549,12 +548,15 @@ impl<O: ForestObligation> ObligationForest<O> {
             let node = &self.nodes[i];
             node.state.set(NodeState::Error);
             trace.push(node.obligation.clone());
-            error_stack.extend(node.dependents.iter().map(|index| index.index()));
-
-            // Loop to the parent.
-            match node.parent {
-                Some(parent_index) => i = parent_index.index(),
-                None => break
+            if node.has_parent {
+                // The first dependent is the parent, which is treated
+                // specially.
+                error_stack.extend(node.dependents.iter().skip(1).map(|index| index.index()));
+                i = node.dependents[0].index();
+            } else {
+                // No parent; treat all dependents non-specially.
+                error_stack.extend(node.dependents.iter().map(|index| index.index()));
+                break;
             }
         }
 
@@ -565,9 +567,7 @@ impl<O: ForestObligation> ObligationForest<O> {
                 _ => node.state.set(NodeState::Error),
             }
 
-            error_stack.extend(
-                node.parent.iter().chain(node.dependents.iter()).map(|index| index.index())
-            );
+            error_stack.extend(node.dependents.iter().map(|index| index.index()));
         }
 
         self.scratch.replace(error_stack);
@@ -577,7 +577,7 @@ impl<O: ForestObligation> ObligationForest<O> {
     // This always-inlined function is for the hot call site.
     #[inline(always)]
     fn inlined_mark_neighbors_as_waiting_from(&self, node: &Node<O>) {
-        for dependent in node.parent.iter().chain(node.dependents.iter()) {
+        for dependent in node.dependents.iter() {
             self.mark_as_waiting_from(&self.nodes[dependent.index()]);
         }
     }
@@ -706,20 +706,15 @@ impl<O: ForestObligation> ObligationForest<O> {
         let nodes_len = node_rewrites.len();
 
         for node in &mut self.nodes {
-            if let Some(index) = node.parent {
-                let new_i = node_rewrites[index.index()];
-                if new_i >= nodes_len {
-                    node.parent = None;
-                } else {
-                    node.parent = Some(NodeIndex::new(new_i));
-                }
-            }
-
             let mut i = 0;
             while i < node.dependents.len() {
                 let new_i = node_rewrites[node.dependents[i].index()];
                 if new_i >= nodes_len {
                     node.dependents.swap_remove(i);
+                    if i == 0 && node.has_parent {
+                        // We just removed the parent.
+                        node.has_parent = false;
+                    }
                 } else {
                     node.dependents[i] = NodeIndex::new(new_i);
                     i += 1;