blob: 9d693e098cff04dcfd6dab9583438f86a23c1df8 [file] [log] [blame]
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! # Representation of Algebraic Data Types
//!
//! This module determines how to represent enums, structs, and tuples
//! based on their monomorphized types; it is responsible both for
//! choosing a representation and translating basic operations on
//! values of those types. (Note: exporting the representations for
//! debuggers is handled in debuginfo.rs, not here.)
//!
//! Note that the interface treats everything as a general case of an
//! enum, so structs/tuples/etc. have one pseudo-variant with
//! discriminant 0; i.e., as if they were a univariant enum.
//!
//! Having everything in one place will enable improvements to data
//! structure representation; possibilities include:
//!
//! - User-specified alignment (e.g., cacheline-aligning parts of
//! concurrently accessed data structures); LLVM can't represent this
//! directly, so we'd have to insert padding fields in any structure
//! that might contain one and adjust GEP indices accordingly. See
//! issue #4578.
//!
//! - Store nested enums' discriminants in the same word. Rather, if
//! some variants start with enums, and those enums representations
//! have unused alignment padding between discriminant and body, the
//! outer enum's discriminant can be stored there and those variants
//! can start at offset 0. Kind of fancy, and might need work to
//! make copies of the inner enum type cooperate, but it could help
//! with `Option` or `Result` wrapped around another enum.
//!
//! - Tagged pointers would be neat, but given that any type can be
//! used unboxed and any field can have pointers (including mutable)
//! taken to it, implementing them for Rust seems difficult.
use rustc::ty::{self, Ty};
use rustc::ty::layout::{self, Align, HasDataLayout, LayoutTyper, Size};
use context::CrateContext;
use monomorphize;
use type_::Type;
use type_of;
/// LLVM-level types are a little complicated.
///
/// C-like enums need to be actual ints, not wrapped in a struct,
/// because that changes the ABI on some platforms (see issue #10308).
///
/// For nominal types, in some cases, we need to use LLVM named structs
/// and fill in the actual contents in a second pass to prevent
/// unbounded recursion; see also the comments in `trans::type_of`.
pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
generic_type_of(cx, t, None)
}
pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>, name: &str) -> Type {
generic_type_of(cx, t, Some(name))
}
pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>, llty: &mut Type) {
let l = cx.layout_of(t);
debug!("finish_type_of: {} with layout {:#?}", t, l);
match *l {
layout::CEnum { .. } | layout::General { .. }
| layout::UntaggedUnion { .. } | layout::RawNullablePointer { .. } => { }
layout::Univariant { ..}
| layout::StructWrappedNullablePointer { .. } => {
let (nonnull_variant_index, nonnull_variant, packed) = match *l {
layout::Univariant { ref variant, .. } => (0, variant, variant.packed),
layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } =>
(nndiscr, nonnull, nonnull.packed),
_ => unreachable!()
};
llty.set_struct_body(&struct_llfields(cx, t, nonnull_variant_index as usize,
nonnull_variant, None),
packed)
},
_ => bug!("This function cannot handle {} with layout {:#?}", t, l)
}
}
fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>,
name: Option<&str>) -> Type {
let l = cx.layout_of(t);
debug!("adt::generic_type_of t: {:?} name: {:?}", t, name);
match *l {
layout::CEnum { discr, .. } => Type::from_integer(cx, discr),
layout::RawNullablePointer { nndiscr, .. } => {
let (def, substs) = match t.sty {
ty::TyAdt(d, s) => (d, s),
_ => bug!("{} is not an ADT", t)
};
let nnty = monomorphize::field_ty(cx.tcx(), substs,
&def.variants[nndiscr as usize].fields[0]);
if let layout::Scalar { value: layout::Pointer, .. } = *cx.layout_of(nnty) {
Type::i8p(cx)
} else {
type_of::type_of(cx, nnty)
}
}
layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
match name {
None => {
Type::struct_(cx, &struct_llfields(cx, t, nndiscr as usize, nonnull, None),
nonnull.packed)
}
Some(name) => {
Type::named_struct(cx, name)
}
}
}
layout::Univariant { ref variant, .. } => {
match name {
None => {
Type::struct_(cx, &struct_llfields(cx, t, 0, &variant, None),
variant.packed)
}
Some(name) => {
Type::named_struct(cx, name)
}
}
}
layout::UntaggedUnion { ref variants, .. }=> {
// Use alignment-sized ints to fill all the union storage.
let fill = union_fill(cx, variants.stride(), variants.align);
match name {
None => {
Type::struct_(cx, &[fill], variants.packed)
}
Some(name) => {
let mut llty = Type::named_struct(cx, name);
llty.set_struct_body(&[fill], variants.packed);
llty
}
}
}
layout::General { discr, size, align, primitive_align, .. } => {
// We need a representation that has:
// * The alignment of the most-aligned field
// * The size of the largest variant (rounded up to that alignment)
// * No alignment padding anywhere any variant has actual data
// (currently matters only for enums small enough to be immediate)
// * The discriminant in an obvious place.
//
// So we start with the discriminant, pad it up to the alignment with
// more of its own type, then use alignment-sized ints to get the rest
// of the size.
let discr_ty = Type::from_integer(cx, discr);
let discr_size = discr.size().bytes();
let padded_discr_size = discr.size().abi_align(align);
let variant_part_size = size - padded_discr_size;
// Ensure discr_ty can fill pad evenly
assert_eq!(padded_discr_size.bytes() % discr_size, 0);
let fields = [
discr_ty,
Type::array(&discr_ty, padded_discr_size.bytes() / discr_size - 1),
union_fill(cx, variant_part_size, primitive_align)
];
match name {
None => {
Type::struct_(cx, &fields, false)
}
Some(name) => {
let mut llty = Type::named_struct(cx, name);
llty.set_struct_body(&fields, false);
llty
}
}
}
_ => bug!("Unsupported type {} represented as {:#?}", t, l)
}
}
fn union_fill(cx: &CrateContext, size: Size, align: Align) -> Type {
let abi_align = align.abi();
let elem_ty = if let Some(ity) = layout::Integer::for_abi_align(cx, align) {
Type::from_integer(cx, ity)
} else {
let vec_align = cx.data_layout().vector_align(Size::from_bytes(abi_align));
assert_eq!(vec_align.abi(), abi_align);
Type::vector(&Type::i32(cx), abi_align / 4)
};
let size = size.bytes();
assert_eq!(size % abi_align, 0);
Type::array(&elem_ty, size / abi_align)
}
/// Double an index to account for padding.
pub fn memory_index_to_gep(index: usize) -> usize {
index * 2
}
/// Lookup `Struct::memory_index`, double it to account for padding.
pub fn struct_llfields_index(variant: &layout::Struct, index: usize) -> usize {
memory_index_to_gep(variant.memory_index[index] as usize)
}
pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>,
variant_index: usize,
variant: &layout::Struct,
discr: Option<Ty<'tcx>>) -> Vec<Type> {
let field_count = match t.sty {
ty::TyAdt(ref def, _) if def.variants.len() == 0 => return vec![],
ty::TyAdt(ref def, _) => {
discr.is_some() as usize + def.variants[variant_index].fields.len()
},
ty::TyTuple(fields, _) => fields.len(),
ty::TyClosure(def_id, substs) => {
if variant_index > 0 { bug!("{} is a closure, which only has one variant", t);}
substs.upvar_tys(def_id, cx.tcx()).count()
},
ty::TyGenerator(def_id, substs, _) => {
if variant_index > 0 { bug!("{} is a generator, which only has one variant", t);}
substs.field_tys(def_id, cx.tcx()).count()
},
_ => bug!("{} is not a type that can have fields.", t)
};
debug!("struct_llfields: variant: {:?}", variant);
let mut first_field = true;
let mut offset = Size::from_bytes(0);
let mut result: Vec<Type> = Vec::with_capacity(field_count * 2);
let field_iter = variant.field_index_by_increasing_offset().map(|i| {
(i, match t.sty {
ty::TyAdt(..) if i == 0 && discr.is_some() => discr.unwrap(),
ty::TyAdt(ref def, ref substs) => {
monomorphize::field_ty(cx.tcx(), substs,
&def.variants[variant_index].fields[i as usize - discr.is_some() as usize])
},
ty::TyTuple(fields, _) => fields[i as usize],
ty::TyClosure(def_id, substs) => {
substs.upvar_tys(def_id, cx.tcx()).nth(i).unwrap()
},
ty::TyGenerator(def_id, substs, _) => {
let ty = substs.field_tys(def_id, cx.tcx()).nth(i).unwrap();
cx.tcx().normalize_associated_type(&ty)
},
_ => bug!()
}, variant.offsets[i as usize])
});
for (index, ty, target_offset) in field_iter {
debug!("struct_llfields: {} ty: {} offset: {:?} target_offset: {:?}",
index, ty, offset, target_offset);
assert!(target_offset >= offset);
let padding = target_offset - offset;
if first_field {
assert_eq!(padding.bytes(), 0);
first_field = false;
} else {
result.push(Type::array(&Type::i8(cx), padding.bytes()));
debug!(" padding before: {:?}", padding);
}
let llty = type_of::in_memory_type_of(cx, ty);
result.push(llty);
let layout = cx.layout_of(ty);
if variant.packed {
assert_eq!(padding.bytes(), 0);
} else {
let field_align = layout.align(cx);
assert!(field_align.abi() <= variant.align.abi(),
"non-packed type has field with larger align ({}): {:#?}",
field_align.abi(), variant);
}
let target_size = layout.size(&cx.tcx().data_layout);
offset = target_offset + target_size;
}
if variant.sized && field_count > 0 {
if offset > variant.stride() {
bug!("variant: {:?} stride: {:?} offset: {:?}",
variant, variant.stride(), offset);
}
let padding = variant.stride() - offset;
debug!("struct_llfields: pad_bytes: {:?} offset: {:?} min_size: {:?} stride: {:?}",
padding, offset, variant.min_size, variant.stride());
result.push(Type::array(&Type::i8(cx), padding.bytes()));
assert!(result.len() == (field_count * 2));
} else {
debug!("struct_llfields: offset: {:?} min_size: {:?} stride: {:?}",
offset, variant.min_size, variant.stride());
}
result
}
pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool {
match *l {
layout::CEnum { signed, .. }=> signed,
_ => false,
}
}
pub fn assert_discr_in_range<D: PartialOrd>(min: D, max: D, discr: D) {
if min <= max {
assert!(min <= discr && discr <= max)
} else {
assert!(min <= discr || discr <= max)
}
}