@@ -31,10 +31,10 @@ use crate::transform::MirPass;
31
31
use rustc_index:: vec:: { Idx , IndexVec } ;
32
32
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
33
33
use rustc_middle:: mir:: * ;
34
+ use rustc_middle:: ty:: ParamEnv ;
34
35
use rustc_middle:: ty:: TyCtxt ;
35
36
use smallvec:: SmallVec ;
36
- use std:: borrow:: Cow ;
37
- use std:: convert:: TryInto ;
37
+ use std:: { borrow:: Cow , convert:: TryInto } ;
38
38
39
39
pub struct SimplifyCfg {
40
40
label : String ,
@@ -322,17 +322,18 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
322
322
trace ! ( "running SimplifyLocals on {:?}" , body. source) ;
323
323
324
324
// First, we're going to get a count of *actual* uses for every `Local`.
325
- let mut used_locals = UsedLocals :: new ( body) ;
325
+ let mut used_locals = UsedLocals :: new ( body, tcx ) ;
326
326
327
327
// Next, we're going to remove any `Local` with zero actual uses. When we remove those
328
328
// `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
329
329
// count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from
330
330
// `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a
331
331
// fixedpoint where there are no more unused locals.
332
- remove_unused_definitions ( & mut used_locals, body, tcx ) ;
332
+ remove_unused_definitions ( & mut used_locals, body) ;
333
333
334
334
// Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
335
- let map = make_local_map ( & mut body. local_decls , & used_locals) ;
335
+ let arg_count = body. arg_count . try_into ( ) . unwrap ( ) ;
336
+ let map = make_local_map ( & mut body. local_decls , & used_locals, arg_count) ;
336
337
337
338
// Only bother running the `LocalUpdater` if we actually found locals to remove.
338
339
if map. iter ( ) . any ( Option :: is_none) {
@@ -346,54 +347,61 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
346
347
}
347
348
348
349
/// Construct the mapping while swapping out unused stuff out from the `vec`.
349
- fn make_local_map < V > (
350
+ fn make_local_map < ' tcx , V > (
350
351
local_decls : & mut IndexVec < Local , V > ,
351
- used_locals : & UsedLocals ,
352
+ used_locals : & UsedLocals < ' tcx > ,
353
+ arg_count : u32 ,
352
354
) -> IndexVec < Local , Option < Local > > {
353
- let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , & * local_decls) ;
355
+ let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , local_decls) ;
354
356
let mut used = Local :: new ( 0 ) ;
355
357
356
358
for alive_index in local_decls. indices ( ) {
357
- // `is_used` treats the `RETURN_PLACE` and arguments as used.
358
- if !used_locals. is_used ( alive_index) {
359
- continue ;
360
- }
361
-
362
- map[ alive_index] = Some ( used) ;
363
- if alive_index != used {
364
- local_decls. swap ( alive_index, used) ;
359
+ // When creating the local map treat the `RETURN_PLACE` and arguments as used.
360
+ if alive_index. as_u32 ( ) <= arg_count || used_locals. is_used ( alive_index) {
361
+ map[ alive_index] = Some ( used) ;
362
+ if alive_index != used {
363
+ local_decls. swap ( alive_index, used) ;
364
+ }
365
+ used. increment_by ( 1 ) ;
365
366
}
366
- used. increment_by ( 1 ) ;
367
367
}
368
368
local_decls. truncate ( used. index ( ) ) ;
369
369
map
370
370
}
371
371
372
372
/// Keeps track of used & unused locals.
373
- struct UsedLocals {
373
+ struct UsedLocals < ' tcx > {
374
374
increment : bool ,
375
- arg_count : u32 ,
376
375
use_count : IndexVec < Local , u32 > ,
376
+ is_static : bool ,
377
+ local_decls : IndexVec < Local , LocalDecl < ' tcx > > ,
378
+ param_env : ParamEnv < ' tcx > ,
379
+ tcx : TyCtxt < ' tcx > ,
377
380
}
378
381
379
- impl UsedLocals {
382
+ impl UsedLocals < ' tcx > {
380
383
/// Determines which locals are used & unused in the given body.
381
- fn new ( body : & Body < ' _ > ) -> Self {
384
+ fn new ( body : & Body < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
385
+ let def_id = body. source . def_id ( ) ;
386
+ let is_static = tcx. is_static ( def_id) ;
387
+ let param_env = tcx. param_env ( def_id) ;
388
+ let local_decls = body. local_decls . clone ( ) ;
382
389
let mut this = Self {
383
390
increment : true ,
384
- arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
385
391
use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
392
+ is_static,
393
+ local_decls,
394
+ param_env,
395
+ tcx,
386
396
} ;
387
397
this. visit_body ( body) ;
388
398
this
389
399
}
390
400
391
401
/// Checks if local is used.
392
- ///
393
- /// Return place and arguments are always considered used.
394
402
fn is_used ( & self , local : Local ) -> bool {
395
403
trace ! ( "is_used({:?}): use_count: {:?}" , local, self . use_count[ local] ) ;
396
- local . as_u32 ( ) <= self . arg_count || self . use_count [ local] != 0
404
+ self . use_count [ local] != 0
397
405
}
398
406
399
407
/// Updates the use counts to reflect the removal of given statement.
@@ -422,7 +430,7 @@ impl UsedLocals {
422
430
}
423
431
}
424
432
425
- impl Visitor < ' _ > for UsedLocals {
433
+ impl Visitor < ' tcx > for UsedLocals < ' tcx > {
426
434
fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
427
435
match statement. kind {
428
436
StatementKind :: LlvmInlineAsm ( ..)
@@ -448,7 +456,23 @@ impl Visitor<'_> for UsedLocals {
448
456
}
449
457
}
450
458
451
- fn visit_local ( & mut self , local : & Local , _ctx : PlaceContext , _location : Location ) {
459
+ fn visit_local ( & mut self , local : & Local , ctx : PlaceContext , _location : Location ) {
460
+ debug ! ( "local: {:?} is_static: {:?}, ctx: {:?}" , local, self . is_static, ctx) ;
461
+ // Do not count a local as used in `_local = <rhs>` if RHS is a ZST.
462
+ let store = matches ! ( ctx, PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ) ;
463
+ // Do not count _0 as a used in `return;` if it is a ZST.
464
+ let return_place = * local == RETURN_PLACE
465
+ && matches ! ( ctx, PlaceContext :: NonMutatingUse ( visit:: NonMutatingUseContext :: Move ) ) ;
466
+ if !self . is_static && ( store || return_place) {
467
+ let ty = self . local_decls [ * local] . ty ;
468
+ let param_env_and = self . param_env . and ( ty) ;
469
+ if let Ok ( layout) = self . tcx . layout_of ( param_env_and) {
470
+ debug ! ( "layout.is_zst: {:?}" , layout. is_zst( ) ) ;
471
+ if layout. is_zst ( ) {
472
+ return ;
473
+ }
474
+ }
475
+ }
452
476
if self . increment {
453
477
self . use_count [ * local] += 1 ;
454
478
} else {
@@ -460,21 +484,14 @@ impl Visitor<'_> for UsedLocals {
460
484
461
485
/// Removes unused definitions. Updates the used locals to reflect the changes made.
462
486
fn remove_unused_definitions < ' a , ' tcx > (
463
- used_locals : & ' a mut UsedLocals ,
487
+ used_locals : & ' a mut UsedLocals < ' tcx > ,
464
488
body : & mut Body < ' tcx > ,
465
- tcx : TyCtxt < ' tcx > ,
466
489
) {
467
490
// The use counts are updated as we remove the statements. A local might become unused
468
491
// during the retain operation, leading to a temporary inconsistency (storage statements or
469
492
// definitions referencing the local might remain). For correctness it is crucial that this
470
493
// computation reaches a fixed point.
471
494
472
- let def_id = body. source . def_id ( ) ;
473
- let is_static = tcx. is_static ( def_id) ;
474
- let param_env = tcx. param_env ( def_id) ;
475
-
476
- let local_decls = body. local_decls . clone ( ) ;
477
-
478
495
let mut modified = true ;
479
496
while modified {
480
497
modified = false ;
@@ -486,21 +503,7 @@ fn remove_unused_definitions<'a, 'tcx>(
486
503
StatementKind :: StorageLive ( local) | StatementKind :: StorageDead ( local) => {
487
504
used_locals. is_used ( * local)
488
505
}
489
- StatementKind :: Assign ( box ( place, _) ) => {
490
- let used = used_locals. is_used ( place. local ) ;
491
- let mut is_zst = false ;
492
-
493
- // ZST locals can be removed
494
- if used && !is_static {
495
- let ty = local_decls[ place. local ] . ty ;
496
- let param_env_and = param_env. and ( ty) ;
497
- if let Ok ( layout) = tcx. layout_of ( param_env_and) {
498
- is_zst = layout. is_zst ( ) ;
499
- }
500
- }
501
-
502
- used && !is_zst
503
- }
506
+ StatementKind :: Assign ( box ( place, _) ) => used_locals. is_used ( place. local ) ,
504
507
505
508
StatementKind :: SetDiscriminant { ref place, .. } => {
506
509
used_locals. is_used ( place. local )
0 commit comments