rustc: do not inject discriminant fields into Layout::General's variants.

This commit is contained in:
Eduard-Mihai Burtescu 2017-09-10 17:15:29 +03:00
parent 8afa3a01e6
commit 44eef7c9ac
7 changed files with 70 additions and 89 deletions

View File

@ -651,14 +651,14 @@ pub struct Struct {
} }
/// Info required to optimize struct layout. /// Info required to optimize struct layout.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] #[derive(Copy, Clone, Debug)]
enum StructKind { enum StructKind {
/// A tuple, closure, or univariant which cannot be coerced to unsized. /// A tuple, closure, or univariant which cannot be coerced to unsized.
AlwaysSizedUnivariant, AlwaysSizedUnivariant,
/// A univariant, the last field of which may be coerced to unsized. /// A univariant, the last field of which may be coerced to unsized.
MaybeUnsizedUnivariant, MaybeUnsizedUnivariant,
/// A univariant, but part of an enum. /// A univariant, but part of an enum.
EnumVariant, EnumVariant(Integer),
} }
impl<'a, 'tcx> Struct { impl<'a, 'tcx> Struct {
@ -692,30 +692,27 @@ impl<'a, 'tcx> Struct {
// Neither do 1-member and 2-member structs. // Neither do 1-member and 2-member structs.
// In addition, code in trans assume that 2-element structs can become pairs. // In addition, code in trans assume that 2-element structs can become pairs.
// It's easier to just short-circuit here. // It's easier to just short-circuit here.
let can_optimize = (fields.len() > 2 || StructKind::EnumVariant == kind) let (mut optimize, sort_ascending) = match kind {
&& (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty(); StructKind::AlwaysSizedUnivariant |
StructKind::MaybeUnsizedUnivariant => (fields.len() > 2, false),
let (optimize, sort_ascending) = match kind { StructKind::EnumVariant(discr) => {
StructKind::AlwaysSizedUnivariant => (can_optimize, false), (discr.size().bytes() == 1, true)
StructKind::MaybeUnsizedUnivariant => (can_optimize, false),
StructKind::EnumVariant => {
assert!(fields.len() >= 1, "Enum variants must have discriminants.");
(can_optimize && fields[0].size(dl).bytes() == 1, true)
} }
}; };
optimize &= (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
ret.offsets = vec![Size::from_bytes(0); fields.len()]; ret.offsets = vec![Size::from_bytes(0); fields.len()];
let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect(); let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
if optimize { if optimize {
let start = if let StructKind::EnumVariant = kind { 1 } else { 0 };
let end = if let StructKind::MaybeUnsizedUnivariant = kind { let end = if let StructKind::MaybeUnsizedUnivariant = kind {
fields.len() - 1 fields.len() - 1
} else { } else {
fields.len() fields.len()
}; };
if end > start { if end > 0 {
let optimizing = &mut inverse_memory_index[start..end]; let optimizing = &mut inverse_memory_index[..end];
if sort_ascending { if sort_ascending {
optimizing.sort_by_key(|&x| fields[x as usize].align(dl).abi()); optimizing.sort_by_key(|&x| fields[x as usize].align(dl).abi());
} else { } else {
@ -734,13 +731,17 @@ impl<'a, 'tcx> Struct {
// field 5 with offset 0 puts 0 in offsets[5]. // field 5 with offset 0 puts 0 in offsets[5].
// At the bottom of this function, we use inverse_memory_index to produce memory_index. // At the bottom of this function, we use inverse_memory_index to produce memory_index.
if let StructKind::EnumVariant = kind {
assert_eq!(inverse_memory_index[0], 0,
"Enum variant discriminants must have the lowest offset.");
}
let mut offset = Size::from_bytes(0); let mut offset = Size::from_bytes(0);
if let StructKind::EnumVariant(discr) = kind {
offset = discr.size();
if !ret.packed {
let align = discr.align(dl);
ret.align = ret.align.max(align);
ret.primitive_align = ret.primitive_align.max(align);
}
}
for i in inverse_memory_index.iter() { for i in inverse_memory_index.iter() {
let field = fields[*i as usize]; let field = fields[*i as usize];
if !ret.sized { if !ret.sized {
@ -1112,8 +1113,9 @@ pub enum Layout {
variants: Union, variants: Union,
}, },
/// General-case enums: for each case there is a struct, and they /// General-case enums: for each case there is a struct, and they all have
/// all start with a field for the discriminant. /// all space reserved for the discriminant, and their first field starts
/// at a non-0 offset, after where the discriminant would go.
General { General {
discr: Integer, discr: Integer,
variants: Vec<Struct>, variants: Vec<Struct>,
@ -1495,21 +1497,17 @@ impl<'a, 'tcx> Layout {
// We're interested in the smallest alignment, so start large. // We're interested in the smallest alignment, so start large.
let mut start_align = Align::from_bytes(256, 256).unwrap(); let mut start_align = Align::from_bytes(256, 256).unwrap();
// Create the set of structs that represent each variant // Create the set of structs that represent each variant.
// Use the minimum integer type we figured out above
let discr = Scalar { value: Int(min_ity), non_zero: false };
let mut variants = variants.into_iter().map(|fields| { let mut variants = variants.into_iter().map(|fields| {
let mut fields = fields.into_iter().map(|field| { let fields = fields.into_iter().map(|field| {
field.layout(tcx, param_env) field.layout(tcx, param_env)
}).collect::<Result<Vec<_>, _>>()?; }).collect::<Result<Vec<_>, _>>()?;
fields.insert(0, &discr);
let st = Struct::new(dl, let st = Struct::new(dl,
&fields, &fields,
&def.repr, StructKind::EnumVariant, ty)?; &def.repr, StructKind::EnumVariant(min_ity), ty)?;
// Find the first field we can't move later // Find the first field we can't move later
// to make room for a larger discriminant. // to make room for a larger discriminant.
// It is important to skip the first field. for i in st.field_index_by_increasing_offset() {
for i in st.field_index_by_increasing_offset().skip(1) {
let field = fields[i]; let field = fields[i];
let field_align = field.align(dl); let field_align = field.align(dl);
if field.size(dl).bytes() != 0 || field_align.abi() != 1 { if field.size(dl).bytes() != 0 || field_align.abi() != 1 {
@ -1569,9 +1567,8 @@ impl<'a, 'tcx> Layout {
let new_ity_size = Int(ity).size(dl); let new_ity_size = Int(ity).size(dl);
for variant in &mut variants { for variant in &mut variants {
for i in variant.offsets.iter_mut() { for i in variant.offsets.iter_mut() {
// The first field is the discrimminant, at offset 0. if *i <= old_ity_size {
// These aren't in order, and we need to skip it. assert_eq!(*i, old_ity_size);
if *i <= old_ity_size && *i > Size::from_bytes(0) {
*i = new_ity_size; *i = new_ity_size;
} }
} }
@ -1759,7 +1756,7 @@ impl<'a, 'tcx> Layout {
General { ref variants, .. } => { General { ref variants, .. } => {
let v = variant_index.expect("variant index required"); let v = variant_index.expect("variant index required");
variants[v].offsets[i + 1] variants[v].offsets[i]
} }
StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
@ -1857,21 +1854,12 @@ impl<'a, 'tcx> Layout {
} }
}; };
enum Fields<'a> {
WithDiscrim(&'a Struct),
NoDiscrim(&'a Struct),
}
let build_variant_info = |n: Option<ast::Name>, let build_variant_info = |n: Option<ast::Name>,
flds: &[(ast::Name, Ty<'tcx>)], flds: &[(ast::Name, Ty<'tcx>)],
layout: Fields| { s: &Struct| {
let (s, field_offsets) = match layout {
Fields::WithDiscrim(s) => (s, &s.offsets[1..]),
Fields::NoDiscrim(s) => (s, &s.offsets[0..]),
};
let field_info: Vec<_> = let field_info: Vec<_> =
flds.iter() flds.iter()
.zip(field_offsets.iter()) .zip(&s.offsets)
.map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset)) .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset))
.collect(); .collect();
@ -1904,7 +1892,7 @@ impl<'a, 'tcx> Layout {
None, None,
vec![build_variant_info(Some(variant_def.name), vec![build_variant_info(Some(variant_def.name),
&fields, &fields,
Fields::NoDiscrim(variant_layout))]); variant_layout)]);
} }
Layout::RawNullablePointer { nndiscr, value } => { Layout::RawNullablePointer { nndiscr, value } => {
debug!("print-type-size t: `{:?}` adt raw nullable nndiscr {} is {:?}", debug!("print-type-size t: `{:?}` adt raw nullable nndiscr {} is {:?}",
@ -1931,7 +1919,7 @@ impl<'a, 'tcx> Layout {
None, None,
vec![build_variant_info(Some(variant_def.name), vec![build_variant_info(Some(variant_def.name),
&fields, &fields,
Fields::NoDiscrim(variant_layout))]); variant_layout)]);
} else { } else {
// (This case arises for *empty* enums; so give it // (This case arises for *empty* enums; so give it
// zero variants.) // zero variants.)
@ -1953,7 +1941,7 @@ impl<'a, 'tcx> Layout {
.collect(); .collect();
build_variant_info(Some(variant_def.name), build_variant_info(Some(variant_def.name),
&fields, &fields,
Fields::WithDiscrim(variant_layout)) variant_layout)
}) })
.collect(); .collect();
record(adt_kind.into(), Some(discr.size()), variant_infos); record(adt_kind.into(), Some(discr.size()), variant_infos);

View File

@ -90,8 +90,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
(l.for_variant(nndiscr as usize), nonnull), (l.for_variant(nndiscr as usize), nonnull),
_ => unreachable!() _ => unreachable!()
}; };
llty.set_struct_body(&struct_llfields(cx, variant_layout, variant, None), llty.set_struct_body(&struct_llfields(cx, variant_layout, variant), variant.packed)
variant.packed)
}, },
_ => bug!("This function cannot handle {} with layout {:#?}", t, l) _ => bug!("This function cannot handle {} with layout {:#?}", t, l)
} }
@ -116,7 +115,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
match name { match name {
None => { None => {
Type::struct_(cx, &struct_llfields(cx, l.for_variant(nndiscr as usize), Type::struct_(cx, &struct_llfields(cx, l.for_variant(nndiscr as usize),
nonnull, None), nonnull),
nonnull.packed) nonnull.packed)
} }
Some(name) => { Some(name) => {
@ -127,7 +126,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
layout::Univariant { ref variant, .. } => { layout::Univariant { ref variant, .. } => {
match name { match name {
None => { None => {
Type::struct_(cx, &struct_llfields(cx, l, &variant, None), Type::struct_(cx, &struct_llfields(cx, l, &variant),
variant.packed) variant.packed)
} }
Some(name) => { Some(name) => {
@ -209,23 +208,16 @@ pub fn memory_index_to_gep(index: u64) -> u64 {
pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
layout: TyLayout<'tcx>, layout: TyLayout<'tcx>,
variant: &layout::Struct, variant: &layout::Struct) -> Vec<Type> {
discr: Option<Ty<'tcx>>) -> Vec<Type> { let field_count = layout.field_count();
let field_count = (discr.is_some() as usize) + layout.field_count();
debug!("struct_llfields: variant: {:?}", variant); debug!("struct_llfields: variant: {:?}", variant);
let mut offset = Size::from_bytes(0); let mut offset = Size::from_bytes(0);
let mut result: Vec<Type> = Vec::with_capacity(1 + field_count * 2); let mut result: Vec<Type> = Vec::with_capacity(1 + field_count * 2);
let field_iter = variant.field_index_by_increasing_offset().map(|i| { for i in variant.field_index_by_increasing_offset() {
let ty = if i == 0 && discr.is_some() { let field = layout.field(cx, i);
cx.layout_of(discr.unwrap()) let target_offset = variant.offsets[i as usize];
} else {
layout.field(cx, i - discr.is_some() as usize)
};
(i, ty, variant.offsets[i as usize])
});
for (index, field, target_offset) in field_iter {
debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}", debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}",
index, field, offset, target_offset); i, field, offset, target_offset);
assert!(target_offset >= offset); assert!(target_offset >= offset);
let padding = target_offset - offset; let padding = target_offset - offset;
result.push(Type::array(&Type::i8(cx), padding.bytes())); result.push(Type::array(&Type::i8(cx), padding.bytes()));

View File

@ -1340,7 +1340,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
// Creates MemberDescriptions for the fields of a single enum variant. // Creates MemberDescriptions for the fields of a single enum variant.
struct VariantMemberDescriptionFactory<'tcx> { struct VariantMemberDescriptionFactory<'tcx> {
// Cloned from the layout::Struct describing the variant. // Cloned from the layout::Struct describing the variant.
offsets: &'tcx [layout::Size], offsets: Vec<layout::Size>,
args: Vec<(String, Ty<'tcx>)>, args: Vec<(String, Ty<'tcx>)>,
discriminant_type_metadata: Option<DIType>, discriminant_type_metadata: Option<DIType>,
span: Span, span: Span,
@ -1436,8 +1436,12 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}; };
// If this is not a univariant enum, there is also the discriminant field. // If this is not a univariant enum, there is also the discriminant field.
let mut offsets = struct_def.offsets.clone();
match discriminant_info { match discriminant_info {
RegularDiscriminant(_) => arg_names.insert(0, "RUST$ENUM$DISR".to_string()), RegularDiscriminant(_) => {
arg_names.insert(0, "RUST$ENUM$DISR".to_string());
offsets.insert(0, Size::from_bytes(0));
}
_ => { /* do nothing */ } _ => { /* do nothing */ }
}; };
@ -1449,7 +1453,7 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let member_description_factory = let member_description_factory =
VariantMDF(VariantMemberDescriptionFactory { VariantMDF(VariantMemberDescriptionFactory {
offsets: &struct_def.offsets[..], offsets,
args, args,
discriminant_type_metadata: match discriminant_info { discriminant_type_metadata: match discriminant_info {
RegularDiscriminant(discriminant_type_metadata) => { RegularDiscriminant(discriminant_type_metadata) => {

View File

@ -1108,11 +1108,8 @@ fn trans_const_adt<'a, 'tcx>(
layout::General { discr: d, ref variants, .. } => { layout::General { discr: d, ref variants, .. } => {
let variant = &variants[variant_index]; let variant = &variants[variant_index];
let lldiscr = C_int(Type::from_integer(ccx, d), variant_index as i64); let lldiscr = C_int(Type::from_integer(ccx, d), variant_index as i64);
let mut vals_with_discr = vec![ build_const_struct(ccx, l, &variant, vals,
Const::new(lldiscr, d.to_ty(ccx.tcx(), false)) Some(Const::new(lldiscr, d.to_ty(ccx.tcx(), false))))
];
vals_with_discr.extend_from_slice(vals);
build_const_struct(ccx, l, &variant, &vals_with_discr)
} }
layout::UntaggedUnion { ref variants, .. }=> { layout::UntaggedUnion { ref variants, .. }=> {
assert_eq!(variant_index, 0); assert_eq!(variant_index, 0);
@ -1125,7 +1122,7 @@ fn trans_const_adt<'a, 'tcx>(
} }
layout::Univariant { ref variant, .. } => { layout::Univariant { ref variant, .. } => {
assert_eq!(variant_index, 0); assert_eq!(variant_index, 0);
build_const_struct(ccx, l, &variant, vals) build_const_struct(ccx, l, &variant, vals, None)
} }
layout::Vector { .. } => { layout::Vector { .. } => {
Const::new(C_vector(&vals.iter().map(|x| x.llval).collect::<Vec<_>>()), t) Const::new(C_vector(&vals.iter().map(|x| x.llval).collect::<Vec<_>>()), t)
@ -1140,7 +1137,7 @@ fn trans_const_adt<'a, 'tcx>(
} }
layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
if variant_index as u64 == nndiscr { if variant_index as u64 == nndiscr {
build_const_struct(ccx, l, &nonnull, vals) build_const_struct(ccx, l, &nonnull, vals, None)
} else { } else {
// Always use null even if it's not the `discrfield`th // Always use null even if it's not the `discrfield`th
// field; see #8506. // field; see #8506.
@ -1162,14 +1159,20 @@ fn trans_const_adt<'a, 'tcx>(
fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
layout: layout::TyLayout<'tcx>, layout: layout::TyLayout<'tcx>,
st: &layout::Struct, st: &layout::Struct,
vals: &[Const<'tcx>]) vals: &[Const<'tcx>],
discr: Option<Const<'tcx>>)
-> Const<'tcx> { -> Const<'tcx> {
assert_eq!(vals.len(), st.offsets.len()); assert_eq!(vals.len(), st.offsets.len());
// offset of current value // offset of current value
let mut offset = Size::from_bytes(0); let mut offset = Size::from_bytes(0);
let mut cfields = Vec::new(); let mut cfields = Vec::new();
cfields.reserve(st.offsets.len()*2); cfields.reserve(discr.is_some() as usize + 1 + st.offsets.len() * 2);
if let Some(discr) = discr {
cfields.push(discr.llval);
offset = ccx.size_of(discr.ty);
}
let parts = st.field_index_by_increasing_offset().map(|i| { let parts = st.field_index_by_increasing_offset().map(|i| {
(vals[i], st.offsets[i]) (vals[i], st.offsets[i])

View File

@ -236,14 +236,6 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
_ => {} _ => {}
} }
// Adjust the index to account for enum discriminants in variants.
let mut ix = ix;
if let layout::General { .. } = *l {
if l.variant_index.is_some() {
ix += 1;
}
}
let simple = || { let simple = || {
LvalueRef { LvalueRef {
llval: bcx.struct_gep(self.llval, l.llvm_field_index(ix)), llval: bcx.struct_gep(self.llval, l.llvm_field_index(ix)),
@ -474,11 +466,10 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
// If this is an enum, cast to the appropriate variant struct type. // If this is an enum, cast to the appropriate variant struct type.
let layout = bcx.ccx.layout_of(ty).for_variant(variant_index); let layout = bcx.ccx.layout_of(ty).for_variant(variant_index);
if let layout::General { discr, ref variants, .. } = *layout { if let layout::General { ref variants, .. } = *layout {
let st = &variants[variant_index]; let st = &variants[variant_index];
let variant_ty = Type::struct_(bcx.ccx, let variant_ty = Type::struct_(bcx.ccx,
&adt::struct_llfields(bcx.ccx, layout, st, &adt::struct_llfields(bcx.ccx, layout, st), st.packed);
Some(discr.to_ty(bcx.tcx(), false))), st.packed);
downcast.llval = bcx.pointercast(downcast.llval, variant_ty.ptr_to()); downcast.llval = bcx.pointercast(downcast.llval, variant_ty.ptr_to());
} }

View File

@ -54,7 +54,7 @@ pub fn inline_enum_const() -> E<i8, i16> {
#[no_mangle] #[no_mangle]
pub fn low_align_const() -> E<i16, [i16; 3]> { pub fn low_align_const() -> E<i16, [i16; 3]> {
// Check that low_align_const and high_align_const use the same constant // Check that low_align_const and high_align_const use the same constant
// CHECK: load {{.*}} bitcast ({ [0 x i8], i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] // CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]]
*&E::A(0) *&E::A(0)
} }
@ -62,6 +62,6 @@ pub fn low_align_const() -> E<i16, [i16; 3]> {
#[no_mangle] #[no_mangle]
pub fn high_align_const() -> E<i16, i32> { pub fn high_align_const() -> E<i16, i32> {
// Check that low_align_const and high_align_const use the same constant // Check that low_align_const and high_align_const use the same constant
// CHECK: load {{.*}} bitcast ({ [0 x i8], i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]] // CHECK: load {{.*}} bitcast ({ i16, [0 x i8], i16, [4 x i8] }** [[LOW_HIGH_REF]]
*&E::A(0) *&E::A(0)
} }

View File

@ -108,6 +108,9 @@ pub fn main() {
let array_expected_size = round_up(28, align_of::<Eu64NonCLike<[u32; 5]>>()); let array_expected_size = round_up(28, align_of::<Eu64NonCLike<[u32; 5]>>());
assert_eq!(size_of::<Eu64NonCLike<[u32; 5]>>(), array_expected_size); assert_eq!(size_of::<Eu64NonCLike<[u32; 5]>>(), array_expected_size);
assert_eq!(size_of::<Eu64NonCLike<[u32; 6]>>(), 32); assert_eq!(size_of::<Eu64NonCLike<[u32; 6]>>(), 32);
assert_eq!(align_of::<Eu32>(), align_of::<u32>());
assert_eq!(align_of::<Eu64NonCLike<u8>>(), align_of::<u64>());
} }
// Rounds x up to the next multiple of a // Rounds x up to the next multiple of a