@@ -58,15 +58,19 @@ use rustc::ty::GeneratorSubsts;
5858use rustc:: ty:: layout:: VariantIdx ;
5959use rustc:: ty:: subst:: SubstsRef ;
6060use rustc_data_structures:: fx:: FxHashMap ;
61- use rustc_data_structures:: indexed_vec:: Idx ;
61+ use rustc_data_structures:: indexed_vec:: { Idx , IndexVec } ;
6262use rustc_data_structures:: bit_set:: BitSet ;
6363use std:: borrow:: Cow ;
64+ use std:: collections:: hash_map;
6465use std:: iter;
6566use std:: mem;
6667use crate :: transform:: { MirPass , MirSource } ;
6768use crate :: transform:: simplify;
6869use crate :: transform:: no_landing_pads:: no_landing_pads;
69- use crate :: dataflow:: { do_dataflow, DebugFormatted , state_for_location} ;
70+ use crate :: dataflow:: { DataflowResults } ;
71+ //use crate::dataflow::{DataflowResults, DataflowResultsConsumer};
72+ //use crate::dataflow::FlowAtLocation;
73+ use crate :: dataflow:: { do_dataflow, DebugFormatted , state_for_location, for_each_location} ;
7074use crate :: dataflow:: { MaybeStorageLive , HaveBeenBorrowedLocals } ;
7175use crate :: util:: dump_mir;
7276use crate :: util:: liveness;
@@ -392,6 +396,8 @@ fn locals_live_across_suspend_points(
392396 source : MirSource < ' tcx > ,
393397 movable : bool ,
394398) -> (
399+ liveness:: LiveVarSet ,
400+ FxHashMap < BasicBlock , liveness:: LiveVarSet > ,
395401 liveness:: LiveVarSet ,
396402 FxHashMap < BasicBlock , liveness:: LiveVarSet > ,
397403 BitSet < BasicBlock > ,
@@ -496,7 +502,123 @@ fn locals_live_across_suspend_points(
496502 // The generator argument is ignored
497503 set. remove ( self_arg ( ) ) ;
498504
499- ( set, storage_liveness_map, suspending_blocks)
505+ let ( variant_assignments, prefix_locals) = locals_eligible_for_overlap (
506+ mir,
507+ & set,
508+ & storage_liveness_map,
509+ & ignored,
510+ storage_live,
511+ storage_live_analysis) ;
512+
513+ ( set, variant_assignments, prefix_locals, storage_liveness_map, suspending_blocks)
514+ }
515+
516+ fn locals_eligible_for_overlap (
517+ mir : & ' mir Mir < ' tcx > ,
518+ stored_locals : & liveness:: LiveVarSet ,
519+ storage_liveness_map : & FxHashMap < BasicBlock , liveness:: LiveVarSet > ,
520+ ignored : & StorageIgnored ,
521+ storage_live : DataflowResults < ' tcx , MaybeStorageLive < ' mir , ' tcx > > ,
522+ storage_live_analysis : MaybeStorageLive < ' mir , ' tcx > ,
523+ ) -> ( FxHashMap < BasicBlock , liveness:: LiveVarSet > , liveness:: LiveVarSet ) {
524+ debug ! ( "locals_eligible_for_overlap({:?})" , mir. span) ;
525+ debug ! ( "ignored = {:?}" , ignored. 0 ) ;
526+
527+ let mut candidate_locals = stored_locals. clone ( ) ;
528+ // Storage ignored locals are not candidates, since their storage is always
529+ // live. Remove these.
530+ candidate_locals. subtract ( & ignored. 0 ) ;
531+
532+ let mut eligible_locals = candidate_locals. clone ( ) ;
533+ debug ! ( "eligible_locals = {:?}" , eligible_locals) ;
534+
535+ // Figure out which of our candidate locals are storage live across only one
536+ // suspension point. There will be an entry for each candidate (`None` if it
537+ // is live across more than one).
538+ let mut variants_map: FxHashMap < Local , Option < BasicBlock > > = FxHashMap :: default ( ) ;
539+ for ( block, locals) in storage_liveness_map {
540+ let mut live_locals = candidate_locals. clone ( ) ;
541+ live_locals. intersect ( & locals) ;
542+ for local in live_locals. iter ( ) {
543+ match variants_map. entry ( local) {
544+ hash_map:: Entry :: Occupied ( mut entry) => {
545+ // We've already seen this local at another suspension
546+ // point, so it is no longer a candidate.
547+ debug ! ( "removing local {:?} with live storage across >1 yield ({:?}, {:?})" ,
548+ local, block, entry. get( ) ) ;
549+ * entry. get_mut ( ) = None ;
550+ eligible_locals. remove ( local) ;
551+ }
552+ hash_map:: Entry :: Vacant ( entry) => {
553+ entry. insert ( Some ( * block) ) ;
554+ }
555+ }
556+ }
557+ }
558+
559+ // Of our remaining candidates, find out if any have overlapping storage
560+ // liveness. Those that do must be in the same variant to remain candidates.
561+ // TODO revisit data structures here. should this be a sparse map? what
562+ // about the bitset?
563+ let mut local_overlaps: IndexVec < Local , _ > =
564+ iter:: repeat ( liveness:: LiveVarSet :: new_empty ( mir. local_decls . len ( ) ) )
565+ . take ( mir. local_decls . len ( ) )
566+ . collect ( ) ;
567+
568+ for block in mir. basic_blocks ( ) . indices ( ) {
569+ for_each_location ( block, & storage_live_analysis, & storage_live, mir, |state, loc| {
570+ let mut eligible_storage_live = state. clone ( ) . to_dense ( ) ;
571+ eligible_storage_live. intersect ( & eligible_locals) ;
572+
573+ for local in eligible_storage_live. iter ( ) {
574+ let mut overlaps = eligible_storage_live. clone ( ) ;
575+ overlaps. remove ( local) ;
576+ local_overlaps[ local] . union ( & overlaps) ;
577+
578+ if !overlaps. is_empty ( ) {
579+ trace ! ( "local {:?} overlaps with these locals at {:?}: {:?}" ,
580+ local, loc, overlaps) ;
581+ }
582+ }
583+ } ) ;
584+ }
585+
586+ for ( local, overlaps) in local_overlaps. iter_enumerated ( ) {
587+ // This local may have been deemed ineligible already.
588+ if !eligible_locals. contains ( local) {
589+ continue ;
590+ }
591+
592+ for other_local in overlaps. iter ( ) {
593+ // local and other_local have overlapping storage, therefore they
594+ // cannot overlap in the generator layout. The only way to guarantee
595+ // this is if they are in the same variant, or one is ineligible
596+ // (which means it is stored in every variant).
597+ if eligible_locals. contains ( other_local) &&
598+ variants_map[ & local] != variants_map[ & other_local] {
599+ // We arbitrarily choose other_local, which will be the higher
600+ // indexed of the two locals, to remove.
601+ eligible_locals. remove ( other_local) ;
602+ debug ! ( "removing local {:?} due to overlap" , other_local) ;
603+ }
604+ }
605+ }
606+
607+ let mut assignments = FxHashMap :: default ( ) ;
608+ for local in eligible_locals. iter ( ) {
609+ assignments
610+ . entry ( variants_map[ & local] . unwrap ( ) )
611+ . or_insert ( liveness:: LiveVarSet :: new_empty ( mir. local_decls . len ( ) ) )
612+ . insert ( local) ;
613+ }
614+
615+ let mut ineligible_locals = stored_locals. clone ( ) ;
616+ ineligible_locals. subtract ( & eligible_locals) ;
617+
618+ debug ! ( "locals_eligible_for_overlap() => (assignments: {:?}, eligible_locals: {:?}" ,
619+ assignments, eligible_locals) ;
620+
621+ ( assignments, ineligible_locals)
500622}
501623
502624fn compute_layout < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
@@ -510,7 +632,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
510632 FxHashMap < BasicBlock , liveness:: LiveVarSet > )
511633{
512634 // Use a liveness analysis to compute locals which are live across a suspension point
513- let ( live_locals, storage_liveness, suspending_blocks) =
635+ let ( live_locals, variant_assignments , prefix_locals , storage_liveness, suspending_blocks) =
514636 locals_live_across_suspend_points ( tcx, mir, source, movable) ;
515637
516638 // Erase regions from the types passed in from typeck so we can compare them with
@@ -539,29 +661,37 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
539661 }
540662
541663 let dummy_local = LocalDecl :: new_internal ( tcx. mk_unit ( ) , mir. span ) ;
542-
543- // Gather live locals and their indices replacing values in mir.local_decls with a dummy
544- // to avoid changing local indices
545- let live_decls = live_locals. iter ( ) . map ( |local| {
546- let var = mem:: replace ( & mut mir. local_decls [ local] , dummy_local. clone ( ) ) ;
547- ( local, var)
664+ let mut remap = FxHashMap :: default ( ) ;
665+
666+ const INITIAL_VARIANTS : usize = 3 ;
667+ let prefix_variant = VariantIdx :: new ( INITIAL_VARIANTS ) ;
668+ let prefix_fields = prefix_locals. iter ( ) . enumerate ( ) . map ( |( idx, local) | {
669+ // Gather LocalDecls of live locals replacing values in mir.local_decls
670+ // with a dummy to avoid changing local indices.
671+ let field = mem:: replace ( & mut mir. local_decls [ local] , dummy_local. clone ( ) ) ;
672+ remap. insert ( local, ( field. ty , prefix_variant, idx) ) ;
673+ field
674+ } )
675+ . collect :: < Vec < _ > > ( ) ;
676+
677+ let state_variants = suspending_blocks. iter ( ) . enumerate ( ) . map ( |( variant, block) | {
678+ let variant_index = VariantIdx :: new ( INITIAL_VARIANTS + variant) ;
679+ let no_assignments = & liveness:: LiveVarSet :: new_empty ( 0 ) ;
680+ let variant_locals = variant_assignments. get ( & block)
681+ . unwrap_or ( no_assignments)
682+ . iter ( ) . enumerate ( ) . map ( |( idx, local) | {
683+ // Gather LocalDecls of live locals replacing values in
684+ // mir.local_decls with a dummy to avoid changing local indices.
685+ let field = mem:: replace ( & mut mir. local_decls [ local] , dummy_local. clone ( ) ) ;
686+ // These fields follow the prefix fields.
687+ remap. insert ( local, ( field. ty , variant_index, prefix_fields. len ( ) + idx) ) ;
688+ field
689+ } ) ;
690+ prefix_fields. iter ( ) . cloned ( ) . chain ( variant_locals) . collect :: < Vec < _ > > ( )
548691 } ) ;
549692
550- // For now we will access everything via variant #3, leaving empty variants
551- // for the UNRESUMED, RETURNED, and POISONED states.
552- // If there were a yield-less generator without a variant #3, it would not
553- // have any vars to remap, so we would never use this.
554- let variant_index = VariantIdx :: new ( 3 ) ;
555-
556- // Create a map from local indices to generator struct indices.
557- // We also create a vector of the LocalDecls of these locals.
558- let ( remap, vars) = live_decls. enumerate ( ) . map ( |( idx, ( local, var) ) | {
559- ( ( local, ( var. ty , variant_index, idx) ) , var)
560- } ) . unzip ( ) ;
561-
562693 // Put every var in each variant, for now.
563694 let empty_variants = iter:: repeat ( vec ! [ ] ) . take ( 3 ) ;
564- let state_variants = iter:: repeat ( vars) . take ( suspending_blocks. count ( ) ) ;
565695 let layout = GeneratorLayout {
566696 variant_fields : empty_variants. chain ( state_variants) . collect ( )
567697 } ;
0 commit comments