diff options
| author | bobtwinkles <srkoser+github@gmail.com> | 2018-03-03 17:44:06 -0500 |
|---|---|---|
| committer | bobtwinkles <srkoser+github@gmail.com> | 2018-03-09 13:53:35 -0500 |
| commit | 047bec69b9ec54d400b1e255c8757bff8a5a854d (patch) | |
| tree | 08b1913ea9a61727a5b1cbd4ef271825a96f737e | |
| parent | 138365368a71a04ff9539704f478b014379b7ee5 (diff) | |
| download | rust-047bec69b9ec54d400b1e255c8757bff8a5a854d.tar.gz rust-047bec69b9ec54d400b1e255c8757bff8a5a854d.zip | |
mir dataflow: change graphviz output
The new output format is perhaps a little more readable. As a bonus, we get labels on the outgoing edges to more easily corroborate the dataflow with the plain MIR graphviz output.
| -rw-r--r-- | src/libgraphviz/lib.rs | 6 | ||||
| -rw-r--r-- | src/librustc_mir/dataflow/graphviz.rs | 285 |
2 files changed, 149 insertions, 142 deletions
diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index cd893b9784a..d8c366d2413 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -711,6 +711,12 @@ impl<'a> IntoCow<'a, str> for &'a str { } } +impl<'a> IntoCow<'a, str> for Cow<'a, str> { + fn into_cow(self) -> Cow<'a, str> { + self + } +} + impl<'a, T: Clone> IntoCow<'a, [T]> for Vec<T> { fn into_cow(self) -> Cow<'a, [T]> { Cow::Owned(self) diff --git a/src/librustc_mir/dataflow/graphviz.rs b/src/librustc_mir/dataflow/graphviz.rs index fb3cb1518cb..0305e4c93b6 100644 --- a/src/librustc_mir/dataflow/graphviz.rs +++ b/src/librustc_mir/dataflow/graphviz.rs @@ -20,12 +20,9 @@ use dot::IntoCow; use std::fs; use std::io; -use std::io::prelude::*; use std::marker::PhantomData; use std::path::Path; -use util; - use super::{BitDenotation, DataflowState}; use super::DataflowBuilder; use super::DebugFormatted; @@ -98,157 +95,161 @@ impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P> } fn node_label(&self, n: &Node) -> dot::LabelText { - // A standard MIR label, as generated by write_node_label, is - // presented in a single column in a table. - // - // The code below does a bunch of formatting work to format a - // node (i.e. MIR basic-block) label with extra - // dataflow-enriched information. In particular, the goal is - // to add extra columns that present the three dataflow - // bitvectors, and the data those bitvectors represent. - // - // It presents it in the following format (where I am - // presenting the table rendering via ASCII art, one line per - // row of the table, and a chunk size of 3 rather than 5): - // - // ------ ----------------------- ------------ -------------------- - // [e1, e3, e4] - // [e8, e9] "= ENTRY:" <ENTRY-BITS> - // ------ ----------------------- ------------ -------------------- - // Left - // Most - // Column - // Is - // Just - // Normal - // Series - // Of - // MIR - // Stmts - // ------ ----------------------- ------------ -------------------- - // [g1, g4, g5] "= GEN:" <GEN-BITS> - // ------ ----------------------- ------------ -------------------- - // "KILL:" <KILL-BITS> "=" [k1, k3, k8] - // [k9] - // ------ ----------------------- ------------ -------------------- - // - // (In addition, the added dataflow is rendered with a colored - // background just so it will stand out compared to the - // statements.) + // Node label is something like this: + // +---------+----------------------------------+------------------+------------------+ + // | ENTRY | MIR | GEN | KILL | + // +---------+----------------------------------+------------------+------------------+ + // | | 0: StorageLive(_7) | bb3[2]: reserved | bb2[0]: reserved | + // | | 1: StorageLive(_8) | bb3[2]: active | bb2[0]: active | + // | | 2: _8 = &mut _1 | | bb4[2]: reserved | + // | | | | bb4[2]: active | + // | | | | bb9[0]: reserved | + // | | | | bb9[0]: active | + // | | | | bb10[0]: reserved| + // | | | | bb10[0]: active | + // | | | | bb11[0]: reserved| + // | | | | bb11[0]: active | + // +---------+----------------------------------+------------------+------------------+ + // | [00-00] | _7 = const Foo::twiddle(move _8) | [0c-00] | [f3-0f] | + // +---------+----------------------------------+------------------+------------------+ let mut v = Vec::new(); + self.node_label_internal(n, &mut v, *n, self.mbcx.mir()).unwrap(); + dot::LabelText::html(String::from_utf8(v).unwrap()) + } + + + fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> { + Some(dot::LabelText::label("none")) + } + + fn edge_label(&'a self, e: &Edge) -> dot::LabelText<'a> { + let term = self.mbcx.mir()[e.source].terminator(); + let label = &term.kind.fmt_successor_labels()[e.index]; + dot::LabelText::label(label.clone()) + } +} + +impl<'a, 'tcx, MWF, P> Graph<'a, 'tcx, MWF, P> +where MWF: MirWithFlowState<'tcx>, + P: Fn(&MWF::BD, <MWF::BD as BitDenotation>::Idx) -> DebugFormatted, +{ + /// Generate the node label + fn node_label_internal<W: io::Write>(&self, + n: &Node, + w: &mut W, + block: BasicBlock, + mir: &Mir) -> io::Result<()> { + // Header rows + const HDRS: [&'static str; 4] = ["ENTRY", "MIR", "GEN", "KILL"]; + const HDR_FMT: &'static str = "bgcolor=\"grey\""; + write!(w, "<table><tr><td rowspan=\"{}\">", HDRS.len())?; + write!(w, "{:?}", block.index())?; + write!(w, "</td></tr><tr>")?; + for hdr in &HDRS { + write!(w, "<td {}>{}</td>", HDR_FMT, hdr)?; + } + write!(w, "</tr>")?; + + // Data row + self.node_label_verbose_row(n, w, block, mir)?; + self.node_label_final_row(n, w, block, mir)?; + write!(w, "</table>")?; + + Ok(()) + } + + /// Build the verbose row: full MIR data, and detailed gen/kill/entry sets + fn node_label_verbose_row<W: io::Write>(&self, + n: &Node, + w: &mut W, + block: BasicBlock, + mir: &Mir) + -> io::Result<()> { let i = n.index(); - let chunk_size = 5; - const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#; - const ALIGN_RIGHT: &'static str = r#"align="right""#; - const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#; - fn chunked_present_left<W:io::Write>(w: &mut W, - interpreted: &[DebugFormatted], - chunk_size: usize) - -> io::Result<()> - { - // This function may emit a sequence of <tr>'s, but it - // always finishes with an (unfinished) - // <tr><td></td><td> - // - // Thus, after being called, one should finish both the - // pending <td> as well as the <tr> itself. - let mut seen_one = false; - for c in interpreted.chunks(chunk_size) { - if seen_one { - // if not the first row, finish off the previous row - write!(w, "</td><td></td><td></td></tr>")?; + + macro_rules! dump_set_for { + ($set:ident) => { + write!(w, "<td>")?; + + let flow = self.mbcx.flow_state(); + let entry_interp = flow.interpret_set(&flow.operator, + flow.sets.$set(i), + &self.render_idx); + for e in &entry_interp { + write!(w, "{:?}<br/>", e)?; } - write!(w, "<tr><td></td><td {bg} {align}>{objs:?}", - bg = BG_FLOWCONTENT, - align = ALIGN_RIGHT, - objs = c)?; - seen_one = true; + write!(w, "</td>")?; } - if !seen_one { - write!(w, "<tr><td></td><td {bg} {align}>[]", - bg = BG_FLOWCONTENT, - align = ALIGN_RIGHT)?; + } + + write!(w, "<tr>")?; + // Entry + dump_set_for!(on_entry_set_for); + + // MIR statements + write!(w, "<td>")?; + { + let data = &mir[block]; + for (i, statement) in data.statements.iter().enumerate() { + write!(w, "{}<br align=\"left\"/>", + dot::escape_html(&format!("{:3}: {:?}", i, statement)))?; } - Ok(()) } - util::write_graphviz_node_label( - *n, self.mbcx.mir(), &mut v, 4, - |w| { - let flow = self.mbcx.flow_state(); - let entry_interp = flow.interpret_set(&flow.operator, - flow.sets.on_entry_set_for(i), - &self.render_idx); - chunked_present_left(w, &entry_interp[..], chunk_size)?; - let bits_per_block = flow.sets.bits_per_block(); - let entry = flow.sets.on_entry_set_for(i); - debug!("entry set for i={i} bits_per_block: {bpb} entry: {e:?} interp: {ei:?}", - i=i, e=entry, bpb=bits_per_block, ei=entry_interp); - write!(w, "= ENTRY:</td><td {bg}><FONT {face}>{entrybits:?}</FONT></td>\ - <td></td></tr>", - bg = BG_FLOWCONTENT, - face = FACE_MONOSPACE, - entrybits=bits_to_string(entry.words(), bits_per_block)) - }, - |w| { + write!(w, "</td>")?; + + // Gen + dump_set_for!(gen_set_for); + + // Kill + dump_set_for!(kill_set_for); + + write!(w, "</tr>")?; + + Ok(()) + } + + /// Build the summary row: terminator, gen/kill/entry bit sets + fn node_label_final_row<W: io::Write>(&self, + n: &Node, + w: &mut W, + block: BasicBlock, + mir: &Mir) + -> io::Result<()> { + let i = n.index(); + + macro_rules! dump_set_for { + ($set:ident) => { let flow = self.mbcx.flow_state(); - let gen_interp = - flow.interpret_set(&flow.operator, flow.sets.gen_set_for(i), &self.render_idx); - let kill_interp = - flow.interpret_set(&flow.operator, flow.sets.kill_set_for(i), &self.render_idx); - chunked_present_left(w, &gen_interp[..], chunk_size)?; let bits_per_block = flow.sets.bits_per_block(); - { - let gen = flow.sets.gen_set_for(i); - debug!("gen set for i={i} bits_per_block: {bpb} gen: {g:?} interp: {gi:?}", - i=i, g=gen, bpb=bits_per_block, gi=gen_interp); - write!(w, " = GEN:</td><td {bg}><FONT {face}>{genbits:?}</FONT></td>\ - <td></td></tr>", - bg = BG_FLOWCONTENT, - face = FACE_MONOSPACE, - genbits=bits_to_string(gen.words(), bits_per_block))?; - } + let set = flow.sets.$set(i); + write!(w, "<td>{:?}</td>", + dot::escape_html(&bits_to_string(set.words(), bits_per_block)))?; + } + } - { - let kill = flow.sets.kill_set_for(i); - debug!("kill set for i={i} bits_per_block: {bpb} kill: {k:?} interp: {ki:?}", - i=i, k=kill, bpb=bits_per_block, ki=kill_interp); - write!(w, "<tr><td></td><td {bg} {align}>KILL:</td>\ - <td {bg}><FONT {face}>{killbits:?}</FONT></td>", - bg = BG_FLOWCONTENT, - align = ALIGN_RIGHT, - face = FACE_MONOSPACE, - killbits=bits_to_string(kill.words(), bits_per_block))?; - } + write!(w, "<tr>")?; + // Entry + dump_set_for!(on_entry_set_for); - // (chunked_present_right) - let mut seen_one = false; - for k in kill_interp.chunks(chunk_size) { - if !seen_one { - // continuation of row; this is fourth <td> - write!(w, "<td {bg}>= {kill:?}</td></tr>", - bg = BG_FLOWCONTENT, - kill=k)?; - } else { - // new row, with indent of three <td>'s - write!(w, "<tr><td></td><td></td><td></td><td {bg}>{kill:?}</td></tr>", - bg = BG_FLOWCONTENT, - kill=k)?; - } - seen_one = true; - } - if !seen_one { - write!(w, "<td {bg}>= []</td></tr>", - bg = BG_FLOWCONTENT)?; - } + // Terminator + write!(w, "<td>")?; + { + let data = &mir[block]; + let mut terminator_head = String::new(); + data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); + write!(w, "{}", dot::escape_html(&terminator_head))?; + } + write!(w, "</td>")?; - Ok(()) - }) - .unwrap(); - dot::LabelText::html(String::from_utf8(v).unwrap()) - } + // Gen + dump_set_for!(gen_set_for); - fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> { - Some(dot::LabelText::label("none")) + // Kill + dump_set_for!(kill_set_for); + + write!(w, "</tr>")?; + + Ok(()) } } |
