2019-11-11 11:48:47 -08:00
|
|
|
//! A helpful diagram for debugging dataflow problems.
|
|
|
|
|
2019-09-26 16:26:11 -07:00
|
|
|
use std::cell::RefCell;
|
2019-11-11 11:48:47 -08:00
|
|
|
use std::{io, ops, str};
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::{self, BasicBlock, Body, Location};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir::def_id::DefId;
|
2019-09-26 16:26:11 -07:00
|
|
|
use rustc_index::bit_set::{BitSet, HybridBitSet};
|
2019-11-11 11:48:47 -08:00
|
|
|
use rustc_index::vec::{Idx, IndexVec};
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
use super::{Analysis, GenKillSet, Results, ResultsRefCursor};
|
2019-12-22 17:42:04 -05:00
|
|
|
use crate::util::graphviz_safe_def_name;
|
2019-09-26 16:26:11 -07:00
|
|
|
|
|
|
|
pub struct Formatter<'a, 'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
body: &'a Body<'tcx>,
|
|
|
|
def_id: DefId,
|
|
|
|
|
|
|
|
// This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
|
|
|
|
block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A> Formatter<'a, 'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
2019-11-11 11:48:47 -08:00
|
|
|
pub fn new(
|
|
|
|
body: &'a Body<'tcx>,
|
|
|
|
def_id: DefId,
|
|
|
|
results: &'a Results<'tcx, A>,
|
|
|
|
state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
|
|
|
|
) -> Self {
|
2019-09-26 16:26:11 -07:00
|
|
|
let block_formatter = BlockFormatter {
|
|
|
|
bg: Background::Light,
|
|
|
|
results: ResultsRefCursor::new(body, results),
|
2019-11-11 11:48:47 -08:00
|
|
|
state_formatter,
|
2019-09-26 16:26:11 -07:00
|
|
|
};
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A pair of a basic block and an index into that basic blocks `successors`.
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct CfgEdge {
|
|
|
|
source: BasicBlock,
|
|
|
|
index: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
|
|
|
|
body[bb]
|
|
|
|
.terminator()
|
|
|
|
.successors()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(index, _)| CfgEdge { source: bb, index })
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
type Node = BasicBlock;
|
|
|
|
type Edge = CfgEdge;
|
|
|
|
|
|
|
|
fn graph_id(&self) -> dot::Id<'_> {
|
|
|
|
let name = graphviz_safe_def_name(self.def_id);
|
|
|
|
dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
|
|
|
|
dot::Id::new(format!("bb_{}", n.index())).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
|
|
|
|
let mut label = Vec::new();
|
2019-12-22 17:42:04 -05:00
|
|
|
self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap();
|
2019-09-26 16:26:11 -07:00
|
|
|
dot::LabelText::html(String::from_utf8(label).unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
|
|
|
|
Some(dot::LabelText::label("none"))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
|
2019-12-22 17:42:04 -05:00
|
|
|
let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index];
|
2019-09-26 16:26:11 -07:00
|
|
|
dot::LabelText::label(label.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
type Node = BasicBlock;
|
|
|
|
type Edge = CfgEdge;
|
|
|
|
|
|
|
|
fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
|
2019-12-22 17:42:04 -05:00
|
|
|
self.body.basic_blocks().indices().collect::<Vec<_>>().into()
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn edges(&self) -> dot::Edges<'_, Self::Edge> {
|
|
|
|
self.body
|
|
|
|
.basic_blocks()
|
|
|
|
.indices()
|
|
|
|
.flat_map(|bb| outgoing_edges(self.body, bb))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn source(&self, edge: &Self::Edge) -> Self::Node {
|
|
|
|
edge.source
|
|
|
|
}
|
|
|
|
|
|
|
|
fn target(&self, edge: &Self::Edge) -> Self::Node {
|
2019-12-22 17:42:04 -05:00
|
|
|
self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap()
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct BlockFormatter<'a, 'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
results: ResultsRefCursor<'a, 'a, 'tcx, A>,
|
|
|
|
bg: Background,
|
2019-11-11 11:48:47 -08:00
|
|
|
state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<A> BlockFormatter<'a, 'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
2019-11-11 11:48:47 -08:00
|
|
|
const HEADER_COLOR: &'static str = "#a0a0a0";
|
|
|
|
|
|
|
|
fn num_state_columns(&self) -> usize {
|
|
|
|
std::cmp::max(1, self.state_formatter.column_names().len())
|
|
|
|
}
|
|
|
|
|
2019-09-26 16:26:11 -07:00
|
|
|
fn toggle_background(&mut self) -> Background {
|
|
|
|
let bg = self.bg;
|
|
|
|
self.bg = !bg;
|
|
|
|
bg
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_node_label(
|
|
|
|
&mut self,
|
|
|
|
w: &mut impl io::Write,
|
|
|
|
body: &'a Body<'tcx>,
|
|
|
|
block: BasicBlock,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
// Sample output:
|
2019-10-01 15:23:04 -07:00
|
|
|
// +-+-----------------------------------------------+
|
|
|
|
// A | bb4 |
|
|
|
|
// +-+----------------------------------+------------+
|
|
|
|
// B | MIR | STATE |
|
|
|
|
// +-+----------------------------------+------------+
|
|
|
|
// C | | (on entry) | {_0,_2,_3} |
|
|
|
|
// +-+----------------------------------+------------+
|
|
|
|
// D |0| StorageLive(_7) | |
|
|
|
|
// +-+----------------------------------+------------+
|
|
|
|
// |1| StorageLive(_8) | |
|
|
|
|
// +-+----------------------------------+------------+
|
|
|
|
// |2| _8 = &mut _1 | +_8 |
|
|
|
|
// +-+----------------------------------+------------+
|
|
|
|
// E |T| _4 = const Foo::twiddle(move _2) | -_2 |
|
|
|
|
// +-+----------------------------------+------------+
|
|
|
|
// F | | (on unwind) | {_0,_3,_8} |
|
|
|
|
// +-+----------------------------------+------------+
|
|
|
|
// | | (on successful return) | +_4 |
|
|
|
|
// +-+----------------------------------+------------+
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2020-01-21 13:37:59 -08:00
|
|
|
// N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their
|
|
|
|
// children. This is because `xdot` seemed to have a hard time correctly propagating
|
|
|
|
// attributes. Make sure to test the output before trying to remove the redundancy.
|
|
|
|
// Notably, `align` was found to have no effect when applied only to <table>.
|
|
|
|
|
|
|
|
let table_fmt = concat!(
|
|
|
|
" border=\"1\"",
|
|
|
|
" cellborder=\"1\"",
|
|
|
|
" cellspacing=\"0\"",
|
|
|
|
" cellpadding=\"3\"",
|
|
|
|
" sides=\"rb\"",
|
|
|
|
);
|
|
|
|
write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
// A + B: Block header
|
|
|
|
if self.state_formatter.column_names().is_empty() {
|
|
|
|
self.write_block_header_simple(w, block)?;
|
|
|
|
} else {
|
|
|
|
self.write_block_header_with_state_columns(w, block)?;
|
|
|
|
}
|
2019-09-26 16:26:11 -07:00
|
|
|
|
|
|
|
// C: Entry state
|
2019-10-01 15:21:58 -07:00
|
|
|
self.bg = Background::Light;
|
2019-09-26 16:26:11 -07:00
|
|
|
self.results.seek_to_block_start(block);
|
2020-02-13 15:27:54 -08:00
|
|
|
let block_entry_state = self.results.get().clone();
|
|
|
|
|
2020-01-21 13:37:59 -08:00
|
|
|
self.write_row_with_full_state(w, "", "(on entry)")?;
|
2019-09-26 16:26:11 -07:00
|
|
|
|
|
|
|
// D: Statement transfer functions
|
|
|
|
for (i, statement) in body[block].statements.iter().enumerate() {
|
|
|
|
let location = Location { block, statement_index: i };
|
2019-11-11 11:48:47 -08:00
|
|
|
let statement_str = format!("{:?}", statement);
|
|
|
|
self.write_row_for_location(w, &i.to_string(), &statement_str, location)?;
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// E: Terminator transfer function
|
|
|
|
let terminator = body[block].terminator();
|
2019-11-11 11:48:47 -08:00
|
|
|
let terminator_loc = body.terminator_loc(block);
|
|
|
|
let mut terminator_str = String::new();
|
|
|
|
terminator.kind.fmt_head(&mut terminator_str).unwrap();
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?;
|
2019-09-26 16:26:11 -07:00
|
|
|
|
|
|
|
// F: Exit state
|
2020-02-13 15:27:54 -08:00
|
|
|
|
|
|
|
// Write the full dataflow state immediately after the terminator if it differs from the
|
|
|
|
// state at block entry.
|
2019-11-11 11:48:47 -08:00
|
|
|
self.results.seek_after(terminator_loc);
|
2020-02-13 15:27:54 -08:00
|
|
|
if self.results.get() != &block_entry_state {
|
|
|
|
let after_terminator_name = match terminator.kind {
|
|
|
|
mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)",
|
|
|
|
_ => "(on exit)",
|
|
|
|
};
|
|
|
|
|
|
|
|
self.write_row_with_full_state(w, "", after_terminator_name)?;
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
2020-02-13 15:27:54 -08:00
|
|
|
// Write any changes caused by terminator-specific effects
|
|
|
|
match terminator.kind {
|
|
|
|
mir::TerminatorKind::Call { destination: Some(_), .. } => {
|
|
|
|
let num_state_columns = self.num_state_columns();
|
|
|
|
self.write_row(w, "", "(on successful return)", |this, w, fmt| {
|
|
|
|
write!(
|
|
|
|
w,
|
|
|
|
r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
|
|
|
|
colspan = num_state_columns,
|
|
|
|
fmt = fmt,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let state_on_unwind = this.results.get().clone();
|
2020-03-06 00:32:06 +01:00
|
|
|
this.results.seek_after_assume_success(terminator_loc);
|
2020-02-13 15:27:54 -08:00
|
|
|
write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?;
|
|
|
|
|
|
|
|
write!(w, "</td>")
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => {}
|
|
|
|
};
|
|
|
|
|
2019-09-26 16:26:11 -07:00
|
|
|
write!(w, "</table>")
|
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
fn write_block_header_simple(
|
2019-09-26 16:26:11 -07:00
|
|
|
&mut self,
|
|
|
|
w: &mut impl io::Write,
|
2019-11-11 11:48:47 -08:00
|
|
|
block: BasicBlock,
|
2019-09-26 16:26:11 -07:00
|
|
|
) -> io::Result<()> {
|
2019-11-11 11:48:47 -08:00
|
|
|
// +-------------------------------------------------+
|
|
|
|
// A | bb4 |
|
|
|
|
// +-----------------------------------+-------------+
|
|
|
|
// B | MIR | STATE |
|
|
|
|
// +-+---------------------------------+-------------+
|
|
|
|
// | | ... | |
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
// A
|
|
|
|
write!(
|
|
|
|
w,
|
|
|
|
concat!("<tr>", r#"<td colspan="3" sides="tl">bb{block_id}</td>"#, "</tr>",),
|
|
|
|
block_id = block.index(),
|
|
|
|
)?;
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
// B
|
2019-09-26 16:26:11 -07:00
|
|
|
write!(
|
|
|
|
w,
|
2019-11-11 11:48:47 -08:00
|
|
|
concat!(
|
|
|
|
"<tr>",
|
|
|
|
r#"<td colspan="2" {fmt}>MIR</td>"#,
|
|
|
|
r#"<td {fmt}>STATE</td>"#,
|
|
|
|
"</tr>",
|
|
|
|
),
|
|
|
|
fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR),
|
2019-09-26 16:26:11 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
fn write_block_header_with_state_columns(
|
2019-09-26 16:26:11 -07:00
|
|
|
&mut self,
|
|
|
|
w: &mut impl io::Write,
|
2019-11-11 11:48:47 -08:00
|
|
|
block: BasicBlock,
|
2019-09-26 16:26:11 -07:00
|
|
|
) -> io::Result<()> {
|
2019-11-11 11:48:47 -08:00
|
|
|
// +------------------------------------+-------------+
|
|
|
|
// A | bb4 | STATE |
|
|
|
|
// +------------------------------------+------+------+
|
|
|
|
// B | MIR | GEN | KILL |
|
|
|
|
// +-+----------------------------------+------+------+
|
|
|
|
// | | ... | | |
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
let state_column_names = self.state_formatter.column_names();
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
// A
|
|
|
|
write!(
|
|
|
|
w,
|
|
|
|
concat!(
|
|
|
|
"<tr>",
|
|
|
|
r#"<td {fmt} colspan="2">bb{block_id}</td>"#,
|
|
|
|
r#"<td {fmt} colspan="{num_state_cols}">STATE</td>"#,
|
|
|
|
"</tr>",
|
|
|
|
),
|
|
|
|
fmt = "sides=\"tl\"",
|
|
|
|
num_state_cols = state_column_names.len(),
|
|
|
|
block_id = block.index(),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// B
|
|
|
|
let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR);
|
|
|
|
write!(w, concat!("<tr>", r#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?;
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
for name in state_column_names {
|
|
|
|
write!(w, "<td {fmt}>{name}</td>", fmt = fmt, name = name)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(w, "</tr>")
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Write a row with the given index and MIR, using the function argument to fill in the
|
|
|
|
/// "STATE" column(s).
|
|
|
|
fn write_row<W: io::Write>(
|
|
|
|
&mut self,
|
|
|
|
w: &mut W,
|
|
|
|
i: &str,
|
|
|
|
mir: &str,
|
|
|
|
f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
let bg = self.toggle_background();
|
2020-01-21 13:37:59 -08:00
|
|
|
let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" };
|
|
|
|
|
|
|
|
let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr());
|
2019-09-26 16:26:11 -07:00
|
|
|
|
|
|
|
write!(
|
|
|
|
w,
|
2019-11-11 11:48:47 -08:00
|
|
|
concat!(
|
|
|
|
"<tr>",
|
|
|
|
r#"<td {fmt} align="right">{i}</td>"#,
|
|
|
|
r#"<td {fmt} align="left">{mir}</td>"#,
|
|
|
|
),
|
2019-09-26 16:26:11 -07:00
|
|
|
i = i,
|
2019-11-11 11:48:47 -08:00
|
|
|
fmt = fmt,
|
2019-09-26 16:26:11 -07:00
|
|
|
mir = dot::escape_html(mir),
|
|
|
|
)?;
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
f(self, w, &fmt)?;
|
|
|
|
write!(w, "</tr>")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_row_with_full_state(
|
|
|
|
&mut self,
|
|
|
|
w: &mut impl io::Write,
|
|
|
|
i: &str,
|
|
|
|
mir: &str,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
self.write_row(w, i, mir, |this, w, fmt| {
|
|
|
|
let state = this.results.get();
|
|
|
|
let analysis = this.results.analysis();
|
|
|
|
|
2019-09-26 16:26:11 -07:00
|
|
|
write!(
|
|
|
|
w,
|
2019-11-11 11:48:47 -08:00
|
|
|
r#"<td colspan="{colspan}" {fmt} align="left">{{"#,
|
|
|
|
colspan = this.num_state_columns(),
|
|
|
|
fmt = fmt,
|
2019-09-26 16:26:11 -07:00
|
|
|
)?;
|
2020-01-21 13:37:59 -08:00
|
|
|
pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?;
|
2019-11-11 11:48:47 -08:00
|
|
|
write!(w, "}}</td>")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_row_for_location(
|
|
|
|
&mut self,
|
|
|
|
w: &mut impl io::Write,
|
|
|
|
i: &str,
|
|
|
|
mir: &str,
|
|
|
|
location: Location,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
self.write_row(w, i, mir, |this, w, fmt| {
|
|
|
|
this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Controls what gets printed under the `STATE` header.
|
|
|
|
pub trait StateFormatter<'tcx, A>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
/// The columns that will get printed under `STATE`.
|
|
|
|
fn column_names(&self) -> &[&str];
|
|
|
|
|
|
|
|
fn write_state_for_location(
|
|
|
|
&mut self,
|
|
|
|
w: &mut dyn io::Write,
|
|
|
|
fmt: &str,
|
|
|
|
results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
|
|
|
|
location: Location,
|
|
|
|
) -> io::Result<()>;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Prints a single column containing the state vector immediately *after* each statement.
|
|
|
|
pub struct SimpleDiff<T: Idx> {
|
|
|
|
prev_state: BitSet<T>,
|
|
|
|
prev_loc: Location,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Idx> SimpleDiff<T> {
|
|
|
|
pub fn new(bits_per_block: usize) -> Self {
|
|
|
|
SimpleDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A> StateFormatter<'tcx, A> for SimpleDiff<A::Idx>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
fn column_names(&self) -> &[&str] {
|
|
|
|
&[]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_state_for_location(
|
|
|
|
&mut self,
|
|
|
|
mut w: &mut dyn io::Write,
|
|
|
|
fmt: &str,
|
|
|
|
results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
|
|
|
|
location: Location,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
if location.statement_index == 0 {
|
|
|
|
results.seek_to_block_start(location.block);
|
|
|
|
self.prev_state.overwrite(results.get());
|
|
|
|
} else {
|
|
|
|
// Ensure that we are visiting statements in order, so `prev_state` is correct.
|
|
|
|
assert_eq!(self.prev_loc.successor_within_block(), location);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.prev_loc = location;
|
2020-01-21 13:37:59 -08:00
|
|
|
write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?;
|
2020-01-20 18:36:54 -08:00
|
|
|
results.seek_after(location);
|
2019-11-11 11:48:47 -08:00
|
|
|
let curr_state = results.get();
|
|
|
|
write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
|
|
|
|
self.prev_state.overwrite(curr_state);
|
|
|
|
write!(w, "</td>")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Prints two state columns, one containing only the "before" effect of each statement and one
|
|
|
|
/// containing the full effect.
|
|
|
|
pub struct TwoPhaseDiff<T: Idx> {
|
|
|
|
prev_state: BitSet<T>,
|
|
|
|
prev_loc: Location,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Idx> TwoPhaseDiff<T> {
|
|
|
|
pub fn new(bits_per_block: usize) -> Self {
|
|
|
|
TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
fn column_names(&self) -> &[&str] {
|
2020-01-20 18:36:54 -08:00
|
|
|
&["BEFORE", " AFTER"]
|
2019-11-11 11:48:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn write_state_for_location(
|
|
|
|
&mut self,
|
|
|
|
mut w: &mut dyn io::Write,
|
|
|
|
fmt: &str,
|
|
|
|
results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
|
|
|
|
location: Location,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
if location.statement_index == 0 {
|
|
|
|
results.seek_to_block_start(location.block);
|
|
|
|
self.prev_state.overwrite(results.get());
|
|
|
|
} else {
|
|
|
|
// Ensure that we are visiting statements in order, so `prev_state` is correct.
|
|
|
|
assert_eq!(self.prev_loc.successor_within_block(), location);
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
self.prev_loc = location;
|
|
|
|
|
2020-01-20 18:36:54 -08:00
|
|
|
// Before
|
2019-11-11 11:48:47 -08:00
|
|
|
|
|
|
|
write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
|
|
|
|
results.seek_before(location);
|
|
|
|
let curr_state = results.get();
|
|
|
|
write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
|
|
|
|
self.prev_state.overwrite(curr_state);
|
|
|
|
write!(w, "</td>")?;
|
|
|
|
|
2020-01-20 18:36:54 -08:00
|
|
|
// After
|
2019-11-11 11:48:47 -08:00
|
|
|
|
|
|
|
write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
|
|
|
|
results.seek_after(location);
|
|
|
|
let curr_state = results.get();
|
|
|
|
write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
|
|
|
|
self.prev_state.overwrite(curr_state);
|
|
|
|
write!(w, "</td>")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Prints the gen/kill set for the entire block.
|
|
|
|
pub struct BlockTransferFunc<'a, 'tcx, T: Idx> {
|
|
|
|
body: &'a mir::Body<'tcx>,
|
|
|
|
trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
|
|
|
|
pub fn new(
|
|
|
|
body: &'mir mir::Body<'tcx>,
|
|
|
|
trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
|
|
|
|
) -> Self {
|
|
|
|
BlockTransferFunc { body, trans_for_block }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx>
|
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
|
|
|
fn column_names(&self) -> &[&str] {
|
|
|
|
&["GEN", "KILL"]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_state_for_location(
|
|
|
|
&mut self,
|
|
|
|
mut w: &mut dyn io::Write,
|
|
|
|
fmt: &str,
|
|
|
|
results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
|
|
|
|
location: Location,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
// Only print a single row.
|
|
|
|
if location.statement_index != 0 {
|
|
|
|
return Ok(());
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
let block_trans = &self.trans_for_block[location.block];
|
|
|
|
let rowspan = self.body.basic_blocks()[location.block].statements.len();
|
|
|
|
|
|
|
|
for set in &[&block_trans.gen, &block_trans.kill] {
|
2019-09-26 16:26:11 -07:00
|
|
|
write!(
|
|
|
|
w,
|
2020-01-21 13:37:59 -08:00
|
|
|
r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
|
2019-11-11 11:48:47 -08:00
|
|
|
fmt = fmt,
|
|
|
|
rowspan = rowspan
|
2019-09-26 16:26:11 -07:00
|
|
|
)?;
|
2019-11-11 11:48:47 -08:00
|
|
|
|
2020-01-21 13:37:59 -08:00
|
|
|
pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?;
|
2019-11-11 11:48:47 -08:00
|
|
|
write!(w, "</td>")?;
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
Ok(())
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
/// Writes two lines, one containing the added bits and one the removed bits.
|
|
|
|
fn write_diff<A: Analysis<'tcx>>(
|
|
|
|
w: &mut impl io::Write,
|
|
|
|
analysis: &A,
|
|
|
|
from: &BitSet<A::Idx>,
|
|
|
|
to: &BitSet<A::Idx>,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
assert_eq!(from.domain_size(), to.domain_size());
|
|
|
|
let len = from.domain_size();
|
|
|
|
|
|
|
|
let mut set = HybridBitSet::new_empty(len);
|
|
|
|
let mut clear = HybridBitSet::new_empty(len);
|
|
|
|
|
|
|
|
// FIXME: Implement a lazy iterator over the symmetric difference of two bitsets.
|
2020-03-22 12:43:19 +01:00
|
|
|
for i in (0..len).map(A::Idx::new) {
|
2019-11-11 11:48:47 -08:00
|
|
|
match (from.contains(i), to.contains(i)) {
|
|
|
|
(false, true) => set.insert(i),
|
|
|
|
(true, false) => clear.insert(i),
|
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
}
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
if !set.is_empty() {
|
|
|
|
write!(w, r#"<font color="darkgreen">+"#)?;
|
2020-01-21 13:37:59 -08:00
|
|
|
pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?;
|
2019-11-11 11:48:47 -08:00
|
|
|
write!(w, r#"</font>"#)?;
|
|
|
|
}
|
2019-09-26 16:26:11 -07:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
if !set.is_empty() && !clear.is_empty() {
|
2020-01-21 13:37:59 -08:00
|
|
|
write!(w, "{}", BR_LEFT)?;
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
2019-11-11 11:48:47 -08:00
|
|
|
|
|
|
|
if !clear.is_empty() {
|
|
|
|
write!(w, r#"<font color="red">-"#)?;
|
2020-01-21 13:37:59 -08:00
|
|
|
pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?;
|
2019-11-11 11:48:47 -08:00
|
|
|
write!(w, r#"</font>"#)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
2020-03-05 11:33:05 +01:00
|
|
|
const BR_LEFT: &str = r#"<br align="left"/>"#;
|
|
|
|
const BR_LEFT_SPACE: &str = r#"<br align="left"/> "#;
|
2020-01-21 13:37:59 -08:00
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
/// Line break policy that breaks at 40 characters and starts the next line with a single space.
|
2020-01-21 13:37:59 -08:00
|
|
|
const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 });
|
2019-11-11 11:48:47 -08:00
|
|
|
|
|
|
|
struct LineBreak {
|
|
|
|
sequence: &'static str,
|
|
|
|
limit: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
|
|
|
|
/// separator (`sep`).
|
|
|
|
///
|
|
|
|
/// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
|
|
|
|
/// character limit.
|
2019-09-26 16:26:11 -07:00
|
|
|
fn pretty_print_state_elems<A>(
|
|
|
|
w: &mut impl io::Write,
|
|
|
|
analysis: &A,
|
|
|
|
elems: impl Iterator<Item = A::Idx>,
|
2019-11-11 11:48:47 -08:00
|
|
|
sep: &str,
|
|
|
|
line_break: Option<LineBreak>,
|
|
|
|
) -> io::Result<bool>
|
2019-09-26 16:26:11 -07:00
|
|
|
where
|
|
|
|
A: Analysis<'tcx>,
|
|
|
|
{
|
2019-11-11 11:48:47 -08:00
|
|
|
let sep_width = sep.chars().count();
|
|
|
|
|
|
|
|
let mut buf = Vec::new();
|
|
|
|
|
2019-09-26 16:26:11 -07:00
|
|
|
let mut first = true;
|
2019-11-11 11:48:47 -08:00
|
|
|
let mut curr_line_width = 0;
|
|
|
|
let mut line_break_inserted = false;
|
|
|
|
|
2019-09-26 16:26:11 -07:00
|
|
|
for idx in elems {
|
2019-11-11 11:48:47 -08:00
|
|
|
buf.clear();
|
|
|
|
analysis.pretty_print_idx(&mut buf, idx)?;
|
|
|
|
let idx_str =
|
|
|
|
str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8");
|
|
|
|
let escaped = dot::escape_html(idx_str);
|
|
|
|
let escaped_width = escaped.chars().count();
|
|
|
|
|
2020-01-21 13:37:02 -08:00
|
|
|
if first {
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
write!(w, "{}", sep)?;
|
|
|
|
curr_line_width += sep_width;
|
|
|
|
|
|
|
|
if let Some(line_break) = &line_break {
|
|
|
|
if curr_line_width + sep_width + escaped_width > line_break.limit {
|
|
|
|
write!(w, "{}", line_break.sequence)?;
|
|
|
|
line_break_inserted = true;
|
|
|
|
curr_line_width = 0;
|
|
|
|
}
|
2019-11-11 11:48:47 -08:00
|
|
|
}
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
write!(w, "{}", escaped)?;
|
|
|
|
curr_line_width += escaped_width;
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
2019-11-11 11:48:47 -08:00
|
|
|
Ok(line_break_inserted)
|
2019-09-26 16:26:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The background color used for zebra-striping the table.
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
enum Background {
|
|
|
|
Light,
|
|
|
|
Dark,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Background {
|
|
|
|
fn attr(self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::Dark => "bgcolor=\"#f0f0f0\"",
|
|
|
|
Self::Light => "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Not for Background {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn not(self) -> Self {
|
|
|
|
match self {
|
|
|
|
Self::Light => Self::Dark,
|
|
|
|
Self::Dark => Self::Light,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|