Skip to content

Commit 23d815c

Browse files
committed
Put the largest niche first
1 parent df768c5 commit 23d815c

File tree

3 files changed

+47
-25
lines changed

3 files changed

+47
-25
lines changed

src/librustc_middle/ty/layout.rs

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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

src/test/ui/consts/const-eval/const_transmute.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ impl Drop for Foo {
3333
}
3434

3535
#[derive(Copy, Clone)]
36+
#[repr(C)]
3637
struct Fat<'a>(&'a Foo, &'static VTable);
3738

39+
#[repr(C)]
3840
struct VTable {
3941
drop: Option<for<'a> fn(&'a mut Foo)>,
4042
size: usize,

src/test/ui/print_type_sizes/padding.stdout

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ print-type-size field `.0`: 1 bytes
1717
print-type-size padding: 2 bytes
1818
print-type-size field `.1`: 4 bytes, alignment: 4 bytes
1919
print-type-size type: `S`: 8 bytes, alignment: 4 bytes
20-
print-type-size field `.g`: 4 bytes
21-
print-type-size field `.a`: 1 bytes
2220
print-type-size field `.b`: 1 bytes
23-
print-type-size end padding: 2 bytes
21+
print-type-size field `.a`: 1 bytes
22+
print-type-size padding: 2 bytes
23+
print-type-size field `.g`: 4 bytes, alignment: 4 bytes

0 commit comments

Comments
 (0)