Skip to content

Commit b5da991

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 b5da991

File tree

5 files changed

+142
-80
lines changed

5 files changed

+142
-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: 113 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,115 @@ 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+
/// ICEs on types for which [`OperandRef::supports_builder`] returns false.
580+
pub(crate) fn builder(layout: TyAndLayout<'tcx>) -> OperandRef<'tcx, Result<V, abi::Scalar>> {
581+
let val = match layout.backend_repr {
582+
BackendRepr::Memory { .. } if layout.is_zst() => OperandValue::ZeroSized,
583+
BackendRepr::Scalar(s) => OperandValue::Immediate(Err(s)),
584+
BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Err(a), Err(b)),
585+
_ => bug!("Cannot use type in operand builder: {layout:?}"),
586+
};
587+
OperandRef { val, layout }
588+
}
589+
590+
/// Determines whether [`OperandRef::builder`] is supported for this `layout`.
591+
///
592+
/// Only needed by [`FunctionCx::rvalue_creates_operand`], but here so the
593+
/// two `match`es are proximate in implementation.
594+
pub(crate) fn supports_builder(layout: TyAndLayout<'tcx>) -> bool {
595+
match layout.backend_repr {
596+
BackendRepr::Memory { .. } if layout.is_zst() => true,
597+
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _) => true,
598+
BackendRepr::Memory { .. } | BackendRepr::SimdVector { .. } => false,
599+
}
600+
}
601+
}
602+
603+
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
604+
pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
605+
&mut self,
606+
bx: &mut Bx,
607+
v: VariantIdx,
608+
f: FieldIdx,
609+
operand: OperandRef<'tcx, V>,
610+
) {
611+
let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields {
612+
// The other branch looking at field layouts ICEs for primitives,
613+
// so we need to handle them separately.
614+
// Multiple fields is possible for cases such as aggregating
615+
// a thin pointer, where the second field is the unit.
616+
assert!(!self.layout.is_zst());
617+
assert_eq!(v, FIRST_VARIANT);
618+
let first_field = f == FieldIdx::ZERO;
619+
(!first_field, first_field)
620+
} else {
621+
let variant_layout = self.layout.for_variant(bx.cx(), v);
622+
let field_layout = variant_layout.field(bx.cx(), f.as_usize());
623+
let field_offset = variant_layout.fields.offset(f.as_usize());
624+
(field_layout.is_zst(), field_offset == Size::ZERO)
625+
};
626+
627+
let mut update = |tgt: &mut Result<V, abi::Scalar>, src, from_scalar| {
628+
let from_bty = bx.cx().type_from_scalar(from_scalar);
629+
let to_scalar = tgt.unwrap_err();
630+
let to_bty = bx.cx().type_from_scalar(to_scalar);
631+
let imm = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
632+
*tgt = Ok(imm);
633+
};
634+
635+
match (operand.val, operand.layout.backend_repr) {
636+
(OperandValue::ZeroSized, _) if expect_zst => {}
637+
(OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val {
638+
OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
639+
update(val, v, from_scalar);
640+
}
641+
OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
642+
update(fst, v, from_scalar);
643+
}
644+
OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
645+
update(snd, v, from_scalar);
646+
}
647+
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
648+
},
649+
(OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => {
650+
match &mut self.val {
651+
OperandValue::Pair(fst @ Err(_), snd @ Err(_)) => {
652+
update(fst, a, from_sa);
653+
update(snd, b, from_sb);
654+
}
655+
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
656+
}
657+
}
658+
_ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"),
659+
}
660+
}
661+
662+
/// After having set all necessary fields, this converts the
663+
/// `OperandValue<Result<V, _>>` (as obtained from [`OperandRef::builder`])
664+
/// to the normal `OperandValue<V>`.
665+
///
666+
/// ICEs if any required fields were not set.
667+
pub fn build(&self) -> OperandRef<'tcx, V> {
668+
let OperandRef { val, layout } = *self;
669+
670+
let unwrap = |r: Result<V, abi::Scalar>| match r {
671+
Ok(v) => v,
672+
Err(_) => bug!("OperandRef::build called while fields are missing {self:?}"),
673+
};
674+
675+
let val = match val {
676+
OperandValue::ZeroSized => OperandValue::ZeroSized,
677+
OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)),
678+
OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)),
679+
OperandValue::Ref(_) => bug!(),
680+
};
681+
OperandRef { val, layout }
682+
}
598683
}
599684

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

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 10 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,13 @@ 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 mut builder = OperandRef::builder(layout);
711+
for (field_idx, field) in fields.iter_enumerated() {
712+
let op = self.codegen_operand(bx, field);
713+
builder.insert_field(bx, FIRST_VARIANT, field_idx, op);
723714
}
724715

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 }
716+
builder.build()
743717
}
744718
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
745719
let operand = self.codegen_operand(bx, operand);
@@ -1082,10 +1056,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10821056
mir::AggregateKind::Coroutine(..) | mir::AggregateKind::CoroutineClosure(..) => false,
10831057
};
10841058
allowed_kind && {
1085-
let ty = rvalue.ty(self.mir, self.cx.tcx());
1086-
let ty = self.monomorphize(ty);
1059+
let ty = rvalue.ty(self.mir, self.cx.tcx());
1060+
let ty = self.monomorphize(ty);
10871061
let layout = self.cx.spanned_layout_of(ty, span);
1088-
!self.cx.is_backend_ref(layout)
1062+
OperandRef::<Bx::Value>::supports_builder(layout)
10891063
}
10901064
}
10911065
}
@@ -1129,23 +1103,12 @@ enum OperandValueKind {
11291103
ZeroSized,
11301104
}
11311105

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-
11431106
/// Transmutes one of the immediates from an [`OperandValue::Immediate`]
11441107
/// or an [`OperandValue::Pair`] to an immediate of the target type.
11451108
///
11461109
/// `to_backend_ty` must be the *non*-immediate backend type (so it will be
11471110
/// `i8`, not `i1`, for `bool`-like types.)
1148-
fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
1111+
pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
11491112
bx: &mut Bx,
11501113
mut imm: Bx::Value,
11511114
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)