Skip to content

Commit cad8c01

Browse files
committed
Auto merge of #151742 - moulins:variant-layout, r=saethlin
Remove redundant information in `rustc_abi::Variants` Follow-up to #151040; partially addresses #113988. Replaces the nested `LayoutData` in `Variants::Multiple` by a new, smaller `VariantLayout` struct, and adjust `LayoutData::for_variant`and the layout algorithm in consequence. This PR is best reviewed commit-by-commit.
2 parents 0424cc1 + 93db272 commit cad8c01

22 files changed

Lines changed: 561 additions & 1435 deletions

compiler/rustc_abi/src/layout.rs

Lines changed: 36 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use tracing::{debug, trace};
1111
use crate::{
1212
AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer,
1313
LayoutData, Niche, NonZeroUsize, NumScalableVectors, Primitive, ReprOptions, Scalar, Size,
14-
StructKind, TagEncoding, TargetDataLayout, Variants, WrappingRange,
14+
StructKind, TagEncoding, TargetDataLayout, VariantLayout, Variants, WrappingRange,
1515
};
1616

1717
mod coroutine;
@@ -638,6 +638,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
638638
}
639639

640640
let calculate_niche_filling_layout = || -> Option<LayoutData<FieldIdx, VariantIdx>> {
641+
struct VariantLayoutInfo {
642+
align_abi: Align,
643+
}
644+
641645
if repr.inhibit_enum_layout_opt() {
642646
return None;
643647
}
@@ -649,18 +653,22 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
649653
let mut align = dl.aggregate_align;
650654
let mut max_repr_align = repr.align;
651655
let mut unadjusted_abi_align = align;
656+
let mut combined_seed = repr.field_shuffle_seed;
652657

658+
let mut variants_info = IndexVec::<VariantIdx, _>::with_capacity(variants.len());
653659
let mut variant_layouts = variants
654-
.iter_enumerated()
655-
.map(|(j, v)| {
656-
let mut st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
657-
st.variants = Variants::Single { index: j };
660+
.iter()
661+
.map(|v| {
662+
let st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
663+
664+
variants_info.push(VariantLayoutInfo { align_abi: st.align.abi });
658665

659666
align = align.max(st.align.abi);
660667
max_repr_align = max_repr_align.max(st.max_repr_align);
661668
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
669+
combined_seed = combined_seed.wrapping_add(st.randomization_seed);
662670

663-
Some(st)
671+
Some(VariantLayout::from_layout(st))
664672
})
665673
.collect::<Option<IndexVec<VariantIdx, _>>>()?;
666674

@@ -698,23 +706,16 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
698706
}
699707

700708
// Determine if it'll fit after the niche.
701-
let this_align = layout.align.abi;
709+
let this_align = variants_info[i].align_abi;
702710
let this_offset = (niche_offset + niche_size).align_to(this_align);
703711

704712
if this_offset + layout.size > size {
705713
return false;
706714
}
707715

708716
// It'll fit, but we need to make some adjustments.
709-
match layout.fields {
710-
FieldsShape::Arbitrary { ref mut offsets, .. } => {
711-
for offset in offsets.iter_mut() {
712-
*offset += this_offset;
713-
}
714-
}
715-
FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
716-
panic!("Layout of fields should be Arbitrary for variants")
717-
}
717+
for offset in layout.field_offsets.iter_mut() {
718+
*offset += this_offset;
718719
}
719720

720721
// It can't be a Scalar or ScalarPair because the offset isn't 0.
@@ -736,7 +737,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
736737
.iter_enumerated()
737738
.all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO);
738739
let same_size = size == variant_layouts[largest_variant_index].size;
739-
let same_align = align == variant_layouts[largest_variant_index].align.abi;
740+
let same_align = align == variants_info[largest_variant_index].align_abi;
740741

741742
let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited());
742743
let abi = if same_size && same_align && others_zst {
@@ -759,11 +760,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
759760
BackendRepr::Memory { sized: true }
760761
};
761762

762-
let combined_seed = variant_layouts
763-
.iter()
764-
.map(|v| v.randomization_seed)
765-
.fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed));
766-
767763
let layout = LayoutData {
768764
variants: Variants::Multiple {
769765
tag: niche_scalar,
@@ -856,6 +852,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
856852
let mut align = dl.aggregate_align;
857853
let mut max_repr_align = repr.align;
858854
let mut unadjusted_abi_align = align;
855+
let mut combined_seed = repr.field_shuffle_seed;
859856

860857
let mut size = Size::ZERO;
861858

@@ -879,14 +876,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
879876

880877
// Create the set of structs that represent each variant.
881878
let mut layout_variants = variants
882-
.iter_enumerated()
883-
.map(|(i, field_layouts)| {
884-
let mut st = self.univariant(
879+
.iter()
880+
.map(|field_layouts| {
881+
let st = self.univariant(
885882
field_layouts,
886883
repr,
887884
StructKind::Prefixed(min_ity.size(), prefix_align),
888885
)?;
889-
st.variants = Variants::Single { index: i };
890886
// Find the first field we can't move later
891887
// to make room for a larger discriminant.
892888
for field_idx in st.fields.index_by_increasing_offset() {
@@ -900,7 +896,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
900896
align = align.max(st.align.abi);
901897
max_repr_align = max_repr_align.max(st.max_repr_align);
902898
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
903-
Ok(st)
899+
combined_seed = combined_seed.wrapping_add(st.randomization_seed);
900+
Ok(VariantLayout::from_layout(st))
904901
})
905902
.collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
906903

@@ -955,23 +952,16 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
955952
let old_ity_size = min_ity.size();
956953
let new_ity_size = ity.size();
957954
for variant in &mut layout_variants {
958-
match variant.fields {
959-
FieldsShape::Arbitrary { ref mut offsets, .. } => {
960-
for i in offsets {
961-
if *i <= old_ity_size {
962-
assert_eq!(*i, old_ity_size);
963-
*i = new_ity_size;
964-
}
965-
}
966-
// We might be making the struct larger.
967-
if variant.size <= old_ity_size {
968-
variant.size = new_ity_size;
969-
}
970-
}
971-
FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
972-
panic!("encountered a non-arbitrary layout during enum layout")
955+
for i in &mut variant.field_offsets {
956+
if *i <= old_ity_size {
957+
assert_eq!(*i, old_ity_size);
958+
*i = new_ity_size;
973959
}
974960
}
961+
// We might be making the struct larger.
962+
if variant.size <= old_ity_size {
963+
variant.size = new_ity_size;
964+
}
975965
}
976966
}
977967

@@ -996,12 +986,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
996986
let mut common_prim = None;
997987
let mut common_prim_initialized_in_all_variants = true;
998988
for (field_layouts, layout_variant) in iter::zip(variants, &layout_variants) {
999-
let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
1000-
panic!("encountered a non-arbitrary layout during enum layout");
1001-
};
1002989
// We skip *all* ZST here and later check if we are good in terms of alignment.
1003990
// This lets us handle some cases involving aligned ZST.
1004-
let mut fields = iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
991+
let mut fields = iter::zip(field_layouts, &layout_variant.field_offsets)
992+
.filter(|p| !p.0.is_zst());
1005993
let (field, offset) = match (fields.next(), fields.next()) {
1006994
(None, None) => {
1007995
common_prim_initialized_in_all_variants = false;
@@ -1096,25 +1084,17 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
10961084
for variant in &mut layout_variants {
10971085
// We only do this for variants with fields; the others are not accessed anyway.
10981086
// Also do not overwrite any already existing "clever" ABIs.
1099-
if variant.fields.count() > 0
1100-
&& matches!(variant.backend_repr, BackendRepr::Memory { .. })
1087+
if matches!(variant.backend_repr, BackendRepr::Memory { .. } if variant.has_fields())
11011088
{
11021089
variant.backend_repr = abi;
1103-
// Also need to bump up the size and alignment, so that the entire value fits
1104-
// in here.
1090+
// Also need to bump up the size, so that the entire value fits in here.
11051091
variant.size = cmp::max(variant.size, size);
1106-
variant.align.abi = cmp::max(variant.align.abi, align);
11071092
}
11081093
}
11091094
}
11101095

11111096
let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag);
11121097

1113-
let combined_seed = layout_variants
1114-
.iter()
1115-
.map(|v| v.randomization_seed)
1116-
.fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed));
1117-
11181098
let tagged_layout = LayoutData {
11191099
variants: Variants::Multiple {
11201100
tag,

compiler/rustc_abi/src/layout/coroutine.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use tracing::{debug, trace};
2727

2828
use crate::{
2929
BackendRepr, FieldsShape, HasDataLayout, Integer, LayoutData, Primitive, ReprOptions, Scalar,
30-
StructKind, TagEncoding, Variants, WrappingRange,
30+
StructKind, TagEncoding, VariantLayout, Variants, WrappingRange,
3131
};
3232

3333
/// Overlap eligibility and variant assignment for each CoroutineSavedLocal.
@@ -230,7 +230,6 @@ pub(super) fn layout<
230230
&ReprOptions::default(),
231231
StructKind::Prefixed(prefix_size, prefix_align.abi),
232232
)?;
233-
variant.variants = Variants::Single { index };
234233

235234
let FieldsShape::Arbitrary { offsets, in_memory_order } = variant.fields else {
236235
unreachable!();
@@ -281,7 +280,7 @@ pub(super) fn layout<
281280

282281
size = size.max(variant.size);
283282
align = align.max(variant.align);
284-
Ok(variant)
283+
Ok(VariantLayout::from_layout(variant))
285284
})
286285
.collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
287286

compiler/rustc_abi/src/layout/simple.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,28 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
146146
randomization_seed: Hash64::ZERO,
147147
}
148148
}
149+
150+
/// Returns a layout for an inhabited variant.
151+
pub fn for_variant(parent: &Self, index: VariantIdx) -> Self {
152+
let layout = match &parent.variants {
153+
Variants::Multiple { variants, .. } => &variants[index],
154+
_ => panic!("Expected multi-variant layout in `Layout::for_variant`"),
155+
};
156+
157+
Self {
158+
fields: FieldsShape::Arbitrary {
159+
offsets: layout.field_offsets.clone(),
160+
in_memory_order: layout.fields_in_memory_order.clone(),
161+
},
162+
variants: Variants::Single { index },
163+
backend_repr: layout.backend_repr,
164+
largest_niche: layout.largest_niche,
165+
uninhabited: layout.uninhabited,
166+
size: layout.size,
167+
align: parent.align,
168+
max_repr_align: parent.max_repr_align,
169+
unadjusted_abi_align: parent.unadjusted_abi_align,
170+
randomization_seed: Hash64::ZERO,
171+
}
172+
}
149173
}

compiler/rustc_abi/src/lib.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1974,7 +1974,7 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
19741974
tag: Scalar,
19751975
tag_encoding: TagEncoding<VariantIdx>,
19761976
tag_field: FieldIdx,
1977-
variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
1977+
variants: IndexVec<VariantIdx, VariantLayout<FieldIdx>>,
19781978
},
19791979
}
19801980

@@ -2340,3 +2340,40 @@ pub enum AbiFromStrErr {
23402340
/// no "-unwind" variant can be used here
23412341
NoExplicitUnwind,
23422342
}
2343+
2344+
// NOTE: This struct is generic over the FieldIdx and VariantIdx for rust-analyzer usage.
2345+
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
2346+
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
2347+
pub struct VariantLayout<FieldIdx: Idx> {
2348+
pub size: Size,
2349+
pub backend_repr: BackendRepr,
2350+
pub field_offsets: IndexVec<FieldIdx, Size>,
2351+
fields_in_memory_order: IndexVec<u32, FieldIdx>,
2352+
largest_niche: Option<Niche>,
2353+
uninhabited: bool,
2354+
}
2355+
2356+
impl<FieldIdx: Idx> VariantLayout<FieldIdx> {
2357+
pub fn from_layout(layout: LayoutData<FieldIdx, impl Idx>) -> Self {
2358+
let FieldsShape::Arbitrary { offsets, in_memory_order } = layout.fields else {
2359+
panic!("Layout of fields should be Arbitrary for variants");
2360+
};
2361+
2362+
Self {
2363+
size: layout.size,
2364+
backend_repr: layout.backend_repr,
2365+
field_offsets: offsets,
2366+
fields_in_memory_order: in_memory_order,
2367+
largest_niche: layout.largest_niche,
2368+
uninhabited: layout.uninhabited,
2369+
}
2370+
}
2371+
2372+
pub fn is_uninhabited(&self) -> bool {
2373+
self.uninhabited
2374+
}
2375+
2376+
pub fn has_fields(&self) -> bool {
2377+
self.field_offsets.len() > 0
2378+
}
2379+
}

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,8 +765,8 @@ where
765765
tcx.mk_layout(LayoutData::uninhabited_variant(cx, variant_index, fields))
766766
}
767767

768-
Variants::Multiple { ref variants, .. } => {
769-
cx.tcx().mk_layout(variants[variant_index].clone())
768+
Variants::Multiple { .. } => {
769+
cx.tcx().mk_layout(LayoutData::for_variant(&this, variant_index))
770770
}
771771
};
772772

compiler/rustc_public/src/unstable/convert/stable/abi.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,8 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Variants<rustc_abi::FieldIdx, rustc_abi::
216216
tag_field: tag_field.stable(tables, cx),
217217
variants: variants
218218
.iter()
219-
.map(|v| match &v.fields {
220-
rustc_abi::FieldsShape::Arbitrary { offsets, .. } => VariantFields {
221-
offsets: offsets.iter().as_slice().stable(tables, cx),
222-
},
223-
_ => panic!("variant layout should be Arbitrary"),
219+
.map(|v| VariantFields {
220+
offsets: v.field_offsets.iter().as_slice().stable(tables, cx),
224221
})
225222
.collect(),
226223
}

compiler/rustc_ty_utils/src/layout/invariant.rs

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -302,29 +302,16 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou
302302
}
303303
}
304304
for variant in variants.iter() {
305-
// No nested "multiple".
306-
assert_matches!(variant.variants, Variants::Single { .. });
307-
// Variants should have the same or a smaller size as the full thing,
308-
// and same for alignment.
305+
// Variants should have the same or a smaller size as the full thing.
309306
if variant.size > layout.size {
310307
bug!(
311308
"Type with size {} bytes has variant with size {} bytes: {layout:#?}",
312309
layout.size.bytes(),
313310
variant.size.bytes(),
314311
)
315312
}
316-
if variant.align.abi > layout.align.abi {
317-
bug!(
318-
"Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
319-
layout.align.bytes(),
320-
variant.align.bytes(),
321-
)
322-
}
323313
// Skip empty variants.
324-
if variant.size == Size::ZERO
325-
|| variant.fields.count() == 0
326-
|| variant.is_uninhabited()
327-
{
314+
if variant.size == Size::ZERO || !variant.has_fields() || variant.is_uninhabited() {
328315
// These are never actually accessed anyway, so we can skip the coherence check
329316
// for them. They also fail that check, since they may have
330317
// a different ABI even when the main type is

src/librustdoc/html/render/type_layout.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub(crate) fn document_type_layout(cx: &Context<'_>, ty_def_id: DefId) -> impl f
5454
span_bug!(tcx.def_span(ty_def_id), "not an adt")
5555
};
5656
let name = adt.variant(variant_idx).name;
57-
let is_unsized = variant_layout.is_unsized();
57+
let is_unsized = variant_layout.backend_repr.is_unsized();
5858
let is_uninhabited = variant_layout.is_uninhabited();
5959
let size = variant_layout.size.bytes() - tag_size;
6060
let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size };

0 commit comments

Comments
 (0)