@@ -14,6 +14,9 @@ use std::ops::Bound;
14
14
15
15
use crate :: hir;
16
16
use crate :: ich:: StableHashingContext ;
17
+ use crate :: mir:: GeneratorSavedLocal ;
18
+ use crate :: ty:: subst:: Subst ;
19
+ use rustc_data_structures:: bit_set:: BitSet ;
17
20
use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
18
21
use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher ,
19
22
StableHasherResult } ;
@@ -612,34 +615,219 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
612
615
}
613
616
614
617
ty:: Generator ( def_id, ref substs, _) => {
615
- // FIXME(tmandry): For fields that are repeated in multiple
616
- // variants in the GeneratorLayout, we need code to ensure that
617
- // the offset of these fields never change. Right now this is
618
- // not an issue since every variant has every field, but once we
619
- // optimize this we have to be more careful.
618
+ // When laying out generators, we divide our saved local fields
619
+ // into two categories: overlap-eligible and overlap-ineligible.
620
+ //
621
+ // Those fields which are ineligible for overlap go in a
622
+ // "prefix" at the beginning of the layout, and always have
623
+ // space reserved for them.
624
+ //
625
+ // Overlap-eligible fields are only assigned to one variant, so
626
+ // we lay those fields out for each variant and put them right
627
+ // after the prefix.
628
+ //
629
+ // Finally, in the layout details, we point to the fields
630
+ // from the variants they are assigned to. It is possible for
631
+ // some fields to be included in multiple variants. No field
632
+ // ever "moves around" in the layout; its offset is always the
633
+ // same.
634
+ //
635
+ // Also included in the layout are the upvars and the
636
+ // discriminant. These are included as fields on the "outer"
637
+ // layout; they are not part of any variant.
638
+
639
+ let info = tcx. generator_layout ( def_id) ;
640
+ let subst_field = |ty : Ty < ' tcx > | { ty. subst ( tcx, substs. substs ) } ;
641
+
642
+ #[ derive( Clone , Debug , PartialEq ) ]
643
+ enum SavedLocalEligibility {
644
+ Unassigned ,
645
+ Assigned ( VariantIdx ) ,
646
+ // FIXME: Use newtype_index so we aren't wasting bytes
647
+ Ineligible ( Option < u32 > ) ,
648
+ }
649
+ use SavedLocalEligibility :: * ;
650
+
651
+ let mut assignments: IndexVec < GeneratorSavedLocal , SavedLocalEligibility > =
652
+ iter:: repeat ( Unassigned )
653
+ . take ( info. field_tys . len ( ) )
654
+ . collect ( ) ;
655
+
656
+ // The saved locals not eligible for overlap. These will get
657
+ // "promoted" to the prefix of our generator.
658
+ let mut eligible_locals = BitSet :: new_filled ( info. field_tys . len ( ) ) ;
659
+
660
+ // Figure out which of our saved locals are fields in only
661
+ // one variant. The rest are deemed ineligible for overlap.
662
+ for ( variant_index, fields) in info. variant_fields . iter_enumerated ( ) {
663
+ for local in fields {
664
+ match assignments[ * local] {
665
+ Unassigned => {
666
+ assignments[ * local] = Assigned ( variant_index) ;
667
+ }
668
+ Assigned ( idx) => {
669
+ // We've already seen this local at another suspension
670
+ // point, so it is no longer a candidate.
671
+ trace ! ( "removing local {:?} in >1 variant ({:?}, {:?})" ,
672
+ local, variant_index, idx) ;
673
+ eligible_locals. remove ( * local) ;
674
+ assignments[ * local] = Ineligible ( None ) ;
675
+ }
676
+ Ineligible ( _) => { } ,
677
+ }
678
+ }
679
+ }
680
+
681
+ // Next, check every pair of eligible locals to see if they
682
+ // conflict.
683
+ for ( local_a, conflicts_a) in info. storage_conflicts . iter_enumerated ( ) {
684
+ if !eligible_locals. contains ( local_a) {
685
+ continue ;
686
+ }
687
+
688
+ for local_b in conflicts_a. iter ( ) {
689
+ // local_a and local_b have overlapping storage, therefore they
690
+ // cannot overlap in the generator layout. The only way to guarantee
691
+ // this is if they are in the same variant, or one is ineligible
692
+ // (which means it is stored in every variant).
693
+ if !eligible_locals. contains ( local_b) ||
694
+ assignments[ local_a] == assignments[ local_b]
695
+ {
696
+ continue ;
697
+ }
698
+
699
+ // If they conflict, we will choose one to make ineligible.
700
+ let conflicts_b = & info. storage_conflicts [ local_b] ;
701
+ let ( remove, other) = if conflicts_a. count ( ) > conflicts_b. count ( ) {
702
+ ( local_a, local_b)
703
+ } else {
704
+ ( local_b, local_a)
705
+ } ;
706
+ eligible_locals. remove ( remove) ;
707
+ assignments[ remove] = Ineligible ( None ) ;
708
+ trace ! ( "removing local {:?} due to conflict with {:?}" , remove, other) ;
709
+ }
710
+ }
711
+
712
+ let mut ineligible_locals = BitSet :: new_filled ( info. field_tys . len ( ) ) ;
713
+ ineligible_locals. subtract ( & eligible_locals) ;
620
714
715
+ // Write down the order of our locals that will be promoted to
716
+ // the prefix.
717
+ for ( idx, local) in ineligible_locals. iter ( ) . enumerate ( ) {
718
+ assignments[ local] = Ineligible ( Some ( idx as u32 ) ) ;
719
+ }
720
+ debug ! ( "generator saved local assignments: {:?}" , assignments) ;
721
+
722
+ // Build a prefix layout, including "promoting" all ineligible
723
+ // locals as part of the prefix.
621
724
let discr_index = substs. prefix_tys ( def_id, tcx) . count ( ) ;
725
+ let promoted_tys =
726
+ ineligible_locals. iter ( ) . map ( |local| subst_field ( info. field_tys [ local] ) ) ;
622
727
let prefix_tys = substs. prefix_tys ( def_id, tcx)
623
- . chain ( iter:: once ( substs. discr_ty ( tcx) ) ) ;
728
+ . chain ( iter:: once ( substs. discr_ty ( tcx) ) )
729
+ . chain ( promoted_tys) ;
624
730
let prefix = univariant_uninterned (
625
731
& prefix_tys. map ( |ty| self . layout_of ( ty) ) . collect :: < Result < Vec < _ > , _ > > ( ) ?,
626
732
& ReprOptions :: default ( ) ,
627
733
StructKind :: AlwaysSized ) ?;
734
+ let ( prefix_size, prefix_align) = ( prefix. size , prefix. align ) ;
735
+
736
+ // Split the prefix layout into the "outer" fields (upvars and
737
+ // discriminant) and the "promoted" fields. Promoted fields will
738
+ // get included in each variant that requested them in
739
+ // GeneratorLayout.
740
+ let renumber_indices = |mut index : Vec < u32 > | -> Vec < u32 > {
741
+ debug ! ( "renumber_indices({:?})" , index) ;
742
+ let mut inverse_index = ( 0 ..index. len ( ) as u32 ) . collect :: < Vec < _ > > ( ) ;
743
+ inverse_index. sort_unstable_by_key ( |i| index[ * i as usize ] ) ;
744
+ for i in 0 ..index. len ( ) {
745
+ index[ inverse_index[ i] as usize ] = i as u32 ;
746
+ }
747
+ debug ! ( "renumber_indices() => {:?}" , index) ;
748
+ index
749
+ } ;
750
+ debug ! ( "prefix = {:#?}" , prefix) ;
751
+ let ( outer_fields, promoted_offsets, promoted_memory_index) = match prefix. fields {
752
+ FieldPlacement :: Arbitrary { offsets, memory_index } => {
753
+ let ( offsets_a, offsets_b) =
754
+ offsets. split_at ( discr_index + 1 ) ;
755
+ let ( memory_index_a, memory_index_b) =
756
+ memory_index. split_at ( discr_index + 1 ) ;
757
+ let outer_fields = FieldPlacement :: Arbitrary {
758
+ offsets : offsets_a. to_vec ( ) ,
759
+ memory_index : renumber_indices ( memory_index_a. to_vec ( ) )
760
+ } ;
761
+ ( outer_fields,
762
+ offsets_b. to_vec ( ) ,
763
+ renumber_indices ( memory_index_b. to_vec ( ) ) )
764
+ }
765
+ _ => bug ! ( ) ,
766
+ } ;
628
767
629
768
let mut size = prefix. size ;
630
769
let mut align = prefix. align ;
631
- let variants_tys = substs. state_tys ( def_id, tcx) ;
632
- let variants = variants_tys. enumerate ( ) . map ( |( i, variant_tys) | {
770
+ let variants = info. variant_fields . iter_enumerated ( ) . map ( |( index, variant_fields) | {
771
+ // Only include overlap-eligible fields when we compute our variant layout.
772
+ let variant_only_tys = variant_fields. iter ( ) . flat_map ( |local| {
773
+ let ty = info. field_tys [ * local] ;
774
+ match assignments[ * local] {
775
+ Unassigned => bug ! ( ) ,
776
+ Assigned ( v) if v == index => Some ( subst_field ( ty) ) ,
777
+ Assigned ( _) => bug ! ( "assignment does not match variant" ) ,
778
+ Ineligible ( _) => None ,
779
+ }
780
+ } ) ;
781
+
633
782
let mut variant = univariant_uninterned (
634
- & variant_tys. map ( |ty| self . layout_of ( ty) ) . collect :: < Result < Vec < _ > , _ > > ( ) ?,
783
+ & variant_only_tys
784
+ . map ( |ty| self . layout_of ( ty) )
785
+ . collect :: < Result < Vec < _ > , _ > > ( ) ?,
635
786
& ReprOptions :: default ( ) ,
636
- StructKind :: Prefixed ( prefix. size , prefix. align . abi ) ) ?;
787
+ StructKind :: Prefixed ( prefix_size, prefix_align. abi ) ) ?;
788
+ variant. variants = Variants :: Single { index } ;
637
789
638
- variant. variants = Variants :: Single { index : VariantIdx :: new ( i) } ;
790
+ let ( offsets, memory_index) = match variant. fields {
791
+ FieldPlacement :: Arbitrary { offsets, memory_index } =>
792
+ ( offsets, memory_index) ,
793
+ _ => bug ! ( ) ,
794
+ } ;
795
+
796
+ // Now, stitch the promoted and variant-only fields back
797
+ // together in the order they are mentioned by our
798
+ // GeneratorLayout.
799
+ let mut next_variant_field = 0 ;
800
+ let mut combined_offsets = Vec :: new ( ) ;
801
+ let mut combined_memory_index = Vec :: new ( ) ;
802
+ for local in variant_fields. iter ( ) {
803
+ match assignments[ * local] {
804
+ Unassigned => bug ! ( ) ,
805
+ Assigned ( _) => {
806
+ combined_offsets. push ( offsets[ next_variant_field] ) ;
807
+ // Shift memory indices by the number of
808
+ // promoted fields, which all come first. We
809
+ // may not use all promoted fields in our
810
+ // variant but that's okay; we'll renumber them
811
+ // below.
812
+ combined_memory_index. push (
813
+ promoted_memory_index. len ( ) as u32 +
814
+ memory_index[ next_variant_field] ) ;
815
+ next_variant_field += 1 ;
816
+ }
817
+ Ineligible ( field_idx) => {
818
+ let field_idx = field_idx. unwrap ( ) as usize ;
819
+ combined_offsets. push ( promoted_offsets[ field_idx] ) ;
820
+ combined_memory_index. push ( promoted_memory_index[ field_idx] ) ;
821
+ }
822
+ }
823
+ }
824
+ variant. fields = FieldPlacement :: Arbitrary {
825
+ offsets : combined_offsets,
826
+ memory_index : renumber_indices ( combined_memory_index) ,
827
+ } ;
639
828
640
829
size = size. max ( variant. size ) ;
641
830
align = align. max ( variant. align ) ;
642
-
643
831
Ok ( variant)
644
832
} ) . collect :: < Result < IndexVec < VariantIdx , _ > , _ > > ( ) ?;
645
833
@@ -661,7 +849,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
661
849
discr_index,
662
850
variants,
663
851
} ,
664
- fields : prefix . fields ,
852
+ fields : outer_fields ,
665
853
abi,
666
854
size,
667
855
align,
0 commit comments