@@ -283,6 +283,23 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
283283
284284 let mut align = if pack. is_some ( ) { dl. i8_align } else { dl. aggregate_align } ;
285285
286+ let largest_niche_index = if matches ! ( kind, StructKind :: Prefixed { ..} ) || repr. hide_niche ( ) {
287+ None
288+ } else {
289+ fields
290+ . iter ( )
291+ . enumerate ( )
292+ . filter_map ( |( i, & field) | field. largest_niche . as_ref ( ) . map ( |n| ( i, n) ) )
293+ . max_by_key ( |( _, niche) | ( niche. available ( dl) , cmp:: Reverse ( niche. offset ) ) )
294+ . map ( |( i, _) | i as u32 )
295+ } ;
296+
297+ // inverse_memory_index holds field indices by increasing memory offset.
298+ // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5.
299+ // We now write field offsets to the corresponding offset slot;
300+ // field 5 with offset 0 puts 0 in offsets[5].
301+ // At the bottom of this function, we invert `inverse_memory_index` to
302+ // produce `memory_index` (see `invert_mapping`).
286303 let mut inverse_memory_index: Vec < u32 > = ( 0 ..fields. len ( ) as u32 ) . collect ( ) ;
287304
288305 let optimize = !repr. inhibit_struct_field_reordering_opt ( ) ;
@@ -296,10 +313,15 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
296313 match kind {
297314 StructKind :: AlwaysSized | StructKind :: MaybeUnsized => {
298315 optimizing. sort_by_key ( |& x| {
299- // Place ZSTs first to avoid "interesting offsets",
300- // especially with only one or two non-ZST fields.
301316 let f = & fields[ x as usize ] ;
302- ( !f. is_zst ( ) , cmp:: Reverse ( field_align ( f) ) )
317+ (
318+ // Place ZSTs first to avoid "interesting offsets",
319+ // especially with only one or two non-ZST fields.
320+ !f. is_zst ( ) ,
321+ cmp:: Reverse ( field_align ( f) ) ,
322+ // Try to put the largest niche earlier.
323+ Some ( x) != largest_niche_index,
324+ )
303325 } ) ;
304326 }
305327 StructKind :: Prefixed ( ..) => {
@@ -308,20 +330,25 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
308330 optimizing. sort_by_key ( |& x| field_align ( & fields[ x as usize ] ) ) ;
309331 }
310332 }
333+ // Rotate index array to put the largest niche first.
334+ // Since it is already the first amongst the types with the same alignement,
335+ // this will just move some of the potential padding within the structure.
336+ if let ( Some ( niche_index) , StructKind :: AlwaysSized ) = ( largest_niche_index, kind) {
337+ // ZSTs are always first, and the largest niche is not one, so we can unwrap
338+ let first_non_zst = inverse_memory_index
339+ . iter ( )
340+ . position ( |& x| !fields[ x as usize ] . is_zst ( ) )
341+ . unwrap ( ) ;
342+ let non_zsts = & mut inverse_memory_index[ first_non_zst..] ;
343+ let pivot = non_zsts. iter ( ) . position ( |& x| x == niche_index) . unwrap ( ) ;
344+ non_zsts. rotate_left ( pivot) ;
345+ }
311346 }
312347
313- // inverse_memory_index holds field indices by increasing memory offset.
314- // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5.
315- // We now write field offsets to the corresponding offset slot;
316- // field 5 with offset 0 puts 0 in offsets[5].
317- // At the bottom of this function, we invert `inverse_memory_index` to
318- // produce `memory_index` (see `invert_mapping`).
319-
320348 let mut sized = true ;
321349 let mut offsets = vec ! [ Size :: ZERO ; fields. len( ) ] ;
322350 let mut offset = Size :: ZERO ;
323351 let mut largest_niche = None ;
324- let mut largest_niche_available = 0 ;
325352
326353 if let StructKind :: Prefixed ( prefix_size, prefix_align) = kind {
327354 let prefix_align =
@@ -351,18 +378,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
351378
352379 debug ! ( "univariant offset: {:?} field: {:#?}" , offset, field) ;
353380 offsets[ i as usize ] = offset;
354-
355- if !repr. hide_niche ( ) {
356- if let Some ( mut niche) = field. largest_niche . clone ( ) {
357- let available = niche. available ( dl) ;
358- if available > largest_niche_available {
359- largest_niche_available = available;
360- niche. offset += offset;
361- largest_niche = Some ( niche) ;
362- }
363- }
381+ if largest_niche_index == Some ( i) {
382+ let mut niche = field. largest_niche . clone ( ) . unwrap ( ) ;
383+ niche. offset += offset;
384+ largest_niche = Some ( niche)
364385 }
365-
366386 offset = offset. checked_add ( field. size , dl) . ok_or ( LayoutError :: SizeOverflow ( ty) ) ?;
367387 }
368388
0 commit comments