1
Fork 0

compiler: BackendRepr::inherent_{size,align} -> scalar_{size,align}

This pair of fn was introduced to perform invariant checks for scalars.
Their current behavior doesn't mesh as well with checking SIMD types,
so change the name of the fn to reflect their actual use-case and
refactor the corresponding checks.

Also simplify the returns from Option<AbiAndPrefAlign> to Option<Align>,
because every site was mapping away the "preferred" alignment anyways.
This commit is contained in:
Jubilee Young 2025-02-18 18:53:15 -08:00
parent efff15afea
commit 5c474fd99b
3 changed files with 78 additions and 63 deletions

View file

@ -310,10 +310,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align };
let mut max_repr_align = repr.align; let mut max_repr_align = repr.align;
// If all the non-ZST fields have the same ABI and union ABI optimizations aren't // If all the non-ZST fields have the same repr and union repr optimizations aren't
// disabled, we can use that common ABI for the union as a whole. // disabled, we can use that common repr for the union as a whole.
struct AbiMismatch; struct AbiMismatch;
let mut common_non_zst_abi_and_align = if repr.inhibits_union_abi_opt() { let mut common_non_zst_repr_and_align = if repr.inhibits_union_abi_opt() {
// Can't optimize // Can't optimize
Err(AbiMismatch) Err(AbiMismatch)
} else { } else {
@ -337,14 +337,14 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
continue; continue;
} }
if let Ok(common) = common_non_zst_abi_and_align { if let Ok(common) = common_non_zst_repr_and_align {
// Discard valid range information and allow undef // Discard valid range information and allow undef
let field_abi = field.backend_repr.to_union(); let field_abi = field.backend_repr.to_union();
if let Some((common_abi, common_align)) = common { if let Some((common_abi, common_align)) = common {
if common_abi != field_abi { if common_abi != field_abi {
// Different fields have different ABI: disable opt // Different fields have different ABI: disable opt
common_non_zst_abi_and_align = Err(AbiMismatch); common_non_zst_repr_and_align = Err(AbiMismatch);
} else { } else {
// Fields with the same non-Aggregate ABI should also // Fields with the same non-Aggregate ABI should also
// have the same alignment // have the same alignment
@ -357,7 +357,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
} }
} else { } else {
// First non-ZST field: record its ABI and alignment // First non-ZST field: record its ABI and alignment
common_non_zst_abi_and_align = Ok(Some((field_abi, field.align.abi))); common_non_zst_repr_and_align = Ok(Some((field_abi, field.align.abi)));
} }
} }
} }
@ -376,16 +376,25 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// If all non-ZST fields have the same ABI, we may forward that ABI // If all non-ZST fields have the same ABI, we may forward that ABI
// for the union as a whole, unless otherwise inhibited. // for the union as a whole, unless otherwise inhibited.
let abi = match common_non_zst_abi_and_align { let backend_repr = match common_non_zst_repr_and_align {
Err(AbiMismatch) | Ok(None) => BackendRepr::Memory { sized: true }, Err(AbiMismatch) | Ok(None) => BackendRepr::Memory { sized: true },
Ok(Some((abi, _))) => { Ok(Some((repr, _))) => match repr {
if abi.inherent_align(dl).map(|a| a.abi) != Some(align.abi) {
// Mismatched alignment (e.g. union is #[repr(packed)]): disable opt // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _)
if repr.scalar_align(dl).unwrap() != align.abi =>
{
BackendRepr::Memory { sized: true } BackendRepr::Memory { sized: true }
} else {
abi
} }
// Vectors require at least element alignment, else disable the opt
BackendRepr::Vector { element, count: _ } if element.align(dl).abi > align.abi => {
BackendRepr::Memory { sized: true }
} }
// the alignment tests passed and we can use this
BackendRepr::Scalar(..)
| BackendRepr::ScalarPair(..)
| BackendRepr::Vector { .. }
| BackendRepr::Memory { .. } => repr,
},
}; };
let Some(union_field_count) = NonZeroUsize::new(only_variant.len()) else { let Some(union_field_count) = NonZeroUsize::new(only_variant.len()) else {
@ -400,7 +409,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
Ok(LayoutData { Ok(LayoutData {
variants: Variants::Single { index: only_variant_idx }, variants: Variants::Single { index: only_variant_idx },
fields: FieldsShape::Union(union_field_count), fields: FieldsShape::Union(union_field_count),
backend_repr: abi, backend_repr,
largest_niche: None, largest_niche: None,
uninhabited: false, uninhabited: false,
align, align,

View file

@ -1454,37 +1454,38 @@ impl BackendRepr {
matches!(*self, BackendRepr::Scalar(s) if s.is_bool()) matches!(*self, BackendRepr::Scalar(s) if s.is_bool())
} }
/// Returns the fixed alignment of this ABI, if any is mandated. /// The psABI alignment for a `Scalar` or `ScalarPair`
pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> { ///
Some(match *self { /// `None` for other variants.
BackendRepr::Scalar(s) => s.align(cx), pub fn scalar_align<C: HasDataLayout>(&self, cx: &C) -> Option<Align> {
BackendRepr::ScalarPair(s1, s2) => s1.align(cx).max(s2.align(cx)), match *self {
BackendRepr::Vector { element, count } => { BackendRepr::Scalar(s) => Some(s.align(cx).abi),
cx.data_layout().vector_align(element.size(cx) * count) BackendRepr::ScalarPair(s1, s2) => Some(s1.align(cx).max(s2.align(cx)).abi),
// The align of a Vector can vary in surprising ways
BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => None,
} }
BackendRepr::Memory { .. } => return None,
})
} }
/// Returns the fixed size of this ABI, if any is mandated. /// The psABI size for a `Scalar` or `ScalarPair`
pub fn inherent_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> { ///
Some(match *self { /// `None` for other variants
BackendRepr::Scalar(s) => { pub fn scalar_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
match *self {
// No padding in scalars. // No padding in scalars.
s.size(cx) BackendRepr::Scalar(s) => Some(s.size(cx)),
}
BackendRepr::ScalarPair(s1, s2) => {
// May have some padding between the pair. // May have some padding between the pair.
BackendRepr::ScalarPair(s1, s2) => {
let field2_offset = s1.size(cx).align_to(s2.align(cx).abi); let field2_offset = s1.size(cx).align_to(s2.align(cx).abi);
(field2_offset + s2.size(cx)).align_to(self.inherent_align(cx)?.abi) let size = (field2_offset + s2.size(cx)).align_to(
self.scalar_align(cx)
// We absolutely must have an answer here or everything is FUBAR.
.unwrap(),
);
Some(size)
} }
BackendRepr::Vector { element, count } => { // The size of a Vector can vary in surprising ways
// No padding in vectors, except possibly for trailing padding BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => None,
// to make the size a multiple of align (e.g. for vectors of size 3).
(element.size(cx) * count).align_to(self.inherent_align(cx)?.abi)
} }
BackendRepr::Memory { .. } => return None,
})
} }
/// Discard validity range information and allow undef. /// Discard validity range information and allow undef.

View file

@ -69,31 +69,30 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou
} }
fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
// Verify the ABI mandated alignment and size. // Verify the ABI-mandated alignment and size for scalars.
let align = layout.backend_repr.inherent_align(cx).map(|align| align.abi); let align = layout.backend_repr.scalar_align(cx);
let size = layout.backend_repr.inherent_size(cx); let size = layout.backend_repr.scalar_size(cx);
let Some((align, size)) = align.zip(size) else { if let Some(align) = align {
assert_matches!(
layout.layout.backend_repr(),
BackendRepr::Memory { .. },
"ABI unexpectedly missing alignment and/or size in {layout:#?}"
);
return;
};
assert_eq!( assert_eq!(
layout.layout.align().abi, layout.layout.align().abi,
align, align,
"alignment mismatch between ABI and layout in {layout:#?}" "alignment mismatch between ABI and layout in {layout:#?}"
); );
}
if let Some(size) = size {
assert_eq!( assert_eq!(
layout.layout.size(), layout.layout.size(),
size, size,
"size mismatch between ABI and layout in {layout:#?}" "size mismatch between ABI and layout in {layout:#?}"
); );
}
// Verify per-ABI invariants // Verify per-ABI invariants
match layout.layout.backend_repr() { match layout.layout.backend_repr() {
BackendRepr::Scalar(_) => { BackendRepr::Scalar(_) => {
// These must always be present for `Scalar` types.
let align = align.unwrap();
let size = size.unwrap();
// Check that this matches the underlying field. // Check that this matches the underlying field.
let inner = skip_newtypes(cx, layout); let inner = skip_newtypes(cx, layout);
assert!( assert!(
@ -235,9 +234,15 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou
"`ScalarPair` second field with bad ABI in {inner:#?}", "`ScalarPair` second field with bad ABI in {inner:#?}",
); );
} }
BackendRepr::Vector { element, .. } => { BackendRepr::Vector { element, count } => {
assert!(align >= element.align(cx).abi); // just sanity-checking `vector_align`. let align = layout.align.abi;
// FIXME: Do some kind of check of the inner type, like for Scalar and ScalarPair. let size = layout.size;
let element_align = element.align(cx).abi;
let element_size = element.size(cx);
// Currently, vectors must always be aligned to at least their elements:
assert!(align >= element_align);
// And the size has to be element * count plus alignment padding, of course
assert!(size == (element_size * count).align_to(align));
} }
BackendRepr::Memory { .. } => {} // Nothing to check. BackendRepr::Memory { .. } => {} // Nothing to check.
} }