1 //! Hook into libgraphviz for rendering dataflow graphs for MIR.
4 use rustc::mir::{BasicBlock, Mir};
8 use std::marker::PhantomData;
11 use super::{BitDenotation, DataflowState};
12 use super::DataflowBuilder;
13 use super::DebugFormatted;
15 pub trait MirWithFlowState<'tcx> {
16 type BD: BitDenotation<'tcx>;
17 fn hir_id(&self) -> HirId;
18 fn mir(&self) -> &Mir<'tcx>;
19 fn flow_state(&self) -> &DataflowState<'tcx, Self::BD>;
22 impl<'a, 'tcx, BD> MirWithFlowState<'tcx> for DataflowBuilder<'a, 'tcx, BD>
23 where BD: BitDenotation<'tcx>
26 fn hir_id(&self) -> HirId { self.hir_id }
27 fn mir(&self) -> &Mir<'tcx> { self.flow_state.mir() }
28 fn flow_state(&self) -> &DataflowState<'tcx, Self::BD> { &self.flow_state.flow_state }
31 struct Graph<'a, 'tcx, MWF:'a, P> where
32 MWF: MirWithFlowState<'tcx>
35 phantom: PhantomData<&'tcx ()>,
39 pub(crate) fn print_borrowck_graph_to<'a, 'tcx, BD, P>(
40 mbcx: &DataflowBuilder<'a, 'tcx, BD>,
44 where BD: BitDenotation<'tcx>,
45 P: Fn(&BD, BD::Idx) -> DebugFormatted,
47 let g = Graph { mbcx, phantom: PhantomData, render_idx };
48 let mut v = Vec::new();
49 dot::render(&g, &mut v)?;
50 debug!("print_borrowck_graph_to path: {} hir_id: {}",
51 path.display(), mbcx.hir_id);
55 pub type Node = BasicBlock;
57 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
58 pub struct Edge { source: BasicBlock, index: usize }
60 fn outgoing(mir: &Mir<'_>, bb: BasicBlock) -> Vec<Edge> {
61 (0..mir[bb].terminator().successors().count())
62 .map(|index| Edge { source: bb, index: index}).collect()
65 impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P>
66 where MWF: MirWithFlowState<'tcx>,
67 P: Fn(&MWF::BD, <MWF::BD as BitDenotation<'tcx>>::Idx) -> DebugFormatted,
71 fn graph_id(&self) -> dot::Id<'_> {
72 dot::Id::new(format!("graph_for_node_{}",
77 fn node_id(&self, n: &Node) -> dot::Id<'_> {
78 dot::Id::new(format!("bb_{}", n.index()))
82 fn node_label(&self, n: &Node) -> dot::LabelText<'_> {
83 // Node label is something like this:
84 // +---------+----------------------------------+------------------+------------------+
85 // | ENTRY | MIR | GEN | KILL |
86 // +---------+----------------------------------+------------------+------------------+
87 // | | 0: StorageLive(_7) | bb3[2]: reserved | bb2[0]: reserved |
88 // | | 1: StorageLive(_8) | bb3[2]: active | bb2[0]: active |
89 // | | 2: _8 = &mut _1 | | bb4[2]: reserved |
90 // | | | | bb4[2]: active |
91 // | | | | bb9[0]: reserved |
92 // | | | | bb9[0]: active |
93 // | | | | bb10[0]: reserved|
94 // | | | | bb10[0]: active |
95 // | | | | bb11[0]: reserved|
96 // | | | | bb11[0]: active |
97 // +---------+----------------------------------+------------------+------------------+
98 // | [00-00] | _7 = const Foo::twiddle(move _8) | [0c-00] | [f3-0f] |
99 // +---------+----------------------------------+------------------+------------------+
100 let mut v = Vec::new();
101 self.node_label_internal(n, &mut v, *n, self.mbcx.mir()).unwrap();
102 dot::LabelText::html(String::from_utf8(v).unwrap())
106 fn node_shape(&self, _n: &Node) -> Option<dot::LabelText<'_>> {
107 Some(dot::LabelText::label("none"))
110 fn edge_label(&'a self, e: &Edge) -> dot::LabelText<'a> {
111 let term = self.mbcx.mir()[e.source].terminator();
112 let label = &term.kind.fmt_successor_labels()[e.index];
113 dot::LabelText::label(label.clone())
117 impl<'a, 'tcx, MWF, P> Graph<'a, 'tcx, MWF, P>
118 where MWF: MirWithFlowState<'tcx>,
119 P: Fn(&MWF::BD, <MWF::BD as BitDenotation<'tcx>>::Idx) -> DebugFormatted,
121 /// Generate the node label
122 fn node_label_internal<W: io::Write>(&self,
126 mir: &Mir<'_>) -> io::Result<()> {
128 const HDRS: [&str; 4] = ["ENTRY", "MIR", "BLOCK GENS", "BLOCK KILLS"];
129 const HDR_FMT: &str = "bgcolor=\"grey\"";
130 write!(w, "<table><tr><td rowspan=\"{}\">", HDRS.len())?;
131 write!(w, "{:?}", block.index())?;
132 write!(w, "</td></tr><tr>")?;
134 write!(w, "<td {}>{}</td>", HDR_FMT, hdr)?;
139 self.node_label_verbose_row(n, w, block, mir)?;
140 self.node_label_final_row(n, w, block, mir)?;
141 write!(w, "</table>")?;
146 /// Builds the verbose row: full MIR data, and detailed gen/kill/entry sets.
147 fn node_label_verbose_row<W: io::Write>(&self,
155 macro_rules! dump_set_for {
156 ($set:ident, $interpret:ident) => {
159 let flow = self.mbcx.flow_state();
160 let entry_interp = flow.$interpret(&flow.operator,
163 for e in &entry_interp {
164 write!(w, "{:?}<br/>", e)?;
172 dump_set_for!(on_entry_set_for, interpret_set);
177 let data = &mir[block];
178 for (i, statement) in data.statements.iter().enumerate() {
179 write!(w, "{}<br align=\"left\"/>",
180 dot::escape_html(&format!("{:3}: {:?}", i, statement)))?;
186 dump_set_for!(gen_set_for, interpret_hybrid_set);
189 dump_set_for!(kill_set_for, interpret_hybrid_set);
196 /// Builds the summary row: terminator, gen/kill/entry bit sets.
197 fn node_label_final_row<W: io::Write>(&self,
205 let flow = self.mbcx.flow_state();
210 let set = flow.sets.on_entry_set_for(i);
211 write!(w, "<td>{:?}</td>", dot::escape_html(&set.to_string()))?;
216 let data = &mir[block];
217 let mut terminator_head = String::new();
218 data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
219 write!(w, "{}", dot::escape_html(&terminator_head))?;
224 let set = flow.sets.gen_set_for(i);
225 write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", set)))?;
228 let set = flow.sets.kill_set_for(i);
229 write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", set)))?;
237 impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P>
238 where MWF: MirWithFlowState<'tcx>
242 fn nodes(&self) -> dot::Nodes<'_, Node> {
250 fn edges(&self) -> dot::Edges<'_, Edge> {
251 let mir = self.mbcx.mir();
255 .flat_map(|bb| outgoing(mir, bb))
260 fn source(&self, edge: &Edge) -> Node {
264 fn target(&self, edge: &Edge) -> Node {
265 let mir = self.mbcx.mir();
266 *mir[edge.source].terminator().successors().nth(edge.index).unwrap()