Skip to content

Commit da235ce

Browse files
committed
Do not recompute liveness for DestinationPropagation.
1 parent c4b1054 commit da235ce

File tree

2 files changed

+102
-45
lines changed

2 files changed

+102
-45
lines changed

compiler/rustc_mir_dataflow/src/points.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use crate::framework::{visit_results, ResultsVisitable, ResultsVisitor};
2+
use rustc_index::bit_set::ChunkedBitSet;
3+
use rustc_index::interval::SparseIntervalMatrix;
14
use rustc_index::Idx;
25
use rustc_index::IndexVec;
3-
use rustc_middle::mir::{BasicBlock, Body, Location};
6+
use rustc_middle::mir::{self, BasicBlock, Body, Location};
47

58
/// Maps between a `Location` and a `PointIndex` (and vice versa).
69
pub struct DenseLocationMap {
@@ -92,3 +95,64 @@ rustc_index::newtype_index! {
9295
#[debug_format = "PointIndex({})"]
9396
pub struct PointIndex {}
9497
}
98+
99+
/// Add points depending on the result of the given dataflow analysis.
100+
pub fn save_as_intervals<'tcx, N, R>(
101+
elements: &DenseLocationMap,
102+
body: &mir::Body<'tcx>,
103+
mut results: R,
104+
) -> SparseIntervalMatrix<N, PointIndex>
105+
where
106+
N: Idx,
107+
R: ResultsVisitable<'tcx, FlowState = ChunkedBitSet<N>>,
108+
{
109+
let values = SparseIntervalMatrix::new(elements.num_points());
110+
let mut visitor = Visitor { elements, values };
111+
visit_results(
112+
body,
113+
body.basic_blocks.reverse_postorder().iter().copied(),
114+
&mut results,
115+
&mut visitor,
116+
);
117+
visitor.values
118+
}
119+
120+
struct Visitor<'a, N: Idx> {
121+
elements: &'a DenseLocationMap,
122+
values: SparseIntervalMatrix<N, PointIndex>,
123+
}
124+
125+
impl<'mir, 'tcx, R, N> ResultsVisitor<'mir, 'tcx, R> for Visitor<'_, N>
126+
where
127+
N: Idx,
128+
{
129+
type FlowState = ChunkedBitSet<N>;
130+
131+
fn visit_statement_after_primary_effect(
132+
&mut self,
133+
_results: &mut R,
134+
state: &Self::FlowState,
135+
_statement: &'mir mir::Statement<'tcx>,
136+
location: Location,
137+
) {
138+
let point = self.elements.point_from_location(location);
139+
// Use internal iterator manually as it is much more efficient.
140+
state.iter().fold((), |(), node| {
141+
self.values.insert(node, point);
142+
});
143+
}
144+
145+
fn visit_terminator_after_primary_effect(
146+
&mut self,
147+
_results: &mut R,
148+
state: &Self::FlowState,
149+
_terminator: &'mir mir::Terminator<'tcx>,
150+
location: Location,
151+
) {
152+
let point = self.elements.point_from_location(location);
153+
// Use internal iterator manually as it is much more efficient.
154+
state.iter().fold((), |(), node| {
155+
self.values.insert(node, point);
156+
});
157+
}
158+
}

compiler/rustc_mir_transform/src/dest_prop.rs

Lines changed: 37 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
use crate::MirPass;
135135
use rustc_data_structures::fx::{FxIndexMap, IndexEntry, IndexOccupiedEntry};
136136
use rustc_index::bit_set::BitSet;
137+
use rustc_index::interval::SparseIntervalMatrix;
137138
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
138139
use rustc_middle::mir::HasLocalDecls;
139140
use rustc_middle::mir::{dump_mir, PassWhere};
@@ -143,7 +144,8 @@ use rustc_middle::mir::{
143144
};
144145
use rustc_middle::ty::TyCtxt;
145146
use rustc_mir_dataflow::impls::MaybeLiveLocals;
146-
use rustc_mir_dataflow::{Analysis, ResultsCursor};
147+
use rustc_mir_dataflow::points::{save_as_intervals, DenseLocationMap, PointIndex};
148+
use rustc_mir_dataflow::Analysis;
147149

148150
pub struct DestinationPropagation;
149151

@@ -167,6 +169,13 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
167169

168170
let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body);
169171

172+
let live = MaybeLiveLocals
173+
.into_engine(tcx, body)
174+
.pass_name("MaybeLiveLocals-DestinationPropagation")
175+
.iterate_to_fixpoint();
176+
let points = DenseLocationMap::new(body);
177+
let mut live = save_as_intervals(&points, body, live);
178+
170179
// In order to avoid having to collect data for every single pair of locals in the body, we
171180
// do not allow doing more than one merge for places that are derived from the same local at
172181
// once. To avoid missed opportunities, we instead iterate to a fixed point - we'll refer to
@@ -190,22 +199,19 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
190199
&mut allocations.candidates_reverse,
191200
);
192201
trace!(?candidates);
193-
let mut live = MaybeLiveLocals
194-
.into_engine(tcx, body)
195-
.iterate_to_fixpoint()
196-
.into_results_cursor(body);
197-
dest_prop_mir_dump(tcx, body, &mut live, round_count);
202+
dest_prop_mir_dump(tcx, body, &points, &live, round_count);
198203

199204
FilterInformation::filter_liveness(
200205
&mut candidates,
201-
&mut live,
206+
&points,
207+
&live,
202208
&mut allocations.write_info,
203209
body,
204210
);
205211

206-
// Because we do not update liveness information, it is unsound to use a local for more
207-
// than one merge operation within a single round of optimizations. We store here which
208-
// 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.
209215
let mut merged_locals: BitSet<Local> = BitSet::new_empty(body.local_decls.len());
210216

211217
// This is the set of merges we will apply this round. It is a subset of the candidates.
@@ -224,9 +230,15 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
224230
}) {
225231
break;
226232
}
233+
234+
// Replace `src` by `dest` everywhere.
227235
merges.insert(*src, *dest);
228236
merged_locals.insert(*src);
229237
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_rows(*src, *dest);
230242
}
231243
trace!(merging = ?merges);
232244

@@ -349,7 +361,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
349361

350362
struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
351363
body: &'body Body<'tcx>,
352-
live: &'a mut ResultsCursor<'body, 'tcx, MaybeLiveLocals>,
364+
points: &'a DenseLocationMap,
365+
live: &'a SparseIntervalMatrix<Local, PointIndex>,
353366
candidates: &'a mut Candidates<'alloc>,
354367
write_info: &'alloc mut WriteInfo,
355368
at: Location,
@@ -452,12 +465,14 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
452465
/// locals as also being read from.
453466
fn filter_liveness<'b>(
454467
candidates: &mut Candidates<'alloc>,
455-
live: &mut ResultsCursor<'b, 'tcx, MaybeLiveLocals>,
468+
points: &DenseLocationMap,
469+
live: &SparseIntervalMatrix<Local, PointIndex>,
456470
write_info_alloc: &'alloc mut WriteInfo,
457471
body: &'b Body<'tcx>,
458472
) {
459473
let mut this = FilterInformation {
460474
body,
475+
points,
461476
live,
462477
candidates,
463478
// We don't actually store anything at this scope, we just keep things here to be able
@@ -472,13 +487,11 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
472487
fn internal_filter_liveness(&mut self) {
473488
for (block, data) in traversal::preorder(self.body) {
474489
self.at = Location { block, statement_index: data.statements.len() };
475-
self.live.seek_after_primary_effect(self.at);
476490
self.write_info.for_terminator(&data.terminator().kind);
477491
self.apply_conflicts();
478492

479493
for (i, statement) in data.statements.iter().enumerate().rev() {
480494
self.at = Location { block, statement_index: i };
481-
self.live.seek_after_primary_effect(self.at);
482495
self.write_info.for_statement(&statement.kind, self.body);
483496
self.apply_conflicts();
484497
}
@@ -497,6 +510,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
497510
None
498511
}
499512
});
513+
let at = self.points.point_from_location(self.at);
500514
self.candidates.filter_candidates_by(
501515
*p,
502516
|q| {
@@ -508,7 +522,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
508522
// calls or inline asm. Because of this, we also mark locals as
509523
// conflicting when both of them are written to in the same
510524
// statement.
511-
if self.live.contains(q) || writes.contains(&q) {
525+
if self.live.contains(q, at) || writes.contains(&q) {
512526
CandidateFilter::Remove
513527
} else {
514528
CandidateFilter::Keep
@@ -801,38 +815,17 @@ fn is_local_required(local: Local, body: &Body<'_>) -> bool {
801815
fn dest_prop_mir_dump<'body, 'tcx>(
802816
tcx: TyCtxt<'tcx>,
803817
body: &'body Body<'tcx>,
804-
live: &mut ResultsCursor<'body, 'tcx, MaybeLiveLocals>,
818+
points: &DenseLocationMap,
819+
live: &SparseIntervalMatrix<Local, PointIndex>,
805820
round: usize,
806821
) {
807-
let mut reachable = None;
822+
let locals_live_at = |location| {
823+
let location = points.point_from_location(location);
824+
live.rows().filter(|&r| live.contains(r, location)).collect::<Vec<_>>()
825+
};
808826
dump_mir(tcx, false, "DestinationPropagation-dataflow", &round, body, |pass_where, w| {
809-
let reachable = reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body));
810-
811-
match pass_where {
812-
PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => {
813-
live.seek_after_primary_effect(loc);
814-
writeln!(w, " // live: {:?}", live.get())?;
815-
}
816-
PassWhere::AfterTerminator(bb) if reachable.contains(bb) => {
817-
let loc = body.terminator_loc(bb);
818-
live.seek_before_primary_effect(loc);
819-
writeln!(w, " // live: {:?}", live.get())?;
820-
}
821-
822-
PassWhere::BeforeBlock(bb) if reachable.contains(bb) => {
823-
live.seek_to_block_start(bb);
824-
writeln!(w, " // live: {:?}", live.get())?;
825-
}
826-
827-
PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {}
828-
829-
PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => {
830-
writeln!(w, " // live: <unreachable>")?;
831-
}
832-
833-
PassWhere::BeforeBlock(_) => {
834-
writeln!(w, " // live: <unreachable>")?;
835-
}
827+
if let PassWhere::BeforeLocation(loc) = pass_where {
828+
writeln!(w, " // live: {:?}", locals_live_at(loc))?;
836829
}
837830

838831
Ok(())

0 commit comments

Comments
 (0)