@@ -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) => {
0 commit comments