@@ -14,6 +14,9 @@ use std::ops::Bound;
1414
1515use crate :: hir;
1616use crate :: ich:: StableHashingContext ;
17+ use crate :: mir:: GeneratorSavedLocal ;
18+ use crate :: ty:: subst:: Subst ;
19+ use rustc_data_structures:: bit_set:: BitSet ;
1720use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
1821use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher ,
1922 StableHasherResult } ;
@@ -612,34 +615,219 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
612615 }
613616
614617 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) ;
620714
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.
621724 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] ) ) ;
622727 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) ;
624730 let prefix = univariant_uninterned (
625731 & prefix_tys. map ( |ty| self . layout_of ( ty) ) . collect :: < Result < Vec < _ > , _ > > ( ) ?,
626732 & ReprOptions :: default ( ) ,
627733 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+ } ;
628767
629768 let mut size = prefix. size ;
630769 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+
633782 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 < _ > , _ > > ( ) ?,
635786 & ReprOptions :: default ( ) ,
636- StructKind :: Prefixed ( prefix. size , prefix. align . abi ) ) ?;
787+ StructKind :: Prefixed ( prefix_size, prefix_align. abi ) ) ?;
788+ variant. variants = Variants :: Single { index } ;
637789
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+ } ;
639828
640829 size = size. max ( variant. size ) ;
641830 align = align. max ( variant. align ) ;
642-
643831 Ok ( variant)
644832 } ) . collect :: < Result < IndexVec < VariantIdx , _ > , _ > > ( ) ?;
645833
@@ -661,7 +849,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
661849 discr_index,
662850 variants,
663851 } ,
664- fields : prefix . fields ,
852+ fields : outer_fields ,
665853 abi,
666854 size,
667855 align,
0 commit comments