@@ -66,7 +66,8 @@ use std::mem;
6666use crate :: transform:: { MirPass , MirSource } ;
6767use crate :: transform:: simplify;
6868use crate :: transform:: no_landing_pads:: no_landing_pads;
69- use crate :: dataflow:: { do_dataflow, DebugFormatted , state_for_location} ;
69+ use crate :: dataflow:: { DataflowResults } ;
70+ use crate :: dataflow:: { do_dataflow, DebugFormatted , state_for_location, for_each_location} ;
7071use crate :: dataflow:: { MaybeStorageLive , HaveBeenBorrowedLocals } ;
7172use crate :: util:: dump_mir;
7273use crate :: util:: liveness;
@@ -400,6 +401,7 @@ fn locals_live_across_suspend_points(
400401 movable : bool ,
401402) -> (
402403 liveness:: LiveVarSet ,
404+ IndexVec < GeneratorSavedLocal , BitSet < GeneratorSavedLocal > > ,
403405 FxHashMap < BasicBlock , liveness:: LiveVarSet > ,
404406 BitSet < BasicBlock > ,
405407) {
@@ -503,7 +505,99 @@ fn locals_live_across_suspend_points(
503505 // The generator argument is ignored
504506 set. remove ( self_arg ( ) ) ;
505507
506- ( set, storage_liveness_map, suspending_blocks)
508+ let storage_conflicts = compute_storage_conflicts (
509+ body,
510+ & set,
511+ & ignored,
512+ storage_live,
513+ storage_live_analysis) ;
514+
515+ ( set, storage_conflicts, storage_liveness_map, suspending_blocks)
516+ }
517+
518+ /// For every saved local, looks for which locals are StorageLive at the same
519+ /// time. Generates a bitset for every local of all the other locals that may be
520+ /// StorageLive simultaneously with that local. This is used in the layout
521+ /// computation; see `GeneratorLayout` for more.
522+ fn compute_storage_conflicts (
523+ body : & ' mir Body < ' tcx > ,
524+ stored_locals : & liveness:: LiveVarSet ,
525+ ignored : & StorageIgnored ,
526+ storage_live : DataflowResults < ' tcx , MaybeStorageLive < ' mir , ' tcx > > ,
527+ storage_live_analysis : MaybeStorageLive < ' mir , ' tcx > ,
528+ ) -> IndexVec < GeneratorSavedLocal , BitSet < GeneratorSavedLocal > > {
529+ debug ! ( "compute_storage_conflicts({:?})" , body. span) ;
530+ debug ! ( "ignored = {:?}" , ignored. 0 ) ;
531+
532+ // Storage ignored locals are not eligible for overlap, since their storage
533+ // is always live.
534+ let mut ineligible_locals = ignored. 0 . clone ( ) ;
535+ ineligible_locals. intersect ( & stored_locals) ;
536+
537+ // Of our remaining candidates, find out if any have overlapping storage
538+ // liveness. Those that do must be in the same variant to remain candidates.
539+ // FIXME(tmandry): Consider using sparse bitsets here once we have good
540+ // benchmarks for generators.
541+ let mut local_conflicts: IndexVec < Local , _ > =
542+ // Add conflicts for every ineligible local.
543+ iter:: repeat ( ineligible_locals. clone ( ) )
544+ . take ( body. local_decls . len ( ) )
545+ . collect ( ) ;
546+
547+ for_each_location ( body, & storage_live_analysis, & storage_live, |state, loc| {
548+ let mut eligible_storage_live = state. clone ( ) . to_dense ( ) ;
549+ eligible_storage_live. intersect ( & stored_locals) ;
550+
551+ for local in eligible_storage_live. iter ( ) {
552+ let mut overlaps = eligible_storage_live. clone ( ) ;
553+ overlaps. remove ( local) ;
554+ local_conflicts[ local] . union ( & overlaps) ;
555+
556+ if !overlaps. is_empty ( ) {
557+ trace ! ( "at {:?}, local {:?} conflicts with {:?}" ,
558+ loc, local, overlaps) ;
559+ }
560+ }
561+ } ) ;
562+
563+ // NOTE: Today we store a full conflict bitset for every local. Technically
564+ // this is twice as many bits as we need, since the relation is symmetric.
565+ // However, in practice these bitsets are not usually large. The layout code
566+ // also needs to keep track of how many conflicts each local has, so it's
567+ // simpler to keep it this way for now.
568+ let storage_conflicts: IndexVec < GeneratorSavedLocal , _ > = stored_locals
569+ . iter ( )
570+ . map ( |local_a| {
571+ if ineligible_locals. contains ( local_a) {
572+ // Conflicts with everything.
573+ BitSet :: new_filled ( stored_locals. count ( ) )
574+ } else {
575+ // Keep overlap information only for stored locals.
576+ renumber_bitset ( & local_conflicts[ local_a] , stored_locals)
577+ }
578+ } )
579+ . collect ( ) ;
580+
581+ storage_conflicts
582+ }
583+
584+ /// Renumbers the items present in `stored_locals` and applies the renumbering
585+ /// to 'input`.
586+ ///
587+ /// For example, if `stored_locals = [1, 3, 5]`, this would be renumbered to
588+ /// `[0, 1, 2]`. Thus, if `input = [3, 5]` we would return `[1, 2]`.
589+ fn renumber_bitset ( input : & BitSet < Local > , stored_locals : & liveness:: LiveVarSet )
590+ -> BitSet < GeneratorSavedLocal > {
591+ assert ! ( stored_locals. superset( & input) , "{:?} not a superset of {:?}" , stored_locals, input) ;
592+ let mut out = BitSet :: new_empty ( stored_locals. count ( ) ) ;
593+ for ( idx, local) in stored_locals. iter ( ) . enumerate ( ) {
594+ let saved_local = GeneratorSavedLocal :: from ( idx) ;
595+ if input. contains ( local) {
596+ out. insert ( saved_local) ;
597+ }
598+ }
599+ debug ! ( "renumber_bitset({:?}, {:?}) => {:?}" , input, stored_locals, out) ;
600+ out
507601}
508602
509603fn compute_layout < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
@@ -517,7 +611,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
517611 FxHashMap < BasicBlock , liveness:: LiveVarSet > )
518612{
519613 // Use a liveness analysis to compute locals which are live across a suspension point
520- let ( live_locals, storage_liveness, suspending_blocks) =
614+ let ( live_locals, storage_conflicts , storage_liveness, suspending_blocks) =
521615 locals_live_across_suspend_points ( tcx, body, source, movable) ;
522616
523617 // Erase regions from the types passed in from typeck so we can compare them with
@@ -547,7 +641,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
547641
548642 let dummy_local = LocalDecl :: new_internal ( tcx. mk_unit ( ) , body. span ) ;
549643
550- // Gather live locals and their indices replacing values in mir .local_decls with a dummy
644+ // Gather live locals and their indices replacing values in body .local_decls with a dummy
551645 // to avoid changing local indices
552646 let live_decls = live_locals. iter ( ) . map ( |local| {
553647 let var = mem:: replace ( & mut body. local_decls [ local] , dummy_local. clone ( ) ) ;
@@ -568,6 +662,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
568662 remap. insert ( local, ( var. ty , variant_index, idx) ) ;
569663 decls. push ( var) ;
570664 }
665+ debug ! ( "generator saved local mappings: {:?}" , decls) ;
571666 let field_tys = decls. iter ( ) . map ( |field| field. ty ) . collect :: < IndexVec < _ , _ > > ( ) ;
572667
573668 // Put every var in each variant, for now.
@@ -578,6 +673,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
578673 let layout = GeneratorLayout {
579674 field_tys,
580675 variant_fields : empty_variants. chain ( state_variants) . collect ( ) ,
676+ storage_conflicts,
581677 __local_debuginfo_codegen_only_do_not_use : decls,
582678 } ;
583679
0 commit comments