Skip to content

Commit ae04988

Browse files
fix: Organises the enum niche-filling variant packing implementation.
Organises the niche-repacking solution structure for better mantainability. Minor bug fix cause by type mismatch. Signed-off-by: Miguel Marques <miguel.m.marques@tecnico.ulisboa.pt> Co-authored-by: Vicente Gusmão <vicente.gusmao@tecnico.ulisboa.pt>
1 parent 1ee3813 commit ae04988

1 file changed

Lines changed: 68 additions & 214 deletions

File tree

compiler/rustc_abi/src/layout.rs

Lines changed: 68 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
608608
let mut combined_seed = repr.field_shuffle_seed;
609609

610610
let mut variants_info = IndexVec::<VariantIdx, _>::with_capacity(variants.len());
611-
let mut variant_layouts = variants
611+
let variant_layouts = variants
612612
.iter()
613613
.map(|v| {
614614
let st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
@@ -624,195 +624,27 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
624624
})
625625
.collect::<Option<IndexVec<VariantIdx, _>>>()?;
626626

627-
let largest_variant_index = variant_layouts
628-
.iter_enumerated()
629-
.max_by_key(|(_i, layout)| layout.size.bytes())
630-
.map(|(i, _layout)| i)?;
631-
632-
let all_indices = variants.indices();
633-
let needs_disc =
634-
|index: VariantIdx| index != largest_variant_index && !absent(&variants[index]);
635-
let niche_variants = RangeInclusive {
636-
start: all_indices.clone().find(|v| needs_disc(*v)).unwrap(),
637-
last: all_indices.rev().find(|v| needs_disc(*v)).unwrap(),
638-
};
639-
640-
let count =
641-
(niche_variants.last.index() as u128 - niche_variants.start.index() as u128) + 1;
642-
643-
// Use the largest niche in the largest variant.
644-
let niche = variant_layouts[largest_variant_index].largest_niche?;
645-
let (niche_start, niche_scalar) = niche.reserve(dl, count)?;
646-
let niche_offset = niche.offset;
647-
let niche_size = niche.value.size(dl);
648-
let size = variant_layouts[largest_variant_index].size.align_to(align);
649-
650-
let all_variants_fit = variant_layouts.iter_enumerated_mut().all(|(i, layout)| {
651-
if i == largest_variant_index {
652-
return true;
653-
}
654-
655-
layout.largest_niche = None;
656-
657-
if layout.size <= niche_offset {
658-
// This variant will fit before the niche.
659-
return true;
660-
}
661-
662-
// Determine if it'll fit after the niche.
663-
let this_align = variants_info[i].align_abi;
664-
let this_offset = (niche_offset + niche_size).align_to(this_align);
665-
666-
if this_offset + layout.size > size {
667-
return false;
668-
}
669-
670-
// It'll fit, but we need to make some adjustments.
671-
for offset in layout.field_offsets.iter_mut() {
672-
*offset += this_offset;
673-
}
674-
675-
// It can't be a Scalar or ScalarPair because the offset isn't 0.
676-
if !layout.is_uninhabited() {
677-
layout.backend_repr = BackendRepr::Memory { sized: true };
678-
}
679-
layout.size += this_offset;
680-
681-
true
682-
});
683-
684-
if !all_variants_fit {
685-
return None;
686-
}
687-
688-
let largest_niche = Niche::from_scalar(dl, niche_offset, niche_scalar);
689-
690-
let others_zst = variant_layouts
691-
.iter_enumerated()
692-
.all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO);
693-
let same_size = size == variant_layouts[largest_variant_index].size;
694-
let same_align = align == variants_info[largest_variant_index].align_abi;
695-
696-
let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited());
697-
let abi = if same_size && same_align && others_zst {
698-
match variant_layouts[largest_variant_index].backend_repr {
699-
// When the total alignment and size match, we can use the
700-
// same ABI as the scalar variant with the reserved niche.
701-
BackendRepr::Scalar(_) => BackendRepr::Scalar(niche_scalar),
702-
BackendRepr::ScalarPair(first, second) => {
703-
// Only the niche is guaranteed to be initialised,
704-
// so use union layouts for the other primitive.
705-
if niche_offset == Size::ZERO {
706-
BackendRepr::ScalarPair(niche_scalar, second.to_union())
707-
} else {
708-
BackendRepr::ScalarPair(first.to_union(), niche_scalar)
709-
}
710-
}
711-
_ => BackendRepr::Memory { sized: true },
712-
}
713-
} else {
714-
BackendRepr::Memory { sized: true }
715-
};
716-
717-
let layout = LayoutData {
718-
variants: Variants::Multiple {
719-
tag: niche_scalar,
720-
tag_encoding: TagEncoding::Niche {
721-
untagged_variant: largest_variant_index,
722-
niche_variants,
723-
niche_start,
724-
},
725-
tag_field: FieldIdx::new(0),
726-
variants: variant_layouts,
727-
},
728-
fields: FieldsShape::Arbitrary {
729-
offsets: [niche_offset].into(),
730-
in_memory_order: [FieldIdx::new(0)].into(),
731-
},
732-
backend_repr: abi,
733-
largest_niche,
734-
uninhabited,
735-
size,
736-
align: AbiAlign::new(align),
737-
max_repr_align,
738-
unadjusted_abi_align,
739-
randomization_seed: combined_seed,
740-
};
741-
742-
Some(layout)
743-
};
744-
745-
let calculate_niche_filling_layout_repacked =
746-
|| -> Option<LayoutData<FieldIdx, VariantIdx>> {
747-
struct VariantLayoutInfo {
748-
align_abi: Align,
749-
}
750-
751-
if repr.inhibit_enum_layout_opt() {
752-
return None;
753-
}
754-
755-
if variants.len() < 2 {
756-
return None;
757-
}
758-
759-
let mut align = dl.aggregate_align;
760-
let mut max_repr_align = repr.align;
761-
let mut unadjusted_abi_align = align;
762-
let mut combined_seed = repr.field_shuffle_seed;
763-
764-
let mut variants_info = IndexVec::<VariantIdx, _>::with_capacity(variants.len());
765-
let variant_layouts = variants
766-
.iter()
767-
.map(|v| {
768-
let st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
769-
770-
variants_info.push(VariantLayoutInfo { align_abi: st.align.abi });
771-
772-
align = align.max(st.align.abi);
773-
max_repr_align = max_repr_align.max(st.max_repr_align);
774-
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
775-
combined_seed = combined_seed.wrapping_add(st.randomization_seed);
776-
777-
Some(VariantLayout::from_layout(st))
778-
})
779-
.collect::<Option<IndexVec<VariantIdx, _>>>()?;
780-
781-
let max_variant_size = variant_layouts.iter().map(|layout| layout.size).max()?;
782-
783-
// Chooses the first max-sized niche-providing variant whose layout succeeds.
784-
for (largest_variant_index, _) in
785-
variant_layouts.iter_enumerated().filter(|&(_, layout)| {
786-
layout.size == max_variant_size && layout.largest_niche.is_some()
787-
})
788-
{
627+
let try_niche_variant =
628+
|largest_variant_index: VariantIdx| -> Option<LayoutData<FieldIdx, VariantIdx>> {
629+
//so niche_variant candidates don't contaminate each other if one fails
789630
let mut variant_layouts = variant_layouts.clone();
790631

791632
let all_indices = variants.indices();
792633
let needs_disc = |index: VariantIdx| {
793634
index != largest_variant_index && !absent(&variants[index])
794635
};
795-
let Some(niche_variants_start) = all_indices.clone().find(|v| needs_disc(*v))
796-
else {
797-
continue;
798-
};
799-
let Some(niche_variants_end) = all_indices.rev().find(|v| needs_disc(*v))
800-
else {
801-
continue;
636+
let niche_variants = RangeInclusive {
637+
start: all_indices.clone().find(|v| needs_disc(*v)).unwrap(),
638+
last: all_indices.rev().find(|v| needs_disc(*v)).unwrap(),
802639
};
803-
let niche_variants = niche_variants_start..=niche_variants_end;
804640

805-
let count = (niche_variants.end().index() as u128
806-
- niche_variants.start().index() as u128)
641+
let count = (niche_variants.last.index() as u128
642+
- niche_variants.start.index() as u128)
807643
+ 1;
808644

809645
// Use the largest niche in the largest variant.
810-
let Some(niche) = variant_layouts[largest_variant_index].largest_niche else {
811-
continue;
812-
};
813-
let Some((niche_start, niche_scalar)) = niche.reserve(dl, count) else {
814-
continue;
815-
};
646+
let niche = variant_layouts[largest_variant_index].largest_niche?;
647+
let (niche_start, niche_scalar) = niche.reserve(dl, count)?;
816648
let niche_offset = niche.offset;
817649
let niche_size = niche.value.size(dl);
818650
let size = variant_layouts[largest_variant_index].size.align_to(align);
@@ -834,41 +666,46 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
834666
let this_align = variants_info[i].align_abi;
835667
let this_offset = (niche_offset + niche_size).align_to(this_align);
836668

837-
if this_offset + layout.size > size {
838-
// The ordinary niche-filling path can only move a non-largest variant as a
839-
// whole before or after the chosen niche. If that fails, try placing the
840-
// variant's fields individually while treating the niche bytes as reserved.
841-
if let Some(repacked) = self.try_layout_variant_around_niche(
842-
&variants[i],
843-
repr,
844-
i,
845-
size,
846-
align,
847-
niche_offset,
848-
niche_size,
849-
) {
850-
*layout = VariantLayout::from_layout(repacked);
851-
return true;
669+
if this_offset + layout.size <= size {
670+
// It'll fit, but we need to make some adjustments.
671+
for offset in layout.field_offsets.iter_mut() {
672+
*offset += this_offset;
852673
}
853-
return false;
854-
}
855674

856-
// It'll fit, but we need to make some adjustments.
857-
for offset in layout.field_offsets.iter_mut() {
858-
*offset += this_offset;
859-
}
675+
// It can't be a Scalar or ScalarPair because the offset isn't 0.
676+
if !layout.is_uninhabited() {
677+
layout.backend_repr = BackendRepr::Memory { sized: true };
678+
}
679+
layout.size += this_offset;
860680

861-
// It can't be a Scalar or ScalarPair because the offset isn't 0.
862-
if !layout.is_uninhabited() {
863-
layout.backend_repr = BackendRepr::Memory { sized: true };
681+
return true;
682+
}
683+
// Repacking is currently only on future edition. For editions where it is disabled,
684+
// fall back to the original niche-filling behaviour.
685+
if !repr.can_repack_variant_around_niche() {
686+
return false;
687+
}
688+
// The ordinary niche-filling path can only move a non-largest variant as a
689+
// whole before or after the chosen niche. If that fails, try placing the
690+
// variant's fields individually while treating the niche bytes as reserved.
691+
if let Some(repacked) = self.try_layout_variant_around_niche(
692+
&variants[i],
693+
repr,
694+
i,
695+
size,
696+
align,
697+
niche_offset,
698+
niche_size,
699+
) {
700+
*layout = VariantLayout::from_layout(repacked);
701+
return true;
864702
}
865-
layout.size += this_offset;
866703

867-
true
704+
false
868705
});
869706

870707
if !all_variants_fit {
871-
continue;
708+
return None;
872709
}
873710

874711
let largest_niche = Niche::from_scalar(dl, niche_offset, niche_scalar);
@@ -900,7 +737,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
900737
BackendRepr::Memory { sized: true }
901738
};
902739

903-
return Some(LayoutData {
740+
let layout = LayoutData {
904741
variants: Variants::Multiple {
905742
tag: niche_scalar,
906743
tag_encoding: TagEncoding::Niche {
@@ -923,18 +760,35 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
923760
max_repr_align,
924761
unadjusted_abi_align,
925762
randomization_seed: combined_seed,
926-
});
763+
};
764+
765+
Some(layout)
766+
};
767+
if repr.can_repack_variant_around_niche() {
768+
let max_variant_size = variant_layouts.iter().map(|layout| layout.size).max()?;
769+
// Chooses the first max-sized niche-providing variant whose layout succeeds.
770+
for (largest_variant_index, _) in
771+
variant_layouts.iter_enumerated().filter(|&(_, layout)| {
772+
layout.size == max_variant_size && layout.largest_niche.is_some()
773+
})
774+
{
775+
if let Some(layout) = try_niche_variant(largest_variant_index) {
776+
return Some(layout);
777+
}
927778
}
928779

929780
None
930-
};
931-
932-
let niche_filling_layout = if repr.can_repack_variant_around_niche() {
933-
calculate_niche_filling_layout_repacked()
934-
} else {
935-
calculate_niche_filling_layout()
781+
} else {
782+
let largest_variant_index = variant_layouts
783+
.iter_enumerated()
784+
.max_by_key(|(_i, layout)| layout.size.bytes())
785+
.map(|(i, _layout)| i)?;
786+
try_niche_variant(largest_variant_index)
787+
}
936788
};
937789

790+
let niche_filling_layout = calculate_niche_filling_layout();
791+
938792
let discr_type = repr.discr_type();
939793
let discr_int = Integer::from_attr(dl, discr_type);
940794
// Because we can only represent one range of valid values, we'll look for the

0 commit comments

Comments
 (0)