Skip to content

Commit

Permalink
Keep simple transparent things from needing allocas
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmcm committed Apr 13, 2024
1 parent ab19c5f commit 7cb89e3
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 17 deletions.
71 changes: 64 additions & 7 deletions compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
use rustc_session::config::OptLevel;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{self, FIRST_VARIANT};
use rustc_target::abi::{self, FieldIdx, FIRST_VARIANT};

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
#[instrument(level = "trace", skip(self, bx))]
Expand Down Expand Up @@ -720,12 +720,44 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
OperandRef { val: OperandValue::Immediate(static_), layout }
}
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => {
mir::Rvalue::Repeat(..) => {
// According to `rvalue_creates_operand`, only ZST
// aggregate rvalues are allowed to be operands.
// repat rvalues are allowed to be operands.
let ty = rvalue.ty(self.mir, self.cx.tcx());
OperandRef::zero_sized(self.cx.layout_of(self.monomorphize(ty)))
}
mir::Rvalue::Aggregate(ref kind, ref fields) => {
let ty = rvalue.ty(self.mir, self.cx.tcx());
let ty = self.monomorphize(ty);
let layout = self.cx.layout_of(self.monomorphize(ty));
match **kind {
_ if layout.is_zst() => OperandRef::zero_sized(layout),
mir::AggregateKind::Tuple => {
debug_assert_eq!(
fields.len(),
2,
"We should only get pairs, but got {rvalue:?}"
);
let a = self.codegen_operand(bx, &fields[FieldIdx::ZERO]);
let b = self.codegen_operand(bx, &fields[FieldIdx::from_u32(1)]);
let val = OperandValue::Pair(a.immediate(), b.immediate());
OperandRef { val, layout }
}
mir::AggregateKind::Adt(..) => {
let (field_idx, _) = layout
.non_1zst_field(self.cx)
.expect("only transparent non-ZST structs should get here");
let field = self.codegen_operand(bx, &fields[field_idx]);
// While the size is the same, since the struct is transparent,
// calling transmute here handles the `i1`-vs-`i8` issues for `bool`.
let Some(val) = self.codegen_transmute_operand(bx, field, layout) else {
bug!("Couldn't transmute {field:?} to {layout:?}");
};
OperandRef { val, layout }
}
_ => bug!("Unexpected in codegen_rvalue_operand: {rvalue:?}"),
}
}
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
let operand = self.codegen_operand(bx, operand);
let val = operand.immediate();
Expand Down Expand Up @@ -1032,12 +1064,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::ThreadLocalRef(_) |
mir::Rvalue::Use(..) => // (*)
true,
mir::Rvalue::Repeat(..) |
mir::Rvalue::Aggregate(..) => {
mir::Rvalue::Repeat(..) => {
let ty = rvalue.ty(self.mir, self.cx.tcx());
let ty = self.monomorphize(ty);
// For ZST this can be `OperandValueKind::ZeroSized`.
self.cx.spanned_layout_of(ty, span).is_zst()
let layout = self.cx.spanned_layout_of(ty, span);
layout.is_zst()
}
mir::Rvalue::Aggregate(ref kind, ref fields) => {
let ty = rvalue.ty(self.mir, self.cx.tcx());
let ty = self.monomorphize(ty);
let layout = self.cx.spanned_layout_of(ty, span);
match **kind {
// OperandValue::ZeroSized is easy
_ if layout.is_zst() => true,
// 2-Tuple of scalars is an easy scalar pair
mir::AggregateKind::Tuple => {
fields.len() == 2
&& self.cx.is_backend_scalar_pair(layout)
&& fields.iter().all(|field| {
let field_ty = field.ty(self.mir, self.cx.tcx());
let field_ty = self.monomorphize(field_ty);
let field_layout = self.cx.spanned_layout_of(field_ty, span);
self.cx.is_backend_immediate(field_layout)
})
}
// If a non-union is transparent, we can pass it along
mir::AggregateKind::Adt(_, _, _, _, None) => {
ty.ty_adt_def().is_some_and(|def| def.repr().transparent())
&& !self.cx.is_backend_ref(layout)
}
_ => false,
}
}
}

Expand Down
60 changes: 50 additions & 10 deletions tests/codegen/transparent-aggregates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,67 @@
#![crate_type = "lib"]

#[repr(transparent)]
struct Transparent32(u32);
pub struct Transparent32(u32);

// CHECK: i32 @make_transparent(i32 noundef %x)
#[no_mangle]
pub fn make_transparent(x: u32) -> Transparent32 {
// CHECK: %a = alloca i32
// CHECK: store i32 %x, ptr %a
// CHECK: %[[TEMP:.+]] = load i32, ptr %a
// CHECK: ret i32 %[[TEMP]]
// CHECK-NOT: alloca
// CHECK: ret i32 %x
let a = Transparent32(x);
a
}

#[repr(transparent)]
pub struct TransparentPair((), (u16, u16), ());

// CHECK: { i16, i16 } @make_transparent_pair(i16 noundef %x.0, i16 noundef %x.1)
#[no_mangle]
pub fn make_transparent_pair(x: (u16, u16)) -> TransparentPair {
// CHECK-NOT: alloca
// CHECK: %[[TEMP0:.+]] = insertvalue { i16, i16 } poison, i16 %x.0, 0
// CHECK: %[[TEMP1:.+]] = insertvalue { i16, i16 } %[[TEMP0]], i16 %x.1, 1
// CHECK: ret { i16, i16 } %[[TEMP1]]
let a = TransparentPair((), x, ());
a
}

// CHECK-LABEL: { i32, i32 } @make_2_tuple(i32 noundef %x)
#[no_mangle]
pub fn make_2_tuple(x: u32) -> (u32, u32) {
// CHECK: %pair = alloca { i32, i32 }
// CHECK: store i32
// CHECK: store i32
// CHECK: load i32
// CHECK: load i32
// CHECK-NOT: alloca
// CHECK: %[[TEMP0:.+]] = insertvalue { i32, i32 } poison, i32 %x, 0
// CHECK: %[[TEMP1:.+]] = insertvalue { i32, i32 } %[[TEMP0]], i32 %x, 1
// CHECK: ret { i32, i32 } %[[TEMP1]]
let pair = (x, x);
pair
}

// CHECK-LABEL: i8 @make_cell_of_bool(i1 noundef zeroext %b)
#[no_mangle]
pub fn make_cell_of_bool(b: bool) -> std::cell::Cell<bool> {
// CHECK: %[[BYTE:.+]] = zext i1 %b to i8
// CHECK: ret i8 %[[BYTE]]
std::cell::Cell::new(b)
}

// CHECK-LABLE: { i8, i16 } @make_cell_of_bool_and_short(i1 noundef zeroext %b, i16 noundef %s)
#[no_mangle]
pub fn make_cell_of_bool_and_short(b: bool, s: u16) -> std::cell::Cell<(bool, u16)> {
// CHECK-NOT: alloca
// CHECK: %[[BYTE:.+]] = zext i1 %b to i8
// CHECK: %[[TEMP0:.+]] = insertvalue { i8, i16 } poison, i8 %[[BYTE]], 0
// CHECK: %[[TEMP1:.+]] = insertvalue { i8, i16 } %[[TEMP0]], i16 %s, 1
// CHECK: ret { i8, i16 } %[[TEMP1]]
std::cell::Cell::new((b, s))
}

// CHECK-LABEL: { i1, i1 } @make_tuple_of_bools(i1 noundef zeroext %a, i1 noundef zeroext %b)
#[no_mangle]
pub fn make_tuple_of_bools(a: bool, b: bool) -> (bool, bool) {
// CHECK-NOT: alloca
// CHECK: %[[TEMP0:.+]] = insertvalue { i1, i1 } poison, i1 %a, 0
// CHECK: %[[TEMP1:.+]] = insertvalue { i1, i1 } %[[TEMP0]], i1 %b, 1
// CHECK: ret { i1, i1 } %[[TEMP1]]
(a, b)
}

0 comments on commit 7cb89e3

Please sign in to comment.