Skip to content

[EXPERIMENT] Reimplement DestinationPropagation according to live ranges. #145541

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions compiler/rustc_index/src/interval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,52 @@ impl<I: Idx> IntervalSet<I> {
result
}

/// Specialized version of `insert` when we know that the inserted point is *before* any
/// contained.
pub fn prepend(&mut self, point: I) {
let point = point.index() as u32;

if let Some((first_start, _)) = self.map.first_mut() {
assert!(point <= *first_start);
if point == *first_start {
// The point is already present in the set.
} else if point + 1 == *first_start {
// Just extend the first range.
*first_start = point;
} else {
self.map.insert(0, (point, point));
}
} else {
// If the map is empty, push is faster than insert.
self.map.push((point, point));
}

debug_assert!(
self.check_invariants(),
"wrong intervals after prepend {point:?} to {self:?}"
);
}

/// Specialized version of `insert` when we know that the inserted point is *after* any
/// contained.
pub fn append(&mut self, point: I) {
let point = point.index() as u32;

if let Some((_, last_end)) = self.map.last_mut()
&& let _ = assert!(*last_end < point)
&& point == *last_end + 1
{
*last_end = point;
} else {
self.map.push((point, point));
}

debug_assert!(
self.check_invariants(),
"wrong intervals after append {point:?} to {self:?}"
);
}

pub fn contains(&self, needle: I) -> bool {
let needle = needle.index() as u32;
let Some(last) = self.map.partition_point(|r| r.0 <= needle).checked_sub(1) else {
Expand Down Expand Up @@ -176,6 +222,32 @@ impl<I: Idx> IntervalSet<I> {
})
}

pub fn disjoint(&self, other: &IntervalSet<I>) -> bool
where
I: Step,
{
let helper = move || {
let mut self_iter = self.iter_intervals();
let mut other_iter = other.iter_intervals();

let mut self_current = self_iter.next()?;
let mut other_current = other_iter.next()?;

loop {
if self_current.end <= other_current.start {
self_current = self_iter.next()?;
continue;
}
if other_current.end <= self_current.start {
other_current = other_iter.next()?;
continue;
}
return Some(false);
}
};
helper().unwrap_or(true)
}

pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
Expand Down Expand Up @@ -325,6 +397,14 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> {
self.ensure_row(row).insert(point)
}

pub fn prepend(&mut self, row: R, point: C) {
self.ensure_row(row).prepend(point)
}

pub fn append(&mut self, row: R, point: C) {
self.ensure_row(row).append(point)
}

pub fn contains(&self, row: R, point: C) -> bool {
self.row(row).is_some_and(|r| r.contains(point))
}
Expand Down
37 changes: 21 additions & 16 deletions compiler/rustc_mir_dataflow/src/impls/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_> {
}

match DefUse::for_place(*place, context) {
Some(DefUse::Def) => {
DefUse::Def => {
if let PlaceContext::MutatingUse(
MutatingUseContext::Call | MutatingUseContext::AsmOutput,
) = context
Expand All @@ -105,8 +105,8 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_> {
self.0.kill(place.local);
}
}
Some(DefUse::Use) => self.0.gen_(place.local),
None => {}
DefUse::Use => self.0.gen_(place.local),
DefUse::PartialWrite | DefUse::NonUse => {}
}

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

#[derive(Eq, PartialEq, Clone)]
enum DefUse {
pub enum DefUse {
/// Full write to the local.
Def,
/// Read of any part of the local.
Use,
/// Partial write to the local.
PartialWrite,
/// Non-use, like debuginfo.
NonUse,
}

impl DefUse {
fn apply(state: &mut DenseBitSet<Local>, place: Place<'_>, context: PlaceContext) {
match DefUse::for_place(place, context) {
Some(DefUse::Def) => state.kill(place.local),
Some(DefUse::Use) => state.gen_(place.local),
None => {}
DefUse::Def => state.kill(place.local),
DefUse::Use => state.gen_(place.local),
DefUse::PartialWrite | DefUse::NonUse => {}
}
}

fn for_place(place: Place<'_>, context: PlaceContext) -> Option<DefUse> {
pub fn for_place(place: Place<'_>, context: PlaceContext) -> DefUse {
match context {
PlaceContext::NonUse(_) => None,
PlaceContext::NonUse(_) => DefUse::NonUse,

PlaceContext::MutatingUse(
MutatingUseContext::Call
Expand All @@ -156,21 +162,20 @@ impl DefUse {
| MutatingUseContext::Store
| MutatingUseContext::Deinit,
) => {
// Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use.
if place.is_indirect() {
// Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a
// use.
Some(DefUse::Use)
DefUse::Use
} else if place.projection.is_empty() {
Some(DefUse::Def)
DefUse::Def
} else {
None
DefUse::PartialWrite
}
}

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

// All other contexts are uses...
Expand All @@ -188,7 +193,7 @@ impl DefUse {
| NonMutatingUseContext::PlaceMention
| NonMutatingUseContext::FakeBorrow
| NonMutatingUseContext::SharedBorrow,
) => Some(DefUse::Use),
) => DefUse::Use,

PlaceContext::MutatingUse(MutatingUseContext::Projection)
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_dataflow/src/impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ pub use self::initialized::{
MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain,
};
pub use self::liveness::{
MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction,
DefUse, MaybeLiveLocals, MaybeTransitiveLiveLocals,
TransferFunction as LivenessTransferFunction,
};
pub use self::storage_liveness::{
MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive, always_storage_live_locals,
Expand Down
68 changes: 1 addition & 67 deletions compiler/rustc_mir_dataflow/src/points.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use rustc_index::bit_set::DenseBitSet;
use rustc_index::interval::SparseIntervalMatrix;
use rustc_index::{Idx, IndexVec};
use rustc_middle::mir::{self, BasicBlock, Body, Location};

use crate::framework::{Analysis, Results, ResultsVisitor, visit_results};
use rustc_middle::mir::{BasicBlock, Body, Location};

/// Maps between a `Location` and a `PointIndex` (and vice versa).
pub struct DenseLocationMap {
Expand Down Expand Up @@ -93,65 +89,3 @@ rustc_index::newtype_index! {
#[debug_format = "PointIndex({})"]
pub struct PointIndex {}
}

/// Add points depending on the result of the given dataflow analysis.
pub fn save_as_intervals<'tcx, N, A>(
elements: &DenseLocationMap,
body: &mir::Body<'tcx>,
mut analysis: A,
results: Results<A::Domain>,
) -> SparseIntervalMatrix<N, PointIndex>
where
N: Idx,
A: Analysis<'tcx, Domain = DenseBitSet<N>>,
{
let values = SparseIntervalMatrix::new(elements.num_points());
let mut visitor = Visitor { elements, values };
visit_results(
body,
body.basic_blocks.reverse_postorder().iter().copied(),
&mut analysis,
&results,
&mut visitor,
);
visitor.values
}

struct Visitor<'a, N: Idx> {
elements: &'a DenseLocationMap,
values: SparseIntervalMatrix<N, PointIndex>,
}

impl<'tcx, A, N> ResultsVisitor<'tcx, A> for Visitor<'_, N>
where
A: Analysis<'tcx, Domain = DenseBitSet<N>>,
N: Idx,
{
fn visit_after_primary_statement_effect<'mir>(
&mut self,
_analysis: &mut A,
state: &A::Domain,
_statement: &'mir mir::Statement<'tcx>,
location: Location,
) {
let point = self.elements.point_from_location(location);
// Use internal iterator manually as it is much more efficient.
state.iter().for_each(|node| {
self.values.insert(node, point);
});
}

fn visit_after_primary_terminator_effect<'mir>(
&mut self,
_analysis: &mut A,
state: &A::Domain,
_terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) {
let point = self.elements.point_from_location(location);
// Use internal iterator manually as it is much more efficient.
state.iter().for_each(|node| {
self.values.insert(node, point);
});
}
}
Loading
Loading