Factor out checks in layout check and add helper inherent_size.

This commit is contained in:
Luqman Aden 2023-02-18 19:21:07 -08:00
parent f2d81defa1
commit 3b1e535f36
3 changed files with 63 additions and 55 deletions

View File

@ -1273,7 +1273,7 @@ impl Abi {
matches!(*self, Abi::Scalar(_)) matches!(*self, Abi::Scalar(_))
} }
/// Returns the fixed alignment of this ABI, if any /// Returns the fixed alignment of this ABI, if any is mandated.
pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> { pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> {
Some(match *self { Some(match *self {
Abi::Scalar(s) => s.align(cx), Abi::Scalar(s) => s.align(cx),
@ -1287,6 +1287,27 @@ impl Abi {
}) })
} }
/// Returns the fixed size of this ABI, if any is mandated.
pub fn inherent_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
Some(match *self {
Abi::Scalar(s) => {
// No padding in scalars.
s.size(cx)
}
Abi::ScalarPair(s1, s2) => {
// May have some padding between the pair.
let field2_offset = s1.size(cx).align_to(s2.align(cx).abi);
(field2_offset + s2.size(cx)).align_to(self.inherent_align(cx)?.abi)
}
Abi::Vector { element, count } => {
// No padding in vectors, except possibly for trailing padding
// 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)
}
Abi::Uninhabited | Abi::Aggregate { .. } => return None,
})
}
/// Discard valid range information and allow undef /// Discard valid range information and allow undef
pub fn to_union(&self) -> Self { pub fn to_union(&self) -> Self {
assert!(self.is_sized()); assert!(self.is_sized());

View File

@ -4,7 +4,7 @@ use rustc_middle::ty::{
}; };
use rustc_target::abi::*; use rustc_target::abi::*;
use std::cmp; use std::assert_matches::assert_matches;
/// Enforce some basic invariants on layouts. /// Enforce some basic invariants on layouts.
pub(super) fn sanity_check_layout<'tcx>( pub(super) fn sanity_check_layout<'tcx>(
@ -68,21 +68,31 @@ pub(super) fn sanity_check_layout<'tcx>(
} }
fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: &TyAndLayout<'tcx>) { fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: &TyAndLayout<'tcx>) {
match layout.layout.abi() { // Verify the ABI mandated alignment and size.
Abi::Scalar(scalar) => { let align = layout.abi.inherent_align(cx).map(|align| align.abi);
// No padding in scalars. let size = layout.abi.inherent_size(cx);
let size = scalar.size(cx); let Some((align, size)) = align.zip(size) else {
let align = scalar.align(cx).abi; assert_matches!(
assert_eq!( layout.layout.abi(),
layout.layout.size(), Abi::Uninhabited | Abi::Aggregate { .. },
size, "ABI unexpectedly missing alignment and/or size in {layout:#?}"
"size mismatch between ABI and layout 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:#?}"
); );
assert_eq!(
layout.layout.size(),
size,
"size mismatch between ABI and layout in {layout:#?}"
);
// Verify per-ABI invariants
match layout.layout.abi() {
Abi::Scalar(_) => {
// 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!(
@ -135,24 +145,6 @@ pub(super) fn sanity_check_layout<'tcx>(
} }
} }
Abi::ScalarPair(scalar1, scalar2) => { Abi::ScalarPair(scalar1, scalar2) => {
// Sanity-check scalar pairs. Computing the expected size and alignment is a bit of work.
let size1 = scalar1.size(cx);
let align1 = scalar1.align(cx).abi;
let size2 = scalar2.size(cx);
let align2 = scalar2.align(cx).abi;
let align = cmp::max(align1, align2);
let field2_offset = size1.align_to(align2);
let size = (field2_offset + size2).align_to(align);
assert_eq!(
layout.layout.size(),
size,
"size mismatch between ABI and layout in {layout:#?}"
);
assert_eq!(
layout.layout.align().abi,
align,
"alignment mismatch between ABI and layout in {layout:#?}",
);
// Check that the underlying pair of fields matches. // Check that the underlying pair of fields matches.
let inner = skip_newtypes(cx, layout); let inner = skip_newtypes(cx, layout);
assert!( assert!(
@ -189,8 +181,9 @@ pub(super) fn sanity_check_layout<'tcx>(
"`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}" "`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}"
) )
}); });
assert!( assert_matches!(
fields.next().is_none(), fields.next(),
None,
"`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}" "`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}"
); );
// The fields might be in opposite order. // The fields might be in opposite order.
@ -200,6 +193,10 @@ pub(super) fn sanity_check_layout<'tcx>(
(offset2, field2, offset1, field1) (offset2, field2, offset1, field1)
}; };
// The fields should be at the right offset, and match the `scalar` layout. // The fields should be at the right offset, and match the `scalar` layout.
let size1 = scalar1.size(cx);
let align1 = scalar1.align(cx).abi;
let size2 = scalar2.size(cx);
let align2 = scalar2.align(cx).abi;
assert_eq!( assert_eq!(
offset1, offset1,
Size::ZERO, Size::ZERO,
@ -213,10 +210,12 @@ pub(super) fn sanity_check_layout<'tcx>(
field1.align.abi, align1, field1.align.abi, align1,
"`ScalarPair` first field with bad align in {inner:#?}", "`ScalarPair` first field with bad align in {inner:#?}",
); );
assert!( assert_matches!(
matches!(field1.abi, Abi::Scalar(_)), field1.abi,
Abi::Scalar(_),
"`ScalarPair` first field with bad ABI in {inner:#?}", "`ScalarPair` first field with bad ABI in {inner:#?}",
); );
let field2_offset = size1.align_to(align2);
assert_eq!( assert_eq!(
offset2, field2_offset, offset2, field2_offset,
"`ScalarPair` second field at bad offset in {inner:#?}", "`ScalarPair` second field at bad offset in {inner:#?}",
@ -229,27 +228,14 @@ pub(super) fn sanity_check_layout<'tcx>(
field2.align.abi, align2, field2.align.abi, align2,
"`ScalarPair` second field with bad align in {inner:#?}", "`ScalarPair` second field with bad align in {inner:#?}",
); );
assert!( assert_matches!(
matches!(field2.abi, Abi::Scalar(_)), field2.abi,
Abi::Scalar(_),
"`ScalarPair` second field with bad ABI in {inner:#?}", "`ScalarPair` second field with bad ABI in {inner:#?}",
); );
} }
Abi::Vector { count, element } => { Abi::Vector { element, .. } => {
// No padding in vectors, except possibly for trailing padding to make the size a multiple of align.
let size = element.size(cx) * count;
let align = cx.data_layout().vector_align(size).abi;
let size = size.align_to(align); // needed e.g. for vectors of size 3
assert!(align >= element.align(cx).abi); // just sanity-checking `vector_align`. assert!(align >= element.align(cx).abi); // just sanity-checking `vector_align`.
assert_eq!(
layout.layout.size(),
size,
"size mismatch between ABI and layout in {layout:#?}"
);
assert_eq!(
layout.layout.align().abi,
align,
"alignment mismatch between ABI and layout in {layout:#?}"
);
// FIXME: Do some kind of check of the inner type, like for Scalar and ScalarPair. // FIXME: Do some kind of check of the inner type, like for Scalar and ScalarPair.
} }
Abi::Uninhabited | Abi::Aggregate { .. } => {} // Nothing to check. Abi::Uninhabited | Abi::Aggregate { .. } => {} // Nothing to check.

View File

@ -5,6 +5,7 @@
//! This API is completely unstable and subject to change. //! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(assert_matches)]
#![feature(iterator_try_collect)] #![feature(iterator_try_collect)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(never_type)] #![feature(never_type)]