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