Skip to content

Commit 6ace5ea

Browse files
committed
Merge branch 'interval-kill'
2 parents 809a9e8 + 4dd91c0 commit 6ace5ea

File tree

4 files changed

+113
-70
lines changed

4 files changed

+113
-70
lines changed

compiler/rustc_borrowck/src/dataflow.rs

Lines changed: 58 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,9 @@ pub struct Borrows<'a, 'tcx> {
125125
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
126126
}
127127

128-
struct StackEntry {
129-
bb: mir::BasicBlock,
130-
lo: usize,
131-
hi: usize,
132-
}
133-
134128
struct OutOfScopePrecomputer<'a, 'tcx> {
135129
visited: BitSet<mir::BasicBlock>,
136-
visit_stack: Vec<StackEntry>,
130+
visit_stack: Vec<mir::BasicBlock>,
137131
body: &'a Body<'tcx>,
138132
regioncx: &'a RegionInferenceContext<'tcx>,
139133
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
@@ -156,73 +150,68 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
156150
&mut self,
157151
borrow_index: BorrowIndex,
158152
borrow_region: RegionVid,
159-
location: Location,
153+
first_location: Location,
160154
) {
161-
// We visit one BB at a time. The complication is that we may start in the
162-
// middle of the first BB visited (the one containing `location`), in which
163-
// case we may have to later on process the first part of that BB if there
164-
// is a path back to its start.
165-
166-
// For visited BBs, we record the index of the first statement processed.
167-
// (In fully processed BBs this index is 0.) Note also that we add BBs to
168-
// `visited` once they are added to `stack`, before they are actually
169-
// processed, because this avoids the need to look them up again on
170-
// completion.
171-
self.visited.insert(location.block);
172-
173-
let mut first_lo = location.statement_index;
174-
let first_hi = self.body[location.block].statements.len();
175-
176-
self.visit_stack.push(StackEntry { bb: location.block, lo: first_lo, hi: first_hi });
177-
178-
while let Some(StackEntry { bb, lo, hi }) = self.visit_stack.pop() {
179-
// If we process the first part of the first basic block (i.e. we encounter that block
180-
// for the second time), we no longer have to visit its successors again.
181-
let mut finished_early = bb == location.block && hi != first_hi;
182-
for i in lo..=hi {
183-
let location = Location { block: bb, statement_index: i };
155+
let first_block = first_location.block;
156+
let first_bb_data = &self.body.basic_blocks[first_block];
157+
158+
// This is the first block, we only want to visit it from the creation of the borrow at
159+
// `first_location`.
160+
let first_lo = first_location.statement_index;
161+
let first_hi = first_bb_data.statements.len();
162+
163+
if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
164+
borrow_region,
165+
first_block,
166+
first_lo,
167+
first_hi,
168+
) {
169+
let kill_location = Location { block: first_block, statement_index: kill_stmt };
170+
// If region does not contain a point at the location, then add to list and skip
171+
// successor locations.
172+
debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
173+
self.borrows_out_of_scope_at_location
174+
.entry(kill_location)
175+
.or_default()
176+
.push(borrow_index);
177+
178+
// The borrow is already dead, there is no need to visit other blocks.
179+
return;
180+
}
181+
182+
// The borrow is not dead. Add successor BBs to the work list, if necessary.
183+
for succ_bb in first_bb_data.terminator().successors() {
184+
if self.visited.insert(succ_bb) {
185+
self.visit_stack.push(succ_bb);
186+
}
187+
}
188+
189+
// We may end up visiting `first_block` again. This is not an issue: we know at this point
190+
// that it does not kill the borrow in the `first_lo..=first_hi` range, so checking the
191+
// `0..first_lo` range and the `0..first_hi` range give the same result.
192+
while let Some(block) = self.visit_stack.pop() {
193+
let bb_data = &self.body[block];
194+
let num_stmts = bb_data.statements.len();
195+
if let Some(kill_stmt) =
196+
self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
197+
{
198+
let kill_location = Location { block, statement_index: kill_stmt };
184199
// If region does not contain a point at the location, then add to list and skip
185200
// successor locations.
186-
if !self.regioncx.region_contains(borrow_region, location) {
187-
debug!("borrow {:?} gets killed at {:?}", borrow_index, location);
188-
self.borrows_out_of_scope_at_location
189-
.entry(location)
190-
.or_default()
191-
.push(borrow_index);
192-
finished_early = true;
193-
break;
194-
}
201+
debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
202+
self.borrows_out_of_scope_at_location
203+
.entry(kill_location)
204+
.or_default()
205+
.push(borrow_index);
206+
207+
// We killed the borrow, so we do not visit this block's successors.
208+
continue;
195209
}
196210

197-
if !finished_early {
198-
// Add successor BBs to the work list, if necessary.
199-
let bb_data = &self.body[bb];
200-
debug_assert!(hi == bb_data.statements.len());
201-
for succ_bb in bb_data.terminator().successors() {
202-
if !self.visited.insert(succ_bb) {
203-
if succ_bb == location.block && first_lo > 0 {
204-
// `succ_bb` has been seen before. If it wasn't
205-
// fully processed, add its first part to `stack`
206-
// for processing.
207-
self.visit_stack.push(StackEntry {
208-
bb: succ_bb,
209-
lo: 0,
210-
hi: first_lo - 1,
211-
});
212-
213-
// And update this entry with 0, to represent the
214-
// whole BB being processed.
215-
first_lo = 0;
216-
}
217-
} else {
218-
// succ_bb hasn't been seen before. Add it to
219-
// `stack` for processing.
220-
self.visit_stack.push(StackEntry {
221-
bb: succ_bb,
222-
lo: 0,
223-
hi: self.body[succ_bb].statements.len(),
224-
});
225-
}
211+
// Add successor BBs to the work list, if necessary.
212+
for succ_bb in bb_data.terminator().successors() {
213+
if self.visited.insert(succ_bb) {
214+
self.visit_stack.push(succ_bb);
226215
}
227216
}
228217
}

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_infer::infer::outlives::test_type_match;
1212
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
1313
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
1414
use rustc_middle::mir::{
15-
Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy,
15+
BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy,
1616
ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint,
1717
TerminatorKind,
1818
};
@@ -599,6 +599,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
599599
self.scc_values.contains(scc, p)
600600
}
601601

602+
/// Returns the lowest statement index in `start..=end` which is not contained by `r`.
603+
///
604+
/// Panics if called before `solve()` executes.
605+
pub(crate) fn first_non_contained_inclusive(
606+
&self,
607+
r: RegionVid,
608+
block: BasicBlock,
609+
start: usize,
610+
end: usize,
611+
) -> Option<usize> {
612+
let scc = self.constraint_sccs.scc(r);
613+
self.scc_values.first_non_contained_inclusive(scc, block, start, end)
614+
}
615+
602616
/// Returns access to the value of `r` for debugging purposes.
603617
pub(crate) fn region_value_str(&self, r: RegionVid) -> String {
604618
let scc = self.constraint_sccs.scc(r);

compiler/rustc_borrowck/src/region_infer/values.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,22 @@ impl<N: Idx> RegionValues<N> {
283283
elem.contained_in_row(self, r)
284284
}
285285

286+
/// Returns the lowest statement index in `start..=end` which is not contained by `r`.
287+
pub(crate) fn first_non_contained_inclusive(
288+
&self,
289+
r: N,
290+
block: BasicBlock,
291+
start: usize,
292+
end: usize,
293+
) -> Option<usize> {
294+
let row = self.points.row(r)?;
295+
let block = self.elements.entry_point(block);
296+
let start = block.plus(start);
297+
let end = block.plus(end);
298+
let first_unset = row.first_unset_in(start..=end)?;
299+
Some(first_unset.index() - block.index())
300+
}
301+
286302
/// `self[to] |= values[from]`, essentially: that is, take all the
287303
/// elements for the region `from` from `values` and add them to
288304
/// the region `to` in `self`.

compiler/rustc_index/src/interval.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,30 @@ impl<I: Idx> IntervalSet<I> {
181181
self.map.is_empty()
182182
}
183183

184+
/// Equivalent to `range.iter().find(|i| !self.contains(i))`.
185+
pub fn first_unset_in(&self, range: impl RangeBounds<I> + Clone) -> Option<I> {
186+
let start = inclusive_start(range.clone());
187+
let Some(end) = inclusive_end(self.domain, range) else {
188+
// empty range
189+
return None;
190+
};
191+
if start > end {
192+
return None;
193+
}
194+
let Some(last) = self.map.partition_point(|r| r.0 <= start).checked_sub(1) else {
195+
// All ranges in the map start after the new range's end
196+
return Some(I::new(start as usize));
197+
};
198+
let (_, prev_end) = self.map[last];
199+
if start > prev_end {
200+
Some(I::new(start as usize))
201+
} else if prev_end < end {
202+
Some(I::new(prev_end as usize + 1))
203+
} else {
204+
None
205+
}
206+
}
207+
184208
/// Returns the maximum (last) element present in the set from `range`.
185209
pub fn last_set_in(&self, range: impl RangeBounds<I> + Clone) -> Option<I> {
186210
let start = inclusive_start(range.clone());

0 commit comments

Comments
 (0)