@@ -306,49 +306,74 @@ pub struct SimplifyLocals;
306306impl < ' tcx > MirPass < ' tcx > for SimplifyLocals {
307307 fn run_pass ( & self , tcx : TyCtxt < ' tcx > , source : MirSource < ' tcx > , body : & mut BodyAndCache < ' tcx > ) {
308308 trace ! ( "running SimplifyLocals on {:?}" , source) ;
309- let locals = {
309+
310+ let mut used_locals = {
310311 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) ;
312313 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- }
318314
319- marker. locals
315+ marker. local_counts
320316 } ;
321317
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+ }
326339 }
327340}
328341
329342/// Construct the mapping while swapping out unused stuff out from the `vec`.
330343fn 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 ,
333347) -> 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 ) ;
335349 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+
337356 map[ alive_index] = Some ( used) ;
338357 if alive_index != used {
339- vec . swap ( alive_index, used) ;
358+ local_decls . swap ( alive_index, used) ;
340359 }
341360 used. increment_by ( 1 ) ;
342361 }
343- vec . truncate ( used. index ( ) ) ;
362+ local_decls . truncate ( used. index ( ) ) ;
344363 map
345364}
346365
347366struct DeclMarker < ' a , ' tcx > {
348- pub locals : BitSet < Local > ,
367+ pub local_counts : IndexVec < Local , usize > ,
349368 pub body : & ' a Body < ' tcx > ,
350369}
351370
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+
352377impl < ' a , ' tcx > Visitor < ' tcx > for DeclMarker < ' a , ' tcx > {
353378 fn visit_local ( & mut self , local : & Local , ctx : PlaceContext , location : Location ) {
354379 // 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> {
408433 }
409434 }
410435
411- self . locals . insert ( * local) ;
436+ self . local_counts [ * local] += 1 ;
412437 }
413438}
414439
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 ,
417467 tcx : TyCtxt < ' tcx > ,
468+ modified : bool ,
418469}
419470
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 > {
421487 fn tcx ( & self ) -> TyCtxt < ' tcx > {
422488 self . tcx
423489 }
424490
425491 fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & mut BasicBlockData < ' tcx > ) {
426492 // 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
431523 } ) ;
524+
432525 self . super_basic_block_data ( block, data) ;
433526 }
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+ }
434538
435539 fn visit_local ( & mut self , l : & mut Local , _: PlaceContext , _: Location ) {
436540 * l = self . map [ * l] . unwrap ( ) ;
0 commit comments