Skip to content

Commit 055bf4c

Browse files
committed
Auto merge of #96116 - ouz-a:mir-opt, r=oli-obk
Make derefer work everwhere Follow up work on previous PR's #95649 and #95857. r? rust-lang/mir-opt _Co-Authored-By: `@oli-obk_`
2 parents 18b53ce + 5364c86 commit 055bf4c

12 files changed

+612
-270
lines changed

compiler/rustc_middle/src/mir/patch.rs

+20
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ impl<'tcx> MirPatch<'tcx> {
141141

142142
let mut delta = 0;
143143
let mut last_bb = START_BLOCK;
144+
let mut stmts_and_targets: Vec<(Statement<'_>, BasicBlock)> = Vec::new();
144145
for (mut loc, stmt) in new_statements {
145146
if loc.block != last_bb {
146147
delta = 0;
@@ -149,11 +150,30 @@ impl<'tcx> MirPatch<'tcx> {
149150
debug!("MirPatch: adding statement {:?} at loc {:?}+{}", stmt, loc, delta);
150151
loc.statement_index += delta;
151152
let source_info = Self::source_info_for_index(&body[loc.block], loc);
153+
154+
// For mir-opt `Derefer` to work in all cases we need to
155+
// get terminator's targets and apply the statement to all of them.
156+
if loc.statement_index > body[loc.block].statements.len() {
157+
let term = body[loc.block].terminator();
158+
let successors = term.successors().clone();
159+
160+
for i in successors {
161+
stmts_and_targets
162+
.push((Statement { source_info, kind: stmt.clone() }, i.clone()));
163+
}
164+
delta += 1;
165+
continue;
166+
}
167+
152168
body[loc.block]
153169
.statements
154170
.insert(loc.statement_index, Statement { source_info, kind: stmt });
155171
delta += 1;
156172
}
173+
174+
for (stmt, target) in stmts_and_targets.into_iter().rev() {
175+
body[target].statements.insert(0, stmt);
176+
}
157177
}
158178

159179
pub fn source_info_for_index(data: &BasicBlockData<'_>, loc: Location) -> SourceInfo {

compiler/rustc_mir_transform/src/deref_separator.rs

+76-55
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,89 @@
11
use crate::MirPass;
2+
use rustc_index::vec::IndexVec;
23
use rustc_middle::mir::patch::MirPatch;
4+
use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
35
use rustc_middle::mir::*;
46
use rustc_middle::ty::TyCtxt;
57
pub struct Derefer;
68

7-
pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
8-
let mut patch = MirPatch::new(body);
9-
let (basic_blocks, local_decl) = body.basic_blocks_and_local_decls_mut();
10-
for (block, data) in basic_blocks.iter_enumerated_mut() {
11-
for (i, stmt) in data.statements.iter_mut().enumerate() {
12-
match stmt.kind {
13-
StatementKind::Assign(box (og_place, Rvalue::Ref(region, borrow_knd, place))) => {
14-
let mut place_local = place.local;
15-
let mut last_len = 0;
16-
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
17-
if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
18-
// The type that we are derefing.
19-
let ty = p_ref.ty(local_decl, tcx).ty;
20-
let temp = patch.new_temp(ty, stmt.source_info.span);
21-
22-
// Because we are assigning this right before original statement
23-
// we are using index i of statement.
24-
let loc = Location { block: block, statement_index: i };
25-
patch.add_statement(loc, StatementKind::StorageLive(temp));
26-
27-
// We are adding current p_ref's projections to our
28-
// temp value, excluding projections we already covered.
29-
let deref_place = Place::from(place_local)
30-
.project_deeper(&p_ref.projection[last_len..], tcx);
31-
patch.add_assign(
32-
loc,
33-
Place::from(temp),
34-
Rvalue::Use(Operand::Move(deref_place)),
35-
);
36-
37-
place_local = temp;
38-
last_len = p_ref.projection.len();
39-
40-
// We are creating a place by using our temp value's location
41-
// and copying derefed values which we need to create new statement.
42-
let temp_place =
43-
Place::from(temp).project_deeper(&place.projection[idx..], tcx);
44-
let new_stmt = Statement {
45-
source_info: stmt.source_info,
46-
kind: StatementKind::Assign(Box::new((
47-
og_place,
48-
Rvalue::Ref(region, borrow_knd, temp_place),
49-
))),
50-
};
51-
52-
// Replace current statement with newly created one.
53-
*stmt = new_stmt;
54-
55-
// Since our job with the temp is done it should be gone
56-
let loc = Location { block: block, statement_index: i + 1 };
57-
patch.add_statement(loc, StatementKind::StorageDead(temp));
58-
}
59-
}
9+
pub struct DerefChecker<'tcx> {
10+
tcx: TyCtxt<'tcx>,
11+
patcher: MirPatch<'tcx>,
12+
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
13+
}
14+
15+
impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
16+
fn tcx(&self) -> TyCtxt<'tcx> {
17+
self.tcx
18+
}
19+
20+
fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
21+
let mut place_local = place.local;
22+
let mut last_len = 0;
23+
let mut last_deref_idx = 0;
24+
25+
let mut prev_temp: Option<Local> = None;
26+
27+
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
28+
if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
29+
last_deref_idx = idx;
30+
}
31+
}
32+
33+
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
34+
if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
35+
let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
36+
let temp =
37+
self.patcher.new_temp(ty, self.local_decls[p_ref.local].source_info.span);
38+
39+
self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
40+
41+
// We are adding current p_ref's projections to our
42+
// temp value, excluding projections we already covered.
43+
let deref_place = Place::from(place_local)
44+
.project_deeper(&p_ref.projection[last_len..], self.tcx);
45+
self.patcher.add_assign(
46+
loc,
47+
Place::from(temp),
48+
Rvalue::Use(Operand::Move(deref_place)),
49+
);
50+
51+
place_local = temp;
52+
last_len = p_ref.projection.len();
53+
54+
// Change `Place` only if we are actually at the Place's last deref
55+
if idx == last_deref_idx {
56+
let temp_place =
57+
Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
58+
*place = temp_place;
59+
}
60+
61+
// We are destroying last temp since it's no longer used.
62+
if let Some(prev_temp) = prev_temp {
63+
self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
6064
}
61-
_ => (),
65+
66+
prev_temp = Some(temp);
6267
}
6368
}
69+
70+
// Since we won't be able to reach final temp, we destroy it outside the loop.
71+
if let Some(prev_temp) = prev_temp {
72+
let last_loc = Location { block: loc.block, statement_index: loc.statement_index + 1 };
73+
self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
74+
}
6475
}
65-
patch.apply(body);
76+
}
77+
78+
pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
79+
let patch = MirPatch::new(body);
80+
let mut checker = DerefChecker { tcx, patcher: patch, local_decls: body.local_decls.clone() };
81+
82+
for (bb, data) in body.basic_blocks_mut().iter_enumerated_mut() {
83+
checker.visit_basic_block_data(bb, data);
84+
}
85+
86+
checker.patcher.apply(body);
6687
}
6788

6889
impl<'tcx> MirPass<'tcx> for Derefer {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
- // MIR for `main` before Derefer
2+
+ // MIR for `main` after Derefer
3+
4+
fn main() -> () {
5+
let mut _0: (); // return place in scope 0 at $DIR/derefer_complex_case.rs:3:11: 3:11
6+
let mut _1: std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
7+
let mut _2: &[i32; 2]; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
8+
let _3: [i32; 2]; // in scope 0 at $DIR/derefer_complex_case.rs:4:18: 4:26
9+
let mut _4: std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
10+
let mut _5: (); // in scope 0 at $DIR/derefer_complex_case.rs:3:1: 5:2
11+
let _6: (); // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
12+
let mut _7: std::option::Option<&i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
13+
let mut _8: &mut std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
14+
let mut _9: &mut std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
15+
let mut _10: isize; // in scope 0 at $DIR/derefer_complex_case.rs:4:5: 4:40
16+
let mut _11: !; // in scope 0 at $DIR/derefer_complex_case.rs:4:5: 4:40
17+
let mut _13: i32; // in scope 0 at $DIR/derefer_complex_case.rs:4:34: 4:37
18+
let mut _14: &[i32; 2]; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
19+
+ let mut _15: &i32; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
20+
scope 1 {
21+
debug iter => _4; // in scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
22+
let _12: i32; // in scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
23+
scope 2 {
24+
debug foo => _12; // in scope 2 at $DIR/derefer_complex_case.rs:4:10: 4:13
25+
}
26+
}
27+
28+
bb0: {
29+
StorageLive(_1); // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
30+
StorageLive(_2); // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
31+
_14 = const main::promoted[0]; // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
32+
// mir::Constant
33+
// + span: $DIR/derefer_complex_case.rs:4:17: 4:26
34+
// + literal: Const { ty: &[i32; 2], val: Unevaluated(main, [], Some(promoted[0])) }
35+
_2 = &(*_14); // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
36+
_1 = <&[i32; 2] as IntoIterator>::into_iter(move _2) -> bb1; // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
37+
// mir::Constant
38+
// + span: $DIR/derefer_complex_case.rs:4:17: 4:26
39+
// + literal: Const { ty: fn(&[i32; 2]) -> <&[i32; 2] as IntoIterator>::IntoIter {<&[i32; 2] as IntoIterator>::into_iter}, val: Value(Scalar(<ZST>)) }
40+
}
41+
42+
bb1: {
43+
StorageDead(_2); // scope 0 at $DIR/derefer_complex_case.rs:4:25: 4:26
44+
StorageLive(_4); // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
45+
_4 = move _1; // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
46+
goto -> bb2; // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
47+
}
48+
49+
bb2: {
50+
StorageLive(_6); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
51+
StorageLive(_7); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
52+
StorageLive(_8); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
53+
StorageLive(_9); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
54+
_9 = &mut _4; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
55+
_8 = &mut (*_9); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
56+
_7 = <std::slice::Iter<i32> as Iterator>::next(move _8) -> bb3; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
57+
// mir::Constant
58+
// + span: $DIR/derefer_complex_case.rs:4:17: 4:26
59+
// + literal: Const { ty: for<'r> fn(&'r mut std::slice::Iter<i32>) -> Option<<std::slice::Iter<i32> as Iterator>::Item> {<std::slice::Iter<i32> as Iterator>::next}, val: Value(Scalar(<ZST>)) }
60+
}
61+
62+
bb3: {
63+
StorageDead(_8); // scope 1 at $DIR/derefer_complex_case.rs:4:25: 4:26
64+
_10 = discriminant(_7); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
65+
switchInt(move _10) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
66+
}
67+
68+
bb4: {
69+
StorageLive(_12); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
70+
- _12 = (*((_7 as Some).0: &i32)); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
71+
+ StorageLive(_15); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
72+
+ _15 = move ((_7 as Some).0: &i32); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
73+
+ _12 = (*_15); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
74+
+ StorageDead(_15); // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
75+
StorageLive(_13); // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
76+
_13 = _12; // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
77+
_6 = std::mem::drop::<i32>(move _13) -> bb7; // scope 2 at $DIR/derefer_complex_case.rs:4:29: 4:38
78+
// mir::Constant
79+
// + span: $DIR/derefer_complex_case.rs:4:29: 4:33
80+
// + literal: Const { ty: fn(i32) {std::mem::drop::<i32>}, val: Value(Scalar(<ZST>)) }
81+
}
82+
83+
bb5: {
84+
unreachable; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
85+
}
86+
87+
bb6: {
88+
_0 = const (); // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
89+
StorageDead(_9); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
90+
StorageDead(_7); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
91+
StorageDead(_6); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
92+
StorageDead(_4); // scope 0 at $DIR/derefer_complex_case.rs:4:39: 4:40
93+
StorageDead(_1); // scope 0 at $DIR/derefer_complex_case.rs:4:39: 4:40
94+
return; // scope 0 at $DIR/derefer_complex_case.rs:5:2: 5:2
95+
}
96+
97+
bb7: {
98+
StorageDead(_13); // scope 2 at $DIR/derefer_complex_case.rs:4:37: 4:38
99+
StorageDead(_12); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
100+
StorageDead(_9); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
101+
StorageDead(_7); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
102+
StorageDead(_6); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
103+
_5 = const (); // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
104+
goto -> bb2; // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
105+
+ }
106+
+
107+
+ bb8 (cleanup): {
108+
+ resume; // scope 0 at $DIR/derefer_complex_case.rs:3:1: 5:2
109+
}
110+
}
111+
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// EMIT_MIR derefer_complex_case.main.Derefer.diff
2+
3+
fn main() {
4+
for &foo in &[42, 43] { drop(foo) }
5+
}

0 commit comments

Comments
 (0)