Skip to content

Commit 4c822e9

Browse files
committed
Allow nearly all ADT aggregates to create operands
1 parent fbb321e commit 4c822e9

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};
@@ -580,67 +580,109 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
580580
};
581581
OperandRef { val, layout }
582582
}
583+
584+
pub(crate) fn supports_builder(layout: TyAndLayout<'tcx>) -> bool {
585+
match layout.backend_repr {
586+
BackendRepr::Memory { .. } if layout.is_zst() => true,
587+
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _) => true,
588+
_ => false,
589+
}
590+
}
583591
}
584592

585593
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
586594
pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
587595
&mut self,
588596
bx: &mut Bx,
597+
v: VariantIdx,
589598
f: FieldIdx,
590599
operand: OperandRef<'tcx, V>,
591600
) {
592-
let field_layout = self.layout.field(bx.cx(), f.as_usize());
593-
let field_offset = self.layout.fields.offset(f.as_usize());
601+
let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields {
602+
// Don't ask for field layout for primitives, because that will panic.
603+
if !self.layout.uninhabited {
604+
// Real primitives only have one variant, but weird types like
605+
// `Result<!, !>` turn out to also be "Primitive", and dead code
606+
// like `Err(never)` needs to not ICE.
607+
assert_eq!(v, FIRST_VARIANT);
608+
}
609+
let first_field = f == FieldIdx::ZERO;
610+
(self.layout.is_zst() || !first_field, first_field)
611+
} else {
612+
let variant_layout = self.layout.for_variant(bx.cx(), v);
613+
let field_layout = variant_layout.field(bx.cx(), f.as_usize());
614+
let field_offset = variant_layout.fields.offset(f.as_usize());
615+
(field_layout.is_zst(), field_offset == Size::ZERO)
616+
};
594617

595618
let mut update = |tgt: &mut Result<V, abi::Scalar>, src, from_scalar| {
596619
let from_bty = bx.cx().type_from_scalar(from_scalar);
597620
let to_scalar = tgt.unwrap_err();
598621
let to_bty = bx.cx().type_from_scalar(to_scalar);
599-
let v = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
600-
*tgt = Ok(v);
622+
let imm = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
623+
*tgt = Ok(imm);
601624
};
602625

603626
match (operand.val, operand.layout.backend_repr) {
604-
(OperandValue::ZeroSized, _) => {
605-
debug_assert_eq!(field_layout.size, Size::ZERO);
606-
}
627+
(OperandValue::ZeroSized, _) if expect_zst => {}
607628
(OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val {
608-
OperandValue::Immediate(val @ Err(_)) => {
609-
debug_assert_eq!(field_offset, Size::ZERO);
629+
OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
610630
update(val, v, from_scalar);
611-
//*val = Ok(v);
612631
}
613-
OperandValue::Pair(fst @ Err(_), _) if field_offset == Size::ZERO => {
632+
OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
614633
update(fst, v, from_scalar);
615-
//*fst = Ok(v);
616634
}
617-
OperandValue::Pair(_, snd @ Err(_)) if field_offset != Size::ZERO => {
635+
OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
618636
update(snd, v, from_scalar);
619-
//*snd = Ok(v);
620637
}
621-
_ => bug!("Tried to insert {operand:?} into field {f:?} of {self:?}"),
638+
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
622639
},
623640
(OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => {
624641
match &mut self.val {
625642
OperandValue::Pair(fst @ Err(_), snd @ Err(_)) => {
626643
update(fst, a, from_sa);
627-
//*fst = Ok(a);
628644
update(snd, b, from_sb);
629-
//*snd = Ok(b);
630645
}
631-
_ => bug!("Tried to insert {operand:?} into field {f:?} of {self:?}"),
646+
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
632647
}
633648
}
634-
_ => bug!("Unsupported operand {operand:?} inserting into field {f:?} of {self:?}"),
649+
_ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"),
650+
}
651+
}
652+
653+
pub(super) fn insert_imm(&mut self, f: FieldIdx, imm: V) {
654+
let field_offset = self.layout.fields.offset(f.as_usize());
655+
let is_zero_offset = field_offset == Size::ZERO;
656+
match &mut self.val {
657+
OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
658+
*val = Ok(imm);
659+
}
660+
OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
661+
*fst = Ok(imm);
662+
}
663+
OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
664+
*snd = Ok(imm);
665+
}
666+
_ => bug!("Tried to insert {imm:?} into field {f:?} of {self:?}"),
635667
}
636668
}
637669

638-
pub fn finalize(self) -> OperandRef<'tcx, V> {
639-
let OperandRef { val, layout } = self;
670+
pub fn finalize(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> {
671+
let OperandRef { val, layout } = *self;
672+
673+
let unwrap = |r: Result<V, abi::Scalar>| match r {
674+
Ok(v) => v,
675+
Err(s) if s.is_uninit_valid() => {
676+
let bty = cx.type_from_scalar(s);
677+
cx.const_undef(bty)
678+
}
679+
Err(_) => bug!("OperandRef::finalize called while fields are missing {self:?}"),
680+
};
681+
640682
let val = match val {
641683
OperandValue::ZeroSized => OperandValue::ZeroSized,
642-
OperandValue::Immediate(v) => OperandValue::Immediate(v.unwrap()),
643-
OperandValue::Pair(a, b) => OperandValue::Pair(a.unwrap(), b.unwrap()),
684+
OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)),
685+
OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)),
644686
OperandValue::Ref(_) => bug!(),
645687
};
646688
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.as_usize());
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.as_usize());
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.as_usize());
472+
let tag_llty = cx.immediate_backend_type(tag_layout);
473+
let imm = cx.const_uint_big(tag_llty, to);
474+
Some((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.as_usize());
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((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::*;
@@ -699,17 +699,42 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
699699
}
700700
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
701701
mir::Rvalue::Repeat(..) => bug!("{rvalue:?} in codegen_rvalue_operand"),
702-
mir::Rvalue::Aggregate(_, ref fields) => {
702+
mir::Rvalue::Aggregate(ref kind, ref fields) => {
703+
let (variant_index, active_field_index) = match **kind {
704+
mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
705+
(variant_index, active_field_index)
706+
}
707+
_ => (FIRST_VARIANT, None),
708+
};
709+
703710
let ty = rvalue.ty(self.mir, self.cx.tcx());
704711
let ty = self.monomorphize(ty);
705712
let layout = self.cx.layout_of(ty);
706713

707714
let mut builder = OperandRef::builder(layout);
708715
for (field_idx, field) in fields.iter_enumerated() {
709716
let op = self.codegen_operand(bx, field);
710-
builder.insert_field(bx, field_idx, op);
717+
let fi = active_field_index.unwrap_or(field_idx);
718+
builder.insert_field(bx, variant_index, fi, op);
719+
}
720+
721+
let tag_result = codegen_tagged_field_value(self.cx, variant_index, layout);
722+
match tag_result {
723+
Err(super::place::UninhabitedVariantError) => {
724+
// Like codegen_set_discr we use a sound abort, but could
725+
// potentially `unreachable` or just return the poison for
726+
// more optimizability, if that turns out to be helpful.
727+
bx.abort();
728+
let val = OperandValue::poison(bx, layout);
729+
OperandRef { val, layout }
730+
}
731+
Ok(maybe_tag_value) => {
732+
if let Some((tag_field, tag_imm)) = maybe_tag_value {
733+
builder.insert_imm(tag_field, tag_imm);
734+
}
735+
builder.finalize(bx.cx())
736+
}
711737
}
712-
builder.finalize()
713738
}
714739
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
715740
let operand = self.codegen_operand(bx, operand);
@@ -1043,19 +1068,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10431068
mir::AggregateKind::RawPtr(..) => true,
10441069
mir::AggregateKind::Array(..) => false,
10451070
mir::AggregateKind::Tuple => true,
1046-
mir::AggregateKind::Adt(def_id, ..) => {
1047-
let adt_def = self.cx.tcx().adt_def(def_id);
1048-
adt_def.is_struct() && !adt_def.repr().simd()
1049-
}
1071+
mir::AggregateKind::Adt(..) => true,
10501072
mir::AggregateKind::Closure(..) => true,
10511073
// FIXME: Can we do this for simple coroutines too?
10521074
mir::AggregateKind::Coroutine(..) | mir::AggregateKind::CoroutineClosure(..) => false,
10531075
};
10541076
allowed_kind && {
1055-
let ty = rvalue.ty(self.mir, self.cx.tcx());
1056-
let ty = self.monomorphize(ty);
1077+
let ty = rvalue.ty(self.mir, self.cx.tcx());
1078+
let ty = self.monomorphize(ty);
10571079
let layout = self.cx.spanned_layout_of(ty, span);
1058-
!self.cx.is_backend_ref(layout)
1080+
OperandRef::<Bx::Value>::supports_builder(layout)
10591081
}
10601082
}
10611083
}

tests/codegen/align-struct.rs

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

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

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

0 commit comments

Comments
 (0)