Skip to content

Commit 019c41a

Browse files
committed
Also allow constructing enums as SSA values
This means that simple enum usages, like the ones internal to `for i in 0..n` stay entirely in SSA, no allocas needed.
1 parent 755f04b commit 019c41a

File tree

5 files changed

+167
-13
lines changed

5 files changed

+167
-13
lines changed

Diff for: compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+81-5
Original file line numberDiff line numberDiff line change
@@ -779,16 +779,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
779779
}
780780
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
781781
mir::Rvalue::Repeat(..) => bug!("{rvalue:?} in codegen_rvalue_operand"),
782-
mir::Rvalue::Aggregate(_, ref fields) => {
782+
mir::Rvalue::Aggregate(ref kind, ref fields) => {
783783
let ty = rvalue.ty(self.mir, self.cx.tcx());
784784
let ty = self.monomorphize(ty);
785785
let layout = self.cx.layout_of(ty);
786786

787+
let variant_idx = match **kind {
788+
mir::AggregateKind::Adt(_, vi, _, _, None) => vi,
789+
mir::AggregateKind::RawPtr(..)
790+
| mir::AggregateKind::Closure(..)
791+
| mir::AggregateKind::Tuple => FIRST_VARIANT,
792+
_ => bug!("{rvalue:?} in codegen_rvalue_operand"),
793+
};
794+
let variant_layout = layout.for_variant(self.cx, variant_idx);
795+
787796
// `rvalue_creates_operand` has arranged that we only get here if
788797
// we can build the aggregate immediate from the field immediates.
789798
let mut inputs = ArrayVec::<Bx::Value, 2>::new();
790799
let mut input_scalars = ArrayVec::<abi::Scalar, 2>::new();
791-
for field_idx in layout.fields.index_by_increasing_offset() {
800+
for field_idx in variant_layout.fields.index_by_increasing_offset() {
792801
let field_idx = FieldIdx::from_usize(field_idx);
793802
let op = self.codegen_operand(bx, &fields[field_idx]);
794803
let values = op.val.immediates_or_place().left_or_else(|p| {
@@ -801,12 +810,66 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
801810
}
802811

803812
let output_scalars = self.value_kind(layout).scalars().unwrap();
813+
match layout.variants {
814+
abi::Variants::Empty => bug!(),
815+
abi::Variants::Single { index } => assert_eq!(index, variant_idx),
816+
abi::Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
817+
match *tag_encoding {
818+
abi::TagEncoding::Direct => {
819+
assert_eq!(layout.fields.offset(tag_field), abi::Size::ZERO);
820+
821+
let discr = ty
822+
.discriminant_for_variant(self.cx.tcx(), variant_idx)
823+
.unwrap();
824+
let discr_layout = bx.cx().layout_of(discr.ty);
825+
let discr_ibty = self.cx.immediate_backend_type(discr_layout);
826+
let discr_const = self.cx.const_uint_big(discr_ibty, discr.val);
827+
828+
let tag_layout = layout.field(self.cx, tag_field);
829+
let tag_ibty = self.cx.immediate_backend_type(tag_layout);
830+
let tag_const = bx.intcast(discr_const, tag_ibty, tag.is_signed());
831+
832+
inputs.insert(0, tag_const);
833+
input_scalars.insert(0, tag);
834+
835+
// If this variant has fewer payloads, add the extra `undef`s.
836+
if input_scalars.len() != output_scalars.len() {
837+
// Since we just inserted a tag, this can only happen
838+
// if we're missing the second thing in a pair.
839+
assert_eq!(input_scalars.len(), 1);
840+
assert_eq!(output_scalars.len(), 2);
841+
842+
let ibty =
843+
self.cx.scalar_pair_element_backend_type(layout, 1, true);
844+
let undef = self.cx.const_undef(ibty);
845+
inputs.push(undef);
846+
input_scalars.push(output_scalars[1]);
847+
}
848+
}
849+
abi::TagEncoding::Niche { untagged_variant, .. } => {
850+
// For now we only handle variants where the niche is automatic,
851+
// but catch it if `rvalue_creates_operand` changes.
852+
assert_eq!(untagged_variant, variant_idx);
853+
}
854+
}
855+
}
856+
}
857+
804858
itertools::izip!(&mut inputs, input_scalars, output_scalars).for_each(
805859
|(v, in_s, out_s)| {
806860
if in_s != out_s {
807861
// We have to be really careful about bool here, because
808862
// `(bool,)` stays i1 but `Cell<bool>` becomes i8.
809863
*v = bx.from_immediate(*v);
864+
865+
// Also we might be storing `usize` into a pointer field,
866+
// in which case we have to `without_provenance` it.
867+
if let abi::Primitive::Int(..) = in_s.primitive()
868+
&& let abi::Primitive::Pointer(..) = out_s.primitive()
869+
{
870+
*v = bx.ptradd(bx.const_null(bx.type_ptr()), *v);
871+
}
872+
810873
*v = bx.to_immediate_scalar(*v, out_s);
811874
}
812875
},
@@ -1146,14 +1209,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11461209
// (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.)
11471210
mir::Rvalue::Repeat(..) => false,
11481211
mir::Rvalue::Aggregate(ref kind, _) => {
1212+
let mut variant_idx = None;
11491213
let allowed_kind = match **kind {
11501214
// This always produces a `ty::RawPtr`, so will be Immediate or Pair
11511215
mir::AggregateKind::RawPtr(..) => true,
11521216
mir::AggregateKind::Array(..) => false,
11531217
mir::AggregateKind::Tuple => true,
1154-
mir::AggregateKind::Adt(def_id, ..) => {
1218+
mir::AggregateKind::Adt(def_id, vidx, ..) => {
1219+
variant_idx = Some(vidx);
11551220
let adt_def = self.cx.tcx().adt_def(def_id);
1156-
adt_def.is_struct() && !adt_def.repr().simd()
1221+
(adt_def.is_struct() || adt_def.is_enum())
1222+
&& !adt_def.repr().simd()
11571223
}
11581224
mir::AggregateKind::Closure(..) => true,
11591225
// FIXME: Can we do this for simple coroutines too?
@@ -1163,7 +1229,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11631229
let ty = rvalue.ty(self.mir, self.cx.tcx());
11641230
let ty = self.monomorphize(ty);
11651231
let layout = self.cx.spanned_layout_of(ty, span);
1166-
!self.cx.is_backend_ref(layout)
1232+
1233+
variant_idx.is_none_or(|v| !layout.for_variant(self.cx, v).uninhabited)
1234+
&& !self.cx.is_backend_ref(layout)
1235+
&& match &layout.variants {
1236+
abi::Variants::Empty => bug!(),
1237+
abi::Variants::Single { .. } => true,
1238+
abi::Variants::Multiple { tag_encoding, ..} => match *tag_encoding {
1239+
abi::TagEncoding::Direct => true,
1240+
abi::TagEncoding::Niche { untagged_variant, ..} => Some(untagged_variant) == variant_idx,
1241+
}
1242+
}
11671243
}
11681244
}
11691245
}

Diff for: tests/codegen/align-struct.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub struct Nested64 {
1515

1616
pub enum Enum4 {
1717
A(i32),
18-
B(i32),
18+
B(i32, i32),
1919
}
2020

2121
pub enum Enum64 {
@@ -52,7 +52,7 @@ pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 {
5252
// CHECK-LABEL: @enum4
5353
#[no_mangle]
5454
pub fn enum4(a: i32) -> Enum4 {
55-
// CHECK: %e4 = alloca [8 x i8], align 4
55+
// CHECK: %e4 = alloca [12 x i8], align 4
5656
let e4 = Enum4::A(a);
5757
e4
5858
}

Diff for: tests/codegen/common_prim_int_ptr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#[no_mangle]
1212
pub fn insert_int(x: usize) -> Result<usize, Box<()>> {
1313
// CHECK: start:
14-
// CHECK-NEXT: inttoptr i{{[0-9]+}} %x to ptr
14+
// CHECK-NEXT: getelementptr i8, ptr null, i{{[0-9]+}} %x
1515
// CHECK-NEXT: insertvalue
1616
// CHECK-NEXT: ret { i{{[0-9]+}}, ptr }
1717
Ok(x)

Diff for: tests/codegen/enum/enum-aggregate.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//@ compile-flags: -Copt-level=0 -Cno-prepopulate-passes -Cdebuginfo=0
2+
//@ min-llvm-version: 19
3+
//@ only-64bit
4+
5+
#![crate_type = "lib"]
6+
7+
use std::cmp::Ordering;
8+
use std::num::NonZero;
9+
use std::ptr::NonNull;
10+
11+
#[no_mangle]
12+
fn make_some_bool(x: bool) -> Option<bool> {
13+
// CHECK-LABEL: i8 @make_some_bool(i1 zeroext %x)
14+
// CHECK-NEXT: start:
15+
// CHECK-NEXT: %[[WIDER:.+]] = zext i1 %x to i8
16+
// CHECK-NEXT: ret i8 %[[WIDER]]
17+
Some(x)
18+
}
19+
20+
#[no_mangle]
21+
fn make_none_bool() -> Option<bool> {
22+
// FIXME: Aggregating a niched variant other than the untagged one isn't supported yet
23+
24+
// CHECK-LABEL: i8 @make_none_bool()
25+
// CHECK-NEXT: start:
26+
// CHECK-NEXT: %_0 = alloca
27+
// CHECK-NEXT: store i8 2, ptr %_0
28+
None
29+
}
30+
31+
#[no_mangle]
32+
fn make_some_ordering(x: Ordering) -> Option<Ordering> {
33+
// CHECK-LABEL: i8 @make_some_ordering(i8 %x)
34+
// CHECK-NEXT: start:
35+
// CHECK-NEXT: ret i8 %x
36+
Some(x)
37+
}
38+
39+
#[no_mangle]
40+
fn make_some_u16(x: u16) -> Option<u16> {
41+
// CHECK-LABEL: { i16, i16 } @make_some_u16(i16 %x)
42+
// CHECK-NEXT: start:
43+
// CHECK-NEXT: %0 = insertvalue { i16, i16 } { i16 1, i16 poison }, i16 %x, 1
44+
// CHECK-NEXT: ret { i16, i16 } %0
45+
Some(x)
46+
}
47+
48+
#[no_mangle]
49+
fn make_none_u16() -> Option<u16> {
50+
// CHECK-LABEL: { i16, i16 } @make_none_u16()
51+
// CHECK-NEXT: start:
52+
// CHECK-NEXT: ret { i16, i16 } { i16 0, i16 undef }
53+
None
54+
}
55+
56+
#[no_mangle]
57+
fn make_some_nzu32(x: NonZero<u32>) -> Option<NonZero<u32>> {
58+
// CHECK-LABEL: i32 @make_some_nzu32(i32 %x)
59+
// CHECK-NEXT: start:
60+
// CHECK-NEXT: ret i32 %x
61+
Some(x)
62+
}
63+
64+
#[no_mangle]
65+
fn make_ok_ptr(x: NonNull<u16>) -> Result<NonNull<u16>, usize> {
66+
// CHECK-LABEL: { i64, ptr } @make_ok_ptr(ptr %x)
67+
// CHECK-NEXT: start:
68+
// CHECK-NEXT: %0 = insertvalue { i64, ptr } { i64 0, ptr poison }, ptr %x, 1
69+
// CHECK-NEXT: ret { i64, ptr } %0
70+
Ok(x)
71+
}
72+
73+
#[no_mangle]
74+
fn make_ok_int(x: usize) -> Result<usize, NonNull<u16>> {
75+
// CHECK-LABEL: { i64, ptr } @make_ok_int(i64 %x)
76+
// CHECK-NEXT: start:
77+
// CHECK-NEXT: %[[NOPROV:.+]] = getelementptr i8, ptr null, i64 %x
78+
// CHECK-NEXT: %[[R:.+]] = insertvalue { i64, ptr } { i64 0, ptr poison }, ptr %[[NOPROV]], 1
79+
// CHECK-NEXT: ret { i64, ptr } %[[R]]
80+
Ok(x)
81+
}

Diff for: tests/codegen/range-loop.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ pub fn call_for_zero_to_n(n: u32, f: fn(u32)) {
1414
// CHECK: start:
1515
// CHECK-NOT: alloca
1616
// CHECK: %[[IND:.+]] = alloca [4 x i8]
17-
// CHECK-NEXT: %[[ALWAYS_SOME_OPTION:.+]] = alloca
1817
// CHECK-NOT: alloca
1918
// CHECK: store i32 0, ptr %[[IND]],
2019
// CHECK: br label %[[HEAD:.+]]
@@ -31,10 +30,8 @@ pub fn call_for_zero_to_n(n: u32, f: fn(u32)) {
3130
// CHECK: %[[T2:.+]] = load i32, ptr %[[IND]],
3231
// CHECK: %[[T3:.+]] = add nuw i32 %[[T2]], 1
3332
// CHECK: store i32 %[[T3]], ptr %[[IND]],
34-
35-
// CHECK: store i32 %[[T2]]
36-
// CHECK: %[[T4:.+]] = load i32
37-
// CHECK: call void %f(i32{{.+}}%[[T4]])
33+
// CHECK: call void %f(i32{{.+}}%[[T2]])
34+
// CHECK: br label %[[HEAD]]
3835

3936
for i in 0..n {
4037
f(i);

0 commit comments

Comments
 (0)