Skip to content

Commit d3f39d7

Browse files
committed
Do not recompute liveness for DestinationPropagation.
1 parent 5f389e7 commit d3f39d7

File tree

2 files changed

+90
-44
lines changed

2 files changed

+90
-44
lines changed

compiler/rustc_mir_dataflow/src/points.rs

+64-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use crate::framework::{visit_results, ResultsVisitable, ResultsVisitor};
2+
use rustc_index::bit_set::ChunkedBitSet;
13
use rustc_index::interval::IntervalSet;
24
use rustc_index::interval::SparseIntervalMatrix;
35
use rustc_index::Idx;
46
use rustc_index::IndexVec;
5-
use rustc_middle::mir::{BasicBlock, Body, Location};
7+
use rustc_middle::mir::{self, BasicBlock, Body, Location};
68
use std::rc::Rc;
79

810
/// Maps between a `Location` and a `PointIndex` (and vice versa).
@@ -161,4 +163,65 @@ impl<N: Idx> LivenessValues<N> {
161163
pub fn get_intervals(&self, region: N) -> Option<&IntervalSet<PointIndex>> {
162164
self.points.row(region)
163165
}
166+
167+
/// Insert all the elements from the `src` region into the `dest` region.
168+
pub fn union_regions(&mut self, src: N, dest: N) {
169+
self.points.union_rows(src, dest);
170+
}
171+
172+
/// Add points depending on the result of the given dataflow analysis.
173+
pub fn fill_from_dataflow<'tcx, R>(body: &mir::Body<'tcx>, mut results: R) -> Self
174+
where
175+
R: ResultsVisitable<'tcx, FlowState = ChunkedBitSet<N>>,
176+
{
177+
let elements = Rc::new(DenseLocationMap::new(body));
178+
let values = Self::new(elements);
179+
let mut visitor = Visitor { values };
180+
visit_results(
181+
body,
182+
body.basic_blocks.reverse_postorder().iter().copied(),
183+
&mut results,
184+
&mut visitor,
185+
);
186+
visitor.values
187+
}
188+
}
189+
190+
struct Visitor<N: Idx> {
191+
values: LivenessValues<N>,
192+
}
193+
194+
impl<'mir, 'tcx, R, N> ResultsVisitor<'mir, 'tcx, R> for Visitor<N>
195+
where
196+
N: Idx,
197+
{
198+
type FlowState = ChunkedBitSet<N>;
199+
200+
fn visit_statement_after_primary_effect(
201+
&mut self,
202+
_results: &mut R,
203+
state: &Self::FlowState,
204+
_statement: &'mir mir::Statement<'tcx>,
205+
location: Location,
206+
) {
207+
let point = self.values.elements.point_from_location(location);
208+
// Use internal iterator manually as it is much more efficient.
209+
state.iter().fold((), |(), node| {
210+
self.values.points.insert(node, point);
211+
});
212+
}
213+
214+
fn visit_terminator_after_primary_effect(
215+
&mut self,
216+
_results: &mut R,
217+
state: &Self::FlowState,
218+
_terminator: &'mir mir::Terminator<'tcx>,
219+
location: Location,
220+
) {
221+
let point = self.values.elements.point_from_location(location);
222+
// Use internal iterator manually as it is much more efficient.
223+
state.iter().fold((), |(), node| {
224+
self.values.points.insert(node, point);
225+
});
226+
}
164227
}

compiler/rustc_mir_transform/src/dest_prop.rs

+26-43
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ use rustc_middle::mir::{
146146
};
147147
use rustc_middle::ty::TyCtxt;
148148
use rustc_mir_dataflow::impls::MaybeLiveLocals;
149-
use rustc_mir_dataflow::{Analysis, ResultsCursor};
149+
use rustc_mir_dataflow::points::LivenessValues;
150+
use rustc_mir_dataflow::Analysis;
150151

151152
pub struct DestinationPropagation;
152153

@@ -170,6 +171,12 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
170171

171172
let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body);
172173

174+
let live = MaybeLiveLocals
175+
.into_engine(tcx, body)
176+
.pass_name("MaybeLiveLocals-DestinationPropagation")
177+
.iterate_to_fixpoint();
178+
let mut live = LivenessValues::fill_from_dataflow(body, live);
179+
173180
// In order to avoid having to collect data for every single pair of locals in the body, we
174181
// do not allow doing more than one merge for places that are derived from the same local at
175182
// once. To avoid missed opportunities, we instead iterate to a fixed point - we'll refer to
@@ -193,11 +200,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
193200
&mut allocations.candidates_reverse,
194201
);
195202
trace!(?candidates);
196-
let mut live = MaybeLiveLocals
197-
.into_engine(tcx, body)
198-
.iterate_to_fixpoint()
199-
.into_results_cursor(body);
200-
dest_prop_mir_dump(tcx, body, &mut live, round_count);
203+
dest_prop_mir_dump(tcx, body, &live, round_count);
201204

202205
FilterInformation::filter_liveness(
203206
&mut candidates,
@@ -206,9 +209,9 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
206209
body,
207210
);
208211

209-
// Because we do not update liveness information, it is unsound to use a local for more
210-
// than one merge operation within a single round of optimizations. We store here which
211-
// ones we have already used.
212+
// Because we only filter once per round, it is unsound to use a local for more than
213+
// one merge operation within a single round of optimizations. We store here which ones
214+
// we have already used.
212215
let mut merged_locals: BitSet<Local> = BitSet::new_empty(body.local_decls.len());
213216

214217
// This is the set of merges we will apply this round. It is a subset of the candidates.
@@ -227,9 +230,15 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
227230
}) {
228231
break;
229232
}
233+
234+
// Replace `src` by `dest` everywhere.
230235
merges.insert(*src, *dest);
231236
merged_locals.insert(*src);
232237
merged_locals.insert(*dest);
238+
239+
// Update liveness information based on the merge we just performed.
240+
// Every location where `src` was live, `dest` will be live.
241+
live.union_regions(*src, *dest);
233242
}
234243
trace!(merging = ?merges);
235244

@@ -358,7 +367,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
358367

359368
struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
360369
body: &'body Body<'tcx>,
361-
live: &'a mut ResultsCursor<'body, 'tcx, MaybeLiveLocals>,
370+
live: &'a LivenessValues<Local>,
362371
candidates: &'a mut Candidates<'alloc>,
363372
write_info: &'alloc mut WriteInfo,
364373
at: Location,
@@ -461,7 +470,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
461470
/// locals as also being read from.
462471
fn filter_liveness<'b>(
463472
candidates: &mut Candidates<'alloc>,
464-
live: &mut ResultsCursor<'b, 'tcx, MaybeLiveLocals>,
473+
live: &LivenessValues<Local>,
465474
write_info_alloc: &'alloc mut WriteInfo,
466475
body: &'b Body<'tcx>,
467476
) {
@@ -481,13 +490,11 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
481490
fn internal_filter_liveness(&mut self) {
482491
for (block, data) in traversal::preorder(self.body) {
483492
self.at = Location { block, statement_index: data.statements.len() };
484-
self.live.seek_after_primary_effect(self.at);
485493
self.write_info.for_terminator(&data.terminator().kind);
486494
self.apply_conflicts();
487495

488496
for (i, statement) in data.statements.iter().enumerate().rev() {
489497
self.at = Location { block, statement_index: i };
490-
self.live.seek_after_primary_effect(self.at);
491498
self.write_info.for_statement(&statement.kind, self.body);
492499
self.apply_conflicts();
493500
}
@@ -517,7 +524,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
517524
// calls or inline asm. Because of this, we also mark locals as
518525
// conflicting when both of them are written to in the same
519526
// statement.
520-
if self.live.contains(q) || writes.contains(&q) {
527+
if self.live.contains(q, self.at) || writes.contains(&q) {
521528
CandidateFilter::Remove
522529
} else {
523530
CandidateFilter::Keep
@@ -810,38 +817,14 @@ fn is_local_required(local: Local, body: &Body<'_>) -> bool {
810817
fn dest_prop_mir_dump<'body, 'tcx>(
811818
tcx: TyCtxt<'tcx>,
812819
body: &'body Body<'tcx>,
813-
live: &mut ResultsCursor<'body, 'tcx, MaybeLiveLocals>,
820+
live: &LivenessValues<Local>,
814821
round: usize,
815822
) {
816-
let mut reachable = None;
823+
let locals_live_at =
824+
|location| live.rows().filter(|&r| live.contains(r, location)).collect::<Vec<_>>();
817825
dump_mir(tcx, false, "DestinationPropagation-dataflow", &round, body, |pass_where, w| {
818-
let reachable = reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body));
819-
820-
match pass_where {
821-
PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => {
822-
live.seek_after_primary_effect(loc);
823-
writeln!(w, " // live: {:?}", live.get())?;
824-
}
825-
PassWhere::AfterTerminator(bb) if reachable.contains(bb) => {
826-
let loc = body.terminator_loc(bb);
827-
live.seek_before_primary_effect(loc);
828-
writeln!(w, " // live: {:?}", live.get())?;
829-
}
830-
831-
PassWhere::BeforeBlock(bb) if reachable.contains(bb) => {
832-
live.seek_to_block_start(bb);
833-
writeln!(w, " // live: {:?}", live.get())?;
834-
}
835-
836-
PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {}
837-
838-
PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => {
839-
writeln!(w, " // live: <unreachable>")?;
840-
}
841-
842-
PassWhere::BeforeBlock(_) => {
843-
writeln!(w, " // live: <unreachable>")?;
844-
}
826+
if let PassWhere::BeforeLocation(loc) = pass_where {
827+
writeln!(w, " // live: {:?}", locals_live_at(loc))?;
845828
}
846829

847830
Ok(())

0 commit comments

Comments
 (0)