Skip to content

Commit afe106c

Browse files
committed
[review] add comments, turn flag into enum
1 parent 67a835d commit afe106c

File tree

1 file changed

+44
-23
lines changed

1 file changed

+44
-23
lines changed

compiler/rustc_abi/src/layout.rs

+44-23
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub trait LayoutCalculator {
5050
repr: &ReprOptions,
5151
kind: StructKind,
5252
) -> Option<LayoutS> {
53-
let layout = univariant(self, dl, fields, repr, kind, true);
53+
let layout = univariant(self, dl, fields, repr, kind, NicheBias::Start);
5454
// Enums prefer niches close to the beginning or the end of the variants so that other (smaller)
5555
// data-carrying variants can be packed into the space after/before the niche.
5656
// If the default field ordering does not give us a niche at the front then we do a second
@@ -66,7 +66,7 @@ pub trait LayoutCalculator {
6666
// (e.g. a trailing bool) and there is tail padding. But it's non-trivial to get
6767
// the unpadded size so we try anyway.
6868
if fields.len() > 1 && head_space != 0 && tail_space > 0 {
69-
let alt_layout = univariant(self, dl, fields, repr, kind, false)
69+
let alt_layout = univariant(self, dl, fields, repr, kind, NicheBias::End)
7070
.expect("alt layout should always work");
7171
let niche = alt_layout
7272
.largest_niche
@@ -776,13 +776,19 @@ pub trait LayoutCalculator {
776776
}
777777
}
778778

779+
/// Determines towards which end of a struct layout optimizations will try to place the best niches.
780+
enum NicheBias {
781+
Start,
782+
End,
783+
}
784+
779785
fn univariant(
780786
this: &(impl LayoutCalculator + ?Sized),
781787
dl: &TargetDataLayout,
782788
fields: &IndexSlice<FieldIdx, Layout<'_>>,
783789
repr: &ReprOptions,
784790
kind: StructKind,
785-
niche_bias_start: bool,
791+
niche_bias: NicheBias,
786792
) -> Option<LayoutS> {
787793
let pack = repr.pack;
788794
let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
@@ -809,7 +815,10 @@ fn univariant(
809815
} else {
810816
let max_field_align = fields.iter().map(|f| f.align().abi.bytes()).max().unwrap_or(1);
811817
let any_niche = fields.iter().any(|f| f.largest_niche().is_some());
812-
let effective_field_align = |layout: Layout<'_>| {
818+
819+
// Calculates a sort key to group fields by their alignment or possibly some size-derived
820+
// pseudo-alignment.
821+
let alignment_group_key = |layout: Layout<'_>| {
813822
if let Some(pack) = pack {
814823
// return the packed alignment in bytes
815824
layout.align().abi.min(pack).bytes()
@@ -818,10 +827,13 @@ fn univariant(
818827
// This is ok since `pack` applies to all fields equally.
819828
// The calculation assumes that size is an integer multiple of align, except for ZSTs.
820829
//
821-
// group [u8; 4] with align-4 or [u8; 6] with align-2 fields
822830
let align = layout.align().abi.bytes();
823831
let size = layout.size().bytes();
832+
// group [u8; 4] with align-4 or [u8; 6] with align-2 fields
824833
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.
825837
let size_as_align = if any_niche {
826838
max_field_align.trailing_zeros().min(size_as_align)
827839
} else {
@@ -833,35 +845,43 @@ fn univariant(
833845

834846
match kind {
835847
StructKind::AlwaysSized | StructKind::MaybeUnsized => {
848+
// Currently `LayoutS` only exposes a single niche so sorting is usually sufficient
849+
// to get one niche into the preferred position. If it ever supported multiple niches
850+
// then a more advanced pick-and-pack approach could provide better results.
851+
// But even for the single-niche cache it's not optimal. E.g. for
852+
// A(u32, (bool, u8), u16) it would be possible to move the bool to the front
853+
// but it would require packing the tuple together with the u16 to build a 4-byte
854+
// group so that the u32 can be placed after it without padding. This kind
855+
// of packing can't be achieved by sorting.
836856
optimizing.sort_by_key(|&x| {
837857
let f = fields[x];
838858
let field_size = f.size().bytes();
839859
let niche_size = f.largest_niche().map_or(0, |n| n.available(dl));
840-
let niche_size = if niche_bias_start {
841-
u128::MAX - niche_size // large niche first
842-
} else {
843-
niche_size // large niche last
860+
let niche_size_key = match niche_bias {
861+
// large niche first
862+
NicheBias::Start => !niche_size,
863+
// large niche last
864+
NicheBias::End => niche_size,
844865
};
845-
let inner_niche_placement = if niche_bias_start {
846-
f.largest_niche().map_or(0, |n| n.offset.bytes())
847-
} else {
848-
f.largest_niche().map_or(0, |n| {
849-
field_size - n.value.size(dl).bytes() - n.offset.bytes()
850-
})
866+
let inner_niche_offset_key = match niche_bias {
867+
NicheBias::Start => f.largest_niche().map_or(0, |n| n.offset.bytes()),
868+
NicheBias::End => f.largest_niche().map_or(0, |n| {
869+
!(field_size - n.value.size(dl).bytes() - n.offset.bytes())
870+
}),
851871
};
852872

853873
(
854874
// Place ZSTs first to avoid "interesting offsets", especially with only one
855875
// or two non-ZST fields. This helps Scalar/ScalarPair layouts.
856876
!f.0.is_zst(),
857877
// Then place largest alignments first.
858-
cmp::Reverse(effective_field_align(f)),
878+
cmp::Reverse(alignment_group_key(f)),
859879
// Then prioritize niche placement within alignment group according to
860880
// `niche_bias_start`.
861-
niche_size,
881+
niche_size_key,
862882
// Then among fields with equally-sized niches prefer the ones
863883
// closer to the start/end of the field.
864-
inner_niche_placement,
884+
inner_niche_offset_key,
865885
)
866886
});
867887
}
@@ -874,7 +894,7 @@ fn univariant(
874894
optimizing.sort_by_key(|&x| {
875895
let f = fields[x];
876896
let niche_size = f.largest_niche().map_or(0, |n| n.available(dl));
877-
(effective_field_align(f), niche_size)
897+
(alignment_group_key(f), niche_size)
878898
});
879899
}
880900
}
@@ -927,10 +947,11 @@ fn univariant(
927947

928948
if let Some(mut niche) = field.largest_niche() {
929949
let available = niche.available(dl);
930-
let prefer_new_niche = if niche_bias_start {
931-
available > largest_niche_available
932-
} else {
933-
available >= largest_niche_available
950+
// Pick up larger niches.
951+
let prefer_new_niche = match niche_bias {
952+
NicheBias::Start => available > largest_niche_available,
953+
// if there are several niches of the same size then pick the last one
954+
NicheBias::End => available >= largest_niche_available,
934955
};
935956
if prefer_new_niche {
936957
largest_niche_available = available;

0 commit comments

Comments
 (0)