Skip to content

Commit 3ca6ad9

Browse files
committed
Use ScalarPair for tagged enums
1 parent 65d201f commit 3ca6ad9

File tree

4 files changed

+90
-14
lines changed

4 files changed

+90
-14
lines changed

src/librustc/ty/layout.rs

+73-8
Original file line numberDiff line numberDiff line change
@@ -1622,7 +1622,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
16221622
}
16231623

16241624
// Create the set of structs that represent each variant.
1625-
let mut variants = variants.into_iter().enumerate().map(|(i, field_layouts)| {
1625+
let mut layout_variants = variants.iter().enumerate().map(|(i, field_layouts)| {
16261626
let mut st = univariant_uninterned(&field_layouts,
16271627
&def.repr, StructKind::Prefixed(min_ity.size(), prefix_align))?;
16281628
st.variants = Variants::Single { index: i };
@@ -1683,7 +1683,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
16831683
// Patch up the variants' first few fields.
16841684
let old_ity_size = min_ity.size();
16851685
let new_ity_size = ity.size();
1686-
for variant in &mut variants {
1686+
for variant in &mut layout_variants {
16871687
if variant.abi == Abi::Uninhabited {
16881688
continue;
16891689
}
@@ -1710,15 +1710,80 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
17101710
value: Int(ity, signed),
17111711
valid_range: (min as u128 & tag_mask)..=(max as u128 & tag_mask),
17121712
};
1713-
let abi = if tag.value.size(dl) == size {
1714-
Abi::Scalar(tag.clone())
1715-
} else {
1716-
Abi::Aggregate { sized: true }
1717-
};
1713+
let mut abi = Abi::Aggregate { sized: true };
1714+
if tag.value.size(dl) == size {
1715+
abi = Abi::Scalar(tag.clone());
1716+
} else if !tag.is_bool() {
1717+
// HACK(nox): Blindly using ScalarPair for all tagged enums
1718+
// where applicable leads to Option<u8> being handled as {i1, i8},
1719+
// which later confuses SROA and some loop optimisations,
1720+
// ultimately leading to the repeat-trusted-len test
1721+
// failing. We make the trade-off of using ScalarPair only
1722+
// for types where the tag isn't a boolean.
1723+
let mut common_prim = None;
1724+
for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) {
1725+
let offsets = match layout_variant.fields {
1726+
FieldPlacement::Arbitrary { ref offsets, .. } => offsets,
1727+
_ => bug!(),
1728+
};
1729+
let mut fields = field_layouts
1730+
.iter()
1731+
.zip(offsets)
1732+
.filter(|p| !p.0.is_zst());
1733+
let (field, offset) = match (fields.next(), fields.next()) {
1734+
(None, None) => continue,
1735+
(Some(pair), None) => pair,
1736+
_ => {
1737+
common_prim = None;
1738+
break;
1739+
}
1740+
};
1741+
let prim = match field.details.abi {
1742+
Abi::Scalar(ref scalar) => scalar.value,
1743+
_ => {
1744+
common_prim = None;
1745+
break;
1746+
}
1747+
};
1748+
if let Some(pair) = common_prim {
1749+
// This is pretty conservative. We could go fancier
1750+
// by conflating things like i32 and u32, or even
1751+
// realising that (u8, u8) could just cohabit with
1752+
// u16 or even u32.
1753+
if pair != (prim, offset) {
1754+
common_prim = None;
1755+
break;
1756+
}
1757+
} else {
1758+
common_prim = Some((prim, offset));
1759+
}
1760+
}
1761+
if let Some((prim, offset)) = common_prim {
1762+
let pair = scalar_pair(tag.clone(), scalar_unit(prim));
1763+
let pair_offsets = match pair.fields {
1764+
FieldPlacement::Arbitrary {
1765+
ref offsets,
1766+
ref memory_index
1767+
} => {
1768+
assert_eq!(memory_index, &[0, 1]);
1769+
offsets
1770+
}
1771+
_ => bug!()
1772+
};
1773+
if pair_offsets[0] == Size::from_bytes(0) &&
1774+
pair_offsets[1] == *offset &&
1775+
align == pair.align &&
1776+
size == pair.size {
1777+
// We can use `ScalarPair` only when it matches our
1778+
// already computed layout (including `#[repr(C)]`).
1779+
abi = pair.abi;
1780+
}
1781+
}
1782+
}
17181783
tcx.intern_layout(LayoutDetails {
17191784
variants: Variants::Tagged {
17201785
discr: tag,
1721-
variants
1786+
variants: layout_variants,
17221787
},
17231788
fields: FieldPlacement::Arbitrary {
17241789
offsets: vec![Size::from_bytes(0)],

src/test/codegen/align-struct.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ pub enum Enum4 {
2929
A(i32),
3030
B(i32),
3131
}
32-
// CHECK: %Enum4 = type { [0 x i32], i32, [1 x i32] }
3332
// CHECK: %"Enum4::A" = type { [1 x i32], i32, [0 x i32] }
3433

3534
pub enum Enum64 {
@@ -59,7 +58,7 @@ pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 {
5958
// CHECK-LABEL: @enum4
6059
#[no_mangle]
6160
pub fn enum4(a: i32) -> Enum4 {
62-
// CHECK: %e4 = alloca %Enum4, align 4
61+
// CHECK: %e4 = alloca { i32, i32 }, align 4
6362
let e4 = Enum4::A(a);
6463
e4
6564
}

src/test/codegen/function-arguments.rs

+12
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ pub fn return_slice(x: &[u16]) -> &[u16] {
145145
x
146146
}
147147

148+
// CHECK: { i16, i16 } @enum_id_1(i16 %x.0, i16 %x.1)
149+
#[no_mangle]
150+
pub fn enum_id_1(x: Option<Result<u16, u16>>) -> Option<Result<u16, u16>> {
151+
x
152+
}
153+
154+
// CHECK: i16 @enum_id_2(i16)
155+
#[no_mangle]
156+
pub fn enum_id_2(x: Option<u8>) -> Option<u8> {
157+
x
158+
}
159+
148160
// CHECK: noalias i8* @allocator()
149161
#[no_mangle]
150162
#[allocator]

src/test/codegen/lifetime_start_end.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ pub fn test() {
2525
let b = &Some(a);
2626
&b; // keep variable in an alloca
2727

28-
// CHECK: [[S_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
28+
// CHECK: [[S_b:%[0-9]+]] = bitcast { i32, i32 }** %b to i8*
2929
// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_b]])
3030

31-
// CHECK: [[S__4:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_4 to i8*
31+
// CHECK: [[S__4:%[0-9]+]] = bitcast { i32, i32 }* %_4 to i8*
3232
// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S__4]])
3333

34-
// CHECK: [[E_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
34+
// CHECK: [[E_b:%[0-9]+]] = bitcast { i32, i32 }** %b to i8*
3535
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_b]])
3636

37-
// CHECK: [[E__4:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_4 to i8*
37+
// CHECK: [[E__4:%[0-9]+]] = bitcast { i32, i32 }* %_4 to i8*
3838
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E__4]])
3939
}
4040

0 commit comments

Comments
 (0)