@@ -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,23 +323,15 @@ 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
333
loop {
341
- let mut remove_statements = RemoveStatements :: new ( & mut used_locals, arg_count , tcx) ;
334
+ let mut remove_statements = RemoveStatements :: new ( & mut used_locals, tcx) ;
342
335
remove_statements. visit_body ( body) ;
343
336
344
337
if !remove_statements. modified {
@@ -347,7 +340,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
347
340
}
348
341
349
342
// 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 ) ;
343
+ let map = make_local_map ( & mut body. local_decls , & used_locals) ;
351
344
352
345
// Only bother running the `LocalUpdater` if we actually found locals to remove.
353
346
if map. iter ( ) . any ( Option :: is_none) {
@@ -363,14 +356,14 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
363
356
/// Construct the mapping while swapping out unused stuff out from the `vec`.
364
357
fn make_local_map < V > (
365
358
local_decls : & mut IndexVec < Local , V > ,
366
- used_locals : IndexVec < Local , u32 > ,
367
- arg_count : usize ,
359
+ used_locals : & UsedLocals ,
368
360
) -> IndexVec < Local , Option < Local > > {
369
361
let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , & * local_decls) ;
370
362
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 {
363
+
364
+ for alive_index in local_decls. indices ( ) {
365
+ // `is_used` treats the `RETURN_PLACE` and arguments as used.
366
+ if !used_locals. is_used ( alive_index) {
374
367
continue ;
375
368
}
376
369
@@ -384,115 +377,125 @@ fn make_local_map<V>(
384
377
map
385
378
}
386
379
387
- struct DeclMarker < ' a , ' tcx > {
388
- pub local_counts : IndexVec < Local , u32 > ,
389
- pub body : & ' a Body < ' tcx > ,
380
+ /// Keeps track of used & unused locals.
381
+ struct UsedLocals {
382
+ increment : bool ,
383
+ arg_count : u32 ,
384
+ use_count : IndexVec < Local , u32 > ,
390
385
}
391
386
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 }
387
+ impl UsedLocals {
388
+ /// Determines which locals are used & unused in the given body.
389
+ fn new ( body : & Body < ' _ > ) -> Self {
390
+ let mut this = Self {
391
+ increment : true ,
392
+ arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
393
+ use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
394
+ } ;
395
+ this. visit_body ( body) ;
396
+ this
395
397
}
396
- }
397
398
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
- }
399
+ /// Checks if local is used.
400
+ ///
401
+ /// Return place and arguments are always considered used.
402
+ fn is_used ( & self , local : Local ) -> bool {
403
+ trace ! ( "is_used({:?}): use_count: {:?}" , local, self . use_count[ local] ) ;
404
+ local. as_u32 ( ) <= self . arg_count || self . use_count [ local] != 0
405
+ }
406
406
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
- }
407
+ /// Updates the use counts to reflect the removal of given statement.
408
+ fn statement_removed ( & mut self , statement : & Statement < ' tcx > ) {
409
+ self . increment = false ;
441
410
442
- self . local_counts [ * local] += 1 ;
411
+ // The location of the statement is irrelevant.
412
+ let location = Location { block : START_BLOCK , statement_index : 0 } ;
413
+ self . visit_statement ( statement, location) ;
443
414
}
444
- }
445
-
446
- struct StatementDeclMarker < ' a , ' tcx > {
447
- used_locals : & ' a mut IndexVec < Local , u32 > ,
448
- statement : & ' a Statement < ' tcx > ,
449
- }
450
415
451
- impl < ' a , ' tcx > StatementDeclMarker < ' a , ' tcx > {
452
- pub fn new (
453
- used_locals : & ' a mut IndexVec < Local , u32 > ,
454
- statement : & ' a Statement < ' tcx > ,
455
- ) -> Self {
456
- Self { used_locals, statement }
416
+ /// Visits a left-hand side of an assignment.
417
+ fn visit_lhs ( & mut self , place : & Place < ' tcx > , location : Location ) {
418
+ if place. is_indirect ( ) {
419
+ // A use, not a definition.
420
+ self . visit_place ( place, PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) , location) ;
421
+ } else {
422
+ // A definition. Although, it still might use other locals for indexing.
423
+ self . super_projection (
424
+ place. local ,
425
+ & place. projection ,
426
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Projection ) ,
427
+ location,
428
+ ) ;
429
+ }
457
430
}
458
431
}
459
432
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 ;
433
+ impl Visitor < ' _ > for UsedLocals {
434
+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
435
+ match statement. kind {
436
+ StatementKind :: LlvmInlineAsm ( ..)
437
+ | StatementKind :: SetDiscriminant { .. } // FIXME: Try to remove those as well.
438
+ | StatementKind :: Retag ( ..)
439
+ | StatementKind :: Coverage ( ..)
440
+ | StatementKind :: FakeRead ( ..)
441
+ | StatementKind :: AscribeUserType ( ..) => {
442
+ self . super_statement ( statement, location) ;
443
+ }
444
+
445
+ StatementKind :: Nop => { }
446
+
447
+ StatementKind :: StorageLive ( _local) | StatementKind :: StorageDead ( _local) => { }
448
+
449
+ StatementKind :: Assign ( box ( ref place, ref rvalue) ) => {
450
+ let can_skip = match rvalue {
451
+ Rvalue :: Use ( _)
452
+ | Rvalue :: Discriminant ( _)
453
+ | Rvalue :: BinaryOp ( _, _, _)
454
+ | Rvalue :: CheckedBinaryOp ( _, _, _)
455
+ | Rvalue :: Repeat ( _, _)
456
+ | Rvalue :: AddressOf ( _, _)
457
+ | Rvalue :: Len ( _)
458
+ | Rvalue :: UnaryOp ( _, _)
459
+ | Rvalue :: Aggregate ( _, _) => true ,
460
+
461
+ Rvalue :: Ref ( ..)
462
+ | Rvalue :: ThreadLocalRef ( ..)
463
+ | Rvalue :: Cast ( ..)
464
+ | Rvalue :: NullaryOp ( ..) => false ,
465
+ } ;
466
+ if can_skip {
467
+ self . visit_lhs ( place, location) ;
468
+ } else {
469
+ self . visit_place (
470
+ place,
471
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
472
+ location,
473
+ ) ;
474
+ }
475
+ self . visit_rvalue ( rvalue, location) ;
466
476
}
467
477
}
478
+ }
468
479
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 ;
480
+ fn visit_local ( & mut self , local : & Local , _ctx : PlaceContext , _location : Location ) {
481
+ if self . increment {
482
+ self . use_count [ * local] += 1 ;
483
+ } else {
484
+ assert_ne ! ( self . use_count[ * local] , 0 ) ;
485
+ self . use_count [ * local] -= 1 ;
473
486
}
474
487
}
475
488
}
476
489
477
490
struct RemoveStatements < ' a , ' tcx > {
478
- used_locals : & ' a mut IndexVec < Local , u32 > ,
479
- arg_count : usize ,
491
+ used_locals : & ' a mut UsedLocals ,
480
492
tcx : TyCtxt < ' tcx > ,
481
493
modified : bool ,
482
494
}
483
495
484
496
impl < ' a , ' tcx > RemoveStatements < ' a , ' tcx > {
485
- fn new (
486
- used_locals : & ' a mut IndexVec < Local , u32 > ,
487
- arg_count : usize ,
488
- tcx : TyCtxt < ' tcx > ,
489
- ) -> Self {
490
- Self { used_locals, arg_count, tcx, modified : false }
491
- }
492
-
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
497
+ fn new ( used_locals : & ' a mut UsedLocals , tcx : TyCtxt < ' tcx > ) -> Self {
498
+ Self { used_locals, tcx, modified : false }
496
499
}
497
500
}
498
501
@@ -503,26 +506,21 @@ impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> {
503
506
504
507
fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & mut BasicBlockData < ' tcx > ) {
505
508
// 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)
509
+ data. statements . retain ( |statement| {
510
+ let keep = match & statement. kind {
511
+ StatementKind :: StorageLive ( local) | StatementKind :: StorageDead ( local) => {
512
+ self . used_locals . is_used ( * local)
511
513
}
512
- StatementKind :: Assign ( box ( place, _) ) => self . keep_local ( place. local ) ,
514
+ StatementKind :: Assign ( box ( place, _) ) => self . used_locals . is_used ( place. local ) ,
513
515
_ => true ,
514
516
} ;
515
517
516
518
if !keep {
517
- trace ! ( "removing statement {:?}" , stmt ) ;
519
+ trace ! ( "removing statement {:?}" , statement ) ;
518
520
self . modified = true ;
519
-
520
- let mut visitor = StatementDeclMarker :: new ( self . used_locals , stmt) ;
521
- visitor. visit_statement ( stmt, Location { block, statement_index : i } ) ;
521
+ self . used_locals . statement_removed ( statement) ;
522
522
}
523
523
524
- i += 1 ;
525
-
526
524
keep
527
525
} ) ;
528
526
0 commit comments