Implement layout calculation and add more trans stubs

This commit is contained in:
Vadim Petrochenkov 2016-08-18 15:44:00 +03:00
parent c2ca1530db
commit 957971b63a
5 changed files with 205 additions and 20 deletions

View File

@ -488,7 +488,7 @@ impl<'a, 'gcx, 'tcx> Struct {
for field in fields {
if !self.sized {
bug!("Struct::compute: field #{} of `{}` comes after unsized field",
bug!("Struct::extend: field #{} of `{}` comes after unsized field",
self.offset_after_field.len(), scapegoat);
}
@ -623,6 +623,54 @@ impl<'a, 'gcx, 'tcx> Struct {
}
}
/// An untagged union.
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct Union {
pub align: Align,
pub min_size: Size,
/// If true, no alignment padding is used.
pub packed: bool,
}
impl<'a, 'gcx, 'tcx> Union {
pub fn new(dl: &TargetDataLayout, packed: bool) -> Union {
Union {
align: if packed { dl.i8_align } else { dl.aggregate_align },
min_size: Size::from_bytes(0),
packed: packed,
}
}
/// Extend the Struct with more fields.
pub fn extend<I>(&mut self, dl: &TargetDataLayout,
fields: I,
scapegoat: Ty<'gcx>)
-> Result<(), LayoutError<'gcx>>
where I: Iterator<Item=Result<&'a Layout, LayoutError<'gcx>>> {
for (index, field) in fields.enumerate() {
let field = field?;
if field.is_unsized() {
bug!("Union::extend: field #{} of `{}` is unsized",
index, scapegoat);
}
if !self.packed {
self.align = self.align.max(field.align(dl));
}
self.min_size = cmp::max(self.min_size, field.size(dl));
}
Ok(())
}
/// Get the size with trailing aligment padding.
pub fn stride(&self) -> Size {
self.min_size.abi_align(self.align)
}
}
/// The first half of a fat pointer.
/// - For a trait object, this is the address of the box.
/// - For a slice, this is the base address.
@ -690,6 +738,11 @@ pub enum Layout {
non_zero: bool
},
/// Untagged unions.
UntaggedUnion {
variants: Union,
},
/// General-case enums: for each case there is a struct, and they
/// all start with a field for the discriminant.
General {
@ -896,8 +949,14 @@ impl<'a, 'gcx, 'tcx> Layout {
non_zero: Some(def.did) == tcx.lang_items.non_zero()
}
}
ty::TyUnion(..) => {
unimplemented_unions!();
ty::TyUnion(def, substs) => {
let fields = def.struct_variant().fields.iter().map(|field| {
field.ty(tcx, substs).layout(infcx)
});
let packed = tcx.lookup_packed(def.did);
let mut un = Union::new(dl, packed);
un.extend(dl, fields, ty)?;
UntaggedUnion { variants: un }
}
ty::TyEnum(def, substs) => {
let hint = *tcx.lookup_repr_hints(def.did).get(0)
@ -1118,7 +1177,7 @@ impl<'a, 'gcx, 'tcx> Layout {
pub fn is_unsized(&self) -> bool {
match *self {
Scalar {..} | Vector {..} | FatPointer {..} |
CEnum {..} | General {..} |
CEnum {..} | UntaggedUnion {..} | General {..} |
RawNullablePointer {..} |
StructWrappedNullablePointer {..} => false,
@ -1152,6 +1211,7 @@ impl<'a, 'gcx, 'tcx> Layout {
CEnum { discr, .. } => Int(discr).size(dl),
Array { size, .. } | General { size, .. } => size,
UntaggedUnion { ref variants } => variants.stride(),
Univariant { ref variant, .. } |
StructWrappedNullablePointer { nonnull: ref variant, .. } => {
@ -1191,6 +1251,7 @@ impl<'a, 'gcx, 'tcx> Layout {
CEnum { discr, .. } => Int(discr).align(dl),
Array { align, .. } | General { align, .. } => align,
UntaggedUnion { ref variants } => variants.align,
Univariant { ref variant, .. } |
StructWrappedNullablePointer { nonnull: ref variant, .. } => {
@ -1256,9 +1317,6 @@ impl<'a, 'gcx, 'tcx> SizeSkeleton<'gcx> {
}
}
ty::TyUnion(..) => {
unimplemented_unions!();
}
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
// Only newtypes and enums w/ nullable pointer optimization.
if def.variants.is_empty() || def.variants.len() > 2 {

View File

@ -459,7 +459,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
hir::ExprStruct(_, ref fields, ref base) => {
match expr_ty.sty {
ty::TyStruct(adt, substs) => {
ty::TyStruct(adt, substs) | ty::TyUnion(adt, substs) => {
let field_refs = field_refs(&adt.variants[0], fields);
ExprKind::Adt {
adt_def: adt,
@ -477,9 +477,6 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
})
}
}
ty::TyUnion(..) => {
unimplemented_unions!();
}
ty::TyEnum(adt, substs) => {
match cx.tcx.expect_def(expr.id) {
Def::Variant(enum_id, variant_id) => {

View File

@ -79,6 +79,8 @@ pub enum Repr<'tcx> {
CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType)
/// Single-case variants, and structs/tuples/records.
Univariant(Struct<'tcx>),
/// Untagged unions.
UntaggedUnion(Union<'tcx>),
/// General-case enums: for each case there is a struct, and they
/// all start with a field for the discriminant.
General(IntType, Vec<Struct<'tcx>>),
@ -121,6 +123,15 @@ pub struct Struct<'tcx> {
pub fields: Vec<Ty<'tcx>>,
}
/// For untagged unions.
#[derive(Eq, PartialEq, Debug)]
pub struct Union<'tcx> {
pub min_size: u64,
pub align: u32,
pub packed: bool,
pub fields: Vec<Ty<'tcx>>,
}
#[derive(Copy, Clone)]
pub struct MaybeSizedValue {
pub value: ValueRef,
@ -176,8 +187,12 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
Univariant(mk_struct(cx, &ftys[..], packed, t))
}
ty::TyUnion(..) => {
unimplemented_unions!();
ty::TyUnion(def, substs) => {
let ftys = def.struct_variant().fields.iter().map(|field| {
monomorphize::field_ty(cx.tcx(), substs, field)
}).collect::<Vec<_>>();
let packed = cx.tcx().lookup_packed(def.did);
UntaggedUnion(mk_union(cx, &ftys[..], packed, t))
}
ty::TyClosure(_, ref substs) => {
Univariant(mk_struct(cx, &substs.upvar_tys, false, t))
@ -482,6 +497,31 @@ fn mk_struct<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
}
fn mk_union<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
tys: &[Ty<'tcx>], packed: bool,
_scapegoat: Ty<'tcx>)
-> Union<'tcx> {
let mut min_size = 0;
let mut align = 0;
for llty in tys.iter().map(|&ty| type_of::sizing_type_of(cx, ty)) {
let field_size = machine::llsize_of_alloc(cx, llty);
if min_size < field_size {
min_size = field_size;
}
let field_align = machine::llalign_of_min(cx, llty);
if align < field_align {
align = field_align;
}
}
Union {
min_size: min_size,
align: align,
packed: packed,
fields: tys.to_vec(),
}
}
#[derive(Debug)]
struct IntBounds {
slo: i64,
@ -646,7 +686,7 @@ pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
r: &Repr<'tcx>, llty: &mut Type) {
match *r {
CEnum(..) | General(..) | RawNullablePointer { .. } => { }
CEnum(..) | General(..) | UntaggedUnion(..) | RawNullablePointer { .. } => { }
Univariant(ref st) | StructWrappedNullablePointer { nonnull: ref st, .. } =>
llty.set_struct_body(&struct_llfields(cx, st, false, false),
st.packed)
@ -690,6 +730,34 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
}
}
UntaggedUnion(ref un) => {
// Use alignment-sized ints to fill all the union storage.
let (size, align) = (roundup(un.min_size, un.align), un.align);
let align_s = align as u64;
assert_eq!(size % align_s, 0); // Ensure division in align_units comes out evenly
let align_units = size / align_s;
let fill_ty = match align_s {
1 => Type::array(&Type::i8(cx), align_units),
2 => Type::array(&Type::i16(cx), align_units),
4 => Type::array(&Type::i32(cx), align_units),
8 if machine::llalign_of_min(cx, Type::i64(cx)) == 8 =>
Type::array(&Type::i64(cx), align_units),
a if a.count_ones() == 1 => Type::array(&Type::vector(&Type::i32(cx), a / 4),
align_units),
_ => bug!("unsupported union alignment: {}", align)
};
match name {
None => {
TypeContext::direct(Type::struct_(cx, &[fill_ty], un.packed))
}
Some(name) => {
let mut llty = Type::named_struct(cx, name);
llty.set_struct_body(&[fill_ty], un.packed);
TypeContext::direct(llty)
}
}
}
General(ity, ref sts) => {
// We need a representation that has:
// * The alignment of the most-aligned field
@ -762,7 +830,7 @@ pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
(BranchKind::Switch, Some(trans_get_discr(bcx, r, scrutinee, None, range_assert)))
}
Univariant(..) => {
Univariant(..) | UntaggedUnion(..) => {
// N.B.: Univariant means <= 1 enum variants (*not* == 1 variants).
(BranchKind::Single, None)
}
@ -773,7 +841,7 @@ pub fn is_discr_signed<'tcx>(r: &Repr<'tcx>) -> bool {
match *r {
CEnum(ity, _, _) => ity.is_signed(),
General(ity, _) => ity.is_signed(),
Univariant(..) => false,
Univariant(..) | UntaggedUnion(..) => false,
RawNullablePointer { .. } => false,
StructWrappedNullablePointer { .. } => false,
}
@ -794,7 +862,7 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
load_discr(bcx, ity, ptr, Disr(0), Disr(cases.len() as u64 - 1),
range_assert)
}
Univariant(..) => C_u8(bcx.ccx(), 0),
Univariant(..) | UntaggedUnion(..) => C_u8(bcx.ccx(), 0),
RawNullablePointer { nndiscr, nnty, .. } => {
let cmp = if nndiscr == Disr(0) { IntEQ } else { IntNE };
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
@ -856,8 +924,8 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
General(ity, _) => {
C_integral(ll_inttype(bcx.ccx(), ity), discr.0, true)
}
Univariant(..) => {
bug!("no cases for univariants or structs")
Univariant(..) | UntaggedUnion(..) => {
bug!("no cases for univariants, structs or unions")
}
RawNullablePointer { .. } |
StructWrappedNullablePointer { .. } => {
@ -884,6 +952,9 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
Univariant(_) => {
assert_eq!(discr, Disr(0));
}
UntaggedUnion(..) => {
assert_eq!(discr, Disr(0));
}
RawNullablePointer { nndiscr, nnty, ..} => {
if discr != nndiscr {
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
@ -939,6 +1010,11 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
General(_, ref cases) => {
struct_field_ptr(bcx, &cases[discr.0 as usize], val, ix + 1, true)
}
UntaggedUnion(ref un) => {
let ty = type_of::in_memory_type_of(bcx.ccx(), un.fields[ix]);
if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
bcx.pointercast(val.value, ty.ptr_to())
}
RawNullablePointer { nndiscr, ref nullfields, .. } |
StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => {
// The unit-like case might have a nonzero number of unit-like fields.
@ -1100,6 +1176,9 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
contents.extend_from_slice(&[padding(ccx, max_sz - case.size)]);
C_struct(ccx, &contents[..], false)
}
UntaggedUnion(..) => {
unimplemented_unions!();
}
Univariant(ref st) => {
assert_eq!(discr, Disr(0));
let contents = build_const_struct(ccx, st, vals);
@ -1211,6 +1290,7 @@ pub fn const_get_field(r: &Repr, val: ValueRef, _discr: Disr,
match *r {
CEnum(..) => bug!("element access in C-like enum const"),
Univariant(..) => const_struct_field(val, ix),
UntaggedUnion(..) => const_struct_field(val, 0),
General(..) => const_struct_field(val, ix + 1),
RawNullablePointer { .. } => {
assert_eq!(ix, 0);

View File

@ -1302,6 +1302,9 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
]
}
}
adt::UntaggedUnion(..) => {
unimplemented_unions!();
}
adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => {
// As far as debuginfo is concerned, the pointer this enum
// represents is still wrapped in a struct. This is to make the
@ -1616,7 +1619,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
},
adt::RawNullablePointer { .. } |
adt::StructWrappedNullablePointer { .. } |
adt::Univariant(..) => None,
adt::Univariant(..) | adt::UntaggedUnion(..) => None,
adt::General(inttype, _) => Some(discriminant_type_metadata(inttype)),
};

View File

@ -0,0 +1,47 @@
// Copyright 2016 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.
#![feature(untagged_unions)]
use std::mem::{size_of, align_of, zeroed};
union U {
a: u8,
}
union U64 {
a: u64,
}
union W {
a: u8,
b: u64,
}
fn main() {
assert_eq!(size_of::<U>(), 1);
assert_eq!(size_of::<U64>(), 8);
assert_eq!(size_of::<W>(), 8);
assert_eq!(align_of::<U>(), 1);
assert_eq!(align_of::<U64>(), align_of::<u64>());
assert_eq!(align_of::<W>(), align_of::<u64>());
let u = U { a: 10 };
assert_eq!(u.a, 10);
let U { a } = u;
assert_eq!(a, 10);
let mut w: W = unsafe { zeroed() };
assert_eq!(w.a, 0);
assert_eq!(w.b, 0);
// w.a = 1;
// assert_eq!(w.a, 0);
// assert_eq!(w.b, 0);
}