2020-06-02 17:19:49 +00:00
|
|
|
use rustc_graphviz as dot;
|
2020-01-05 01:37:57 +00:00
|
|
|
use rustc_hir::def_id::DefId;
|
2019-09-26 05:38:33 +00:00
|
|
|
use rustc_index::vec::Idx;
|
2020-03-29 15:19:48 +00:00
|
|
|
use rustc_middle::mir::*;
|
|
|
|
use rustc_middle::ty::TyCtxt;
|
2015-12-19 03:52:55 +00:00
|
|
|
use std::fmt::Debug;
|
2015-12-19 01:29:03 +00:00
|
|
|
use std::io::{self, Write};
|
|
|
|
|
2017-04-25 19:56:02 +00:00
|
|
|
use super::pretty::dump_mir_def_ids;
|
|
|
|
|
2016-03-12 17:07:00 +00:00
|
|
|
/// Write a graphviz DOT graph of a list of MIRs.
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn write_mir_graphviz<W>(tcx: TyCtxt<'_>, single: Option<DefId>, w: &mut W) -> io::Result<()>
|
2019-06-11 21:11:55 +00:00
|
|
|
where
|
|
|
|
W: Write,
|
2016-08-08 22:42:06 +00:00
|
|
|
{
|
2019-10-16 04:54:08 +00:00
|
|
|
let def_ids = dump_mir_def_ids(tcx, single);
|
|
|
|
|
|
|
|
let use_subgraphs = def_ids.len() > 1;
|
|
|
|
if use_subgraphs {
|
|
|
|
writeln!(w, "digraph __crate__ {{")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
for def_id in def_ids {
|
2019-06-03 22:26:48 +00:00
|
|
|
let body = &tcx.optimized_mir(def_id);
|
2020-10-04 18:01:38 +00:00
|
|
|
write_mir_fn_graphviz(tcx, body, use_subgraphs, w)?;
|
2017-10-24 11:48:39 +00:00
|
|
|
}
|
2019-10-16 04:54:08 +00:00
|
|
|
|
|
|
|
if use_subgraphs {
|
|
|
|
writeln!(w, "}}")?;
|
|
|
|
}
|
|
|
|
|
2017-10-24 11:48:39 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2016-08-08 22:42:06 +00:00
|
|
|
|
2019-03-17 10:36:10 +00:00
|
|
|
// 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 {
|
2019-12-22 22:42:04 +00:00
|
|
|
format!("{}_{}", def_id.krate.index(), def_id.index.index(),)
|
2019-03-17 10:36:10 +00:00
|
|
|
}
|
|
|
|
|
2017-10-24 11:48:39 +00:00
|
|
|
/// Write a graphviz DOT graph of the MIR.
|
2019-06-11 21:11:55 +00:00
|
|
|
pub fn write_mir_fn_graphviz<'tcx, W>(
|
2019-06-13 21:48:52 +00:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-11 21:11:55 +00:00
|
|
|
body: &Body<'_>,
|
2019-10-16 04:54:08 +00:00
|
|
|
subgraph: bool,
|
2019-06-11 21:11:55 +00:00
|
|
|
w: &mut W,
|
|
|
|
) -> io::Result<()>
|
|
|
|
where
|
|
|
|
W: Write,
|
2017-10-24 11:48:39 +00:00
|
|
|
{
|
2020-10-04 18:01:38 +00:00
|
|
|
let def_id = body.source.def_id();
|
2019-10-16 04:54:08 +00:00
|
|
|
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)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2017-10-24 11:48:39 +00:00
|
|
|
// Global graph properties
|
2020-09-16 15:10:06 +00:00
|
|
|
let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
|
|
|
|
let mut graph_attrs = vec![&font[..]];
|
|
|
|
let mut content_attrs = vec![&font[..]];
|
2020-09-08 23:08:35 +00:00
|
|
|
|
|
|
|
let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
|
|
|
|
if dark_mode {
|
|
|
|
graph_attrs.push(r#"bgcolor="black""#);
|
2020-10-05 23:36:10 +00:00
|
|
|
graph_attrs.push(r#"fontcolor="white""#);
|
2020-09-08 23:08:35 +00:00
|
|
|
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)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2017-10-24 11:48:39 +00:00
|
|
|
// Graph label
|
2020-10-04 22:22:23 +00:00
|
|
|
write_graph_label(tcx, body, w)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2017-10-24 11:48:39 +00:00
|
|
|
// Nodes
|
2019-06-03 22:26:48 +00:00
|
|
|
for (block, _) in body.basic_blocks().iter_enumerated() {
|
2020-10-04 22:22:23 +00:00
|
|
|
write_node(block, body, dark_mode, w)?;
|
2017-10-24 11:48:39 +00:00
|
|
|
}
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2017-10-24 11:48:39 +00:00
|
|
|
// Edges
|
2019-06-03 22:26:48 +00:00
|
|
|
for (source, _) in body.basic_blocks().iter_enumerated() {
|
2020-10-04 22:22:23 +00:00
|
|
|
write_edges(source, body, w)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
2017-10-24 11:48:39 +00:00
|
|
|
writeln!(w, "}}")
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
|
|
|
|
2016-01-25 13:34:34 +00:00
|
|
|
/// 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
|
2020-06-02 17:03:40 +00:00
|
|
|
/// LabelText::HtmlStr from librustc_graphviz.)
|
2016-01-25 13:34:34 +00:00
|
|
|
///
|
|
|
|
/// `init` and `fini` are callbacks for emitting additional rows of
|
|
|
|
/// data (using HTML enclosed with `<tr>` in the emitted text).
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn write_node_label<W: Write, INIT, FINI>(
|
|
|
|
block: BasicBlock,
|
|
|
|
body: &Body<'_>,
|
2020-09-08 23:08:35 +00:00
|
|
|
dark_mode: bool,
|
2019-12-22 22:42:04 +00:00
|
|
|
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<()>,
|
2016-01-25 13:34:34 +00:00
|
|
|
{
|
2019-06-03 22:26:48 +00:00
|
|
|
let data = &body[block];
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2015-12-19 03:52:55 +00:00
|
|
|
// Basic block number at the top.
|
2019-11-16 13:23:31 +00:00
|
|
|
let (blk, bgcolor) = if data.is_cleanup {
|
2020-10-05 23:36:10 +00:00
|
|
|
let color = if dark_mode { "royalblue" } else { "lightblue" };
|
|
|
|
(format!("{} (cleanup)", block.index()), color)
|
2019-11-16 13:23:31 +00:00
|
|
|
} else {
|
2020-10-02 19:40:24 +00:00
|
|
|
let color = if dark_mode { "dimgray" } else { "gray" };
|
|
|
|
(format!("{}", block.index()), color)
|
2019-11-16 13:23:31 +00:00
|
|
|
};
|
2019-12-22 22:42:04 +00:00
|
|
|
write!(
|
|
|
|
w,
|
2020-09-08 23:08:35 +00:00
|
|
|
r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
|
|
|
|
attrs = r#"align="center""#,
|
2019-12-22 22:42:04 +00:00
|
|
|
colspan = num_cols,
|
2019-11-16 13:23:31 +00:00
|
|
|
blk = blk,
|
2020-10-02 19:40:24 +00:00
|
|
|
bgcolor = bgcolor
|
2019-12-22 22:42:04 +00:00
|
|
|
)?;
|
2016-01-25 13:34:34 +00:00
|
|
|
|
2016-03-23 03:01:37 +00:00
|
|
|
init(w)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2015-12-19 03:52:55 +00:00
|
|
|
// List of statements in the middle.
|
2015-12-19 01:29:03 +00:00
|
|
|
if !data.statements.is_empty() {
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, r#"<tr><td align="left" balign="left">"#)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
for statement in &data.statements {
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, "{}<br/>", escape(statement))?;
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, "</td></tr>")?;
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
|
|
|
|
2015-12-19 03:52:55 +00:00
|
|
|
// Terminator head at the bottom, not including the list of successor blocks. Those will be
|
|
|
|
// displayed as labels on the edges between blocks.
|
2015-12-19 01:29:03 +00:00
|
|
|
let mut terminator_head = String::new();
|
2016-03-10 14:55:15 +00:00
|
|
|
data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2016-03-23 03:01:37 +00:00
|
|
|
fini(w)?;
|
2016-01-25 13:34:34 +00:00
|
|
|
|
|
|
|
// Close the table
|
2019-10-16 04:35:09 +00:00
|
|
|
write!(w, "</table>")
|
2016-01-25 13:34:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Write a graphviz DOT node for the given basic block.
|
2019-10-16 04:53:11 +00:00
|
|
|
fn write_node<W: Write>(
|
|
|
|
block: BasicBlock,
|
|
|
|
body: &Body<'_>,
|
2020-09-08 23:08:35 +00:00
|
|
|
dark_mode: bool,
|
2019-10-16 04:53:11 +00:00
|
|
|
w: &mut W,
|
|
|
|
) -> io::Result<()> {
|
2020-10-04 22:22:23 +00:00
|
|
|
let def_id = body.source.def_id();
|
2016-01-25 13:34:34 +00:00
|
|
|
// Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
|
2019-10-16 04:53:11 +00:00
|
|
|
write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?;
|
2020-09-08 23:08:35 +00:00
|
|
|
write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?;
|
2016-01-25 13:34:34 +00:00
|
|
|
// Close the node label and the node itself.
|
|
|
|
writeln!(w, ">];")
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
|
|
|
|
2015-12-19 03:52:55 +00:00
|
|
|
/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
|
2020-10-04 22:22:23 +00:00
|
|
|
fn write_edges<W: Write>(source: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
|
|
|
|
let def_id = body.source.def_id();
|
2019-06-03 22:26:48 +00:00
|
|
|
let terminator = body[source].terminator();
|
2016-03-10 14:55:15 +00:00
|
|
|
let labels = terminator.kind.fmt_successor_labels();
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2018-04-27 11:02:09 +00:00
|
|
|
for (&target, label) in terminator.successors().zip(labels) {
|
2019-10-16 04:53:11 +00:00
|
|
|
let src = node(def_id, source);
|
|
|
|
let trg = node(def_id, target);
|
|
|
|
writeln!(w, r#" {} -> {} [label="{}"];"#, src, trg, label)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2015-12-19 03:52:55 +00:00
|
|
|
/// 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.
|
2019-06-13 21:48:52 +00:00
|
|
|
fn write_graph_label<'tcx, W: Write>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-11 21:11:55 +00:00
|
|
|
body: &Body<'_>,
|
|
|
|
w: &mut W,
|
|
|
|
) -> io::Result<()> {
|
2020-10-04 22:22:23 +00:00
|
|
|
let def_id = body.source.def_id();
|
|
|
|
|
2018-12-19 10:31:35 +00:00
|
|
|
write!(w, " label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2015-12-19 03:52:55 +00:00
|
|
|
// fn argument types.
|
2019-06-03 22:26:48 +00:00
|
|
|
for (i, arg) in body.args_iter().enumerate() {
|
2015-12-19 01:29:03 +00:00
|
|
|
if i > 0 {
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, ", ")?;
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
2019-12-22 22:42:04 +00:00
|
|
|
write!(w, "{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty))?;
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
|
|
|
|
2019-06-03 22:26:48 +00:00
|
|
|
write!(w, ") -> {}", escape(&body.return_ty()))?;
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, r#"<br align="left"/>"#)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
|
2019-06-03 22:26:48 +00:00
|
|
|
for local in body.vars_and_temps_iter() {
|
|
|
|
let decl = &body.local_decls[local];
|
2016-09-24 23:38:27 +00:00
|
|
|
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, "let ")?;
|
2016-09-24 23:38:27 +00:00
|
|
|
if decl.mutability == Mutability::Mut {
|
2016-03-23 03:01:37 +00:00
|
|
|
write!(w, "mut ")?;
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
write!(w, r#"{:?}: {};<br align="left"/>"#, Place::from(local), escape(&decl.ty))?;
|
2018-05-16 15:58:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for var_debug_info in &body.var_debug_info {
|
2019-12-22 22:42:04 +00:00
|
|
|
write!(
|
|
|
|
w,
|
|
|
|
r#"debug {} => {};<br align="left"/>"#,
|
|
|
|
var_debug_info.name,
|
|
|
|
escape(&var_debug_info.place)
|
|
|
|
)?;
|
2015-12-19 01:29:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
writeln!(w, ">;")
|
|
|
|
}
|
2015-12-19 03:52:55 +00:00
|
|
|
|
2019-10-16 04:53:11 +00:00
|
|
|
fn node(def_id: DefId, block: BasicBlock) -> String {
|
|
|
|
format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
|
2015-12-19 03:52:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn escape<T: Debug>(t: &T) -> String {
|
|
|
|
dot::escape_html(&format!("{:?}", t))
|
|
|
|
}
|