Skip to content

Commit dd8ffd0

Browse files
committed
Keep simple transparent things from needing allocas
1 parent ab19c5f commit dd8ffd0

File tree

2 files changed

+117
-17
lines changed

2 files changed

+117
-17
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+67-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
1515
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
1616
use rustc_session::config::OptLevel;
1717
use rustc_span::{Span, DUMMY_SP};
18-
use rustc_target::abi::{self, FIRST_VARIANT};
18+
use rustc_target::abi::{self, FieldIdx, FIRST_VARIANT};
1919

2020
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
2121
#[instrument(level = "trace", skip(self, bx))]
@@ -720,12 +720,47 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
720720
OperandRef { val: OperandValue::Immediate(static_), layout }
721721
}
722722
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
723-
mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => {
723+
mir::Rvalue::Repeat(..) => {
724724
// According to `rvalue_creates_operand`, only ZST
725-
// aggregate rvalues are allowed to be operands.
725+
// repat rvalues are allowed to be operands.
726726
let ty = rvalue.ty(self.mir, self.cx.tcx());
727727
OperandRef::zero_sized(self.cx.layout_of(self.monomorphize(ty)))
728728
}
729+
mir::Rvalue::Aggregate(ref kind, ref fields) => {
730+
let ty = rvalue.ty(self.mir, self.cx.tcx());
731+
let ty = self.monomorphize(ty);
732+
let layout = self.cx.layout_of(self.monomorphize(ty));
733+
match **kind {
734+
_ if layout.is_zst() => OperandRef::zero_sized(layout),
735+
mir::AggregateKind::Tuple => {
736+
debug_assert_eq!(
737+
fields.len(),
738+
2,
739+
"We should only get pairs, but got {rvalue:?}"
740+
);
741+
let a = self.codegen_operand(bx, &fields[FieldIdx::ZERO]);
742+
let b = self.codegen_operand(bx, &fields[FieldIdx::from_u32(1)]);
743+
let val = OperandValue::Pair(a.immediate(), b.immediate());
744+
OperandRef { val, layout }
745+
}
746+
mir::AggregateKind::Adt(..) => {
747+
let (field, _) = layout
748+
.non_1zst_field(self.cx)
749+
.expect("only transparent non-ZST structs should get here");
750+
let val = match self.codegen_operand(bx, &fields[field]).val {
751+
OperandValue::Immediate(a) => {
752+
OperandValue::Immediate(bx.from_immediate(a))
753+
}
754+
OperandValue::Pair(a, b) => {
755+
OperandValue::Pair(bx.from_immediate(a), bx.from_immediate(b))
756+
}
757+
other => bug!("Unexpected operand {other:?}"),
758+
};
759+
OperandRef { val, layout }
760+
}
761+
_ => bug!("Unexpected in codegen_rvalue_operand: {rvalue:?}"),
762+
}
763+
}
729764
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
730765
let operand = self.codegen_operand(bx, operand);
731766
let val = operand.immediate();
@@ -1032,12 +1067,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10321067
mir::Rvalue::ThreadLocalRef(_) |
10331068
mir::Rvalue::Use(..) => // (*)
10341069
true,
1035-
mir::Rvalue::Repeat(..) |
1036-
mir::Rvalue::Aggregate(..) => {
1070+
mir::Rvalue::Repeat(..) => {
1071+
let ty = rvalue.ty(self.mir, self.cx.tcx());
1072+
let ty = self.monomorphize(ty);
1073+
let layout = self.cx.spanned_layout_of(ty, span);
1074+
layout.is_zst()
1075+
}
1076+
mir::Rvalue::Aggregate(ref kind, ref fields) => {
10371077
let ty = rvalue.ty(self.mir, self.cx.tcx());
10381078
let ty = self.monomorphize(ty);
1039-
// For ZST this can be `OperandValueKind::ZeroSized`.
1040-
self.cx.spanned_layout_of(ty, span).is_zst()
1079+
let layout = self.cx.spanned_layout_of(ty, span);
1080+
match **kind {
1081+
// OperandValue::ZeroSized is easy
1082+
_ if layout.is_zst() => true,
1083+
// 2-Tuple of scalars is an easy scalar pair
1084+
mir::AggregateKind::Tuple => {
1085+
fields.len() == 2
1086+
&& self.cx.is_backend_scalar_pair(layout)
1087+
&& fields.iter().all(|field| {
1088+
let field_ty = field.ty(self.mir, self.cx.tcx());
1089+
let field_ty = self.monomorphize(field_ty);
1090+
let field_layout = self.cx.spanned_layout_of(field_ty, span);
1091+
self.cx.is_backend_immediate(field_layout)
1092+
})
1093+
}
1094+
// If a non-union is transparent, we can pass it along
1095+
mir::AggregateKind::Adt(_, _, _, _, None) => {
1096+
ty.ty_adt_def().is_some_and(|def| def.repr().transparent())
1097+
&& !self.cx.is_backend_ref(layout)
1098+
}
1099+
_ => false,
1100+
}
10411101
}
10421102
}
10431103

tests/codegen/transparent-aggregates.rs

+50-10
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,67 @@
33
#![crate_type = "lib"]
44

55
#[repr(transparent)]
6-
struct Transparent32(u32);
6+
pub struct Transparent32(u32);
77

88
// CHECK: i32 @make_transparent(i32 noundef %x)
99
#[no_mangle]
1010
pub fn make_transparent(x: u32) -> Transparent32 {
11-
// CHECK: %a = alloca i32
12-
// CHECK: store i32 %x, ptr %a
13-
// CHECK: %[[TEMP:.+]] = load i32, ptr %a
14-
// CHECK: ret i32 %[[TEMP]]
11+
// CHECK-NOT: alloca
12+
// CHECK: ret i32 %x
1513
let a = Transparent32(x);
1614
a
1715
}
1816

17+
#[repr(transparent)]
18+
pub struct TransparentPair((), (u16, u16), ());
19+
20+
// CHECK: { i16, i16 } @make_transparent_pair(i16 noundef %x.0, i16 noundef %x.1)
21+
#[no_mangle]
22+
pub fn make_transparent_pair(x: (u16, u16)) -> TransparentPair {
23+
// CHECK-NOT: alloca
24+
// CHECK: %[[TEMP0:.+]] = insertvalue { i16, i16 } poison, i16 %x.0, 0
25+
// CHECK: %[[TEMP1:.+]] = insertvalue { i16, i16 } %[[TEMP0]], i16 %x.1, 1
26+
// CHECK: ret { i16, i16 } %[[TEMP1]]
27+
let a = TransparentPair((), x, ());
28+
a
29+
}
30+
1931
// CHECK-LABEL: { i32, i32 } @make_2_tuple(i32 noundef %x)
2032
#[no_mangle]
2133
pub fn make_2_tuple(x: u32) -> (u32, u32) {
22-
// CHECK: %pair = alloca { i32, i32 }
23-
// CHECK: store i32
24-
// CHECK: store i32
25-
// CHECK: load i32
26-
// CHECK: load i32
34+
// CHECK-NOT: alloca
35+
// CHECK: %[[TEMP0:.+]] = insertvalue { i32, i32 } poison, i32 %x, 0
36+
// CHECK: %[[TEMP1:.+]] = insertvalue { i32, i32 } %[[TEMP0]], i32 %x, 1
37+
// CHECK: ret { i32, i32 } %[[TEMP1]]
2738
let pair = (x, x);
2839
pair
2940
}
41+
42+
// CHECK-LABEL: i8 @make_cell_of_bool(i1 noundef zeroext %b)
43+
#[no_mangle]
44+
pub fn make_cell_of_bool(b: bool) -> std::cell::Cell<bool> {
45+
// CHECK: %[[BYTE:.+]] = zext i1 %b to i8
46+
// CHECK: ret i8 %[[BYTE]]
47+
std::cell::Cell::new(b)
48+
}
49+
50+
// CHECK-LABLE: { i8, i16 } @make_cell_of_bool_and_short(i1 noundef zeroext %b, i16 noundef %s)
51+
#[no_mangle]
52+
pub fn make_cell_of_bool_and_short(b: bool, s: u16) -> std::cell::Cell<(bool, u16)> {
53+
// CHECK-NOT: alloca
54+
// CHECK: %[[BYTE:.+]] = zext i1 %b to i8
55+
// CHECK: %[[TEMP0:.+]] = insertvalue { i8, i16 } poison, i8 %[[BYTE]], 0
56+
// CHECK: %[[TEMP1:.+]] = insertvalue { i8, i16 } %[[TEMP0]], i16 %s, 1
57+
// CHECK: ret { i8, i16 } %[[TEMP1]]
58+
std::cell::Cell::new((b, s))
59+
}
60+
61+
// CHECK-LABEL: { i1, i1 } @make_tuple_of_bools(i1 noundef zeroext %a, i1 noundef zeroext %b)
62+
#[no_mangle]
63+
pub fn make_tuple_of_bools(a: bool, b: bool) -> (bool, bool) {
64+
// CHECK-NOT: alloca
65+
// CHECK: %[[TEMP0:.+]] = insertvalue { i1, i1 } poison, i1 %a, 0
66+
// CHECK: %[[TEMP1:.+]] = insertvalue { i1, i1 } %[[TEMP0]], i1 %b, 1
67+
// CHECK: ret { i1, i1 } %[[TEMP1]]
68+
(a, b)
69+
}

0 commit comments

Comments
 (0)