1 //! A helpful diagram for debugging dataflow problems.
3 use std
::cell
::RefCell
;
4 use std
::{io, ops, str}
;
6 use rustc_hir
::def_id
::DefId
;
7 use rustc_index
::bit_set
::{BitSet, HybridBitSet}
;
8 use rustc_index
::vec
::{Idx, IndexVec}
;
9 use rustc_middle
::mir
::{self, BasicBlock, Body, Location}
;
11 use super::{Analysis, Direction, 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 dataflow_successors(body
: &Body
<'tcx
>, 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
| dataflow_successors(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
)?
;
195 // C: State at start of 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 start)")?
;
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
)?
;
217 // F: State at end of block
219 // Write the full dataflow state immediately after the terminator if it differs from the
220 // state at block entry.
221 self.results
.seek_to_block_end(block
);
222 if self.results
.get() != &block_entry_state
|| A
::Direction
::is_backward() {
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 let num_state_columns
= self.num_state_columns();
233 match terminator
.kind
{
234 mir
::TerminatorKind
::Call
{
235 destination
: Some((return_place
, _
)),
240 self.write_row(w
, "", "(on successful return)", |this
, w
, fmt
| {
243 r
#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
244 colspan
= num_state_columns
,
248 let state_on_unwind
= this
.results
.get().clone();
249 this
.results
.apply_custom_effect(|analysis
, state
| {
250 analysis
.apply_call_return_effect(state
, block
, func
, args
, return_place
);
253 write_diff(w
, this
.results
.analysis(), &state_on_unwind
, this
.results
.get())?
;
258 mir
::TerminatorKind
::Yield { resume, resume_arg, .. }
=> {
259 self.write_row(w
, "", "(on yield resume)", |this
, w
, fmt
| {
262 r
#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
263 colspan
= num_state_columns
,
267 let state_on_generator_drop
= this
.results
.get().clone();
268 this
.results
.apply_custom_effect(|analysis
, state
| {
269 analysis
.apply_yield_resume_effect(state
, resume
, resume_arg
);
274 this
.results
.analysis(),
275 &state_on_generator_drop
,
285 write
!(w
, "</table>")
288 fn write_block_header_simple(
290 w
: &mut impl io
::Write
,
292 ) -> io
::Result
<()> {
293 // +-------------------------------------------------+
295 // +-----------------------------------+-------------+
297 // +-+---------------------------------+-------------+
303 concat
!("<tr>", r
#"<td colspan="3" sides="tl">bb{block_id}</td>"#, "</tr>",),
304 block_id
= block
.index(),
312 r
#"<td colspan="2" {fmt}>MIR</td>"#,
313 r
#"<td {fmt}>STATE</td>"#,
316 fmt
= format
!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR
),
320 fn write_block_header_with_state_columns(
322 w
: &mut impl io
::Write
,
324 ) -> io
::Result
<()> {
325 // +------------------------------------+-------------+
327 // +------------------------------------+------+------+
328 // B | MIR | GEN | KILL |
329 // +-+----------------------------------+------+------+
332 let state_column_names
= self.state_formatter
.column_names();
339 r
#"<td {fmt} colspan="2">bb{block_id}</td>"#,
340 r
#"<td {fmt} colspan="{num_state_cols}">STATE</td>"#,
343 fmt
= "sides=\"tl\"",
344 num_state_cols
= state_column_names
.len(),
345 block_id
= block
.index(),
349 let fmt
= format
!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR
);
350 write
!(w
, concat
!("<tr>", r
#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?;
352 for name
in state_column_names
{
353 write
!(w
, "<td {fmt}>{name}</td>", fmt
= fmt
, name
= name
)?
;
359 /// Write a row with the given index and MIR, using the function argument to fill in the
360 /// "STATE" column(s).
361 fn write_row
<W
: io
::Write
>(
366 f
: impl FnOnce(&mut Self, &mut W
, &str) -> io
::Result
<()>,
367 ) -> io
::Result
<()> {
368 let bg
= self.toggle_background();
369 let valign
= if mir
.starts_with("(on ") && mir
!= "(on entry)" { "bottom" }
else { "top" }
;
371 let fmt
= format
!("valign=\"{}\" sides=\"tl\" {}", valign
, bg
.attr());
377 r
#"<td {fmt} align="right">{i}</td>"#,
378 r
#"<td {fmt} align="left">{mir}</td>"#,
382 mir
= dot
::escape_html(mir
),
389 fn write_row_with_full_state(
391 w
: &mut impl io
::Write
,
394 ) -> io
::Result
<()> {
395 self.write_row(w
, i
, mir
, |this
, w
, fmt
| {
396 let state
= this
.results
.get();
397 let analysis
= this
.results
.analysis();
401 r
#"<td colspan="{colspan}" {fmt} align="left">{{"#,
402 colspan
= this
.num_state_columns(),
405 pretty_print_state_elems(w
, analysis
, state
.iter(), ", ", LIMIT_30_ALIGN_1
)?
;
410 fn write_row_for_location(
412 w
: &mut impl io
::Write
,
416 ) -> io
::Result
<()> {
417 self.write_row(w
, i
, mir
, |this
, w
, fmt
| {
418 this
.state_formatter
.write_state_for_location(w
, fmt
, &mut this
.results
, location
)
423 /// Controls what gets printed under the `STATE` header.
424 pub trait StateFormatter
<'tcx
, A
>
428 /// The columns that will get printed under `STATE`.
429 fn column_names(&self) -> &[&str];
431 fn write_state_for_location(
433 w
: &mut dyn io
::Write
,
435 results
: &mut ResultsRefCursor
<'_
, '_
, 'tcx
, A
>,
440 /// Prints a single column containing the state vector immediately *after* each statement.
441 pub struct SimpleDiff
<'a
, 'tcx
, A
>
445 prev_state
: ResultsRefCursor
<'a
, 'a
, 'tcx
, A
>,
448 impl<A
> SimpleDiff
<'a
, 'tcx
, A
>
452 pub fn new(body
: &'a Body
<'tcx
>, results
: &'a Results
<'tcx
, A
>) -> Self {
453 SimpleDiff { prev_state: ResultsRefCursor::new(body, results) }
457 impl<A
> StateFormatter
<'tcx
, A
> for SimpleDiff
<'_
, 'tcx
, A
>
461 fn column_names(&self) -> &[&str] {
465 fn write_state_for_location(
467 mut w
: &mut dyn io
::Write
,
469 results
: &mut ResultsRefCursor
<'_
, '_
, 'tcx
, A
>,
471 ) -> io
::Result
<()> {
472 if A
::Direction
::is_forward() {
473 if location
.statement_index
== 0 {
474 self.prev_state
.seek_to_block_start(location
.block
);
476 self.prev_state
.seek_after_primary_effect(Location
{
477 statement_index
: location
.statement_index
- 1,
482 if location
== results
.body().terminator_loc(location
.block
) {
483 self.prev_state
.seek_to_block_end(location
.block
);
485 self.prev_state
.seek_after_primary_effect(location
.successor_within_block());
489 write
!(w
, r
#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?;
490 results
.seek_after_primary_effect(location
);
491 let curr_state
= results
.get();
492 write_diff(&mut w
, results
.analysis(), self.prev_state
.get(), curr_state
)?
;
497 /// Prints two state columns, one containing only the "before" effect of each statement and one
498 /// containing the full effect.
499 pub struct TwoPhaseDiff
<T
: Idx
> {
500 prev_state
: BitSet
<T
>,
504 impl<T
: Idx
> TwoPhaseDiff
<T
> {
505 pub fn new(bits_per_block
: usize) -> Self {
506 TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
510 impl<A
> StateFormatter
<'tcx
, A
> for TwoPhaseDiff
<A
::Idx
>
514 fn column_names(&self) -> &[&str] {
515 &["BEFORE", " AFTER"]
518 fn write_state_for_location(
520 mut w
: &mut dyn io
::Write
,
522 results
: &mut ResultsRefCursor
<'_
, '_
, 'tcx
, A
>,
524 ) -> io
::Result
<()> {
525 if location
.statement_index
== 0 {
526 results
.seek_to_block_entry(location
.block
);
527 self.prev_state
.overwrite(results
.get());
529 // Ensure that we are visiting statements in order, so `prev_state` is correct.
530 assert_eq
!(self.prev_loc
.successor_within_block(), location
);
533 self.prev_loc
= location
;
537 write
!(w
, r
#"<td {fmt} align="left">"#, fmt = fmt)?;
538 results
.seek_before_primary_effect(location
);
539 let curr_state
= results
.get();
540 write_diff(&mut w
, results
.analysis(), &self.prev_state
, curr_state
)?
;
541 self.prev_state
.overwrite(curr_state
);
546 write
!(w
, r
#"<td {fmt} align="left">"#, fmt = fmt)?;
547 results
.seek_after_primary_effect(location
);
548 let curr_state
= results
.get();
549 write_diff(&mut w
, results
.analysis(), &self.prev_state
, curr_state
)?
;
550 self.prev_state
.overwrite(curr_state
);
555 /// Prints the gen/kill set for the entire block.
556 pub struct BlockTransferFunc
<'a
, 'tcx
, T
: Idx
> {
557 body
: &'a mir
::Body
<'tcx
>,
558 trans_for_block
: IndexVec
<BasicBlock
, GenKillSet
<T
>>,
561 impl<T
: Idx
> BlockTransferFunc
<'mir
, 'tcx
, T
> {
563 body
: &'mir mir
::Body
<'tcx
>,
564 trans_for_block
: IndexVec
<BasicBlock
, GenKillSet
<T
>>,
566 BlockTransferFunc { body, trans_for_block }
570 impl<A
> StateFormatter
<'tcx
, A
> for BlockTransferFunc
<'mir
, 'tcx
, A
::Idx
>
574 fn column_names(&self) -> &[&str] {
578 fn write_state_for_location(
580 mut w
: &mut dyn io
::Write
,
582 results
: &mut ResultsRefCursor
<'_
, '_
, 'tcx
, A
>,
584 ) -> io
::Result
<()> {
585 // Only print a single row.
586 if location
.statement_index
!= 0 {
590 let block_trans
= &self.trans_for_block
[location
.block
];
591 let rowspan
= self.body
.basic_blocks()[location
.block
].statements
.len();
593 for set
in &[&block_trans
.gen
, &block_trans
.kill
] {
596 r
#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
601 pretty_print_state_elems(&mut w
, results
.analysis(), set
.iter(), BR_LEFT
, None
)?
;
609 /// Writes two lines, one containing the added bits and one the removed bits.
610 fn write_diff
<A
: Analysis
<'tcx
>>(
611 w
: &mut impl io
::Write
,
613 from
: &BitSet
<A
::Idx
>,
615 ) -> io
::Result
<()> {
616 assert_eq
!(from
.domain_size(), to
.domain_size());
617 let len
= from
.domain_size();
619 let mut set
= HybridBitSet
::new_empty(len
);
620 let mut clear
= HybridBitSet
::new_empty(len
);
622 // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets.
623 for i
in (0..len
).map(A
::Idx
::new
) {
624 match (from
.contains(i
), to
.contains(i
)) {
625 (false, true) => set
.insert(i
),
626 (true, false) => clear
.insert(i
),
632 write
!(w
, r
#"<font color="darkgreen">+"#)?;
633 pretty_print_state_elems(w
, analysis
, set
.iter(), ", ", LIMIT_30_ALIGN_1
)?
;
634 write
!(w
, r
#"</font>"#)?;
637 if !set
.is_empty() && !clear
.is_empty() {
638 write
!(w
, "{}", BR_LEFT
)?
;
641 if !clear
.is_empty() {
642 write
!(w
, r
#"<font color="red">-"#)?;
643 pretty_print_state_elems(w
, analysis
, clear
.iter(), ", ", LIMIT_30_ALIGN_1
)?
;
644 write
!(w
, r
#"</font>"#)?;
650 const BR_LEFT
: &str = r
#"<br align="left"/>"#;
651 const BR_LEFT_SPACE
: &str = r
#"<br align="left"/> "#;
653 /// Line break policy that breaks at 40 characters and starts the next line with a single space.
654 const LIMIT_30_ALIGN_1
: Option
<LineBreak
> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 }
);
657 sequence
: &'
static str,
661 /// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
662 /// separator (`sep`).
664 /// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
666 fn pretty_print_state_elems
<A
>(
667 w
: &mut impl io
::Write
,
669 elems
: impl Iterator
<Item
= A
::Idx
>,
671 line_break
: Option
<LineBreak
>,
672 ) -> io
::Result
<bool
>
676 let sep_width
= sep
.chars().count();
678 let mut buf
= Vec
::new();
680 let mut first
= true;
681 let mut curr_line_width
= 0;
682 let mut line_break_inserted
= false;
686 analysis
.pretty_print_idx(&mut buf
, idx
)?
;
688 str::from_utf8(&buf
).expect("Output of `pretty_print_idx` must be valid UTF-8");
689 let escaped
= dot
::escape_html(idx_str
);
690 let escaped_width
= escaped
.chars().count();
695 write
!(w
, "{}", sep
)?
;
696 curr_line_width
+= sep_width
;
698 if let Some(line_break
) = &line_break
{
699 if curr_line_width
+ sep_width
+ escaped_width
> line_break
.limit
{
700 write
!(w
, "{}", line_break
.sequence
)?
;
701 line_break_inserted
= true;
707 write
!(w
, "{}", escaped
)?
;
708 curr_line_width
+= escaped_width
;
711 Ok(line_break_inserted
)
714 /// The background color used for zebra-striping the table.
715 #[derive(Clone, Copy)]
722 fn attr(self) -> &'
static str {
724 Self::Dark
=> "bgcolor=\"#f0f0f0\"",
730 impl ops
::Not
for Background
{
733 fn not(self) -> Self {
735 Self::Light
=> Self::Dark
,
736 Self::Dark
=> Self::Light
,