Skip to content

Commit ea14607

Browse files
committed
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.
1 parent 25f6938 commit ea14607

File tree

6 files changed

+109
-148
lines changed

6 files changed

+109
-148
lines changed

Cargo.lock

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,15 @@ dependencies = [
13401340
"regex",
13411341
]
13421342

1343+
[[package]]
1344+
name = "gsgdt"
1345+
version = "0.1.1"
1346+
source = "registry+https://github.com/rust-lang/crates.io-index"
1347+
checksum = "9cb4a3313cdc3c65906272ddd8987c7291ff6df4b5c9997c1232b6acd1ceab24"
1348+
dependencies = [
1349+
"serde",
1350+
]
1351+
13431352
[[package]]
13441353
name = "handlebars"
13451354
version = "3.4.0"
@@ -3923,6 +3932,7 @@ name = "rustc_mir"
39233932
version = "0.0.0"
39243933
dependencies = [
39253934
"either",
3935+
"gsgdt",
39263936
"itertools 0.9.0",
39273937
"polonius-engine",
39283938
"regex",
@@ -5252,7 +5262,7 @@ dependencies = [
52525262
"chrono",
52535263
"lazy_static",
52545264
"matchers",
5255-
"parking_lot 0.9.0",
5265+
"parking_lot 0.11.0",
52565266
"regex",
52575267
"serde",
52585268
"serde_json",

compiler/rustc_mir/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ doctest = false
1010
[dependencies]
1111
either = "1.5.0"
1212
rustc_graphviz = { path = "../rustc_graphviz" }
13+
gsgdt = "0.1.1"
1314
itertools = "0.9"
1415
tracing = "0.1"
1516
polonius-engine = "0.12.0"
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use gsgdt::{Edge, Graph, GraphKind, Node, NodeStyle};
2+
use rustc_hir::def_id::DefId;
3+
use rustc_index::vec::Idx;
4+
use rustc_middle::mir::*;
5+
use rustc_middle::ty::TyCtxt;
6+
7+
/// Convert an MIR function into a gsgdt Graph
8+
pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>, subgraph: bool) -> Graph {
9+
let def_id = body.source.def_id();
10+
let kind = if subgraph { GraphKind::Subgraph } else { GraphKind::Digraph };
11+
let def_name = graphviz_safe_def_name(def_id);
12+
let graph_name = format!("Mir_{}", def_name);
13+
let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
14+
15+
// Nodes
16+
let nodes: Vec<Node> = body
17+
.basic_blocks()
18+
.iter_enumerated()
19+
.map(|(block, _)| bb_to_graph_node(block, body, dark_mode))
20+
.collect();
21+
22+
// Edges
23+
let mut edges = Vec::new();
24+
for (source, _) in body.basic_blocks().iter_enumerated() {
25+
let def_id = body.source.def_id();
26+
let terminator = body[source].terminator();
27+
let labels = terminator.kind.fmt_successor_labels();
28+
29+
for (&target, label) in terminator.successors().zip(labels) {
30+
let src = node(def_id, source);
31+
let trg = node(def_id, target);
32+
edges.push(Edge::new(src, trg, label.to_string()));
33+
}
34+
}
35+
36+
Graph::new(graph_name, kind, nodes, edges)
37+
}
38+
39+
fn bb_to_graph_node(block: BasicBlock, body: &Body<'_>, dark_mode: bool) -> Node {
40+
let def_id = body.source.def_id();
41+
let data = &body[block];
42+
let label = node(def_id, block);
43+
44+
let (title, bgcolor) = if data.is_cleanup {
45+
(format!("{} (cleanup)", block.index()), "lightblue")
46+
} else {
47+
let color = if dark_mode { "dimgray" } else { "gray" };
48+
(format!("{}", block.index()), color)
49+
};
50+
51+
let style = NodeStyle { title_bg: Some(bgcolor.to_owned()), ..Default::default() };
52+
let mut stmts: Vec<String> = data.statements.iter().map(|x| format!("{:?}", x)).collect();
53+
54+
// add the terminator to the stmts, gsgdt can print it out seperately
55+
let mut terminator_head = String::new();
56+
data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
57+
stmts.push(terminator_head);
58+
59+
Node::new(stmts, label, title, style)
60+
}
61+
62+
// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
63+
// it does not have to be user friendly.
64+
pub fn graphviz_safe_def_name(def_id: DefId) -> String {
65+
format!("{}_{}", def_id.krate.index(), def_id.index.index(),)
66+
}
67+
68+
fn node(def_id: DefId, block: BasicBlock) -> String {
69+
format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
70+
}

compiler/rustc_mir/src/util/graphviz.rs

Lines changed: 23 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use gsgdt::GraphvizSettings;
12
use rustc_graphviz as dot;
23
use rustc_hir::def_id::DefId;
3-
use rustc_index::vec::Idx;
44
use rustc_middle::mir::*;
55
use rustc_middle::ty::TyCtxt;
66
use std::fmt::Debug;
77
use std::io::{self, Write};
88

9+
use super::generic_graph::mir_fn_to_generic_graph;
910
use super::pretty::dump_mir_def_ids;
1011

1112
/// Write a graphviz DOT graph of a list of MIRs.
@@ -32,12 +33,6 @@ where
3233
Ok(())
3334
}
3435

35-
// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
36-
// it does not have to be user friendly.
37-
pub fn graphviz_safe_def_name(def_id: DefId) -> String {
38-
format!("{}_{}", def_id.krate.index(), def_id.index.index(),)
39-
}
40-
4136
/// Write a graphviz DOT graph of the MIR.
4237
pub fn write_mir_fn_graphviz<'tcx, W>(
4338
tcx: TyCtxt<'tcx>,
@@ -48,12 +43,6 @@ pub fn write_mir_fn_graphviz<'tcx, W>(
4843
where
4944
W: Write,
5045
{
51-
let def_id = body.source.def_id();
52-
let kind = if subgraph { "subgraph" } else { "digraph" };
53-
let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR
54-
let def_name = graphviz_safe_def_name(def_id);
55-
writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;
56-
5746
// Global graph properties
5847
let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
5948
let mut graph_attrs = vec![&font[..]];
@@ -67,168 +56,57 @@ where
6756
content_attrs.push(r#"fontcolor="white""#);
6857
}
6958

70-
writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?;
71-
let content_attrs_str = content_attrs.join(" ");
72-
writeln!(w, r#" node [{}];"#, content_attrs_str)?;
73-
writeln!(w, r#" edge [{}];"#, content_attrs_str)?;
74-
7559
// Graph label
76-
write_graph_label(tcx, body, w)?;
77-
78-
// Nodes
79-
for (block, _) in body.basic_blocks().iter_enumerated() {
80-
write_node(block, body, dark_mode, w)?;
81-
}
82-
83-
// Edges
84-
for (source, _) in body.basic_blocks().iter_enumerated() {
85-
write_edges(source, body, w)?;
86-
}
87-
writeln!(w, "}}")
88-
}
89-
90-
/// Write a graphviz HTML-styled label for the given basic block, with
91-
/// all necessary escaping already performed. (This is suitable for
92-
/// emitting directly, as is done in this module, or for use with the
93-
/// LabelText::HtmlStr from librustc_graphviz.)
94-
///
95-
/// `init` and `fini` are callbacks for emitting additional rows of
96-
/// data (using HTML enclosed with `<tr>` in the emitted text).
97-
pub fn write_node_label<W: Write, INIT, FINI>(
98-
block: BasicBlock,
99-
body: &Body<'_>,
100-
dark_mode: bool,
101-
w: &mut W,
102-
num_cols: u32,
103-
init: INIT,
104-
fini: FINI,
105-
) -> io::Result<()>
106-
where
107-
INIT: Fn(&mut W) -> io::Result<()>,
108-
FINI: Fn(&mut W) -> io::Result<()>,
109-
{
110-
let data = &body[block];
111-
112-
write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
113-
114-
// Basic block number at the top.
115-
let (blk, bgcolor) = if data.is_cleanup {
116-
let color = if dark_mode { "royalblue" } else { "lightblue" };
117-
(format!("{} (cleanup)", block.index()), color)
118-
} else {
119-
let color = if dark_mode { "dimgray" } else { "gray" };
120-
(format!("{}", block.index()), color)
60+
let label = get_graph_label(tcx, body);
61+
let g = mir_fn_to_generic_graph(tcx, body, subgraph);
62+
let settings = GraphvizSettings {
63+
graph_attrs: Some(graph_attrs.join(" ")),
64+
node_attrs: Some(content_attrs.join(" ")),
65+
edge_attrs: Some(content_attrs.join(" ")),
66+
graph_label: Some(label),
12167
};
122-
write!(
123-
w,
124-
r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
125-
attrs = r#"align="center""#,
126-
colspan = num_cols,
127-
blk = blk,
128-
bgcolor = bgcolor
129-
)?;
130-
131-
init(w)?;
132-
133-
// List of statements in the middle.
134-
if !data.statements.is_empty() {
135-
write!(w, r#"<tr><td align="left" balign="left">"#)?;
136-
for statement in &data.statements {
137-
write!(w, "{}<br/>", escape(statement))?;
138-
}
139-
write!(w, "</td></tr>")?;
140-
}
141-
142-
// Terminator head at the bottom, not including the list of successor blocks. Those will be
143-
// displayed as labels on the edges between blocks.
144-
let mut terminator_head = String::new();
145-
data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
146-
write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
147-
148-
fini(w)?;
149-
150-
// Close the table
151-
write!(w, "</table>")
152-
}
153-
154-
/// Write a graphviz DOT node for the given basic block.
155-
fn write_node<W: Write>(
156-
block: BasicBlock,
157-
body: &Body<'_>,
158-
dark_mode: bool,
159-
w: &mut W,
160-
) -> io::Result<()> {
161-
let def_id = body.source.def_id();
162-
// Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
163-
write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?;
164-
write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?;
165-
// Close the node label and the node itself.
166-
writeln!(w, ">];")
167-
}
168-
169-
/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
170-
fn write_edges<W: Write>(source: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
171-
let def_id = body.source.def_id();
172-
let terminator = body[source].terminator();
173-
let labels = terminator.kind.fmt_successor_labels();
174-
175-
for (&target, label) in terminator.successors().zip(labels) {
176-
let src = node(def_id, source);
177-
let trg = node(def_id, target);
178-
writeln!(w, r#" {} -> {} [label="{}"];"#, src, trg, label)?;
179-
}
180-
181-
Ok(())
68+
g.to_dot(w, &settings)
18269
}
18370

18471
/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
18572
/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
18673
/// all the variables and temporaries.
187-
fn write_graph_label<'tcx, W: Write>(
188-
tcx: TyCtxt<'tcx>,
189-
body: &Body<'_>,
190-
w: &mut W,
191-
) -> io::Result<()> {
74+
fn get_graph_label<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> String {
19275
let def_id = body.source.def_id();
76+
let mut label: Vec<String> = Vec::new();
19377

194-
write!(w, " label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
78+
label.push(format!("fn {}(", dot::escape_html(&tcx.def_path_str(def_id))));
19579

19680
// fn argument types.
19781
for (i, arg) in body.args_iter().enumerate() {
19882
if i > 0 {
199-
write!(w, ", ")?;
83+
label.push(", ".to_owned());
20084
}
201-
write!(w, "{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty))?;
85+
label.push(format!("{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty)));
20286
}
20387

204-
write!(w, ") -&gt; {}", escape(&body.return_ty()))?;
205-
write!(w, r#"<br align="left"/>"#)?;
88+
label.push(format!(") -&gt; {}", escape(&body.return_ty())));
89+
label.push(r#"<br align="left"/>"#.to_owned());
20690

20791
for local in body.vars_and_temps_iter() {
20892
let decl = &body.local_decls[local];
20993

210-
write!(w, "let ")?;
94+
label.push("let ".to_owned());
21195
if decl.mutability == Mutability::Mut {
212-
write!(w, "mut ")?;
96+
label.push("mut ".to_owned());
21397
}
21498

215-
write!(w, r#"{:?}: {};<br align="left"/>"#, Place::from(local), escape(&decl.ty))?;
99+
label.push(format!(r#"{:?}: {};<br align="left"/>"#, Place::from(local), escape(&decl.ty)));
216100
}
217101

218102
for var_debug_info in &body.var_debug_info {
219-
write!(
220-
w,
103+
label.push(format!(
221104
r#"debug {} =&gt; {};<br align="left"/>"#,
222105
var_debug_info.name,
223106
escape(&var_debug_info.place)
224-
)?;
107+
));
225108
}
226-
227-
writeln!(w, ">;")
228-
}
229-
230-
fn node(def_id: DefId, block: BasicBlock) -> String {
231-
format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
109+
label.join("")
232110
}
233111

234112
fn escape<T: Debug>(t: &T) -> String {

compiler/rustc_mir/src/util/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ mod alignment;
88
pub mod collect_writes;
99
mod find_self_call;
1010
pub(crate) mod generic_graphviz;
11+
mod generic_graph;
1112
mod graphviz;
1213
pub(crate) mod pretty;
1314
pub(crate) mod spanview;
1415

1516
pub use self::aggregate::expand_aggregate;
1617
pub use self::alignment::is_disaligned;
1718
pub use self::find_self_call::find_self_call;
18-
pub use self::graphviz::write_node_label as write_graphviz_node_label;
19-
pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz};
19+
pub use self::generic_graph::graphviz_safe_def_name;
20+
pub use self::graphviz::write_mir_graphviz;
2021
pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere};

src/tools/tidy/src/deps.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
104104
"getopts",
105105
"getrandom",
106106
"gimli",
107+
"gsgdt",
107108
"hashbrown",
108109
"hermit-abi",
109110
"humantime",

0 commit comments

Comments
 (0)