]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/graphviz.rs
Rollup merge of #35558 - lukehinds:master, r=nikomatsakis
[rust.git] / src / librustc_mir / graphviz.rs
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::hir::def_id::DefId;
13 use rustc::mir::repr::*;
14 use rustc::mir::mir_map::MirMap;
15 use rustc::ty::{self, TyCtxt};
16 use std::fmt::Debug;
17 use std::io::{self, Write};
18 use syntax::ast::NodeId;
19
20 use rustc_data_structures::indexed_vec::Idx;
21
22 /// Write a graphviz DOT graph of a list of MIRs.
23 pub fn write_mir_graphviz<'a, 'b, 'tcx, W, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
24                                               iter: I,
25                                               mir_map: &MirMap<'tcx>,
26                                               w: &mut W)
27                                               -> io::Result<()>
28     where W: Write, I: Iterator<Item=DefId>
29 {
30     for def_id in iter {
31         let nodeid = tcx.map.as_local_node_id(def_id).unwrap();
32         let mir = &mir_map.map[&def_id];
33
34         writeln!(w, "digraph Mir_{} {{", nodeid)?;
35
36         // Global graph properties
37         writeln!(w, r#"    graph [fontname="monospace"];"#)?;
38         writeln!(w, r#"    node [fontname="monospace"];"#)?;
39         writeln!(w, r#"    edge [fontname="monospace"];"#)?;
40
41         // Graph label
42         write_graph_label(tcx, nodeid, mir, w)?;
43
44         // Nodes
45         for (block, _) in mir.basic_blocks().iter_enumerated() {
46             write_node(block, mir, w)?;
47         }
48
49         // Edges
50         for (source, _) in mir.basic_blocks().iter_enumerated() {
51             write_edges(source, mir, w)?;
52         }
53         writeln!(w, "}}")?
54     }
55     Ok(())
56 }
57
58 /// Write a graphviz HTML-styled label for the given basic block, with
59 /// all necessary escaping already performed. (This is suitable for
60 /// emitting directly, as is done in this module, or for use with the
61 /// LabelText::HtmlStr from libgraphviz.)
62 ///
63 /// `init` and `fini` are callbacks for emitting additional rows of
64 /// data (using HTML enclosed with `<tr>` in the emitted text).
65 pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
66                                               mir: &Mir,
67                                               w: &mut W,
68                                               num_cols: u32,
69                                               init: INIT,
70                                               fini: FINI) -> io::Result<()>
71     where INIT: Fn(&mut W) -> io::Result<()>,
72           FINI: Fn(&mut W) -> io::Result<()>
73 {
74     let data = &mir[block];
75
76     write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
77
78     // Basic block number at the top.
79     write!(w, r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
80            attrs=r#"bgcolor="gray" align="center""#,
81            colspan=num_cols,
82            blk=block.index())?;
83
84     init(w)?;
85
86     // List of statements in the middle.
87     if !data.statements.is_empty() {
88         write!(w, r#"<tr><td align="left" balign="left">"#)?;
89         for statement in &data.statements {
90             write!(w, "{}<br/>", escape(statement))?;
91         }
92         write!(w, "</td></tr>")?;
93     }
94
95     // Terminator head at the bottom, not including the list of successor blocks. Those will be
96     // displayed as labels on the edges between blocks.
97     let mut terminator_head = String::new();
98     data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
99     write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
100
101     fini(w)?;
102
103     // Close the table
104     writeln!(w, "</table>")
105 }
106
107 /// Write a graphviz DOT node for the given basic block.
108 fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
109     // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
110     write!(w, r#"    {} [shape="none", label=<"#, node(block))?;
111     write_node_label(block, mir, w, 1, |_| Ok(()), |_| Ok(()))?;
112     // Close the node label and the node itself.
113     writeln!(w, ">];")
114 }
115
116 /// Write graphviz DOT edges with labels between the given basic block and all of its successors.
117 fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
118     let terminator = mir[source].terminator();
119     let labels = terminator.kind.fmt_successor_labels();
120
121     for (&target, label) in terminator.successors().iter().zip(labels) {
122         writeln!(w, r#"    {} -> {} [label="{}"];"#, node(source), node(target), label)?;
123     }
124
125     Ok(())
126 }
127
128 /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
129 /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
130 /// all the variables and temporaries.
131 fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
132                                          nid: NodeId,
133                                          mir: &Mir,
134                                          w: &mut W)
135                                          -> io::Result<()> {
136     write!(w, "    label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?;
137
138     // fn argument types.
139     for (i, arg) in mir.arg_decls.iter().enumerate() {
140         if i > 0 {
141             write!(w, ", ")?;
142         }
143         write!(w, "{:?}: {}", Lvalue::Arg(Arg::new(i)), escape(&arg.ty))?;
144     }
145
146     write!(w, ") -&gt; ")?;
147
148     // fn return type.
149     match mir.return_ty {
150         ty::FnOutput::FnConverging(ty) => write!(w, "{}", escape(ty))?,
151         ty::FnOutput::FnDiverging => write!(w, "!")?,
152     }
153
154     write!(w, r#"<br align="left"/>"#)?;
155
156     // User variable types (including the user's name in a comment).
157     for (i, var) in mir.var_decls.iter().enumerate() {
158         write!(w, "let ")?;
159         if var.mutability == Mutability::Mut {
160             write!(w, "mut ")?;
161         }
162         write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
163                Lvalue::Var(Var::new(i)), escape(&var.ty), var.name)?;
164     }
165
166     // Compiler-introduced temporary types.
167     for (i, temp) in mir.temp_decls.iter().enumerate() {
168         write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
169                Lvalue::Temp(Temp::new(i)), escape(&temp.ty))?;
170     }
171
172     writeln!(w, ">;")
173 }
174
175 fn node(block: BasicBlock) -> String {
176     format!("bb{}", block.index())
177 }
178
179 fn escape<T: Debug>(t: &T) -> String {
180     dot::escape_html(&format!("{:?}", t))
181 }