Skip to content

Commit d69d324

Browse files
committed
Allow nearly all ADT aggregates to create operands
1 parent 5d13765 commit d69d324

File tree

8 files changed

+369
-89
lines changed

8 files changed

+369
-89
lines changed

compiler/rustc_codegen_ssa/src/mir/operand.rs

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt;
22

33
use rustc_abi as abi;
44
use rustc_abi::{
5-
Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, Variants,
5+
Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, VariantIdx, Variants,
66
};
77
use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range};
88
use rustc_middle::mir::{self, ConstValue};
@@ -545,67 +545,109 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
545545
};
546546
OperandRef { val, layout }
547547
}
548+
549+
pub(crate) fn supports_builder(layout: TyAndLayout<'tcx>) -> bool {
550+
match layout.backend_repr {
551+
BackendRepr::Memory { .. } if layout.is_zst() => true,
552+
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _) => true,
553+
_ => false,
554+
}
555+
}
548556
}
549557

550558
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
551559
pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
552560
&mut self,
553561
bx: &mut Bx,
562+
v: VariantIdx,
554563
f: FieldIdx,
555564
operand: OperandRef<'tcx, V>,
556565
) {
557-
let field_layout = self.layout.field(bx.cx(), f.as_usize());
558-
let field_offset = self.layout.fields.offset(f.as_usize());
566+
let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields {
567+
// Don't ask for field layout for primitives, because that will panic.
568+
if !self.layout.uninhabited {
569+
// Real primitives only have one variant, but weird types like
570+
// `Result<!, !>` turn out to also be "Primitive", and dead code
571+
// like `Err(never)` needs to not ICE.
572+
assert_eq!(v, FIRST_VARIANT);
573+
}
574+
let first_field = f == FieldIdx::ZERO;
575+
(self.layout.is_zst() || !first_field, first_field)
576+
} else {
577+
let variant_layout = self.layout.for_variant(bx.cx(), v);
578+
let field_layout = variant_layout.field(bx.cx(), f.as_usize());
579+
let field_offset = variant_layout.fields.offset(f.as_usize());
580+
(field_layout.is_zst(), field_offset == Size::ZERO)
581+
};
559582

560583
let mut update = |tgt: &mut Result<V, abi::Scalar>, src, from_scalar| {
561584
let from_bty = bx.cx().type_from_scalar(from_scalar);
562585
let to_scalar = tgt.unwrap_err();
563586
let to_bty = bx.cx().type_from_scalar(to_scalar);
564-
let v = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
565-
*tgt = Ok(v);
587+
let imm = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
588+
*tgt = Ok(imm);
566589
};
567590

568591
match (operand.val, operand.layout.backend_repr) {
569-
(OperandValue::ZeroSized, _) => {
570-
debug_assert_eq!(field_layout.size, Size::ZERO);
571-
}
592+
(OperandValue::ZeroSized, _) if expect_zst => {}
572593
(OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val {
573-
OperandValue::Immediate(val @ Err(_)) => {
574-
debug_assert_eq!(field_offset, Size::ZERO);
594+
OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
575595
update(val, v, from_scalar);
576-
//*val = Ok(v);
577596
}
578-
OperandValue::Pair(fst @ Err(_), _) if field_offset == Size::ZERO => {
597+
OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
579598
update(fst, v, from_scalar);
580-
//*fst = Ok(v);
581599
}
582-
OperandValue::Pair(_, snd @ Err(_)) if field_offset != Size::ZERO => {
600+
OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
583601
update(snd, v, from_scalar);
584-
//*snd = Ok(v);
585602
}
586-
_ => bug!("Tried to insert {operand:?} into field {f:?} of {self:?}"),
603+
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
587604
},
588605
(OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => {
589606
match &mut self.val {
590607
OperandValue::Pair(fst @ Err(_), snd @ Err(_)) => {
591608
update(fst, a, from_sa);
592-
//*fst = Ok(a);
593609
update(snd, b, from_sb);
594-
//*snd = Ok(b);
595610
}
596-
_ => bug!("Tried to insert {operand:?} into field {f:?} of {self:?}"),
611+
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
597612
}
598613
}
599-
_ => bug!("Unsupported operand {operand:?} inserting into field {f:?} of {self:?}"),
614+
_ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"),
615+
}
616+
}
617+
618+
pub(super) fn insert_imm(&mut self, f: FieldIdx, imm: V) {
619+
let field_offset = self.layout.fields.offset(f.as_usize());
620+
let is_zero_offset = field_offset == Size::ZERO;
621+
match &mut self.val {
622+
OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
623+
*val = Ok(imm);
624+
}
625+
OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
626+
*fst = Ok(imm);
627+
}
628+
OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
629+
*snd = Ok(imm);
630+
}
631+
_ => bug!("Tried to insert {imm:?} into field {f:?} of {self:?}"),
600632
}
601633
}
602634

603-
pub fn finalize(self) -> OperandRef<'tcx, V> {
604-
let OperandRef { val, layout } = self;
635+
pub fn finalize(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> {
636+
let OperandRef { val, layout } = *self;
637+
638+
let unwrap = |r: Result<V, abi::Scalar>| match r {
639+
Ok(v) => v,
640+
Err(s) if s.is_uninit_valid() => {
641+
let bty = cx.type_from_scalar(s);
642+
cx.const_undef(bty)
643+
}
644+
Err(_) => bug!("OperandRef::finalize called while fields are missing {self:?}"),
645+
};
646+
605647
let val = match val {
606648
OperandValue::ZeroSized => OperandValue::ZeroSized,
607-
OperandValue::Immediate(v) => OperandValue::Immediate(v.unwrap()),
608-
OperandValue::Pair(a, b) => OperandValue::Pair(a.unwrap(), b.unwrap()),
649+
OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)),
650+
OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)),
609651
OperandValue::Ref(_) => bug!(),
610652
};
611653
OperandRef { val, layout }

compiler/rustc_codegen_ssa/src/mir/place.rs

Lines changed: 82 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use rustc_abi::{Align, BackendRepr, FieldsShape, Size, TagEncoding, VariantIdx, Variants};
1+
use rustc_abi::{
2+
Align, BackendRepr, FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants,
3+
};
24
use rustc_middle::mir::PlaceTy;
35
use rustc_middle::mir::interpret::Scalar;
46
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
@@ -239,53 +241,17 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
239241
bx: &mut Bx,
240242
variant_index: VariantIdx,
241243
) {
242-
if self.layout.for_variant(bx.cx(), variant_index).is_uninhabited() {
243-
// We play it safe by using a well-defined `abort`, but we could go for immediate UB
244-
// if that turns out to be helpful.
245-
bx.abort();
246-
return;
247-
}
248-
match self.layout.variants {
249-
Variants::Empty => unreachable!("we already handled uninhabited types"),
250-
Variants::Single { index } => assert_eq!(index, variant_index),
251-
252-
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
253-
let ptr = self.project_field(bx, tag_field);
254-
let to =
255-
self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val;
256-
bx.store_to_place(
257-
bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to),
258-
ptr.val,
259-
);
244+
match codegen_tagged_field_value(bx.cx(), variant_index, self.layout) {
245+
Err(UninhabitedVariantError) => {
246+
// We play it safe by using a well-defined `abort`, but we could go for immediate UB
247+
// if that turns out to be helpful.
248+
bx.abort();
260249
}
261-
Variants::Multiple {
262-
tag_encoding:
263-
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
264-
tag_field,
265-
..
266-
} => {
267-
if variant_index != untagged_variant {
268-
let niche = self.project_field(bx, tag_field);
269-
let niche_llty = bx.cx().immediate_backend_type(niche.layout);
270-
let BackendRepr::Scalar(scalar) = niche.layout.backend_repr else {
271-
bug!("expected a scalar placeref for the niche");
272-
};
273-
// We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping
274-
// around the `niche`'s type.
275-
// The easiest way to do that is to do wrapping arithmetic on `u128` and then
276-
// masking off any extra bits that occur because we did the arithmetic with too many bits.
277-
let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
278-
let niche_value = (niche_value as u128).wrapping_add(niche_start);
279-
let niche_value = niche_value & niche.layout.size.unsigned_int_max();
280-
281-
let niche_llval = bx.cx().scalar_to_backend(
282-
Scalar::from_uint(niche_value, niche.layout.size),
283-
scalar,
284-
niche_llty,
285-
);
286-
OperandValue::Immediate(niche_llval).store(bx, niche);
287-
}
250+
Ok(Some((tag_field, imm))) => {
251+
let tag_place = self.project_field(bx, tag_field.as_usize());
252+
OperandValue::Immediate(imm).store(bx, tag_place);
288253
}
254+
Ok(None) => {}
289255
}
290256
}
291257

@@ -471,3 +437,73 @@ fn round_up_const_value_to_alignment<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
471437
let offset = bx.and(neg_value, align_minus_1);
472438
bx.add(value, offset)
473439
}
440+
441+
/// Calculates the value that needs to be stored to mark the discriminant.
442+
///
443+
/// This might be `None` for a `struct` or a niched variant (like `Some(&3)`).
444+
///
445+
/// If it's `Some`, it returns the value to store and the field in which to
446+
/// store it. Note that this value is *not* the same as the discriminant, in
447+
/// general, as it might be a niche value or have a different size.
448+
///
449+
/// It might also be an `Err` because the variant is uninhabited.
450+
pub(super) fn codegen_tagged_field_value<'tcx, V>(
451+
cx: &impl CodegenMethods<'tcx, Value = V>,
452+
variant_index: VariantIdx,
453+
layout: TyAndLayout<'tcx>,
454+
) -> Result<Option<(FieldIdx, V)>, UninhabitedVariantError> {
455+
// By checking uninhabited-ness first we don't need to worry about types
456+
// like `(u32, !)` which are single-variant but weird.
457+
if layout.for_variant(cx, variant_index).is_uninhabited() {
458+
return Err(UninhabitedVariantError);
459+
}
460+
461+
Ok(match layout.variants {
462+
Variants::Empty => unreachable!("we already handled uninhabited types"),
463+
Variants::Single { index } => {
464+
assert_eq!(index, variant_index);
465+
None
466+
}
467+
468+
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
469+
let discr = layout.ty.discriminant_for_variant(cx.tcx(), variant_index);
470+
let to = discr.unwrap().val;
471+
let tag_layout = layout.field(cx, tag_field);
472+
let tag_llty = cx.immediate_backend_type(tag_layout);
473+
let imm = cx.const_uint_big(tag_llty, to);
474+
Some((FieldIdx::from_usize(tag_field), imm))
475+
}
476+
Variants::Multiple {
477+
tag_encoding: TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
478+
tag_field,
479+
..
480+
} => {
481+
if variant_index != untagged_variant {
482+
let niche_layout = layout.field(cx, tag_field);
483+
let niche_llty = cx.immediate_backend_type(niche_layout);
484+
let BackendRepr::Scalar(scalar) = niche_layout.backend_repr else {
485+
bug!("expected a scalar placeref for the niche");
486+
};
487+
// We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping
488+
// around the `niche`'s type.
489+
// The easiest way to do that is to do wrapping arithmetic on `u128` and then
490+
// masking off any extra bits that occur because we did the arithmetic with too many bits.
491+
let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
492+
let niche_value = (niche_value as u128).wrapping_add(niche_start);
493+
let niche_value = niche_value & niche_layout.size.unsigned_int_max();
494+
495+
let niche_llval = cx.scalar_to_backend(
496+
Scalar::from_uint(niche_value, niche_layout.size),
497+
scalar,
498+
niche_llty,
499+
);
500+
Some((FieldIdx::from_usize(tag_field), niche_llval))
501+
} else {
502+
None
503+
}
504+
}
505+
})
506+
}
507+
508+
#[derive(Debug)]
509+
pub(super) struct UninhabitedVariantError;

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_span::{DUMMY_SP, Span};
1010
use tracing::{debug, instrument};
1111

1212
use super::operand::{OperandRef, OperandValue, assume_scalar_range, transmute_immediate};
13-
use super::place::PlaceRef;
13+
use super::place::{PlaceRef, codegen_tagged_field_value};
1414
use super::{FunctionCx, LocalRef};
1515
use crate::common::IntPredicate;
1616
use crate::traits::*;
@@ -682,17 +682,42 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
682682
}
683683
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
684684
mir::Rvalue::Repeat(..) => bug!("{rvalue:?} in codegen_rvalue_operand"),
685-
mir::Rvalue::Aggregate(_, ref fields) => {
685+
mir::Rvalue::Aggregate(ref kind, ref fields) => {
686+
let (variant_index, active_field_index) = match **kind {
687+
mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
688+
(variant_index, active_field_index)
689+
}
690+
_ => (FIRST_VARIANT, None),
691+
};
692+
686693
let ty = rvalue.ty(self.mir, self.cx.tcx());
687694
let ty = self.monomorphize(ty);
688695
let layout = self.cx.layout_of(ty);
689696

690697
let mut builder = OperandRef::builder(layout);
691698
for (field_idx, field) in fields.iter_enumerated() {
692699
let op = self.codegen_operand(bx, field);
693-
builder.insert_field(bx, field_idx, op);
700+
let fi = active_field_index.unwrap_or(field_idx);
701+
builder.insert_field(bx, variant_index, fi, op);
702+
}
703+
704+
let tag_result = codegen_tagged_field_value(self.cx, variant_index, layout);
705+
match tag_result {
706+
Err(super::place::UninhabitedVariantError) => {
707+
// Like codegen_set_discr we use a sound abort, but could
708+
// potentially `unreachable` or just return the poison for
709+
// more optimizability, if that turns out to be helpful.
710+
bx.abort();
711+
let val = OperandValue::poison(bx, layout);
712+
OperandRef { val, layout }
713+
}
714+
Ok(maybe_tag_value) => {
715+
if let Some((tag_field, tag_imm)) = maybe_tag_value {
716+
builder.insert_imm(tag_field, tag_imm);
717+
}
718+
builder.finalize(bx.cx())
719+
}
694720
}
695-
builder.finalize()
696721
}
697722
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
698723
let operand = self.codegen_operand(bx, operand);
@@ -1023,19 +1048,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10231048
mir::AggregateKind::RawPtr(..) => true,
10241049
mir::AggregateKind::Array(..) => false,
10251050
mir::AggregateKind::Tuple => true,
1026-
mir::AggregateKind::Adt(def_id, ..) => {
1027-
let adt_def = self.cx.tcx().adt_def(def_id);
1028-
adt_def.is_struct() && !adt_def.repr().simd()
1029-
}
1051+
mir::AggregateKind::Adt(..) => true,
10301052
mir::AggregateKind::Closure(..) => true,
10311053
// FIXME: Can we do this for simple coroutines too?
10321054
mir::AggregateKind::Coroutine(..) | mir::AggregateKind::CoroutineClosure(..) => false,
10331055
};
10341056
allowed_kind && {
1035-
let ty = rvalue.ty(self.mir, self.cx.tcx());
1036-
let ty = self.monomorphize(ty);
1057+
let ty = rvalue.ty(self.mir, self.cx.tcx());
1058+
let ty = self.monomorphize(ty);
10371059
let layout = self.cx.spanned_layout_of(ty, span);
1038-
!self.cx.is_backend_ref(layout)
1060+
OperandRef::<Bx::Value>::supports_builder(layout)
10391061
}
10401062
}
10411063
}

tests/codegen/align-struct.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ pub struct Nested64 {
1313
d: i8,
1414
}
1515

16+
// This has the extra field in B to ensure it's not ScalarPair,
17+
// and thus that the test actually emits it via memory, not `insertvalue`.
1618
pub enum Enum4 {
1719
A(i32),
18-
B(i32),
20+
B(i32, i32),
1921
}
2022

2123
pub enum Enum64 {
@@ -52,7 +54,7 @@ pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 {
5254
// CHECK-LABEL: @enum4
5355
#[no_mangle]
5456
pub fn enum4(a: i32) -> Enum4 {
55-
// CHECK: %e4 = alloca [8 x i8], align 4
57+
// CHECK: %e4 = alloca [12 x i8], align 4
5658
let e4 = Enum4::A(a);
5759
e4
5860
}

0 commit comments

Comments
 (0)