Skip to content

Commit 00ffd2b

Browse files
committed
Auto merge of #142915 - cjgillot:dest-prop-default, r=<try>
Enable DestinationPropagation by default This PR proposes to perform destination propagation on MIR. Most of the pass was fully rewritten by `@JakobDegen` in #96451. This pass is quite heavy, as it needs to perform and save the results of a full liveness dataflow analysis. This accounts for ~50% of the pass' runtime. Perf sees a few decent savings in later llvm passes, but also sizeable régressions when there are no savings to balance this pass' runtime.
2 parents f51c987 + 9298dfa commit 00ffd2b

30 files changed

+824
-1111
lines changed

compiler/rustc_index/src/bit_set.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,28 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
15381538
changed != 0
15391539
}
15401540

1541+
/// Adds the bits from col `read` to the bits from col `write`, and
1542+
/// returns `true` if anything changed.
1543+
pub fn union_cols(&mut self, read: C, write: C) -> bool {
1544+
assert!(read.index() < self.num_columns && write.index() < self.num_columns);
1545+
1546+
let words_per_row = num_words(self.num_columns);
1547+
let (read_index, read_mask) = word_index_and_mask(read);
1548+
let (write_index, write_mask) = word_index_and_mask(write);
1549+
1550+
let words = &mut self.words[..];
1551+
let mut changed = 0;
1552+
for row in 0..self.num_rows {
1553+
let write_word = words[row * words_per_row + write_index];
1554+
let read_word = (words[row * words_per_row + read_index] & read_mask) != 0;
1555+
let new_word = if read_word { write_word | write_mask } else { write_word };
1556+
words[row * words_per_row + write_index] = new_word;
1557+
// See `bitwise` for the rationale.
1558+
changed |= write_word ^ new_word;
1559+
}
1560+
changed != 0
1561+
}
1562+
15411563
/// Adds the bits from `with` to the bits from row `write`, and
15421564
/// returns `true` if anything changed.
15431565
pub fn union_row_with(&mut self, with: &DenseBitSet<C>, write: R) -> bool {

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)