Skip to content

Commit 9d4b3bf

Browse files
Auto merge of #145541 - cjgillot:dest-prop-live-range, r=<try>
[EXPERIMENT] Reimplement DestinationPropagation according to live ranges.
2 parents 425a9c0 + d0d5f20 commit 9d4b3bf

File tree

34 files changed

+1116
-1486
lines changed

34 files changed

+1116
-1486
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: 77 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 {
@@ -176,6 +219,32 @@ impl<I: Idx> IntervalSet<I> {
176219
})
177220
}
178221

222+
pub fn disjoint(&self, other: &IntervalSet<I>) -> bool
223+
where
224+
I: Step,
225+
{
226+
let helper = move || {
227+
let mut self_iter = self.iter_intervals();
228+
let mut other_iter = other.iter_intervals();
229+
230+
let mut self_current = self_iter.next()?;
231+
let mut other_current = other_iter.next()?;
232+
233+
loop {
234+
if self_current.end <= other_current.start {
235+
self_current = self_iter.next()?;
236+
continue;
237+
}
238+
if other_current.end <= self_current.start {
239+
other_current = other_iter.next()?;
240+
continue;
241+
}
242+
return Some(false);
243+
}
244+
};
245+
helper().unwrap_or(true)
246+
}
247+
179248
pub fn is_empty(&self) -> bool {
180249
self.map.is_empty()
181250
}
@@ -325,6 +394,14 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> {
325394
self.ensure_row(row).insert(point)
326395
}
327396

397+
pub fn prepend(&mut self, row: R, point: C) {
398+
self.ensure_row(row).prepend(point)
399+
}
400+
401+
pub fn append(&mut self, row: R, point: C) {
402+
self.ensure_row(row).append(point)
403+
}
404+
328405
pub fn contains(&self, row: R, point: C) -> bool {
329406
self.row(row).is_some_and(|r| r.contains(point))
330407
}

compiler/rustc_mir_dataflow/src/impls/liveness.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_> {
9292
}
9393

9494
match DefUse::for_place(*place, context) {
95-
Some(DefUse::Def) => {
95+
DefUse::Def => {
9696
if let PlaceContext::MutatingUse(
9797
MutatingUseContext::Call | MutatingUseContext::AsmOutput,
9898
) = context
@@ -105,8 +105,8 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_> {
105105
self.0.kill(place.local);
106106
}
107107
}
108-
Some(DefUse::Use) => self.0.gen_(place.local),
109-
None => {}
108+
DefUse::Use => self.0.gen_(place.local),
109+
DefUse::PartialWrite | DefUse::NonUse => {}
110110
}
111111

112112
self.visit_projection(place.as_ref(), context, location);
@@ -131,23 +131,29 @@ impl<'tcx> Visitor<'tcx> for YieldResumeEffect<'_> {
131131
}
132132

133133
#[derive(Eq, PartialEq, Clone)]
134-
enum DefUse {
134+
pub enum DefUse {
135+
/// Full write to the local.
135136
Def,
137+
/// Read of any part of the local.
136138
Use,
139+
/// Partial write to the local.
140+
PartialWrite,
141+
/// Non-use, like debuginfo.
142+
NonUse,
137143
}
138144

139145
impl DefUse {
140146
fn apply(state: &mut DenseBitSet<Local>, place: Place<'_>, context: PlaceContext) {
141147
match DefUse::for_place(place, context) {
142-
Some(DefUse::Def) => state.kill(place.local),
143-
Some(DefUse::Use) => state.gen_(place.local),
144-
None => {}
148+
DefUse::Def => state.kill(place.local),
149+
DefUse::Use => state.gen_(place.local),
150+
DefUse::PartialWrite | DefUse::NonUse => {}
145151
}
146152
}
147153

148-
fn for_place(place: Place<'_>, context: PlaceContext) -> Option<DefUse> {
154+
pub fn for_place(place: Place<'_>, context: PlaceContext) -> DefUse {
149155
match context {
150-
PlaceContext::NonUse(_) => None,
156+
PlaceContext::NonUse(_) => DefUse::NonUse,
151157

152158
PlaceContext::MutatingUse(
153159
MutatingUseContext::Call
@@ -156,21 +162,20 @@ impl DefUse {
156162
| MutatingUseContext::Store
157163
| MutatingUseContext::Deinit,
158164
) => {
165+
// Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use.
159166
if place.is_indirect() {
160-
// Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a
161-
// use.
162-
Some(DefUse::Use)
167+
DefUse::Use
163168
} else if place.projection.is_empty() {
164-
Some(DefUse::Def)
169+
DefUse::Def
165170
} else {
166-
None
171+
DefUse::PartialWrite
167172
}
168173
}
169174

170175
// Setting the discriminant is not a use because it does no reading, but it is also not
171176
// a def because it does not overwrite the whole place
172177
PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant) => {
173-
place.is_indirect().then_some(DefUse::Use)
178+
if place.is_indirect() { DefUse::Use } else { DefUse::PartialWrite }
174179
}
175180

176181
// All other contexts are uses...
@@ -188,7 +193,7 @@ impl DefUse {
188193
| NonMutatingUseContext::PlaceMention
189194
| NonMutatingUseContext::FakeBorrow
190195
| NonMutatingUseContext::SharedBorrow,
191-
) => Some(DefUse::Use),
196+
) => DefUse::Use,
192197

193198
PlaceContext::MutatingUse(MutatingUseContext::Projection)
194199
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {

compiler/rustc_mir_dataflow/src/impls/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ pub use self::initialized::{
99
MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain,
1010
};
1111
pub use self::liveness::{
12-
MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction,
12+
DefUse, MaybeLiveLocals, MaybeTransitiveLiveLocals,
13+
TransferFunction as LivenessTransferFunction,
1314
};
1415
pub use self::storage_liveness::{
1516
MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive, always_storage_live_locals,

compiler/rustc_mir_dataflow/src/points.rs

Lines changed: 1 addition & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
use rustc_index::bit_set::DenseBitSet;
2-
use rustc_index::interval::SparseIntervalMatrix;
31
use rustc_index::{Idx, IndexVec};
4-
use rustc_middle::mir::{self, BasicBlock, Body, Location};
5-
6-
use crate::framework::{Analysis, Results, ResultsVisitor, visit_results};
2+
use rustc_middle::mir::{BasicBlock, Body, Location};
73

84
/// Maps between a `Location` and a `PointIndex` (and vice versa).
95
pub struct DenseLocationMap {
@@ -93,65 +89,3 @@ rustc_index::newtype_index! {
9389
#[debug_format = "PointIndex({})"]
9490
pub struct PointIndex {}
9591
}
96-
97-
/// Add points depending on the result of the given dataflow analysis.
98-
pub fn save_as_intervals<'tcx, N, A>(
99-
elements: &DenseLocationMap,
100-
body: &mir::Body<'tcx>,
101-
mut analysis: A,
102-
results: Results<A::Domain>,
103-
) -> SparseIntervalMatrix<N, PointIndex>
104-
where
105-
N: Idx,
106-
A: Analysis<'tcx, Domain = DenseBitSet<N>>,
107-
{
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
118-
}
119-
120-
struct Visitor<'a, N: Idx> {
121-
elements: &'a DenseLocationMap,
122-
values: SparseIntervalMatrix<N, PointIndex>,
123-
}
124-
125-
impl<'tcx, A, N> ResultsVisitor<'tcx, A> for Visitor<'_, N>
126-
where
127-
A: Analysis<'tcx, Domain = DenseBitSet<N>>,
128-
N: Idx,
129-
{
130-
fn visit_after_primary_statement_effect<'mir>(
131-
&mut self,
132-
_analysis: &mut A,
133-
state: &A::Domain,
134-
_statement: &'mir mir::Statement<'tcx>,
135-
location: Location,
136-
) {
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-
});
142-
}
143-
144-
fn visit_after_primary_terminator_effect<'mir>(
145-
&mut self,
146-
_analysis: &mut A,
147-
state: &A::Domain,
148-
_terminator: &'mir mir::Terminator<'tcx>,
149-
location: Location,
150-
) {
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-
});
156-
}
157-
}

0 commit comments

Comments
 (0)