]>
Commit | Line | Data |
---|---|---|
9cc50fc6 SL |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | use dot; | |
12 | use rustc::mir::repr::*; | |
54a0048b | 13 | use rustc::ty; |
9cc50fc6 SL |
14 | use std::fmt::Debug; |
15 | use std::io::{self, Write}; | |
54a0048b | 16 | use syntax::ast::NodeId; |
9cc50fc6 | 17 | |
54a0048b SL |
18 | /// Write a graphviz DOT graph of a list of MIRs. |
19 | pub fn write_mir_graphviz<'a, 't, W, I>(tcx: &ty::TyCtxt<'t>, iter: I, w: &mut W) -> io::Result<()> | |
20 | where W: Write, I: Iterator<Item=(&'a NodeId, &'a Mir<'a>)> { | |
21 | for (&nodeid, mir) in iter { | |
22 | writeln!(w, "digraph Mir_{} {{", nodeid)?; | |
9cc50fc6 | 23 | |
54a0048b SL |
24 | // Global graph properties |
25 | writeln!(w, r#" graph [fontname="monospace"];"#)?; | |
26 | writeln!(w, r#" node [fontname="monospace"];"#)?; | |
27 | writeln!(w, r#" edge [fontname="monospace"];"#)?; | |
9cc50fc6 | 28 | |
54a0048b SL |
29 | // Graph label |
30 | write_graph_label(tcx, nodeid, mir, w)?; | |
9cc50fc6 | 31 | |
54a0048b SL |
32 | // Nodes |
33 | for block in mir.all_basic_blocks() { | |
34 | write_node(block, mir, w)?; | |
35 | } | |
9cc50fc6 | 36 | |
54a0048b SL |
37 | // Edges |
38 | for source in mir.all_basic_blocks() { | |
39 | write_edges(source, mir, w)?; | |
40 | } | |
41 | writeln!(w, "}}")? | |
9cc50fc6 | 42 | } |
54a0048b | 43 | Ok(()) |
9cc50fc6 SL |
44 | } |
45 | ||
54a0048b SL |
46 | /// Write a graphviz HTML-styled label for the given basic block, with |
47 | /// all necessary escaping already performed. (This is suitable for | |
48 | /// emitting directly, as is done in this module, or for use with the | |
49 | /// LabelText::HtmlStr from libgraphviz.) | |
50 | /// | |
51 | /// `init` and `fini` are callbacks for emitting additional rows of | |
52 | /// data (using HTML enclosed with `<tr>` in the emitted text). | |
53 | pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock, | |
54 | mir: &Mir, | |
55 | w: &mut W, | |
56 | num_cols: u32, | |
57 | init: INIT, | |
58 | fini: FINI) -> io::Result<()> | |
59 | where INIT: Fn(&mut W) -> io::Result<()>, | |
60 | FINI: Fn(&mut W) -> io::Result<()> | |
61 | { | |
9cc50fc6 SL |
62 | let data = mir.basic_block_data(block); |
63 | ||
54a0048b | 64 | write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?; |
9cc50fc6 SL |
65 | |
66 | // Basic block number at the top. | |
54a0048b SL |
67 | write!(w, r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#, |
68 | attrs=r#"bgcolor="gray" align="center""#, | |
69 | colspan=num_cols, | |
70 | blk=block.index())?; | |
71 | ||
72 | init(w)?; | |
9cc50fc6 SL |
73 | |
74 | // List of statements in the middle. | |
75 | if !data.statements.is_empty() { | |
54a0048b | 76 | write!(w, r#"<tr><td align="left" balign="left">"#)?; |
9cc50fc6 | 77 | for statement in &data.statements { |
54a0048b | 78 | write!(w, "{}<br/>", escape(statement))?; |
9cc50fc6 | 79 | } |
54a0048b | 80 | write!(w, "</td></tr>")?; |
9cc50fc6 SL |
81 | } |
82 | ||
83 | // Terminator head at the bottom, not including the list of successor blocks. Those will be | |
84 | // displayed as labels on the edges between blocks. | |
85 | let mut terminator_head = String::new(); | |
54a0048b SL |
86 | data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); |
87 | write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?; | |
88 | ||
89 | fini(w)?; | |
90 | ||
91 | // Close the table | |
92 | writeln!(w, "</table>") | |
93 | } | |
9cc50fc6 | 94 | |
54a0048b SL |
95 | /// Write a graphviz DOT node for the given basic block. |
96 | fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { | |
97 | // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. | |
98 | write!(w, r#" {} [shape="none", label=<"#, node(block))?; | |
99 | write_node_label(block, mir, w, 1, |_| Ok(()), |_| Ok(()))?; | |
100 | // Close the node label and the node itself. | |
101 | writeln!(w, ">];") | |
9cc50fc6 SL |
102 | } |
103 | ||
104 | /// Write graphviz DOT edges with labels between the given basic block and all of its successors. | |
105 | fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { | |
106 | let terminator = &mir.basic_block_data(source).terminator(); | |
54a0048b | 107 | let labels = terminator.kind.fmt_successor_labels(); |
9cc50fc6 SL |
108 | |
109 | for (&target, label) in terminator.successors().iter().zip(labels) { | |
54a0048b | 110 | writeln!(w, r#" {} -> {} [label="{}"];"#, node(source), node(target), label)?; |
9cc50fc6 SL |
111 | } |
112 | ||
113 | Ok(()) | |
114 | } | |
115 | ||
116 | /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that | |
117 | /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of | |
118 | /// all the variables and temporaries. | |
54a0048b SL |
119 | fn write_graph_label<W: Write>(tcx: &ty::TyCtxt, nid: NodeId, mir: &Mir, w: &mut W) |
120 | -> io::Result<()> { | |
121 | write!(w, " label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?; | |
9cc50fc6 SL |
122 | |
123 | // fn argument types. | |
124 | for (i, arg) in mir.arg_decls.iter().enumerate() { | |
125 | if i > 0 { | |
54a0048b | 126 | write!(w, ", ")?; |
9cc50fc6 | 127 | } |
54a0048b | 128 | write!(w, "{:?}: {}", Lvalue::Arg(i as u32), escape(&arg.ty))?; |
9cc50fc6 SL |
129 | } |
130 | ||
54a0048b | 131 | write!(w, ") -> ")?; |
9cc50fc6 SL |
132 | |
133 | // fn return type. | |
134 | match mir.return_ty { | |
54a0048b SL |
135 | ty::FnOutput::FnConverging(ty) => write!(w, "{}", escape(ty))?, |
136 | ty::FnOutput::FnDiverging => write!(w, "!")?, | |
9cc50fc6 SL |
137 | } |
138 | ||
54a0048b | 139 | write!(w, r#"<br align="left"/>"#)?; |
9cc50fc6 SL |
140 | |
141 | // User variable types (including the user's name in a comment). | |
142 | for (i, var) in mir.var_decls.iter().enumerate() { | |
54a0048b | 143 | write!(w, "let ")?; |
9cc50fc6 | 144 | if var.mutability == Mutability::Mut { |
54a0048b | 145 | write!(w, "mut ")?; |
9cc50fc6 | 146 | } |
54a0048b SL |
147 | write!(w, r#"{:?}: {}; // {}<br align="left"/>"#, |
148 | Lvalue::Var(i as u32), escape(&var.ty), var.name)?; | |
9cc50fc6 SL |
149 | } |
150 | ||
151 | // Compiler-introduced temporary types. | |
152 | for (i, temp) in mir.temp_decls.iter().enumerate() { | |
54a0048b SL |
153 | write!(w, r#"let mut {:?}: {};<br align="left"/>"#, |
154 | Lvalue::Temp(i as u32), escape(&temp.ty))?; | |
9cc50fc6 SL |
155 | } |
156 | ||
157 | writeln!(w, ">;") | |
158 | } | |
159 | ||
160 | fn node(block: BasicBlock) -> String { | |
161 | format!("bb{}", block.index()) | |
162 | } | |
163 | ||
164 | fn escape<T: Debug>(t: &T) -> String { | |
165 | dot::escape_html(&format!("{:?}", t)) | |
166 | } |