Skip to content

Commit 09fb70a

Browse files
committed
add CFG to polonius MIR dump
1 parent 82c0168 commit 09fb70a

File tree

1 file changed

+71
-2
lines changed
  • compiler/rustc_borrowck/src/polonius

1 file changed

+71
-2
lines changed

compiler/rustc_borrowck/src/polonius/dump.rs

+71-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::io;
22

33
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,
55
};
6-
use rustc_middle::mir::{Body, ClosureRegionRequirements, PassWhere};
6+
use rustc_middle::mir::{Body, ClosureRegionRequirements};
77
use rustc_middle::ty::TyCtxt;
88
use rustc_session::config::MirIncludeSpans;
99

@@ -49,6 +49,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
4949
/// The polonius dump consists of:
5050
/// - the NLL MIR
5151
/// - the list of polonius localized constraints
52+
/// - a mermaid graph of the CFG
5253
fn emit_polonius_dump<'tcx>(
5354
tcx: TyCtxt<'tcx>,
5455
body: &Body<'tcx>,
@@ -80,7 +81,23 @@ fn emit_polonius_dump<'tcx>(
8081
writeln!(out, "</pre></code>")?;
8182
writeln!(out, "</div>")?;
8283

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+
8392
// 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>")?;
84101
writeln!(out, "</body>")?;
85102
writeln!(out, "</html>")?;
86103

@@ -192,3 +209,55 @@ fn emit_polonius_mir<'tcx>(
192209

193210
Ok(())
194211
}
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

Comments
 (0)