1 //! A helpful diagram for debugging dataflow problems.
3 use std::cell::RefCell;
4 use std::{io, ops, str};
6 use rustc::mir::{self, BasicBlock, Body, Location};
7 use rustc_hir::def_id::DefId;
8 use rustc_index::bit_set::{BitSet, HybridBitSet};
9 use rustc_index::vec::{Idx, IndexVec};
11 use super::{Analysis, GenKillSet, Results, ResultsRefCursor};
12 use crate::util::graphviz_safe_def_name;
14 pub struct Formatter<'a, 'tcx, A>
21 // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
22 block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
25 impl<A> Formatter<'a, 'tcx, A>
32 results: &'a Results<'tcx, A>,
33 state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
35 let block_formatter = BlockFormatter {
36 bg: Background::Light,
37 results: ResultsRefCursor::new(body, results),
41 Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
45 /// A pair of a basic block and an index into that basic blocks `successors`.
46 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
52 fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
57 .map(|(index, _)| CfgEdge { source: bb, index })
61 impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
65 type Node = BasicBlock;
68 fn graph_id(&self) -> dot::Id<'_> {
69 let name = graphviz_safe_def_name(self.def_id);
70 dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
73 fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
74 dot::Id::new(format!("bb_{}", n.index())).unwrap()
77 fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
78 let mut label = Vec::new();
79 self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap();
80 dot::LabelText::html(String::from_utf8(label).unwrap())
83 fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
84 Some(dot::LabelText::label("none"))
87 fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
88 let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index];
89 dot::LabelText::label(label.clone())
93 impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
97 type Node = BasicBlock;
100 fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
101 self.body.basic_blocks().indices().collect::<Vec<_>>().into()
104 fn edges(&self) -> dot::Edges<'_, Self::Edge> {
108 .flat_map(|bb| outgoing_edges(self.body, bb))
113 fn source(&self, edge: &Self::Edge) -> Self::Node {
117 fn target(&self, edge: &Self::Edge) -> Self::Node {
118 self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap()
122 struct BlockFormatter<'a, 'tcx, A>
126 results: ResultsRefCursor<'a, 'a, 'tcx, A>,
128 state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
131 impl<A> BlockFormatter<'a, 'tcx, A>
135 const HEADER_COLOR: &'static str = "#a0a0a0";
137 fn num_state_columns(&self) -> usize {
138 std::cmp::max(1, self.state_formatter.column_names().len())
141 fn toggle_background(&mut self) -> Background {
149 w: &mut impl io::Write,
150 body: &'a Body<'tcx>,
152 ) -> io::Result<()> {
154 // +-+-----------------------------------------------+
156 // +-+----------------------------------+------------+
158 // +-+----------------------------------+------------+
159 // C | | (on entry) | {_0,_2,_3} |
160 // +-+----------------------------------+------------+
161 // D |0| StorageLive(_7) | |
162 // +-+----------------------------------+------------+
163 // |1| StorageLive(_8) | |
164 // +-+----------------------------------+------------+
165 // |2| _8 = &mut _1 | +_8 |
166 // +-+----------------------------------+------------+
167 // E |T| _4 = const Foo::twiddle(move _2) | -_2 |
168 // +-+----------------------------------+------------+
169 // F | | (on unwind) | {_0,_3,_8} |
170 // +-+----------------------------------+------------+
171 // | | (on successful return) | +_4 |
172 // +-+----------------------------------+------------+
174 // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their
175 // children. This is because `xdot` seemed to have a hard time correctly propagating
176 // attributes. Make sure to test the output before trying to remove the redundancy.
177 // Notably, `align` was found to have no effect when applied only to <table>.
179 let table_fmt = concat!(
182 " cellspacing=\"0\"",
183 " cellpadding=\"3\"",
186 write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
188 // A + B: Block header
189 if self.state_formatter.column_names().is_empty() {
190 self.write_block_header_simple(w, block)?;
192 self.write_block_header_with_state_columns(w, block)?;
196 self.bg = Background::Light;
197 self.results.seek_to_block_start(block);
198 let block_entry_state = self.results.get().clone();
200 self.write_row_with_full_state(w, "", "(on entry)")?;
202 // D: Statement transfer functions
203 for (i, statement) in body[block].statements.iter().enumerate() {
204 let location = Location { block, statement_index: i };
205 let statement_str = format!("{:?}", statement);
206 self.write_row_for_location(w, &i.to_string(), &statement_str, location)?;
209 // E: Terminator transfer function
210 let terminator = body[block].terminator();
211 let terminator_loc = body.terminator_loc(block);
212 let mut terminator_str = String::new();
213 terminator.kind.fmt_head(&mut terminator_str).unwrap();
215 self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?;
219 // Write the full dataflow state immediately after the terminator if it differs from the
220 // state at block entry.
221 self.results.seek_after(terminator_loc);
222 if self.results.get() != &block_entry_state {
223 let after_terminator_name = match terminator.kind {
224 mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)",
228 self.write_row_with_full_state(w, "", after_terminator_name)?;
231 // Write any changes caused by terminator-specific effects
232 match terminator.kind {
233 mir::TerminatorKind::Call { destination: Some(_), .. } => {
234 let num_state_columns = self.num_state_columns();
235 self.write_row(w, "", "(on successful return)", |this, w, fmt| {
238 r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
239 colspan = num_state_columns,
243 let state_on_unwind = this.results.get().clone();
244 this.results.seek_after_assume_call_returns(terminator_loc);
245 write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?;
254 write!(w, "</table>")
257 fn write_block_header_simple(
259 w: &mut impl io::Write,
261 ) -> io::Result<()> {
262 // +-------------------------------------------------+
264 // +-----------------------------------+-------------+
266 // +-+---------------------------------+-------------+
272 concat!("<tr>", r#"<td colspan="3" sides="tl">bb{block_id}</td>"#, "</tr>",),
273 block_id = block.index(),
281 r#"<td colspan="2" {fmt}>MIR</td>"#,
282 r#"<td {fmt}>STATE</td>"#,
285 fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR),
289 fn write_block_header_with_state_columns(
291 w: &mut impl io::Write,
293 ) -> io::Result<()> {
294 // +------------------------------------+-------------+
296 // +------------------------------------+------+------+
297 // B | MIR | GEN | KILL |
298 // +-+----------------------------------+------+------+
301 let state_column_names = self.state_formatter.column_names();
308 r#"<td {fmt} colspan="2">bb{block_id}</td>"#,
309 r#"<td {fmt} colspan="{num_state_cols}">STATE</td>"#,
312 fmt = "sides=\"tl\"",
313 num_state_cols = state_column_names.len(),
314 block_id = block.index(),
318 let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR);
319 write!(w, concat!("<tr>", r#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?;
321 for name in state_column_names {
322 write!(w, "<td {fmt}>{name}</td>", fmt = fmt, name = name)?;
328 /// Write a row with the given index and MIR, using the function argument to fill in the
329 /// "STATE" column(s).
330 fn write_row<W: io::Write>(
335 f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>,
336 ) -> io::Result<()> {
337 let bg = self.toggle_background();
338 let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" };
340 let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr());
346 r#"<td {fmt} align="right">{i}</td>"#,
347 r#"<td {fmt} align="left">{mir}</td>"#,
351 mir = dot::escape_html(mir),
358 fn write_row_with_full_state(
360 w: &mut impl io::Write,
363 ) -> io::Result<()> {
364 self.write_row(w, i, mir, |this, w, fmt| {
365 let state = this.results.get();
366 let analysis = this.results.analysis();
370 r#"<td colspan="{colspan}" {fmt} align="left">{{"#,
371 colspan = this.num_state_columns(),
374 pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?;
379 fn write_row_for_location(
381 w: &mut impl io::Write,
385 ) -> io::Result<()> {
386 self.write_row(w, i, mir, |this, w, fmt| {
387 this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location)
392 /// Controls what gets printed under the `STATE` header.
393 pub trait StateFormatter<'tcx, A>
397 /// The columns that will get printed under `STATE`.
398 fn column_names(&self) -> &[&str];
400 fn write_state_for_location(
402 w: &mut dyn io::Write,
404 results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
409 /// Prints a single column containing the state vector immediately *after* each statement.
410 pub struct SimpleDiff<T: Idx> {
411 prev_state: BitSet<T>,
415 impl<T: Idx> SimpleDiff<T> {
416 pub fn new(bits_per_block: usize) -> Self {
417 SimpleDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
421 impl<A> StateFormatter<'tcx, A> for SimpleDiff<A::Idx>
425 fn column_names(&self) -> &[&str] {
429 fn write_state_for_location(
431 mut w: &mut dyn io::Write,
433 results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
435 ) -> io::Result<()> {
436 if location.statement_index == 0 {
437 results.seek_to_block_start(location.block);
438 self.prev_state.overwrite(results.get());
440 // Ensure that we are visiting statements in order, so `prev_state` is correct.
441 assert_eq!(self.prev_loc.successor_within_block(), location);
444 self.prev_loc = location;
445 write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?;
446 results.seek_after(location);
447 let curr_state = results.get();
448 write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
449 self.prev_state.overwrite(curr_state);
454 /// Prints two state columns, one containing only the "before" effect of each statement and one
455 /// containing the full effect.
456 pub struct TwoPhaseDiff<T: Idx> {
457 prev_state: BitSet<T>,
461 impl<T: Idx> TwoPhaseDiff<T> {
462 pub fn new(bits_per_block: usize) -> Self {
463 TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
467 impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx>
471 fn column_names(&self) -> &[&str] {
472 &["BEFORE", " AFTER"]
475 fn write_state_for_location(
477 mut w: &mut dyn io::Write,
479 results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
481 ) -> io::Result<()> {
482 if location.statement_index == 0 {
483 results.seek_to_block_start(location.block);
484 self.prev_state.overwrite(results.get());
486 // Ensure that we are visiting statements in order, so `prev_state` is correct.
487 assert_eq!(self.prev_loc.successor_within_block(), location);
490 self.prev_loc = location;
494 write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
495 results.seek_before(location);
496 let curr_state = results.get();
497 write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
498 self.prev_state.overwrite(curr_state);
503 write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
504 results.seek_after(location);
505 let curr_state = results.get();
506 write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
507 self.prev_state.overwrite(curr_state);
512 /// Prints the gen/kill set for the entire block.
513 pub struct BlockTransferFunc<'a, 'tcx, T: Idx> {
514 body: &'a mir::Body<'tcx>,
515 trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
518 impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
520 body: &'mir mir::Body<'tcx>,
521 trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
523 BlockTransferFunc { body, trans_for_block }
527 impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx>
531 fn column_names(&self) -> &[&str] {
535 fn write_state_for_location(
537 mut w: &mut dyn io::Write,
539 results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
541 ) -> io::Result<()> {
542 // Only print a single row.
543 if location.statement_index != 0 {
547 let block_trans = &self.trans_for_block[location.block];
548 let rowspan = self.body.basic_blocks()[location.block].statements.len();
550 for set in &[&block_trans.gen, &block_trans.kill] {
553 r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
558 pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?;
566 /// Writes two lines, one containing the added bits and one the removed bits.
567 fn write_diff<A: Analysis<'tcx>>(
568 w: &mut impl io::Write,
570 from: &BitSet<A::Idx>,
572 ) -> io::Result<()> {
573 assert_eq!(from.domain_size(), to.domain_size());
574 let len = from.domain_size();
576 let mut set = HybridBitSet::new_empty(len);
577 let mut clear = HybridBitSet::new_empty(len);
579 // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets.
580 for i in (0..len).map(|i| A::Idx::new(i)) {
581 match (from.contains(i), to.contains(i)) {
582 (false, true) => set.insert(i),
583 (true, false) => clear.insert(i),
589 write!(w, r#"<font color="darkgreen">+"#)?;
590 pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?;
591 write!(w, r#"</font>"#)?;
594 if !set.is_empty() && !clear.is_empty() {
595 write!(w, "{}", BR_LEFT)?;
598 if !clear.is_empty() {
599 write!(w, r#"<font color="red">-"#)?;
600 pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?;
601 write!(w, r#"</font>"#)?;
607 const BR_LEFT: &'static str = r#"<br align="left"/>"#;
608 const BR_LEFT_SPACE: &'static str = r#"<br align="left"/> "#;
610 /// Line break policy that breaks at 40 characters and starts the next line with a single space.
611 const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 });
614 sequence: &'static str,
618 /// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
619 /// separator (`sep`).
621 /// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
623 fn pretty_print_state_elems<A>(
624 w: &mut impl io::Write,
626 elems: impl Iterator<Item = A::Idx>,
628 line_break: Option<LineBreak>,
629 ) -> io::Result<bool>
633 let sep_width = sep.chars().count();
635 let mut buf = Vec::new();
637 let mut first = true;
638 let mut curr_line_width = 0;
639 let mut line_break_inserted = false;
643 analysis.pretty_print_idx(&mut buf, idx)?;
645 str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8");
646 let escaped = dot::escape_html(idx_str);
647 let escaped_width = escaped.chars().count();
652 write!(w, "{}", sep)?;
653 curr_line_width += sep_width;
655 if let Some(line_break) = &line_break {
656 if curr_line_width + sep_width + escaped_width > line_break.limit {
657 write!(w, "{}", line_break.sequence)?;
658 line_break_inserted = true;
664 write!(w, "{}", escaped)?;
665 curr_line_width += escaped_width;
668 Ok(line_break_inserted)
671 /// The background color used for zebra-striping the table.
672 #[derive(Clone, Copy)]
679 fn attr(self) -> &'static str {
681 Self::Dark => "bgcolor=\"#f0f0f0\"",
687 impl ops::Not for Background {
690 fn not(self) -> Self {
692 Self::Light => Self::Dark,
693 Self::Dark => Self::Light,