Skip to content

Commit b653ffb

Browse files
committed
improve scope computation
- integrate free regions with placeholders as regions that are live at all points: flowing into these is enough to stop the computation - pre-compute liveness of SCCs per point, and check escaping the function, and the liveness of SCCs instead of regions
1 parent 2a86364 commit b653ffb

File tree

1 file changed

+61
-35
lines changed

1 file changed

+61
-35
lines changed

compiler/rustc_borrowck/src/dataflow.rs

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![deny(rustc::untranslatable_diagnostic)]
22
#![deny(rustc::diagnostic_outside_of_impl)]
33
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
4-
use rustc_index::bit_set::BitSet;
4+
use rustc_index::bit_set::{BitSet, SparseBitMatrix};
55
use rustc_middle::mir::{
66
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
77
};
@@ -14,6 +14,7 @@ use rustc_mir_dataflow::{Analysis, Direction, Results};
1414
use std::fmt;
1515

1616
use crate::constraints::ConstraintSccIndex;
17+
use crate::region_infer::values::PointIndex;
1718
use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext};
1819

1920
/// A tuple with named fields that can hold either the results or the transient state of the
@@ -123,7 +124,6 @@ rustc_index::newtype_index! {
123124
pub struct Borrows<'a, 'tcx> {
124125
tcx: TyCtxt<'tcx>,
125126
body: &'a Body<'tcx>,
126-
127127
borrow_set: &'a BorrowSet<'tcx>,
128128
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
129129
}
@@ -250,39 +250,63 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
250250
visit_stack: Vec<StackEntry>,
251251
body: &'a Body<'tcx>,
252252
regioncx: &'a RegionInferenceContext<'tcx>,
253+
254+
sccs_live_at_all_points: FxHashSet<ConstraintSccIndex>,
255+
live_sccs_per_point: SparseBitMatrix<PointIndex, ConstraintSccIndex>,
256+
257+
reachability: BitSet<ConstraintSccIndex>,
258+
reachability_stack: Vec<ConstraintSccIndex>,
259+
253260
loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
254-
placeholders: FxHashSet<ConstraintSccIndex>,
255-
reachability: BitSet<RegionVid>,
256-
reachability_stack: Vec<RegionVid>,
257261
}
258262

259263
impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
260264
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
261-
// Compute the placeholder list once, as it will be used for all the loan scopes we'll
262-
// compute.
265+
// Compute the list of SCCs that are live at all points, as it will be used for all the
266+
// loan scopes we'll compute.
263267
// FIXME: they're surely already available somewhere.
264-
let placeholders = regioncx
268+
let sccs_live_at_all_points: FxHashSet<_> = regioncx
265269
.regions()
266270
.filter(|&r| {
267271
use rustc_infer::infer::{NllRegionVariableOrigin, RegionVariableOrigin};
268272
let origin = regioncx.var_infos[r].origin;
269-
let is_placeholder = matches!(
273+
let live_at_all_points = matches!(
270274
origin,
271-
RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_))
275+
RegionVariableOrigin::Nll(
276+
NllRegionVariableOrigin::Placeholder(_)
277+
| NllRegionVariableOrigin::FreeRegion
278+
)
272279
);
273-
is_placeholder
280+
live_at_all_points
274281
})
275282
.map(|r| regioncx.constraint_sccs.scc(r))
276283
.collect();
277284

285+
// Pre-compute the set of live SCCs per point
286+
let liveness = &regioncx.liveness_constraints;
287+
let sccs = &regioncx.constraint_sccs;
288+
let mut live_sccs_per_point = SparseBitMatrix::new(sccs.num_sccs());
289+
290+
for region in liveness.rows() {
291+
let scc = sccs.scc(region);
292+
if sccs_live_at_all_points.contains(&scc) {
293+
continue;
294+
}
295+
for location in liveness.get_elements(region) {
296+
let point = liveness.point_from_location(location);
297+
live_sccs_per_point.insert(point, scc);
298+
}
299+
}
300+
278301
Self {
279302
visited: BitSet::new_empty(body.basic_blocks.len()),
280303
visit_stack: vec![],
281304
body,
282305
regioncx,
283306
loans_out_of_scope_at_location: FxIndexMap::default(),
284-
placeholders,
285-
reachability: BitSet::new_empty(regioncx.regions().count()),
307+
sccs_live_at_all_points,
308+
live_sccs_per_point,
309+
reachability: BitSet::new_empty(regioncx.constraint_sccs.num_sccs()),
286310
reachability_stack: vec![],
287311
}
288312
}
@@ -306,13 +330,11 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
306330
let sccs = &self.regioncx.constraint_sccs;
307331
let member_constraints = &self.regioncx.member_constraints;
308332

309-
self.reachability_stack.push(issuing_region);
310-
self.reachability.insert(issuing_region);
311-
312-
let static_region = self.regioncx.universal_regions().fr_static;
313-
while let Some(region) = self.reachability_stack.pop() {
314-
let scc = sccs.scc(region);
333+
let issuing_region_scc = sccs.scc(issuing_region);
334+
self.reachability_stack.push(issuing_region_scc);
335+
self.reachability.insert(issuing_region_scc);
315336

337+
while let Some(scc) = self.reachability_stack.pop() {
316338
// Handle successors of this SCC:
317339
//
318340
// 1. Via member constraints
@@ -324,11 +346,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
324346
// That is to say, if there are member constraints here, the loan escapes the
325347
// function and cannot go out of scope. We can early return.
326348
//
327-
// 2. Via placeholders
349+
// 2. Via regions that are live at all points: placeholders and free regions.
328350
//
329-
// If the issuing region outlives placeholders, its loan escapes the function and
351+
// If the issuing region outlives such a region, its loan escapes the function and
330352
// cannot go out of scope. We can early return.
331-
if member_constraints.indices(scc).next().is_some() || self.placeholders.contains(&scc)
353+
if member_constraints.indices(scc).next().is_some()
354+
|| self.sccs_live_at_all_points.contains(&scc)
332355
{
333356
self.reachability_stack.clear();
334357
self.reachability.clear();
@@ -337,15 +360,9 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
337360

338361
// 3. Via outlives successors, which we want to record and traverse, so we add them
339362
// to the worklist stack
340-
let successors = self.regioncx.constraint_graph.outgoing_edges(
341-
region,
342-
&self.regioncx.constraints,
343-
static_region,
344-
);
345-
for outlives_constraint in successors {
346-
let succ = outlives_constraint.sub;
347-
if self.reachability.insert(succ) {
348-
self.reachability_stack.push(succ);
363+
for &succ_scc in sccs.successors(scc) {
364+
if self.reachability.insert(succ_scc) {
365+
self.reachability_stack.push(succ_scc);
349366
}
350367
}
351368
}
@@ -380,10 +397,19 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
380397

381398
// Check whether the issuing region can reach local regions that are live at this
382399
// point.
383-
for reachable_region in self.reachability.iter() {
384-
if self.regioncx.liveness_constraints.contains(reachable_region, location) {
385-
issuing_region_can_reach_live_regions = true;
386-
break;
400+
//
401+
// FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and
402+
// `r` is live at point `q`, then it's guaranteed that `i` would reach `r` at point
403+
// `q`. The reachability is location-insensitive, and we could take advantage of
404+
// that, by jumping to a further point than the next statement. We can jump to the
405+
// furthest point within the block where `r` is live.
406+
let point = self.regioncx.liveness_constraints.point_from_location(location);
407+
if let Some(live_sccs) = self.live_sccs_per_point.row(point) {
408+
for live_scc in live_sccs.iter() {
409+
if self.reachability.contains(live_scc) {
410+
issuing_region_can_reach_live_regions = true;
411+
break;
412+
}
387413
}
388414
}
389415

0 commit comments

Comments
 (0)