Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make MIR graphviz generation use gsgdt #78399

Merged
merged 6 commits into from
Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,15 @@ dependencies = [
"regex",
]

[[package]]
name = "gsgdt"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d876ce7262df96262a2a19531da6ff9a86048224d49580a585fc5c04617825"
dependencies = [
"serde",
]

[[package]]
name = "handlebars"
version = "3.4.0"
Expand Down Expand Up @@ -3923,6 +3932,7 @@ name = "rustc_mir"
version = "0.0.0"
dependencies = [
"either",
"gsgdt",
"itertools 0.9.0",
"polonius-engine",
"regex",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ doctest = false
[dependencies]
either = "1.5.0"
rustc_graphviz = { path = "../rustc_graphviz" }
gsgdt = "0.1.2"
itertools = "0.9"
tracing = "0.1"
polonius-engine = "0.12.0"
Expand Down
70 changes: 70 additions & 0 deletions compiler/rustc_mir/src/util/generic_graph.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use gsgdt::{Edge, Graph, 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<'_>) -> Graph {
let def_id = body.source.def_id();
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, 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 {
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 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))
}
147 changes: 16 additions & 131 deletions compiler/rustc_mir/src/util/graphviz.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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>,
Expand All @@ -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[..]];
Expand All @@ -67,131 +56,31 @@ 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 mut label = String::from("");
// FIXME: remove this unwrap
write_graph_label(tcx, body, &mut label).unwrap();
let g = mir_fn_to_generic_graph(tcx, body);
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, subgraph)
}

/// 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>(
fn write_graph_label<'tcx, W: std::fmt::Write>(
tcx: TyCtxt<'tcx>,
body: &Body<'_>,
w: &mut W,
) -> io::Result<()> {
) -> std::fmt::Result {
let def_id = body.source.def_id();

write!(w, " label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
write!(w, "fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;

// fn argument types.
for (i, arg) in body.args_iter().enumerate() {
Expand Down Expand Up @@ -224,11 +113,7 @@ fn write_graph_label<'tcx, W: Write>(
)?;
}

writeln!(w, ">;")
}

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

fn escape<T: Debug>(t: &T) -> String {
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_mir/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod storage;
mod alignment;
pub mod collect_writes;
mod find_self_call;
mod generic_graph;
pub(crate) mod generic_graphviz;
mod graphviz;
pub(crate) mod pretty;
Expand All @@ -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};
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#![allow(clippy::useless_attribute)] //issue #2910
// edition:2018

#[macro_use]
extern crate serde_derive;
use serde::Deserialize;

/// Tests that we do not lint for unused underscores in a `MacroAttribute`
/// expansion
Expand Down
1 change: 1 addition & 0 deletions src/tools/tidy/src/deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"getopts",
"getrandom",
"gimli",
"gsgdt",
"hashbrown",
"hermit-abi",
"humantime",
Expand Down