Skip to content

Commit 7ef5c99

Browse files
authored
Rollup merge of rust-lang#55834 - ogoffart:union-abi, r=eddyb
Forward the ABI of the non-zero sized fields of an union if they have the same ABI This is supposed to fix the performence regression of using MaybeUninit in rust-lang#54668
2 parents eef30d2 + c040a48 commit 7ef5c99

File tree

3 files changed

+120
-1
lines changed

3 files changed

+120
-1
lines changed

src/librustc/ty/layout.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,9 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
697697
Align::from_bytes(repr_align, repr_align).unwrap());
698698
}
699699

700+
let optimize = !def.repr.inhibit_union_abi_opt();
700701
let mut size = Size::ZERO;
702+
let mut abi = Abi::Aggregate { sized: true };
701703
let index = VariantIdx::new(0);
702704
for field in &variants[index] {
703705
assert!(!field.is_unsized());
@@ -708,13 +710,44 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
708710
} else {
709711
align = align.max(field.align);
710712
}
713+
714+
// If all non-ZST fields have the same ABI, forward this ABI
715+
if optimize && !field.is_zst() {
716+
// Normalize scalar_unit to the maximal valid range
717+
let field_abi = match &field.abi {
718+
Abi::Scalar(x) => Abi::Scalar(scalar_unit(x.value)),
719+
Abi::ScalarPair(x, y) => {
720+
Abi::ScalarPair(
721+
scalar_unit(x.value),
722+
scalar_unit(y.value),
723+
)
724+
}
725+
Abi::Vector { element: x, count } => {
726+
Abi::Vector {
727+
element: scalar_unit(x.value),
728+
count: *count,
729+
}
730+
}
731+
Abi::Uninhabited |
732+
Abi::Aggregate { .. } => Abi::Aggregate { sized: true },
733+
};
734+
735+
if size == Size::ZERO {
736+
// first non ZST: initialize 'abi'
737+
abi = field_abi;
738+
} else if abi != field_abi {
739+
// different fields have different ABI: reset to Aggregate
740+
abi = Abi::Aggregate { sized: true };
741+
}
742+
}
743+
711744
size = cmp::max(size, field.size);
712745
}
713746

714747
return Ok(tcx.intern_layout(LayoutDetails {
715748
variants: Variants::Single { index },
716749
fields: FieldPlacement::Union(variants[index].len()),
717-
abi: Abi::Aggregate { sized: true },
750+
abi,
718751
align,
719752
size: size.abi_align(align)
720753
}));

src/librustc/ty/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,12 @@ impl ReprOptions {
19981998
pub fn inhibit_struct_field_reordering_opt(&self) -> bool {
19991999
!(self.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty() || (self.pack == 1)
20002000
}
2001+
2002+
/// Returns true if this `#[repr()]` should inhibit union abi optimisations
2003+
pub fn inhibit_union_abi_opt(&self) -> bool {
2004+
self.c()
2005+
}
2006+
20012007
}
20022008

20032009
impl<'a, 'gcx, 'tcx> AdtDef {

src/test/codegen/union-abi.rs

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright 2017 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+
// compile-flags: -C no-prepopulate-passes
12+
13+
// This test that using union forward the abi of the inner type, as
14+
// discussed in #54668
15+
16+
#![crate_type="lib"]
17+
#![feature(repr_simd)]
18+
19+
#[derive(Copy, Clone)]
20+
pub enum Unhab {}
21+
22+
#[repr(simd)]
23+
#[derive(Copy, Clone)]
24+
pub struct i64x4(i64, i64, i64, i64);
25+
26+
#[derive(Copy, Clone)]
27+
pub union UnionI64x4{ a:(), b: i64x4 }
28+
29+
// CHECK: define void @test_UnionI64x4(<4 x i64>* {{.*}} %arg0)
30+
#[no_mangle]
31+
pub fn test_UnionI64x4(_: UnionI64x4) { loop {} }
32+
33+
pub union UnionI64x4_{ a: i64x4, b: (), c:i64x4, d: Unhab, e: ((),()), f: UnionI64x4 }
34+
35+
// CHECK: define void @test_UnionI64x4_(<4 x i64>* {{.*}} %arg0)
36+
#[no_mangle]
37+
pub fn test_UnionI64x4_(_: UnionI64x4_) { loop {} }
38+
39+
pub union UnionI64x4I64{ a: i64x4, b: i64 }
40+
41+
// CHECK: define void @test_UnionI64x4I64(%UnionI64x4I64* {{.*}} %arg0)
42+
#[no_mangle]
43+
pub fn test_UnionI64x4I64(_: UnionI64x4I64) { loop {} }
44+
45+
pub union UnionI64x4Tuple{ a: i64x4, b: (i64, i64, i64, i64) }
46+
47+
// CHECK: define void @test_UnionI64x4Tuple(%UnionI64x4Tuple* {{.*}} %arg0)
48+
#[no_mangle]
49+
pub fn test_UnionI64x4Tuple(_: UnionI64x4Tuple) { loop {} }
50+
51+
52+
pub union UnionF32{a:f32}
53+
54+
// CHECK: define float @test_UnionF32(float %arg0)
55+
#[no_mangle]
56+
pub fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} }
57+
58+
pub union UnionF32F32{a:f32, b:f32}
59+
60+
// CHECK: define float @test_UnionF32F32(float %arg0)
61+
#[no_mangle]
62+
pub fn test_UnionF32F32(_: UnionF32F32) -> UnionF32F32 { loop {} }
63+
64+
pub union UnionF32U32{a:f32, b:u32}
65+
66+
// CHECK: define i32 @test_UnionF32U32(i32)
67+
#[no_mangle]
68+
pub fn test_UnionF32U32(_: UnionF32U32) -> UnionF32U32 { loop {} }
69+
70+
pub union UnionU128{a:u128}
71+
// CHECK: define i128 @test_UnionU128(i128 %arg0)
72+
#[no_mangle]
73+
pub fn test_UnionU128(_: UnionU128) -> UnionU128 { loop {} }
74+
75+
#[repr(C)]
76+
pub union CUnionU128{a:u128}
77+
// CHECK: define void @test_CUnionU128(%CUnionU128* {{.*}} %arg0)
78+
#[no_mangle]
79+
pub fn test_CUnionU128(_: CUnionU128) { loop {} }
80+

0 commit comments

Comments
 (0)