Skip to content

Commit f4ecbcf

Browse files
committed
Use a single bitset to check the storage in copy_prop, only check reachable blocks
1 parent 2ee4a9b commit f4ecbcf

7 files changed

+108
-43
lines changed

compiler/rustc_mir_transform/src/copy_prop.rs

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -32,30 +32,20 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
3232

3333
let typing_env = body.typing_env(tcx);
3434
let ssa = SsaLocals::new(tcx, body, typing_env);
35-
let borrowed_locals = ssa.borrowed_locals().clone();
3635

37-
debug!(?borrowed_locals);
36+
debug!(borrowed_locals = ?ssa.borrowed_locals());
3837
debug!(copy_classes = ?ssa.copy_classes());
3938

4039
let mut any_replacement = false;
4140
let fully_moved = fully_moved_locals(&ssa, body);
4241
debug!(?fully_moved);
4342

44-
let mut head_storage_to_check = DenseBitSet::new_empty(fully_moved.domain_size());
4543
let mut storage_to_remove = DenseBitSet::new_empty(fully_moved.domain_size());
4644

4745
for (local, &head) in ssa.copy_classes().iter_enumerated() {
4846
if local != head {
4947
any_replacement = true;
50-
// We need to determine if we can keep the head's storage statements (which enables better optimizations).
51-
// For every local's usage location, if the head is maybe-uninitialized, we'll need to remove it's storage statements.
52-
head_storage_to_check.insert(head);
53-
54-
if borrowed_locals.contains(local) {
55-
// To keep the storage of a head, we require that none of the locals in it's copy class are borrowed,
56-
// since otherwise we cannot easily identify when it is used.
57-
storage_to_remove.insert(head);
58-
}
48+
storage_to_remove.insert(head);
5949
}
6050
}
6151

@@ -66,25 +56,31 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
6656
let fully_moved = fully_moved_locals(&ssa, body);
6757
debug!(?fully_moved);
6858

69-
// Debug builds have no use for the storage statements, so avoid extra work.
70-
let storage_to_remove = if any_replacement && tcx.sess.emit_lifetime_markers() {
59+
// We can determine if we can keep the head's storage statements (which enables better optimizations).
60+
// For every local's usage location, we'll to remove it's storage statements only if the head is maybe-uninitialized,
61+
// or if the local is borrowed (since we cannot easily identify when it is used).
62+
let storage_to_remove = if tcx.sess.emit_lifetime_markers() {
63+
storage_to_remove.clear();
64+
7165
let maybe_uninit = MaybeUninitializedLocals::new()
7266
.iterate_to_fixpoint(tcx, body, Some("mir_opt::copy_prop"))
7367
.into_results_cursor(body);
7468

7569
let mut storage_checker = StorageChecker {
7670
maybe_uninit,
7771
copy_classes: ssa.copy_classes(),
78-
head_storage_to_check,
72+
borrowed_locals: ssa.borrowed_locals(),
7973
storage_to_remove,
8074
};
8175

82-
storage_checker.visit_body(body);
76+
for (bb, data) in traversal::reachable(body) {
77+
storage_checker.visit_basic_block_data(bb, data);
78+
}
8379

8480
storage_checker.storage_to_remove
8581
} else {
8682
// Conservatively remove all storage statements for the head locals.
87-
head_storage_to_check
83+
storage_to_remove
8884
};
8985

9086
debug!(?storage_to_remove);
@@ -200,7 +196,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
200196
struct StorageChecker<'a, 'tcx> {
201197
maybe_uninit: ResultsCursor<'a, 'tcx, MaybeUninitializedLocals>,
202198
copy_classes: &'a IndexSlice<Local, Local>,
203-
head_storage_to_check: DenseBitSet<Local>,
199+
borrowed_locals: &'a DenseBitSet<Local>,
204200
storage_to_remove: DenseBitSet<Local>,
205201
}
206202

@@ -222,23 +218,28 @@ impl<'a, 'tcx> Visitor<'tcx> for StorageChecker<'a, 'tcx> {
222218

223219
let head = self.copy_classes[local];
224220

225-
// The head must be initialized at the location of the local, otherwise we must remove it's storage statements.
226-
if self.head_storage_to_check.contains(head) {
227-
self.maybe_uninit.seek_before_primary_effect(loc);
228-
229-
if self.maybe_uninit.get().contains(head) {
230-
debug!(
231-
?loc,
232-
?context,
233-
?local,
234-
?head,
235-
"found a head at a location in which it is maybe uninit, marking head for storage statement removal"
236-
);
237-
self.storage_to_remove.insert(head);
238-
239-
// Once we found a use of the head that is maybe uninit, we do not need to check it again.
240-
self.head_storage_to_check.remove(head);
241-
}
221+
// If the local is the head, or if we already marked it for deletion, we do not need to check it.
222+
if head == local || self.storage_to_remove.contains(head) {
223+
return;
224+
}
225+
226+
// If the local is borrowed, we cannot easily determine if it is used, so we have to remove the storage statements.
227+
if self.borrowed_locals.contains(local) {
228+
self.storage_to_remove.insert(head);
229+
return;
230+
}
231+
232+
self.maybe_uninit.seek_before_primary_effect(loc);
233+
234+
if self.maybe_uninit.get().contains(head) {
235+
debug!(
236+
?loc,
237+
?context,
238+
?local,
239+
?head,
240+
"found a head at a location in which it is maybe uninit, marking head for storage statement removal"
241+
);
242+
self.storage_to_remove.insert(head);
242243
}
243244
}
244245
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
- // MIR for `f` before CopyProp
2+
+ // MIR for `f` after CopyProp
3+
4+
fn f(_1: &mut usize) -> () {
5+
let mut _0: ();
6+
let mut _2: usize;
7+
let mut _3: usize;
8+
9+
bb0: {
10+
StorageLive(_2);
11+
_2 = const 42_usize;
12+
- _3 = copy _2;
13+
- (*_1) = copy _3;
14+
+ (*_1) = copy _2;
15+
StorageDead(_2);
16+
return;
17+
}
18+
19+
bb1: {
20+
StorageLive(_2);
21+
- (*_1) = copy _3;
22+
+ (*_1) = copy _2;
23+
StorageDead(_2);
24+
return;
25+
}
26+
}
27+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// skip-filecheck
2+
//@ test-mir-pass: CopyProp
3+
4+
#![feature(custom_mir, core_intrinsics)]
5+
6+
// Check that we do not remove the storage statements if the head
7+
// is uninitialized in an unreachable block.
8+
9+
use std::intrinsics::mir::*;
10+
11+
// EMIT_MIR copy_prop_storage_unreachable.f.CopyProp.diff
12+
13+
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
14+
pub fn f(_1: &mut usize) {
15+
mir! {
16+
let _2: usize;
17+
let _3: usize;
18+
{
19+
StorageLive(_2);
20+
_2 = 42;
21+
_3 = _2;
22+
(*_1) = _3;
23+
StorageDead(_2);
24+
Return()
25+
}
26+
bb1 = {
27+
// Ensure that _2 is considered uninitialized by `MaybeUninitializedLocals`.
28+
StorageLive(_2);
29+
// Use of _3 (in an unreachable block) when definition of _2 is unavailable.
30+
(*_1) = _3;
31+
StorageDead(_2);
32+
Return()
33+
}
34+
}
35+
}

tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
4949

5050
bb1: {
5151
StorageLive(_9);
52+
StorageLive(_7);
5253
StorageLive(_6);
5354
StorageLive(_5);
5455
_5 = copy _4;
@@ -59,6 +60,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
5960

6061
bb2: {
6162
StorageDead(_6);
63+
StorageDead(_7);
6264
StorageDead(_9);
6365
StorageDead(_4);
6466
drop(_3) -> [return: bb3, unwind unreachable];
@@ -69,15 +71,14 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
6971
}
7072

7173
bb4: {
72-
StorageLive(_7);
7374
_7 = copy _4;
7475
StorageLive(_8);
7576
_8 = AddUnchecked(copy _7, const 1_u32);
7677
_4 = move _8;
7778
StorageDead(_8);
7879
_9 = Option::<u32>::Some(copy _7);
79-
StorageDead(_7);
8080
StorageDead(_6);
81+
StorageDead(_7);
8182
StorageLive(_10);
8283
_10 = copy ((_9 as Some).0: u32);
8384
StorageLive(_11);

tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
4949

5050
bb1: {
5151
StorageLive(_9);
52+
StorageLive(_7);
5253
StorageLive(_6);
5354
StorageLive(_5);
5455
_5 = copy _4;
@@ -59,6 +60,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
5960

6061
bb2: {
6162
StorageDead(_6);
63+
StorageDead(_7);
6264
StorageDead(_9);
6365
StorageDead(_4);
6466
drop(_3) -> [return: bb3, unwind continue];
@@ -69,15 +71,14 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () {
6971
}
7072

7173
bb4: {
72-
StorageLive(_7);
7374
_7 = copy _4;
7475
StorageLive(_8);
7576
_8 = AddUnchecked(copy _7, const 1_u32);
7677
_4 = move _8;
7778
StorageDead(_8);
7879
_9 = Option::<u32>::Some(copy _7);
79-
StorageDead(_7);
8080
StorageDead(_6);
81+
StorageDead(_7);
8182
StorageLive(_10);
8283
_10 = copy ((_9 as Some).0: u32);
8384
StorageLive(_11);

tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-abort.mir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> {
2626
}
2727

2828
bb0: {
29+
StorageLive(_5);
2930
StorageLive(_4);
3031
StorageLive(_2);
3132
_2 = copy ((*_1).0: u32);
@@ -43,19 +44,18 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> {
4344
}
4445

4546
bb2: {
46-
StorageLive(_5);
4747
_5 = copy ((*_1).0: u32);
4848
StorageLive(_6);
4949
_6 = AddUnchecked(copy _5, const 1_u32);
5050
((*_1).0: u32) = move _6;
5151
StorageDead(_6);
5252
_0 = Option::<u32>::Some(copy _5);
53-
StorageDead(_5);
5453
goto -> bb3;
5554
}
5655

5756
bb3: {
5857
StorageDead(_4);
58+
StorageDead(_5);
5959
return;
6060
}
6161
}

tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.panic-unwind.mir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> {
2626
}
2727

2828
bb0: {
29+
StorageLive(_5);
2930
StorageLive(_4);
3031
StorageLive(_2);
3132
_2 = copy ((*_1).0: u32);
@@ -43,19 +44,18 @@ fn range_iter_next(_1: &mut std::ops::Range<u32>) -> Option<u32> {
4344
}
4445

4546
bb2: {
46-
StorageLive(_5);
4747
_5 = copy ((*_1).0: u32);
4848
StorageLive(_6);
4949
_6 = AddUnchecked(copy _5, const 1_u32);
5050
((*_1).0: u32) = move _6;
5151
StorageDead(_6);
5252
_0 = Option::<u32>::Some(copy _5);
53-
StorageDead(_5);
5453
goto -> bb3;
5554
}
5655

5756
bb3: {
5857
StorageDead(_4);
58+
StorageDead(_5);
5959
return;
6060
}
6161
}

0 commit comments

Comments
 (0)