]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/graphviz.rs
Rollup merge of #67094 - RalfJung:fields, r=Mark-Simulacrum
[rust.git] / src / librustc_mir / util / graphviz.rs
1 use rustc::hir::def_id::DefId;
2 use rustc::mir::*;
3 use rustc::ty::TyCtxt;
4 use rustc_index::vec::Idx;
5 use std::fmt::Debug;
6 use std::io::{self, Write};
7
8 use super::pretty::dump_mir_def_ids;
9
10 /// Write a graphviz DOT graph of a list of MIRs.
11 pub fn write_mir_graphviz<W>(
12     tcx: TyCtxt<'_>,
13     single: Option<DefId>,
14     w: &mut W,
15 ) -> io::Result<()>
16 where
17     W: Write,
18 {
19     let def_ids = dump_mir_def_ids(tcx, single);
20
21     let use_subgraphs = def_ids.len() > 1;
22     if use_subgraphs {
23         writeln!(w, "digraph __crate__ {{")?;
24     }
25
26     for def_id in def_ids {
27         let body = &tcx.optimized_mir(def_id);
28         write_mir_fn_graphviz(tcx, def_id, body, use_subgraphs, w)?;
29     }
30
31     if use_subgraphs {
32         writeln!(w, "}}")?;
33     }
34
35     Ok(())
36 }
37
38 // Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
39 // it does not have to be user friendly.
40 pub fn graphviz_safe_def_name(def_id: DefId) -> String {
41     format!(
42         "{}_{}",
43         def_id.krate.index(),
44         def_id.index.index(),
45     )
46 }
47
48 /// Write a graphviz DOT graph of the MIR.
49 pub fn write_mir_fn_graphviz<'tcx, W>(
50     tcx: TyCtxt<'tcx>,
51     def_id: DefId,
52     body: &Body<'_>,
53     subgraph: bool,
54     w: &mut W,
55 ) -> io::Result<()>
56 where
57     W: Write,
58 {
59     let kind = if subgraph { "subgraph" } else { "digraph" };
60     let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR
61     let def_name = graphviz_safe_def_name(def_id);
62     writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;
63
64     // Global graph properties
65     writeln!(w, r#"    graph [fontname="monospace"];"#)?;
66     writeln!(w, r#"    node [fontname="monospace"];"#)?;
67     writeln!(w, r#"    edge [fontname="monospace"];"#)?;
68
69     // Graph label
70     write_graph_label(tcx, def_id, body, w)?;
71
72     // Nodes
73     for (block, _) in body.basic_blocks().iter_enumerated() {
74         write_node(def_id, block, body, w)?;
75     }
76
77     // Edges
78     for (source, _) in body.basic_blocks().iter_enumerated() {
79         write_edges(def_id, source, body, w)?;
80     }
81     writeln!(w, "}}")
82 }
83
84 /// Write a graphviz HTML-styled label for the given basic block, with
85 /// all necessary escaping already performed. (This is suitable for
86 /// emitting directly, as is done in this module, or for use with the
87 /// LabelText::HtmlStr from libgraphviz.)
88 ///
89 /// `init` and `fini` are callbacks for emitting additional rows of
90 /// data (using HTML enclosed with `<tr>` in the emitted text).
91 pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
92                                               body: &Body<'_>,
93                                               w: &mut W,
94                                               num_cols: u32,
95                                               init: INIT,
96                                               fini: FINI) -> io::Result<()>
97     where INIT: Fn(&mut W) -> io::Result<()>,
98           FINI: Fn(&mut W) -> io::Result<()>
99 {
100     let data = &body[block];
101
102     write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
103
104     // Basic block number at the top.
105     write!(w, r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
106            attrs=r#"bgcolor="gray" align="center""#,
107            colspan=num_cols,
108            blk=block.index())?;
109
110     init(w)?;
111
112     // List of statements in the middle.
113     if !data.statements.is_empty() {
114         write!(w, r#"<tr><td align="left" balign="left">"#)?;
115         for statement in &data.statements {
116             write!(w, "{}<br/>", escape(statement))?;
117         }
118         write!(w, "</td></tr>")?;
119     }
120
121     // Terminator head at the bottom, not including the list of successor blocks. Those will be
122     // displayed as labels on the edges between blocks.
123     let mut terminator_head = String::new();
124     data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
125     write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
126
127     fini(w)?;
128
129     // Close the table
130     write!(w, "</table>")
131 }
132
133 /// Write a graphviz DOT node for the given basic block.
134 fn write_node<W: Write>(
135     def_id: DefId,
136     block: BasicBlock,
137     body: &Body<'_>,
138     w: &mut W,
139 ) -> io::Result<()> {
140     // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
141     write!(w, r#"    {} [shape="none", label=<"#, node(def_id, block))?;
142     write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?;
143     // Close the node label and the node itself.
144     writeln!(w, ">];")
145 }
146
147 /// Write graphviz DOT edges with labels between the given basic block and all of its successors.
148 fn write_edges<W: Write>(
149     def_id: DefId,
150     source: BasicBlock,
151     body: &Body<'_>,
152     w: &mut W,
153 ) -> io::Result<()> {
154     let terminator = body[source].terminator();
155     let labels = terminator.kind.fmt_successor_labels();
156
157     for (&target, label) in terminator.successors().zip(labels) {
158         let src = node(def_id, source);
159         let trg = node(def_id, target);
160         writeln!(w, r#"    {} -> {} [label="{}"];"#, src, trg, label)?;
161     }
162
163     Ok(())
164 }
165
166 /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
167 /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
168 /// all the variables and temporaries.
169 fn write_graph_label<'tcx, W: Write>(
170     tcx: TyCtxt<'tcx>,
171     def_id: DefId,
172     body: &Body<'_>,
173     w: &mut W,
174 ) -> io::Result<()> {
175     write!(w, "    label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
176
177     // fn argument types.
178     for (i, arg) in body.args_iter().enumerate() {
179         if i > 0 {
180             write!(w, ", ")?;
181         }
182         write!(w,
183                "{:?}: {}",
184                Place::from(arg),
185                escape(&body.local_decls[arg].ty)
186         )?;
187     }
188
189     write!(w, ") -&gt; {}", escape(&body.return_ty()))?;
190     write!(w, r#"<br align="left"/>"#)?;
191
192     for local in body.vars_and_temps_iter() {
193         let decl = &body.local_decls[local];
194
195         write!(w, "let ")?;
196         if decl.mutability == Mutability::Mut {
197             write!(w, "mut ")?;
198         }
199
200         write!(w, r#"{:?}: {};<br align="left"/>"#,
201                Place::from(local), escape(&decl.ty))?;
202     }
203
204     for var_debug_info in &body.var_debug_info {
205         write!(w, r#"debug {} =&gt; {};<br align="left"/>"#,
206                var_debug_info.name, escape(&var_debug_info.place))?;
207     }
208
209     writeln!(w, ">;")
210 }
211
212 fn node(def_id: DefId, block: BasicBlock) -> String {
213     format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
214 }
215
216 fn escape<T: Debug>(t: &T) -> String {
217     dot::escape_html(&format!("{:?}", t))
218 }