Skip to content

Commit

Permalink
Auto merge of #65459 - ecstatic-morse:graphviz-subgraph, r=estebank
Browse files Browse the repository at this point in the history
Fix `-Zunpretty=mir-cfg` to render multiple items

`-Zunpretty=mir-cfg` outputs DOT to stdout for all items being compiled. However, it puts all of these items in separate `digraph`s, which means the result of redirecting that output to a file is not valid. Most dot renderers (I have tried `dot` and `xdot`) cannot render the output.

This PR checks to see if `write_mir_graphviz` will  process multiple items, and writes them each as a `subgraph` in a single, top-level `digraph`. As a result, DOT can be viewed without manually editing the output file. The output is unchanged when printing a single item (e.g.`-Zunpretty=mir-cfg=item_name`).

Here's the output of `xdot` for a rust file containing three items:
![three-items](https://user-images.githubusercontent.com/29463364/66889712-4bf62200-ef98-11e9-83b5-60faa2a300dd.png)

The borders are a result of the nonstandard–but well-supported–[`cluster` prefix](https://graphviz.gitlab.io/_pages/doc/info/lang.html) (search for "Subgraphs and Clusters"). They will not appear if your renderer does not support this extension, but the graph will still render properly.
  • Loading branch information
bors committed Nov 1, 2019
2 parents aa4e57c + cd3e9c4 commit 472e678
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 20 deletions.
52 changes: 40 additions & 12 deletions src/librustc_mir/util/graphviz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,22 @@ pub fn write_mir_graphviz<W>(
where
W: Write,
{
for def_id in dump_mir_def_ids(tcx, single) {
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 {
let body = &tcx.optimized_mir(def_id);
write_mir_fn_graphviz(tcx, def_id, body, w)?;
write_mir_fn_graphviz(tcx, def_id, body, use_subgraphs, w)?;
}

if use_subgraphs {
writeln!(w, "}}")?;
}

Ok(())
}

Expand All @@ -38,12 +50,16 @@ pub fn write_mir_fn_graphviz<'tcx, W>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
body: &Body<'_>,
subgraph: bool,
w: &mut W,
) -> io::Result<()>
where
W: Write,
{
writeln!(w, "digraph Mir_{} {{", graphviz_safe_def_name(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
writeln!(w, r#" graph [fontname="monospace"];"#)?;
Expand All @@ -55,12 +71,12 @@ where

// Nodes
for (block, _) in body.basic_blocks().iter_enumerated() {
write_node(block, body, w)?;
write_node(def_id, block, body, w)?;
}

// Edges
for (source, _) in body.basic_blocks().iter_enumerated() {
write_edges(source, body, w)?;
write_edges(def_id, source, body, w)?;
}
writeln!(w, "}}")
}
Expand Down Expand Up @@ -111,25 +127,37 @@ pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
fini(w)?;

// Close the table
writeln!(w, "</table>")
write!(w, "</table>")
}

/// Write a graphviz DOT node for the given basic block.
fn write_node<W: Write>(block: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
fn write_node<W: Write>(
def_id: DefId,
block: BasicBlock,
body: &Body<'_>,
w: &mut W,
) -> io::Result<()> {
// Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
write!(w, r#" {} [shape="none", label=<"#, node(block))?;
write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?;
write_node_label(block, body, 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<()> {
fn write_edges<W: Write>(
def_id: DefId,
source: BasicBlock,
body: &Body<'_>,
w: &mut W,
) -> io::Result<()> {
let terminator = body[source].terminator();
let labels = terminator.kind.fmt_successor_labels();

for (&target, label) in terminator.successors().zip(labels) {
writeln!(w, r#" {} -> {} [label="{}"];"#, node(source), node(target), label)?;
let src = node(def_id, source);
let trg = node(def_id, target);
writeln!(w, r#" {} -> {} [label="{}"];"#, src, trg, label)?;
}

Ok(())
Expand Down Expand Up @@ -181,8 +209,8 @@ fn write_graph_label<'tcx, W: Write>(
writeln!(w, ">;")
}

fn node(block: BasicBlock) -> String {
format!("bb{}", block.index())
fn node(def_id: DefId, block: BasicBlock) -> String {
format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
}

fn escape<T: Debug>(t: &T) -> String {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/util/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ fn dump_matched_mir_node<'tcx, F>(
let _: io::Result<()> = try {
let mut file =
create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?;
write_mir_fn_graphviz(tcx, source.def_id(), body, &mut file)?;
write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?;
};
}
}
Expand Down
11 changes: 4 additions & 7 deletions src/test/mir-opt/graphviz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ fn main() {}
// node [fontname="monospace"];
// edge [fontname="monospace"];
// label=<fn main() -&gt; ()<br align="left"/>>;
// bb0 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">0</td></tr><tr><td align="left" balign="left">_0 = ()<br/></td></tr><tr><td align="left">goto</td></tr></table>
// >];
// bb1 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">1</td></tr><tr><td align="left">resume</td></tr></table>
// >];
// bb2 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">2</td></tr><tr><td align="left">return</td></tr></table>
// >];
// bb0 -> bb2 [label=""];
// bb0__0_12 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">0</td></tr><tr><td align="left" balign="left">_0 = ()<br/></td></tr><tr><td align="left">goto</td></tr></table>>];
// bb1__0_12 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">1</td></tr><tr><td align="left">resume</td></tr></table>>];
// bb2__0_12 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">2</td></tr><tr><td align="left">return</td></tr></table>>];
// bb0__0_12 -> bb2__0_12 [label=""];
// }
// END rustc.main.mir_map.0.dot

0 comments on commit 472e678

Please sign in to comment.