]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/generic/graphviz.rs
Update const_forget.rs
[rust.git] / src / librustc_mir / dataflow / generic / graphviz.rs
1 //! A helpful diagram for debugging dataflow problems.
2
3 use std::cell::RefCell;
4 use std::{io, ops, str};
5
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};
10
11 use super::{Analysis, GenKillSet, Results, ResultsRefCursor};
12 use crate::util::graphviz_safe_def_name;
13
14 pub struct Formatter<'a, 'tcx, A>
15 where
16     A: Analysis<'tcx>,
17 {
18     body: &'a Body<'tcx>,
19     def_id: DefId,
20
21     // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
22     block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
23 }
24
25 impl<A> Formatter<'a, 'tcx, A>
26 where
27     A: Analysis<'tcx>,
28 {
29     pub fn new(
30         body: &'a Body<'tcx>,
31         def_id: DefId,
32         results: &'a Results<'tcx, A>,
33         state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
34     ) -> Self {
35         let block_formatter = BlockFormatter {
36             bg: Background::Light,
37             results: ResultsRefCursor::new(body, results),
38             state_formatter,
39         };
40
41         Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
42     }
43 }
44
45 /// A pair of a basic block and an index into that basic blocks `successors`.
46 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
47 pub struct CfgEdge {
48     source: BasicBlock,
49     index: usize,
50 }
51
52 fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
53     body[bb]
54         .terminator()
55         .successors()
56         .enumerate()
57         .map(|(index, _)| CfgEdge { source: bb, index })
58         .collect()
59 }
60
61 impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
62 where
63     A: Analysis<'tcx>,
64 {
65     type Node = BasicBlock;
66     type Edge = CfgEdge;
67
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()
71     }
72
73     fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
74         dot::Id::new(format!("bb_{}", n.index())).unwrap()
75     }
76
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())
81     }
82
83     fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
84         Some(dot::LabelText::label("none"))
85     }
86
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())
90     }
91 }
92
93 impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
94 where
95     A: Analysis<'tcx>,
96 {
97     type Node = BasicBlock;
98     type Edge = CfgEdge;
99
100     fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
101         self.body.basic_blocks().indices().collect::<Vec<_>>().into()
102     }
103
104     fn edges(&self) -> dot::Edges<'_, Self::Edge> {
105         self.body
106             .basic_blocks()
107             .indices()
108             .flat_map(|bb| outgoing_edges(self.body, bb))
109             .collect::<Vec<_>>()
110             .into()
111     }
112
113     fn source(&self, edge: &Self::Edge) -> Self::Node {
114         edge.source
115     }
116
117     fn target(&self, edge: &Self::Edge) -> Self::Node {
118         self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap()
119     }
120 }
121
122 struct BlockFormatter<'a, 'tcx, A>
123 where
124     A: Analysis<'tcx>,
125 {
126     results: ResultsRefCursor<'a, 'a, 'tcx, A>,
127     bg: Background,
128     state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
129 }
130
131 impl<A> BlockFormatter<'a, 'tcx, A>
132 where
133     A: Analysis<'tcx>,
134 {
135     const HEADER_COLOR: &'static str = "#a0a0a0";
136
137     fn num_state_columns(&self) -> usize {
138         std::cmp::max(1, self.state_formatter.column_names().len())
139     }
140
141     fn toggle_background(&mut self) -> Background {
142         let bg = self.bg;
143         self.bg = !bg;
144         bg
145     }
146
147     fn write_node_label(
148         &mut self,
149         w: &mut impl io::Write,
150         body: &'a Body<'tcx>,
151         block: BasicBlock,
152     ) -> io::Result<()> {
153         //   Sample output:
154         //   +-+-----------------------------------------------+
155         // A |                      bb4                        |
156         //   +-+----------------------------------+------------+
157         // B |                MIR                 |   STATE    |
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         //   +-+----------------------------------+------------+
173
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>.
178
179         let table_fmt = concat!(
180             " border=\"1\"",
181             " cellborder=\"1\"",
182             " cellspacing=\"0\"",
183             " cellpadding=\"3\"",
184             " sides=\"rb\"",
185         );
186         write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
187
188         // A + B: Block header
189         if self.state_formatter.column_names().is_empty() {
190             self.write_block_header_simple(w, block)?;
191         } else {
192             self.write_block_header_with_state_columns(w, block)?;
193         }
194
195         // C: Entry state
196         self.bg = Background::Light;
197         self.results.seek_to_block_start(block);
198         let block_entry_state = self.results.get().clone();
199
200         self.write_row_with_full_state(w, "", "(on entry)")?;
201
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)?;
207         }
208
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();
214
215         self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?;
216
217         // F: Exit state
218
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)",
225                 _ => "(on exit)",
226             };
227
228             self.write_row_with_full_state(w, "", after_terminator_name)?;
229         }
230
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| {
236                     write!(
237                         w,
238                         r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
239                         colspan = num_state_columns,
240                         fmt = fmt,
241                     )?;
242
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())?;
246
247                     write!(w, "</td>")
248                 })?;
249             }
250
251             _ => {}
252         };
253
254         write!(w, "</table>")
255     }
256
257     fn write_block_header_simple(
258         &mut self,
259         w: &mut impl io::Write,
260         block: BasicBlock,
261     ) -> io::Result<()> {
262         //   +-------------------------------------------------+
263         // A |                      bb4                        |
264         //   +-----------------------------------+-------------+
265         // B |                MIR                |    STATE    |
266         //   +-+---------------------------------+-------------+
267         //   | |              ...                |             |
268
269         // A
270         write!(
271             w,
272             concat!("<tr>", r#"<td colspan="3" sides="tl">bb{block_id}</td>"#, "</tr>",),
273             block_id = block.index(),
274         )?;
275
276         // B
277         write!(
278             w,
279             concat!(
280                 "<tr>",
281                 r#"<td colspan="2" {fmt}>MIR</td>"#,
282                 r#"<td {fmt}>STATE</td>"#,
283                 "</tr>",
284             ),
285             fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR),
286         )
287     }
288
289     fn write_block_header_with_state_columns(
290         &mut self,
291         w: &mut impl io::Write,
292         block: BasicBlock,
293     ) -> io::Result<()> {
294         //   +------------------------------------+-------------+
295         // A |                bb4                 |    STATE    |
296         //   +------------------------------------+------+------+
297         // B |                MIR                 |  GEN | KILL |
298         //   +-+----------------------------------+------+------+
299         //   | |              ...                 |      |      |
300
301         let state_column_names = self.state_formatter.column_names();
302
303         // A
304         write!(
305             w,
306             concat!(
307                 "<tr>",
308                 r#"<td {fmt} colspan="2">bb{block_id}</td>"#,
309                 r#"<td {fmt} colspan="{num_state_cols}">STATE</td>"#,
310                 "</tr>",
311             ),
312             fmt = "sides=\"tl\"",
313             num_state_cols = state_column_names.len(),
314             block_id = block.index(),
315         )?;
316
317         // B
318         let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR);
319         write!(w, concat!("<tr>", r#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?;
320
321         for name in state_column_names {
322             write!(w, "<td {fmt}>{name}</td>", fmt = fmt, name = name)?;
323         }
324
325         write!(w, "</tr>")
326     }
327
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>(
331         &mut self,
332         w: &mut W,
333         i: &str,
334         mir: &str,
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" };
339
340         let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr());
341
342         write!(
343             w,
344             concat!(
345                 "<tr>",
346                 r#"<td {fmt} align="right">{i}</td>"#,
347                 r#"<td {fmt} align="left">{mir}</td>"#,
348             ),
349             i = i,
350             fmt = fmt,
351             mir = dot::escape_html(mir),
352         )?;
353
354         f(self, w, &fmt)?;
355         write!(w, "</tr>")
356     }
357
358     fn write_row_with_full_state(
359         &mut self,
360         w: &mut impl io::Write,
361         i: &str,
362         mir: &str,
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();
367
368             write!(
369                 w,
370                 r#"<td colspan="{colspan}" {fmt} align="left">{{"#,
371                 colspan = this.num_state_columns(),
372                 fmt = fmt,
373             )?;
374             pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?;
375             write!(w, "}}</td>")
376         })
377     }
378
379     fn write_row_for_location(
380         &mut self,
381         w: &mut impl io::Write,
382         i: &str,
383         mir: &str,
384         location: Location,
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)
388         })
389     }
390 }
391
392 /// Controls what gets printed under the `STATE` header.
393 pub trait StateFormatter<'tcx, A>
394 where
395     A: Analysis<'tcx>,
396 {
397     /// The columns that will get printed under `STATE`.
398     fn column_names(&self) -> &[&str];
399
400     fn write_state_for_location(
401         &mut self,
402         w: &mut dyn io::Write,
403         fmt: &str,
404         results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
405         location: Location,
406     ) -> io::Result<()>;
407 }
408
409 /// Prints a single column containing the state vector immediately *after* each statement.
410 pub struct SimpleDiff<T: Idx> {
411     prev_state: BitSet<T>,
412     prev_loc: Location,
413 }
414
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 }
418     }
419 }
420
421 impl<A> StateFormatter<'tcx, A> for SimpleDiff<A::Idx>
422 where
423     A: Analysis<'tcx>,
424 {
425     fn column_names(&self) -> &[&str] {
426         &[]
427     }
428
429     fn write_state_for_location(
430         &mut self,
431         mut w: &mut dyn io::Write,
432         fmt: &str,
433         results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
434         location: Location,
435     ) -> io::Result<()> {
436         if location.statement_index == 0 {
437             results.seek_to_block_start(location.block);
438             self.prev_state.overwrite(results.get());
439         } else {
440             // Ensure that we are visiting statements in order, so `prev_state` is correct.
441             assert_eq!(self.prev_loc.successor_within_block(), location);
442         }
443
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);
450         write!(w, "</td>")
451     }
452 }
453
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>,
458     prev_loc: Location,
459 }
460
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 }
464     }
465 }
466
467 impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx>
468 where
469     A: Analysis<'tcx>,
470 {
471     fn column_names(&self) -> &[&str] {
472         &["BEFORE", " AFTER"]
473     }
474
475     fn write_state_for_location(
476         &mut self,
477         mut w: &mut dyn io::Write,
478         fmt: &str,
479         results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
480         location: Location,
481     ) -> io::Result<()> {
482         if location.statement_index == 0 {
483             results.seek_to_block_start(location.block);
484             self.prev_state.overwrite(results.get());
485         } else {
486             // Ensure that we are visiting statements in order, so `prev_state` is correct.
487             assert_eq!(self.prev_loc.successor_within_block(), location);
488         }
489
490         self.prev_loc = location;
491
492         // Before
493
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);
499         write!(w, "</td>")?;
500
501         // After
502
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);
508         write!(w, "</td>")
509     }
510 }
511
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>>,
516 }
517
518 impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
519     pub fn new(
520         body: &'mir mir::Body<'tcx>,
521         trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
522     ) -> Self {
523         BlockTransferFunc { body, trans_for_block }
524     }
525 }
526
527 impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx>
528 where
529     A: Analysis<'tcx>,
530 {
531     fn column_names(&self) -> &[&str] {
532         &["GEN", "KILL"]
533     }
534
535     fn write_state_for_location(
536         &mut self,
537         mut w: &mut dyn io::Write,
538         fmt: &str,
539         results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
540         location: Location,
541     ) -> io::Result<()> {
542         // Only print a single row.
543         if location.statement_index != 0 {
544             return Ok(());
545         }
546
547         let block_trans = &self.trans_for_block[location.block];
548         let rowspan = self.body.basic_blocks()[location.block].statements.len();
549
550         for set in &[&block_trans.gen, &block_trans.kill] {
551             write!(
552                 w,
553                 r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
554                 fmt = fmt,
555                 rowspan = rowspan
556             )?;
557
558             pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?;
559             write!(w, "</td>")?;
560         }
561
562         Ok(())
563     }
564 }
565
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,
569     analysis: &A,
570     from: &BitSet<A::Idx>,
571     to: &BitSet<A::Idx>,
572 ) -> io::Result<()> {
573     assert_eq!(from.domain_size(), to.domain_size());
574     let len = from.domain_size();
575
576     let mut set = HybridBitSet::new_empty(len);
577     let mut clear = HybridBitSet::new_empty(len);
578
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),
584             _ => continue,
585         };
586     }
587
588     if !set.is_empty() {
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>"#)?;
592     }
593
594     if !set.is_empty() && !clear.is_empty() {
595         write!(w, "{}", BR_LEFT)?;
596     }
597
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>"#)?;
602     }
603
604     Ok(())
605 }
606
607 const BR_LEFT: &'static str = r#"<br align="left"/>"#;
608 const BR_LEFT_SPACE: &'static str = r#"<br align="left"/> "#;
609
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 });
612
613 struct LineBreak {
614     sequence: &'static str,
615     limit: usize,
616 }
617
618 /// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
619 /// separator (`sep`).
620 ///
621 /// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
622 /// character limit.
623 fn pretty_print_state_elems<A>(
624     w: &mut impl io::Write,
625     analysis: &A,
626     elems: impl Iterator<Item = A::Idx>,
627     sep: &str,
628     line_break: Option<LineBreak>,
629 ) -> io::Result<bool>
630 where
631     A: Analysis<'tcx>,
632 {
633     let sep_width = sep.chars().count();
634
635     let mut buf = Vec::new();
636
637     let mut first = true;
638     let mut curr_line_width = 0;
639     let mut line_break_inserted = false;
640
641     for idx in elems {
642         buf.clear();
643         analysis.pretty_print_idx(&mut buf, idx)?;
644         let idx_str =
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();
648
649         if first {
650             first = false;
651         } else {
652             write!(w, "{}", sep)?;
653             curr_line_width += sep_width;
654
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;
659                     curr_line_width = 0;
660                 }
661             }
662         }
663
664         write!(w, "{}", escaped)?;
665         curr_line_width += escaped_width;
666     }
667
668     Ok(line_break_inserted)
669 }
670
671 /// The background color used for zebra-striping the table.
672 #[derive(Clone, Copy)]
673 enum Background {
674     Light,
675     Dark,
676 }
677
678 impl Background {
679     fn attr(self) -> &'static str {
680         match self {
681             Self::Dark => "bgcolor=\"#f0f0f0\"",
682             Self::Light => "",
683         }
684     }
685 }
686
687 impl ops::Not for Background {
688     type Output = Self;
689
690     fn not(self) -> Self {
691         match self {
692             Self::Light => Self::Dark,
693             Self::Dark => Self::Light,
694         }
695     }
696 }