Skip to content

Commit 968e542

Browse files
committed
Auto merge of #151742 - moulins:variant-layout, r=<try>
Remove redundant information in `rustc_abi::Variants`
2 parents e96bb7e + 8d1fb7e commit 968e542

23 files changed

Lines changed: 577 additions & 1544 deletions

compiler/rustc_abi/src/layout.rs

Lines changed: 47 additions & 64 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, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding,
14-
TargetDataLayout, Variants, WrappingRange,
14+
TargetDataLayout, VariantLayout, Variants, WrappingRange,
1515
};
1616

1717
mod coroutine;
@@ -575,6 +575,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
575575
}
576576

577577
let calculate_niche_filling_layout = || -> Option<LayoutData<FieldIdx, VariantIdx>> {
578+
struct VariantLayoutInfo {
579+
align_abi: Align,
580+
}
581+
578582
if repr.inhibit_enum_layout_opt() {
579583
return None;
580584
}
@@ -586,26 +590,35 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
586590
let mut align = dl.aggregate_align;
587591
let mut max_repr_align = repr.align;
588592
let mut unadjusted_abi_align = align;
593+
let mut combined_seed = repr.field_shuffle_seed;
594+
595+
let mut largest_variant_size = Size::ZERO;
596+
let mut largest_variant_index = VariantIdx::new(0);
597+
let mut largest_variant_niche = None;
589598

599+
let mut variants_info = IndexVec::<VariantIdx, _>::with_capacity(variants.len());
590600
let mut variant_layouts = variants
591601
.iter_enumerated()
592-
.map(|(j, v)| {
593-
let mut st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
594-
st.variants = Variants::Single { index: j };
602+
.map(|(i, v)| {
603+
let st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
604+
605+
variants_info.push(VariantLayoutInfo { align_abi: st.align.abi });
606+
607+
if st.size > largest_variant_size {
608+
largest_variant_index = i;
609+
largest_variant_size = st.size;
610+
largest_variant_niche = st.largest_niche;
611+
}
595612

596613
align = align.max(st.align.abi);
597614
max_repr_align = max_repr_align.max(st.max_repr_align);
598615
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
616+
combined_seed = combined_seed.wrapping_add(st.randomization_seed);
599617

600-
Some(st)
618+
Some(VariantLayout::from_layout(st))
601619
})
602620
.collect::<Option<IndexVec<VariantIdx, _>>>()?;
603621

604-
let largest_variant_index = variant_layouts
605-
.iter_enumerated()
606-
.max_by_key(|(_i, layout)| layout.size.bytes())
607-
.map(|(i, _layout)| i)?;
608-
609622
let all_indices = variants.indices();
610623
let needs_disc =
611624
|index: VariantIdx| index != largest_variant_index && !absent(&variants[index]);
@@ -616,42 +629,33 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
616629
(niche_variants.end().index() as u128 - niche_variants.start().index() as u128) + 1;
617630

618631
// Use the largest niche in the largest variant.
619-
let niche = variant_layouts[largest_variant_index].largest_niche?;
632+
let niche = largest_variant_niche?;
620633
let (niche_start, niche_scalar) = niche.reserve(dl, count)?;
621634
let niche_offset = niche.offset;
622635
let niche_size = niche.value.size(dl);
623-
let size = variant_layouts[largest_variant_index].size.align_to(align);
636+
let size = largest_variant_size.align_to(align);
624637

625638
let all_variants_fit = variant_layouts.iter_enumerated_mut().all(|(i, layout)| {
626639
if i == largest_variant_index {
627640
return true;
628641
}
629642

630-
layout.largest_niche = None;
631-
632643
if layout.size <= niche_offset {
633644
// This variant will fit before the niche.
634645
return true;
635646
}
636647

637648
// Determine if it'll fit after the niche.
638-
let this_align = layout.align.abi;
649+
let this_align = variants_info[i].align_abi;
639650
let this_offset = (niche_offset + niche_size).align_to(this_align);
640651

641652
if this_offset + layout.size > size {
642653
return false;
643654
}
644655

645656
// It'll fit, but we need to make some adjustments.
646-
match layout.fields {
647-
FieldsShape::Arbitrary { ref mut offsets, .. } => {
648-
for offset in offsets.iter_mut() {
649-
*offset += this_offset;
650-
}
651-
}
652-
FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
653-
panic!("Layout of fields should be Arbitrary for variants")
654-
}
657+
for offset in layout.field_offsets.iter_mut() {
658+
*offset += this_offset;
655659
}
656660

657661
// It can't be a Scalar or ScalarPair because the offset isn't 0.
@@ -673,7 +677,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
673677
.iter_enumerated()
674678
.all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO);
675679
let same_size = size == variant_layouts[largest_variant_index].size;
676-
let same_align = align == variant_layouts[largest_variant_index].align.abi;
680+
let same_align = align == variants_info[largest_variant_index].align_abi;
677681

678682
let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited());
679683
let abi = if same_size && same_align && others_zst {
@@ -696,11 +700,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
696700
BackendRepr::Memory { sized: true }
697701
};
698702

699-
let combined_seed = variant_layouts
700-
.iter()
701-
.map(|v| v.randomization_seed)
702-
.fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed));
703-
704703
let layout = LayoutData {
705704
variants: Variants::Multiple {
706705
tag: niche_scalar,
@@ -793,6 +792,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
793792
let mut align = dl.aggregate_align;
794793
let mut max_repr_align = repr.align;
795794
let mut unadjusted_abi_align = align;
795+
let mut combined_seed = repr.field_shuffle_seed;
796796

797797
let mut size = Size::ZERO;
798798

@@ -816,14 +816,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
816816

817817
// Create the set of structs that represent each variant.
818818
let mut layout_variants = variants
819-
.iter_enumerated()
820-
.map(|(i, field_layouts)| {
821-
let mut st = self.univariant(
819+
.iter()
820+
.map(|field_layouts| {
821+
let st = self.univariant(
822822
field_layouts,
823823
repr,
824824
StructKind::Prefixed(min_ity.size(), prefix_align),
825825
)?;
826-
st.variants = Variants::Single { index: i };
827826
// Find the first field we can't move later
828827
// to make room for a larger discriminant.
829828
for field_idx in st.fields.index_by_increasing_offset() {
@@ -837,7 +836,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
837836
align = align.max(st.align.abi);
838837
max_repr_align = max_repr_align.max(st.max_repr_align);
839838
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
840-
Ok(st)
839+
combined_seed = combined_seed.wrapping_add(st.randomization_seed);
840+
Ok(VariantLayout::from_layout(st))
841841
})
842842
.collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
843843

@@ -892,23 +892,16 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
892892
let old_ity_size = min_ity.size();
893893
let new_ity_size = ity.size();
894894
for variant in &mut layout_variants {
895-
match variant.fields {
896-
FieldsShape::Arbitrary { ref mut offsets, .. } => {
897-
for i in offsets {
898-
if *i <= old_ity_size {
899-
assert_eq!(*i, old_ity_size);
900-
*i = new_ity_size;
901-
}
902-
}
903-
// We might be making the struct larger.
904-
if variant.size <= old_ity_size {
905-
variant.size = new_ity_size;
906-
}
907-
}
908-
FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
909-
panic!("encountered a non-arbitrary layout during enum layout")
895+
for i in &mut variant.field_offsets {
896+
if *i <= old_ity_size {
897+
assert_eq!(*i, old_ity_size);
898+
*i = new_ity_size;
910899
}
911900
}
901+
// We might be making the struct larger.
902+
if variant.size <= old_ity_size {
903+
variant.size = new_ity_size;
904+
}
912905
}
913906
}
914907

@@ -933,12 +926,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
933926
let mut common_prim = None;
934927
let mut common_prim_initialized_in_all_variants = true;
935928
for (field_layouts, layout_variant) in iter::zip(variants, &layout_variants) {
936-
let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
937-
panic!("encountered a non-arbitrary layout during enum layout");
938-
};
939929
// We skip *all* ZST here and later check if we are good in terms of alignment.
940930
// This lets us handle some cases involving aligned ZST.
941-
let mut fields = iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
931+
let mut fields = iter::zip(field_layouts, &layout_variant.field_offsets)
932+
.filter(|p| !p.0.is_zst());
942933
let (field, offset) = match (fields.next(), fields.next()) {
943934
(None, None) => {
944935
common_prim_initialized_in_all_variants = false;
@@ -1033,25 +1024,17 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
10331024
for variant in &mut layout_variants {
10341025
// We only do this for variants with fields; the others are not accessed anyway.
10351026
// Also do not overwrite any already existing "clever" ABIs.
1036-
if variant.fields.count() > 0
1037-
&& matches!(variant.backend_repr, BackendRepr::Memory { .. })
1027+
if matches!(variant.backend_repr, BackendRepr::Memory { .. } if variant.has_fields())
10381028
{
10391029
variant.backend_repr = abi;
1040-
// Also need to bump up the size and alignment, so that the entire value fits
1041-
// in here.
1030+
// Also need to bump up the size, so that the entire value fits in here.
10421031
variant.size = cmp::max(variant.size, size);
1043-
variant.align.abi = cmp::max(variant.align.abi, align);
10441032
}
10451033
}
10461034
}
10471035

10481036
let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag);
10491037

1050-
let combined_seed = layout_variants
1051-
.iter()
1052-
.map(|v| v.randomization_seed)
1053-
.fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed));
1054-
10551038
let tagged_layout = LayoutData {
10561039
variants: Variants::Multiple {
10571040
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: None,
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: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1875,7 +1875,7 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
18751875
tag: Scalar,
18761876
tag_encoding: TagEncoding<VariantIdx>,
18771877
tag_field: FieldIdx,
1878-
variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
1878+
variants: IndexVec<VariantIdx, VariantLayout<FieldIdx>>,
18791879
},
18801880
}
18811881

@@ -2230,3 +2230,38 @@ pub enum AbiFromStrErr {
22302230
/// no "-unwind" variant can be used here
22312231
NoExplicitUnwind,
22322232
}
2233+
2234+
// NOTE: This struct is generic over the FieldIdx and VariantIdx for rust-analyzer usage.
2235+
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
2236+
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
2237+
pub struct VariantLayout<FieldIdx: Idx> {
2238+
pub size: Size,
2239+
pub backend_repr: BackendRepr,
2240+
pub field_offsets: IndexVec<FieldIdx, Size>,
2241+
fields_in_memory_order: IndexVec<u32, FieldIdx>,
2242+
uninhabited: bool,
2243+
}
2244+
2245+
impl<FieldIdx: Idx> VariantLayout<FieldIdx> {
2246+
pub fn from_layout(layout: LayoutData<FieldIdx, impl Idx>) -> Self {
2247+
let FieldsShape::Arbitrary { offsets, in_memory_order } = layout.fields else {
2248+
panic!("Layout of fields should be Arbitrary for variants");
2249+
};
2250+
2251+
Self {
2252+
size: layout.size,
2253+
backend_repr: layout.backend_repr,
2254+
field_offsets: offsets,
2255+
fields_in_memory_order: in_memory_order,
2256+
uninhabited: layout.uninhabited,
2257+
}
2258+
}
2259+
2260+
pub fn is_uninhabited(&self) -> bool {
2261+
self.uninhabited
2262+
}
2263+
2264+
pub fn has_fields(&self) -> bool {
2265+
self.field_offsets.len() > 0
2266+
}
2267+
}

compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::borrow::Cow;
22

3-
use rustc_abi::{FieldIdx, TagEncoding, VariantIdx, Variants};
3+
use rustc_abi::{FieldIdx, TagEncoding, VariantIdx, Variants, WrappingRange};
44
use rustc_codegen_ssa::debuginfo::type_names::{compute_debuginfo_type_name, cpp_like_debuginfo};
55
use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
66
use rustc_codegen_ssa::traits::MiscCodegenMethods;
@@ -399,16 +399,36 @@ fn compute_discriminant_value<'ll, 'tcx>(
399399
),
400400
&Variants::Multiple {
401401
tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, untagged_variant },
402+
tag_field,
402403
tag,
403404
..
404405
} => {
405406
if variant_index == untagged_variant {
406-
let valid_range = enum_type_and_layout
407-
.for_variant(cx, variant_index)
408-
.largest_niche
409-
.as_ref()
410-
.unwrap()
411-
.valid_range;
407+
let niche_end = niche_start
408+
.wrapping_sub(niche_variants.start().as_u32() as u128)
409+
.wrapping_add(niche_variants.end().as_u32() as u128);
410+
411+
// Remove discriminant values of the other variants from the largest niche. This assumes
412+
// that the largest niche, when it exists, always corresponds to the enum discriminant.
413+
let mut valid_range = WrappingRange {
414+
start: niche_end.wrapping_add(1),
415+
end: niche_start.wrapping_sub(1),
416+
};
417+
if let Some(niche) = &enum_type_and_layout.largest_niche {
418+
assert_eq!(enum_type_and_layout.fields.offset(tag_field.into()), niche.offset);
419+
420+
if niche.valid_range.start == niche_start {
421+
valid_range.end = niche.valid_range.end;
422+
} else if niche.valid_range.end == niche_end {
423+
valid_range.start = niche.valid_range.start;
424+
} else {
425+
bug!(
426+
"largest niche (range: {:?}) doesn't match with niched tag encoding (range: {:?})",
427+
niche.valid_range,
428+
&WrappingRange { start: niche_start, end: niche_end },
429+
)
430+
}
431+
}
412432

413433
let min = valid_range.start.min(valid_range.end);
414434
let min = tag.size(cx).truncate(min);

0 commit comments

Comments
 (0)