Do not use prepend to avoid quadratic behaviour.
This commit is contained in:
@@ -140,42 +140,20 @@ impl<I: Idx> IntervalSet<I> {
|
|||||||
result
|
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
|
/// Specialized version of `insert` when we know that the inserted point is *after* any
|
||||||
/// contained.
|
/// contained.
|
||||||
pub fn append(&mut self, point: I) {
|
pub fn append(&mut self, point: I) {
|
||||||
let point = point.index() as u32;
|
let point = point.index() as u32;
|
||||||
|
|
||||||
if let Some((_, last_end)) = self.map.last_mut()
|
if let Some((_, last_end)) = self.map.last_mut() {
|
||||||
&& let _ = assert!(*last_end < point)
|
assert!(*last_end <= point);
|
||||||
&& point == *last_end + 1
|
if point == *last_end {
|
||||||
{
|
// The point is already in the set.
|
||||||
*last_end = point;
|
} else if point == *last_end + 1 {
|
||||||
|
*last_end = point;
|
||||||
|
} else {
|
||||||
|
self.map.push((point, point));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.map.push((point, point));
|
self.map.push((point, point));
|
||||||
}
|
}
|
||||||
@@ -397,10 +375,6 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> {
|
|||||||
self.ensure_row(row).insert(point)
|
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) {
|
pub fn append(&mut self, row: R, point: C) {
|
||||||
self.ensure_row(row).append(point)
|
self.ensure_row(row).append(point)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
|
|||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals};
|
use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals};
|
||||||
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
|
use rustc_mir_dataflow::points::DenseLocationMap;
|
||||||
use rustc_mir_dataflow::{Analysis, Results};
|
use rustc_mir_dataflow::{Analysis, Results};
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
@@ -502,9 +502,7 @@ fn dest_prop_mir_dump<'tcx>(
|
|||||||
live: &SparseIntervalMatrix<RelevantLocal, TwoStepIndex>,
|
live: &SparseIntervalMatrix<RelevantLocal, TwoStepIndex>,
|
||||||
relevant: &RelevantLocals,
|
relevant: &RelevantLocals,
|
||||||
) {
|
) {
|
||||||
let locals_live_at = |location, effect| {
|
let locals_live_at = |location| {
|
||||||
let location = points.point_from_location(location);
|
|
||||||
let location = TwoStepIndex::new(location, effect);
|
|
||||||
live.rows()
|
live.rows()
|
||||||
.filter(|&r| live.contains(r, location))
|
.filter(|&r| live.contains(r, location))
|
||||||
.map(|rl| relevant.original[rl])
|
.map(|rl| relevant.original[rl])
|
||||||
@@ -514,10 +512,14 @@ fn dest_prop_mir_dump<'tcx>(
|
|||||||
if let Some(dumper) = MirDumper::new(tcx, "DestinationPropagation-dataflow", body) {
|
if let Some(dumper) = MirDumper::new(tcx, "DestinationPropagation-dataflow", body) {
|
||||||
let extra_data = &|pass_where, w: &mut dyn std::io::Write| {
|
let extra_data = &|pass_where, w: &mut dyn std::io::Write| {
|
||||||
if let PassWhere::BeforeLocation(loc) = pass_where {
|
if let PassWhere::BeforeLocation(loc) = pass_where {
|
||||||
writeln!(w, " // before: {:?}", locals_live_at(loc, Effect::Before))?;
|
let location = TwoStepIndex::new(points, loc, Effect::Before);
|
||||||
|
let live = locals_live_at(location);
|
||||||
|
writeln!(w, " // before: {:?} => {:?}", location, live)?;
|
||||||
}
|
}
|
||||||
if let PassWhere::AfterLocation(loc) = pass_where {
|
if let PassWhere::AfterLocation(loc) = pass_where {
|
||||||
writeln!(w, " // after: {:?}", locals_live_at(loc, Effect::After))?;
|
let location = TwoStepIndex::new(points, loc, Effect::After);
|
||||||
|
let live = locals_live_at(location);
|
||||||
|
writeln!(w, " // after: {:?} => {:?}", location, live)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
@@ -533,19 +535,25 @@ enum Effect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rustc_index::newtype_index! {
|
rustc_index::newtype_index! {
|
||||||
/// A `PointIndex` but with the lower bit encoding early/late inside the statement.
|
/// A reversed `PointIndex` but with the lower bit encoding early/late inside the statement.
|
||||||
|
/// The reversed order allows to use the more efficient `IntervalSet::append` method while we
|
||||||
|
/// iterate on the statements in reverse order.
|
||||||
#[orderable]
|
#[orderable]
|
||||||
#[debug_format = "TwoStepIndex({})"]
|
#[debug_format = "TwoStepIndex({})"]
|
||||||
struct TwoStepIndex {}
|
struct TwoStepIndex {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TwoStepIndex {
|
impl TwoStepIndex {
|
||||||
fn new(point: PointIndex, effect: Effect) -> TwoStepIndex {
|
fn new(elements: &DenseLocationMap, location: Location, effect: Effect) -> TwoStepIndex {
|
||||||
|
let point = elements.point_from_location(location);
|
||||||
let effect = match effect {
|
let effect = match effect {
|
||||||
Effect::Before => 0,
|
Effect::Before => 0,
|
||||||
Effect::After => 1,
|
Effect::After => 1,
|
||||||
};
|
};
|
||||||
TwoStepIndex::from_u32(2 * point.as_u32() + (effect as u32))
|
let max_index = 2 * elements.num_points() as u32 - 1;
|
||||||
|
let index = 2 * point.as_u32() + (effect as u32);
|
||||||
|
// Reverse the indexing to use more efficient `IntervalSet::append`.
|
||||||
|
TwoStepIndex::from_u32(max_index - index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -576,21 +584,18 @@ fn save_as_intervals<'tcx>(
|
|||||||
let mut state = MaybeLiveLocals.bottom_value(body);
|
let mut state = MaybeLiveLocals.bottom_value(body);
|
||||||
let reachable_blocks = traversal::reachable_as_bitset(body);
|
let reachable_blocks = traversal::reachable_as_bitset(body);
|
||||||
|
|
||||||
let two_step_loc = |location, effect| {
|
let two_step_loc = |location, effect| TwoStepIndex::new(elements, location, effect);
|
||||||
let point = elements.point_from_location(location);
|
let append_at =
|
||||||
TwoStepIndex::new(point, effect)
|
|
||||||
};
|
|
||||||
let prepend_at =
|
|
||||||
|values: &mut SparseIntervalMatrix<_, _>, state: &DenseBitSet<Local>, twostep| {
|
|values: &mut SparseIntervalMatrix<_, _>, state: &DenseBitSet<Local>, twostep| {
|
||||||
for (relevant, &original) in relevant.original.iter_enumerated() {
|
for (relevant, &original) in relevant.original.iter_enumerated() {
|
||||||
if state.contains(original) {
|
if state.contains(original) {
|
||||||
values.prepend(relevant, twostep);
|
values.append(relevant, twostep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
|
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
|
||||||
// allows to use the more efficient `prepend` method to interval sets.
|
// allows to use the more efficient `append` method to interval sets.
|
||||||
for block in body.basic_blocks.indices().rev() {
|
for block in body.basic_blocks.indices().rev() {
|
||||||
if !reachable_blocks.contains(block) {
|
if !reachable_blocks.contains(block) {
|
||||||
continue;
|
continue;
|
||||||
@@ -603,7 +608,7 @@ fn save_as_intervals<'tcx>(
|
|||||||
|
|
||||||
let term = block_data.terminator();
|
let term = block_data.terminator();
|
||||||
let mut twostep = two_step_loc(loc, Effect::After);
|
let mut twostep = two_step_loc(loc, Effect::After);
|
||||||
prepend_at(&mut values, &state, twostep);
|
append_at(&mut values, &state, twostep);
|
||||||
// Ensure we have a non-zero live range even for dead stores. This is done by marking all
|
// Ensure we have a non-zero live range even for dead stores. This is done by marking all
|
||||||
// the written-to locals as live in the second half of the statement.
|
// the written-to locals as live in the second half of the statement.
|
||||||
// We also ensure that operands read by terminators conflict with writes by that terminator.
|
// We also ensure that operands read by terminators conflict with writes by that terminator.
|
||||||
@@ -618,17 +623,17 @@ fn save_as_intervals<'tcx>(
|
|||||||
})
|
})
|
||||||
.visit_terminator(term, loc);
|
.visit_terminator(term, loc);
|
||||||
|
|
||||||
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
|
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
|
||||||
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
|
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
|
||||||
MaybeLiveLocals.apply_early_terminator_effect(&mut state, term, loc);
|
MaybeLiveLocals.apply_early_terminator_effect(&mut state, term, loc);
|
||||||
MaybeLiveLocals.apply_primary_terminator_effect(&mut state, term, loc);
|
MaybeLiveLocals.apply_primary_terminator_effect(&mut state, term, loc);
|
||||||
prepend_at(&mut values, &state, twostep);
|
append_at(&mut values, &state, twostep);
|
||||||
|
|
||||||
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
|
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
|
||||||
let loc = Location { block, statement_index };
|
let loc = Location { block, statement_index };
|
||||||
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
|
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
|
||||||
debug_assert_eq!(twostep, two_step_loc(loc, Effect::After));
|
debug_assert_eq!(twostep, two_step_loc(loc, Effect::After));
|
||||||
prepend_at(&mut values, &state, twostep);
|
append_at(&mut values, &state, twostep);
|
||||||
// Ensure we have a non-zero live range even for dead stores. This is done by marking
|
// Ensure we have a non-zero live range even for dead stores. This is done by marking
|
||||||
// all the written-to locals as live in the second half of the statement.
|
// all the written-to locals as live in the second half of the statement.
|
||||||
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
|
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
|
||||||
@@ -641,13 +646,13 @@ fn save_as_intervals<'tcx>(
|
|||||||
})
|
})
|
||||||
.visit_statement(stmt, loc);
|
.visit_statement(stmt, loc);
|
||||||
|
|
||||||
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
|
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
|
||||||
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
|
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
|
||||||
MaybeLiveLocals.apply_early_statement_effect(&mut state, stmt, loc);
|
MaybeLiveLocals.apply_early_statement_effect(&mut state, stmt, loc);
|
||||||
MaybeLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc);
|
MaybeLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc);
|
||||||
// ... but reads from operands are marked as live here so they do not conflict with
|
// ... but reads from operands are marked as live here so they do not conflict with
|
||||||
// the all the writes we manually marked as live in the second half of the statement.
|
// the all the writes we manually marked as live in the second half of the statement.
|
||||||
prepend_at(&mut values, &state, twostep);
|
append_at(&mut values, &state, twostep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user