@@ -49,7 +49,42 @@ pub trait LayoutCalculator {
49
49
repr : & ReprOptions ,
50
50
kind : StructKind ,
51
51
) -> 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
53
88
}
54
89
55
90
fn layout_of_never_type ( & self ) -> LayoutS {
@@ -728,6 +763,7 @@ fn univariant(
728
763
fields : & IndexSlice < FieldIdx , Layout < ' _ > > ,
729
764
repr : & ReprOptions ,
730
765
kind : StructKind ,
766
+ niche_bias_start : bool ,
731
767
) -> Option < LayoutS > {
732
768
let pack = repr. pack ;
733
769
let mut align = if pack. is_some ( ) { dl. i8_align } else { dl. aggregate_align } ;
@@ -768,12 +804,35 @@ fn univariant(
768
804
match kind {
769
805
StructKind :: AlwaysSized | StructKind :: MaybeUnsized => {
770
806
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
774
807
let f = fields[ x] ;
808
+ let field_size = f. size ( ) . bytes ( ) ;
775
809
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
+ )
777
836
} ) ;
778
837
}
779
838
@@ -838,7 +897,12 @@ fn univariant(
838
897
839
898
if let Some ( mut niche) = field. largest_niche ( ) {
840
899
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 {
842
906
largest_niche_available = available;
843
907
niche. offset += offset;
844
908
largest_niche = Some ( niche) ;
0 commit comments