Skip to content
Open
54 changes: 54 additions & 0 deletions compiler/rustc_index/src/interval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,30 @@ impl<I: Idx> IntervalSet<I> {
result
}

/// 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() {
assert!(*last_end <= point);
if point == *last_end {
// The point is already in the set.
} else if point == *last_end + 1 {
*last_end = point;
} else {
self.map.push((point, 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 +200,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 +375,10 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> {
self.ensure_row(row).insert(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