@@ -306,49 +306,74 @@ pub struct SimplifyLocals;
306
306
impl < ' tcx > MirPass < ' tcx > for SimplifyLocals {
307
307
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , source : MirSource < ' tcx > , body : & mut BodyAndCache < ' tcx > ) {
308
308
trace ! ( "running SimplifyLocals on {:?}" , source) ;
309
- let locals = {
309
+
310
+ let mut used_locals = {
310
311
let read_only_cache = read_only ! ( body) ;
311
- let mut marker = DeclMarker { locals : BitSet :: new_empty ( body. local_decls . len ( ) ) , body } ;
312
+ let mut marker = DeclMarker :: new ( body) ;
312
313
marker. visit_body ( & read_only_cache) ;
313
- // Return pointer and arguments are always live
314
- marker. locals . insert ( RETURN_PLACE ) ;
315
- for arg in body. args_iter ( ) {
316
- marker. locals . insert ( arg) ;
317
- }
318
314
319
- marker. locals
315
+ marker. local_counts
320
316
} ;
321
317
322
- let map = make_local_map ( & mut body. local_decls , locals) ;
323
- // Update references to all vars and tmps now
324
- LocalUpdater { map, tcx } . visit_body ( body) ;
325
- body. local_decls . shrink_to_fit ( ) ;
318
+ let arg_count = body. arg_count ;
319
+
320
+ loop {
321
+ let mut remove_statements = RemoveStatements :: new ( & mut used_locals, arg_count, tcx) ;
322
+ remove_statements. visit_body ( body) ;
323
+
324
+ if !remove_statements. modified {
325
+ break ;
326
+ }
327
+ }
328
+
329
+ let map = make_local_map ( & mut body. local_decls , used_locals, arg_count) ;
330
+
331
+ // Only bother running the `LocalUpdater` if we actually found locals to remove.
332
+ if map. iter ( ) . any ( Option :: is_none) {
333
+ // Update references to all vars and tmps now
334
+ let mut updater = LocalUpdater { map, tcx } ;
335
+ updater. visit_body ( body) ;
336
+
337
+ body. local_decls . shrink_to_fit ( ) ;
338
+ }
326
339
}
327
340
}
328
341
329
342
/// Construct the mapping while swapping out unused stuff out from the `vec`.
330
343
fn make_local_map < V > (
331
- vec : & mut IndexVec < Local , V > ,
332
- mask : BitSet < Local > ,
344
+ local_decls : & mut IndexVec < Local , V > ,
345
+ used_locals : IndexVec < Local , usize > ,
346
+ arg_count : usize ,
333
347
) -> IndexVec < Local , Option < Local > > {
334
- let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , & * vec ) ;
348
+ let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , & * local_decls ) ;
335
349
let mut used = Local :: new ( 0 ) ;
336
- for alive_index in mask. iter ( ) {
350
+ for ( alive_index, count) in used_locals. iter_enumerated ( ) {
351
+ // The `RETURN_PLACE` and arguments are always live.
352
+ if alive_index. as_usize ( ) > arg_count && * count == 0 {
353
+ continue ;
354
+ }
355
+
337
356
map[ alive_index] = Some ( used) ;
338
357
if alive_index != used {
339
- vec . swap ( alive_index, used) ;
358
+ local_decls . swap ( alive_index, used) ;
340
359
}
341
360
used. increment_by ( 1 ) ;
342
361
}
343
- vec . truncate ( used. index ( ) ) ;
362
+ local_decls . truncate ( used. index ( ) ) ;
344
363
map
345
364
}
346
365
347
366
struct DeclMarker < ' a , ' tcx > {
348
- pub locals : BitSet < Local > ,
367
+ pub local_counts : IndexVec < Local , usize > ,
349
368
pub body : & ' a Body < ' tcx > ,
350
369
}
351
370
371
+ impl < ' a , ' tcx > DeclMarker < ' a , ' tcx > {
372
+ pub fn new ( body : & ' a Body < ' tcx > ) -> Self {
373
+ Self { local_counts : IndexVec :: from_elem ( 0 , & body. local_decls ) , body }
374
+ }
375
+ }
376
+
352
377
impl < ' a , ' tcx > Visitor < ' tcx > for DeclMarker < ' a , ' tcx > {
353
378
fn visit_local ( & mut self , local : & Local , ctx : PlaceContext , location : Location ) {
354
379
// Ignore storage markers altogether, they get removed along with their otherwise unused
@@ -408,29 +433,108 @@ impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
408
433
}
409
434
}
410
435
411
- self . locals . insert ( * local) ;
436
+ self . local_counts [ * local] += 1 ;
412
437
}
413
438
}
414
439
415
- struct LocalUpdater < ' tcx > {
416
- map : IndexVec < Local , Option < Local > > ,
440
+ struct StatementDeclMarker < ' a , ' tcx > {
441
+ used_locals : IndexVec < Local , usize > ,
442
+ statement : & ' a Statement < ' tcx > ,
443
+ }
444
+
445
+ impl < ' a , ' tcx > StatementDeclMarker < ' a , ' tcx > {
446
+ pub fn new ( local_count : usize , statement : & ' a Statement < ' tcx > ) -> Self {
447
+ Self { used_locals : IndexVec :: from_elem_n ( 0 , local_count) , statement }
448
+ }
449
+ }
450
+
451
+ impl < ' a , ' tcx > Visitor < ' tcx > for StatementDeclMarker < ' a , ' tcx > {
452
+ fn visit_local ( & mut self , local : & Local , context : PlaceContext , _location : Location ) {
453
+ // Skip the lvalue for assignments
454
+ if let StatementKind :: Assign ( box ( p, _) ) = self . statement . kind {
455
+ if p. local == * local && context. is_place_assignment ( ) {
456
+ return ;
457
+ }
458
+ }
459
+
460
+ self . used_locals [ * local] += 1 ;
461
+ }
462
+ }
463
+
464
+ struct RemoveStatements < ' a , ' tcx > {
465
+ used_locals : & ' a mut IndexVec < Local , usize > ,
466
+ arg_count : usize ,
417
467
tcx : TyCtxt < ' tcx > ,
468
+ modified : bool ,
418
469
}
419
470
420
- impl < ' tcx > MutVisitor < ' tcx > for LocalUpdater < ' tcx > {
471
+ impl < ' a , ' tcx > RemoveStatements < ' a , ' tcx > {
472
+ fn new (
473
+ used_locals : & ' a mut IndexVec < Local , usize > ,
474
+ arg_count : usize ,
475
+ tcx : TyCtxt < ' tcx > ,
476
+ ) -> Self {
477
+ Self { used_locals, arg_count, tcx, modified : false }
478
+ }
479
+
480
+ fn keep_local ( & self , l : Local ) -> bool {
481
+ trace ! ( "keep_local({:?}): count: {:?}" , l, self . used_locals[ l] ) ;
482
+ l. as_usize ( ) <= self . arg_count || self . used_locals [ l] != 0
483
+ }
484
+ }
485
+
486
+ impl < ' a , ' tcx > MutVisitor < ' tcx > for RemoveStatements < ' a , ' tcx > {
421
487
fn tcx ( & self ) -> TyCtxt < ' tcx > {
422
488
self . tcx
423
489
}
424
490
425
491
fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & mut BasicBlockData < ' tcx > ) {
426
492
// Remove unnecessary StorageLive and StorageDead annotations.
427
- data. statements . retain ( |stmt| match & stmt. kind {
428
- StatementKind :: StorageLive ( l) | StatementKind :: StorageDead ( l) => self . map [ * l] . is_some ( ) ,
429
- StatementKind :: Assign ( box ( place, _) ) => self . map [ place. local ] . is_some ( ) ,
430
- _ => true ,
493
+ let mut i = 0usize ;
494
+ data. statements . retain ( |stmt| {
495
+ let keep = match & stmt. kind {
496
+ StatementKind :: StorageLive ( l) | StatementKind :: StorageDead ( l) => {
497
+ self . keep_local ( * l)
498
+ }
499
+ StatementKind :: Assign ( box ( place, _) ) => self . keep_local ( place. local ) ,
500
+ _ => true ,
501
+ } ;
502
+
503
+ if !keep {
504
+ trace ! ( "removing statement {:?}" , stmt) ;
505
+ self . modified = true ;
506
+
507
+ let mut visitor = StatementDeclMarker :: new ( self . used_locals . len ( ) , stmt) ;
508
+ visitor. visit_statement ( stmt, Location { block, statement_index : i } ) ;
509
+
510
+ for ( local, count) in visitor. used_locals . iter_enumerated ( ) {
511
+ let used_count = & mut self . used_locals [ local] ;
512
+
513
+ // If this is the local we're removing...
514
+ if * used_count != 0 {
515
+ * used_count -= count;
516
+ }
517
+ }
518
+ }
519
+
520
+ i += 1 ;
521
+
522
+ keep
431
523
} ) ;
524
+
432
525
self . super_basic_block_data ( block, data) ;
433
526
}
527
+ }
528
+
529
+ struct LocalUpdater < ' tcx > {
530
+ map : IndexVec < Local , Option < Local > > ,
531
+ tcx : TyCtxt < ' tcx > ,
532
+ }
533
+
534
+ impl < ' tcx > MutVisitor < ' tcx > for LocalUpdater < ' tcx > {
535
+ fn tcx ( & self ) -> TyCtxt < ' tcx > {
536
+ self . tcx
537
+ }
434
538
435
539
fn visit_local ( & mut self , l : & mut Local , _: PlaceContext , _: Location ) {
436
540
* l = self . map [ * l] . unwrap ( ) ;
0 commit comments