about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVishnunarayan K I <appukuttancr@gmail.com>2020-10-26 19:04:29 +0530
committerVishnunarayan K I <appukuttancr@gmail.com>2020-11-09 22:39:06 +0530
commitea1460773f1be2d0d717f6ab7609727e52ffa226 (patch)
tree936946c629de3e6543636cd78788782d1a474044
parent25f6938da459a57b43bdf16ed6bdad3225b2a3ce (diff)
downloadrust-ea1460773f1be2d0d717f6ab7609727e52ffa226.tar.gz
rust-ea1460773f1be2d0d717f6ab7609727e52ffa226.zip
make MIR graphviz generation use gsgdt
gsgdt [https://crates.io/crates/gsgdt] is a crate which provides an
interface for stringly typed graphs. It also provides generation of
graphviz dot format from said graph.
-rw-r--r--Cargo.lock12
-rw-r--r--compiler/rustc_mir/Cargo.toml1
-rw-r--r--compiler/rustc_mir/src/util/generic_graph.rs70
-rw-r--r--compiler/rustc_mir/src/util/graphviz.rs168
-rw-r--r--compiler/rustc_mir/src/util/mod.rs5
-rw-r--r--src/tools/tidy/src/deps.rs1
6 files changed, 109 insertions, 148 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 30b7628120b..fb52f4201c0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1341,6 +1341,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "gsgdt"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cb4a3313cdc3c65906272ddd8987c7291ff6df4b5c9997c1232b6acd1ceab24"
+dependencies = [
+ "serde",
+]
+
+[[package]]
 name = "handlebars"
 version = "3.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3923,6 +3932,7 @@ name = "rustc_mir"
 version = "0.0.0"
 dependencies = [
  "either",
+ "gsgdt",
  "itertools 0.9.0",
  "polonius-engine",
  "regex",
@@ -5252,7 +5262,7 @@ dependencies = [
  "chrono",
  "lazy_static",
  "matchers",
- "parking_lot 0.9.0",
+ "parking_lot 0.11.0",
  "regex",
  "serde",
  "serde_json",
diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml
index 487668cfa11..28ba089d062 100644
--- a/compiler/rustc_mir/Cargo.toml
+++ b/compiler/rustc_mir/Cargo.toml
@@ -10,6 +10,7 @@ doctest = false
 [dependencies]
 either = "1.5.0"
 rustc_graphviz = { path = "../rustc_graphviz" }
+gsgdt = "0.1.1"
 itertools = "0.9"
 tracing = "0.1"
 polonius-engine = "0.12.0"
diff --git a/compiler/rustc_mir/src/util/generic_graph.rs b/compiler/rustc_mir/src/util/generic_graph.rs
new file mode 100644
index 00000000000..df9f94016c8
--- /dev/null
+++ b/compiler/rustc_mir/src/util/generic_graph.rs
@@ -0,0 +1,70 @@
+use gsgdt::{Edge, Graph, GraphKind, Node, NodeStyle};
+use rustc_hir::def_id::DefId;
+use rustc_index::vec::Idx;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+
+/// Convert an MIR function into a gsgdt Graph
+pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>, subgraph: bool) -> Graph {
+    let def_id = body.source.def_id();
+    let kind = if subgraph { GraphKind::Subgraph } else { GraphKind::Digraph };
+    let def_name = graphviz_safe_def_name(def_id);
+    let graph_name = format!("Mir_{}", def_name);
+    let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
+
+    // Nodes
+    let nodes: Vec<Node> = body
+        .basic_blocks()
+        .iter_enumerated()
+        .map(|(block, _)| bb_to_graph_node(block, body, dark_mode))
+        .collect();
+
+    // Edges
+    let mut edges = Vec::new();
+    for (source, _) in body.basic_blocks().iter_enumerated() {
+        let def_id = body.source.def_id();
+        let terminator = body[source].terminator();
+        let labels = terminator.kind.fmt_successor_labels();
+
+        for (&target, label) in terminator.successors().zip(labels) {
+            let src = node(def_id, source);
+            let trg = node(def_id, target);
+            edges.push(Edge::new(src, trg, label.to_string()));
+        }
+    }
+
+    Graph::new(graph_name, kind, nodes, edges)
+}
+
+fn bb_to_graph_node(block: BasicBlock, body: &Body<'_>, dark_mode: bool) -> Node {
+    let def_id = body.source.def_id();
+    let data = &body[block];
+    let label = node(def_id, block);
+
+    let (title, bgcolor) = if data.is_cleanup {
+        (format!("{} (cleanup)", block.index()), "lightblue")
+    } else {
+        let color = if dark_mode { "dimgray" } else { "gray" };
+        (format!("{}", block.index()), color)
+    };
+
+    let style = NodeStyle { title_bg: Some(bgcolor.to_owned()), ..Default::default() };
+    let mut stmts: Vec<String> = data.statements.iter().map(|x| format!("{:?}", x)).collect();
+
+    // add the terminator to the stmts, gsgdt can print it out seperately
+    let mut terminator_head = String::new();
+    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
+    stmts.push(terminator_head);
+
+    Node::new(stmts, label, title, style)
+}
+
+// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
+// it does not have to be user friendly.
+pub fn graphviz_safe_def_name(def_id: DefId) -> String {
+    format!("{}_{}", def_id.krate.index(), def_id.index.index(),)
+}
+
+fn node(def_id: DefId, block: BasicBlock) -> String {
+    format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
+}
diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs
index 625f1a3e684..c83a56c22ae 100644
--- a/compiler/rustc_mir/src/util/graphviz.rs
+++ b/compiler/rustc_mir/src/util/graphviz.rs
@@ -1,11 +1,12 @@
+use gsgdt::GraphvizSettings;
 use rustc_graphviz as dot;
 use rustc_hir::def_id::DefId;
-use rustc_index::vec::Idx;
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use std::fmt::Debug;
 use std::io::{self, Write};
 
+use super::generic_graph::mir_fn_to_generic_graph;
 use super::pretty::dump_mir_def_ids;
 
 /// Write a graphviz DOT graph of a list of MIRs.
@@ -32,12 +33,6 @@ where
     Ok(())
 }
 
-// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
-// it does not have to be user friendly.
-pub fn graphviz_safe_def_name(def_id: DefId) -> String {
-    format!("{}_{}", def_id.krate.index(), def_id.index.index(),)
-}
-
 /// Write a graphviz DOT graph of the MIR.
 pub fn write_mir_fn_graphviz<'tcx, W>(
     tcx: TyCtxt<'tcx>,
@@ -48,12 +43,6 @@ pub fn write_mir_fn_graphviz<'tcx, W>(
 where
     W: Write,
 {
-    let def_id = body.source.def_id();
-    let kind = if subgraph { "subgraph" } else { "digraph" };
-    let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR
-    let def_name = graphviz_safe_def_name(def_id);
-    writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;
-
     // Global graph properties
     let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
     let mut graph_attrs = vec![&font[..]];
@@ -67,168 +56,57 @@ where
         content_attrs.push(r#"fontcolor="white""#);
     }
 
-    writeln!(w, r#"    graph [{}];"#, graph_attrs.join(" "))?;
-    let content_attrs_str = content_attrs.join(" ");
-    writeln!(w, r#"    node [{}];"#, content_attrs_str)?;
-    writeln!(w, r#"    edge [{}];"#, content_attrs_str)?;
-
     // Graph label
-    write_graph_label(tcx, body, w)?;
-
-    // Nodes
-    for (block, _) in body.basic_blocks().iter_enumerated() {
-        write_node(block, body, dark_mode, w)?;
-    }
-
-    // Edges
-    for (source, _) in body.basic_blocks().iter_enumerated() {
-        write_edges(source, body, w)?;
-    }
-    writeln!(w, "}}")
-}
-
-/// Write a graphviz HTML-styled label for the given basic block, with
-/// all necessary escaping already performed. (This is suitable for
-/// emitting directly, as is done in this module, or for use with the
-/// LabelText::HtmlStr from librustc_graphviz.)
-///
-/// `init` and `fini` are callbacks for emitting additional rows of
-/// data (using HTML enclosed with `<tr>` in the emitted text).
-pub fn write_node_label<W: Write, INIT, FINI>(
-    block: BasicBlock,
-    body: &Body<'_>,
-    dark_mode: bool,
-    w: &mut W,
-    num_cols: u32,
-    init: INIT,
-    fini: FINI,
-) -> io::Result<()>
-where
-    INIT: Fn(&mut W) -> io::Result<()>,
-    FINI: Fn(&mut W) -> io::Result<()>,
-{
-    let data = &body[block];
-
-    write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
-
-    // Basic block number at the top.
-    let (blk, bgcolor) = if data.is_cleanup {
-        let color = if dark_mode { "royalblue" } else { "lightblue" };
-        (format!("{} (cleanup)", block.index()), color)
-    } else {
-        let color = if dark_mode { "dimgray" } else { "gray" };
-        (format!("{}", block.index()), color)
+    let label = get_graph_label(tcx, body);
+    let g = mir_fn_to_generic_graph(tcx, body, subgraph);
+    let settings = GraphvizSettings {
+        graph_attrs: Some(graph_attrs.join(" ")),
+        node_attrs: Some(content_attrs.join(" ")),
+        edge_attrs: Some(content_attrs.join(" ")),
+        graph_label: Some(label),
     };
-    write!(
-        w,
-        r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
-        attrs = r#"align="center""#,
-        colspan = num_cols,
-        blk = blk,
-        bgcolor = bgcolor
-    )?;
-
-    init(w)?;
-
-    // List of statements in the middle.
-    if !data.statements.is_empty() {
-        write!(w, r#"<tr><td align="left" balign="left">"#)?;
-        for statement in &data.statements {
-            write!(w, "{}<br/>", escape(statement))?;
-        }
-        write!(w, "</td></tr>")?;
-    }
-
-    // Terminator head at the bottom, not including the list of successor blocks. Those will be
-    // displayed as labels on the edges between blocks.
-    let mut terminator_head = String::new();
-    data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
-    write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
-
-    fini(w)?;
-
-    // Close the table
-    write!(w, "</table>")
-}
-
-/// Write a graphviz DOT node for the given basic block.
-fn write_node<W: Write>(
-    block: BasicBlock,
-    body: &Body<'_>,
-    dark_mode: bool,
-    w: &mut W,
-) -> io::Result<()> {
-    let def_id = body.source.def_id();
-    // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
-    write!(w, r#"    {} [shape="none", label=<"#, node(def_id, block))?;
-    write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?;
-    // Close the node label and the node itself.
-    writeln!(w, ">];")
-}
-
-/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
-fn write_edges<W: Write>(source: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
-    let def_id = body.source.def_id();
-    let terminator = body[source].terminator();
-    let labels = terminator.kind.fmt_successor_labels();
-
-    for (&target, label) in terminator.successors().zip(labels) {
-        let src = node(def_id, source);
-        let trg = node(def_id, target);
-        writeln!(w, r#"    {} -> {} [label="{}"];"#, src, trg, label)?;
-    }
-
-    Ok(())
+    g.to_dot(w, &settings)
 }
 
 /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
 /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
 /// all the variables and temporaries.
-fn write_graph_label<'tcx, W: Write>(
-    tcx: TyCtxt<'tcx>,
-    body: &Body<'_>,
-    w: &mut W,
-) -> io::Result<()> {
+fn get_graph_label<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> String {
     let def_id = body.source.def_id();
+    let mut label: Vec<String> = Vec::new();
 
-    write!(w, "    label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
+    label.push(format!("fn {}(", dot::escape_html(&tcx.def_path_str(def_id))));
 
     // fn argument types.
     for (i, arg) in body.args_iter().enumerate() {
         if i > 0 {
-            write!(w, ", ")?;
+            label.push(", ".to_owned());
         }
-        write!(w, "{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty))?;
+        label.push(format!("{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty)));
     }
 
-    write!(w, ") -&gt; {}", escape(&body.return_ty()))?;
-    write!(w, r#"<br align="left"/>"#)?;
+    label.push(format!(") -&gt; {}", escape(&body.return_ty())));
+    label.push(r#"<br align="left"/>"#.to_owned());
 
     for local in body.vars_and_temps_iter() {
         let decl = &body.local_decls[local];
 
-        write!(w, "let ")?;
+        label.push("let ".to_owned());
         if decl.mutability == Mutability::Mut {
-            write!(w, "mut ")?;
+            label.push("mut ".to_owned());
         }
 
-        write!(w, r#"{:?}: {};<br align="left"/>"#, Place::from(local), escape(&decl.ty))?;
+        label.push(format!(r#"{:?}: {};<br align="left"/>"#, Place::from(local), escape(&decl.ty)));
     }
 
     for var_debug_info in &body.var_debug_info {
-        write!(
-            w,
+        label.push(format!(
             r#"debug {} =&gt; {};<br align="left"/>"#,
             var_debug_info.name,
             escape(&var_debug_info.place)
-        )?;
+        ));
     }
-
-    writeln!(w, ">;")
-}
-
-fn node(def_id: DefId, block: BasicBlock) -> String {
-    format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
+    label.join("")
 }
 
 fn escape<T: Debug>(t: &T) -> String {
diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs
index aaee0bc526d..58e0f908108 100644
--- a/compiler/rustc_mir/src/util/mod.rs
+++ b/compiler/rustc_mir/src/util/mod.rs
@@ -8,6 +8,7 @@ mod alignment;
 pub mod collect_writes;
 mod find_self_call;
 pub(crate) mod generic_graphviz;
+mod generic_graph;
 mod graphviz;
 pub(crate) mod pretty;
 pub(crate) mod spanview;
@@ -15,6 +16,6 @@ pub(crate) mod spanview;
 pub use self::aggregate::expand_aggregate;
 pub use self::alignment::is_disaligned;
 pub use self::find_self_call::find_self_call;
-pub use self::graphviz::write_node_label as write_graphviz_node_label;
-pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz};
+pub use self::generic_graph::graphviz_safe_def_name;
+pub use self::graphviz::write_mir_graphviz;
 pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere};
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 057b0884e28..3aeb0b8c5b3 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -104,6 +104,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
     "getopts",
     "getrandom",
     "gimli",
+    "gsgdt",
     "hashbrown",
     "hermit-abi",
     "humantime",