@@ -49,7 +49,42 @@ pub trait LayoutCalculator {
4949 repr : & ReprOptions ,
5050 kind : StructKind ,
5151 ) -> Option < LayoutS > {
52- univariant ( self , dl, fields, repr, kind)
52+ let layout = univariant ( self , dl, fields, repr, kind, true ) ;
53+ // Enums prefer niches close to the beginning or the end of the variants so that other (smaller)
54+ // data-carrying variants can be packed into the space after/before the niche.
55+ // If the default field ordering does not give us a niche at the front then we do a second
56+ // run and bias niches to the right and then check which one is closer to one of the struct's
57+ // edges.
58+ if let Some ( layout) = & layout {
59+ if let Some ( niche) = layout. largest_niche {
60+ let head_space = niche. offset . bytes ( ) ;
61+ let niche_length = niche. value . size ( dl) . bytes ( ) ;
62+ let tail_space = layout. size . bytes ( ) - head_space - niche_length;
63+
64+ // This may end up doing redundant work if the niche is already in the last field
65+ // (e.g. a trailing bool) and there is tail padding. But it's non-trivial to get
66+ // the unpadded size so we try anyway.
67+ if fields. len ( ) > 1 && head_space != 0 && tail_space > 0 {
68+ let alt_layout = univariant ( self , dl, fields, repr, kind, false )
69+ . expect ( "alt layout should always work" ) ;
70+ let niche = alt_layout
71+ . largest_niche
72+ . expect ( "alt layout should have a niche like the regular one" ) ;
73+ let alt_head_space = niche. offset . bytes ( ) ;
74+ let alt_niche_len = niche. value . size ( dl) . bytes ( ) ;
75+
76+ debug_assert_eq ! ( layout. size. bytes( ) , alt_layout. size. bytes( ) ) ;
77+
78+ let prefer_alt_layout =
79+ alt_head_space > head_space && alt_head_space > tail_space;
80+
81+ if prefer_alt_layout {
82+ return Some ( alt_layout) ;
83+ }
84+ }
85+ }
86+ }
87+ layout
5388 }
5489
5590 fn layout_of_never_type ( & self ) -> LayoutS {
@@ -728,6 +763,7 @@ fn univariant(
728763 fields : & IndexSlice < FieldIdx , Layout < ' _ > > ,
729764 repr : & ReprOptions ,
730765 kind : StructKind ,
766+ niche_bias_start : bool ,
731767) -> Option < LayoutS > {
732768 let pack = repr. pack ;
733769 let mut align = if pack. is_some ( ) { dl. i8_align } else { dl. aggregate_align } ;
@@ -768,12 +804,35 @@ fn univariant(
768804 match kind {
769805 StructKind :: AlwaysSized | StructKind :: MaybeUnsized => {
770806 optimizing. sort_by_key ( |& x| {
771- // Place ZSTs first to avoid "interesting offsets",
772- // especially with only one or two non-ZST fields.
773- // Then place largest alignments first, largest niches within an alignment group last
774807 let f = fields[ x] ;
808+ let field_size = f. size ( ) . bytes ( ) ;
775809 let niche_size = f. largest_niche ( ) . map_or ( 0 , |n| n. available ( dl) ) ;
776- ( !f. 0 . is_zst ( ) , cmp:: Reverse ( effective_field_align ( f) ) , niche_size)
810+ let niche_size = if niche_bias_start {
811+ u128:: MAX - niche_size // large niche first
812+ } else {
813+ niche_size // large niche last
814+ } ;
815+ let inner_niche_placement = if niche_bias_start {
816+ f. largest_niche ( ) . map_or ( 0 , |n| n. offset . bytes ( ) )
817+ } else {
818+ f. largest_niche ( ) . map_or ( 0 , |n| {
819+ field_size - n. value . size ( dl) . bytes ( ) - n. offset . bytes ( )
820+ } )
821+ } ;
822+
823+ (
824+ // Place ZSTs first to avoid "interesting offsets", especially with only one
825+ // or two non-ZST fields. This helps Scalar/ScalarPair layouts.
826+ !f. 0 . is_zst ( ) ,
827+ // Then place largest alignments first.
828+ cmp:: Reverse ( effective_field_align ( f) ) ,
829+ // Then prioritize niche placement within alignment group according to
830+ // `niche_bias_start`.
831+ niche_size,
832+ // Then among fields with equally-sized niches prefer the ones
833+ // closer to the start/end of the field.
834+ inner_niche_placement,
835+ )
777836 } ) ;
778837 }
779838
@@ -838,7 +897,12 @@ fn univariant(
838897
839898 if let Some ( mut niche) = field. largest_niche ( ) {
840899 let available = niche. available ( dl) ;
841- if available > largest_niche_available {
900+ let prefer_new_niche = if niche_bias_start {
901+ available > largest_niche_available
902+ } else {
903+ available >= largest_niche_available
904+ } ;
905+ if prefer_new_niche {
842906 largest_niche_available = available;
843907 niche. offset += offset;
844908 largest_niche = Some ( niche) ;
0 commit comments