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.
This commit is contained in:
parent
25f6938da4
commit
ea1460773f
6 changed files with 109 additions and 148 deletions
12
Cargo.lock
12
Cargo.lock
|
@ -1340,6 +1340,15 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
70
compiler/rustc_mir/src/util/generic_graph.rs
Normal file
70
compiler/rustc_mir/src/util/generic_graph.rs
Normal file
|
@ -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))
|
||||
}
|
|
@ -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, ") -> {}", escape(&body.return_ty()))?;
|
||||
write!(w, r#"<br align="left"/>"#)?;
|
||||
label.push(format!(") -> {}", 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 {} => {};<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 {
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -104,6 +104,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
|
|||
"getopts",
|
||||
"getrandom",
|
||||
"gimli",
|
||||
"gsgdt",
|
||||
"hashbrown",
|
||||
"hermit-abi",
|
||||
"humantime",
|
||||
|
|
Loading…
Add table
Reference in a new issue