Skip to content

Commit da1a5ae

Browse files
committed
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.
1 parent 894c9af commit da1a5ae

File tree

3 files changed

+49
-25
lines changed

3 files changed

+49
-25
lines changed

compiler/rustc_borrowck/src/polonius/loan_liveness.rs

+43-8
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,21 @@ use rustc_middle::ty::{RegionVid, TyCtxt};
99
use rustc_mir_dataflow::points::PointIndex;
1010

1111
use super::{LiveLoans, LocalizedOutlivesConstraintSet};
12+
use crate::constraints::OutlivesConstraint;
1213
use crate::dataflow::BorrowIndex;
1314
use crate::region_infer::values::LivenessValues;
15+
use crate::type_check::Locations;
1416
use crate::{BorrowSet, PlaceConflictBias, places_conflict};
1517

16-
/// With the full graph of constraints, we can compute loan reachability, stop at kills, and trace
17-
/// loan liveness throughout the CFG.
18+
/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by
19+
/// traversing the full graph of constraints that combines:
20+
/// - the localized constraints (the physical edges),
21+
/// - with the constraints that hold at all points (the logical edges).
1822
pub(super) fn compute_loan_liveness<'tcx>(
1923
tcx: TyCtxt<'tcx>,
2024
body: &Body<'tcx>,
2125
liveness: &LivenessValues,
26+
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
2227
borrow_set: &BorrowSet<'tcx>,
2328
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
2429
) -> LiveLoans {
@@ -29,7 +34,11 @@ pub(super) fn compute_loan_liveness<'tcx>(
2934
// edges when visualizing the constraint graph anyways.
3035
let kills = collect_kills(body, tcx, borrow_set);
3136

32-
let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints);
37+
// Create the full graph with the physical edges we've localized earlier, and the logical edges
38+
// of constraints that hold at all points.
39+
let logical_constraints =
40+
outlives_constraints.filter(|c| matches!(c.locations, Locations::All(_)));
41+
let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints, logical_constraints);
3342
let mut visited = FxHashSet::default();
3443
let mut stack = Vec::new();
3544

@@ -125,11 +134,16 @@ pub(super) fn compute_loan_liveness<'tcx>(
125134
live_loans
126135
}
127136

128-
/// The localized constraint graph indexes the physical edges to compute a given node's successors
129-
/// during traversal.
137+
/// The localized constraint graph indexes the physical and logical edges to compute a given node's
138+
/// successors during traversal.
130139
struct LocalizedConstraintGraph {
131140
/// The actual, physical, edges we have recorded for a given node.
132141
edges: FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>,
142+
143+
/// The logical edges representing the outlives constraints that hold at all points in the CFG,
144+
/// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs
145+
/// can be big, and we don't need to create such a physical edge for every point in the CFG.
146+
logical_edges: FxHashMap<RegionVid, FxIndexSet<RegionVid>>,
133147
}
134148

135149
/// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint.
@@ -141,20 +155,41 @@ struct LocalizedNode {
141155

142156
impl LocalizedConstraintGraph {
143157
/// Traverses the constraints and returns the indexed graph of edges per node.
144-
fn new(constraints: &LocalizedOutlivesConstraintSet) -> Self {
158+
fn new<'tcx>(
159+
constraints: &LocalizedOutlivesConstraintSet,
160+
logical_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
161+
) -> Self {
145162
let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
146163
for constraint in &constraints.outlives {
147164
let source = LocalizedNode { region: constraint.source, point: constraint.from };
148165
let target = LocalizedNode { region: constraint.target, point: constraint.to };
149166
edges.entry(source).or_default().insert(target);
150167
}
151168

152-
LocalizedConstraintGraph { edges }
169+
let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
170+
for constraint in logical_constraints {
171+
logical_edges.entry(constraint.sup).or_default().insert(constraint.sub);
172+
}
173+
174+
LocalizedConstraintGraph { edges, logical_edges }
153175
}
154176

155177
/// Returns the outgoing edges of a given node, not its transitive closure.
156178
fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> + use<'_> {
157-
self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied())
179+
// The outgoing edges are:
180+
// - the physical edges present at this node,
181+
// - the materialized logical edges that exist virtually at all points for this node's
182+
// region, localized at this point.
183+
let physical_edges =
184+
self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied());
185+
let materialized_edges =
186+
self.logical_edges.get(&node.region).into_iter().flat_map(move |targets| {
187+
targets
188+
.iter()
189+
.copied()
190+
.map(move |target| LocalizedNode { point: node.point, region: target })
191+
});
192+
physical_edges.chain(materialized_edges)
158193
}
159194
}
160195

compiler/rustc_borrowck/src/polonius/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ impl PoloniusContext {
130130
tcx,
131131
body,
132132
regioncx.liveness_constraints(),
133+
regioncx.outlives_constraints(),
133134
borrow_set,
134135
&localized_outlives_constraints,
135136
);

compiler/rustc_borrowck/src/polonius/typeck_constraints.rs

+5-17
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,11 @@ pub(super) fn convert_typeck_constraints<'tcx>(
2222
for outlives_constraint in outlives_constraints {
2323
match outlives_constraint.locations {
2424
Locations::All(_) => {
25-
// For now, turn logical constraints holding at all points into physical edges at
26-
// every point in the graph.
27-
// FIXME: encode this into *traversal* instead.
28-
for (block, bb) in body.basic_blocks.iter_enumerated() {
29-
let statement_count = bb.statements.len();
30-
for statement_index in 0..=statement_count {
31-
let current_location = Location { block, statement_index };
32-
let current_point = liveness.point_from_location(current_location);
33-
34-
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
35-
source: outlives_constraint.sup,
36-
from: current_point,
37-
target: outlives_constraint.sub,
38-
to: current_point,
39-
});
40-
}
41-
}
25+
// We don't turn constraints holding at all points into physical edges at every
26+
// point in the graph. They are encoded into *traversal* instead: a given node's
27+
// successors will combine these logical edges with the regular, physical, localized
28+
// edges.
29+
continue;
4230
}
4331

4432
Locations::Single(location) => {

0 commit comments

Comments
 (0)