|
1 | 1 | use std::io;
|
2 | 2 |
|
3 | 3 | use rustc_middle::mir::pretty::{
|
4 |
| - PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, |
| 4 | + PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, |
5 | 5 | };
|
6 |
| -use rustc_middle::mir::{Body, ClosureRegionRequirements, PassWhere}; |
| 6 | +use rustc_middle::mir::{Body, ClosureRegionRequirements}; |
7 | 7 | use rustc_middle::ty::TyCtxt;
|
8 | 8 | use rustc_session::config::MirIncludeSpans;
|
9 | 9 |
|
@@ -49,6 +49,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
49 | 49 | /// The polonius dump consists of:
|
50 | 50 | /// - the NLL MIR
|
51 | 51 | /// - the list of polonius localized constraints
|
| 52 | +/// - a mermaid graph of the CFG |
52 | 53 | fn emit_polonius_dump<'tcx>(
|
53 | 54 | tcx: TyCtxt<'tcx>,
|
54 | 55 | body: &Body<'tcx>,
|
@@ -80,7 +81,23 @@ fn emit_polonius_dump<'tcx>(
|
80 | 81 | writeln!(out, "</pre></code>")?;
|
81 | 82 | writeln!(out, "</div>")?;
|
82 | 83 |
|
| 84 | + // Section 2: mermaid visualization of the CFG. |
| 85 | + writeln!(out, "<div>")?; |
| 86 | + writeln!(out, "Control-flow graph")?; |
| 87 | + writeln!(out, "<code><pre class='mermaid'>")?; |
| 88 | + emit_mermaid_cfg(body, out)?; |
| 89 | + writeln!(out, "</pre></code>")?; |
| 90 | + writeln!(out, "</div>")?; |
| 91 | + |
83 | 92 | // Finalize the dump with the HTML epilogue.
|
| 93 | + writeln!( |
| 94 | + out, |
| 95 | + "<script src='https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js'></script>" |
| 96 | + )?; |
| 97 | + writeln!(out, "<script>")?; |
| 98 | + writeln!(out, "mermaid.initialize({{ startOnLoad: false, maxEdges: 100 }});")?; |
| 99 | + writeln!(out, "mermaid.run({{ querySelector: '.mermaid' }})")?; |
| 100 | + writeln!(out, "</script>")?; |
84 | 101 | writeln!(out, "</body>")?;
|
85 | 102 | writeln!(out, "</html>")?;
|
86 | 103 |
|
@@ -192,3 +209,55 @@ fn emit_polonius_mir<'tcx>(
|
192 | 209 |
|
193 | 210 | Ok(())
|
194 | 211 | }
|
| 212 | + |
| 213 | +/// Emits a mermaid flowchart of the CFG blocks and edges, similar to the graphviz version. |
| 214 | +fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> { |
| 215 | + use rustc_middle::mir::{TerminatorEdges, TerminatorKind}; |
| 216 | + |
| 217 | + // The mermaid chart type: a top-down flowchart. |
| 218 | + writeln!(out, "flowchart TD")?; |
| 219 | + |
| 220 | + // Emit the block nodes. |
| 221 | + for (block_idx, block) in body.basic_blocks.iter_enumerated() { |
| 222 | + let block_idx = block_idx.as_usize(); |
| 223 | + let cleanup = if block.is_cleanup { " (cleanup)" } else { "" }; |
| 224 | + writeln!(out, "{block_idx}[\"bb{block_idx}{cleanup}\"]")?; |
| 225 | + } |
| 226 | + |
| 227 | + // Emit the edges between blocks, from the terminator edges. |
| 228 | + for (block_idx, block) in body.basic_blocks.iter_enumerated() { |
| 229 | + let block_idx = block_idx.as_usize(); |
| 230 | + let terminator = block.terminator(); |
| 231 | + match terminator.edges() { |
| 232 | + TerminatorEdges::None => {} |
| 233 | + TerminatorEdges::Single(bb) => { |
| 234 | + writeln!(out, "{block_idx} --> {}", bb.as_usize())?; |
| 235 | + } |
| 236 | + TerminatorEdges::Double(bb1, bb2) => { |
| 237 | + if matches!(terminator.kind, TerminatorKind::FalseEdge { .. }) { |
| 238 | + writeln!(out, "{block_idx} --> {}", bb1.as_usize())?; |
| 239 | + writeln!(out, "{block_idx} -- imaginary --> {}", bb2.as_usize())?; |
| 240 | + } else { |
| 241 | + writeln!(out, "{block_idx} --> {}", bb1.as_usize())?; |
| 242 | + writeln!(out, "{block_idx} -- unwind --> {}", bb2.as_usize())?; |
| 243 | + } |
| 244 | + } |
| 245 | + TerminatorEdges::AssignOnReturn { return_, cleanup, .. } => { |
| 246 | + for to_idx in return_ { |
| 247 | + writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?; |
| 248 | + } |
| 249 | + |
| 250 | + if let Some(to_idx) = cleanup { |
| 251 | + writeln!(out, "{block_idx} -- unwind --> {}", to_idx.as_usize())?; |
| 252 | + } |
| 253 | + } |
| 254 | + TerminatorEdges::SwitchInt { targets, .. } => { |
| 255 | + for to_idx in targets.all_targets() { |
| 256 | + writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?; |
| 257 | + } |
| 258 | + } |
| 259 | + } |
| 260 | + } |
| 261 | + |
| 262 | + Ok(()) |
| 263 | +} |
0 commit comments