about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbobtwinkles <srkoser+github@gmail.com>2018-03-03 17:44:06 -0500
committerbobtwinkles <srkoser+github@gmail.com>2018-03-09 13:53:35 -0500
commit047bec69b9ec54d400b1e255c8757bff8a5a854d (patch)
tree08b1913ea9a61727a5b1cbd4ef271825a96f737e
parent138365368a71a04ff9539704f478b014379b7ee5 (diff)
downloadrust-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.rs6
-rw-r--r--src/librustc_mir/dataflow/graphviz.rs285
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(())
     }
 }