Leverage the interval property to precompute borrow kill points.

This commit is contained in:
Camille GILLOT 2023-05-19 11:55:13 +00:00
parent fdd030127c
commit 340fc2d08a
4 changed files with 100 additions and 49 deletions

View file

@ -156,10 +156,10 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
&mut self, &mut self,
borrow_index: BorrowIndex, borrow_index: BorrowIndex,
borrow_region: RegionVid, borrow_region: RegionVid,
location: Location, first_location: Location,
) { ) {
// We visit one BB at a time. The complication is that we may start in the // We visit one BB at a time. The complication is that we may start in the
// middle of the first BB visited (the one containing `location`), in which // middle of the first BB visited (the one containing `first_location`), in which
// case we may have to later on process the first part of that BB if there // case we may have to later on process the first part of that BB if there
// is a path back to its start. // is a path back to its start.
@ -168,61 +168,58 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
// `visited` once they are added to `stack`, before they are actually // `visited` once they are added to `stack`, before they are actually
// processed, because this avoids the need to look them up again on // processed, because this avoids the need to look them up again on
// completion. // completion.
self.visited.insert(location.block); self.visited.insert(first_location.block);
let mut first_lo = location.statement_index; let first_block = first_location.block;
let first_hi = self.body[location.block].statements.len(); let mut first_lo = first_location.statement_index;
let first_hi = self.body[first_block].statements.len();
self.visit_stack.push(StackEntry { bb: location.block, lo: first_lo, hi: first_hi }); self.visit_stack.push(StackEntry { bb: first_block, lo: first_lo, hi: first_hi });
while let Some(StackEntry { bb, lo, hi }) = self.visit_stack.pop() { 'preorder: while let Some(StackEntry { bb, lo, hi }) = self.visit_stack.pop() {
// If we process the first part of the first basic block (i.e. we encounter that block if let Some(kill_stmt) =
// for the second time), we no longer have to visit its successors again. self.regioncx.first_non_contained_inclusive(borrow_region, bb, lo, hi)
let mut finished_early = bb == location.block && hi != first_hi; {
for i in lo..=hi { let kill_location = Location { block: bb, statement_index: kill_stmt };
let location = Location { block: bb, statement_index: i };
// If region does not contain a point at the location, then add to list and skip // If region does not contain a point at the location, then add to list and skip
// successor locations. // successor locations.
if !self.regioncx.region_contains(borrow_region, location) { debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
debug!("borrow {:?} gets killed at {:?}", borrow_index, location); self.borrows_out_of_scope_at_location
self.borrows_out_of_scope_at_location .entry(kill_location)
.entry(location) .or_default()
.or_default() .push(borrow_index);
.push(borrow_index); continue 'preorder;
finished_early = true;
break;
}
} }
if !finished_early { // If we process the first part of the first basic block (i.e. we encounter that block
// Add successor BBs to the work list, if necessary. // for the second time), we no longer have to visit its successors again.
let bb_data = &self.body[bb]; if bb == first_block && hi != first_hi {
debug_assert!(hi == bb_data.statements.len()); continue;
for succ_bb in bb_data.terminator().successors() { }
if !self.visited.insert(succ_bb) {
if succ_bb == location.block && first_lo > 0 {
// `succ_bb` has been seen before. If it wasn't
// fully processed, add its first part to `stack`
// for processing.
self.visit_stack.push(StackEntry {
bb: succ_bb,
lo: 0,
hi: first_lo - 1,
});
// And update this entry with 0, to represent the // Add successor BBs to the work list, if necessary.
// whole BB being processed. let bb_data = &self.body[bb];
first_lo = 0; debug_assert!(hi == bb_data.statements.len());
} for succ_bb in bb_data.terminator().successors() {
} else { if !self.visited.insert(succ_bb) {
// succ_bb hasn't been seen before. Add it to if succ_bb == first_block && first_lo > 0 {
// `stack` for processing. // `succ_bb` has been seen before. If it wasn't
self.visit_stack.push(StackEntry { // fully processed, add its first part to `stack`
bb: succ_bb, // for processing.
lo: 0, self.visit_stack.push(StackEntry { bb: succ_bb, lo: 0, hi: first_lo - 1 });
hi: self.body[succ_bb].statements.len(),
}); // And update this entry with 0, to represent the
// whole BB being processed.
first_lo = 0;
} }
} else {
// succ_bb hasn't been seen before. Add it to
// `stack` for processing.
self.visit_stack.push(StackEntry {
bb: succ_bb,
lo: 0,
hi: self.body[succ_bb].statements.len(),
});
} }
} }
} }

View file

@ -12,7 +12,7 @@ use rustc_infer::infer::outlives::test_type_match;
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
use rustc_middle::mir::{ use rustc_middle::mir::{
Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy, BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy,
ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint, ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint,
TerminatorKind, TerminatorKind,
}; };
@ -598,6 +598,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_values.contains(scc, p) self.scc_values.contains(scc, p)
} }
/// Returns the lowest statement index in `start..=end` which is not contained by `r`.
///
/// Panics if called before `solve()` executes.
pub(crate) fn first_non_contained_inclusive(
&self,
r: RegionVid,
block: BasicBlock,
start: usize,
end: usize,
) -> Option<usize> {
let scc = self.constraint_sccs.scc(r);
self.scc_values.first_non_contained_inclusive(scc, block, start, end)
}
/// Returns access to the value of `r` for debugging purposes. /// Returns access to the value of `r` for debugging purposes.
pub(crate) fn region_value_str(&self, r: RegionVid) -> String { pub(crate) fn region_value_str(&self, r: RegionVid) -> String {
let scc = self.constraint_sccs.scc(r); let scc = self.constraint_sccs.scc(r);

View file

@ -283,6 +283,22 @@ impl<N: Idx> RegionValues<N> {
elem.contained_in_row(self, r) elem.contained_in_row(self, r)
} }
/// Returns the lowest statement index in `start..=end` which is not contained by `r`.
pub(crate) fn first_non_contained_inclusive(
&self,
r: N,
block: BasicBlock,
start: usize,
end: usize,
) -> Option<usize> {
let row = self.points.row(r)?;
let block = self.elements.entry_point(block);
let start = block.plus(start);
let end = block.plus(end);
let first_unset = row.first_unset_in(start..=end)?;
Some(first_unset.index() - block.index())
}
/// `self[to] |= values[from]`, essentially: that is, take all the /// `self[to] |= values[from]`, essentially: that is, take all the
/// elements for the region `from` from `values` and add them to /// elements for the region `from` from `values` and add them to
/// the region `to` in `self`. /// the region `to` in `self`.

View file

@ -181,6 +181,30 @@ impl<I: Idx> IntervalSet<I> {
self.map.is_empty() self.map.is_empty()
} }
/// Equivalent to `range.iter().find(|i| !self.contains(i))`.
pub fn first_unset_in(&self, range: impl RangeBounds<I> + Clone) -> Option<I> {
let start = inclusive_start(range.clone());
let Some(end) = inclusive_end(self.domain, range) else {
// empty range
return None;
};
if start > end {
return None;
}
let Some(last) = self.map.partition_point(|r| r.0 <= start).checked_sub(1) else {
// All ranges in the map start after the new range's end
return Some(I::new(start as usize));
};
let (_, prev_end) = self.map[last];
if start > prev_end {
Some(I::new(start as usize))
} else if prev_end < end {
Some(I::new(prev_end as usize + 1))
} else {
None
}
}
/// Returns the maximum (last) element present in the set from `range`. /// Returns the maximum (last) element present in the set from `range`.
pub fn last_set_in(&self, range: impl RangeBounds<I> + Clone) -> Option<I> { pub fn last_set_in(&self, range: impl RangeBounds<I> + Clone) -> Option<I> {
let start = inclusive_start(range.clone()); let start = inclusive_start(range.clone());