/
graphviz.rs
220 lines (186 loc) · 6.39 KB
/
graphviz.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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::pretty::dump_mir_def_ids;
/// Write a graphviz DOT graph of a list of MIRs.
pub fn write_mir_graphviz<W>(tcx: TyCtxt<'_>, single: Option<DefId>, w: &mut W) -> io::Result<()>
where
W: Write,
{
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, use_subgraphs, w)?;
}
if use_subgraphs {
writeln!(w, "}}")?;
}
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>,
def_id: DefId,
body: &Body<'_>,
subgraph: bool,
w: &mut W,
) -> io::Result<()>
where
W: Write,
{
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"];"#)?;
writeln!(w, r#" node [fontname="monospace"];"#)?;
writeln!(w, r#" edge [fontname="monospace"];"#)?;
// Graph label
write_graph_label(tcx, def_id, body, w)?;
// Nodes
for (block, _) in body.basic_blocks().iter_enumerated() {
write_node(def_id, block, body, w)?;
}
// Edges
for (source, _) in body.basic_blocks().iter_enumerated() {
write_edges(def_id, 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<'_>,
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, color) = if data.is_cleanup {
(format!("{} (cleanup)", block.index()), "lightblue")
} else {
(format!("{}", block.index()), "gray")
};
write!(
w,
r#"<tr><td bgcolor="{color}" align="center" colspan="{colspan}">{blk}</td></tr>"#,
colspan = num_cols,
blk = blk,
color = color
)?;
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>(
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(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>(
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) {
let src = node(def_id, source);
let trg = node(def_id, target);
writeln!(w, r#" {} -> {} [label="{}"];"#, src, trg, label)?;
}
Ok(())
}
/// 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>,
def_id: DefId,
body: &Body<'_>,
w: &mut W,
) -> io::Result<()> {
write!(w, " label=<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, ", ")?;
}
write!(w, "{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty))?;
}
write!(w, ") -> {}", escape(&body.return_ty()))?;
write!(w, r#"<br align="left"/>"#)?;
for local in body.vars_and_temps_iter() {
let decl = &body.local_decls[local];
write!(w, "let ")?;
if decl.mutability == Mutability::Mut {
write!(w, "mut ")?;
}
write!(w, r#"{:?}: {};<br align="left"/>"#, Place::from(local), escape(&decl.ty))?;
}
for var_debug_info in &body.var_debug_info {
write!(
w,
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))
}
fn escape<T: Debug>(t: &T) -> String {
dot::escape_html(&format!("{:?}", t))
}