summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/mir/generic_graphviz.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/mir/generic_graphviz.rs')
-rw-r--r--compiler/rustc_middle/src/mir/generic_graphviz.rs173
1 files changed, 173 insertions, 0 deletions
diff --git a/compiler/rustc_middle/src/mir/generic_graphviz.rs b/compiler/rustc_middle/src/mir/generic_graphviz.rs
new file mode 100644
index 000000000..11ac45943
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/generic_graphviz.rs
@@ -0,0 +1,173 @@
+use rustc_data_structures::graph::{self, iterate};
+use rustc_graphviz as dot;
+use rustc_middle::ty::TyCtxt;
+use std::io::{self, Write};
+
+pub struct GraphvizWriter<
+ 'a,
+ G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes,
+ NodeContentFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
+ EdgeLabelsFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
+> {
+ graph: &'a G,
+ is_subgraph: bool,
+ graphviz_name: String,
+ graph_label: Option<String>,
+ node_content_fn: NodeContentFn,
+ edge_labels_fn: EdgeLabelsFn,
+}
+
+impl<
+ 'a,
+ G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes,
+ NodeContentFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
+ EdgeLabelsFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
+> GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn>
+{
+ pub fn new(
+ graph: &'a G,
+ graphviz_name: &str,
+ node_content_fn: NodeContentFn,
+ edge_labels_fn: EdgeLabelsFn,
+ ) -> Self {
+ Self {
+ graph,
+ is_subgraph: false,
+ graphviz_name: graphviz_name.to_owned(),
+ graph_label: None,
+ node_content_fn,
+ edge_labels_fn,
+ }
+ }
+
+ pub fn set_graph_label(&mut self, graph_label: &str) {
+ self.graph_label = Some(graph_label.to_owned());
+ }
+
+ /// Write a graphviz DOT of the graph
+ pub fn write_graphviz<'tcx, W>(&self, tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()>
+ where
+ W: Write,
+ {
+ let kind = if self.is_subgraph { "subgraph" } else { "digraph" };
+ let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Print border around graph
+ // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
+ // prepend "Mir_" to the graphviz_safe_def_name(def_id)
+ writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?;
+
+ // Global graph properties
+ let font = format!(r#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font);
+ let mut graph_attrs = vec![&font[..]];
+ let mut content_attrs = vec![&font[..]];
+
+ let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode;
+ if dark_mode {
+ graph_attrs.push(r#"bgcolor="black""#);
+ graph_attrs.push(r#"fontcolor="white""#);
+ content_attrs.push(r#"color="white""#);
+ 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
+ if let Some(graph_label) = &self.graph_label {
+ self.write_graph_label(graph_label, w)?;
+ }
+
+ // Nodes
+ for node in iterate::post_order_from(self.graph, self.graph.start_node()) {
+ self.write_node(node, dark_mode, w)?;
+ }
+
+ // Edges
+ for source in iterate::post_order_from(self.graph, self.graph.start_node()) {
+ self.write_edges(source, w)?;
+ }
+ writeln!(w, "}}")
+ }
+
+ /// Write a graphviz DOT node for the given node.
+ pub fn write_node<W>(&self, node: G::Node, dark_mode: bool, w: &mut W) -> io::Result<()>
+ where
+ W: Write,
+ {
+ // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
+ write!(w, r#" {} [shape="none", label=<"#, self.node(node))?;
+
+ write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
+
+ // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
+ // we need generic way to know if node header should have a different color. For example,
+ // for MIR:
+ //
+ // let (blk, bgcolor) = if data.is_cleanup {
+ // let color = if dark_mode { "royalblue" } else { "lightblue" };
+ // (format!("{:?} (cleanup)", node), color)
+ // } else {
+ // let color = if dark_mode { "dimgray" } else { "gray" };
+ // (format!("{:?}", node), color)
+ // };
+ let color = if dark_mode { "dimgray" } else { "gray" };
+ let (blk, bgcolor) = (format!("{:?}", node), color);
+ write!(
+ w,
+ r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
+ attrs = r#"align="center""#,
+ colspan = 1,
+ blk = blk,
+ bgcolor = bgcolor
+ )?;
+
+ for section in (self.node_content_fn)(node) {
+ write!(
+ w,
+ r#"<tr><td align="left" balign="left">{}</td></tr>"#,
+ dot::escape_html(&section).replace('\n', "<br/>")
+ )?;
+ }
+
+ // Close the table
+ write!(w, "</table>")?;
+
+ // Close the node label and the node itself.
+ writeln!(w, ">];")
+ }
+
+ /// Write graphviz DOT edges with labels between the given node and all of its successors.
+ fn write_edges<W>(&self, source: G::Node, w: &mut W) -> io::Result<()>
+ where
+ W: Write,
+ {
+ let edge_labels = (self.edge_labels_fn)(source);
+ for (index, target) in self.graph.successors(source).enumerate() {
+ let src = self.node(source);
+ let trg = self.node(target);
+ let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) {
+ dot::escape_html(edge_label).replace('\n', r#"<br align="left"/>"#)
+ } else {
+ "".to_owned()
+ };
+ writeln!(w, r#" {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?;
+ }
+ Ok(())
+ }
+
+ /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
+ /// will appear below the graph.
+ fn write_graph_label<W>(&self, label: &str, w: &mut W) -> io::Result<()>
+ where
+ W: Write,
+ {
+ let lines = label.split('\n').map(|s| dot::escape_html(s)).collect::<Vec<_>>();
+ let escaped_label = lines.join(r#"<br align="left"/>"#);
+ writeln!(w, r#" label=<<br/><br/>{}<br align="left"/><br/><br/><br/>>;"#, escaped_label)
+ }
+
+ fn node(&self, node: G::Node) -> String {
+ format!("{:?}__{}", node, self.graphviz_name)
+ }
+}