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