@@ -285,6 +285,31 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
285
285
286
286
let mut align = if pack. is_some ( ) { dl. i8_align } else { dl. aggregate_align } ;
287
287
288
+ let largest_niche_index = if matches ! ( kind, StructKind :: Prefixed { ..} ) || repr. hide_niche ( ) {
289
+ None
290
+ } else {
291
+ fields
292
+ . iter ( )
293
+ . enumerate ( )
294
+ . filter_map ( |( i, & field) | field. largest_niche . as_ref ( ) . map ( |n| ( i, n) ) )
295
+ . max_by_key ( |( i, niche) | {
296
+ (
297
+ niche. available ( dl) ,
298
+ // Prefer niches that occur earlier in their respective field, to maximize space after the niche.
299
+ cmp:: Reverse ( niche. offset ) ,
300
+ // Prefer fields that occur earlier in the struct, to avoid reordering fields unnecessarily.
301
+ cmp:: Reverse ( * i) ,
302
+ )
303
+ } )
304
+ . map ( |( i, _) | i as u32 )
305
+ } ;
306
+
307
+ // inverse_memory_index holds field indices by increasing memory offset.
308
+ // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5.
309
+ // We now write field offsets to the corresponding offset slot;
310
+ // field 5 with offset 0 puts 0 in offsets[5].
311
+ // At the bottom of this function, we invert `inverse_memory_index` to
312
+ // produce `memory_index` (see `invert_mapping`).
288
313
let mut inverse_memory_index: Vec < u32 > = ( 0 ..fields. len ( ) as u32 ) . collect ( ) ;
289
314
290
315
let optimize = !repr. inhibit_struct_field_reordering_opt ( ) ;
@@ -298,10 +323,15 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
298
323
match kind {
299
324
StructKind :: AlwaysSized | StructKind :: MaybeUnsized => {
300
325
optimizing. sort_by_key ( |& x| {
301
- // Place ZSTs first to avoid "interesting offsets",
302
- // especially with only one or two non-ZST fields.
303
326
let f = & fields[ x as usize ] ;
304
- ( !f. is_zst ( ) , cmp:: Reverse ( field_align ( f) ) )
327
+ (
328
+ // Place ZSTs first to avoid "interesting offsets",
329
+ // especially with only one or two non-ZST fields.
330
+ !f. is_zst ( ) ,
331
+ cmp:: Reverse ( field_align ( f) ) ,
332
+ // Try to put the largest niche earlier.
333
+ Some ( x) != largest_niche_index,
334
+ )
305
335
} ) ;
306
336
}
307
337
StructKind :: Prefixed ( ..) => {
@@ -310,20 +340,29 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
310
340
optimizing. sort_by_key ( |& x| field_align ( & fields[ x as usize ] ) ) ;
311
341
}
312
342
}
313
- }
314
343
315
- // inverse_memory_index holds field indices by increasing memory offset.
316
- // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5.
317
- // We now write field offsets to the corresponding offset slot;
318
- // field 5 with offset 0 puts 0 in offsets[5].
319
- // At the bottom of this function, we invert `inverse_memory_index` to
320
- // produce `memory_index` (see `invert_mapping`).
344
+ // Rotate index array to put the largest niche first. Then reverse the ones with larger
345
+ // alignment. Since it is already the first amongst the types with the same alignment,
346
+ // this will just move some of the potential padding within the structure.
347
+ if let ( Some ( niche_index) , StructKind :: AlwaysSized ) = ( largest_niche_index, kind) {
348
+ // ZSTs are always first, and the largest niche is not one, so we can unwrap
349
+ let first_non_zst = inverse_memory_index
350
+ . iter ( )
351
+ . position ( |& x| !fields[ x as usize ] . is_zst ( ) )
352
+ . unwrap ( ) ;
353
+ let non_zsts = & mut inverse_memory_index[ first_non_zst..] ;
354
+ let pivot = non_zsts. iter ( ) . position ( |& x| x == niche_index) . unwrap ( ) ;
355
+ non_zsts. rotate_left ( pivot) ;
356
+ let pivot = non_zsts. len ( ) - pivot;
357
+ non_zsts[ pivot..] . reverse ( ) ;
358
+ debug_assert_eq ! ( non_zsts[ 0 ] , niche_index) ;
359
+ }
360
+ }
321
361
322
362
let mut sized = true ;
323
363
let mut offsets = vec ! [ Size :: ZERO ; fields. len( ) ] ;
324
364
let mut offset = Size :: ZERO ;
325
365
let mut largest_niche = None ;
326
- let mut largest_niche_available = 0 ;
327
366
328
367
if let StructKind :: Prefixed ( prefix_size, prefix_align) = kind {
329
368
let prefix_align =
@@ -354,15 +393,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
354
393
debug ! ( "univariant offset: {:?} field: {:#?}" , offset, field) ;
355
394
offsets[ i as usize ] = offset;
356
395
357
- if !repr. hide_niche ( ) {
358
- if let Some ( mut niche) = field. largest_niche . clone ( ) {
359
- let available = niche. available ( dl) ;
360
- if available > largest_niche_available {
361
- largest_niche_available = available;
362
- niche. offset += offset;
363
- largest_niche = Some ( niche) ;
364
- }
365
- }
396
+ if largest_niche_index == Some ( i) {
397
+ let mut niche = field. largest_niche . clone ( ) . unwrap ( ) ;
398
+ niche. offset += offset;
399
+ largest_niche = Some ( niche)
366
400
}
367
401
368
402
offset = offset. checked_add ( field. size , dl) . ok_or ( LayoutError :: SizeOverflow ( ty) ) ?;
@@ -864,72 +898,123 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
864
898
if !def. repr . inhibit_enum_layout_opt ( ) && no_explicit_discriminants {
865
899
let mut dataful_variant = None ;
866
900
let mut niche_variants = VariantIdx :: MAX ..=VariantIdx :: new ( 0 ) ;
901
+ let mut max_size = Size :: ZERO ;
902
+ let mut second_max_size = Size :: ZERO ;
903
+ let mut align = dl. aggregate_align ;
904
+
905
+ // The size computations below assume that the padding is minimum.
906
+ // This is the case when fields are re-ordered.
907
+ let struct_reordering_opt = !def. repr . inhibit_struct_field_reordering_opt ( ) ;
908
+
909
+ let mut extend_niche_range = |d| {
910
+ niche_variants =
911
+ * niche_variants. start ( ) . min ( & d) ..=* niche_variants. end ( ) . max ( & d) ;
912
+ } ;
867
913
868
- // Find one non-ZST variant.
869
- ' variants : for ( v, fields) in variants. iter_enumerated ( ) {
914
+ // Find the largest and second largest variant.
915
+ for ( v, fields) in variants. iter_enumerated ( ) {
870
916
if absent ( fields) {
871
- continue ' variants ;
917
+ continue ;
872
918
}
873
- for f in fields {
874
- if !f. is_zst ( ) {
875
- if dataful_variant. is_none ( ) {
876
- dataful_variant = Some ( v) ;
877
- continue ' variants;
878
- } else {
879
- dataful_variant = None ;
880
- break ' variants;
881
- }
919
+ let mut size = Size :: ZERO ;
920
+ for & f in fields {
921
+ align = align. max ( f. align ) ;
922
+ size += f. size ;
923
+ }
924
+ if size > max_size {
925
+ second_max_size = max_size;
926
+ max_size = size;
927
+ if let Some ( d) = dataful_variant {
928
+ extend_niche_range ( d) ;
882
929
}
930
+ dataful_variant = Some ( v) ;
931
+ } else if size == max_size {
932
+ if let Some ( d) = dataful_variant {
933
+ extend_niche_range ( d) ;
934
+ }
935
+ dataful_variant = None ;
936
+ extend_niche_range ( v) ;
937
+ } else {
938
+ second_max_size = second_max_size. max ( size) ;
939
+ extend_niche_range ( v) ;
883
940
}
884
- niche_variants = * niche_variants. start ( ) . min ( & v) ..=v;
885
941
}
886
942
887
943
if niche_variants. start ( ) > niche_variants. end ( ) {
888
944
dataful_variant = None ;
889
945
}
890
946
891
- if let Some ( i ) = dataful_variant {
947
+ if let Some ( dataful_variant ) = dataful_variant {
892
948
let count = ( niche_variants. end ( ) . as_u32 ( )
893
949
- niche_variants. start ( ) . as_u32 ( )
894
950
+ 1 ) as u128 ;
895
951
896
952
// Find the field with the largest niche
897
- let niche_candidate = variants[ i ]
953
+ let niche_candidate = variants[ dataful_variant ]
898
954
. iter ( )
899
955
. enumerate ( )
900
956
. filter_map ( |( j, & field) | Some ( ( j, field. largest_niche . as_ref ( ) ?) ) )
901
- . max_by_key ( |( _, niche) | niche. available ( dl) ) ;
957
+ . max_by_key ( |( _, n) | ( n. available ( dl) , cmp:: Reverse ( n. offset ) ) )
958
+ . and_then ( |( field_index, niche) | {
959
+ if !struct_reordering_opt && second_max_size > Size :: ZERO {
960
+ return None ;
961
+ }
962
+ // make sure there is enough room for the other variants
963
+ if max_size - ( niche. offset + niche. scalar . value . size ( dl) )
964
+ < second_max_size
965
+ {
966
+ return None ;
967
+ }
968
+ Some ( ( field_index, niche, niche. reserve ( self , count) ?) )
969
+ } ) ;
902
970
903
971
if let Some ( ( field_index, niche, ( niche_start, niche_scalar) ) ) =
904
- niche_candidate. and_then ( |( field_index, niche) | {
905
- Some ( ( field_index, niche, niche. reserve ( self , count) ?) )
906
- } )
972
+ niche_candidate
907
973
{
908
- let mut align = dl . aggregate_align ;
974
+ let prefix = niche . offset + niche . scalar . value . size ( dl ) ;
909
975
let st = variants
910
976
. iter_enumerated ( )
911
977
. map ( |( j, v) | {
912
978
let mut st = self . univariant_uninterned (
913
979
ty,
914
980
v,
915
981
& def. repr ,
916
- StructKind :: AlwaysSized ,
982
+ if j == dataful_variant || second_max_size == Size :: ZERO {
983
+ StructKind :: AlwaysSized
984
+ } else {
985
+ StructKind :: Prefixed (
986
+ prefix,
987
+ Align :: from_bytes ( 1 ) . unwrap ( ) ,
988
+ )
989
+ } ,
917
990
) ?;
918
991
st. variants = Variants :: Single { index : j } ;
919
992
920
- align = align. max ( st. align ) ;
921
-
993
+ debug_assert_eq ! ( align, align. max( st. align) ) ;
922
994
Ok ( st)
923
995
} )
924
996
. collect :: < Result < IndexVec < VariantIdx , _ > , _ > > ( ) ?;
925
997
926
- let offset = st[ i] . fields . offset ( field_index) + niche. offset ;
927
- let size = st[ i] . size ;
998
+ let offset = if struct_reordering_opt {
999
+ debug_assert_eq ! (
1000
+ st[ dataful_variant] . fields. offset( field_index) ,
1001
+ Size :: ZERO
1002
+ ) ;
1003
+ niche. offset
1004
+ } else {
1005
+ st[ dataful_variant] . fields . offset ( field_index) + niche. offset
1006
+ } ;
1007
+
1008
+ let size = st[ dataful_variant] . size . align_to ( align. abi ) ;
1009
+ debug_assert ! (
1010
+ !struct_reordering_opt || size == max_size. align_to( align. abi)
1011
+ ) ;
1012
+ debug_assert ! ( st. iter( ) . all( |v| v. size <= size) ) ;
928
1013
929
1014
let abi = if st. iter ( ) . all ( |v| v. abi . is_uninhabited ( ) ) {
930
1015
Abi :: Uninhabited
931
- } else {
932
- match st[ i ] . abi {
1016
+ } else if second_max_size == Size :: ZERO {
1017
+ match st[ dataful_variant ] . abi {
933
1018
Abi :: Scalar ( _) => Abi :: Scalar ( niche_scalar. clone ( ) ) ,
934
1019
Abi :: ScalarPair ( ref first, ref second) => {
935
1020
// We need to use scalar_unit to reset the
@@ -951,6 +1036,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
951
1036
}
952
1037
_ => Abi :: Aggregate { sized : true } ,
953
1038
}
1039
+ } else {
1040
+ Abi :: Aggregate { sized : true }
954
1041
} ;
955
1042
956
1043
let largest_niche =
@@ -960,7 +1047,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
960
1047
variants : Variants :: Multiple {
961
1048
tag : niche_scalar,
962
1049
tag_encoding : TagEncoding :: Niche {
963
- dataful_variant : i ,
1050
+ dataful_variant,
964
1051
niche_variants,
965
1052
niche_start,
966
1053
} ,
0 commit comments