Auto merge of #36904 - camlorn:field_offsets_refactor, r=eddyb

Refactor layout to store offsets of fields, not offsets after fields

This is the next PR moving us towards being able to reorder struct fields.

The old code implicitly stored the offset of the first field.  This is inadequate because the first field may no longer be offset 0 in future.  This PR refactors `layout` to use a `offsets` vector instead of a `offset_after_field` vector.
This commit is contained in:
bors 2016-10-02 14:13:27 -07:00 committed by GitHub
commit 1cdc0fb11a
5 changed files with 47 additions and 76 deletions

View File

@ -511,11 +511,11 @@ pub struct Struct {
/// If true, the size is exact, otherwise it's only a lower bound.
pub sized: bool,
/// Offsets for the first byte after each field.
/// That is, field_offset(i) = offset_after_field[i - 1] and the
/// whole structure's size is the last offset, excluding padding.
// FIXME(eddyb) use small vector optimization for the common case.
pub offset_after_field: Vec<Size>
/// Offsets for the first byte of each field.
/// FIXME(eddyb) use small vector optimization for the common case.
pub offsets: Vec<Size>,
pub min_size: Size,
}
impl<'a, 'gcx, 'tcx> Struct {
@ -524,7 +524,8 @@ pub fn new(dl: &TargetDataLayout, packed: bool) -> Struct {
align: if packed { dl.i8_align } else { dl.aggregate_align },
packed: packed,
sized: true,
offset_after_field: vec![]
offsets: vec![],
min_size: Size::from_bytes(0),
}
}
@ -534,12 +535,14 @@ pub fn extend<I>(&mut self, dl: &TargetDataLayout,
scapegoat: Ty<'gcx>)
-> Result<(), LayoutError<'gcx>>
where I: Iterator<Item=Result<&'a Layout, LayoutError<'gcx>>> {
self.offset_after_field.reserve(fields.size_hint().0);
self.offsets.reserve(fields.size_hint().0);
let mut offset = self.min_size;
for field in fields {
if !self.sized {
bug!("Struct::extend: field #{} of `{}` comes after unsized field",
self.offset_after_field.len(), scapegoat);
self.offsets.len(), scapegoat);
}
let field = field?;
@ -548,34 +551,29 @@ pub fn extend<I>(&mut self, dl: &TargetDataLayout,
}
// Invariant: offset < dl.obj_size_bound() <= 1<<61
let mut offset = if !self.packed {
if !self.packed {
let align = field.align(dl);
self.align = self.align.max(align);
self.offset_after_field.last_mut().map_or(Size::from_bytes(0), |last| {
*last = last.abi_align(align);
*last
})
} else {
self.offset_after_field.last().map_or(Size::from_bytes(0), |&last| last)
};
offset = offset.abi_align(align);
}
self.offsets.push(offset);
offset = offset.checked_add(field.size(dl), dl)
.map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?;
self.offset_after_field.push(offset);
}
self.min_size = offset;
Ok(())
}
/// Get the size without trailing alignment padding.
pub fn min_size(&self) -> Size {
self.offset_after_field.last().map_or(Size::from_bytes(0), |&last| last)
}
/// Get the size with trailing aligment padding.
pub fn stride(&self) -> Size {
self.min_size().abi_align(self.align)
self.min_size.abi_align(self.align)
}
/// Determine whether a structure would be zero-sized, given its fields.
@ -671,15 +669,6 @@ pub fn non_zero_field_path<I>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
}
Ok(None)
}
pub fn offset_of_field(&self, index: usize) -> Size {
assert!(index < self.offset_after_field.len());
if index == 0 {
Size::from_bytes(0)
} else {
self.offset_after_field[index-1]
}
}
}
/// An untagged union.
@ -1138,7 +1127,7 @@ pub fn compute_uncached(ty: Ty<'gcx>,
});
let mut st = Struct::new(dl, false);
st.extend(dl, discr.iter().map(Ok).chain(fields), ty)?;
size = cmp::max(size, st.min_size());
size = cmp::max(size, st.min_size);
align = align.max(st.align);
Ok(st)
}).collect::<Result<Vec<_>, _>>()?;
@ -1171,12 +1160,16 @@ pub fn compute_uncached(ty: Ty<'gcx>,
let old_ity_size = Int(min_ity).size(dl);
let new_ity_size = Int(ity).size(dl);
for variant in &mut variants {
for offset in &mut variant.offset_after_field {
for offset in &mut variant.offsets[1..] {
if *offset > old_ity_size {
break;
}
*offset = new_ity_size;
}
// We might be making the struct larger.
if variant.min_size <= old_ity_size {
variant.min_size = new_ity_size;
}
}
}

View File

@ -738,7 +738,7 @@ fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
.zip(variants)
.map(|(variant, variant_layout)| {
// Subtract the size of the enum discriminant
let bytes = variant_layout.min_size().bytes()
let bytes = variant_layout.min_size.bytes()
.saturating_sub(discr_size);
debug!("- variant `{}` is {} bytes large", variant.node.name, bytes);

View File

@ -632,7 +632,7 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
let meta = val.meta;
let offset = st.offset_of_field(ix).bytes();
let offset = st.offsets[ix].bytes();
let unaligned_offset = C_uint(bcx.ccx(), offset);
// Get the alignment of the field
@ -695,9 +695,9 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D
let lldiscr = C_integral(Type::from_integer(ccx, d), discr.0 as u64, true);
let mut vals_with_discr = vec![lldiscr];
vals_with_discr.extend_from_slice(vals);
let mut contents = build_const_struct(ccx, &variant.offset_after_field[..],
&vals_with_discr[..], variant.packed);
let needed_padding = l.size(dl).bytes() - variant.min_size().bytes();
let mut contents = build_const_struct(ccx, &variant,
&vals_with_discr[..]);
let needed_padding = l.size(dl).bytes() - variant.min_size.bytes();
if needed_padding > 0 {
contents.push(padding(ccx, needed_padding));
}
@ -711,7 +711,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D
layout::Univariant { ref variant, .. } => {
assert_eq!(discr, Disr(0));
let contents = build_const_struct(ccx,
&variant.offset_after_field[..], vals, variant.packed);
&variant, vals);
C_struct(ccx, &contents[..], variant.packed)
}
layout::Vector { .. } => {
@ -728,9 +728,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D
}
layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
if discr.0 == nndiscr {
C_struct(ccx, &build_const_struct(ccx,
&nonnull.offset_after_field[..],
vals, nonnull.packed),
C_struct(ccx, &build_const_struct(ccx, &nonnull, vals),
false)
} else {
let fields = compute_fields(ccx, t, nndiscr as usize, false);
@ -739,10 +737,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D
// field; see #8506.
C_null(type_of::sizing_type_of(ccx, ty))
}).collect::<Vec<ValueRef>>();
C_struct(ccx, &build_const_struct(ccx,
&nonnull.offset_after_field[..],
&vals[..],
false),
C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]),
false)
}
}
@ -759,11 +754,10 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D
/// a two-element struct will locate it at offset 4, and accesses to it
/// will read the wrong memory.
fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
offset_after_field: &[layout::Size],
vals: &[ValueRef],
packed: bool)
st: &layout::Struct,
vals: &[ValueRef])
-> Vec<ValueRef> {
assert_eq!(vals.len(), offset_after_field.len());
assert_eq!(vals.len(), st.offsets.len());
if vals.len() == 0 {
return Vec::new();
@ -772,24 +766,19 @@ fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// offset of current value
let mut offset = 0;
let mut cfields = Vec::new();
let target_offsets = offset_after_field.iter().map(|i| i.bytes());
for (&val, target_offset) in vals.iter().zip(target_offsets) {
assert!(!is_undef(val));
cfields.push(val);
offset += machine::llsize_of_alloc(ccx, val_ty(val));
if !packed {
let val_align = machine::llalign_of_min(ccx, val_ty(val));
offset = roundup(offset, val_align);
}
if offset != target_offset {
let offsets = st.offsets.iter().map(|i| i.bytes());
for (&val, target_offset) in vals.iter().zip(offsets) {
if offset < target_offset {
cfields.push(padding(ccx, target_offset - offset));
offset = target_offset;
}
assert!(!is_undef(val));
cfields.push(val);
offset += machine::llsize_of_alloc(ccx, val_ty(val));
}
let size = offset_after_field.last().unwrap();
if offset < size.bytes() {
cfields.push(padding(ccx, size.bytes() - offset));
if offset < st.min_size.bytes() {
cfields.push(padding(ccx, st.min_size.bytes() - offset));
}
cfields

View File

@ -127,7 +127,7 @@ pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
Layout::FatPointer { .. } => true,
Layout::Univariant { ref variant, .. } => {
// There must be only 2 fields.
if variant.offset_after_field.len() != 2 {
if variant.offsets.len() != 2 {
return false;
}

View File

@ -335,20 +335,9 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
let layout = ccx.layout_of(t);
debug!("DST {} layout: {:?}", t, layout);
// Returns size in bytes of all fields except the last one
// (we will be recursing on the last one).
fn local_prefix_bytes(variant: &ty::layout::Struct) -> u64 {
let fields = variant.offset_after_field.len();
if fields > 1 {
variant.offset_after_field[fields - 2].bytes()
} else {
0
}
}
let (sized_size, sized_align) = match *layout {
ty::layout::Layout::Univariant { ref variant, .. } => {
(local_prefix_bytes(variant), variant.align.abi())
(variant.offsets.last().map_or(0, |o| o.bytes()), variant.align.abi())
}
_ => {
bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}",