Skip to content

Commit a14ec73

Browse files
committed
auto merge of #5356 : jld/rust/enum-less-magic, r=graydon
Fixes #1645.
2 parents 5820991 + a301db7 commit a14ec73

File tree

2 files changed

+74
-38
lines changed

2 files changed

+74
-38
lines changed

src/librustc/middle/trans/adt.rs

+48-38
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@
2323
* Having everything in one place will enable improvements to data
2424
* structure representation; possibilities include:
2525
*
26-
* - Aligning enum bodies correctly, which in turn makes possible SIMD
27-
* vector types (which are strict-alignment even on x86) and ports
28-
* to strict-alignment architectures (PowerPC, SPARC, etc.).
29-
*
3026
* - User-specified alignment (e.g., cacheline-aligning parts of
3127
* concurrently accessed data structures); LLVM can't represent this
3228
* directly, so we'd have to insert padding fields in any structure
@@ -82,10 +78,8 @@ pub enum Repr {
8278
*/
8379
Univariant(Struct, bool),
8480
/**
85-
* General-case enums: discriminant as int, followed by fields.
86-
* The fields start immediately after the discriminant, meaning
87-
* that they may not be correctly aligned for the platform's ABI;
88-
* see above.
81+
* General-case enums: for each case there is a struct, and they
82+
* all start with a field for the discriminant.
8983
*/
9084
General(~[Struct])
9185
}
@@ -156,7 +150,8 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
156150
discriminants",
157151
ty::item_path_str(cx.tcx, def_id)))
158152
}
159-
General(cases.map(|c| mk_struct(cx, c.tys)))
153+
let discr = ~[ty::mk_int(cx.tcx)];
154+
General(cases.map(|c| mk_struct(cx, discr + c.tys)))
160155
}
161156
}
162157
_ => cx.sess.bug(~"adt::represent_type called on non-ADT type")
@@ -191,20 +186,44 @@ fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
191186
-> ~[TypeRef] {
192187
match *r {
193188
CEnum(*) => ~[T_enum_discrim(cx)],
194-
Univariant(ref st, _dtor) => {
195-
if sizing {
196-
st.fields.map(|&ty| type_of::sizing_type_of(cx, ty))
197-
} else {
198-
st.fields.map(|&ty| type_of::type_of(cx, ty))
199-
}
200-
}
189+
Univariant(ref st, _dtor) => struct_llfields(cx, st, sizing),
201190
General(ref sts) => {
202-
~[T_enum_discrim(cx),
203-
T_array(T_i8(), sts.map(|st| st.size).max() /*bad*/as uint)]
191+
// To get "the" type of a general enum, we pick the case
192+
// with the largest alignment (so it will always align
193+
// correctly in containing structures) and pad it out.
194+
fail_unless!(sts.len() >= 1);
195+
let mut most_aligned = None;
196+
let mut largest_align = 0;
197+
let mut largest_size = 0;
198+
for sts.each |st| {
199+
if largest_size < st.size {
200+
largest_size = st.size;
201+
}
202+
if largest_align < st.align {
203+
// Clang breaks ties by size; it is unclear if
204+
// that accomplishes anything important.
205+
largest_align = st.align;
206+
most_aligned = Some(st);
207+
}
208+
}
209+
let most_aligned = most_aligned.get();
210+
let padding = largest_size - most_aligned.size;
211+
212+
struct_llfields(cx, most_aligned, sizing)
213+
+ [T_array(T_i8(), padding /*bad*/as uint)]
204214
}
205215
}
206216
}
207217

218+
fn struct_llfields(cx: @CrateContext, st: &Struct, sizing: bool)
219+
-> ~[TypeRef] {
220+
if sizing {
221+
st.fields.map(|&ty| type_of::sizing_type_of(cx, ty))
222+
} else {
223+
st.fields.map(|&ty| type_of::type_of(cx, ty))
224+
}
225+
}
226+
208227
/**
209228
* Obtain a representation of the discriminant sufficient to translate
210229
* destructuring; this may or may not involve the actual discriminant.
@@ -309,7 +328,7 @@ pub fn num_args(r: &Repr, discr: int) -> uint {
309328
fail_unless!(discr == 0);
310329
st.fields.len() - (if dtor { 1 } else { 0 })
311330
}
312-
General(ref cases) => cases[discr as uint].fields.len()
331+
General(ref cases) => cases[discr as uint].fields.len() - 1
313332
}
314333
}
315334

@@ -328,8 +347,7 @@ pub fn trans_field_ptr(bcx: block, r: &Repr, val: ValueRef, discr: int,
328347
struct_field_ptr(bcx, st, val, ix, false)
329348
}
330349
General(ref cases) => {
331-
struct_field_ptr(bcx, &cases[discr as uint],
332-
GEPi(bcx, val, [0, 1]), ix, true)
350+
struct_field_ptr(bcx, &cases[discr as uint], val, ix + 1, true)
333351
}
334352
}
335353
}
@@ -371,13 +389,12 @@ pub fn trans_drop_flag_ptr(bcx: block, r: &Repr, val: ValueRef) -> ValueRef {
371389
* depending on which case of an enum it is.
372390
*
373391
* To understand the alignment situation, consider `enum E { V64(u64),
374-
* V32(u32, u32) }` on win32. The type should have 8-byte alignment
375-
* to accommodate the u64 (currently it doesn't; this is a known bug),
376-
* but `V32(x, y)` would have LLVM type `{i32, i32, i32}`, which is
377-
* 4-byte aligned.
392+
* V32(u32, u32) }` on win32. The type has 8-byte alignment to
393+
* accommodate the u64, but `V32(x, y)` would have LLVM type `{i32,
394+
* i32, i32}`, which is 4-byte aligned.
378395
*
379396
* Currently the returned value has the same size as the type, but
380-
* this may be changed in the future to avoid allocating unnecessary
397+
* this could be changed in the future to avoid allocating unnecessary
381398
* space after values of shorter-than-maximum cases.
382399
*/
383400
pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
@@ -395,14 +412,9 @@ pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
395412
General(ref cases) => {
396413
let case = &cases[discr as uint];
397414
let max_sz = cases.map(|s| s.size).max();
398-
let body = build_const_struct(ccx, case, vals);
399-
400-
// The unary packed struct has alignment 1 regardless of
401-
// its contents, so it will always be located at the
402-
// expected offset at runtime.
403-
C_struct([C_int(ccx, discr),
404-
C_packed_struct([C_struct(body)]),
405-
padding(max_sz - case.size)])
415+
let contents = build_const_struct(ccx, case,
416+
~[C_int(ccx, discr)] + vals);
417+
C_struct(contents + [padding(max_sz - case.size)])
406418
}
407419
}
408420
}
@@ -472,11 +484,9 @@ pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
472484
pub fn const_get_field(ccx: @CrateContext, r: &Repr, val: ValueRef,
473485
_discr: int, ix: uint) -> ValueRef {
474486
match *r {
475-
CEnum(*) => ccx.sess.bug(~"element access in C-like enum \
476-
const"),
487+
CEnum(*) => ccx.sess.bug(~"element access in C-like enum const"),
477488
Univariant(*) => const_struct_field(ccx, val, ix),
478-
General(*) => const_struct_field(ccx, const_get_elt(ccx, val,
479-
[1, 0]), ix)
489+
General(*) => const_struct_field(ccx, val, ix + 1)
480490
}
481491
}
482492

src/test/run-pass/enum-alignment.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn addr_of<T>(ptr: &T) -> uint {
12+
let ptr = ptr::addr_of(ptr);
13+
unsafe { ptr as uint }
14+
}
15+
16+
fn is_aligned<T>(ptr: &T) -> bool {
17+
(addr_of(ptr) % sys::min_align_of::<T>()) == 0
18+
}
19+
20+
pub fn main() {
21+
let x = Some(0u64);
22+
match x {
23+
None => fail!(),
24+
Some(ref y) => fail_unless!(is_aligned(y))
25+
}
26+
}

0 commit comments

Comments
 (0)