about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRémy Rakic <remy.rakic+github@gmail.com>2025-01-09 15:33:38 +0000
committerRémy Rakic <remy.rakic+github@gmail.com>2025-01-17 11:52:58 +0000
commitdee52a31789fb87e04b4762df5b84d52738b1adb (patch)
tree16e14e2dfe4d89a1c092039544bc4a2315786f11
parent0114a9707e92306adc15111cfb1ea9d1ab0e8f05 (diff)
downloadrust-dee52a31789fb87e04b4762df5b84d52738b1adb.tar.gz
rust-dee52a31789fb87e04b4762df5b84d52738b1adb.zip
encode `Locations::All` typeck constraints as logical edges
Instead of materializing `Locations::All` constraints as physical edges
at all the points in the CFG, we record them as logical edges and only
materialize them during traversal as successors for a given node.

This fixes the slowness/hang in the `saturating-float-casts.rs` test.
-rw-r--r--compiler/rustc_borrowck/src/polonius/loan_liveness.rs51
-rw-r--r--compiler/rustc_borrowck/src/polonius/mod.rs1
-rw-r--r--compiler/rustc_borrowck/src/polonius/typeck_constraints.rs22
3 files changed, 49 insertions, 25 deletions
diff --git a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
index 3fa2a66e3ba..768c12a97a6 100644
--- a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
+++ b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
@@ -9,16 +9,21 @@ use rustc_middle::ty::{RegionVid, TyCtxt};
 use rustc_mir_dataflow::points::PointIndex;
 
 use super::{LiveLoans, LocalizedOutlivesConstraintSet};
+use crate::constraints::OutlivesConstraint;
 use crate::dataflow::BorrowIndex;
 use crate::region_infer::values::LivenessValues;
+use crate::type_check::Locations;
 use crate::{BorrowSet, PlaceConflictBias, places_conflict};
 
-/// With the full graph of constraints, we can compute loan reachability, stop at kills, and trace
-/// loan liveness throughout the CFG.
+/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by
+/// traversing the full graph of constraints that combines:
+/// - the localized constraints (the physical edges),
+/// - with the constraints that hold at all points (the logical edges).
 pub(super) fn compute_loan_liveness<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
     liveness: &LivenessValues,
+    outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
     borrow_set: &BorrowSet<'tcx>,
     localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
 ) -> LiveLoans {
@@ -29,7 +34,11 @@ pub(super) fn compute_loan_liveness<'tcx>(
     // edges when visualizing the constraint graph anyways.
     let kills = collect_kills(body, tcx, borrow_set);
 
-    let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints);
+    // Create the full graph with the physical edges we've localized earlier, and the logical edges
+    // of constraints that hold at all points.
+    let logical_constraints =
+        outlives_constraints.filter(|c| matches!(c.locations, Locations::All(_)));
+    let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints, logical_constraints);
     let mut visited = FxHashSet::default();
     let mut stack = Vec::new();
 
@@ -125,11 +134,16 @@ pub(super) fn compute_loan_liveness<'tcx>(
     live_loans
 }
 
-/// The localized constraint graph indexes the physical edges to compute a given node's successors
-/// during traversal.
+/// The localized constraint graph indexes the physical and logical edges to compute a given node's
+/// successors during traversal.
 struct LocalizedConstraintGraph {
     /// The actual, physical, edges we have recorded for a given node.
     edges: FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>,
+
+    /// The logical edges representing the outlives constraints that hold at all points in the CFG,
+    /// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs
+    /// can be big, and we don't need to create such a physical edge for every point in the CFG.
+    logical_edges: FxHashMap<RegionVid, FxIndexSet<RegionVid>>,
 }
 
 /// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint.
@@ -141,7 +155,10 @@ struct LocalizedNode {
 
 impl LocalizedConstraintGraph {
     /// Traverses the constraints and returns the indexed graph of edges per node.
-    fn new(constraints: &LocalizedOutlivesConstraintSet) -> Self {
+    fn new<'tcx>(
+        constraints: &LocalizedOutlivesConstraintSet,
+        logical_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
+    ) -> Self {
         let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
         for constraint in &constraints.outlives {
             let source = LocalizedNode { region: constraint.source, point: constraint.from };
@@ -149,12 +166,30 @@ impl LocalizedConstraintGraph {
             edges.entry(source).or_default().insert(target);
         }
 
-        LocalizedConstraintGraph { edges }
+        let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
+        for constraint in logical_constraints {
+            logical_edges.entry(constraint.sup).or_default().insert(constraint.sub);
+        }
+
+        LocalizedConstraintGraph { edges, logical_edges }
     }
 
     /// Returns the outgoing edges of a given node, not its transitive closure.
     fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> + use<'_> {
-        self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied())
+        // The outgoing edges are:
+        // - the physical edges present at this node,
+        // - the materialized logical edges that exist virtually at all points for this node's
+        //   region, localized at this point.
+        let physical_edges =
+            self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied());
+        let materialized_edges =
+            self.logical_edges.get(&node.region).into_iter().flat_map(move |targets| {
+                targets
+                    .iter()
+                    .copied()
+                    .map(move |target| LocalizedNode { point: node.point, region: target })
+            });
+        physical_edges.chain(materialized_edges)
     }
 }
 
diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs
index 52a5f75d8a2..502c868194a 100644
--- a/compiler/rustc_borrowck/src/polonius/mod.rs
+++ b/compiler/rustc_borrowck/src/polonius/mod.rs
@@ -130,6 +130,7 @@ impl PoloniusContext {
             tcx,
             body,
             regioncx.liveness_constraints(),
+            regioncx.outlives_constraints(),
             borrow_set,
             &localized_outlives_constraints,
         );
diff --git a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
index 8235b844886..1289b1899eb 100644
--- a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
+++ b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
@@ -22,23 +22,11 @@ pub(super) fn convert_typeck_constraints<'tcx>(
     for outlives_constraint in outlives_constraints {
         match outlives_constraint.locations {
             Locations::All(_) => {
-                // For now, turn logical constraints holding at all points into physical edges at
-                // every point in the graph.
-                // FIXME: encode this into *traversal* instead.
-                for (block, bb) in body.basic_blocks.iter_enumerated() {
-                    let statement_count = bb.statements.len();
-                    for statement_index in 0..=statement_count {
-                        let current_location = Location { block, statement_index };
-                        let current_point = liveness.point_from_location(current_location);
-
-                        localized_outlives_constraints.push(LocalizedOutlivesConstraint {
-                            source: outlives_constraint.sup,
-                            from: current_point,
-                            target: outlives_constraint.sub,
-                            to: current_point,
-                        });
-                    }
-                }
+                // We don't turn constraints holding at all points into physical edges at every
+                // point in the graph. They are encoded into *traversal* instead: a given node's
+                // successors will combine these logical edges with the regular, physical, localized
+                // edges.
+                continue;
             }
 
             Locations::Single(location) => {