]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/dep_graph/edges.rs
incr.comp.: Cache DepNodes with corresponding query results.
[rust.git] / src / librustc / dep_graph / edges.rs
index a323e44d0d4271537dd56d7c25c88784fdf1a1ce..277b69262c92d2e633968a60f7e28ee526f12a6c 100644 (file)
@@ -8,25 +8,35 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use ich::Fingerprint;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use super::{DepGraphQuery, DepNode};
+use rustc_data_structures::stable_hasher::StableHasher;
+use std::env;
+use std::hash::Hash;
+use std::mem;
+use super::{DepGraphQuery, DepKind, DepNode};
+use super::debug::EdgeFilter;
 
 pub struct DepGraphEdges {
     nodes: Vec<DepNode>,
-    indices: FxHashMap<DepNode, IdIndex>,
-    edges: FxHashSet<(IdIndex, IdIndex)>,
-    open_nodes: Vec<OpenNode>,
+    indices: FxHashMap<DepNode, DepNodeIndex>,
+    edges: FxHashSet<(DepNodeIndex, DepNodeIndex)>,
+    task_stack: Vec<OpenTask>,
+    forbidden_edge: Option<EdgeFilter>,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-struct IdIndex {
+pub struct DepNodeIndex {
     index: u32
 }
 
-impl IdIndex {
-    fn new(v: usize) -> IdIndex {
+impl DepNodeIndex {
+
+    pub const INVALID: DepNodeIndex = DepNodeIndex { index: ::std::u32::MAX };
+
+    fn new(v: usize) -> DepNodeIndex {
         assert!((v & 0xFFFF_FFFF) == v);
-        IdIndex { index: v as u32 }
+        DepNodeIndex { index: v as u32 }
     }
 
     fn index(self) -> usize {
@@ -35,67 +45,136 @@ fn index(self) -> usize {
 }
 
 #[derive(Clone, Debug, PartialEq)]
-enum OpenNode {
-    Node(IdIndex),
+enum OpenTask {
+    Regular {
+        node: DepNode,
+        reads: Vec<DepNode>,
+        read_set: FxHashSet<DepNode>,
+    },
+    Anon {
+        reads: Vec<DepNode>,
+        read_set: FxHashSet<DepNode>,
+    },
     Ignore,
 }
 
 impl DepGraphEdges {
     pub fn new() -> DepGraphEdges {
+        let forbidden_edge = if cfg!(debug_assertions) {
+            match env::var("RUST_FORBID_DEP_GRAPH_EDGE") {
+                Ok(s) => {
+                    match EdgeFilter::new(&s) {
+                        Ok(f) => Some(f),
+                        Err(err) => bug!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err),
+                    }
+                }
+                Err(_) => None,
+            }
+        } else {
+            None
+        };
+
         DepGraphEdges {
             nodes: vec![],
             indices: FxHashMap(),
             edges: FxHashSet(),
-            open_nodes: Vec::new()
+            task_stack: Vec::new(),
+            forbidden_edge,
         }
     }
 
-    fn id(&self, index: IdIndex) -> DepNode {
-        self.nodes[index.index()].clone()
-    }
-
-    /// Creates a node for `id` in the graph.
-    fn make_node(&mut self, id: DepNode) -> IdIndex {
-        if let Some(&i) = self.indices.get(&id) {
-            return i;
-        }
-
-        let index = IdIndex::new(self.nodes.len());
-        self.nodes.push(id.clone());
-        self.indices.insert(id, index);
-        index
-    }
-
-    /// Top of the stack of open nodes.
-    fn current_node(&self) -> Option<OpenNode> {
-        self.open_nodes.last().cloned()
+    fn id(&self, index: DepNodeIndex) -> DepNode {
+        self.nodes[index.index()]
     }
 
     pub fn push_ignore(&mut self) {
-        self.open_nodes.push(OpenNode::Ignore);
+        self.task_stack.push(OpenTask::Ignore);
     }
 
     pub fn pop_ignore(&mut self) {
-        let popped_node = self.open_nodes.pop().unwrap();
-        assert_eq!(popped_node, OpenNode::Ignore);
+        let popped_node = self.task_stack.pop().unwrap();
+        debug_assert_eq!(popped_node, OpenTask::Ignore);
     }
 
     pub fn push_task(&mut self, key: DepNode) {
-        let top_node = self.current_node();
+        self.task_stack.push(OpenTask::Regular {
+            node: key,
+            reads: Vec::new(),
+            read_set: FxHashSet(),
+        });
+    }
 
-        let new_node = self.make_node(key);
-        self.open_nodes.push(OpenNode::Node(new_node));
+    pub fn pop_task(&mut self, key: DepNode) -> DepNodeIndex {
+        let popped_node = self.task_stack.pop().unwrap();
 
-        // if we are in the midst of doing task T, then this new task
-        // N is a subtask of T, so add an edge N -> T.
-        if let Some(top_node) = top_node {
-            self.add_edge_from_open_node(top_node, |t| (new_node, t));
+        if let OpenTask::Regular {
+            node,
+            read_set: _,
+            reads
+        } = popped_node {
+            debug_assert_eq!(node, key);
+
+            let target_id = self.get_or_create_node(node);
+
+            for read in reads.into_iter() {
+                let source_id = self.get_or_create_node(read);
+                self.edges.insert((source_id, target_id));
+            }
+
+            target_id
+        } else {
+            bug!("pop_task() - Expected regular task to be popped")
         }
     }
 
-    pub fn pop_task(&mut self, key: DepNode) {
-        let popped_node = self.open_nodes.pop().unwrap();
-        assert_eq!(OpenNode::Node(self.indices[&key]), popped_node);
+    pub fn push_anon_task(&mut self) {
+        self.task_stack.push(OpenTask::Anon {
+            reads: Vec::new(),
+            read_set: FxHashSet(),
+        });
+    }
+
+    pub fn pop_anon_task(&mut self, kind: DepKind) -> DepNodeIndex {
+        let popped_node = self.task_stack.pop().unwrap();
+
+        if let OpenTask::Anon {
+            read_set: _,
+            reads
+        } = popped_node {
+            let mut fingerprint = Fingerprint::zero();
+            let mut hasher = StableHasher::new();
+
+            for read in reads.iter() {
+                mem::discriminant(&read.kind).hash(&mut hasher);
+
+                // Fingerprint::combine() is faster than sending Fingerprint
+                // through the StableHasher (at least as long as StableHasher
+                // is so slow).
+                fingerprint = fingerprint.combine(read.hash);
+            }
+
+            fingerprint = fingerprint.combine(hasher.finish());
+
+            let target_dep_node = DepNode {
+                kind,
+                hash: fingerprint,
+            };
+
+            if let Some(&index) = self.indices.get(&target_dep_node) {
+                return index;
+            }
+
+            let target_id = self.get_or_create_node(target_dep_node);
+
+            for read in reads.into_iter() {
+                let source_id = self.get_or_create_node(read);
+                self.edges.insert((source_id, target_id));
+            }
+
+            target_id
+        } else {
+            bug!("pop_anon_task() - Expected anonymous task to be popped")
+        }
     }
 
     /// Indicates that the current task `C` reads `v` by adding an
@@ -103,58 +182,42 @@ pub fn pop_task(&mut self, key: DepNode) {
     /// effect. Note that *reading* from tracked state is harmless if
     /// you are not in a task; what is bad is *writing* to tracked
     /// state (and leaking data that you read into a tracked task).
-    pub fn read(&mut self, v: DepNode) {
-        if self.current_node().is_some() {
-            let source = self.make_node(v);
-            self.add_edge_from_current_node(|current| (source, current))
-        }
-    }
-
-    /// Indicates that the current task `C` writes `v` by adding an
-    /// edge from `C` to `v`. If there is no current task, panics. If
-    /// you want to suppress this edge, use `ignore`.
-    pub fn write(&mut self, v: DepNode) {
-        let target = self.make_node(v);
-        self.add_edge_from_current_node(|current| (current, target))
-    }
-
-    /// Invoke `add_edge_from_open_node` with the top of the stack, or
-    /// panic if stack is empty.
-    fn add_edge_from_current_node<OP>(&mut self,
-                                      op: OP)
-        where OP: FnOnce(IdIndex) -> (IdIndex, IdIndex)
-    {
-        match self.current_node() {
-            Some(open_node) => self.add_edge_from_open_node(open_node, op),
-            None => bug!("no current node, cannot add edge into dependency graph")
+    pub fn read(&mut self, source: DepNode) {
+        match self.task_stack.last_mut() {
+            Some(&mut OpenTask::Regular {
+                node: target,
+                ref mut reads,
+                ref mut read_set,
+            }) => {
+                if read_set.insert(source) {
+                    reads.push(source);
+
+                    if cfg!(debug_assertions) {
+                        if let Some(ref forbidden_edge) = self.forbidden_edge {
+                            if forbidden_edge.test(&source, &target) {
+                                bug!("forbidden edge {:?} -> {:?} created", source, target)
+                            }
+                        }
+                    }
+                }
+            }
+            Some(&mut OpenTask::Anon {
+                ref mut reads,
+                ref mut read_set,
+            }) => {
+                if read_set.insert(source) {
+                    reads.push(source);
+                }
+            }
+            Some(&mut OpenTask::Ignore) | None => {
+                // ignore
+            }
         }
     }
 
-    /// Adds an edge to or from the `open_node`, assuming `open_node`
-    /// is not `Ignore`. The direction of the edge is determined by
-    /// the closure `op` --- we pass as argument the open node `n`,
-    /// and the closure returns a (source, target) tuple, which should
-    /// include `n` in one spot or another.
-    fn add_edge_from_open_node<OP>(&mut self,
-                                   open_node: OpenNode,
-                                   op: OP)
-        where OP: FnOnce(IdIndex) -> (IdIndex, IdIndex)
-    {
-        let (source, target) = match open_node {
-            OpenNode::Node(n) => op(n),
-            OpenNode::Ignore => { return; }
-        };
-
-        // ignore trivial self edges, which are not very interesting
-        if source == target {
-            return;
-        }
-
-        if self.edges.insert((source, target)) {
-            debug!("adding edge from {:?} to {:?}",
-                   self.id(source),
-                   self.id(target));
-        }
+    pub fn read_index(&mut self, source: DepNodeIndex) {
+        let dep_node = self.nodes[source.index()];
+        self.read(dep_node);
     }
 
     pub fn query(&self) -> DepGraphQuery {
@@ -163,4 +226,30 @@ pub fn query(&self) -> DepGraphQuery {
                                       .collect();
         DepGraphQuery::new(&self.nodes, &edges)
     }
+
+    #[inline]
+    pub fn add_edge(&mut self, source: DepNode, target: DepNode) {
+        let source = self.get_or_create_node(source);
+        let target = self.get_or_create_node(target);
+        self.edges.insert((source, target));
+    }
+
+    pub fn add_node(&mut self, node: DepNode) {
+        self.get_or_create_node(node);
+    }
+
+    #[inline]
+    fn get_or_create_node(&mut self, dep_node: DepNode) -> DepNodeIndex {
+        let DepGraphEdges {
+            ref mut indices,
+            ref mut nodes,
+            ..
+        } = *self;
+
+        *indices.entry(dep_node).or_insert_with(|| {
+            let next_id = nodes.len();
+            nodes.push(dep_node);
+            DepNodeIndex::new(next_id)
+        })
+     }
 }