]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
move the drop expansion code to rustc_mir
[rust.git] / src / librustc_borrowck / borrowck / mir / dataflow / graphviz.rs
1 // Copyright 2012-2016 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 //! Hook into libgraphviz for rendering dataflow graphs for MIR.
12
13 use syntax::ast::NodeId;
14 use rustc::mir::{BasicBlock, Mir};
15 use rustc_data_structures::bitslice::bits_to_string;
16 use rustc_data_structures::indexed_set::{IdxSet};
17 use rustc_data_structures::indexed_vec::Idx;
18 use rustc_mir::util as mir_util;
19
20 use dot;
21 use dot::IntoCow;
22
23 use std::fmt::Debug;
24 use std::fs::File;
25 use std::io;
26 use std::io::prelude::*;
27 use std::marker::PhantomData;
28 use std::mem;
29 use std::path::Path;
30
31 use super::super::MirBorrowckCtxtPreDataflow;
32 use super::{BitDenotation, DataflowState};
33
34 impl<O: BitDenotation> DataflowState<O> {
35     fn each_bit<F>(&self, words: &IdxSet<O::Idx>, mut f: F)
36         where F: FnMut(O::Idx) {
37         //! Helper for iterating over the bits in a bitvector.
38
39         let bits_per_block = self.operator.bits_per_block();
40         let usize_bits: usize = mem::size_of::<usize>() * 8;
41
42         for (word_index, &word) in words.words().iter().enumerate() {
43             if word != 0 {
44                 let base_index = word_index * usize_bits;
45                 for offset in 0..usize_bits {
46                     let bit = 1 << offset;
47                     if (word & bit) != 0 {
48                         // NB: we round up the total number of bits
49                         // that we store in any given bit set so that
50                         // it is an even multiple of usize::BITS. This
51                         // means that there may be some stray bits at
52                         // the end that do not correspond to any
53                         // actual value; that's why we first check
54                         // that we are in range of bits_per_block.
55                         let bit_index = base_index + offset as usize;
56                         if bit_index >= bits_per_block {
57                             return;
58                         } else {
59                             f(O::Idx::new(bit_index));
60                         }
61                     }
62                 }
63             }
64         }
65     }
66
67     pub fn interpret_set<'c, P>(&self,
68                                 o: &'c O,
69                                 words: &IdxSet<O::Idx>,
70                                 render_idx: &P)
71                                 -> Vec<&'c Debug>
72         where P: Fn(&O, O::Idx) -> &Debug
73     {
74         let mut v = Vec::new();
75         self.each_bit(words, |i| {
76             v.push(render_idx(o, i));
77         });
78         v
79     }
80 }
81
82 pub trait MirWithFlowState<'tcx> {
83     type BD: BitDenotation;
84     fn node_id(&self) -> NodeId;
85     fn mir(&self) -> &Mir<'tcx>;
86     fn flow_state(&self) -> &DataflowState<Self::BD>;
87 }
88
89 impl<'a, 'tcx: 'a, BD> MirWithFlowState<'tcx> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
90     where 'tcx: 'a, BD: BitDenotation
91 {
92     type BD = BD;
93     fn node_id(&self) -> NodeId { self.node_id }
94     fn mir(&self) -> &Mir<'tcx> { self.flow_state.mir() }
95     fn flow_state(&self) -> &DataflowState<Self::BD> { &self.flow_state.flow_state }
96 }
97
98 struct Graph<'a, 'tcx, MWF:'a, P> where
99     MWF: MirWithFlowState<'tcx>
100 {
101     mbcx: &'a MWF,
102     phantom: PhantomData<&'tcx ()>,
103     render_idx: P,
104 }
105
106 pub fn print_borrowck_graph_to<'a, 'tcx, BD, P>(
107     mbcx: &MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>,
108     path: &Path,
109     render_idx: P)
110     -> io::Result<()>
111     where BD: BitDenotation,
112           P: Fn(&BD, BD::Idx) -> &Debug
113 {
114     let g = Graph { mbcx: mbcx, phantom: PhantomData, render_idx: render_idx };
115     let mut v = Vec::new();
116     dot::render(&g, &mut v)?;
117     debug!("print_borrowck_graph_to path: {} node_id: {}",
118            path.display(), mbcx.node_id);
119     File::create(path).and_then(|mut f| f.write_all(&v))
120 }
121
122 pub type Node = BasicBlock;
123
124 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
125 pub struct Edge { source: BasicBlock, index: usize }
126
127 fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec<Edge> {
128     let succ_len = mir[bb].terminator().successors().len();
129     (0..succ_len).map(|index| Edge { source: bb, index: index}).collect()
130 }
131
132 impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P>
133     where MWF: MirWithFlowState<'tcx>,
134           P: for <'b> Fn(&'b MWF::BD, <MWF::BD as BitDenotation>::Idx) -> &'b Debug,
135 {
136     type Node = Node;
137     type Edge = Edge;
138     fn graph_id(&self) -> dot::Id {
139         dot::Id::new(format!("graph_for_node_{}",
140                              self.mbcx.node_id()))
141             .unwrap()
142     }
143
144     fn node_id(&self, n: &Node) -> dot::Id {
145         dot::Id::new(format!("bb_{}", n.index()))
146             .unwrap()
147     }
148
149     fn node_label(&self, n: &Node) -> dot::LabelText {
150         // A standard MIR label, as generated by write_node_label, is
151         // presented in a single column in a table.
152         //
153         // The code below does a bunch of formatting work to format a
154         // node (i.e. MIR basic-block) label with extra
155         // dataflow-enriched information.  In particular, the goal is
156         // to add extra columns that present the three dataflow
157         // bitvectors, and the data those bitvectors represent.
158         //
159         // It presents it in the following format (where I am
160         // presenting the table rendering via ASCII art, one line per
161         // row of the table, and a chunk size of 3 rather than 5):
162         //
163         // ------  -----------------------  ------------  --------------------
164         //                    [e1, e3, e4]
165         //             [e8, e9] "= ENTRY:"  <ENTRY-BITS>
166         // ------  -----------------------  ------------  --------------------
167         // Left
168         // Most
169         // Column
170         // Is
171         // Just
172         // Normal
173         // Series
174         // Of
175         // MIR
176         // Stmts
177         // ------  -----------------------  ------------  --------------------
178         //           [g1, g4, g5] "= GEN:"  <GEN-BITS>
179         // ------  -----------------------  ------------  --------------------
180         //                         "KILL:"  <KILL-BITS>   "=" [k1, k3, k8]
181         //                                                [k9]
182         // ------  -----------------------  ------------  --------------------
183         //
184         // (In addition, the added dataflow is rendered with a colored
185         // background just so it will stand out compared to the
186         // statements.)
187         let mut v = Vec::new();
188         let i = n.index();
189         let chunk_size = 5;
190         const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#;
191         const ALIGN_RIGHT: &'static str = r#"align="right""#;
192         const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#;
193         fn chunked_present_left<W:io::Write>(w: &mut W,
194                                              interpreted: &[&Debug],
195                                              chunk_size: usize)
196                                              -> io::Result<()>
197         {
198             // This function may emit a sequence of <tr>'s, but it
199             // always finishes with an (unfinished)
200             // <tr><td></td><td>
201             //
202             // Thus, after being called, one should finish both the
203             // pending <td> as well as the <tr> itself.
204             let mut seen_one = false;
205             for c in interpreted.chunks(chunk_size) {
206                 if seen_one {
207                     // if not the first row, finish off the previous row
208                     write!(w, "</td><td></td><td></td></tr>")?;
209                 }
210                 write!(w, "<tr><td></td><td {bg} {align}>{objs:?}",
211                        bg = BG_FLOWCONTENT,
212                        align = ALIGN_RIGHT,
213                        objs = c)?;
214                 seen_one = true;
215             }
216             if !seen_one {
217                 write!(w, "<tr><td></td><td {bg} {align}>[]",
218                        bg = BG_FLOWCONTENT,
219                        align = ALIGN_RIGHT)?;
220             }
221             Ok(())
222         }
223         mir_util::write_graphviz_node_label(
224             *n, self.mbcx.mir(), &mut v, 4,
225             |w| {
226                 let flow = self.mbcx.flow_state();
227                 let entry_interp = flow.interpret_set(&flow.operator,
228                                                       flow.sets.on_entry_set_for(i),
229                                                       &self.render_idx);
230                 chunked_present_left(w, &entry_interp[..], chunk_size)?;
231                 let bits_per_block = flow.sets.bits_per_block();
232                 let entry = flow.sets.on_entry_set_for(i);
233                 debug!("entry set for i={i} bits_per_block: {bpb} entry: {e:?} interp: {ei:?}",
234                        i=i, e=entry, bpb=bits_per_block, ei=entry_interp);
235                 write!(w, "= ENTRY:</td><td {bg}><FONT {face}>{entrybits:?}</FONT></td>\
236                                         <td></td></tr>",
237                        bg = BG_FLOWCONTENT,
238                        face = FACE_MONOSPACE,
239                        entrybits=bits_to_string(entry.words(), bits_per_block))
240             },
241             |w| {
242                 let flow = self.mbcx.flow_state();
243                 let gen_interp =
244                     flow.interpret_set(&flow.operator, flow.sets.gen_set_for(i), &self.render_idx);
245                 let kill_interp =
246                     flow.interpret_set(&flow.operator, flow.sets.kill_set_for(i), &self.render_idx);
247                 chunked_present_left(w, &gen_interp[..], chunk_size)?;
248                 let bits_per_block = flow.sets.bits_per_block();
249                 {
250                     let gen = flow.sets.gen_set_for(i);
251                     debug!("gen set for i={i} bits_per_block: {bpb} gen: {g:?} interp: {gi:?}",
252                            i=i, g=gen, bpb=bits_per_block, gi=gen_interp);
253                     write!(w, " = GEN:</td><td {bg}><FONT {face}>{genbits:?}</FONT></td>\
254                                            <td></td></tr>",
255                            bg = BG_FLOWCONTENT,
256                            face = FACE_MONOSPACE,
257                            genbits=bits_to_string(gen.words(), bits_per_block))?;
258                 }
259
260                 {
261                     let kill = flow.sets.kill_set_for(i);
262                     debug!("kill set for i={i} bits_per_block: {bpb} kill: {k:?} interp: {ki:?}",
263                            i=i, k=kill, bpb=bits_per_block, ki=kill_interp);
264                     write!(w, "<tr><td></td><td {bg} {align}>KILL:</td>\
265                                             <td {bg}><FONT {face}>{killbits:?}</FONT></td>",
266                            bg = BG_FLOWCONTENT,
267                            align = ALIGN_RIGHT,
268                            face = FACE_MONOSPACE,
269                            killbits=bits_to_string(kill.words(), bits_per_block))?;
270                 }
271
272                 // (chunked_present_right)
273                 let mut seen_one = false;
274                 for k in kill_interp.chunks(chunk_size) {
275                     if !seen_one {
276                         // continuation of row; this is fourth <td>
277                         write!(w, "<td {bg}>= {kill:?}</td></tr>",
278                                bg = BG_FLOWCONTENT,
279                                kill=k)?;
280                     } else {
281                         // new row, with indent of three <td>'s
282                         write!(w, "<tr><td></td><td></td><td></td><td {bg}>{kill:?}</td></tr>",
283                                bg = BG_FLOWCONTENT,
284                                kill=k)?;
285                     }
286                     seen_one = true;
287                 }
288                 if !seen_one {
289                     write!(w, "<td {bg}>= []</td></tr>",
290                            bg = BG_FLOWCONTENT)?;
291                 }
292
293                 Ok(())
294             })
295             .unwrap();
296         dot::LabelText::html(String::from_utf8(v).unwrap())
297     }
298
299     fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> {
300         Some(dot::LabelText::label("none"))
301     }
302 }
303
304 impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P>
305     where MWF: MirWithFlowState<'tcx>
306 {
307     type Node = Node;
308     type Edge = Edge;
309     fn nodes(&self) -> dot::Nodes<Node> {
310         self.mbcx.mir()
311             .basic_blocks()
312             .indices()
313             .collect::<Vec<_>>()
314             .into_cow()
315     }
316
317     fn edges(&self) -> dot::Edges<Edge> {
318         let mir = self.mbcx.mir();
319         // base initial capacity on assumption every block has at
320         // least one outgoing edge (Which should be true for all
321         // blocks but one, the exit-block).
322         let mut edges = Vec::with_capacity(mir.basic_blocks().len());
323         for bb in mir.basic_blocks().indices() {
324             let outgoing = outgoing(mir, bb);
325             edges.extend(outgoing.into_iter());
326         }
327         edges.into_cow()
328     }
329
330     fn source(&self, edge: &Edge) -> Node {
331         edge.source
332     }
333
334     fn target(&self, edge: &Edge) -> Node {
335         let mir = self.mbcx.mir();
336         mir[edge.source].terminator().successors()[edge.index]
337     }
338 }