Skip to content

Commit 61fb5a9

Browse files
committed
layout-alignment-promotion logic should depend on the niche-bias
For start-biased layout we want to avoid overpromoting so that the niche doesn't get pushed back. For end-biased layout we want to avoid promoting fields that may contain one of the niches of interest.
1 parent afe106c commit 61fb5a9

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

compiler/rustc_abi/src/layout.rs

+22-7
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ fn univariant(
794794
let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
795795
let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect();
796796
let optimize = !repr.inhibit_struct_field_reordering_opt();
797-
if optimize {
797+
if optimize && fields.len() > 1 {
798798
let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() };
799799
let optimizing = &mut inverse_memory_index.raw[..end];
800800

@@ -814,7 +814,12 @@ fn univariant(
814814
// Otherwise we just leave things alone and actually optimize the type's fields
815815
} else {
816816
let max_field_align = fields.iter().map(|f| f.align().abi.bytes()).max().unwrap_or(1);
817-
let any_niche = fields.iter().any(|f| f.largest_niche().is_some());
817+
let largest_niche_size = fields
818+
.iter()
819+
.filter_map(|f| f.largest_niche())
820+
.map(|n| n.available(dl))
821+
.max()
822+
.unwrap_or(0);
818823

819824
// Calculates a sort key to group fields by their alignment or possibly some size-derived
820825
// pseudo-alignment.
@@ -829,13 +834,23 @@ fn univariant(
829834
//
830835
let align = layout.align().abi.bytes();
831836
let size = layout.size().bytes();
837+
let niche_size = layout.largest_niche().map(|n| n.available(dl)).unwrap_or(0);
832838
// group [u8; 4] with align-4 or [u8; 6] with align-2 fields
833839
let size_as_align = align.max(size).trailing_zeros();
834-
// Given `A(u8, [u8; 16])` and `B(bool, [u8; 16])` we want to bump the array
835-
// to the front in the first case (for aligned loads) but keep the bool in front
836-
// in the second case for its niches.
837-
let size_as_align = if any_niche {
838-
max_field_align.trailing_zeros().min(size_as_align)
840+
let size_as_align = if largest_niche_size > 0 {
841+
match niche_bias {
842+
// Given `A(u8, [u8; 16])` and `B(bool, [u8; 16])` we want to bump the array
843+
// to the front in the first case (for aligned loads) but keep the bool in front
844+
// in the second case for its niches.
845+
NicheBias::Start => max_field_align.trailing_zeros().min(size_as_align),
846+
// When moving niches towards the end of the struct then for
847+
// A((u8, u8, u8, bool), (u8, bool, u8)) we want to keep the first tuple
848+
// in the align-1 group because its bool can be moved closer to the end.
849+
NicheBias::End if niche_size == largest_niche_size => {
850+
align.trailing_zeros()
851+
}
852+
NicheBias::End => size_as_align,
853+
}
839854
} else {
840855
size_as_align
841856
};

tests/ui/structs-enums/type-sizes.rs

+19
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,18 @@ struct ReorderWithNiche {
198198
ary: [u8; 8]
199199
}
200200

201+
#[repr(C)]
202+
struct EndNiche8([u8; 7], bool);
203+
204+
#[repr(C)]
205+
struct MiddleNiche4(u8, u8, bool, u8);
206+
207+
struct ReorderEndNiche {
208+
a: EndNiche8,
209+
b: MiddleNiche4,
210+
}
211+
212+
201213
// standins for std types which we want to be laid out in a reasonable way
202214
struct RawVecDummy {
203215
ptr: NonNull<u8>,
@@ -316,4 +328,11 @@ pub fn main() {
316328
"here [u8; 8] should group with _at least_ align-4 fields");
317329
assert_eq!(ptr::from_ref(&v), ptr::from_ref(&v.b).cast(),
318330
"sort niches to the front where possible");
331+
332+
// Neither field has a niche at the beginning so the layout algorithm should try move niches to
333+
// the end which means the 8-sized field shouldn't be alignment-promoted before the 4-sized one.
334+
let v = ReorderEndNiche { a: EndNiche8([0; 7], false), b: MiddleNiche4(0, 0, false, 0) };
335+
assert!(ptr::from_ref(&v.a).addr() > ptr::from_ref(&v.b).addr());
336+
337+
319338
}

0 commit comments

Comments
 (0)