Skip to content

Commit 480b171

Browse files
committed
reverse liveness and subset traversal order
During traversal, instead of checking: for each live region r at point p, whether the issuing region can reach r, we do this in the opposite order: for each rechable region r, is r live at point p. This will prevent continuously checking regions that are live at point p and that we know aren't reachable by the issuing region. If it did, we wouldn't need to compute the scope in the first place.
1 parent 1da5b52 commit 480b171

File tree

3 files changed

+41
-49
lines changed

3 files changed

+41
-49
lines changed

compiler/rustc_borrowck/src/dataflow.rs

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![deny(rustc::untranslatable_diagnostic)]
22
#![deny(rustc::diagnostic_outside_of_impl)]
3-
use rustc_data_structures::fx::FxIndexMap;
3+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
44
use rustc_index::bit_set::BitSet;
55
use rustc_middle::mir::{self, BasicBlock, Body, Location, Place};
66
use rustc_middle::ty::RegionVid;
@@ -235,9 +235,9 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
235235
body: &'a Body<'tcx>,
236236
regioncx: &'a RegionInferenceContext<'tcx>,
237237
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
238-
placeholders: Vec<RegionVid>,
239-
reachability: BitSet<ConstraintSccIndex>,
240-
reachability_stack: Vec<ConstraintSccIndex>,
238+
placeholders: FxHashSet<ConstraintSccIndex>,
239+
reachability: BitSet<RegionVid>,
240+
reachability_stack: Vec<RegionVid>,
241241
}
242242

243243
impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
@@ -256,6 +256,7 @@ impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
256256
);
257257
is_placeholder
258258
})
259+
.map(|r| regioncx.constraint_sccs.scc(r))
259260
.collect();
260261

261262
Self {
@@ -265,7 +266,7 @@ impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
265266
regioncx,
266267
borrows_out_of_scope_at_location: FxIndexMap::default(),
267268
placeholders,
268-
reachability: BitSet::new_empty(regioncx.constraint_sccs.num_sccs()),
269+
reachability: BitSet::new_empty(regioncx.regions().count()),
269270
reachability_stack: vec![],
270271
}
271272
}
@@ -286,42 +287,51 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
286287
// regions via member constraints. (The `OutOfScopePrecomputer` wouldn't be called on a
287288
// region that outlives free regions via outlives constraints.)
288289

289-
let liveness = &self.regioncx.liveness_constraints;
290290
let sccs = &self.regioncx.constraint_sccs;
291+
let member_constraints = &self.regioncx.member_constraints;
291292

292-
let issuing_region_scc = sccs.scc(issuing_region);
293-
self.reachability_stack.push(issuing_region_scc);
294-
self.reachability.insert(issuing_region_scc);
293+
self.reachability_stack.push(issuing_region);
294+
self.reachability.insert(issuing_region);
295295

296-
let member_constraints = &self.regioncx.member_constraints;
296+
let static_region = self.regioncx.universal_regions().fr_static;
297+
while let Some(region) = self.reachability_stack.pop() {
298+
let scc = sccs.scc(region);
297299

298-
while let Some(scc) = self.reachability_stack.pop() {
299300
// Handle successors of this SCC:
300301
//
301-
// 1. Push outlives successors to the worklist stack
302-
303-
// should be something like:
304-
// self.reachability_stack
305-
// .extend(sccs.successors(scc).filter(|succ_scc| reachability.insert(succ_scc)));
306-
for &succ_scc in sccs.successors(scc) {
307-
if self.reachability.insert(succ_scc) {
308-
self.reachability_stack.push(succ_scc);
309-
}
310-
}
311-
312-
// 2. Deal with member constraints
302+
// 1. Via member constraints
313303
//
314304
// The issuing region can flow into the choice regions here, and they are either:
315305
// - placeholders or free regions themselves,
316306
// - or also transitively outlive a free region.
317307
//
318308
// That is to say, if there are member constraints here, the loan escapes the
319309
// function and cannot go out of scope. We can early return.
320-
if member_constraints.indices(scc).next().is_some() {
310+
//
311+
// 2. Via placeholders
312+
//
313+
// If the issuing region outlives placeholders, its loan escapes the function and
314+
// cannot go out of scope. We can early return.
315+
if member_constraints.indices(scc).next().is_some() || self.placeholders.contains(&scc)
316+
{
321317
self.reachability_stack.clear();
322318
self.reachability.clear();
323319
return;
324320
}
321+
322+
// 3. Via outlives successors, which we want to record and traverse, so we add them
323+
// to the worklist stack
324+
let successors = self.regioncx.constraint_graph.outgoing_edges(
325+
region,
326+
&self.regioncx.constraints,
327+
static_region,
328+
);
329+
for outlives_constraint in successors {
330+
let succ = outlives_constraint.sub;
331+
if self.reachability.insert(succ) {
332+
self.reachability_stack.push(succ);
333+
}
334+
}
325335
}
326336

327337
// We visit one BB at a time. The complication is that we may start in the
@@ -352,23 +362,10 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
352362
// live regions.
353363
let mut issuing_region_can_reach_live_regions = false;
354364

355-
// Check reachability of all live regions:
356-
// - the local regions that are live at this point,
357-
// - the placeholders, which are live at all points and don't need liveness to be
358-
// computed, and are thus absent from the liveness values.
359-
//
360-
// As mentioned above, we don't need to check for free regions, if the issuing
361-
// region outlived a free region via outlives constraints, we wouldn't need to
362-
// compute its loan's scope.
363-
for live_region in
364-
liveness.live_regions_at(location).chain(self.placeholders.iter().copied())
365-
{
366-
let live_region_scc = sccs.scc(live_region);
367-
368-
// If a single live region is reachable from the issuing region, then the loan
369-
// is still live at this point. We can stop checking other live regions at this
370-
// location, and go to the next location.
371-
if self.reachability.contains(live_region_scc) {
365+
// Check whether the issuing region can reach local regions that are live at this
366+
// point.
367+
for reachable_region in self.reachability.iter() {
368+
if self.regioncx.liveness_constraints.contains(reachable_region, location) {
372369
issuing_region_can_reach_live_regions = true;
373370
break;
374371
}
@@ -422,7 +419,7 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
422419
self.visited.clear();
423420
self.reachability.clear();
424421
assert!(self.visit_stack.is_empty(), "visit stack should be empty");
425-
assert!(self.reachability_stack.is_empty(), "reachablity stack should be empty");
422+
assert!(self.reachability_stack.is_empty(), "reachability stack should be empty");
426423
}
427424
}
428425

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ pub struct RegionInferenceContext<'tcx> {
6161
pub(crate) liveness_constraints: LivenessValues<RegionVid>,
6262

6363
/// The outlives constraints computed by the type-check.
64-
constraints: Frozen<OutlivesConstraintSet<'tcx>>,
64+
pub(crate) constraints: Frozen<OutlivesConstraintSet<'tcx>>,
6565

6666
/// The constraint-set, but in graph form, making it easy to traverse
6767
/// the constraints adjacent to a particular region. Used to construct
6868
/// the SCC (see `constraint_sccs`) and for error reporting.
69-
constraint_graph: Frozen<NormalConstraintGraph>,
69+
pub(crate) constraint_graph: Frozen<NormalConstraintGraph>,
7070

7171
/// The SCC computed from `constraints` and the constraint
7272
/// graph. We have an edge from SCC A to SCC B if `A: B`. Used to

compiler/rustc_borrowck/src/region_infer/values.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,6 @@ impl<N: Idx> LivenessValues<N> {
176176
pub(crate) fn region_value_str(&self, r: N) -> String {
177177
region_value_str(self.get_elements(r).map(RegionElement::Location))
178178
}
179-
180-
pub(crate) fn live_regions_at(&self, location: Location) -> impl Iterator<Item = N> + '_ {
181-
let point = self.elements.point_from_location(location);
182-
self.points.rows().filter(move |row| self.points.contains(*row, point))
183-
}
184179
}
185180

186181
/// Maps from `ty::PlaceholderRegion` values that are used in the rest of

0 commit comments

Comments
 (0)