Skip to content

Commit 4e9dd1b

Browse files
committed
Do not use prepend to avoid quadratic behaviour.
1 parent de7c633 commit 4e9dd1b

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

@@ -502,9 +502,7 @@ fn dest_prop_mir_dump<'tcx>(
502502
live: &SparseIntervalMatrix<RelevantLocal, TwoStepIndex>,
503503
relevant: &RelevantLocals,
504504
) {
505-
let locals_live_at = |location, effect| {
506-
let location = points.point_from_location(location);
507-
let location = TwoStepIndex::new(location, effect);
505+
let locals_live_at = |location| {
508506
live.rows()
509507
.filter(|&r| live.contains(r, location))
510508
.map(|rl| relevant.original[rl])
@@ -514,10 +512,14 @@ fn dest_prop_mir_dump<'tcx>(
514512
if let Some(dumper) = MirDumper::new(tcx, "DestinationPropagation-dataflow", body) {
515513
let extra_data = &|pass_where, w: &mut dyn std::io::Write| {
516514
if let PassWhere::BeforeLocation(loc) = pass_where {
517-
writeln!(w, " // before: {:?}", locals_live_at(loc, Effect::Before))?;
515+
let location = TwoStepIndex::new(points, loc, Effect::Before);
516+
let live = locals_live_at(location);
517+
writeln!(w, " // before: {:?} => {:?}", location, live)?;
518518
}
519519
if let PassWhere::AfterLocation(loc) = pass_where {
520-
writeln!(w, " // after: {:?}", locals_live_at(loc, Effect::After))?;
520+
let location = TwoStepIndex::new(points, loc, Effect::After);
521+
let live = locals_live_at(location);
522+
writeln!(w, " // after: {:?} => {:?}", location, live)?;
521523
}
522524
Ok(())
523525
};
@@ -533,19 +535,25 @@ enum Effect {
533535
}
534536

535537
rustc_index::newtype_index! {
536-
/// A `PointIndex` but with the lower bit encoding early/late inside the statement.
538+
/// A reversed `PointIndex` but with the lower bit encoding early/late inside the statement.
539+
/// The reversed order allows to use the more efficient `IntervalSet::append` method while we
540+
/// iterate on the statements in reverse order.
537541
#[orderable]
538542
#[debug_format = "TwoStepIndex({})"]
539543
struct TwoStepIndex {}
540544
}
541545

542546
impl TwoStepIndex {
543-
fn new(point: PointIndex, effect: Effect) -> TwoStepIndex {
547+
fn new(elements: &DenseLocationMap, location: Location, effect: Effect) -> TwoStepIndex {
548+
let point = elements.point_from_location(location);
544549
let effect = match effect {
545550
Effect::Before => 0,
546551
Effect::After => 1,
547552
};
548-
TwoStepIndex::from_u32(2 * point.as_u32() + (effect as u32))
553+
let max_index = 2 * elements.num_points() as u32 - 1;
554+
let index = 2 * point.as_u32() + (effect as u32);
555+
// Reverse the indexing to use more efficient `IntervalSet::append`.
556+
TwoStepIndex::from_u32(max_index - index)
549557
}
550558
}
551559

@@ -576,21 +584,18 @@ fn save_as_intervals<'tcx>(
576584
let mut state = MaybeLiveLocals.bottom_value(body);
577585
let reachable_blocks = traversal::reachable_as_bitset(body);
578586

579-
let two_step_loc = |location, effect| {
580-
let point = elements.point_from_location(location);
581-
TwoStepIndex::new(point, effect)
582-
};
583-
let prepend_at =
587+
let two_step_loc = |location, effect| TwoStepIndex::new(elements, location, effect);
588+
let append_at =
584589
|values: &mut SparseIntervalMatrix<_, _>, state: &DenseBitSet<Local>, twostep| {
585590
for (relevant, &original) in relevant.original.iter_enumerated() {
586591
if state.contains(original) {
587-
values.prepend(relevant, twostep);
592+
values.append(relevant, twostep);
588593
}
589594
}
590595
};
591596

592597
// Iterate blocks in decreasing order, to visit locations in decreasing order. This
593-
// allows to use the more efficient `prepend` method to interval sets.
598+
// allows to use the more efficient `append` method to interval sets.
594599
for block in body.basic_blocks.indices().rev() {
595600
if !reachable_blocks.contains(block) {
596601
continue;
@@ -603,7 +608,7 @@ fn save_as_intervals<'tcx>(
603608

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

621-
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
626+
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
622627
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
623628
MaybeLiveLocals.apply_early_terminator_effect(&mut state, term, loc);
624629
MaybeLiveLocals.apply_primary_terminator_effect(&mut state, term, loc);
625-
prepend_at(&mut values, &state, twostep);
630+
append_at(&mut values, &state, twostep);
626631

627632
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
628633
let loc = Location { block, statement_index };
629-
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
634+
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
630635
debug_assert_eq!(twostep, two_step_loc(loc, Effect::After));
631-
prepend_at(&mut values, &state, twostep);
636+
append_at(&mut values, &state, twostep);
632637
// Ensure we have a non-zero live range even for dead stores. This is done by marking
633638
// all the written-to locals as live in the second half of the statement.
634639
VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) {
@@ -641,13 +646,13 @@ fn save_as_intervals<'tcx>(
641646
})
642647
.visit_statement(stmt, loc);
643648

644-
twostep = TwoStepIndex::from_u32(twostep.as_u32() - 1);
649+
twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1);
645650
debug_assert_eq!(twostep, two_step_loc(loc, Effect::Before));
646651
MaybeLiveLocals.apply_early_statement_effect(&mut state, stmt, loc);
647652
MaybeLiveLocals.apply_primary_statement_effect(&mut state, stmt, loc);
648653
// ... but reads from operands are marked as live here so they do not conflict with
649654
// the all the writes we manually marked as live in the second half of the statement.
650-
prepend_at(&mut values, &state, twostep);
655+
append_at(&mut values, &state, twostep);
651656
}
652657
}
653658

0 commit comments

Comments
 (0)