Skip to content

Commit 808ce08

Browse files
committed
Do not use prepend to avoid quadratic behaviour.
1 parent 4997476 commit 808ce08

File tree

2 files changed

+37
-58
lines changed

2 files changed

+37
-58
lines changed

compiler/rustc_index/src/interval.rs

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -140,42 +140,20 @@ 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 == *first_start {
151-
// The point is already present in the set.
152-
} else if point + 1 == *first_start {
153-
// Just extend the first range.
154-
*first_start = point;
155-
} else {
156-
self.map.insert(0, (point, point));
157-
}
158-
} else {
159-
// If the map is empty, push is faster than insert.
160-
self.map.push((point, point));
161-
}
162-
163-
debug_assert!(
164-
self.check_invariants(),
165-
"wrong intervals after prepend {point:?} to {self:?}"
166-
);
167-
}
168-
169143
/// Specialized version of `insert` when we know that the inserted point is *after* any
170144
/// contained.
171145
pub fn append(&mut self, point: I) {
172146
let point = point.index() as u32;
173147

174-
if let Some((_, last_end)) = self.map.last_mut()
175-
&& let _ = assert!(*last_end < point)
176-
&& point == *last_end + 1
177-
{
178-
*last_end = point;
148+
if let Some((_, last_end)) = self.map.last_mut() {
149+
assert!(*last_end <= point);
150+
if point == *last_end {
151+
// The point is already in the set.
152+
} else if point == *last_end + 1 {
153+
*last_end = point;
154+
} else {
155+
self.map.push((point, point));
156+
}
179157
} else {
180158
self.map.push((point, point));
181159
}
@@ -397,10 +375,6 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> {
397375
self.ensure_row(row).insert(point)
398376
}
399377

400-
pub fn prepend(&mut self, row: R, point: C) {
401-
self.ensure_row(row).prepend(point)
402-
}
403-
404378
pub fn append(&mut self, row: R, point: C) {
405379
self.ensure_row(row).append(point)
406380
}

compiler/rustc_mir_transform/src/dest_prop.rs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
145145
use rustc_middle::mir::*;
146146
use rustc_middle::ty::TyCtxt;
147147
use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals};
148-
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
148+
use rustc_mir_dataflow::points::DenseLocationMap;
149149
use rustc_mir_dataflow::{Analysis, Results};
150150
use tracing::{debug, trace};
151151

@@ -494,20 +494,22 @@ fn dest_prop_mir_dump<'tcx>(
494494
live: &SparseIntervalMatrix<RelevantLocal, TwoStepIndex>,
495495
relevant: &RelevantLocals,
496496
) {
497-
let locals_live_at = |location, effect| {
498-
let location = points.point_from_location(location);
499-
let location = TwoStepIndex::new(location, effect);
497+
let locals_live_at = |location| {
500498
live.rows()
501499
.filter(|&r| live.contains(r, location))
502500
.map(|rl| relevant.original[rl])
503501
.collect::<Vec<_>>()
504502
};
505503
dump_mir(tcx, false, "DestinationPropagation-dataflow", &0, body, |pass_where, w| {
506504
if let PassWhere::BeforeLocation(loc) = pass_where {
507-
writeln!(w, " // before: {:?}", locals_live_at(loc, Effect::Before))?;
505+
let location = TwoStepIndex::new(points, loc, Effect::Before);
506+
let live = locals_live_at(location);
507+
writeln!(w, " // before: {:?} => {:?}", location, live)?;
508508
}
509509
if let PassWhere::AfterLocation(loc) = pass_where {
510-
writeln!(w, " // after: {:?}", locals_live_at(loc, Effect::After))?;
510+
let location = TwoStepIndex::new(points, loc, Effect::After);
511+
let live = locals_live_at(location);
512+
writeln!(w, " // after: {:?} => {:?}", location, live)?;
511513
}
512514

513515
Ok(())
@@ -521,19 +523,25 @@ enum Effect {
521523
}
522524

523525
rustc_index::newtype_index! {
524-
/// A `PointIndex` but with the lower bit encoding early/late inside the statement.
526+
/// A reversed `PointIndex` but with the lower bit encoding early/late inside the statement.
527+
/// The reversed order allows to use the more efficient `IntervalSet::append` method while we
528+
/// iterate on the statements in reverse order.
525529
#[orderable]
526530
#[debug_format = "TwoStepIndex({})"]
527531
struct TwoStepIndex {}
528532
}
529533

530534
impl TwoStepIndex {
531-
fn new(point: PointIndex, effect: Effect) -> TwoStepIndex {
535+
fn new(elements: &DenseLocationMap, location: Location, effect: Effect) -> TwoStepIndex {
536+
let point = elements.point_from_location(location);
532537
let effect = match effect {
533538
Effect::Before => 0,
534539
Effect::After => 1,
535540
};
536-
TwoStepIndex::from_u32(2 * point.as_u32() + (effect as u32))
541+
let max_index = 2 * elements.num_points() as u32 - 1;
542+
let index = 2 * point.as_u32() + (effect as u32);
543+
// Reverse the indexing to use more efficient `IntervalSet::append`.
544+
TwoStepIndex::from_u32(max_index - index)
537545
}
538546
}
539547

@@ -564,21 +572,18 @@ fn save_as_intervals<'tcx>(
564572
let mut state = MaybeLiveLocals.bottom_value(body);
565573
let reachable_blocks = traversal::reachable_as_bitset(body);
566574

567-
let two_step_loc = |location, effect| {
568-
let point = elements.point_from_location(location);
569-
TwoStepIndex::new(point, effect)
570-
};
571-
let prepend_at =
575+
let two_step_loc = |location, effect| TwoStepIndex::new(elements, location, effect);
576+
let append_at =
572577
|values: &mut SparseIntervalMatrix<_, _>, state: &DenseBitSet<Local>, twostep| {
573578
for (relevant, &original) in relevant.original.iter_enumerated() {
574579
if state.contains(original) {
575-
values.prepend(relevant, twostep);
580+
values.append(relevant, twostep);
576581
}
577582
}
578583
};
579584

580585
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
581-
// allows to use the more efficient `prepend` method to interval sets.
586+
// allows to use the more efficient `append` method to interval sets.
582587
for block in body.basic_blocks.indices().rev() {
583588
if !reachable_blocks.contains(block) {
584589
continue;
@@ -591,7 +596,7 @@ fn save_as_intervals<'tcx>(
591596

592597
let term = block_data.terminator();
593598
let mut twostep = two_step_loc(loc, Effect::After);
594-
prepend_at(&mut values, &state, twostep);
599+
append_at(&mut values, &state, twostep);
595600
// Ensure we have a non-zero live range even for dead stores. This is done by marking all
596601
// the written-to locals as live in the second half of the statement.
597602
// We also ensure that operands read by terminators conflict with writes by that terminator.
@@ -606,17 +611,17 @@ fn save_as_intervals<'tcx>(
606611
})
607612
.visit_terminator(term, loc);
608613

609-
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
614+
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
610615
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
611616
MaybeLiveLocals.apply_early_terminator_effect(&mut state, term, loc);
612617
MaybeLiveLocals.apply_primary_terminator_effect(&mut state, term, loc);
613-
prepend_at(&mut values, &state, twostep);
618+
append_at(&mut values, &state, twostep);
614619

615620
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
616621
let loc = Location { block, statement_index };
617-
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
622+
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
618623
debug_assert_eq!(twostep, two_step_loc(loc, Effect::After));
619-
prepend_at(&mut values, &state, twostep);
624+
append_at(&mut values, &state, twostep);
620625
// Ensure we have a non-zero live range even for dead stores. This is done by marking
621626
// all the written-to locals as live in the second half of the statement.
622627
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
@@ -629,13 +634,13 @@ fn save_as_intervals<'tcx>(
629634
})
630635
.visit_statement(stmt, loc);
631636

632-
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
637+
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
633638
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
634639
MaybeLiveLocals.apply_early_statement_effect(&mut state, stmt, loc);
635640
MaybeLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc);
636641
// ... but reads from operands are marked as live here so they do not conflict with
637642
// the all the writes we manually marked as live in the second half of the statement.
638-
prepend_at(&mut values, &state, twostep);
643+
append_at(&mut values, &state, twostep);
639644
}
640645
}
641646

0 commit comments

Comments
 (0)