From 047bec69b9ec54d400b1e255c8757bff8a5a854d Mon Sep 17 00:00:00 2001 From: bobtwinkles Date: Sat, 3 Mar 2018 17:44:06 -0500 Subject: [PATCH] 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. --- src/libgraphviz/lib.rs | 6 + src/librustc_mir/dataflow/graphviz.rs | 297 +++++++++++++------------- 2 files changed, 155 insertions(+), 148 deletions(-) diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index cd893b9784ab..d8c366d2413d 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 { 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 fb3cb1518cbb..0305e4c93b6a 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,158 +95,162 @@ 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:" - // ------ ----------------------- ------------ -------------------- - // Left - // Most - // Column - // Is - // Just - // Normal - // Series - // Of - // MIR - // Stmts - // ------ ----------------------- ------------ -------------------- - // [g1, g4, g5] "= GEN:" - // ------ ----------------------- ------------ -------------------- - // "KILL:" "=" [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(); - 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: &mut W, - interpreted: &[DebugFormatted], - chunk_size: usize) - -> io::Result<()> - { - // This function may emit a sequence of 's, but it - // always finishes with an (unfinished) - // - // - // Thus, after being called, one should finish both the - // pending as well as the 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, "")?; - } - write!(w, "{objs:?}", - bg = BG_FLOWCONTENT, - align = ALIGN_RIGHT, - objs = c)?; - seen_one = true; - } - if !seen_one { - write!(w, "[]", - bg = BG_FLOWCONTENT, - align = ALIGN_RIGHT)?; - } - 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:{entrybits:?}\ - ", - bg = BG_FLOWCONTENT, - face = FACE_MONOSPACE, - entrybits=bits_to_string(entry.words(), bits_per_block)) - }, - |w| { - 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:{genbits:?}\ - ", - bg = BG_FLOWCONTENT, - face = FACE_MONOSPACE, - genbits=bits_to_string(gen.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, "KILL:\ - {killbits:?}", - bg = BG_FLOWCONTENT, - align = ALIGN_RIGHT, - face = FACE_MONOSPACE, - killbits=bits_to_string(kill.words(), bits_per_block))?; - } - - // (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 - write!(w, "= {kill:?}", - bg = BG_FLOWCONTENT, - kill=k)?; - } else { - // new row, with indent of three 's - write!(w, "{kill:?}", - bg = BG_FLOWCONTENT, - kill=k)?; - } - seen_one = true; - } - if !seen_one { - write!(w, "= []", - bg = BG_FLOWCONTENT)?; - } - - Ok(()) - }) - .unwrap(); + 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 { 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, ::Idx) -> DebugFormatted, +{ + /// Generate the node label + fn node_label_internal(&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, "")?; + for hdr in &HDRS { + write!(w, "", HDR_FMT, hdr)?; + } + write!(w, "")?; + + // Data row + self.node_label_verbose_row(n, w, block, mir)?; + self.node_label_final_row(n, w, block, mir)?; + write!(w, "
", HDRS.len())?; + write!(w, "{:?}", block.index())?; + write!(w, "
{}
")?; + + Ok(()) + } + + /// Build the verbose row: full MIR data, and detailed gen/kill/entry sets + fn node_label_verbose_row(&self, + n: &Node, + w: &mut W, + block: BasicBlock, + mir: &Mir) + -> io::Result<()> { + let i = n.index(); + + macro_rules! dump_set_for { + ($set:ident) => { + write!(w, "")?; + + 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, "{:?}
", e)?; + } + write!(w, "")?; + } + } + + write!(w, "")?; + // Entry + dump_set_for!(on_entry_set_for); + + // MIR statements + write!(w, "")?; + { + let data = &mir[block]; + for (i, statement) in data.statements.iter().enumerate() { + write!(w, "{}
", + dot::escape_html(&format!("{:3}: {:?}", i, statement)))?; + } + } + write!(w, "")?; + + // Gen + dump_set_for!(gen_set_for); + + // Kill + dump_set_for!(kill_set_for); + + write!(w, "")?; + + Ok(()) + } + + /// Build the summary row: terminator, gen/kill/entry bit sets + fn node_label_final_row(&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 bits_per_block = flow.sets.bits_per_block(); + let set = flow.sets.$set(i); + write!(w, "{:?}", + dot::escape_html(&bits_to_string(set.words(), bits_per_block)))?; + } + } + + write!(w, "")?; + // Entry + dump_set_for!(on_entry_set_for); + + // Terminator + write!(w, "")?; + { + 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, "")?; + + // Gen + dump_set_for!(gen_set_for); + + // Kill + dump_set_for!(kill_set_for); + + write!(w, "")?; + + Ok(()) + } } impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P>