Skip to content

Commit fb307e5

Browse files
committed
Rewrite precompute_borrows_out_of_scope for fewer hash table lookups.
It now does one hash table lookup per basic block, instead of one per statement. This is worthwhile because this function is hot for NLL builds of `ucd`.
1 parent 763d91a commit fb307e5

File tree

1 file changed

+79
-41
lines changed

1 file changed

+79
-41
lines changed

src/librustc_mir/dataflow/impls/borrows.rs

+79-41
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc::ty::{RegionKind, RegionVid};
2121
use rustc::ty::RegionKind::ReScope;
2222

2323
use rustc_data_structures::bitslice::{BitwiseOperator, Word};
24-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
24+
use rustc_data_structures::fx::FxHashMap;
2525
use rustc_data_structures::indexed_set::IdxSet;
2626
use rustc_data_structures::indexed_vec::IndexVec;
2727
use rustc_data_structures::sync::Lrc;
@@ -53,6 +53,13 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
5353
_nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
5454
}
5555

56+
struct StackEntry {
57+
bb: mir::BasicBlock,
58+
lo: usize,
59+
hi: usize,
60+
first_part_only: bool
61+
}
62+
5663
fn precompute_borrows_out_of_scope<'tcx>(
5764
mir: &Mir<'tcx>,
5865
regioncx: &Rc<RegionInferenceContext<'tcx>>,
@@ -61,48 +68,79 @@ fn precompute_borrows_out_of_scope<'tcx>(
6168
borrow_region: RegionVid,
6269
location: Location,
6370
) {
64-
// Keep track of places we've locations to check and locations that we have checked.
65-
let mut stack = vec![ location ];
66-
let mut visited = FxHashSet();
67-
visited.insert(location);
68-
69-
debug!(
70-
"borrow {:?} has region {:?} with value {:?}",
71-
borrow_index,
72-
borrow_region,
73-
regioncx.region_value_str(borrow_region),
74-
);
75-
debug!("borrow {:?} starts at {:?}", borrow_index, location);
76-
while let Some(location) = stack.pop() {
77-
// If region does not contain a point at the location, then add to list and skip
78-
// successor locations.
79-
if !regioncx.region_contains(borrow_region, location) {
80-
debug!("borrow {:?} gets killed at {:?}", borrow_index, location);
81-
borrows_out_of_scope_at_location
82-
.entry(location)
83-
.or_default()
84-
.push(borrow_index);
85-
continue;
71+
// We visit one BB at a time. The complication is that we may start in the
72+
// middle of the first BB visited (the one containing `location`), in which
73+
// case we may have to later on process the first part of that BB if there
74+
// is a path back to its start.
75+
76+
// For visited BBs, we record the index of the first statement processed.
77+
// (In fully processed BBs this index is 0.) Note also that we add BBs to
78+
// `visited` once they are added to `stack`, before they are actually
79+
// processed, because this avoids the need to look them up again on
80+
// completion.
81+
let mut visited = FxHashMap();
82+
visited.insert(location.block, location.statement_index);
83+
84+
let mut stack = vec![];
85+
stack.push(StackEntry {
86+
bb: location.block,
87+
lo: location.statement_index,
88+
hi: mir[location.block].statements.len(),
89+
first_part_only: false,
90+
});
91+
92+
while let Some(StackEntry { bb, lo, hi, first_part_only }) = stack.pop() {
93+
let mut finished_early = first_part_only;
94+
for i in lo ..= hi {
95+
let location = Location { block: bb, statement_index: i };
96+
// If region does not contain a point at the location, then add to list and skip
97+
// successor locations.
98+
if !regioncx.region_contains(borrow_region, location) {
99+
debug!("borrow {:?} gets killed at {:?}", borrow_index, location);
100+
borrows_out_of_scope_at_location
101+
.entry(location)
102+
.or_default()
103+
.push(borrow_index);
104+
finished_early = true;
105+
break;
106+
}
86107
}
87108

88-
let bb_data = &mir[location.block];
89-
// If this is the last statement in the block, then add the
90-
// terminator successors next.
91-
if location.statement_index == bb_data.statements.len() {
92-
// Add successors to locations to visit, if not visited before.
93-
if let Some(ref terminator) = bb_data.terminator {
94-
for block in terminator.successors() {
95-
let loc = block.start_location();
96-
if visited.insert(loc) {
97-
stack.push(loc);
98-
}
99-
}
100-
}
101-
} else {
102-
// Visit next statement in block.
103-
let loc = location.successor_within_block();
104-
if visited.insert(loc) {
105-
stack.push(loc);
109+
if !finished_early {
110+
// Add successor BBs to the work list, if necessary.
111+
let bb_data = &mir[bb];
112+
assert!(hi == bb_data.statements.len());
113+
for &succ_bb in bb_data.terminator.as_ref().unwrap().successors() {
114+
visited.entry(succ_bb)
115+
.and_modify(|lo| {
116+
// `succ_bb` has been seen before. If it wasn't
117+
// fully processed, add its first part to `stack`
118+
// for processing.
119+
if *lo > 0 {
120+
stack.push(StackEntry {
121+
bb: succ_bb,
122+
lo: 0,
123+
hi: *lo - 1,
124+
first_part_only: true,
125+
});
126+
}
127+
// And update this entry with 0, to represent the
128+
// whole BB being processed.
129+
*lo = 0;
130+
})
131+
.or_insert_with(|| {
132+
// succ_bb hasn't been seen before. Add it to
133+
// `stack` for processing.
134+
stack.push(StackEntry {
135+
bb: succ_bb,
136+
lo: 0,
137+
hi: mir[succ_bb].statements.len(),
138+
first_part_only: false,
139+
});
140+
// Insert 0 for this BB, to represent the whole BB
141+
// being processed.
142+
0
143+
});
106144
}
107145
}
108146
}

0 commit comments

Comments
 (0)