Skip to content

Commit 047bec6

Browse files
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.
1 parent 1383653 commit 047bec6

File tree

2 files changed

+149
-142
lines changed

2 files changed

+149
-142
lines changed

src/libgraphviz/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,12 @@ impl<'a> IntoCow<'a, str> for &'a str {
711711
}
712712
}
713713

714+
impl<'a> IntoCow<'a, str> for Cow<'a, str> {
715+
fn into_cow(self) -> Cow<'a, str> {
716+
self
717+
}
718+
}
719+
714720
impl<'a, T: Clone> IntoCow<'a, [T]> for Vec<T> {
715721
fn into_cow(self) -> Cow<'a, [T]> {
716722
Cow::Owned(self)

src/librustc_mir/dataflow/graphviz.rs

Lines changed: 143 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@ use dot::IntoCow;
2020

2121
use std::fs;
2222
use std::io;
23-
use std::io::prelude::*;
2423
use std::marker::PhantomData;
2524
use std::path::Path;
2625

27-
use util;
28-
2926
use super::{BitDenotation, DataflowState};
3027
use super::DataflowBuilder;
3128
use super::DebugFormatted;
@@ -98,157 +95,161 @@ impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P>
9895
}
9996

10097
fn node_label(&self, n: &Node) -> dot::LabelText {
101-
// A standard MIR label, as generated by write_node_label, is
102-
// presented in a single column in a table.
103-
//
104-
// The code below does a bunch of formatting work to format a
105-
// node (i.e. MIR basic-block) label with extra
106-
// dataflow-enriched information. In particular, the goal is
107-
// to add extra columns that present the three dataflow
108-
// bitvectors, and the data those bitvectors represent.
109-
//
110-
// It presents it in the following format (where I am
111-
// presenting the table rendering via ASCII art, one line per
112-
// row of the table, and a chunk size of 3 rather than 5):
113-
//
114-
// ------ ----------------------- ------------ --------------------
115-
// [e1, e3, e4]
116-
// [e8, e9] "= ENTRY:" <ENTRY-BITS>
117-
// ------ ----------------------- ------------ --------------------
118-
// Left
119-
// Most
120-
// Column
121-
// Is
122-
// Just
123-
// Normal
124-
// Series
125-
// Of
126-
// MIR
127-
// Stmts
128-
// ------ ----------------------- ------------ --------------------
129-
// [g1, g4, g5] "= GEN:" <GEN-BITS>
130-
// ------ ----------------------- ------------ --------------------
131-
// "KILL:" <KILL-BITS> "=" [k1, k3, k8]
132-
// [k9]
133-
// ------ ----------------------- ------------ --------------------
134-
//
135-
// (In addition, the added dataflow is rendered with a colored
136-
// background just so it will stand out compared to the
137-
// statements.)
98+
// Node label is something like this:
99+
// +---------+----------------------------------+------------------+------------------+
100+
// | ENTRY | MIR | GEN | KILL |
101+
// +---------+----------------------------------+------------------+------------------+
102+
// | | 0: StorageLive(_7) | bb3[2]: reserved | bb2[0]: reserved |
103+
// | | 1: StorageLive(_8) | bb3[2]: active | bb2[0]: active |
104+
// | | 2: _8 = &mut _1 | | bb4[2]: reserved |
105+
// | | | | bb4[2]: active |
106+
// | | | | bb9[0]: reserved |
107+
// | | | | bb9[0]: active |
108+
// | | | | bb10[0]: reserved|
109+
// | | | | bb10[0]: active |
110+
// | | | | bb11[0]: reserved|
111+
// | | | | bb11[0]: active |
112+
// +---------+----------------------------------+------------------+------------------+
113+
// | [00-00] | _7 = const Foo::twiddle(move _8) | [0c-00] | [f3-0f] |
114+
// +---------+----------------------------------+------------------+------------------+
138115
let mut v = Vec::new();
116+
self.node_label_internal(n, &mut v, *n, self.mbcx.mir()).unwrap();
117+
dot::LabelText::html(String::from_utf8(v).unwrap())
118+
}
119+
120+
121+
fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> {
122+
Some(dot::LabelText::label("none"))
123+
}
124+
125+
fn edge_label(&'a self, e: &Edge) -> dot::LabelText<'a> {
126+
let term = self.mbcx.mir()[e.source].terminator();
127+
let label = &term.kind.fmt_successor_labels()[e.index];
128+
dot::LabelText::label(label.clone())
129+
}
130+
}
131+
132+
impl<'a, 'tcx, MWF, P> Graph<'a, 'tcx, MWF, P>
133+
where MWF: MirWithFlowState<'tcx>,
134+
P: Fn(&MWF::BD, <MWF::BD as BitDenotation>::Idx) -> DebugFormatted,
135+
{
136+
/// Generate the node label
137+
fn node_label_internal<W: io::Write>(&self,
138+
n: &Node,
139+
w: &mut W,
140+
block: BasicBlock,
141+
mir: &Mir) -> io::Result<()> {
142+
// Header rows
143+
const HDRS: [&'static str; 4] = ["ENTRY", "MIR", "GEN", "KILL"];
144+
const HDR_FMT: &'static str = "bgcolor=\"grey\"";
145+
write!(w, "<table><tr><td rowspan=\"{}\">", HDRS.len())?;
146+
write!(w, "{:?}", block.index())?;
147+
write!(w, "</td></tr><tr>")?;
148+
for hdr in &HDRS {
149+
write!(w, "<td {}>{}</td>", HDR_FMT, hdr)?;
150+
}
151+
write!(w, "</tr>")?;
152+
153+
// Data row
154+
self.node_label_verbose_row(n, w, block, mir)?;
155+
self.node_label_final_row(n, w, block, mir)?;
156+
write!(w, "</table>")?;
157+
158+
Ok(())
159+
}
160+
161+
/// Build the verbose row: full MIR data, and detailed gen/kill/entry sets
162+
fn node_label_verbose_row<W: io::Write>(&self,
163+
n: &Node,
164+
w: &mut W,
165+
block: BasicBlock,
166+
mir: &Mir)
167+
-> io::Result<()> {
139168
let i = n.index();
140-
let chunk_size = 5;
141-
const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#;
142-
const ALIGN_RIGHT: &'static str = r#"align="right""#;
143-
const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#;
144-
fn chunked_present_left<W:io::Write>(w: &mut W,
145-
interpreted: &[DebugFormatted],
146-
chunk_size: usize)
147-
-> io::Result<()>
148-
{
149-
// This function may emit a sequence of <tr>'s, but it
150-
// always finishes with an (unfinished)
151-
// <tr><td></td><td>
152-
//
153-
// Thus, after being called, one should finish both the
154-
// pending <td> as well as the <tr> itself.
155-
let mut seen_one = false;
156-
for c in interpreted.chunks(chunk_size) {
157-
if seen_one {
158-
// if not the first row, finish off the previous row
159-
write!(w, "</td><td></td><td></td></tr>")?;
169+
170+
macro_rules! dump_set_for {
171+
($set:ident) => {
172+
write!(w, "<td>")?;
173+
174+
let flow = self.mbcx.flow_state();
175+
let entry_interp = flow.interpret_set(&flow.operator,
176+
flow.sets.$set(i),
177+
&self.render_idx);
178+
for e in &entry_interp {
179+
write!(w, "{:?}<br/>", e)?;
160180
}
161-
write!(w, "<tr><td></td><td {bg} {align}>{objs:?}",
162-
bg = BG_FLOWCONTENT,
163-
align = ALIGN_RIGHT,
164-
objs = c)?;
165-
seen_one = true;
181+
write!(w, "</td>")?;
166182
}
167-
if !seen_one {
168-
write!(w, "<tr><td></td><td {bg} {align}>[]",
169-
bg = BG_FLOWCONTENT,
170-
align = ALIGN_RIGHT)?;
183+
}
184+
185+
write!(w, "<tr>")?;
186+
// Entry
187+
dump_set_for!(on_entry_set_for);
188+
189+
// MIR statements
190+
write!(w, "<td>")?;
191+
{
192+
let data = &mir[block];
193+
for (i, statement) in data.statements.iter().enumerate() {
194+
write!(w, "{}<br align=\"left\"/>",
195+
dot::escape_html(&format!("{:3}: {:?}", i, statement)))?;
171196
}
172-
Ok(())
173197
}
174-
util::write_graphviz_node_label(
175-
*n, self.mbcx.mir(), &mut v, 4,
176-
|w| {
177-
let flow = self.mbcx.flow_state();
178-
let entry_interp = flow.interpret_set(&flow.operator,
179-
flow.sets.on_entry_set_for(i),
180-
&self.render_idx);
181-
chunked_present_left(w, &entry_interp[..], chunk_size)?;
182-
let bits_per_block = flow.sets.bits_per_block();
183-
let entry = flow.sets.on_entry_set_for(i);
184-
debug!("entry set for i={i} bits_per_block: {bpb} entry: {e:?} interp: {ei:?}",
185-
i=i, e=entry, bpb=bits_per_block, ei=entry_interp);
186-
write!(w, "= ENTRY:</td><td {bg}><FONT {face}>{entrybits:?}</FONT></td>\
187-
<td></td></tr>",
188-
bg = BG_FLOWCONTENT,
189-
face = FACE_MONOSPACE,
190-
entrybits=bits_to_string(entry.words(), bits_per_block))
191-
},
192-
|w| {
198+
write!(w, "</td>")?;
199+
200+
// Gen
201+
dump_set_for!(gen_set_for);
202+
203+
// Kill
204+
dump_set_for!(kill_set_for);
205+
206+
write!(w, "</tr>")?;
207+
208+
Ok(())
209+
}
210+
211+
/// Build the summary row: terminator, gen/kill/entry bit sets
212+
fn node_label_final_row<W: io::Write>(&self,
213+
n: &Node,
214+
w: &mut W,
215+
block: BasicBlock,
216+
mir: &Mir)
217+
-> io::Result<()> {
218+
let i = n.index();
219+
220+
macro_rules! dump_set_for {
221+
($set:ident) => {
193222
let flow = self.mbcx.flow_state();
194-
let gen_interp =
195-
flow.interpret_set(&flow.operator, flow.sets.gen_set_for(i), &self.render_idx);
196-
let kill_interp =
197-
flow.interpret_set(&flow.operator, flow.sets.kill_set_for(i), &self.render_idx);
198-
chunked_present_left(w, &gen_interp[..], chunk_size)?;
199223
let bits_per_block = flow.sets.bits_per_block();
200-
{
201-
let gen = flow.sets.gen_set_for(i);
202-
debug!("gen set for i={i} bits_per_block: {bpb} gen: {g:?} interp: {gi:?}",
203-
i=i, g=gen, bpb=bits_per_block, gi=gen_interp);
204-
write!(w, " = GEN:</td><td {bg}><FONT {face}>{genbits:?}</FONT></td>\
205-
<td></td></tr>",
206-
bg = BG_FLOWCONTENT,
207-
face = FACE_MONOSPACE,
208-
genbits=bits_to_string(gen.words(), bits_per_block))?;
209-
}
224+
let set = flow.sets.$set(i);
225+
write!(w, "<td>{:?}</td>",
226+
dot::escape_html(&bits_to_string(set.words(), bits_per_block)))?;
227+
}
228+
}
210229

211-
{
212-
let kill = flow.sets.kill_set_for(i);
213-
debug!("kill set for i={i} bits_per_block: {bpb} kill: {k:?} interp: {ki:?}",
214-
i=i, k=kill, bpb=bits_per_block, ki=kill_interp);
215-
write!(w, "<tr><td></td><td {bg} {align}>KILL:</td>\
216-
<td {bg}><FONT {face}>{killbits:?}</FONT></td>",
217-
bg = BG_FLOWCONTENT,
218-
align = ALIGN_RIGHT,
219-
face = FACE_MONOSPACE,
220-
killbits=bits_to_string(kill.words(), bits_per_block))?;
221-
}
230+
write!(w, "<tr>")?;
231+
// Entry
232+
dump_set_for!(on_entry_set_for);
222233

223-
// (chunked_present_right)
224-
let mut seen_one = false;
225-
for k in kill_interp.chunks(chunk_size) {
226-
if !seen_one {
227-
// continuation of row; this is fourth <td>
228-
write!(w, "<td {bg}>= {kill:?}</td></tr>",
229-
bg = BG_FLOWCONTENT,
230-
kill=k)?;
231-
} else {
232-
// new row, with indent of three <td>'s
233-
write!(w, "<tr><td></td><td></td><td></td><td {bg}>{kill:?}</td></tr>",
234-
bg = BG_FLOWCONTENT,
235-
kill=k)?;
236-
}
237-
seen_one = true;
238-
}
239-
if !seen_one {
240-
write!(w, "<td {bg}>= []</td></tr>",
241-
bg = BG_FLOWCONTENT)?;
242-
}
234+
// Terminator
235+
write!(w, "<td>")?;
236+
{
237+
let data = &mir[block];
238+
let mut terminator_head = String::new();
239+
data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
240+
write!(w, "{}", dot::escape_html(&terminator_head))?;
241+
}
242+
write!(w, "</td>")?;
243243

244-
Ok(())
245-
})
246-
.unwrap();
247-
dot::LabelText::html(String::from_utf8(v).unwrap())
248-
}
244+
// Gen
245+
dump_set_for!(gen_set_for);
249246

250-
fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> {
251-
Some(dot::LabelText::label("none"))
247+
// Kill
248+
dump_set_for!(kill_set_for);
249+
250+
write!(w, "</tr>")?;
251+
252+
Ok(())
252253
}
253254
}
254255

0 commit comments

Comments
 (0)