@@ -58,15 +58,19 @@ use rustc::ty::GeneratorSubsts;
58
58
use rustc:: ty:: layout:: VariantIdx ;
59
59
use rustc:: ty:: subst:: SubstsRef ;
60
60
use rustc_data_structures:: fx:: FxHashMap ;
61
- use rustc_data_structures:: indexed_vec:: Idx ;
61
+ use rustc_data_structures:: indexed_vec:: { Idx , IndexVec } ;
62
62
use rustc_data_structures:: bit_set:: BitSet ;
63
63
use std:: borrow:: Cow ;
64
+ use std:: collections:: hash_map;
64
65
use std:: iter;
65
66
use std:: mem;
66
67
use crate :: transform:: { MirPass , MirSource } ;
67
68
use crate :: transform:: simplify;
68
69
use 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} ;
70
74
use crate :: dataflow:: { MaybeStorageLive , HaveBeenBorrowedLocals } ;
71
75
use crate :: util:: dump_mir;
72
76
use crate :: util:: liveness;
@@ -392,6 +396,8 @@ fn locals_live_across_suspend_points(
392
396
source : MirSource < ' tcx > ,
393
397
movable : bool ,
394
398
) -> (
399
+ liveness:: LiveVarSet ,
400
+ FxHashMap < BasicBlock , liveness:: LiveVarSet > ,
395
401
liveness:: LiveVarSet ,
396
402
FxHashMap < BasicBlock , liveness:: LiveVarSet > ,
397
403
BitSet < BasicBlock > ,
@@ -496,7 +502,123 @@ fn locals_live_across_suspend_points(
496
502
// The generator argument is ignored
497
503
set. remove ( self_arg ( ) ) ;
498
504
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)
500
622
}
501
623
502
624
fn compute_layout < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
@@ -510,7 +632,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
510
632
FxHashMap < BasicBlock , liveness:: LiveVarSet > )
511
633
{
512
634
// 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) =
514
636
locals_live_across_suspend_points ( tcx, mir, source, movable) ;
515
637
516
638
// 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>,
539
661
}
540
662
541
663
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 < _ > > ( )
548
691
} ) ;
549
692
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
-
562
693
// Put every var in each variant, for now.
563
694
let empty_variants = iter:: repeat ( vec ! [ ] ) . take ( 3 ) ;
564
- let state_variants = iter:: repeat ( vars) . take ( suspending_blocks. count ( ) ) ;
565
695
let layout = GeneratorLayout {
566
696
variant_fields : empty_variants. chain ( state_variants) . collect ( )
567
697
} ;
0 commit comments