Skip to content

Commit 48b0529

Browse files
committed
Switch back to replace->uninit->storage remove
1 parent f0bd991 commit 48b0529

File tree

1 file changed

+62
-94
lines changed

1 file changed

+62
-94
lines changed

compiler/rustc_mir_transform/src/copy_prop.rs

Lines changed: 62 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
4949

5050
let any_replacement = ssa.copy_classes().iter_enumerated().any(|(l, &h)| l != h);
5151

52-
let storage_to_remove = if any_replacement {
53-
// The replacer will remove tautological moves from the head to the local,
54-
// so we remove them before running `MaybeUninitializedPlaces`.
55-
TautologicalMoveAssignmentRemover {
52+
if any_replacement {
53+
let storage_to_remove = DenseBitSet::new_empty(fully_moved.domain_size());
54+
55+
Replacer {
5656
tcx,
5757
copy_classes: ssa.copy_classes(),
58+
fully_moved,
5859
borrowed_locals: ssa.borrowed_locals(),
5960
}
6061
.visit_body_preserves_cfg(body);
@@ -65,29 +66,18 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
6566
.iterate_to_fixpoint(tcx, body, Some("mir_opt::copy_prop"))
6667
.into_results_cursor(body);
6768

68-
let mut storage_checker = StorageChecker {
69-
copy_classes: ssa.copy_classes(),
70-
maybe_uninit,
71-
head_storage_to_check,
72-
storage_to_remove: DenseBitSet::new_empty(fully_moved.domain_size()),
73-
};
69+
debug!(?head_storage_to_check);
7470

75-
storage_checker.visit_body(body);
71+
let storage_to_remove = {
72+
let mut storage_checker =
73+
StorageChecker { maybe_uninit, head_storage_to_check, storage_to_remove };
7674

77-
storage_checker.storage_to_remove
78-
} else {
79-
// Will be empty anyway.
80-
head_storage_to_check
81-
};
75+
storage_checker.visit_body(body);
8276

83-
Replacer {
84-
tcx,
85-
copy_classes: ssa.copy_classes(),
86-
fully_moved,
87-
borrowed_locals: ssa.borrowed_locals(),
88-
storage_to_remove,
77+
storage_checker.storage_to_remove
78+
};
79+
StorageRemover { tcx, storage_to_remove }.visit_body_preserves_cfg(body);
8980
}
90-
.visit_body_preserves_cfg(body);
9181

9282
if any_replacement {
9383
crate::simplify::remove_unused_definitions(body);
@@ -134,46 +124,10 @@ fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> DenseBitSet<Local> {
134124
fully_moved
135125
}
136126

137-
// We don't want to consider tautological moves in `MaybeUninitializedPlaces`, so we remove them before running it.
138-
struct TautologicalMoveAssignmentRemover<'a, 'tcx> {
139-
tcx: TyCtxt<'tcx>,
140-
copy_classes: &'a IndexSlice<Local, Local>,
141-
borrowed_locals: &'a DenseBitSet<Local>,
142-
}
143-
144-
impl<'tcx> MutVisitor<'tcx> for TautologicalMoveAssignmentRemover<'_, 'tcx> {
145-
fn tcx(&self) -> TyCtxt<'tcx> {
146-
self.tcx
147-
}
148-
149-
fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, loc: Location) {
150-
self.super_statement(stmt, loc);
151-
152-
// Similar to `Replacer::visit_statement`, but only handle moves.
153-
if let StatementKind::Assign(box (lhs, ref rhs)) = stmt.kind
154-
&& let Rvalue::Use(Operand::Move(rhs)) = *rhs
155-
{
156-
if self.borrowed_locals.contains(lhs.local) || self.borrowed_locals.contains(rhs.local)
157-
{
158-
return;
159-
}
160-
161-
let new_lhs_local = self.copy_classes[lhs.local];
162-
let new_rhs_local = self.copy_classes[rhs.local];
163-
164-
if new_lhs_local == new_rhs_local && lhs.projection == rhs.projection {
165-
debug!(?loc, ?lhs, ?rhs, "removing a to-be-tautological assignment");
166-
stmt.make_nop();
167-
}
168-
}
169-
}
170-
}
171-
172127
/// Utility to help performing substitution of `*pattern` by `target`.
173128
struct Replacer<'a, 'tcx> {
174129
tcx: TyCtxt<'tcx>,
175130
fully_moved: DenseBitSet<Local>,
176-
storage_to_remove: DenseBitSet<Local>,
177131
borrowed_locals: &'a DenseBitSet<Local>,
178132
copy_classes: &'a IndexSlice<Local, Local>,
179133
}
@@ -225,14 +179,6 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
225179
}
226180

227181
fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, loc: Location) {
228-
// When removing storage statements, we need to remove both (#107511).
229-
if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind
230-
&& self.storage_to_remove.contains(l)
231-
{
232-
stmt.make_nop();
233-
return;
234-
}
235-
236182
self.super_statement(stmt, loc);
237183

238184
// Do not leave tautological assignments around.
@@ -241,6 +187,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
241187
*rhs
242188
&& lhs == rhs
243189
{
190+
debug!(?stmt, ?loc, ?lhs, ?rhs, "removing tautological assignment");
244191
stmt.make_nop();
245192
}
246193
}
@@ -250,42 +197,63 @@ struct StorageChecker<'a, 'tcx> {
250197
storage_to_remove: DenseBitSet<Local>,
251198
head_storage_to_check: DenseBitSet<Local>,
252199
maybe_uninit: ResultsCursor<'a, 'tcx, MaybeUninitializedPlaces<'a, 'tcx>>,
253-
copy_classes: &'a IndexSlice<Local, Local>,
254200
}
255201

256202
impl<'a, 'tcx> Visitor<'tcx> for StorageChecker<'a, 'tcx> {
257203
fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
258-
if !context.is_use() {
259-
return;
204+
// We don't need to check storage statements and stores (for which the local doesn't need to be initialized).
205+
match context {
206+
PlaceContext::MutatingUse(MutatingUseContext::Store) | PlaceContext::NonUse(_) => {
207+
return;
208+
}
209+
_ => {}
210+
};
211+
212+
// The head must be initialized at the location of the local, otherwise we must remove it's storage statements.
213+
if self.head_storage_to_check.contains(local) {
214+
if let Some(move_idx) =
215+
self.maybe_uninit.analysis().move_data().rev_lookup.find_local(local)
216+
{
217+
self.maybe_uninit.seek_before_primary_effect(loc);
218+
219+
if self.maybe_uninit.get().contains(move_idx) {
220+
debug!(
221+
?loc,
222+
?context,
223+
?local,
224+
?move_idx,
225+
"found a head at a location in which it is maybe uninit, marking head for storage statement removal"
226+
);
227+
self.storage_to_remove.insert(local);
228+
229+
// Once we found a use of the head that is maybe uninit, we do not need to check it again.
230+
self.head_storage_to_check.remove(local);
231+
}
232+
}
260233
}
234+
}
235+
}
236+
237+
/// Utility to help performing substitution of `*pattern` by `target`.
238+
struct StorageRemover<'tcx> {
239+
tcx: TyCtxt<'tcx>,
240+
storage_to_remove: DenseBitSet<Local>,
241+
}
261242

262-
let head = self.copy_classes[local];
243+
impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> {
244+
fn tcx(&self) -> TyCtxt<'tcx> {
245+
self.tcx
246+
}
263247

264-
if local == head {
265-
// Every original use of the head is known to be initialized, so we don't need to check it.
248+
fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, loc: Location) {
249+
// When removing storage statements, we need to remove both (#107511).
250+
if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind
251+
&& self.storage_to_remove.contains(l)
252+
{
253+
stmt.make_nop();
266254
return;
267255
}
268256

269-
// The head must be initialized at the location of the local, otherwise we must remove it's storage statements.
270-
if self.head_storage_to_check.contains(head)
271-
&& let Some(move_idx) =
272-
self.maybe_uninit.analysis().move_data().rev_lookup.find_local(head)
273-
{
274-
self.maybe_uninit.seek_after_primary_effect(loc);
275-
276-
if self.maybe_uninit.get().contains(move_idx) {
277-
debug!(
278-
?loc,
279-
?local,
280-
?head,
281-
?move_idx,
282-
"found use of local with head at a location in which it is maybe uninit, marking head for storage statement removal"
283-
);
284-
self.storage_to_remove.insert(head);
285-
286-
// Once we found a use of the head that is maybe uninit, we do not need to check it again.
287-
self.head_storage_to_check.remove(head);
288-
}
289-
}
257+
self.super_statement(stmt, loc);
290258
}
291259
}

0 commit comments

Comments
 (0)