Skip to content

Commit e4f196a

Browse files
committed
CodeGen: rework Aggregate implemention for rvalue_creates_operand cases
Another refactor pulled out from 138759 The previous implementation I'd written here based on `index_by_increasing_offset` is complicated to follow and difficult to extend to non-structs. This changes the implementation, without actually changing any codegen (thus no test changes either), to be more like the existing `extract_field` (<https://github.com/rust-lang/rust/blob/2b0274c71dba0e24370ebf65593da450e2e91868/compiler/rustc_codegen_ssa/src/mir/operand.rs#L345-L425>) in that it allows setting a particular field directly. Notably I've found this one much easier to get right, in particular because having the `OperandRef<Result<V, Scalar>>` gives a really useful thing to include in ICE messages if something did happen to go wrong.
1 parent e703dff commit e4f196a

File tree

5 files changed

+134
-80
lines changed

5 files changed

+134
-80
lines changed

Cargo.lock

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3468,11 +3468,9 @@ name = "rustc_codegen_ssa"
34683468
version = "0.0.0"
34693469
dependencies = [
34703470
"ar_archive_writer",
3471-
"arrayvec",
34723471
"bitflags",
34733472
"bstr",
34743473
"cc",
3475-
"either",
34763474
"itertools",
34773475
"libc",
34783476
"object 0.37.0",

compiler/rustc_codegen_ssa/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ edition = "2024"
66
[dependencies]
77
# tidy-alphabetical-start
88
ar_archive_writer = "0.4.2"
9-
arrayvec = { version = "0.7", default-features = false }
109
bitflags = "2.4.1"
1110
bstr = "1.11.3"
1211
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
1312
# `cc` in `rustc_llvm` if you update the `cc` here.
1413
cc = "=1.2.16"
15-
either = "1.5.0"
1614
itertools = "0.12"
1715
pathdiff = "0.2.0"
1816
regex = "1.4"

compiler/rustc_codegen_ssa/src/mir/operand.rs

Lines changed: 103 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::fmt;
22

3-
use arrayvec::ArrayVec;
4-
use either::Either;
53
use rustc_abi as abi;
6-
use rustc_abi::{Align, BackendRepr, FIRST_VARIANT, Primitive, Size, TagEncoding, Variants};
4+
use rustc_abi::{
5+
Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, VariantIdx, Variants,
6+
};
77
use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range};
88
use rustc_middle::mir::{self, ConstValue};
99
use rustc_middle::ty::Ty;
@@ -13,6 +13,7 @@ use rustc_session::config::OptLevel;
1313
use tracing::{debug, instrument};
1414

1515
use super::place::{PlaceRef, PlaceValue};
16+
use super::rvalue::transmute_immediate;
1617
use super::{FunctionCx, LocalRef};
1718
use crate::common::IntPredicate;
1819
use crate::traits::*;
@@ -69,31 +70,6 @@ pub enum OperandValue<V> {
6970
}
7071

7172
impl<V: CodegenObject> OperandValue<V> {
72-
/// If this is ZeroSized/Immediate/Pair, return an array of the 0/1/2 values.
73-
/// If this is Ref, return the place.
74-
#[inline]
75-
pub(crate) fn immediates_or_place(self) -> Either<ArrayVec<V, 2>, PlaceValue<V>> {
76-
match self {
77-
OperandValue::ZeroSized => Either::Left(ArrayVec::new()),
78-
OperandValue::Immediate(a) => Either::Left(ArrayVec::from_iter([a])),
79-
OperandValue::Pair(a, b) => Either::Left([a, b].into()),
80-
OperandValue::Ref(p) => Either::Right(p),
81-
}
82-
}
83-
84-
/// Given an array of 0/1/2 immediate values, return ZeroSized/Immediate/Pair.
85-
#[inline]
86-
pub(crate) fn from_immediates(immediates: ArrayVec<V, 2>) -> Self {
87-
let mut it = immediates.into_iter();
88-
let Some(a) = it.next() else {
89-
return OperandValue::ZeroSized;
90-
};
91-
let Some(b) = it.next() else {
92-
return OperandValue::Immediate(a);
93-
};
94-
OperandValue::Pair(a, b)
95-
}
96-
9773
/// Treat this value as a pointer and return the data pointer and
9874
/// optional metadata as backend values.
9975
///
@@ -595,6 +571,105 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
595571
}
596572
}
597573
}
574+
575+
/// Creates an incomplete operand containing the [`abi::Scalar`]s expected based
576+
/// on the `layout` passed. This is for use with [`OperandRef::insert_field`]
577+
/// later to set the necessary immediate(s).
578+
///
579+
/// Returns `None` for `layout`s which cannot be built this way.
580+
pub(crate) fn builder(
581+
layout: TyAndLayout<'tcx>,
582+
) -> Option<OperandRef<'tcx, Result<V, abi::Scalar>>> {
583+
let val = match layout.backend_repr {
584+
BackendRepr::Memory { .. } if layout.is_zst() => OperandValue::ZeroSized,
585+
BackendRepr::Scalar(s) => OperandValue::Immediate(Err(s)),
586+
BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Err(a), Err(b)),
587+
BackendRepr::Memory { .. } | BackendRepr::SimdVector { .. } => return None,
588+
};
589+
Some(OperandRef { val, layout })
590+
}
591+
}
592+
593+
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
594+
pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
595+
&mut self,
596+
bx: &mut Bx,
597+
v: VariantIdx,
598+
f: FieldIdx,
599+
operand: OperandRef<'tcx, V>,
600+
) {
601+
let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields {
602+
// The other branch looking at field layouts ICEs for primitives,
603+
// so we need to handle them separately.
604+
// Multiple fields is possible for cases such as aggregating
605+
// a thin pointer, where the second field is the unit.
606+
assert!(!self.layout.is_zst());
607+
assert_eq!(v, FIRST_VARIANT);
608+
let first_field = f == FieldIdx::ZERO;
609+
(!first_field, first_field)
610+
} else {
611+
let variant_layout = self.layout.for_variant(bx.cx(), v);
612+
let field_layout = variant_layout.field(bx.cx(), f.as_usize());
613+
let field_offset = variant_layout.fields.offset(f.as_usize());
614+
(field_layout.is_zst(), field_offset == Size::ZERO)
615+
};
616+
617+
let mut update = |tgt: &mut Result<V, abi::Scalar>, src, from_scalar| {
618+
let from_bty = bx.cx().type_from_scalar(from_scalar);
619+
let to_scalar = tgt.unwrap_err();
620+
let to_bty = bx.cx().type_from_scalar(to_scalar);
621+
let imm = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
622+
*tgt = Ok(imm);
623+
};
624+
625+
match (operand.val, operand.layout.backend_repr) {
626+
(OperandValue::ZeroSized, _) if expect_zst => {}
627+
(OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val {
628+
OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
629+
update(val, v, from_scalar);
630+
}
631+
OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
632+
update(fst, v, from_scalar);
633+
}
634+
OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
635+
update(snd, v, from_scalar);
636+
}
637+
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
638+
},
639+
(OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => {
640+
match &mut self.val {
641+
OperandValue::Pair(fst @ Err(_), snd @ Err(_)) => {
642+
update(fst, a, from_sa);
643+
update(snd, b, from_sb);
644+
}
645+
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
646+
}
647+
}
648+
_ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"),
649+
}
650+
}
651+
652+
/// After having set all necessary fields, this converts the
653+
/// `OperandValue<Result<V, _>>` (as obtained from [`OperandRef::builder`])
654+
/// to the normal `OperandValue<V>`.
655+
///
656+
/// ICEs if any required fields were not set.
657+
pub fn build(&self) -> OperandRef<'tcx, V> {
658+
let OperandRef { val, layout } = *self;
659+
660+
let unwrap = |r: Result<V, abi::Scalar>| match r {
661+
Ok(v) => v,
662+
Err(_) => bug!("OperandRef::build called while fields are missing {self:?}"),
663+
};
664+
665+
let val = match val {
666+
OperandValue::ZeroSized => OperandValue::ZeroSized,
667+
OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)),
668+
OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)),
669+
OperandValue::Ref(_) => bug!(),
670+
};
671+
OperandRef { val, layout }
672+
}
598673
}
599674

600675
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::assert_matches::assert_matches;
22

3-
use arrayvec::ArrayVec;
4-
use rustc_abi::{self as abi, FIRST_VARIANT, FieldIdx};
3+
use rustc_abi::{self as abi, FIRST_VARIANT};
54
use rustc_middle::ty::adjustment::PointerCoercion;
65
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
76
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
@@ -708,38 +707,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
708707

709708
// `rvalue_creates_operand` has arranged that we only get here if
710709
// we can build the aggregate immediate from the field immediates.
711-
let mut inputs = ArrayVec::<Bx::Value, 2>::new();
712-
let mut input_scalars = ArrayVec::<abi::Scalar, 2>::new();
713-
for field_idx in layout.fields.index_by_increasing_offset() {
714-
let field_idx = FieldIdx::from_usize(field_idx);
715-
let op = self.codegen_operand(bx, &fields[field_idx]);
716-
let values = op.val.immediates_or_place().left_or_else(|p| {
717-
bug!("Field {field_idx:?} is {p:?} making {layout:?}");
718-
});
719-
let scalars = self.value_kind(op.layout).scalars().unwrap();
720-
assert_eq!(values.len(), scalars.len());
721-
inputs.extend(values);
722-
input_scalars.extend(scalars);
710+
let Some(mut builder) = OperandRef::builder(layout) else {
711+
bug!("Cannot use type in operand builder: {layout:?}")
712+
};
713+
for (field_idx, field) in fields.iter_enumerated() {
714+
let op = self.codegen_operand(bx, field);
715+
builder.insert_field(bx, FIRST_VARIANT, field_idx, op);
723716
}
724717

725-
let output_scalars = self.value_kind(layout).scalars().unwrap();
726-
itertools::izip!(&mut inputs, input_scalars, output_scalars).for_each(
727-
|(v, in_s, out_s)| {
728-
if in_s != out_s {
729-
// We have to be really careful about bool here, because
730-
// `(bool,)` stays i1 but `Cell<bool>` becomes i8.
731-
*v = bx.from_immediate(*v);
732-
*v = bx.to_immediate_scalar(*v, out_s);
733-
}
734-
},
735-
);
736-
737-
let val = OperandValue::from_immediates(inputs);
738-
assert!(
739-
val.is_expected_variant_for_type(self.cx, layout),
740-
"Made wrong variant {val:?} for type {layout:?}",
741-
);
742-
OperandRef { val, layout }
718+
builder.build()
743719
}
744720
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
745721
let operand = self.codegen_operand(bx, operand);
@@ -1082,10 +1058,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10821058
mir::AggregateKind::Coroutine(..) | mir::AggregateKind::CoroutineClosure(..) => false,
10831059
};
10841060
allowed_kind && {
1085-
let ty = rvalue.ty(self.mir, self.cx.tcx());
1086-
let ty = self.monomorphize(ty);
1061+
let ty = rvalue.ty(self.mir, self.cx.tcx());
1062+
let ty = self.monomorphize(ty);
10871063
let layout = self.cx.spanned_layout_of(ty, span);
1088-
!self.cx.is_backend_ref(layout)
1064+
OperandRef::<Bx::Value>::builder(layout).is_some()
10891065
}
10901066
}
10911067
}
@@ -1129,23 +1105,12 @@ enum OperandValueKind {
11291105
ZeroSized,
11301106
}
11311107

1132-
impl OperandValueKind {
1133-
fn scalars(self) -> Option<ArrayVec<abi::Scalar, 2>> {
1134-
Some(match self {
1135-
OperandValueKind::ZeroSized => ArrayVec::new(),
1136-
OperandValueKind::Immediate(a) => ArrayVec::from_iter([a]),
1137-
OperandValueKind::Pair(a, b) => [a, b].into(),
1138-
OperandValueKind::Ref => return None,
1139-
})
1140-
}
1141-
}
1142-
11431108
/// Transmutes one of the immediates from an [`OperandValue::Immediate`]
11441109
/// or an [`OperandValue::Pair`] to an immediate of the target type.
11451110
///
11461111
/// `to_backend_ty` must be the *non*-immediate backend type (so it will be
11471112
/// `i8`, not `i1`, for `bool`-like types.)
1148-
fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
1113+
pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
11491114
bx: &mut Bx,
11501115
mut imm: Bx::Value,
11511116
from_scalar: abi::Scalar,

compiler/rustc_codegen_ssa/src/traits/type_.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_abi::{AddressSpace, Float, Integer, Reg};
1+
use rustc_abi::{AddressSpace, Float, Integer, Primitive, Reg, Scalar};
22
use rustc_middle::bug;
33
use rustc_middle::ty::Ty;
44
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout};
@@ -84,6 +84,24 @@ pub trait DerivedTypeCodegenMethods<'tcx>:
8484
fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
8585
ty.is_freeze(self.tcx(), self.typing_env())
8686
}
87+
88+
fn type_from_primitive(&self, p: Primitive) -> Self::Type {
89+
use Primitive::*;
90+
match p {
91+
Int(i, _) => self.type_from_integer(i),
92+
Float(f) => self.type_from_float(f),
93+
Pointer(address_space) => self.type_ptr_ext(address_space),
94+
}
95+
}
96+
97+
fn type_from_scalar(&self, s: Scalar) -> Self::Type {
98+
// `MaybeUninit` being `repr(transparent)` somewhat implies that the type
99+
// of a scalar has to be the type of its primitive (which is true in LLVM,
100+
// where noundef is a parameter attribute or metadata) but if we ever get
101+
// a backend where that's no longer true, every use of this will need to
102+
// to carefully scrutinized and re-evaluated.
103+
self.type_from_primitive(s.primitive())
104+
}
87105
}
88106

89107
impl<'tcx, T> DerivedTypeCodegenMethods<'tcx> for T where

0 commit comments

Comments
 (0)