Fix type_of for enums to not lose data in some cases when immediate.

The previous implementation, when combined with small discriminants and
immediate types, caused problems for types like `Either<u8, i16>` which
are now small enough to be immediate and can have fields intersecting
the highest-alignment variant's alignment padding (which LLVM doesn't
preserve).  So let's not do that.
This commit is contained in:
Jed Davis 2013-10-26 12:12:53 -07:00
parent ac4644d7de
commit 49f851c2c9

View file

@ -382,30 +382,38 @@ fn generic_fields_of(cx: &mut CrateContext, r: &Repr, sizing: bool) -> ~[Type] {
CEnum(ity, _, _) => ~[ll_inttype(cx, ity)], CEnum(ity, _, _) => ~[ll_inttype(cx, ity)],
Univariant(ref st, _dtor) => struct_llfields(cx, st, sizing), Univariant(ref st, _dtor) => struct_llfields(cx, st, sizing),
NullablePointer{ nonnull: ref st, _ } => struct_llfields(cx, st, sizing), NullablePointer{ nonnull: ref st, _ } => struct_llfields(cx, st, sizing),
General(_ity, ref sts) => { General(ity, ref sts) => {
// To get "the" type of a general enum, we pick the case // We need a representation that has:
// with the largest alignment (so it will always align // * The alignment of the most-aligned field
// correctly in containing structures) and pad it out. // * The size of the largest variant (rounded up to that alignment)
assert!(sts.len() >= 1); // * No alignment padding anywhere any variant has actual data
let mut most_aligned = None; // (currently matters only for enums small enough to be immediate)
let mut largest_align = 0; // * The discriminant in an obvious place.
let mut largest_size = 0; //
for st in sts.iter() { // So we start with the discriminant, pad it up to the alignment with
if largest_size < st.size { // more of its own type, then use alignment-sized ints to get the rest
largest_size = st.size; // of the size.
} //
if largest_align < st.align { // Note: if/when we start exposing SIMD vector types (or f80, on some
// Clang breaks ties by size; it is unclear if // platforms that have it), this will need some adjustment.
// that accomplishes anything important. let size = sts.iter().map(|st| st.size).max().unwrap();
largest_align = st.align; let most_aligned = sts.iter().max_by(|st| st.align).unwrap();
most_aligned = Some(st); let align = most_aligned.align;
} let discr_ty = ll_inttype(cx, ity);
} let discr_size = machine::llsize_of_alloc(cx, discr_ty) as u64;
let most_aligned = most_aligned.unwrap(); let pad_ty = match align {
let padding = largest_size - most_aligned.size; 1 => Type::i8(),
2 => Type::i16(),
struct_llfields(cx, most_aligned, sizing) 4 => Type::i32(),
+ &[Type::array(&Type::i8(), padding)] 8 if machine::llalign_of_min(cx, Type::i64()) == 8 => Type::i64(),
_ => fail!("Unsupported enum alignment: {:?}", align)
};
assert_eq!(machine::llalign_of_min(cx, pad_ty) as u64, align);
let align_units = (size + align - 1) / align;
assert_eq!(align % discr_size, 0);
~[discr_ty,
Type::array(&discr_ty, align / discr_size - 1),
Type::array(&pad_ty, align_units - 1)]
} }
} }
} }