fix computing the dynamic alignment of packed structs with dyn trait tails
This commit is contained in:
parent
7176b8babd
commit
7e4c4271f4
@ -84,10 +84,13 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
debug!("DST {} layout: {:?}", t, layout);
|
debug!("DST {} layout: {:?}", t, layout);
|
||||||
|
|
||||||
let i = layout.fields.count() - 1;
|
let i = layout.fields.count() - 1;
|
||||||
let sized_size = layout.fields.offset(i).bytes();
|
let unsized_offset_unadjusted = layout.fields.offset(i).bytes();
|
||||||
let sized_align = layout.align.abi.bytes();
|
let sized_align = layout.align.abi.bytes();
|
||||||
debug!("DST {} statically sized prefix size: {} align: {}", t, sized_size, sized_align);
|
debug!(
|
||||||
let sized_size = bx.const_usize(sized_size);
|
"DST {} offset of dyn field: {}, statically sized align: {}",
|
||||||
|
t, unsized_offset_unadjusted, sized_align
|
||||||
|
);
|
||||||
|
let unsized_offset_unadjusted = bx.const_usize(unsized_offset_unadjusted);
|
||||||
let sized_align = bx.const_usize(sized_align);
|
let sized_align = bx.const_usize(sized_align);
|
||||||
|
|
||||||
// Recurse to get the size of the dynamically sized field (must be
|
// Recurse to get the size of the dynamically sized field (must be
|
||||||
@ -95,26 +98,26 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
let field_ty = layout.field(bx, i).ty;
|
let field_ty = layout.field(bx, i).ty;
|
||||||
let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info);
|
let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info);
|
||||||
|
|
||||||
// FIXME (#26403, #27023): We should be adding padding
|
// # First compute the dynamic alignment
|
||||||
// to `sized_size` (to accommodate the `unsized_align`
|
|
||||||
// required of the unsized field that follows) before
|
|
||||||
// summing it with `sized_size`. (Note that since #26403
|
|
||||||
// is unfixed, we do not yet add the necessary padding
|
|
||||||
// here. But this is where the add would go.)
|
|
||||||
|
|
||||||
// Return the sum of sizes and max of aligns.
|
// For packed types, we need to cap the alignment.
|
||||||
let size = bx.add(sized_size, unsized_size);
|
if let ty::Adt(def, _) = t.kind()
|
||||||
|
&& let Some(packed) = def.repr().pack
|
||||||
// Packed types ignore the alignment of their fields.
|
{
|
||||||
if let ty::Adt(def, _) = t.kind() {
|
if packed.bytes() == 1 {
|
||||||
if def.repr().packed() {
|
// We know this will be capped to 1.
|
||||||
unsized_align = sized_align;
|
unsized_align = bx.const_usize(1);
|
||||||
|
} else {
|
||||||
|
// We have to dynamically compute `min(unsized_align, packed)`.
|
||||||
|
let packed = bx.const_usize(packed.bytes());
|
||||||
|
let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed);
|
||||||
|
unsized_align = bx.select(cmp, unsized_align, packed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose max of two known alignments (combined value must
|
// Choose max of two known alignments (combined value must
|
||||||
// be aligned according to more restrictive of the two).
|
// be aligned according to more restrictive of the two).
|
||||||
let align = match (
|
let full_align = match (
|
||||||
bx.const_to_opt_u128(sized_align, false),
|
bx.const_to_opt_u128(sized_align, false),
|
||||||
bx.const_to_opt_u128(unsized_align, false),
|
bx.const_to_opt_u128(unsized_align, false),
|
||||||
) {
|
) {
|
||||||
@ -129,6 +132,19 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// # Then compute the dynamic size
|
||||||
|
|
||||||
|
// The full formula for the size would be:
|
||||||
|
// let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align);
|
||||||
|
// let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align);
|
||||||
|
// However, `unsized_size` is a multiple of `unsized_align`.
|
||||||
|
// Therefore, we can equivalently do the `align_to(unsized_align)` *after* adding `unsized_size`:
|
||||||
|
// let full_size = (unsized_offset_unadjusted + unsized_size).align_to(unsized_align).align_to(full_align);
|
||||||
|
// Furthermore, `align >= unsized_align`, and therefore we only need to do:
|
||||||
|
// let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align);
|
||||||
|
|
||||||
|
let full_size = bx.add(unsized_offset_unadjusted, unsized_size);
|
||||||
|
|
||||||
// Issue #27023: must add any necessary padding to `size`
|
// Issue #27023: must add any necessary padding to `size`
|
||||||
// (to make it a multiple of `align`) before returning it.
|
// (to make it a multiple of `align`) before returning it.
|
||||||
//
|
//
|
||||||
@ -140,12 +156,12 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
//
|
//
|
||||||
// `(size + (align-1)) & -align`
|
// `(size + (align-1)) & -align`
|
||||||
let one = bx.const_usize(1);
|
let one = bx.const_usize(1);
|
||||||
let addend = bx.sub(align, one);
|
let addend = bx.sub(full_align, one);
|
||||||
let add = bx.add(size, addend);
|
let add = bx.add(full_size, addend);
|
||||||
let neg = bx.neg(align);
|
let neg = bx.neg(full_align);
|
||||||
let size = bx.and(add, neg);
|
let full_size = bx.and(add, neg);
|
||||||
|
|
||||||
(size, align)
|
(full_size, full_align)
|
||||||
}
|
}
|
||||||
_ => bug!("size_and_align_of_dst: {t} not supported"),
|
_ => bug!("size_and_align_of_dst: {t} not supported"),
|
||||||
}
|
}
|
||||||
|
@ -686,14 +686,8 @@ pub(super) fn size_and_align_of(
|
|||||||
assert!(layout.fields.count() > 0);
|
assert!(layout.fields.count() > 0);
|
||||||
trace!("DST layout: {:?}", layout);
|
trace!("DST layout: {:?}", layout);
|
||||||
|
|
||||||
let sized_size = layout.fields.offset(layout.fields.count() - 1);
|
let unsized_offset_unadjusted = layout.fields.offset(layout.fields.count() - 1);
|
||||||
let sized_align = layout.align.abi;
|
let sized_align = layout.align.abi;
|
||||||
trace!(
|
|
||||||
"DST {} statically sized prefix size: {:?} align: {:?}",
|
|
||||||
layout.ty,
|
|
||||||
sized_size,
|
|
||||||
sized_align
|
|
||||||
);
|
|
||||||
|
|
||||||
// Recurse to get the size of the dynamically sized field (must be
|
// Recurse to get the size of the dynamically sized field (must be
|
||||||
// the last field). Can't have foreign types here, how would we
|
// the last field). Can't have foreign types here, how would we
|
||||||
@ -707,36 +701,35 @@ pub(super) fn size_and_align_of(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME (#26403, #27023): We should be adding padding
|
// # First compute the dynamic alignment
|
||||||
// to `sized_size` (to accommodate the `unsized_align`
|
|
||||||
// required of the unsized field that follows) before
|
|
||||||
// summing it with `sized_size`. (Note that since #26403
|
|
||||||
// is unfixed, we do not yet add the necessary padding
|
|
||||||
// here. But this is where the add would go.)
|
|
||||||
|
|
||||||
// Return the sum of sizes and max of aligns.
|
// Packed type alignment needs to be capped.
|
||||||
let size = sized_size + unsized_size; // `Size` addition
|
|
||||||
|
|
||||||
// Packed types ignore the alignment of their fields.
|
|
||||||
if let ty::Adt(def, _) = layout.ty.kind() {
|
if let ty::Adt(def, _) = layout.ty.kind() {
|
||||||
if def.repr().packed() {
|
if let Some(packed) = def.repr().pack {
|
||||||
unsized_align = sized_align;
|
unsized_align = unsized_align.min(packed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose max of two known alignments (combined value must
|
// Choose max of two known alignments (combined value must
|
||||||
// be aligned according to more restrictive of the two).
|
// be aligned according to more restrictive of the two).
|
||||||
let align = sized_align.max(unsized_align);
|
let full_align = sized_align.max(unsized_align);
|
||||||
|
|
||||||
// Issue #27023: must add any necessary padding to `size`
|
// # Then compute the dynamic size
|
||||||
// (to make it a multiple of `align`) before returning it.
|
|
||||||
let size = size.align_to(align);
|
let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align);
|
||||||
|
let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align);
|
||||||
|
|
||||||
|
// Just for our sanitiy's sake, assert that this is equal to what codegen would compute.
|
||||||
|
assert_eq!(
|
||||||
|
full_size,
|
||||||
|
(unsized_offset_unadjusted + unsized_size).align_to(full_align)
|
||||||
|
);
|
||||||
|
|
||||||
// Check if this brought us over the size limit.
|
// Check if this brought us over the size limit.
|
||||||
if size > self.max_size_of_val() {
|
if full_size > self.max_size_of_val() {
|
||||||
throw_ub!(InvalidMeta(InvalidMetaKind::TooBig));
|
throw_ub!(InvalidMeta(InvalidMetaKind::TooBig));
|
||||||
}
|
}
|
||||||
Ok(Some((size, align)))
|
Ok(Some((full_size, full_align)))
|
||||||
}
|
}
|
||||||
ty::Dynamic(_, _, ty::Dyn) => {
|
ty::Dynamic(_, _, ty::Dyn) => {
|
||||||
let vtable = metadata.unwrap_meta().to_pointer(self)?;
|
let vtable = metadata.unwrap_meta().to_pointer(self)?;
|
||||||
|
21
src/tools/miri/tests/pass/packed-struct-dyn-trait.rs
Normal file
21
src/tools/miri/tests/pass/packed-struct-dyn-trait.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// run-pass
|
||||||
|
use std::ptr::addr_of;
|
||||||
|
|
||||||
|
// When the unsized tail is a `dyn Trait`, its alignments is only dynamically known. This means the
|
||||||
|
// packed(2) needs to be applied at runtime: the actual alignment of the field is `min(2,
|
||||||
|
// usual_alignment)`. Here we check that we do this right by comparing size, alignment, and field
|
||||||
|
// offset before and after unsizing.
|
||||||
|
fn main() {
|
||||||
|
#[repr(C, packed(2))]
|
||||||
|
struct Packed<T: ?Sized>(u8, core::mem::ManuallyDrop<T>);
|
||||||
|
|
||||||
|
let p = Packed(0, core::mem::ManuallyDrop::new(1));
|
||||||
|
let p: &Packed<usize> = &p;
|
||||||
|
let sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
|
||||||
|
let sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
|
||||||
|
let p: &Packed<dyn Send> = p;
|
||||||
|
let un_sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
|
||||||
|
let un_sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
|
||||||
|
assert_eq!(sized, un_sized);
|
||||||
|
assert_eq!(sized_offset, un_sized_offset);
|
||||||
|
}
|
21
tests/ui/packed/dyn-trait.rs
Normal file
21
tests/ui/packed/dyn-trait.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// run-pass
|
||||||
|
use std::ptr::addr_of;
|
||||||
|
|
||||||
|
// When the unsized tail is a `dyn Trait`, its alignments is only dynamically known. This means the
|
||||||
|
// packed(2) needs to be applied at runtime: the actual alignment of the field is `min(2,
|
||||||
|
// usual_alignment)`. Here we check that we do this right by comparing size, alignment, and field
|
||||||
|
// offset before and after unsizing.
|
||||||
|
fn main() {
|
||||||
|
#[repr(C, packed(2))]
|
||||||
|
struct Packed<T: ?Sized>(u8, core::mem::ManuallyDrop<T>);
|
||||||
|
|
||||||
|
let p = Packed(0, core::mem::ManuallyDrop::new(1));
|
||||||
|
let p: &Packed<usize> = &p;
|
||||||
|
let sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
|
||||||
|
let sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
|
||||||
|
let p: &Packed<dyn Send> = p;
|
||||||
|
let un_sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
|
||||||
|
let un_sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
|
||||||
|
assert_eq!(sized, un_sized);
|
||||||
|
assert_eq!(sized_offset, un_sized_offset);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user