@@ -35,6 +35,7 @@ use rustc_middle::mir::*;
35
35
use rustc_middle:: ty:: TyCtxt ;
36
36
use smallvec:: SmallVec ;
37
37
use std:: borrow:: Cow ;
38
+ use std:: convert:: TryInto ;
38
39
39
40
pub struct SimplifyCfg {
40
41
label : String ,
@@ -322,32 +323,17 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
322
323
trace ! ( "running SimplifyLocals on {:?}" , body. source) ;
323
324
324
325
// First, we're going to get a count of *actual* uses for every `Local`.
325
- // Take a look at `DeclMarker::visit_local()` to see exactly what is ignored.
326
- let mut used_locals = {
327
- let mut marker = DeclMarker :: new ( body) ;
328
- marker. visit_body ( & body) ;
329
-
330
- marker. local_counts
331
- } ;
332
-
333
- let arg_count = body. arg_count ;
326
+ let mut used_locals = UsedLocals :: new ( body) ;
334
327
335
328
// Next, we're going to remove any `Local` with zero actual uses. When we remove those
336
329
// `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
337
330
// count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from
338
331
// `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a
339
332
// fixedpoint where there are no more unused locals.
340
- loop {
341
- let mut remove_statements = RemoveStatements :: new ( & mut used_locals, arg_count, tcx) ;
342
- remove_statements. visit_body ( body) ;
343
-
344
- if !remove_statements. modified {
345
- break ;
346
- }
347
- }
333
+ remove_unused_definitions ( & mut used_locals, body) ;
348
334
349
335
// Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
350
- let map = make_local_map ( & mut body. local_decls , used_locals, arg_count ) ;
336
+ let map = make_local_map ( & mut body. local_decls , & used_locals) ;
351
337
352
338
// Only bother running the `LocalUpdater` if we actually found locals to remove.
353
339
if map. iter ( ) . any ( Option :: is_none) {
@@ -363,14 +349,14 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
363
349
/// Construct the mapping while swapping out unused stuff out from the `vec`.
364
350
fn make_local_map < V > (
365
351
local_decls : & mut IndexVec < Local , V > ,
366
- used_locals : IndexVec < Local , usize > ,
367
- arg_count : usize ,
352
+ used_locals : & UsedLocals ,
368
353
) -> IndexVec < Local , Option < Local > > {
369
354
let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , & * local_decls) ;
370
355
let mut used = Local :: new ( 0 ) ;
371
- for ( alive_index, count) in used_locals. iter_enumerated ( ) {
372
- // The `RETURN_PLACE` and arguments are always live.
373
- if alive_index. as_usize ( ) > arg_count && * count == 0 {
356
+
357
+ for alive_index in local_decls. indices ( ) {
358
+ // `is_used` treats the `RETURN_PLACE` and arguments as used.
359
+ if !used_locals. is_used ( alive_index) {
374
360
continue ;
375
361
}
376
362
@@ -384,149 +370,130 @@ fn make_local_map<V>(
384
370
map
385
371
}
386
372
387
- struct DeclMarker < ' a , ' tcx > {
388
- pub local_counts : IndexVec < Local , usize > ,
389
- pub body : & ' a Body < ' tcx > ,
373
+ /// Keeps track of used & unused locals.
374
+ struct UsedLocals {
375
+ increment : bool ,
376
+ arg_count : u32 ,
377
+ use_count : IndexVec < Local , u32 > ,
390
378
}
391
379
392
- impl < ' a , ' tcx > DeclMarker < ' a , ' tcx > {
393
- pub fn new ( body : & ' a Body < ' tcx > ) -> Self {
394
- Self { local_counts : IndexVec :: from_elem ( 0 , & body. local_decls ) , body }
380
+ impl UsedLocals {
381
+ /// Determines which locals are used & unused in the given body.
382
+ fn new ( body : & Body < ' _ > ) -> Self {
383
+ let mut this = Self {
384
+ increment : true ,
385
+ arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
386
+ use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
387
+ } ;
388
+ this. visit_body ( body) ;
389
+ this
395
390
}
396
- }
397
391
398
- impl < ' a , ' tcx > Visitor < ' tcx > for DeclMarker < ' a , ' tcx > {
399
- fn visit_local ( & mut self , local : & Local , ctx : PlaceContext , location : Location ) {
400
- // Ignore storage markers altogether, they get removed along with their otherwise unused
401
- // decls.
402
- // FIXME: Extend this to all non-uses.
403
- if ctx. is_storage_marker ( ) {
404
- return ;
405
- }
392
+ /// Checks if local is used.
393
+ ///
394
+ /// Return place and arguments are always considered used.
395
+ fn is_used ( & self , local : Local ) -> bool {
396
+ trace ! ( "is_used({:?}): use_count: {:?}" , local, self . use_count[ local] ) ;
397
+ local. as_u32 ( ) <= self . arg_count || self . use_count [ local] != 0
398
+ }
406
399
407
- // Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many
408
- // of these locals. However, if the local is still needed, then it will be referenced in
409
- // another place and we'll mark it as being used there.
410
- if ctx == PlaceContext :: MutatingUse ( MutatingUseContext :: Store )
411
- || ctx == PlaceContext :: MutatingUse ( MutatingUseContext :: Projection )
412
- {
413
- let block = & self . body . basic_blocks ( ) [ location. block ] ;
414
- if location. statement_index != block. statements . len ( ) {
415
- let stmt = & block. statements [ location. statement_index ] ;
416
-
417
- if let StatementKind :: Assign ( box ( dest, rvalue) ) = & stmt. kind {
418
- if !dest. is_indirect ( ) && dest. local == * local {
419
- let can_skip = match rvalue {
420
- Rvalue :: Use ( _)
421
- | Rvalue :: Discriminant ( _)
422
- | Rvalue :: BinaryOp ( _, _, _)
423
- | Rvalue :: CheckedBinaryOp ( _, _, _)
424
- | Rvalue :: Repeat ( _, _)
425
- | Rvalue :: AddressOf ( _, _)
426
- | Rvalue :: Len ( _)
427
- | Rvalue :: UnaryOp ( _, _)
428
- | Rvalue :: Aggregate ( _, _) => true ,
429
-
430
- _ => false ,
431
- } ;
432
-
433
- if can_skip {
434
- trace ! ( "skipping store of {:?} to {:?}" , rvalue, dest) ;
435
- return ;
436
- }
437
- }
438
- }
439
- }
440
- }
400
+ /// Updates the use counts to reflect the removal of given statement.
401
+ fn statement_removed ( & mut self , statement : & Statement < ' tcx > ) {
402
+ self . increment = false ;
441
403
442
- self . local_counts [ * local] += 1 ;
404
+ // The location of the statement is irrelevant.
405
+ let location = Location { block : START_BLOCK , statement_index : 0 } ;
406
+ self . visit_statement ( statement, location) ;
443
407
}
444
- }
445
-
446
- struct StatementDeclMarker < ' a , ' tcx > {
447
- used_locals : & ' a mut IndexVec < Local , usize > ,
448
- statement : & ' a Statement < ' tcx > ,
449
- }
450
408
451
- impl < ' a , ' tcx > StatementDeclMarker < ' a , ' tcx > {
452
- pub fn new (
453
- used_locals : & ' a mut IndexVec < Local , usize > ,
454
- statement : & ' a Statement < ' tcx > ,
455
- ) -> Self {
456
- Self { used_locals, statement }
409
+ /// Visits a left-hand side of an assignment.
410
+ fn visit_lhs ( & mut self , place : & Place < ' tcx > , location : Location ) {
411
+ if place. is_indirect ( ) {
412
+ // A use, not a definition.
413
+ self . visit_place ( place, PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) , location) ;
414
+ } else {
415
+ // A definition. Although, it still might use other locals for indexing.
416
+ self . super_projection (
417
+ place. local ,
418
+ & place. projection ,
419
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Projection ) ,
420
+ location,
421
+ ) ;
422
+ }
457
423
}
458
424
}
459
425
460
- impl < ' a , ' tcx > Visitor < ' tcx > for StatementDeclMarker < ' a , ' tcx > {
461
- fn visit_local ( & mut self , local : & Local , context : PlaceContext , _location : Location ) {
462
- // Skip the lvalue for assignments
463
- if let StatementKind :: Assign ( box ( p, _) ) = self . statement . kind {
464
- if p. local == * local && context. is_place_assignment ( ) {
465
- return ;
426
+ impl Visitor < ' _ > for UsedLocals {
427
+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
428
+ match statement. kind {
429
+ StatementKind :: LlvmInlineAsm ( ..)
430
+ | StatementKind :: Retag ( ..)
431
+ | StatementKind :: Coverage ( ..)
432
+ | StatementKind :: FakeRead ( ..)
433
+ | StatementKind :: AscribeUserType ( ..) => {
434
+ self . super_statement ( statement, location) ;
466
435
}
467
- }
468
436
469
- let use_count = & mut self . used_locals [ * local] ;
470
- // If this is the local we're removing...
471
- if * use_count != 0 {
472
- * use_count -= 1 ;
473
- }
474
- }
475
- }
437
+ StatementKind :: Nop => { }
476
438
477
- struct RemoveStatements < ' a , ' tcx > {
478
- used_locals : & ' a mut IndexVec < Local , usize > ,
479
- arg_count : usize ,
480
- tcx : TyCtxt < ' tcx > ,
481
- modified : bool ,
482
- }
439
+ StatementKind :: StorageLive ( _local) | StatementKind :: StorageDead ( _local) => { }
483
440
484
- impl < ' a , ' tcx > RemoveStatements < ' a , ' tcx > {
485
- fn new (
486
- used_locals : & ' a mut IndexVec < Local , usize > ,
487
- arg_count : usize ,
488
- tcx : TyCtxt < ' tcx > ,
489
- ) -> Self {
490
- Self { used_locals, arg_count, tcx, modified : false }
491
- }
441
+ StatementKind :: Assign ( box ( ref place, ref rvalue) ) => {
442
+ self . visit_lhs ( place, location) ;
443
+ self . visit_rvalue ( rvalue, location) ;
444
+ }
492
445
493
- fn keep_local ( & self , l : Local ) -> bool {
494
- trace ! ( "keep_local({:?}): count: {:?}" , l, self . used_locals[ l] ) ;
495
- l. as_usize ( ) <= self . arg_count || self . used_locals [ l] != 0
446
+ StatementKind :: SetDiscriminant { ref place, variant_index : _ } => {
447
+ self . visit_lhs ( place, location) ;
448
+ }
449
+ }
496
450
}
497
- }
498
451
499
- impl < ' a , ' tcx > MutVisitor < ' tcx > for RemoveStatements < ' a , ' tcx > {
500
- fn tcx ( & self ) -> TyCtxt < ' tcx > {
501
- self . tcx
452
+ fn visit_local ( & mut self , local : & Local , _ctx : PlaceContext , _location : Location ) {
453
+ if self . increment {
454
+ self . use_count [ * local] += 1 ;
455
+ } else {
456
+ assert_ne ! ( self . use_count[ * local] , 0 ) ;
457
+ self . use_count [ * local] -= 1 ;
458
+ }
502
459
}
460
+ }
503
461
504
- fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & mut BasicBlockData < ' tcx > ) {
505
- // Remove unnecessary StorageLive and StorageDead annotations.
506
- let mut i = 0usize ;
507
- data . statements . retain ( |stmt| {
508
- let keep = match & stmt . kind {
509
- StatementKind :: StorageLive ( l ) | StatementKind :: StorageDead ( l ) => {
510
- self . keep_local ( * l )
511
- }
512
- StatementKind :: Assign ( box ( place , _ ) ) => self . keep_local ( place . local ) ,
513
- _ => true ,
514
- } ;
515
-
516
- if !keep {
517
- trace ! ( "removing statement {:?}" , stmt ) ;
518
- self . modified = true ;
519
-
520
- let mut visitor = StatementDeclMarker :: new ( self . used_locals , stmt ) ;
521
- visitor . visit_statement ( stmt , Location { block , statement_index : i } ) ;
522
- }
462
+ /// Removes unused definitions. Updates the used locals to reflect the changes made.
463
+ fn remove_unused_definitions < ' a , ' tcx > ( used_locals : & ' a mut UsedLocals , body : & mut Body < ' tcx > ) {
464
+ // The use counts are updated as we remove the statements. A local might become unused
465
+ // during the retain operation, leading to a temporary inconsistency (storage statements or
466
+ // definitions referencing the local might remain). For correctness it is crucial that this
467
+ // computation reaches a fixed point.
468
+
469
+ let mut modified = true ;
470
+ while modified {
471
+ modified = false ;
472
+
473
+ for data in body . basic_blocks_mut ( ) {
474
+ // Remove unnecessary StorageLive and StorageDead annotations.
475
+ data . statements . retain ( | statement| {
476
+ let keep = match & statement . kind {
477
+ StatementKind :: StorageLive ( local ) | StatementKind :: StorageDead ( local ) => {
478
+ used_locals . is_used ( * local )
479
+ }
480
+ StatementKind :: Assign ( box ( place , _ ) ) => used_locals . is_used ( place . local ) ,
523
481
524
- i += 1 ;
482
+ StatementKind :: SetDiscriminant { ref place, .. } => {
483
+ used_locals. is_used ( place. local )
484
+ }
485
+ _ => true ,
486
+ } ;
525
487
526
- keep
527
- } ) ;
488
+ if !keep {
489
+ trace ! ( "removing statement {:?}" , statement) ;
490
+ modified = true ;
491
+ used_locals. statement_removed ( statement) ;
492
+ }
528
493
529
- self . super_basic_block_data ( block, data) ;
494
+ keep
495
+ } ) ;
496
+ }
530
497
}
531
498
}
532
499
0 commit comments