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.
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.
11 //! Hook into libgraphviz for rendering dataflow graphs for MIR.
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;
26 use std::io::prelude::*;
27 use std::marker::PhantomData;
31 use super::super::MirBorrowckCtxtPreDataflow;
32 use super::{BitDenotation, DataflowState};
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.
39 let bits_per_block = self.operator.bits_per_block();
40 let usize_bits: usize = mem::size_of::<usize>() * 8;
42 for (word_index, &word) in words.words().iter().enumerate() {
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 {
59 f(O::Idx::new(bit_index));
67 pub fn interpret_set<'c, P>(&self,
69 words: &IdxSet<O::Idx>,
72 where P: Fn(&O, O::Idx) -> &Debug
74 let mut v = Vec::new();
75 self.each_bit(words, |i| {
76 v.push(render_idx(o, i));
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>;
89 impl<'a, 'tcx: 'a, BD> MirWithFlowState<'tcx> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
90 where 'tcx: 'a, BD: BitDenotation
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 }
98 struct Graph<'a, 'tcx, MWF:'a, P> where
99 MWF: MirWithFlowState<'tcx>
102 phantom: PhantomData<&'tcx ()>,
106 pub fn print_borrowck_graph_to<'a, 'tcx, BD, P>(
107 mbcx: &MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>,
111 where BD: BitDenotation,
112 P: Fn(&BD, BD::Idx) -> &Debug
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))
122 pub type Node = BasicBlock;
124 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
125 pub struct Edge { source: BasicBlock, index: usize }
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()
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,
138 fn graph_id(&self) -> dot::Id {
139 dot::Id::new(format!("graph_for_node_{}",
140 self.mbcx.node_id()))
144 fn node_id(&self, n: &Node) -> dot::Id {
145 dot::Id::new(format!("bb_{}", n.index()))
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.
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.
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):
163 // ------ ----------------------- ------------ --------------------
165 // [e8, e9] "= ENTRY:" <ENTRY-BITS>
166 // ------ ----------------------- ------------ --------------------
177 // ------ ----------------------- ------------ --------------------
178 // [g1, g4, g5] "= GEN:" <GEN-BITS>
179 // ------ ----------------------- ------------ --------------------
180 // "KILL:" <KILL-BITS> "=" [k1, k3, k8]
182 // ------ ----------------------- ------------ --------------------
184 // (In addition, the added dataflow is rendered with a colored
185 // background just so it will stand out compared to the
187 let mut v = Vec::new();
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],
198 // This function may emit a sequence of <tr>'s, but it
199 // always finishes with an (unfinished)
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) {
207 // if not the first row, finish off the previous row
208 write!(w, "</td><td></td><td></td></tr>")?;
210 write!(w, "<tr><td></td><td {bg} {align}>{objs:?}",
217 write!(w, "<tr><td></td><td {bg} {align}>[]",
219 align = ALIGN_RIGHT)?;
223 mir_util::write_graphviz_node_label(
224 *n, self.mbcx.mir(), &mut v, 4,
226 let flow = self.mbcx.flow_state();
227 let entry_interp = flow.interpret_set(&flow.operator,
228 flow.sets.on_entry_set_for(i),
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>\
238 face = FACE_MONOSPACE,
239 entrybits=bits_to_string(entry.words(), bits_per_block))
242 let flow = self.mbcx.flow_state();
244 flow.interpret_set(&flow.operator, flow.sets.gen_set_for(i), &self.render_idx);
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();
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>\
256 face = FACE_MONOSPACE,
257 genbits=bits_to_string(gen.words(), bits_per_block))?;
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>",
268 face = FACE_MONOSPACE,
269 killbits=bits_to_string(kill.words(), bits_per_block))?;
272 // (chunked_present_right)
273 let mut seen_one = false;
274 for k in kill_interp.chunks(chunk_size) {
276 // continuation of row; this is fourth <td>
277 write!(w, "<td {bg}>= {kill:?}</td></tr>",
281 // new row, with indent of three <td>'s
282 write!(w, "<tr><td></td><td></td><td></td><td {bg}>{kill:?}</td></tr>",
289 write!(w, "<td {bg}>= []</td></tr>",
290 bg = BG_FLOWCONTENT)?;
296 dot::LabelText::html(String::from_utf8(v).unwrap())
299 fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> {
300 Some(dot::LabelText::label("none"))
304 impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P>
305 where MWF: MirWithFlowState<'tcx>
309 fn nodes(&self) -> dot::Nodes<Node> {
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());
330 fn source(&self, edge: &Edge) -> Node {
334 fn target(&self, edge: &Edge) -> Node {
335 let mir = self.mbcx.mir();
336 mir[edge.source].terminator().successors()[edge.index]