1 use std::cell::RefCell;
2 use std::io::{self, Write};
5 use rustc::mir::{self, BasicBlock, Body, Location};
6 use rustc_hir::def_id::DefId;
7 use rustc_index::bit_set::{BitSet, HybridBitSet};
8 use rustc_index::vec::Idx;
10 use super::{Analysis, Results, ResultsRefCursor};
11 use crate::util::graphviz_safe_def_name;
13 pub struct Formatter<'a, 'tcx, A>
20 // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
21 block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
24 impl<A> Formatter<'a, 'tcx, A>
28 pub fn new(body: &'a Body<'tcx>, def_id: DefId, results: &'a Results<'tcx, A>) -> Self {
29 let block_formatter = BlockFormatter {
30 bg: Background::Light,
31 prev_state: BitSet::new_empty(results.analysis.bits_per_block(body)),
32 results: ResultsRefCursor::new(body, results),
35 Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
39 /// A pair of a basic block and an index into that basic blocks `successors`.
40 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
46 fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
51 .map(|(index, _)| CfgEdge { source: bb, index })
55 impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
59 type Node = BasicBlock;
62 fn graph_id(&self) -> dot::Id<'_> {
63 let name = graphviz_safe_def_name(self.def_id);
64 dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
67 fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
68 dot::Id::new(format!("bb_{}", n.index())).unwrap()
71 fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
72 let mut label = Vec::new();
73 self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap();
74 dot::LabelText::html(String::from_utf8(label).unwrap())
77 fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
78 Some(dot::LabelText::label("none"))
81 fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
82 let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index];
83 dot::LabelText::label(label.clone())
87 impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
91 type Node = BasicBlock;
94 fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
95 self.body.basic_blocks().indices().collect::<Vec<_>>().into()
98 fn edges(&self) -> dot::Edges<'_, Self::Edge> {
102 .flat_map(|bb| outgoing_edges(self.body, bb))
107 fn source(&self, edge: &Self::Edge) -> Self::Node {
111 fn target(&self, edge: &Self::Edge) -> Self::Node {
112 self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap()
116 struct BlockFormatter<'a, 'tcx, A>
120 prev_state: BitSet<A::Idx>,
121 results: ResultsRefCursor<'a, 'a, 'tcx, A>,
125 impl<A> BlockFormatter<'a, 'tcx, A>
129 fn toggle_background(&mut self) -> Background {
137 w: &mut impl io::Write,
138 body: &'a Body<'tcx>,
140 ) -> io::Result<()> {
142 // +-+-----------------------------------------------+
144 // +-+----------------------------------+------------+
146 // +-+----------------------------------+------------+
147 // C | | (on entry) | {_0,_2,_3} |
148 // +-+----------------------------------+------------+
149 // D |0| StorageLive(_7) | |
150 // +-+----------------------------------+------------+
151 // |1| StorageLive(_8) | |
152 // +-+----------------------------------+------------+
153 // |2| _8 = &mut _1 | +_8 |
154 // +-+----------------------------------+------------+
155 // E |T| _4 = const Foo::twiddle(move _2) | -_2 |
156 // +-+----------------------------------+------------+
157 // F | | (on unwind) | {_0,_3,_8} |
158 // +-+----------------------------------+------------+
159 // | | (on successful return) | +_4 |
160 // +-+----------------------------------+------------+
164 r#"<table border="1" cellborder="1" cellspacing="0" cellpadding="3" sides="rb">"#,
171 <td colspan="{num_headers}" sides="tl">bb{block_id}</td>
174 block_id = block.index(),
177 // B: Column headings
181 <td colspan="2" {fmt}>MIR</td>
184 fmt = r##"bgcolor="#a0a0a0" sides="tl""##,
188 self.bg = Background::Light;
189 self.results.seek_to_block_start(block);
190 self.write_row_with_curr_state(w, "", "(on entry)")?;
191 self.prev_state.overwrite(self.results.get());
193 // D: Statement transfer functions
194 for (i, statement) in body[block].statements.iter().enumerate() {
195 let location = Location { block, statement_index: i };
197 let mir_col = format!("{:?}", statement);
198 let i_col = i.to_string();
200 self.results.seek_after(location);
201 self.write_row_with_curr_diff(w, &i_col, &mir_col)?;
202 self.prev_state.overwrite(self.results.get());
205 // E: Terminator transfer function
206 let terminator = body[block].terminator();
207 let location = body.terminator_loc(block);
209 let mut mir_col = String::new();
210 terminator.kind.fmt_head(&mut mir_col).unwrap();
212 self.results.seek_after(location);
213 self.write_row_with_curr_diff(w, "T", &mir_col)?;
214 self.prev_state.overwrite(self.results.get());
217 if let mir::TerminatorKind::Call { destination: Some(_), .. } = &terminator.kind {
218 self.write_row_with_curr_state(w, "", "(on unwind)")?;
220 self.results.seek_after_assume_call_returns(location);
221 self.write_row_with_curr_diff(w, "", "(on successful return)")?;
223 self.write_row_with_curr_state(w, "", "(on exit)")?;
226 write!(w, "</table>")
229 fn write_row_with_curr_state(
231 w: &mut impl io::Write,
234 ) -> io::Result<()> {
235 let bg = self.toggle_background();
237 let mut out = Vec::new();
238 write!(&mut out, "{{")?;
239 pretty_print_state_elems(&mut out, self.results.analysis(), self.results.get().iter())?;
240 write!(&mut out, "}}")?;
245 <td {fmt} align="right">{i}</td>
246 <td {fmt} align="left">{mir}</td>
247 <td {fmt} align="left">{state}</td>
249 fmt = &["sides=\"tl\"", bg.attr()].join(" "),
251 mir = dot::escape_html(mir),
252 state = dot::escape_html(str::from_utf8(&out).unwrap()),
256 fn write_row_with_curr_diff(
258 w: &mut impl io::Write,
261 ) -> io::Result<()> {
262 let bg = self.toggle_background();
263 let analysis = self.results.analysis();
265 let diff = BitSetDiff::compute(&self.prev_state, self.results.get());
267 let mut set = Vec::new();
268 pretty_print_state_elems(&mut set, analysis, diff.set.iter())?;
270 let mut clear = Vec::new();
271 pretty_print_state_elems(&mut clear, analysis, diff.clear.iter())?;
276 <td {fmt} align="right">{i}</td>
277 <td {fmt} align="left">{mir}</td>
278 <td {fmt} align="left">"#,
280 fmt = &["sides=\"tl\"", bg.attr()].join(" "),
281 mir = dot::escape_html(mir),
287 r#"<font color="darkgreen">+{}</font>"#,
288 dot::escape_html(str::from_utf8(&set).unwrap()),
292 if !set.is_empty() && !clear.is_empty() {
296 if !clear.is_empty() {
299 r#"<font color="red">-{}</font>"#,
300 dot::escape_html(str::from_utf8(&clear).unwrap()),
304 write!(w, "</td></tr>")
308 /// The operations required to transform one `BitSet` into another.
309 struct BitSetDiff<T: Idx> {
310 set: HybridBitSet<T>,
311 clear: HybridBitSet<T>,
314 impl<T: Idx> BitSetDiff<T> {
315 fn compute(from: &BitSet<T>, to: &BitSet<T>) -> Self {
316 assert_eq!(from.domain_size(), to.domain_size());
317 let len = from.domain_size();
319 let mut set = HybridBitSet::new_empty(len);
320 let mut clear = HybridBitSet::new_empty(len);
322 // FIXME: This could be made faster if `BitSet::xor` were implemented.
323 for i in (0..len).map(|i| T::new(i)) {
324 match (from.contains(i), to.contains(i)) {
325 (false, true) => set.insert(i),
326 (true, false) => clear.insert(i),
331 BitSetDiff { set, clear }
335 /// Formats each `elem` using the pretty printer provided by `analysis` into a comma-separated
337 fn pretty_print_state_elems<A>(
338 w: &mut impl io::Write,
340 elems: impl Iterator<Item = A::Idx>,
345 let mut first = true;
353 analysis.pretty_print_idx(w, idx)?;
359 /// The background color used for zebra-striping the table.
360 #[derive(Clone, Copy)]
367 fn attr(self) -> &'static str {
369 Self::Dark => "bgcolor=\"#f0f0f0\"",
375 impl ops::Not for Background {
378 fn not(self) -> Self {
380 Self::Light => Self::Dark,
381 Self::Dark => Self::Light,