1
Fork 0

rustc: store CachedLayout for each variant of enum Layout's instead of Struct.

This commit is contained in:
Eduard-Mihai Burtescu 2017-09-17 19:34:28 +03:00
parent bd51a2bc19
commit ed788a62f6
8 changed files with 131 additions and 175 deletions

View file

@ -78,7 +78,7 @@ use hir;
/// Internal storage
pub struct GlobalArenas<'tcx> {
// internings
layout: TypedArena<Layout>,
layout: TypedArena<Layout<'tcx>>,
// references
generics: TypedArena<ty::Generics>,
@ -918,7 +918,7 @@ pub struct GlobalCtxt<'tcx> {
stability_interner: RefCell<FxHashSet<&'tcx attr::Stability>>,
layout_interner: RefCell<FxHashSet<&'tcx Layout>>,
layout_interner: RefCell<FxHashSet<&'tcx Layout<'tcx>>>,
/// A vector of every trait accessible in the whole crate
/// (i.e. including those from subcrates). This is used only for
@ -1016,7 +1016,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
interned
}
pub fn intern_layout(self, layout: Layout) -> &'gcx Layout {
pub fn intern_layout(self, layout: Layout<'gcx>) -> &'gcx Layout<'gcx> {
if let Some(layout) = self.layout_interner.borrow().get(&layout) {
return layout;
}

View file

@ -1004,7 +1004,7 @@ pub const FAT_PTR_ADDR: usize = 0;
pub const FAT_PTR_EXTRA: usize = 1;
/// Describes how the fields of a type are located in memory.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum FieldPlacement<'a> {
/// Array-like placement. Can also express
/// unions, by using a stride of zero bytes.
@ -1058,7 +1058,7 @@ impl<'a> FieldPlacement<'a> {
/// Describes how values of the type are passed by target ABIs,
/// in terms of categories of C types there are ABI rules for.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Abi {
Scalar(Primitive),
Vector {
@ -1141,8 +1141,8 @@ impl Abi {
/// For ADTs, it also includes field placement and enum optimizations.
/// NOTE: Because Layout is interned, redundant information should be
/// kept to a minimum, e.g. it includes no sub-component Ty or Layout.
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum Layout {
#[derive(PartialEq, Eq, Hash, Debug)]
pub enum Layout<'a> {
/// TyBool, TyChar, TyInt, TyUint, TyFloat, TyRawPtr, TyRef or TyFnPtr.
Scalar(Primitive),
@ -1184,7 +1184,7 @@ pub enum Layout {
// the largest space between two consecutive discriminants and
// taking everything else as the (shortest) discriminant range.
discr_range: RangeInclusive<u64>,
variants: Vec<Struct>,
variants: Vec<CachedLayout<'a>>,
size: Size,
align: Align,
primitive_align: Align,
@ -1202,7 +1202,7 @@ pub enum Layout {
nndiscr: u64,
discr: Primitive,
discr_offset: Size,
variants: Vec<Struct>,
variants: Vec<CachedLayout<'a>>,
size: Size,
align: Align,
primitive_align: Align,
@ -1228,9 +1228,9 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
}
}
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct CachedLayout<'tcx> {
pub layout: &'tcx Layout,
pub layout: &'tcx Layout<'tcx>,
pub fields: FieldPlacement<'tcx>,
pub abi: Abi,
}
@ -1262,7 +1262,7 @@ pub fn provide(providers: &mut ty::maps::Providers) {
};
}
impl<'a, 'tcx> Layout {
impl<'a, 'tcx> Layout<'tcx> {
fn compute_uncached(tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>)
@ -1624,7 +1624,9 @@ impl<'a, 'tcx> Layout {
size: st[discr].stride(),
align,
primitive_align,
variants: st,
variants: st.into_iter().map(|variant| {
success(Univariant(variant))
}).collect::<Result<Vec<_>, _>>()?,
});
}
}
@ -1730,7 +1732,9 @@ impl<'a, 'tcx> Layout {
// FIXME: should be u128?
discr_range: (min as u64)..=(max as u64),
variants,
variants: variants.into_iter().map(|variant| {
success(Univariant(variant))
}).collect::<Result<Vec<_>, _>>()?,
size,
align,
primitive_align,
@ -1897,6 +1901,10 @@ impl<'a, 'tcx> Layout {
.iter()
.map(|f| (f.name, f.ty(tcx, substs)))
.collect();
let variant_layout = match *variant_layout.layout {
Univariant(ref variant) => variant,
_ => bug!()
};
build_variant_info(Some(variant_def.name),
&fields,
variant_layout)
@ -2084,7 +2092,7 @@ impl<'a, 'tcx> SizeSkeleton<'tcx> {
pub struct FullLayout<'tcx> {
pub ty: Ty<'tcx>,
pub variant_index: Option<usize>,
pub layout: &'tcx Layout,
pub layout: &'tcx Layout<'tcx>,
pub fields: FieldPlacement<'tcx>,
pub abi: Abi,
}
@ -2198,27 +2206,22 @@ impl<'a, 'tcx> FullLayout<'tcx> {
variants[variant_index].fields.len()
};
let (fields, abi) = match *self.layout {
Univariant(_) => (self.fields, self.abi),
let (layout, fields, abi) = match *self.layout {
Univariant(_) => (self.layout, self.fields, self.abi),
NullablePointer { ref variants, .. } |
General { ref variants, .. } => {
let variant = &variants[variant_index];
(FieldPlacement::Arbitrary {
offsets: &variant.offsets
}, Abi::Aggregate {
sized: true,
align: variant.align,
primitive_align: variant.primitive_align,
size: variant.stride(),
})
let variant = variants[variant_index];
(variant.layout, variant.fields, variant.abi)
}
_ => (FieldPlacement::union(count), self.abi)
_ => bug!()
};
assert_eq!(fields.count(), count);
FullLayout {
variant_index: Some(variant_index),
layout,
fields,
abi,
..*self
@ -2348,8 +2351,7 @@ impl<'a, 'tcx> FullLayout<'tcx> {
}
}
impl<'gcx> HashStable<StableHashingContext<'gcx>> for Layout
{
impl<'gcx> HashStable<StableHashingContext<'gcx>> for Layout<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {

View file

@ -764,7 +764,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
.zip(variants)
.map(|(variant, variant_layout)| {
// Subtract the size of the enum discriminant
let bytes = variant_layout.min_size
let bytes = variant_layout.abi.size(cx.tcx)
.bytes()
.saturating_sub(discr_size);

View file

@ -87,7 +87,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
} else {
l
};
llty.set_struct_body(&struct_llfields(cx, variant_layout, variant), variant.packed)
llty.set_struct_body(&struct_llfields(cx, variant_layout), variant.packed)
},
_ => bug!("This function cannot handle {} with layout {:#?}", t, l)
}
@ -105,8 +105,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
layout::Univariant(ref variant) => {
match name {
None => {
Type::struct_(cx, &struct_llfields(cx, l, &variant),
variant.packed)
Type::struct_(cx, &struct_llfields(cx, l), variant.packed)
}
Some(name) => {
Type::named_struct(cx, name)
@ -166,8 +165,11 @@ pub fn memory_index_to_gep(index: u64) -> u64 {
}
pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
layout: FullLayout<'tcx>,
variant: &layout::Struct) -> Vec<Type> {
layout: FullLayout<'tcx>) -> Vec<Type> {
let variant = match *layout.layout {
layout::Univariant(ref variant) => variant,
_ => bug!("unexpected {:#?}", layout)
};
let field_count = layout.fields.count();
debug!("struct_llfields: variant: {:?}", variant);
let mut offset = Size::from_bytes(0);

View file

@ -1153,37 +1153,33 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
layout::General { ref variants, .. } => {
let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata
.expect(""));
variants
.iter()
.enumerate()
.map(|(i, struct_def)| {
let (variant_type_metadata, member_desc_factory) =
describe_enum_variant(cx,
self.enum_type,
struct_def,
i,
&adt.variants[i],
discriminant_info,
self.containing_scope,
self.span);
(0..variants.len()).map(|i| {
let variant = self.type_rep.for_variant(i);
let (variant_type_metadata, member_desc_factory) =
describe_enum_variant(cx,
variant,
&adt.variants[i],
discriminant_info,
self.containing_scope,
self.span);
let member_descriptions = member_desc_factory
.create_member_descriptions(cx);
let member_descriptions = member_desc_factory
.create_member_descriptions(cx);
set_members_of_composite_type(cx,
variant_type_metadata,
&member_descriptions);
MemberDescription {
name: "".to_string(),
type_metadata: variant_type_metadata,
offset: Size::from_bytes(0),
size: struct_def.stride(),
align: struct_def.align,
flags: DIFlags::FlagZero
}
}).collect()
set_members_of_composite_type(cx,
variant_type_metadata,
&member_descriptions);
MemberDescription {
name: "".to_string(),
type_metadata: variant_type_metadata,
offset: Size::from_bytes(0),
size: variant.size(cx),
align: variant.align(cx),
flags: DIFlags::FlagZero
}
}).collect()
},
layout::Univariant(ref variant) => {
layout::Univariant(_) => {
assert!(adt.variants.len() <= 1);
if adt.variants.is_empty() {
@ -1191,9 +1187,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
} else {
let (variant_type_metadata, member_description_factory) =
describe_enum_variant(cx,
self.enum_type,
variant,
0,
self.type_rep,
&adt.variants[0],
NoDiscriminant,
self.containing_scope,
@ -1210,8 +1204,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
name: "".to_string(),
type_metadata: variant_type_metadata,
offset: Size::from_bytes(0),
size: variant.stride(),
align: variant.align,
size: self.type_rep.size(cx),
align: self.type_rep.align(cx),
flags: DIFlags::FlagZero
}
]
@ -1221,16 +1215,13 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
nndiscr,
discr,
discr_offset,
ref variants,
..
} => {
let struct_def = &variants[nndiscr as usize];
let variant = self.type_rep.for_variant(nndiscr as usize);
// Create a description of the non-null variant
let (variant_type_metadata, member_description_factory) =
describe_enum_variant(cx,
self.enum_type,
struct_def,
nndiscr as usize,
variant,
&adt.variants[nndiscr as usize],
OptimizedDiscriminant,
self.containing_scope,
@ -1278,8 +1269,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
name,
type_metadata: variant_type_metadata,
offset: Size::from_bytes(0),
size: struct_def.stride(),
align: struct_def.align,
size: variant.size(cx),
align: variant.align(cx),
flags: DIFlags::FlagZero
}
]
@ -1330,78 +1321,48 @@ enum EnumDiscriminantInfo {
// descriptions of the fields of the variant. This is a rudimentary version of a
// full RecursiveTypeDescription.
fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
enum_type: Ty<'tcx>,
struct_def: &'tcx layout::Struct,
variant_index: usize,
layout: layout::FullLayout<'tcx>,
variant: &'tcx ty::VariantDef,
discriminant_info: EnumDiscriminantInfo,
containing_scope: DIScope,
span: Span)
-> (DICompositeType, MemberDescriptionFactory<'tcx>) {
let layout = cx.layout_of(enum_type);
let maybe_discr = match *layout.layout {
layout::General { .. } => Some(layout.field(cx, 0).ty),
_ => None,
};
let layout = layout.for_variant(variant_index);
let mut field_tys = (0..layout.fields.count()).map(|i| {
layout.field(cx, i).ty
}).collect::<Vec<_>>();
if let Some(discr) = maybe_discr {
field_tys.insert(0, discr);
}
// Could do some consistency checks here: size, align, field count, discr type
let variant_name = variant.name.as_str();
let unique_type_id = debug_context(cx).type_map
.borrow_mut()
.get_unique_type_id_of_enum_variant(
cx,
enum_type,
layout.ty,
&variant_name);
let metadata_stub = create_struct_stub(cx,
enum_type,
layout.ty,
&variant_name,
unique_type_id,
containing_scope);
// Get the argument names from the enum variant info
let mut arg_names: Vec<_> = match variant.ctor_kind {
CtorKind::Const => vec![],
CtorKind::Fn => {
variant.fields
.iter()
.enumerate()
.map(|(i, _)| format!("__{}", i))
.collect()
}
CtorKind::Fictive => {
variant.fields
.iter()
.map(|f| f.name.to_string())
.collect()
}
};
// If this is not a univariant enum, there is also the discriminant field.
let mut offsets = struct_def.offsets.clone();
match discriminant_info {
let (discr_offset, discr_arg) = match discriminant_info {
RegularDiscriminant(_) => {
arg_names.insert(0, "RUST$ENUM$DISR".to_string());
offsets.insert(0, Size::from_bytes(0));
let enum_layout = cx.layout_of(layout.ty);
(Some(enum_layout.fields.offset(0)),
Some(("RUST$ENUM$DISR".to_string(), enum_layout.field(cx, 0).ty)))
}
_ => { /* do nothing */ }
_ => (None, None),
};
let offsets = discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| {
layout.fields.offset(i)
})).collect();
// Build an array of (field name, field type) pairs to be captured in the factory closure.
let args: Vec<(String, Ty)> = arg_names.iter()
.zip(field_tys.iter())
.map(|(s, &t)| (s.to_string(), t))
.collect();
let args = discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| {
let name = if variant.ctor_kind == CtorKind::Fn {
format!("__{}", i)
} else {
variant.fields[i].name.to_string()
};
(name, layout.field(cx, i).ty)
})).collect();
let member_description_factory =
VariantMDF(VariantMemberDescriptionFactory {

View file

@ -1091,7 +1091,7 @@ fn trans_const_adt<'a, 'tcx>(
_ => 0,
};
match *l.layout {
layout::General { ref variants, .. } => {
layout::General { .. } => {
let discr = match *kind {
mir::AggregateKind::Adt(adt_def, _, _, _) => {
adt_def.discriminant_for_variant(ccx.tcx(), variant_index)
@ -1105,7 +1105,7 @@ fn trans_const_adt<'a, 'tcx>(
if let layout::Abi::Scalar(_) = l.abi {
discr
} else {
build_const_struct(ccx, l, &variants[variant_index], vals, Some(discr))
build_const_struct(ccx, l.for_variant(variant_index), vals, Some(discr))
}
}
layout::UntaggedUnion(ref un) => {
@ -1117,16 +1117,16 @@ fn trans_const_adt<'a, 'tcx>(
Const::new(C_struct(ccx, &contents, un.packed), t)
}
layout::Univariant(ref variant) => {
layout::Univariant(_) => {
assert_eq!(variant_index, 0);
build_const_struct(ccx, l, &variant, vals, None)
build_const_struct(ccx, l, vals, None)
}
layout::Vector { .. } => {
Const::new(C_vector(&vals.iter().map(|x| x.llval).collect::<Vec<_>>()), t)
}
layout::NullablePointer { ref variants, nndiscr, .. } => {
layout::NullablePointer { nndiscr, .. } => {
if variant_index as u64 == nndiscr {
build_const_struct(ccx, l, &variants[variant_index], vals, None)
build_const_struct(ccx, l.for_variant(variant_index), vals, None)
} else {
// Always use null even if it's not the `discrfield`th
// field; see #8506.
@ -1147,24 +1147,27 @@ fn trans_const_adt<'a, 'tcx>(
/// will read the wrong memory.
fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
layout: layout::FullLayout<'tcx>,
st: &layout::Struct,
vals: &[Const<'tcx>],
discr: Option<Const<'tcx>>)
-> Const<'tcx> {
assert_eq!(vals.len(), st.offsets.len());
assert_eq!(vals.len(), layout.fields.count());
// offset of current value
let mut offset = Size::from_bytes(0);
let mut cfields = Vec::new();
cfields.reserve(discr.is_some() as usize + 1 + st.offsets.len() * 2);
cfields.reserve(discr.is_some() as usize + 1 + layout.fields.count() * 2);
if let Some(discr) = discr {
cfields.push(discr.llval);
offset = ccx.size_of(discr.ty);
}
let st = match *layout.layout {
layout::Univariant(ref variant) => variant,
_ => bug!("unexpected {:#?}", layout)
};
let parts = st.field_index_by_increasing_offset().map(|i| {
(vals[i], st.offsets[i])
(vals[i], layout.fields.offset(i))
});
for (val, target_offset) in parts {
cfields.push(padding(ccx, target_offset - offset));
@ -1172,8 +1175,8 @@ fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
offset = target_offset + ccx.size_of(val.ty);
}
let size = layout.size(ccx);
cfields.push(padding(ccx, size - offset));
// Pad to the size of the whole type, not e.g. the variant.
cfields.push(padding(ccx, ccx.size_of(layout.ty) - offset));
Const::new(C_struct(ccx, &cfields, st.packed), layout.ty)
}

View file

@ -55,7 +55,7 @@ impl ops::BitOr for Alignment {
}
}
impl<'a> From<&'a Layout> for Alignment {
impl<'a> From<&'a Layout<'a>> for Alignment {
fn from(layout: &Layout) -> Self {
let (packed, align) = match *layout {
Layout::UntaggedUnion(ref un) => (un.packed, un.align),
@ -234,27 +234,24 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
}
// Discriminant field of enums.
match *l.layout {
layout::NullablePointer { .. } if l.variant_index.is_none() => {
let ty = ccx.llvm_type_of(field.ty);
let size = field.size(ccx).bytes();
if let layout::NullablePointer { .. } = *l.layout {
let ty = ccx.llvm_type_of(field.ty);
let size = field.size(ccx).bytes();
// If the discriminant is not on a multiple of the primitive's size,
// we need to go through i8*. Also assume the worst alignment.
if offset % size != 0 {
let byte_ptr = bcx.pointercast(self.llval, Type::i8p(ccx));
let byte_ptr = bcx.inbounds_gep(byte_ptr, &[C_usize(ccx, offset)]);
let byte_align = Alignment::Packed(Align::from_bytes(1, 1).unwrap());
return LvalueRef::new_sized(
bcx.pointercast(byte_ptr, ty.ptr_to()), field.ty, byte_align);
}
let discr_ptr = bcx.pointercast(self.llval, ty.ptr_to());
// If the discriminant is not on a multiple of the primitive's size,
// we need to go through i8*. Also assume the worst alignment.
if offset % size != 0 {
let byte_ptr = bcx.pointercast(self.llval, Type::i8p(ccx));
let byte_ptr = bcx.inbounds_gep(byte_ptr, &[C_usize(ccx, offset)]);
let byte_align = Alignment::Packed(Align::from_bytes(1, 1).unwrap());
return LvalueRef::new_sized(
bcx.inbounds_gep(discr_ptr, &[C_usize(ccx, offset / size)]),
field.ty, alignment);
bcx.pointercast(byte_ptr, ty.ptr_to()), field.ty, byte_align);
}
_ => {}
let discr_ptr = bcx.pointercast(self.llval, ty.ptr_to());
return LvalueRef::new_sized(
bcx.inbounds_gep(discr_ptr, &[C_usize(ccx, offset / size)]),
field.ty, alignment);
}
let simple = || {
@ -271,10 +268,8 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
};
// Check whether the variant being used is packed, if applicable.
let is_packed = match (l.layout, l.variant_index) {
(&layout::Univariant(ref variant), _) => variant.packed,
(&layout::NullablePointer { ref variants, .. }, Some(v)) |
(&layout::General { ref variants, .. }, Some(v)) => variants[v].packed,
let is_packed = match *l.layout {
layout::Univariant(ref variant) => variant.packed,
_ => return simple()
};
@ -470,13 +465,13 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
};
// If this is an enum, cast to the appropriate variant struct type.
let layout = bcx.ccx.layout_of(ty).for_variant(variant_index);
match *layout.layout {
layout::NullablePointer { ref variants, .. } |
layout::General { ref variants, .. } => {
let st = &variants[variant_index];
let layout = bcx.ccx.layout_of(ty);
let variant_layout = layout.for_variant(variant_index);
match (layout.layout, variant_layout.layout) {
(&layout::NullablePointer { .. }, &layout::Univariant(ref st)) |
(&layout::General { .. }, &layout::Univariant(ref st)) => {
let variant_ty = Type::struct_(bcx.ccx,
&adt::struct_llfields(bcx.ccx, layout, st), st.packed);
&adt::struct_llfields(bcx.ccx, variant_layout), st.packed);
downcast.llval = bcx.pointercast(downcast.llval, variant_ty.ptr_to());
}
_ => {}

View file

@ -242,7 +242,9 @@ impl<'tcx> LayoutLlvmExt for FullLayout<'tcx> {
}
match *self.layout {
Layout::Scalar { .. } |
Layout::UntaggedUnion { .. } => {
Layout::UntaggedUnion { .. } |
Layout::NullablePointer { .. } |
Layout::General { .. } => {
bug!("FullLayout::llvm_field_index({:?}): not applicable", self)
}
@ -258,15 +260,6 @@ impl<'tcx> LayoutLlvmExt for FullLayout<'tcx> {
Layout::Univariant(ref variant) => {
adt::memory_index_to_gep(variant.memory_index[index] as u64)
}
Layout::NullablePointer { ref variants, .. } |
Layout::General { ref variants, .. } => {
if let Some(v) = self.variant_index {
adt::memory_index_to_gep(variants[v].memory_index[index] as u64)
} else {
bug!("FullLayout::llvm_field_index({:?}): not applicable", self)
}
}
}
}
}