From 1ee75825455328c287cb2ad1bdbd855656f09871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sun, 26 Jan 2025 17:18:07 +0000 Subject: [PATCH 1/3] add NLL region graph to the polonius MIR dump --- compiler/rustc_borrowck/src/polonius/dump.rs | 84 +++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 40e801d03885c..11b00fdd44aaa 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,14 +1,17 @@ use std::io; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; use rustc_middle::mir::{Body, ClosureRegionRequirements}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. @@ -50,6 +53,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( /// - the NLL MIR /// - the list of polonius localized constraints /// - a mermaid graph of the CFG +/// - a mermaid graph of the NLL regions and the constraints between them fn emit_polonius_dump<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -89,6 +93,14 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "")?; writeln!(out, "")?; + // Section 3: mermaid visualization of the NLL region graph. + writeln!(out, "
")?; + writeln!(out, "NLL regions")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_regions(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + // Finalize the dump with the HTML epilogue. writeln!( out, @@ -261,3 +273,73 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> Ok(()) } + +/// Emits a region's label: index, universe, external name. +fn render_region( + region: RegionVid, + regioncx: &RegionInferenceContext<'_>, + out: &mut dyn io::Write, +) -> io::Result<()> { + let def = regioncx.region_definition(region); + let universe = def.universe; + + write!(out, "'{}", region.as_usize())?; + if !universe.is_root() { + write!(out, "/{universe:?}")?; + } + if let Some(name) = def.external_name.and_then(|e| e.get_name()) { + write!(out, " ({name})")?; + } + Ok(()) +} + +/// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_regions<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Emit the region nodes. + for region in regioncx.var_infos.indices() { + write!(out, "{}[\"", region.as_usize())?; + render_region(region, regioncx, out)?; + writeln!(out, "\"]")?; + } + + // Get a set of edges to check for the reverse edge being present. + let edges: FxHashSet<_> = regioncx.outlives_constraints().map(|c| (c.sup, c.sub)).collect(); + + // Order (and deduplicate) edges for traversal, to display them in a generally increasing order. + let constraint_key = |c: &OutlivesConstraint<'_>| { + let min = c.sup.min(c.sub); + let max = c.sup.max(c.sub); + (min, max) + }; + let mut ordered_edges: Vec<_> = regioncx.outlives_constraints().collect(); + ordered_edges.sort_by_key(|c| constraint_key(c)); + ordered_edges.dedup_by_key(|c| constraint_key(c)); + + for outlives in ordered_edges { + // Source node. + write!(out, "{} ", outlives.sup.as_usize())?; + + // The kind of arrow: bidirectional if the opposite edge exists in the set. + if edges.contains(&(outlives.sub, outlives.sup)) { + write!(out, "<")?; + } + write!(out, "-- ")?; + + // Edge label from its `Locations`. + match outlives.locations { + Locations::All(_) => write!(out, "All")?, + Locations::Single(location) => write!(out, "{:?}", location)?, + } + + // Target node. + writeln!(out, " --> {}", outlives.sub.as_usize())?; + } + Ok(()) +} From 052e9b430651f5cb89b511d131d1d7a47a28d215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sun, 26 Jan 2025 21:13:32 +0000 Subject: [PATCH 2/3] add NLL SCCs to polonius MIR dump --- compiler/rustc_borrowck/src/polonius/dump.rs | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 11b00fdd44aaa..944b5b1d4a7df 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,6 +1,7 @@ use std::io; use rustc_data_structures::fx::FxHashSet; +use rustc_index::IndexVec; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; @@ -54,6 +55,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( /// - the list of polonius localized constraints /// - a mermaid graph of the CFG /// - a mermaid graph of the NLL regions and the constraints between them +/// - a mermaid graph of the NLL SCCs and the constraints between them fn emit_polonius_dump<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -101,6 +103,14 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "")?; writeln!(out, "")?; + // Section 4: mermaid visualization of the NLL SCC graph. + writeln!(out, "
")?; + writeln!(out, "NLL SCCs")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_sccs(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + // Finalize the dump with the HTML epilogue. writeln!( out, @@ -343,3 +353,42 @@ fn emit_mermaid_nll_regions<'tcx>( } Ok(()) } + +/// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_sccs<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Gather and emit the SCC nodes. + let mut nodes_per_scc: IndexVec<_, _> = + regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect(); + for region in regioncx.var_infos.indices() { + let scc = regioncx.constraint_sccs().scc(region); + nodes_per_scc[scc].push(region); + } + for (scc, regions) in nodes_per_scc.iter_enumerated() { + // The node label: the regions contained in the SCC. + write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?; + for (idx, ®ion) in regions.iter().enumerate() { + render_region(region, regioncx, out)?; + if idx < regions.len() - 1 { + write!(out, ",")?; + } + } + writeln!(out, "}}\"]")?; + } + + // Emit the edges between SCCs. + let edges = regioncx.constraint_sccs().all_sccs().flat_map(|source| { + regioncx.constraint_sccs().successors(source).iter().map(move |&target| (source, target)) + }); + for (source, target) in edges { + writeln!(out, "{} --> {}", source.as_usize(), target.as_usize())?; + } + + Ok(()) +} From 6bdc2dc3cf7534a8066a8f937698f4a7520b85aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sun, 26 Jan 2025 21:22:46 +0000 Subject: [PATCH 3/3] tidy up html structure - invert pre/code which was an invalid combination, that works fine in practice - remove unneeded code wrapper for graphs --- compiler/rustc_borrowck/src/polonius/dump.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 944b5b1d4a7df..f71e6f3e6f3a6 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -74,7 +74,7 @@ fn emit_polonius_dump<'tcx>( // Section 1: the NLL + Polonius MIR. writeln!(out, "
")?; writeln!(out, "Raw MIR dump")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_html_mir(
         tcx,
         body,
@@ -84,15 +84,15 @@ fn emit_polonius_dump<'tcx>(
         closure_region_requirements,
         out,
     )?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; // Section 2: mermaid visualization of the CFG. writeln!(out, "
")?; writeln!(out, "Control-flow graph")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_mermaid_cfg(body, out)?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; // Section 3: mermaid visualization of the NLL region graph.