1
1
use crate::MirPass;
2
- use rustc_data_structures::fx::FxIndexMap;
3
2
use rustc_index::bit_set::BitSet;
4
3
use rustc_index::vec::IndexVec;
5
4
use rustc_middle::mir::patch::MirPatch;
6
5
use rustc_middle::mir::visit::*;
7
6
use rustc_middle::mir::*;
8
- use rustc_middle::ty::TyCtxt;
7
+ use rustc_middle::ty::{Ty, TyCtxt} ;
9
8
use rustc_mir_dataflow::value_analysis::{excluded_locals, iter_fields};
10
9
11
10
pub struct ScalarReplacementOfAggregates;
@@ -26,13 +25,13 @@ impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates {
26
25
let replacements = compute_flattening(tcx, body, escaping);
27
26
debug!(?replacements);
28
27
let all_dead_locals = replace_flattened_locals(tcx, body, replacements);
29
- if !all_dead_locals.is_empty() && tcx.sess.mir_opt_level() >= 4 {
28
+ if !all_dead_locals.is_empty() {
30
29
for local in excluded.indices() {
31
- excluded[local] |= all_dead_locals.contains(local) ;
30
+ excluded[local] |= all_dead_locals.contains(local);
32
31
}
33
32
excluded.raw.resize(body.local_decls.len(), false);
34
33
} else {
35
- break
34
+ break;
36
35
}
37
36
}
38
37
}
@@ -111,36 +110,29 @@ fn escaping_locals(excluded: &IndexVec<Local, bool>, body: &Body<'_>) -> BitSet<
111
110
112
111
#[derive(Default, Debug)]
113
112
struct ReplacementMap<'tcx> {
114
- fields: FxIndexMap<PlaceRef<'tcx>, Local>,
115
113
/// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
116
114
/// and deinit statement and debuginfo.
117
- fragments: IndexVec<Local, Option<Vec<(&'tcx [PlaceElem< 'tcx>] , Local)>>>,
115
+ fragments: IndexVec<Local, Option<IndexVec<Field, Option<(Ty< 'tcx>, Local)> >>>,
118
116
}
119
117
120
118
impl<'tcx> ReplacementMap<'tcx> {
121
- fn gather_debug_info_fragments(
122
- &self,
123
- place: PlaceRef<'tcx>,
124
- ) -> Option<Vec<VarDebugInfoFragment<'tcx>>> {
125
- let mut fragments = Vec::new();
126
- let Some(parts) = &self.fragments[place.local] else { return None };
127
- for (proj, replacement_local) in parts {
128
- if proj.starts_with(place.projection) {
129
- fragments.push(VarDebugInfoFragment {
130
- projection: proj[place.projection.len()..].to_vec(),
131
- contents: Place::from(*replacement_local),
132
- });
133
- }
134
- }
135
- Some(fragments)
119
+ fn replace_place(&self, tcx: TyCtxt<'tcx>, place: PlaceRef<'tcx>) -> Option<Place<'tcx>> {
120
+ let &[PlaceElem::Field(f, _), ref rest @ ..] = place.projection else { return None; };
121
+ let fields = self.fragments[place.local].as_ref()?;
122
+ let (_, new_local) = fields[f]?;
123
+ Some(Place { local: new_local, projection: tcx.intern_place_elems(&rest) })
136
124
}
137
125
138
126
fn place_fragments(
139
127
&self,
140
128
place: Place<'tcx>,
141
- ) -> Option<&Vec<(&'tcx [PlaceElem <'tcx>] , Local)>> {
129
+ ) -> Option<impl Iterator<Item = (Field, Ty <'tcx>, Local)> + '_ > {
142
130
let local = place.as_local()?;
143
- self.fragments[local].as_ref()
131
+ let fields = self.fragments[local].as_ref()?;
132
+ Some(fields.iter_enumerated().filter_map(|(field, &opt_ty_local)| {
133
+ let (ty, local) = opt_ty_local?;
134
+ Some((field, ty, local))
135
+ }))
144
136
}
145
137
}
146
138
@@ -153,8 +145,7 @@ fn compute_flattening<'tcx>(
153
145
body: &mut Body<'tcx>,
154
146
escaping: BitSet<Local>,
155
147
) -> ReplacementMap<'tcx> {
156
- let mut fields = FxIndexMap::default();
157
- let mut fragments = IndexVec::from_elem(None::<Vec<_>>, &body.local_decls);
148
+ let mut fragments = IndexVec::from_elem(None, &body.local_decls);
158
149
159
150
for local in body.local_decls.indices() {
160
151
if escaping.contains(local) {
@@ -169,14 +160,10 @@ fn compute_flattening<'tcx>(
169
160
};
170
161
let new_local =
171
162
body.local_decls.push(LocalDecl { ty: field_ty, user_ty: None, ..decl.clone() });
172
- let place = Place::from(local)
173
- .project_deeper(&[PlaceElem::Field(field, field_ty)], tcx)
174
- .as_ref();
175
- fields.insert(place, new_local);
176
- fragments[local].get_or_insert_default().push((place.projection, new_local));
163
+ fragments.get_or_insert_with(local, IndexVec::new).insert(field, (field_ty, new_local));
177
164
});
178
165
}
179
- ReplacementMap { fields, fragments }
166
+ ReplacementMap { fragments }
180
167
}
181
168
182
169
/// Perform the replacement computed by `compute_flattening`.
@@ -186,8 +173,10 @@ fn replace_flattened_locals<'tcx>(
186
173
replacements: ReplacementMap<'tcx>,
187
174
) -> BitSet<Local> {
188
175
let mut all_dead_locals = BitSet::new_empty(body.local_decls.len());
189
- for p in replacements.fields.keys() {
190
- all_dead_locals.insert(p.local);
176
+ for (local, replacements) in replacements.fragments.iter_enumerated() {
177
+ if replacements.is_some() {
178
+ all_dead_locals.insert(local);
179
+ }
191
180
}
192
181
debug!(?all_dead_locals);
193
182
if all_dead_locals.is_empty() {
@@ -197,7 +186,7 @@ fn replace_flattened_locals<'tcx>(
197
186
let mut visitor = ReplacementVisitor {
198
187
tcx,
199
188
local_decls: &body.local_decls,
200
- replacements,
189
+ replacements: &replacements ,
201
190
all_dead_locals,
202
191
patch: MirPatch::new(body),
203
192
};
@@ -223,21 +212,23 @@ struct ReplacementVisitor<'tcx, 'll> {
223
212
/// This is only used to compute the type for `VarDebugInfoContents::Composite`.
224
213
local_decls: &'ll LocalDecls<'tcx>,
225
214
/// Work to do.
226
- replacements: ReplacementMap<'tcx>,
215
+ replacements: &'ll ReplacementMap<'tcx>,
227
216
/// This is used to check that we are not leaving references to replaced locals behind.
228
217
all_dead_locals: BitSet<Local>,
229
218
patch: MirPatch<'tcx>,
230
219
}
231
220
232
- impl<'tcx, 'll> ReplacementVisitor<'tcx, 'll> {
233
- fn replace_place(&self, place: PlaceRef<'tcx>) -> Option<Place<'tcx>> {
234
- if let &[PlaceElem::Field(..), ref rest @ ..] = place.projection {
235
- let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
236
- let local = self.replacements.fields.get(&pr)?;
237
- Some(Place { local: *local, projection: self.tcx.intern_place_elems(&rest) })
238
- } else {
239
- None
221
+ impl<'tcx> ReplacementVisitor<'tcx, '_> {
222
+ fn gather_debug_info_fragments(&self, local: Local) -> Option<Vec<VarDebugInfoFragment<'tcx>>> {
223
+ let mut fragments = Vec::new();
224
+ let parts = self.replacements.place_fragments(local.into())?;
225
+ for (field, ty, replacement_local) in parts {
226
+ fragments.push(VarDebugInfoFragment {
227
+ projection: vec![PlaceElem::Field(field, ty)],
228
+ contents: Place::from(replacement_local),
229
+ });
240
230
}
231
+ Some(fragments)
241
232
}
242
233
}
243
234
@@ -246,21 +237,30 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
246
237
self.tcx
247
238
}
248
239
240
+ fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
241
+ if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
242
+ *place = repl
243
+ } else {
244
+ self.super_place(place, context, location)
245
+ }
246
+ }
247
+
249
248
#[instrument(level = "trace", skip(self))]
250
249
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
251
250
match statement.kind {
251
+ // Duplicate storage and deinit statements, as they pretty much apply to all fields.
252
252
StatementKind::StorageLive(l) => {
253
- if let Some(final_locals) = & self.replacements.fragments[l] {
254
- for &( _, fl) in final_locals {
253
+ if let Some(final_locals) = self.replacements.place_fragments(l.into()) {
254
+ for (_, _, fl) in final_locals {
255
255
self.patch.add_statement(location, StatementKind::StorageLive(fl));
256
256
}
257
257
statement.make_nop();
258
258
}
259
259
return;
260
260
}
261
261
StatementKind::StorageDead(l) => {
262
- if let Some(final_locals) = & self.replacements.fragments[l] {
263
- for &( _, fl) in final_locals {
262
+ if let Some(final_locals) = self.replacements.place_fragments(l.into()) {
263
+ for (_, _, fl) in final_locals {
264
264
self.patch.add_statement(location, StatementKind::StorageDead(fl));
265
265
}
266
266
statement.make_nop();
@@ -269,7 +269,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
269
269
}
270
270
StatementKind::Deinit(box place) => {
271
271
if let Some(final_locals) = self.replacements.place_fragments(place) {
272
- for &( _, fl) in final_locals {
272
+ for (_, _, fl) in final_locals {
273
273
self.patch
274
274
.add_statement(location, StatementKind::Deinit(Box::new(fl.into())));
275
275
}
@@ -278,48 +278,80 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
278
278
}
279
279
}
280
280
281
- StatementKind::Assign(box (place, Rvalue::Aggregate(_, ref operands))) => {
282
- if let Some(final_locals) = self.replacements.place_fragments(place) {
283
- for &(projection, fl) in final_locals {
284
- let &[PlaceElem::Field(index, _)] = projection else { bug!() };
285
- let index = index.as_usize();
286
- let rvalue = Rvalue::Use(operands[index].clone());
287
- self.patch.add_statement(
288
- location,
289
- StatementKind::Assign(Box::new((fl.into(), rvalue))),
290
- );
281
+ // We have `a = Struct { 0: x, 1: y, .. }`.
282
+ // We replace it by
283
+ // ```
284
+ // a_0 = x
285
+ // a_1 = y
286
+ // ...
287
+ // ```
288
+ StatementKind::Assign(box (place, Rvalue::Aggregate(_, ref mut operands))) => {
289
+ if let Some(local) = place.as_local()
290
+ && let Some(final_locals) = &self.replacements.fragments[local]
291
+ {
292
+ // This is ok as we delete the statement later.
293
+ let operands = std::mem::take(operands);
294
+ for (&opt_ty_local, mut operand) in final_locals.iter().zip(operands) {
295
+ if let Some((_, new_local)) = opt_ty_local {
296
+ // Replace mentions of SROA'd locals that appear in the operand.
297
+ self.visit_operand(&mut operand, location);
298
+
299
+ let rvalue = Rvalue::Use(operand);
300
+ self.patch.add_statement(
301
+ location,
302
+ StatementKind::Assign(Box::new((new_local.into(), rvalue))),
303
+ );
304
+ }
291
305
}
292
306
statement.make_nop();
293
307
return;
294
308
}
295
309
}
296
310
311
+ // We have `a = some constant`
312
+ // We add the projections.
313
+ // ```
314
+ // a_0 = a.0
315
+ // a_1 = a.1
316
+ // ...
317
+ // ```
318
+ // ConstProp will pick up the pieces and replace them by actual constants.
297
319
StatementKind::Assign(box (place, Rvalue::Use(Operand::Constant(_)))) => {
298
320
if let Some(final_locals) = self.replacements.place_fragments(place) {
299
- for &(projection, fl ) in final_locals {
300
- let rvalue =
301
- Rvalue::Use(Operand::Move(place.project_deeper(projection, self.tcx) ));
321
+ for (field, ty, new_local ) in final_locals {
322
+ let rplace = self.tcx.mk_place_field(place, field, ty);
323
+ let rvalue = Rvalue::Use(Operand::Move(rplace ));
302
324
self.patch.add_statement(
303
325
location,
304
- StatementKind::Assign(Box::new((fl .into(), rvalue))),
326
+ StatementKind::Assign(Box::new((new_local .into(), rvalue))),
305
327
);
306
328
}
307
- self.all_dead_locals.remove( place.local);
329
+ // We still need ` place.local` to exist, so don't make it nop.
308
330
return;
309
331
}
310
332
}
311
333
334
+ // We have `a = move? place`
335
+ // We replace it by
336
+ // ```
337
+ // a_0 = move? place.0
338
+ // a_1 = move? place.1
339
+ // ...
340
+ // ```
312
341
StatementKind::Assign(box (lhs, Rvalue::Use(ref op))) => {
313
- let (rplace, copy) = match op {
342
+ let (rplace, copy) = match * op {
314
343
Operand::Copy(rplace) => (rplace, true),
315
344
Operand::Move(rplace) => (rplace, false),
316
345
Operand::Constant(_) => bug!(),
317
346
};
318
347
if let Some(final_locals) = self.replacements.place_fragments(lhs) {
319
- for &(projection, fl ) in final_locals {
320
- let rplace = rplace.project_deeper(projection, self.tcx );
348
+ for (field, ty, new_local ) in final_locals {
349
+ let rplace = self.tcx.mk_place_field(rplace, field, ty );
321
350
debug!(?rplace);
322
- let rplace = self.replace_place(rplace.as_ref()).unwrap_or(rplace);
351
+ let rplace = self
352
+ .replacements
353
+ .replace_place(self.tcx, rplace.as_ref())
354
+ .unwrap_or(rplace);
323
355
debug!(?rplace);
324
356
let rvalue = if copy {
325
357
Rvalue::Use(Operand::Copy(rplace))
@@ -328,7 +360,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
328
360
};
329
361
self.patch.add_statement(
330
362
location,
331
- StatementKind::Assign(Box::new((fl .into(), rvalue))),
363
+ StatementKind::Assign(Box::new((new_local .into(), rvalue))),
332
364
);
333
365
}
334
366
statement.make_nop();
@@ -341,22 +373,14 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
341
373
self.super_statement(statement, location)
342
374
}
343
375
344
- fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
345
- if let Some(repl) = self.replace_place(place.as_ref()) {
346
- *place = repl
347
- } else {
348
- self.super_place(place, context, location)
349
- }
350
- }
351
-
352
376
#[instrument(level = "trace", skip(self))]
353
377
fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
354
378
match &mut var_debug_info.value {
355
379
VarDebugInfoContents::Place(ref mut place) => {
356
- if let Some(repl) = self.replace_place(place.as_ref()) {
380
+ if let Some(repl) = self.replacements. replace_place(self.tcx, place.as_ref()) {
357
381
*place = repl;
358
- } else if let Some(fragments ) =
359
- self.replacements. gather_debug_info_fragments(place.as_ref() )
382
+ } else if let Some(local ) = place.as_local()
383
+ && let Some(fragments) = self.gather_debug_info_fragments(local )
360
384
{
361
385
let ty = place.ty(self.local_decls, self.tcx).ty;
362
386
var_debug_info.value = VarDebugInfoContents::Composite { ty, fragments };
@@ -367,12 +391,13 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
367
391
debug!(?fragments);
368
392
fragments
369
393
.drain_filter(|fragment| {
370
- if let Some(repl) = self.replace_place(fragment.contents.as_ref()) {
394
+ if let Some(repl) =
395
+ self.replacements.replace_place(self.tcx, fragment.contents.as_ref())
396
+ {
371
397
fragment.contents = repl;
372
398
false
373
- } else if let Some(frg) = self
374
- .replacements
375
- .gather_debug_info_fragments(fragment.contents.as_ref())
399
+ } else if let Some(local) = fragment.contents.as_local()
400
+ && let Some(frg) = self.gather_debug_info_fragments(local)
376
401
{
377
402
new_fragments.extend(frg.into_iter().map(|mut f| {
378
403
f.projection.splice(0..0, fragment.projection.iter().copied());
0 commit comments