Skip to content

Commit b835228

Browse files
committed
Introduce fast insertion at extremities to IntervalSet.
1 parent 7492a82 commit b835228

File tree

2 files changed

+90
-27
lines changed

2 files changed

+90
-27
lines changed

compiler/rustc_index/src/interval.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,49 @@ impl<I: Idx> IntervalSet<I> {
140140
result
141141
}
142142

143+
/// Specialized version of `insert` when we know that the inserted point is *before* any
144+
/// contained.
145+
pub fn prepend(&mut self, point: I) {
146+
let point = point.index() as u32;
147+
148+
if let Some((first_start, _)) = self.map.first_mut() {
149+
assert!(point < *first_start);
150+
if point + 1 == *first_start {
151+
*first_start = point;
152+
} else {
153+
self.map.insert(0, (point, point));
154+
}
155+
} else {
156+
// If the map is empty, push is faster than insert.
157+
self.map.push((point, point));
158+
}
159+
160+
debug_assert!(
161+
self.check_invariants(),
162+
"wrong intervals after prepend {point:?} to {self:?}"
163+
);
164+
}
165+
166+
/// Specialized version of `insert` when we know that the inserted point is *after* any
167+
/// contained.
168+
pub fn append(&mut self, point: I) {
169+
let point = point.index() as u32;
170+
171+
if let Some((_, last_end)) = self.map.last_mut()
172+
&& let _ = assert!(*last_end < point)
173+
&& point == *last_end + 1
174+
{
175+
*last_end = point;
176+
} else {
177+
self.map.push((point, point));
178+
}
179+
180+
debug_assert!(
181+
self.check_invariants(),
182+
"wrong intervals after append {point:?} to {self:?}"
183+
);
184+
}
185+
143186
pub fn contains(&self, needle: I) -> bool {
144187
let needle = needle.index() as u32;
145188
let Some(last) = self.map.partition_point(|r| r.0 <= needle).checked_sub(1) else {
@@ -325,6 +368,14 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> {
325368
self.ensure_row(row).insert(point)
326369
}
327370

371+
pub fn prepend(&mut self, row: R, point: C) {
372+
self.ensure_row(row).prepend(point)
373+
}
374+
375+
pub fn append(&mut self, row: R, point: C) {
376+
self.ensure_row(row).append(point)
377+
}
378+
328379
pub fn contains(&self, row: R, point: C) -> bool {
329380
self.row(row).is_some_and(|r| r.contains(point))
330381
}

compiler/rustc_mir_dataflow/src/points.rs

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_index::interval::SparseIntervalMatrix;
33
use rustc_index::{Idx, IndexVec};
44
use rustc_middle::mir::{self, BasicBlock, Body, Location};
55

6-
use crate::framework::{Analysis, Results, ResultsVisitor, visit_results};
6+
use crate::framework::{Analysis, Direction, Results, ResultsVisitor, visit_results};
77

88
/// Maps between a `Location` and a `PointIndex` (and vice versa).
99
pub struct DenseLocationMap {
@@ -105,27 +105,47 @@ where
105105
N: Idx,
106106
A: Analysis<'tcx, Domain = DenseBitSet<N>>,
107107
{
108-
let values = SparseIntervalMatrix::new(elements.num_points());
109-
let mut visitor = Visitor { elements, values };
110-
visit_results(
111-
body,
112-
body.basic_blocks.reverse_postorder().iter().copied(),
113-
&mut analysis,
114-
&results,
115-
&mut visitor,
116-
);
117-
visitor.values
108+
let mut values = SparseIntervalMatrix::new(elements.num_points());
109+
let reachable_blocks = mir::traversal::reachable_as_bitset(body);
110+
if A::Direction::IS_BACKWARD {
111+
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
112+
// allows to use the more efficient `prepend` method to interval sets.
113+
let callback = |state: &DenseBitSet<N>, location| {
114+
let point = elements.point_from_location(location);
115+
// Use internal iterator manually as it is much more efficient.
116+
state.iter().for_each(|node| values.prepend(node, point));
117+
};
118+
let mut visitor = Visitor { callback };
119+
visit_results(
120+
body,
121+
// Note the `.rev()`.
122+
body.basic_blocks.indices().filter(|&bb| reachable_blocks.contains(bb)).rev(),
123+
&mut analysis,
124+
&results,
125+
&mut visitor,
126+
);
127+
} else {
128+
// Iterate blocks in increasing order, to visit locations in increasing order. This
129+
// allows to use the more efficient `append` method to interval sets.
130+
let callback = |state: &DenseBitSet<N>, location| {
131+
let point = elements.point_from_location(location);
132+
// Use internal iterator manually as it is much more efficient.
133+
state.iter().for_each(|node| values.append(node, point));
134+
};
135+
let mut visitor = Visitor { callback };
136+
visit_results(body, reachable_blocks.iter(), &mut analysis, &results, &mut visitor);
137+
}
138+
values
118139
}
119140

120-
struct Visitor<'a, N: Idx> {
121-
elements: &'a DenseLocationMap,
122-
values: SparseIntervalMatrix<N, PointIndex>,
141+
struct Visitor<F> {
142+
callback: F,
123143
}
124144

125-
impl<'tcx, A, N> ResultsVisitor<'tcx, A> for Visitor<'_, N>
145+
impl<'tcx, A, F> ResultsVisitor<'tcx, A> for Visitor<F>
126146
where
127-
A: Analysis<'tcx, Domain = DenseBitSet<N>>,
128-
N: Idx,
147+
A: Analysis<'tcx>,
148+
F: FnMut(&A::Domain, Location),
129149
{
130150
fn visit_after_primary_statement_effect<'mir>(
131151
&mut self,
@@ -134,11 +154,7 @@ where
134154
_statement: &'mir mir::Statement<'tcx>,
135155
location: Location,
136156
) {
137-
let point = self.elements.point_from_location(location);
138-
// Use internal iterator manually as it is much more efficient.
139-
state.iter().for_each(|node| {
140-
self.values.insert(node, point);
141-
});
157+
(self.callback)(state, location);
142158
}
143159

144160
fn visit_after_primary_terminator_effect<'mir>(
@@ -148,10 +164,6 @@ where
148164
_terminator: &'mir mir::Terminator<'tcx>,
149165
location: Location,
150166
) {
151-
let point = self.elements.point_from_location(location);
152-
// Use internal iterator manually as it is much more efficient.
153-
state.iter().for_each(|node| {
154-
self.values.insert(node, point);
155-
});
167+
(self.callback)(state, location);
156168
}
157169
}

0 commit comments

Comments
 (0)