Skip to content

Commit 29ad853

Browse files
committed
auto merge of #20060 : Aatch/rust/enum-repr, r=alexcrichton
The previous behaviour of using the smallest type possible caused LLVM to treat padding too conservatively, causing poor codegen. This commit changes the behaviour to use an alignment-sized integer as the discriminant. This keeps types the same size, but helps LLVM understand the data structure a little better, resulting in better codegen.
2 parents e64a819 + b473311 commit 29ad853

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

src/librustc_trans/trans/adt.rs

+61-6
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,62 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
256256
assert!((cases.len() - 1) as i64 >= 0);
257257
let bounds = IntBounds { ulo: 0, uhi: (cases.len() - 1) as u64,
258258
slo: 0, shi: (cases.len() - 1) as i64 };
259-
let ity = range_to_inttype(cx, hint, &bounds);
259+
let min_ity = range_to_inttype(cx, hint, &bounds);
260+
261+
// Create the set of structs that represent each variant
262+
// Use the minimum integer type we figured out above
263+
let fields : Vec<_> = cases.iter().map(|c| {
264+
let mut ftys = vec!(ty_of_inttype(min_ity));
265+
ftys.push_all(c.tys.as_slice());
266+
if dtor { ftys.push(ty::mk_bool()); }
267+
mk_struct(cx, ftys.as_slice(), false, t)
268+
}).collect();
269+
270+
271+
// Check to see if we should use a different type for the
272+
// discriminant. If the overall alignment of the type is
273+
// the same as the first field in each variant, we can safely use
274+
// an alignment-sized type.
275+
// We increase the size of the discriminant to avoid LLVM copying
276+
// padding when it doesn't need to. This normally causes unaligned
277+
// load/stores and excessive memcpy/memset operations. By using a
278+
// bigger integer size, LLVM can be sure about it's contents and
279+
// won't be so conservative.
280+
// This check is needed to avoid increasing the size of types when
281+
// the alignment of the first field is smaller than the overall
282+
// alignment of the type.
283+
let (_, align) = union_size_and_align(fields.as_slice());
284+
let mut use_align = true;
285+
for st in fields.iter() {
286+
// Get the first non-zero-sized field
287+
let field = st.fields.iter().skip(1).filter(|ty| {
288+
let t = type_of::sizing_type_of(cx, **ty);
289+
machine::llsize_of_real(cx, t) != 0 ||
290+
// This case is only relevant for zero-sized types with large alignment
291+
machine::llalign_of_min(cx, t) != 1
292+
}).next();
293+
294+
if let Some(field) = field {
295+
let field_align = type_of::align_of(cx, *field);
296+
if field_align != align {
297+
use_align = false;
298+
break;
299+
}
300+
}
301+
}
302+
let ity = if use_align {
303+
// Use the overall alignment
304+
match align {
305+
1 => attr::UnsignedInt(ast::TyU8),
306+
2 => attr::UnsignedInt(ast::TyU16),
307+
4 => attr::UnsignedInt(ast::TyU32),
308+
8 if machine::llalign_of_min(cx, Type::i64(cx)) == 8 =>
309+
attr::UnsignedInt(ast::TyU64),
310+
_ => min_ity // use min_ity as a fallback
311+
}
312+
} else {
313+
min_ity
314+
};
260315

261316
let fields : Vec<_> = cases.iter().map(|c| {
262317
let mut ftys = vec!(ty_of_inttype(ity));
@@ -570,7 +625,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
570625
let discr_ty = ll_inttype(cx, ity);
571626
let discr_size = machine::llsize_of_alloc(cx, discr_ty);
572627
let align_units = (size + align_s - 1) / align_s - 1;
573-
let pad_ty = match align_s {
628+
let fill_ty = match align_s {
574629
1 => Type::array(&Type::i8(cx), align_units),
575630
2 => Type::array(&Type::i16(cx), align_units),
576631
4 => Type::array(&Type::i32(cx), align_units),
@@ -580,11 +635,11 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
580635
align_units),
581636
_ => panic!("unsupported enum alignment: {}", align)
582637
};
583-
assert_eq!(machine::llalign_of_min(cx, pad_ty), align);
638+
assert_eq!(machine::llalign_of_min(cx, fill_ty), align);
584639
assert_eq!(align_s % discr_size, 0);
585-
let fields = vec!(discr_ty,
586-
Type::array(&discr_ty, align_s / discr_size - 1),
587-
pad_ty);
640+
let fields = [discr_ty,
641+
Type::array(&discr_ty, align_s / discr_size - 1),
642+
fill_ty];
588643
match name {
589644
None => Type::struct_(cx, fields[], false),
590645
Some(name) => {

src/test/run-pass/type-sizes.rs

+18
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ struct w {a: int, b: ()}
1818
struct x {a: int, b: (), c: ()}
1919
struct y {x: int}
2020

21+
enum e1 {
22+
a(u8, u32), b(u32), c
23+
}
24+
enum e2 {
25+
a(u32), b
26+
}
27+
28+
enum e3 {
29+
a([u16, ..0], u8), b
30+
}
31+
2132
pub fn main() {
2233
assert_eq!(size_of::<u8>(), 1 as uint);
2334
assert_eq!(size_of::<u32>(), 4 as uint);
@@ -34,4 +45,11 @@ pub fn main() {
3445
assert_eq!(size_of::<w>(), size_of::<int>());
3546
assert_eq!(size_of::<x>(), size_of::<int>());
3647
assert_eq!(size_of::<int>(), size_of::<y>());
48+
49+
// Make sure enum types are the appropriate size, mostly
50+
// around ensuring alignment is handled properly
51+
52+
assert_eq!(size_of::<e1>(), 8 as uint);
53+
assert_eq!(size_of::<e2>(), 8 as uint);
54+
assert_eq!(size_of::<e3>(), 4 as uint);
3755
}

0 commit comments

Comments
 (0)