Skip to content

Commit be4fd06

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 41d6562 commit be4fd06

File tree

1 file changed

+61
-35
lines changed

1 file changed

+61
-35
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+61-35
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::{self, BasicBlock, Body, Location, Place};
66
use rustc_middle::ty::RegionVid;
77
use rustc_middle::ty::TyCtxt;
@@ -12,6 +12,7 @@ use rustc_mir_dataflow::{Analysis, Direction, Results};
1212
use std::fmt;
1313

1414
use crate::constraints::ConstraintSccIndex;
15+
use crate::region_infer::values::PointIndex;
1516
use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext};
1617

1718
/// A tuple with named fields that can hold either the results or the transient state of the
@@ -121,7 +122,6 @@ rustc_index::newtype_index! {
121122
pub struct Borrows<'a, 'tcx> {
122123
tcx: TyCtxt<'tcx>,
123124
body: &'a Body<'tcx>,
124-
125125
borrow_set: &'a BorrowSet<'tcx>,
126126
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
127127
}
@@ -234,39 +234,63 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
234234
visit_stack: Vec<StackEntry>,
235235
body: &'a Body<'tcx>,
236236
regioncx: &'a RegionInferenceContext<'tcx>,
237+
238+
sccs_live_at_all_points: FxHashSet<ConstraintSccIndex>,
239+
live_sccs_per_point: SparseBitMatrix<PointIndex, ConstraintSccIndex>,
240+
241+
reachability: BitSet<ConstraintSccIndex>,
242+
reachability_stack: Vec<ConstraintSccIndex>,
243+
237244
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
238-
placeholders: FxHashSet<ConstraintSccIndex>,
239-
reachability: BitSet<RegionVid>,
240-
reachability_stack: Vec<RegionVid>,
241245
}
242246

243247
impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
244248
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
245-
// Compute the placeholder list once, as it will be used for all the loan scopes we'll
246-
// compute.
249+
// Compute the list of SCCs that are live at all points, as it will be used for all the
250+
// loan scopes we'll compute.
247251
// FIXME: they're probably already available somewhere.
248-
let placeholders = regioncx
252+
let sccs_live_at_all_points: FxHashSet<_> = regioncx
249253
.regions()
250254
.filter(|&r| {
251255
use rustc_infer::infer::{NllRegionVariableOrigin, RegionVariableOrigin};
252256
let origin = regioncx.var_infos[r].origin;
253-
let is_placeholder = matches!(
257+
let live_at_all_points = matches!(
254258
origin,
255-
RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_))
259+
RegionVariableOrigin::Nll(
260+
NllRegionVariableOrigin::Placeholder(_)
261+
| NllRegionVariableOrigin::FreeRegion
262+
)
256263
);
257-
is_placeholder
264+
live_at_all_points
258265
})
259266
.map(|r| regioncx.constraint_sccs.scc(r))
260267
.collect();
261268

269+
// Pre-compute the set of live SCCs per point
270+
let liveness = &regioncx.liveness_constraints;
271+
let sccs = &regioncx.constraint_sccs;
272+
let mut live_sccs_per_point = SparseBitMatrix::new(sccs.num_sccs());
273+
274+
for region in liveness.rows() {
275+
let scc = sccs.scc(region);
276+
if sccs_live_at_all_points.contains(&scc) {
277+
continue;
278+
}
279+
for location in liveness.get_elements(region) {
280+
let point = liveness.point_from_location(location);
281+
live_sccs_per_point.insert(point, scc);
282+
}
283+
}
284+
262285
Self {
263286
visited: BitSet::new_empty(body.basic_blocks.len()),
264287
visit_stack: vec![],
265288
body,
266289
regioncx,
267290
borrows_out_of_scope_at_location: FxIndexMap::default(),
268-
placeholders,
269-
reachability: BitSet::new_empty(regioncx.regions().count()),
291+
sccs_live_at_all_points,
292+
live_sccs_per_point,
293+
reachability: BitSet::new_empty(regioncx.constraint_sccs.num_sccs()),
270294
reachability_stack: vec![],
271295
}
272296
}
@@ -290,13 +314,11 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
290314
let sccs = &self.regioncx.constraint_sccs;
291315
let member_constraints = &self.regioncx.member_constraints;
292316

293-
self.reachability_stack.push(issuing_region);
294-
self.reachability.insert(issuing_region);
295-
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);
317+
let issuing_region_scc = sccs.scc(issuing_region);
318+
self.reachability_stack.push(issuing_region_scc);
319+
self.reachability.insert(issuing_region_scc);
299320

321+
while let Some(scc) = self.reachability_stack.pop() {
300322
// Handle successors of this SCC:
301323
//
302324
// 1. Via member constraints
@@ -308,11 +330,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
308330
// That is to say, if there are member constraints here, the loan escapes the
309331
// function and cannot go out of scope. We can early return.
310332
//
311-
// 2. Via placeholders
333+
// 2. Via regions that are live at all points: placeholders and free regions.
312334
//
313-
// If the issuing region outlives placeholders, its loan escapes the function and
335+
// If the issuing region outlives such a region, its loan escapes the function and
314336
// cannot go out of scope. We can early return.
315-
if member_constraints.indices(scc).next().is_some() || self.placeholders.contains(&scc)
337+
if member_constraints.indices(scc).next().is_some()
338+
|| self.sccs_live_at_all_points.contains(&scc)
316339
{
317340
self.reachability_stack.clear();
318341
self.reachability.clear();
@@ -321,15 +344,9 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
321344

322345
// 3. Via outlives successors, which we want to record and traverse, so we add them
323346
// 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);
347+
for &succ_scc in sccs.successors(scc) {
348+
if self.reachability.insert(succ_scc) {
349+
self.reachability_stack.push(succ_scc);
333350
}
334351
}
335352
}
@@ -364,10 +381,19 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
364381

365382
// Check whether the issuing region can reach local regions that are live at this
366383
// point.
367-
for reachable_region in self.reachability.iter() {
368-
if self.regioncx.liveness_constraints.contains(reachable_region, location) {
369-
issuing_region_can_reach_live_regions = true;
370-
break;
384+
//
385+
// FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and
386+
// `r` is live at point `q`, then it's guaranteed that `i` would reach `r` at point
387+
// `q`. The reachability is location-insensitive, and we could take advantage of
388+
// that, by jumping to a further point than the next statement. We can jump to the
389+
// furthest point within the block where `r` is live.
390+
let point = self.regioncx.liveness_constraints.point_from_location(location);
391+
if let Some(live_sccs) = self.live_sccs_per_point.row(point) {
392+
for live_scc in live_sccs.iter() {
393+
if self.reachability.contains(live_scc) {
394+
issuing_region_can_reach_live_regions = true;
395+
break;
396+
}
371397
}
372398
}
373399

0 commit comments

Comments
 (0)