Skip to content

Commit 0b3a2e8

Browse files
committed
Merge branch 'dest-prop-move'
2 parents 25f8d01 + 31e8901 commit 0b3a2e8

19 files changed

+503
-632
lines changed

compiler/rustc_mir_transform/src/dest_prop.rs

Lines changed: 98 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
//! [attempt 3]: https://github.com/rust-lang/rust/pull/72632
133133
134134
use crate::MirPass;
135-
use rustc_data_structures::fx::{FxIndexMap, IndexEntry, IndexOccupiedEntry};
135+
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry, IndexOccupiedEntry};
136136
use rustc_index::bit_set::BitSet;
137137
use rustc_index::interval::SparseIntervalMatrix;
138138
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
@@ -159,7 +159,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
159159
// 2. Despite being an overall perf improvement, this still causes a 30% regression in
160160
// keccak. We can temporarily fix this by bounding function size, but in the long term
161161
// we should fix this by being smarter about invalidating analysis results.
162-
sess.mir_opt_level() >= 3
162+
sess.mir_opt_level() >= 2
163163
}
164164

165165
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
@@ -216,12 +216,14 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
216216

217217
// This is the set of merges we will apply this round. It is a subset of the candidates.
218218
let mut merges = FxIndexMap::default();
219+
let mut remove_writes = FxHashMap::default();
219220

220-
for (src, candidates) in candidates.c.iter() {
221-
if merged_locals.contains(*src) {
221+
for (src, candidates) in candidates.c.drain(..) {
222+
if merged_locals.contains(src) {
222223
continue;
223224
}
224-
let Some(dest) = candidates.iter().find(|dest| !merged_locals.contains(**dest))
225+
let Some(dest) =
226+
candidates.into_iter().find(|(dest, _)| !merged_locals.contains(*dest))
225227
else {
226228
continue;
227229
};
@@ -231,14 +233,17 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
231233
break;
232234
}
233235

234-
// Replace `src` by `dest` everywhere.
235-
merges.insert(*src, *dest);
236-
merged_locals.insert(*src);
237-
merged_locals.insert(*dest);
238-
239236
// Update liveness information based on the merge we just performed.
240237
// Every location where `src` was live, `dest` will be live.
241-
live.union_rows(*src, *dest);
238+
live.union_rows(src, dest.0);
239+
240+
// Replace `src` by `dest` everywhere.
241+
merged_locals.insert(src);
242+
merged_locals.insert(dest.0);
243+
merges.insert(src, dest.0);
244+
if !dest.1.is_empty() {
245+
remove_writes.insert(dest.0, dest.1);
246+
}
242247
}
243248
trace!(merging = ?merges);
244249

@@ -247,7 +252,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
247252
}
248253
round_count += 1;
249254

250-
apply_merges(body, tcx, &merges, &merged_locals);
255+
apply_merges(body, tcx, &merges, &remove_writes, &merged_locals);
251256
}
252257

253258
trace!(round_count);
@@ -260,7 +265,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
260265
/// frequently. Everything with a `&'alloc` lifetime points into here.
261266
#[derive(Default)]
262267
struct Allocations {
263-
candidates: FxIndexMap<Local, Vec<Local>>,
268+
candidates: FxIndexMap<Local, Vec<(Local, Vec<Location>)>>,
264269
candidates_reverse: FxIndexMap<Local, Vec<Local>>,
265270
write_info: WriteInfo,
266271
// PERF: Do this for `MaybeLiveLocals` allocations too.
@@ -282,7 +287,7 @@ struct Candidates<'alloc> {
282287
///
283288
/// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
284289
/// remove that assignment.
285-
c: &'alloc mut FxIndexMap<Local, Vec<Local>>,
290+
c: &'alloc mut FxIndexMap<Local, Vec<(Local, Vec<Location>)>>,
286291
/// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
287292
/// then this contains `b => a`.
288293
// PERF: Possibly these should be `SmallVec`s?
@@ -298,18 +303,29 @@ fn apply_merges<'tcx>(
298303
body: &mut Body<'tcx>,
299304
tcx: TyCtxt<'tcx>,
300305
merges: &FxIndexMap<Local, Local>,
306+
remove_writes: &FxHashMap<Local, Vec<Location>>,
301307
merged_locals: &BitSet<Local>,
302308
) {
303-
let mut merger = Merger { tcx, merges, merged_locals };
309+
let mut merger = Merger { tcx, merges, remove_writes, merged_locals };
304310
merger.visit_body_preserves_cfg(body);
305311
}
306312

307313
struct Merger<'a, 'tcx> {
308314
tcx: TyCtxt<'tcx>,
309315
merges: &'a FxIndexMap<Local, Local>,
316+
remove_writes: &'a FxHashMap<Local, Vec<Location>>,
310317
merged_locals: &'a BitSet<Local>,
311318
}
312319

320+
impl<'a, 'tcx> Merger<'a, 'tcx> {
321+
fn should_remove_write_at(&self, local: Local, location: Location) -> bool {
322+
let Some(to_remove) = self.remove_writes.get(&local) else {
323+
return false;
324+
};
325+
to_remove.contains(&location)
326+
}
327+
}
328+
313329
impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
314330
fn tcx(&self) -> TyCtxt<'tcx> {
315331
self.tcx
@@ -348,10 +364,27 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
348364
_ => {}
349365
}
350366
}
367+
StatementKind::Deinit(place) => {
368+
if self.should_remove_write_at(place.local, location) {
369+
statement.make_nop();
370+
}
371+
}
351372

352373
_ => {}
353374
}
354375
}
376+
377+
fn visit_operand(&mut self, op: &mut Operand<'tcx>, location: Location) {
378+
self.super_operand(op, location);
379+
match op {
380+
Operand::Move(place) => {
381+
if self.should_remove_write_at(place.local, location) {
382+
*op = Operand::Copy(*place);
383+
}
384+
}
385+
_ => (),
386+
}
387+
}
355388
}
356389

357390
//////////////////////////////////////////////////////////
@@ -373,30 +406,35 @@ struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
373406
// through these methods, and not directly.
374407
impl<'alloc> Candidates<'alloc> {
375408
/// Just `Vec::retain`, but the condition is inverted and we add debugging output
376-
fn vec_filter_candidates(
409+
fn vec_modify_candidates(
377410
src: Local,
378-
v: &mut Vec<Local>,
379-
mut f: impl FnMut(Local) -> CandidateFilter,
411+
v: &mut Vec<(Local, Vec<Location>)>,
412+
mut f: impl FnMut(Local) -> CandidateModification,
380413
at: Location,
381414
) {
382-
v.retain(|dest| {
383-
let remove = f(*dest);
384-
if remove == CandidateFilter::Remove {
415+
v.retain_mut(|(dest, remove_writes)| match f(*dest) {
416+
CandidateModification::Remove => {
385417
trace!("eliminating {:?} => {:?} due to conflict at {:?}", src, dest, at);
418+
false
419+
}
420+
CandidateModification::RemoveWrite => {
421+
trace!("marking write for {:?} => {:?} as needing removing at {:?}", src, dest, at);
422+
remove_writes.push(at);
423+
true
386424
}
387-
remove == CandidateFilter::Keep
425+
CandidateModification::Keep => true,
388426
});
389427
}
390428

391429
/// `vec_filter_candidates` but for an `Entry`
392430
fn entry_filter_candidates(
393-
mut entry: IndexOccupiedEntry<'_, Local, Vec<Local>>,
431+
mut entry: IndexOccupiedEntry<'_, Local, Vec<(Local, Vec<Location>)>>,
394432
p: Local,
395-
f: impl FnMut(Local) -> CandidateFilter,
433+
f: impl FnMut(Local) -> CandidateModification,
396434
at: Location,
397435
) {
398436
let candidates = entry.get_mut();
399-
Self::vec_filter_candidates(p, candidates, f, at);
437+
Self::vec_modify_candidates(p, candidates, f, at);
400438
if candidates.len() == 0 {
401439
entry.remove();
402440
}
@@ -406,7 +444,7 @@ impl<'alloc> Candidates<'alloc> {
406444
fn filter_candidates_by(
407445
&mut self,
408446
p: Local,
409-
mut f: impl FnMut(Local) -> CandidateFilter,
447+
mut f: impl FnMut(Local) -> CandidateModification,
410448
at: Location,
411449
) {
412450
// Cover the cases where `p` appears as a `src`
@@ -420,7 +458,8 @@ impl<'alloc> Candidates<'alloc> {
420458
// We use `retain` here to remove the elements from the reverse set if we've removed the
421459
// matching candidate in the forward set.
422460
srcs.retain(|src| {
423-
if f(*src) == CandidateFilter::Keep {
461+
let modification = f(*src);
462+
if modification == CandidateModification::Keep {
424463
return true;
425464
}
426465
let IndexEntry::Occupied(entry) = self.c.entry(*src) else {
@@ -430,18 +469,20 @@ impl<'alloc> Candidates<'alloc> {
430469
entry,
431470
*src,
432471
|dest| {
433-
if dest == p { CandidateFilter::Remove } else { CandidateFilter::Keep }
472+
if dest == p { modification } else { CandidateModification::Keep }
434473
},
435474
at,
436475
);
437-
false
476+
// Remove the src from the reverse set if we removed the candidate pair
477+
modification == CandidateModification::RemoveWrite
438478
});
439479
}
440480
}
441481

442482
#[derive(Copy, Clone, PartialEq, Eq)]
443-
enum CandidateFilter {
483+
enum CandidateModification {
444484
Keep,
485+
RemoveWrite,
445486
Remove,
446487
}
447488

@@ -500,32 +541,37 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
500541

501542
fn apply_conflicts(&mut self) {
502543
let writes = &self.write_info.writes;
503-
for p in writes {
544+
for &(p, is_removable) in writes {
545+
let modification = if is_removable {
546+
CandidateModification::RemoveWrite
547+
} else {
548+
CandidateModification::Remove
549+
};
504550
let other_skip = self.write_info.skip_pair.and_then(|(a, b)| {
505-
if a == *p {
551+
if a == p {
506552
Some(b)
507-
} else if b == *p {
553+
} else if b == p {
508554
Some(a)
509555
} else {
510556
None
511557
}
512558
});
513559
let at = self.points.point_from_location(self.at);
514560
self.candidates.filter_candidates_by(
515-
*p,
561+
p,
516562
|q| {
517563
if Some(q) == other_skip {
518-
return CandidateFilter::Keep;
564+
return CandidateModification::Keep;
519565
}
520566
// It is possible that a local may be live for less than the
521567
// duration of a statement This happens in the case of function
522568
// calls or inline asm. Because of this, we also mark locals as
523569
// conflicting when both of them are written to in the same
524570
// statement.
525-
if self.live.contains(q, at) || writes.contains(&q) {
526-
CandidateFilter::Remove
571+
if self.live.contains(q, at) || writes.iter().any(|&(x, _)| x == q) {
572+
modification
527573
} else {
528-
CandidateFilter::Keep
574+
CandidateModification::Keep
529575
}
530576
},
531577
self.at,
@@ -537,7 +583,9 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
537583
/// Describes where a statement/terminator writes to
538584
#[derive(Default, Debug)]
539585
struct WriteInfo {
540-
writes: Vec<Local>,
586+
/// Which locals are written to. The `bool` is true if the write is "removable," ie if it comes
587+
/// from a `Operand::Move` or `Deinit`.
588+
writes: Vec<(Local, bool)>,
541589
/// If this pair of locals is a candidate pair, completely skip processing it during this
542590
/// statement. All other candidates are unaffected.
543591
skip_pair: Option<(Local, Local)>,
@@ -581,10 +629,11 @@ impl WriteInfo {
581629
| Rvalue::CopyForDeref(_) => (),
582630
}
583631
}
632+
StatementKind::Deinit(p) => {
633+
self.writes.push((p.local, true));
634+
}
584635
// Retags are technically also reads, but reporting them as a write suffices
585-
StatementKind::SetDiscriminant { place, .. }
586-
| StatementKind::Deinit(place)
587-
| StatementKind::Retag(_, place) => {
636+
StatementKind::SetDiscriminant { place, .. } | StatementKind::Retag(_, place) => {
588637
self.add_place(**place);
589638
}
590639
StatementKind::Intrinsic(_)
@@ -669,16 +718,12 @@ impl WriteInfo {
669718
}
670719

671720
fn add_place(&mut self, place: Place<'_>) {
672-
self.writes.push(place.local);
721+
self.writes.push((place.local, false));
673722
}
674723

675724
fn add_operand<'tcx>(&mut self, op: &Operand<'tcx>) {
676725
match op {
677-
// FIXME(JakobDegen): In a previous version, the `Move` case was incorrectly treated as
678-
// being a read only. This was unsound, however we cannot add a regression test because
679-
// it is not possible to set this off with current MIR. Once we have that ability, a
680-
// regression test should be added.
681-
Operand::Move(p) => self.add_place(*p),
726+
Operand::Move(p) => self.writes.push((p.local, true)),
682727
Operand::Copy(_) | Operand::Constant(_) => (),
683728
}
684729
}
@@ -733,7 +778,7 @@ fn places_to_candidate_pair<'tcx>(
733778
fn find_candidates<'alloc, 'tcx>(
734779
body: &Body<'tcx>,
735780
borrowed: &BitSet<Local>,
736-
candidates: &'alloc mut FxIndexMap<Local, Vec<Local>>,
781+
candidates: &'alloc mut FxIndexMap<Local, Vec<(Local, Vec<Location>)>>,
737782
candidates_reverse: &'alloc mut FxIndexMap<Local, Vec<Local>>,
738783
) -> Candidates<'alloc> {
739784
candidates.clear();
@@ -747,16 +792,16 @@ fn find_candidates<'alloc, 'tcx>(
747792
}
748793
// Generate the reverse map
749794
for (src, cands) in candidates.iter() {
750-
for dest in cands.iter().copied() {
751-
candidates_reverse.entry(dest).or_default().push(*src);
795+
for (dest, _) in cands.iter() {
796+
candidates_reverse.entry(*dest).or_default().push(*src);
752797
}
753798
}
754799
Candidates { c: candidates, reverse: candidates_reverse }
755800
}
756801

757802
struct FindAssignments<'a, 'alloc, 'tcx> {
758803
body: &'a Body<'tcx>,
759-
candidates: &'alloc mut FxIndexMap<Local, Vec<Local>>,
804+
candidates: &'alloc mut FxIndexMap<Local, Vec<(Local, Vec<Location>)>>,
760805
borrowed: &'a BitSet<Local>,
761806
}
762807

@@ -793,7 +838,7 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
793838
}
794839

795840
// We may insert duplicates here, but that's fine
796-
self.candidates.entry(src).or_default().push(dest);
841+
self.candidates.entry(src).or_default().push((dest, Vec::new()));
797842
}
798843
}
799844
}

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ mod lower_slice_len;
9494
mod match_branches;
9595
mod multiple_return_terminators;
9696
mod normalize_array_len;
97-
mod nrvo;
9897
mod prettify;
9998
mod promote_consts;
10099
mod ref_prop;
@@ -609,13 +608,12 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
609608
&jump_threading::JumpThreading,
610609
&early_otherwise_branch::EarlyOtherwiseBranch,
611610
&simplify_comparison_integral::SimplifyComparisonIntegral,
612-
&dest_prop::DestinationPropagation,
613611
&o1(simplify_branches::SimplifyConstCondition::Final),
614612
&o1(remove_noop_landing_pads::RemoveNoopLandingPads),
615613
&o1(simplify::SimplifyCfg::Final),
616614
&copy_prop::CopyProp,
617615
&dead_store_elimination::DeadStoreElimination::Final,
618-
&nrvo::RenameReturnPlace,
616+
&dest_prop::DestinationPropagation,
619617
&simplify::SimplifyLocals::Final,
620618
&multiple_return_terminators::MultipleReturnTerminators,
621619
&deduplicate_blocks::DeduplicateBlocks,

0 commit comments

Comments
 (0)