Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DST size_of_val and align_of_val computations #27351

Merged
merged 5 commits into from
Aug 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 94 additions & 17 deletions src/librustc_trans/trans/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,31 @@ use trans::type_of;

type Hint = attr::ReprAttr;

// Representation of the context surrounding an unsized type. I want
// to be able to track the drop flags that are injected by trans.
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct TypeContext {
prefix: Type,
needs_drop_flag: bool,
}

impl TypeContext {
pub fn prefix(&self) -> Type { self.prefix }
pub fn needs_drop_flag(&self) -> bool { self.needs_drop_flag }

fn direct(t: Type) -> TypeContext {
TypeContext { prefix: t, needs_drop_flag: false }
}
fn may_need_drop_flag(t: Type, needs_drop_flag: bool) -> TypeContext {
TypeContext { prefix: t, needs_drop_flag: needs_drop_flag }
}
pub fn to_string(self) -> String {
let TypeContext { prefix, needs_drop_flag } = self;
format!("TypeContext {{ prefix: {}, needs_drop_flag: {} }}",
prefix.to_string(), needs_drop_flag)
}
}

/// Representations.
#[derive(Eq, PartialEq, Debug)]
pub enum Repr<'tcx> {
Expand Down Expand Up @@ -125,7 +150,7 @@ pub struct Struct<'tcx> {
pub align: u32,
pub sized: bool,
pub packed: bool,
pub fields: Vec<Ty<'tcx>>
pub fields: Vec<Ty<'tcx>>,
}

/// Convenience for `represent_type`. There should probably be more or
Expand Down Expand Up @@ -681,18 +706,30 @@ fn ensure_enum_fits_in_address_space<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
/// 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>, r: &Repr<'tcx>) -> Type {
generic_type_of(cx, r, None, false, false)
let c = generic_type_of(cx, r, None, false, false, false);
assert!(!c.needs_drop_flag);
c.prefix
}


// Pass dst=true if the type you are passing is a DST. Yes, we could figure
// this out, but if you call this on an unsized type without realising it, you
// are going to get the wrong type (it will not include the unsized parts of it).
pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
r: &Repr<'tcx>, dst: bool) -> Type {
generic_type_of(cx, r, None, true, dst)
let c = generic_type_of(cx, r, None, true, dst, false);
assert!(!c.needs_drop_flag);
c.prefix
}
pub fn sizing_type_context_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
r: &Repr<'tcx>, dst: bool) -> TypeContext {
generic_type_of(cx, r, None, true, dst, true)
}
pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
r: &Repr<'tcx>, name: &str) -> Type {
generic_type_of(cx, r, Some(name), false, false)
let c = generic_type_of(cx, r, Some(name), false, false, false);
assert!(!c.needs_drop_flag);
c.prefix
}
pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
r: &Repr<'tcx>, llty: &mut Type) {
Expand All @@ -708,20 +745,50 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
r: &Repr<'tcx>,
name: Option<&str>,
sizing: bool,
dst: bool) -> Type {
dst: bool,
delay_drop_flag: bool) -> TypeContext {
debug!("adt::generic_type_of r: {:?} name: {:?} sizing: {} dst: {} delay_drop_flag: {}",
r, name, sizing, dst, delay_drop_flag);
match *r {
CEnum(ity, _, _) => ll_inttype(cx, ity),
RawNullablePointer { nnty, .. } => type_of::sizing_type_of(cx, nnty),
Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => {
CEnum(ity, _, _) => TypeContext::direct(ll_inttype(cx, ity)),
RawNullablePointer { nnty, .. } =>
TypeContext::direct(type_of::sizing_type_of(cx, nnty)),
StructWrappedNullablePointer { nonnull: ref st, .. } => {
match name {
None => {
TypeContext::direct(
Type::struct_(cx, &struct_llfields(cx, st, sizing, dst),
st.packed))
}
Some(name) => {
assert_eq!(sizing, false);
TypeContext::direct(Type::named_struct(cx, name))
}
}
}
Univariant(ref st, dtor_needed) => {
let dtor_needed = dtor_needed != 0;
match name {
None => {
Type::struct_(cx, &struct_llfields(cx, st, sizing, dst),
st.packed)
let mut fields = struct_llfields(cx, st, sizing, dst);
if delay_drop_flag && dtor_needed {
fields.pop();
}
TypeContext::may_need_drop_flag(
Type::struct_(cx, &fields,
st.packed),
delay_drop_flag && dtor_needed)
}
Some(name) => {
// Hypothesis: named_struct's can never need a
// drop flag. (... needs validation.)
assert_eq!(sizing, false);
TypeContext::direct(Type::named_struct(cx, name))
}
Some(name) => { assert_eq!(sizing, false); Type::named_struct(cx, name) }
}
}
General(ity, ref sts, _) => {
General(ity, ref sts, dtor_needed) => {
let dtor_needed = dtor_needed != 0;
// We need a representation that has:
// * The alignment of the most-aligned field
// * The size of the largest variant (rounded up to that alignment)
Expand Down Expand Up @@ -753,15 +820,25 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
};
assert_eq!(machine::llalign_of_min(cx, fill_ty), align);
assert_eq!(align_s % discr_size, 0);
let fields = [discr_ty,
Type::array(&discr_ty, align_s / discr_size - 1),
fill_ty];
let mut fields: Vec<Type> =
[discr_ty,
Type::array(&discr_ty, align_s / discr_size - 1),
fill_ty].iter().cloned().collect();
if delay_drop_flag && dtor_needed {
fields.pop();
}
match name {
None => Type::struct_(cx, &fields[..], false),
None => {
TypeContext::may_need_drop_flag(
Type::struct_(cx, &fields[..], false),
delay_drop_flag && dtor_needed)
}
Some(name) => {
let mut llty = Type::named_struct(cx, name);
llty.set_struct_body(&fields[..], false);
llty
TypeContext::may_need_drop_flag(
llty,
delay_drop_flag && dtor_needed)
}
}
}
Expand Down
61 changes: 53 additions & 8 deletions src/librustc_trans/trans/glue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,12 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
t, bcx.val_to_string(info));
if type_is_sized(bcx.tcx(), t) {
let sizing_type = sizing_type_of(bcx.ccx(), t);
let size = C_uint(bcx.ccx(), llsize_of_alloc(bcx.ccx(), sizing_type));
let align = C_uint(bcx.ccx(), align_of(bcx.ccx(), t));
let size = llsize_of_alloc(bcx.ccx(), sizing_type);
let align = align_of(bcx.ccx(), t);
debug!("size_and_align_of_dst t={} info={} size: {} align: {}",
t, bcx.val_to_string(info), size, align);
let size = C_uint(bcx.ccx(), size);
let align = C_uint(bcx.ccx(), align);
return (size, align);
}
match t.sty {
Expand All @@ -417,9 +421,14 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
// Don't use type_of::sizing_type_of because that expects t to be sized.
assert!(!t.is_simd(bcx.tcx()));
let repr = adt::represent_type(ccx, t);
let sizing_type = adt::sizing_type_of(ccx, &*repr, true);
let sized_size = C_uint(ccx, llsize_of_alloc(ccx, sizing_type));
let sized_align = C_uint(ccx, llalign_of_min(ccx, sizing_type));
let sizing_type = adt::sizing_type_context_of(ccx, &*repr, true);
debug!("DST {} sizing_type: {}", t, sizing_type.to_string());
let sized_size = llsize_of_alloc(ccx, sizing_type.prefix());
let sized_align = llalign_of_min(ccx, sizing_type.prefix());
debug!("DST {} statically sized prefix size: {} align: {}",
t, sized_size, sized_align);
let sized_size = C_uint(ccx, sized_size);
let sized_align = C_uint(ccx, sized_align);

// Recurse to get the size of the dynamically sized field (must be
// the last field).
Expand All @@ -428,16 +437,52 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
let field_ty = last_field.mt.ty;
let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info);

let dbloc = DebugLoc::None;

// FIXME (#26403, #27023): We should be adding padding
// to `sized_size` (to accommodate the `unsized_align`
// required of the unsized field that follows) before
// summing it with `sized_size`. (Note that since #26403
// is unfixed, we do not yet add the necessary padding
// here. But this is where the add would go.)

// Return the sum of sizes and max of aligns.
let size = Add(bcx, sized_size, unsized_size, DebugLoc::None);
let mut size = Add(bcx, sized_size, unsized_size, dbloc);

// Issue #27023: If there is a drop flag, *now* we add 1
// to the size. (We can do this without adding any
// padding because drop flags do not have any alignment
// constraints.)
if sizing_type.needs_drop_flag() {
size = Add(bcx, size, C_uint(bcx.ccx(), 1_u64), dbloc);
}

// Choose max of two known alignments (combined value must
// be aligned according to more restrictive of the two).
let align = Select(bcx,
ICmp(bcx,
llvm::IntULT,
llvm::IntUGT,
sized_align,
unsized_align,
DebugLoc::None),
dbloc),
sized_align,
unsized_align);

// Issue #27023: must add any necessary padding to `size`
// (to make it a multiple of `align`) before returning it.
//
// Namely, the returned size should be, in C notation:
//
// `size + ((size & (align-1)) ? align : 0)`
//
// emulated via the semi-standard fast bit trick:
//
// `(size + (align-1)) & !align`

let addend = Sub(bcx, align, C_uint(bcx.ccx(), 1_u64), dbloc);
let size = And(
bcx, Add(bcx, size, addend, dbloc), Neg(bcx, align, dbloc), dbloc);

(size, align)
}
ty::TyTrait(..) => {
Expand Down
10 changes: 7 additions & 3 deletions src/librustc_trans/trans/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ impl Type {
self.rf
}

pub fn to_string(self: Type) -> String {
llvm::build_string(|s| unsafe {
llvm::LLVMWriteTypeToString(self.to_ref(), s);
}).expect("non-UTF8 type description from LLVM")
}

pub fn void(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMVoidTypeInContext(ccx.llcx()))
}
Expand Down Expand Up @@ -315,9 +321,7 @@ impl TypeNames {
}

pub fn type_to_string(&self, ty: Type) -> String {
llvm::build_string(|s| unsafe {
llvm::LLVMWriteTypeToString(ty.to_ref(), s);
}).expect("non-UTF8 type description from LLVM")
ty.to_string()
}

pub fn types_to_str(&self, tys: &[Type]) -> String {
Expand Down