mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 23:12:02 +00:00
Represent enums with regular structs; no more alignment-breaking casts.
This commit is contained in:
parent
0ad3a110be
commit
a301db7eef
@ -23,10 +23,6 @@
|
|||||||
* Having everything in one place will enable improvements to data
|
* Having everything in one place will enable improvements to data
|
||||||
* structure representation; possibilities include:
|
* structure representation; possibilities include:
|
||||||
*
|
*
|
||||||
* - Aligning enum bodies correctly, which in turn makes possible SIMD
|
|
||||||
* vector types (which are strict-alignment even on x86) and ports
|
|
||||||
* to strict-alignment architectures (PowerPC, SPARC, etc.).
|
|
||||||
*
|
|
||||||
* - User-specified alignment (e.g., cacheline-aligning parts of
|
* - User-specified alignment (e.g., cacheline-aligning parts of
|
||||||
* concurrently accessed data structures); LLVM can't represent this
|
* concurrently accessed data structures); LLVM can't represent this
|
||||||
* directly, so we'd have to insert padding fields in any structure
|
* directly, so we'd have to insert padding fields in any structure
|
||||||
@ -82,10 +78,8 @@ pub enum Repr {
|
|||||||
*/
|
*/
|
||||||
Univariant(Struct, bool),
|
Univariant(Struct, bool),
|
||||||
/**
|
/**
|
||||||
* General-case enums: discriminant as int, followed by fields.
|
* General-case enums: for each case there is a struct, and they
|
||||||
* The fields start immediately after the discriminant, meaning
|
* all start with a field for the discriminant.
|
||||||
* that they may not be correctly aligned for the platform's ABI;
|
|
||||||
* see above.
|
|
||||||
*/
|
*/
|
||||||
General(~[Struct])
|
General(~[Struct])
|
||||||
}
|
}
|
||||||
@ -156,7 +150,8 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
|
|||||||
discriminants",
|
discriminants",
|
||||||
ty::item_path_str(cx.tcx, def_id)))
|
ty::item_path_str(cx.tcx, def_id)))
|
||||||
}
|
}
|
||||||
General(cases.map(|c| mk_struct(cx, c.tys)))
|
let discr = ~[ty::mk_int(cx.tcx)];
|
||||||
|
General(cases.map(|c| mk_struct(cx, discr + c.tys)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => cx.sess.bug(~"adt::represent_type called on non-ADT type")
|
_ => cx.sess.bug(~"adt::represent_type called on non-ADT type")
|
||||||
@ -191,18 +186,42 @@ fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
|
|||||||
-> ~[TypeRef] {
|
-> ~[TypeRef] {
|
||||||
match *r {
|
match *r {
|
||||||
CEnum(*) => ~[T_enum_discrim(cx)],
|
CEnum(*) => ~[T_enum_discrim(cx)],
|
||||||
Univariant(ref st, _dtor) => {
|
Univariant(ref st, _dtor) => struct_llfields(cx, st, sizing),
|
||||||
|
General(ref sts) => {
|
||||||
|
// To get "the" type of a general enum, we pick the case
|
||||||
|
// with the largest alignment (so it will always align
|
||||||
|
// correctly in containing structures) and pad it out.
|
||||||
|
fail_unless!(sts.len() >= 1);
|
||||||
|
let mut most_aligned = None;
|
||||||
|
let mut largest_align = 0;
|
||||||
|
let mut largest_size = 0;
|
||||||
|
for sts.each |st| {
|
||||||
|
if largest_size < st.size {
|
||||||
|
largest_size = st.size;
|
||||||
|
}
|
||||||
|
if largest_align < st.align {
|
||||||
|
// Clang breaks ties by size; it is unclear if
|
||||||
|
// that accomplishes anything important.
|
||||||
|
largest_align = st.align;
|
||||||
|
most_aligned = Some(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let most_aligned = most_aligned.get();
|
||||||
|
let padding = largest_size - most_aligned.size;
|
||||||
|
|
||||||
|
struct_llfields(cx, most_aligned, sizing)
|
||||||
|
+ [T_array(T_i8(), padding /*bad*/as uint)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn struct_llfields(cx: @CrateContext, st: &Struct, sizing: bool)
|
||||||
|
-> ~[TypeRef] {
|
||||||
if sizing {
|
if sizing {
|
||||||
st.fields.map(|&ty| type_of::sizing_type_of(cx, ty))
|
st.fields.map(|&ty| type_of::sizing_type_of(cx, ty))
|
||||||
} else {
|
} else {
|
||||||
st.fields.map(|&ty| type_of::type_of(cx, ty))
|
st.fields.map(|&ty| type_of::type_of(cx, ty))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
General(ref sts) => {
|
|
||||||
~[T_enum_discrim(cx),
|
|
||||||
T_array(T_i8(), sts.map(|st| st.size).max() /*bad*/as uint)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,7 +328,7 @@ pub fn num_args(r: &Repr, discr: int) -> uint {
|
|||||||
fail_unless!(discr == 0);
|
fail_unless!(discr == 0);
|
||||||
st.fields.len() - (if dtor { 1 } else { 0 })
|
st.fields.len() - (if dtor { 1 } else { 0 })
|
||||||
}
|
}
|
||||||
General(ref cases) => cases[discr as uint].fields.len()
|
General(ref cases) => cases[discr as uint].fields.len() - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,8 +347,7 @@ pub fn trans_field_ptr(bcx: block, r: &Repr, val: ValueRef, discr: int,
|
|||||||
struct_field_ptr(bcx, st, val, ix, false)
|
struct_field_ptr(bcx, st, val, ix, false)
|
||||||
}
|
}
|
||||||
General(ref cases) => {
|
General(ref cases) => {
|
||||||
struct_field_ptr(bcx, &cases[discr as uint],
|
struct_field_ptr(bcx, &cases[discr as uint], val, ix + 1, true)
|
||||||
GEPi(bcx, val, [0, 1]), ix, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -371,13 +389,12 @@ pub fn trans_drop_flag_ptr(bcx: block, r: &Repr, val: ValueRef) -> ValueRef {
|
|||||||
* depending on which case of an enum it is.
|
* depending on which case of an enum it is.
|
||||||
*
|
*
|
||||||
* To understand the alignment situation, consider `enum E { V64(u64),
|
* To understand the alignment situation, consider `enum E { V64(u64),
|
||||||
* V32(u32, u32) }` on win32. The type should have 8-byte alignment
|
* V32(u32, u32) }` on win32. The type has 8-byte alignment to
|
||||||
* to accommodate the u64 (currently it doesn't; this is a known bug),
|
* accommodate the u64, but `V32(x, y)` would have LLVM type `{i32,
|
||||||
* but `V32(x, y)` would have LLVM type `{i32, i32, i32}`, which is
|
* i32, i32}`, which is 4-byte aligned.
|
||||||
* 4-byte aligned.
|
|
||||||
*
|
*
|
||||||
* Currently the returned value has the same size as the type, but
|
* Currently the returned value has the same size as the type, but
|
||||||
* this may be changed in the future to avoid allocating unnecessary
|
* this could be changed in the future to avoid allocating unnecessary
|
||||||
* space after values of shorter-than-maximum cases.
|
* space after values of shorter-than-maximum cases.
|
||||||
*/
|
*/
|
||||||
pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
|
pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
|
||||||
@ -395,14 +412,9 @@ pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
|
|||||||
General(ref cases) => {
|
General(ref cases) => {
|
||||||
let case = &cases[discr as uint];
|
let case = &cases[discr as uint];
|
||||||
let max_sz = cases.map(|s| s.size).max();
|
let max_sz = cases.map(|s| s.size).max();
|
||||||
let body = build_const_struct(ccx, case, vals);
|
let contents = build_const_struct(ccx, case,
|
||||||
|
~[C_int(ccx, discr)] + vals);
|
||||||
// The unary packed struct has alignment 1 regardless of
|
C_struct(contents + [padding(max_sz - case.size)])
|
||||||
// its contents, so it will always be located at the
|
|
||||||
// expected offset at runtime.
|
|
||||||
C_struct([C_int(ccx, discr),
|
|
||||||
C_packed_struct([C_struct(body)]),
|
|
||||||
padding(max_sz - case.size)])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,11 +484,9 @@ pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
|
|||||||
pub fn const_get_field(ccx: @CrateContext, r: &Repr, val: ValueRef,
|
pub fn const_get_field(ccx: @CrateContext, r: &Repr, val: ValueRef,
|
||||||
_discr: int, ix: uint) -> ValueRef {
|
_discr: int, ix: uint) -> ValueRef {
|
||||||
match *r {
|
match *r {
|
||||||
CEnum(*) => ccx.sess.bug(~"element access in C-like enum \
|
CEnum(*) => ccx.sess.bug(~"element access in C-like enum const"),
|
||||||
const"),
|
|
||||||
Univariant(*) => const_struct_field(ccx, val, ix),
|
Univariant(*) => const_struct_field(ccx, val, ix),
|
||||||
General(*) => const_struct_field(ccx, const_get_elt(ccx, val,
|
General(*) => const_struct_field(ccx, val, ix + 1)
|
||||||
[1, 0]), ix)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
src/test/run-pass/enum-alignment.rs
Normal file
26
src/test/run-pass/enum-alignment.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
fn addr_of<T>(ptr: &T) -> uint {
|
||||||
|
let ptr = ptr::addr_of(ptr);
|
||||||
|
unsafe { ptr as uint }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_aligned<T>(ptr: &T) -> bool {
|
||||||
|
(addr_of(ptr) % sys::min_align_of::<T>()) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let x = Some(0u64);
|
||||||
|
match x {
|
||||||
|
None => fail!(),
|
||||||
|
Some(ref y) => fail_unless!(is_aligned(y))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user