Skip to content

Commit f1a3ff0

Browse files
committed
Use type-alignment-sized integer for discriminant types
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 type-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.
1 parent 2f3cff6 commit f1a3ff0

File tree

2 files changed

+77
-6
lines changed

2 files changed

+77
-6
lines changed

src/librustc_trans/trans/adt.rs

Lines changed: 61 additions & 6 deletions
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.as_slice(), false),
590645
Some(name) => {

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ 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+
enum e3 {
28+
a([u64, ..0], u32), b
29+
}
30+
2131
pub fn main() {
2232
assert_eq!(size_of::<u8>(), 1 as uint);
2333
assert_eq!(size_of::<u32>(), 4 as uint);
@@ -34,4 +44,10 @@ pub fn main() {
3444
assert_eq!(size_of::<w>(), size_of::<int>());
3545
assert_eq!(size_of::<x>(), size_of::<int>());
3646
assert_eq!(size_of::<int>(), size_of::<y>());
47+
48+
// Make sure enum types are the appropriate size, mostly
49+
// around ensuring alignment is handled properly
50+
assert_eq!(size_of::<e1>(), 8 as uint);
51+
assert_eq!(size_of::<e2>(), 8 as uint);
52+
assert_eq!(size_of::<e3>(), 16 as uint);
3753
}

0 commit comments

Comments
 (0)