diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 04e24320f9131..1d495bccdc9a3 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -53,7 +53,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( .layout() .non_1zst_field(fx) .expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type"); - arg = arg.value_field(fx, FieldIdx::new(idx)); + arg = arg.value_field(fx, idx); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 24f2c50e882fb..1951043d430ef 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1019,7 +1019,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (idx, _) = op.layout.non_1zst_field(bx).expect( "not exactly one non-1-ZST field in a `DispatchFromDyn` type", ); - op = op.extract_field(bx, idx); + op = op.extract_field(bx, idx.as_usize()); } // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its @@ -1051,7 +1051,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (idx, _) = op.layout.non_1zst_field(bx).expect( "not exactly one non-1-ZST field in a `DispatchFromDyn` type", ); - op = op.extract_field(bx, idx); + op = op.extract_field(bx, idx.as_usize()); } // Make sure that we've actually unwrapped the rcvr down diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 6725a6d9e38d8..6c460cdf176ab 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -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))] @@ -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(); @@ -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, + } } } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index c0e27e86d500a..efd72bf284cd4 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -793,7 +793,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (idx, _) = receiver.layout.non_1zst_field(self).expect( "not exactly one non-1-ZST field in a `DispatchFromDyn` type", ); - receiver = self.project_field(&receiver, idx)?; + receiver = self.project_field(&receiver, idx.as_usize())?; } } }; diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 24e49ff648f2f..5939e59cbd7e2 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -279,7 +279,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// Finds the one field that is not a 1-ZST. /// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields. - pub fn non_1zst_field(&self, cx: &C) -> Option<(usize, Self)> + pub fn non_1zst_field(&self, cx: &C) -> Option<(FieldIdx, Self)> where Ty: TyAbiInterface<'a, C> + Copy, { @@ -293,8 +293,12 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { // More than one non-1-ZST field. return None; } - found = Some((field_idx, field)); + + // Arrays are the only things with more than FieldIdx::MAX fields, + // but only 1-element arrays can ever return non-None here. + found = Some((FieldIdx::from_usize(field_idx), field)); } + found } } diff --git a/tests/codegen/transparent-aggregates.rs b/tests/codegen/transparent-aggregates.rs new file mode 100644 index 0000000000000..2e66525c55848 --- /dev/null +++ b/tests/codegen/transparent-aggregates.rs @@ -0,0 +1,69 @@ +//@ compile-flags: -O -C no-prepopulate-passes + +#![crate_type = "lib"] + +#[repr(transparent)] +pub struct Transparent32(u32); + +// CHECK: i32 @make_transparent(i32 noundef %x) +#[no_mangle] +pub fn make_transparent(x: u32) -> Transparent32 { + // 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-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 { + // 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) +}