Skip to content

Commit c1accaf

Browse files
committed
Flatten aggregates into locals.
1 parent ad3407f commit c1accaf

27 files changed

+987
-390
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
use crate::transform::MirPass;
2+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
3+
use rustc_index::vec::{Idx, IndexVec};
4+
use rustc_middle::mir::visit::*;
5+
use rustc_middle::mir::*;
6+
use rustc_middle::ty::TyCtxt;
7+
use std::collections::hash_map::Entry;
8+
9+
pub struct FlattenLocals;
10+
11+
impl<'tcx> MirPass<'tcx> for FlattenLocals {
12+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
13+
if tcx.sess.mir_opt_level() < 4 {
14+
return;
15+
}
16+
17+
let replacements = compute_flattening(tcx, body);
18+
let mut all_dead_locals = FxHashSet::default();
19+
all_dead_locals.extend(replacements.fields.keys().map(|p| p.local));
20+
if all_dead_locals.is_empty() {
21+
return;
22+
}
23+
24+
ReplacementVisitor { tcx, map: &replacements, all_dead_locals }.visit_body(body);
25+
26+
let mut replaced_locals: IndexVec<_, _> = IndexVec::new();
27+
for (k, v) in replacements.fields {
28+
replaced_locals.ensure_contains_elem(k.local, || Vec::new());
29+
replaced_locals[k.local].push(v)
30+
}
31+
// Sort locals to avoid depending on FxHashMap order.
32+
for v in replaced_locals.iter_mut() {
33+
v.sort_unstable()
34+
}
35+
for bbdata in body.basic_blocks_mut().iter_mut() {
36+
bbdata.expand_statements(|stmt| {
37+
let source_info = stmt.source_info;
38+
let (live, origin_local) = match &stmt.kind {
39+
StatementKind::StorageLive(l) => (true, *l),
40+
StatementKind::StorageDead(l) => (false, *l),
41+
_ => return None,
42+
};
43+
replaced_locals.get(origin_local).map(move |final_locals| {
44+
final_locals.iter().map(move |&l| {
45+
let kind = if live {
46+
StatementKind::StorageLive(l)
47+
} else {
48+
StatementKind::StorageDead(l)
49+
};
50+
Statement { source_info, kind }
51+
})
52+
})
53+
});
54+
}
55+
}
56+
}
57+
58+
fn escaping_locals(body: &Body<'_>) -> FxHashSet<Local> {
59+
let mut set: FxHashSet<_> = (0..body.arg_count + 1).map(Local::new).collect();
60+
for (local, decl) in body.local_decls().iter_enumerated() {
61+
if decl.ty.is_union() || decl.ty.is_enum() {
62+
set.insert(local);
63+
}
64+
}
65+
let mut visitor = EscapeVisitor { set };
66+
visitor.visit_body(body);
67+
return visitor.set;
68+
69+
struct EscapeVisitor {
70+
set: FxHashSet<Local>,
71+
}
72+
73+
impl Visitor<'_> for EscapeVisitor {
74+
fn visit_local(&mut self, local: &Local, _: PlaceContext, _: Location) {
75+
self.set.insert(*local);
76+
}
77+
78+
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
79+
// Mirror the implementation in PreFlattenVisitor.
80+
if let &[PlaceElem::Field(..), ..] = &place.projection[..] {
81+
return;
82+
}
83+
self.super_place(place, context, location);
84+
}
85+
86+
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
87+
if let Rvalue::AddressOf(.., place) | Rvalue::Ref(.., place) = rvalue {
88+
if !place.is_indirect() {
89+
// Raw pointers may be used to access anything inside the enclosing place.
90+
self.set.insert(place.local);
91+
return;
92+
}
93+
}
94+
self.super_rvalue(rvalue, location)
95+
}
96+
97+
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
98+
if let StatementKind::StorageLive(..) | StatementKind::StorageDead(..) = statement.kind
99+
{
100+
// Storage statements are expanded in run_pass.
101+
return;
102+
}
103+
self.super_statement(statement, location)
104+
}
105+
106+
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
107+
if let TerminatorKind::Drop { place, .. }
108+
| TerminatorKind::DropAndReplace { place, .. } = terminator.kind
109+
{
110+
if !place.is_indirect() {
111+
// Raw pointers may be used to access anything inside the enclosing place.
112+
self.set.insert(place.local);
113+
return;
114+
}
115+
}
116+
self.super_terminator(terminator, location);
117+
}
118+
}
119+
}
120+
121+
#[derive(Default, Debug)]
122+
struct ReplacementMap<'tcx> {
123+
fields: FxHashMap<PlaceRef<'tcx>, Local>,
124+
}
125+
126+
fn compute_flattening<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> ReplacementMap<'tcx> {
127+
let escaping = escaping_locals(&*body);
128+
let (basic_blocks, local_decls, var_debug_info) =
129+
body.basic_blocks_local_decls_mut_and_var_debug_info();
130+
let mut visitor =
131+
PreFlattenVisitor { tcx, escaping, local_decls: local_decls, map: Default::default() };
132+
for (block, bbdata) in basic_blocks.iter_enumerated() {
133+
visitor.visit_basic_block_data(block, bbdata);
134+
}
135+
for var_debug_info in &*var_debug_info {
136+
visitor.visit_var_debug_info(var_debug_info);
137+
}
138+
return visitor.map;
139+
140+
struct PreFlattenVisitor<'tcx, 'll> {
141+
tcx: TyCtxt<'tcx>,
142+
local_decls: &'ll mut LocalDecls<'tcx>,
143+
escaping: FxHashSet<Local>,
144+
map: ReplacementMap<'tcx>,
145+
}
146+
147+
impl<'tcx, 'll> PreFlattenVisitor<'tcx, 'll> {
148+
fn create_place(&mut self, place: PlaceRef<'tcx>) {
149+
if self.escaping.contains(&place.local) {
150+
return;
151+
}
152+
153+
match self.map.fields.entry(place) {
154+
Entry::Occupied(_) => {}
155+
Entry::Vacant(v) => {
156+
let ty = place.ty(&*self.local_decls, self.tcx).ty;
157+
let local = self.local_decls.push(LocalDecl {
158+
ty,
159+
user_ty: None,
160+
..self.local_decls[place.local].clone()
161+
});
162+
v.insert(local);
163+
}
164+
}
165+
}
166+
}
167+
168+
impl<'tcx, 'll> Visitor<'tcx> for PreFlattenVisitor<'tcx, 'll> {
169+
fn visit_place(&mut self, place: &Place<'tcx>, _: PlaceContext, _: Location) {
170+
if let &[PlaceElem::Field(..), ..] = &place.projection[..] {
171+
let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
172+
self.create_place(pr)
173+
}
174+
}
175+
}
176+
}
177+
178+
struct ReplacementVisitor<'tcx, 'll> {
179+
tcx: TyCtxt<'tcx>,
180+
map: &'ll ReplacementMap<'tcx>,
181+
all_dead_locals: FxHashSet<Local>,
182+
}
183+
184+
impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
185+
fn tcx(&self) -> TyCtxt<'tcx> {
186+
self.tcx
187+
}
188+
189+
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
190+
if let StatementKind::StorageLive(..) | StatementKind::StorageDead(..) = statement.kind {
191+
// Storage statements are expanded in run_pass.
192+
return;
193+
}
194+
self.super_statement(statement, location)
195+
}
196+
197+
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
198+
if let &[PlaceElem::Field(..), ref rest @ ..] = &place.projection[..] {
199+
let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
200+
if let Some(local) = self.map.fields.get(&pr) {
201+
*place = Place { local: *local, projection: self.tcx.intern_place_elems(&rest) };
202+
return;
203+
}
204+
}
205+
self.super_place(place, context, location)
206+
}
207+
208+
fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
209+
assert!(!self.all_dead_locals.contains(local),);
210+
}
211+
}

compiler/rustc_mir/src/transform/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod dest_prop;
3232
pub mod dump_mir;
3333
pub mod early_otherwise_branch;
3434
pub mod elaborate_drops;
35+
pub mod flatten_locals;
3536
pub mod function_item_references;
3637
pub mod generator;
3738
pub mod inline;
@@ -499,6 +500,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
499500
// The main optimizations that we do on MIR.
500501
let optimizations: &[&dyn MirPass<'tcx>] = &[
501502
&remove_storage_markers::RemoveStorageMarkers,
503+
&flatten_locals::FlattenLocals,
502504
&remove_zsts::RemoveZsts,
503505
&const_goto::ConstGoto,
504506
&remove_unneeded_drops::RemoveUnneededDrops,

src/test/mir-opt/const_prop/aggregate.main.ConstProp.diff

+17-10
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,32 @@
66
let _1: i32; // in scope 0 at $DIR/aggregate.rs:5:9: 5:10
77
let mut _2: i32; // in scope 0 at $DIR/aggregate.rs:5:13: 5:24
88
let mut _3: (i32, i32, i32); // in scope 0 at $DIR/aggregate.rs:5:13: 5:22
9+
let mut _4: i32; // in scope 0 at $DIR/aggregate.rs:5:13: 5:22
10+
let mut _5: i32; // in scope 0 at $DIR/aggregate.rs:5:13: 5:22
11+
let mut _6: i32; // in scope 0 at $DIR/aggregate.rs:5:13: 5:22
912
scope 1 {
1013
debug x => _1; // in scope 1 at $DIR/aggregate.rs:5:9: 5:10
1114
}
1215

1316
bb0: {
14-
StorageLive(_1); // scope 0 at $DIR/aggregate.rs:5:9: 5:10
15-
StorageLive(_2); // scope 0 at $DIR/aggregate.rs:5:13: 5:24
16-
StorageLive(_3); // scope 0 at $DIR/aggregate.rs:5:13: 5:22
17-
(_3.0: i32) = const 0_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22
18-
(_3.1: i32) = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22
19-
(_3.2: i32) = const 2_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22
20-
- _2 = (_3.1: i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:24
17+
nop; // scope 0 at $DIR/aggregate.rs:5:9: 5:10
18+
nop; // scope 0 at $DIR/aggregate.rs:5:13: 5:24
19+
StorageLive(_4); // scope 0 at $DIR/aggregate.rs:5:13: 5:22
20+
StorageLive(_5); // scope 0 at $DIR/aggregate.rs:5:13: 5:22
21+
StorageLive(_6); // scope 0 at $DIR/aggregate.rs:5:13: 5:22
22+
_4 = const 0_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22
23+
_5 = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22
24+
_6 = const 2_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22
25+
- _2 = _5; // scope 0 at $DIR/aggregate.rs:5:13: 5:24
2126
- _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:28
2227
+ _2 = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:24
2328
+ _1 = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:28
24-
StorageDead(_2); // scope 0 at $DIR/aggregate.rs:5:27: 5:28
25-
StorageDead(_3); // scope 0 at $DIR/aggregate.rs:5:28: 5:29
29+
nop; // scope 0 at $DIR/aggregate.rs:5:27: 5:28
30+
StorageDead(_4); // scope 0 at $DIR/aggregate.rs:5:28: 5:29
31+
StorageDead(_5); // scope 0 at $DIR/aggregate.rs:5:28: 5:29
32+
StorageDead(_6); // scope 0 at $DIR/aggregate.rs:5:28: 5:29
2633
nop; // scope 0 at $DIR/aggregate.rs:4:11: 6:2
27-
StorageDead(_1); // scope 0 at $DIR/aggregate.rs:6:1: 6:2
34+
nop; // scope 0 at $DIR/aggregate.rs:6:1: 6:2
2835
return; // scope 0 at $DIR/aggregate.rs:6:2: 6:2
2936
}
3037
}

src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff

+19-15
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34
1111
let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34
1212
let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36
13+
let mut _10: u32; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36
14+
let mut _11: u32; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36
1315
scope 1 {
1416
debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10
1517
let _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10
@@ -23,7 +25,7 @@
2325
}
2426

2527
bb0: {
26-
StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10
28+
nop; // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10
2729
- _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
2830
- assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
2931
+ _2 = const (4_i32, false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
@@ -33,10 +35,10 @@
3335
bb1: {
3436
- _1 = move (_2.0: i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
3537
+ _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
36-
StorageLive(_3); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10
37-
StorageLive(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31
38+
nop; // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10
39+
nop; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31
3840
_4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31
39-
StorageLive(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33
41+
nop; // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33
4042
_5 = const 3_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33
4143
_6 = const 6_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34
4244
- _7 = Lt(_5, _6); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34
@@ -48,19 +50,21 @@
4850
bb2: {
4951
- _3 = _4[_5]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34
5052
+ _3 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34
51-
StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35
52-
StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35
53-
StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
54-
StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36
55-
(_9.0: u32) = const 12_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36
56-
(_9.1: u32) = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36
57-
- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38
53+
nop; // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35
54+
nop; // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35
55+
nop; // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10
56+
StorageLive(_10); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36
57+
StorageLive(_11); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36
58+
_10 = const 12_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36
59+
_11 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36
60+
- _8 = _11; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38
5861
+ _8 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38
59-
StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39
62+
StorageDead(_10); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39
63+
StorageDead(_11); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39
6064
nop; // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2
61-
StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2
62-
StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2
63-
StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2
65+
nop; // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2
66+
nop; // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2
67+
nop; // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2
6468
return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2
6569
}
6670
}

0 commit comments

Comments
 (0)