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` (<2b0274c71d/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.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
use std::fmt;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use either::Either;
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::{Align, BackendRepr, FIRST_VARIANT, Primitive, Size, TagEncoding, Variants};
|
||||
use rustc_abi::{
|
||||
Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, VariantIdx, Variants,
|
||||
};
|
||||
use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range};
|
||||
use rustc_middle::mir::{self, ConstValue};
|
||||
use rustc_middle::ty::Ty;
|
||||
@@ -13,6 +13,7 @@ use rustc_session::config::OptLevel;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::place::{PlaceRef, PlaceValue};
|
||||
use super::rvalue::transmute_immediate;
|
||||
use super::{FunctionCx, LocalRef};
|
||||
use crate::common::IntPredicate;
|
||||
use crate::traits::*;
|
||||
@@ -69,31 +70,6 @@ pub enum OperandValue<V> {
|
||||
}
|
||||
|
||||
impl<V: CodegenObject> OperandValue<V> {
|
||||
/// If this is ZeroSized/Immediate/Pair, return an array of the 0/1/2 values.
|
||||
/// If this is Ref, return the place.
|
||||
#[inline]
|
||||
pub(crate) fn immediates_or_place(self) -> Either<ArrayVec<V, 2>, PlaceValue<V>> {
|
||||
match self {
|
||||
OperandValue::ZeroSized => Either::Left(ArrayVec::new()),
|
||||
OperandValue::Immediate(a) => Either::Left(ArrayVec::from_iter([a])),
|
||||
OperandValue::Pair(a, b) => Either::Left([a, b].into()),
|
||||
OperandValue::Ref(p) => Either::Right(p),
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an array of 0/1/2 immediate values, return ZeroSized/Immediate/Pair.
|
||||
#[inline]
|
||||
pub(crate) fn from_immediates(immediates: ArrayVec<V, 2>) -> Self {
|
||||
let mut it = immediates.into_iter();
|
||||
let Some(a) = it.next() else {
|
||||
return OperandValue::ZeroSized;
|
||||
};
|
||||
let Some(b) = it.next() else {
|
||||
return OperandValue::Immediate(a);
|
||||
};
|
||||
OperandValue::Pair(a, b)
|
||||
}
|
||||
|
||||
/// Treat this value as a pointer and return the data pointer and
|
||||
/// optional metadata as backend values.
|
||||
///
|
||||
@@ -595,6 +571,105 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an incomplete operand containing the [`abi::Scalar`]s expected based
|
||||
/// on the `layout` passed. This is for use with [`OperandRef::insert_field`]
|
||||
/// later to set the necessary immediate(s).
|
||||
///
|
||||
/// Returns `None` for `layout`s which cannot be built this way.
|
||||
pub(crate) fn builder(
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> Option<OperandRef<'tcx, Result<V, abi::Scalar>>> {
|
||||
let val = match layout.backend_repr {
|
||||
BackendRepr::Memory { .. } if layout.is_zst() => OperandValue::ZeroSized,
|
||||
BackendRepr::Scalar(s) => OperandValue::Immediate(Err(s)),
|
||||
BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Err(a), Err(b)),
|
||||
BackendRepr::Memory { .. } | BackendRepr::SimdVector { .. } => return None,
|
||||
};
|
||||
Some(OperandRef { val, layout })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> {
|
||||
pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||
&mut self,
|
||||
bx: &mut Bx,
|
||||
v: VariantIdx,
|
||||
f: FieldIdx,
|
||||
operand: OperandRef<'tcx, V>,
|
||||
) {
|
||||
let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields {
|
||||
// The other branch looking at field layouts ICEs for primitives,
|
||||
// so we need to handle them separately.
|
||||
// Multiple fields is possible for cases such as aggregating
|
||||
// a thin pointer, where the second field is the unit.
|
||||
assert!(!self.layout.is_zst());
|
||||
assert_eq!(v, FIRST_VARIANT);
|
||||
let first_field = f == FieldIdx::ZERO;
|
||||
(!first_field, first_field)
|
||||
} else {
|
||||
let variant_layout = self.layout.for_variant(bx.cx(), v);
|
||||
let field_layout = variant_layout.field(bx.cx(), f.as_usize());
|
||||
let field_offset = variant_layout.fields.offset(f.as_usize());
|
||||
(field_layout.is_zst(), field_offset == Size::ZERO)
|
||||
};
|
||||
|
||||
let mut update = |tgt: &mut Result<V, abi::Scalar>, src, from_scalar| {
|
||||
let from_bty = bx.cx().type_from_scalar(from_scalar);
|
||||
let to_scalar = tgt.unwrap_err();
|
||||
let to_bty = bx.cx().type_from_scalar(to_scalar);
|
||||
let imm = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty);
|
||||
*tgt = Ok(imm);
|
||||
};
|
||||
|
||||
match (operand.val, operand.layout.backend_repr) {
|
||||
(OperandValue::ZeroSized, _) if expect_zst => {}
|
||||
(OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val {
|
||||
OperandValue::Immediate(val @ Err(_)) if is_zero_offset => {
|
||||
update(val, v, from_scalar);
|
||||
}
|
||||
OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => {
|
||||
update(fst, v, from_scalar);
|
||||
}
|
||||
OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => {
|
||||
update(snd, v, from_scalar);
|
||||
}
|
||||
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
|
||||
},
|
||||
(OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => {
|
||||
match &mut self.val {
|
||||
OperandValue::Pair(fst @ Err(_), snd @ Err(_)) => {
|
||||
update(fst, a, from_sa);
|
||||
update(snd, b, from_sb);
|
||||
}
|
||||
_ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"),
|
||||
}
|
||||
}
|
||||
_ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// After having set all necessary fields, this converts the
|
||||
/// `OperandValue<Result<V, _>>` (as obtained from [`OperandRef::builder`])
|
||||
/// to the normal `OperandValue<V>`.
|
||||
///
|
||||
/// ICEs if any required fields were not set.
|
||||
pub fn build(&self) -> OperandRef<'tcx, V> {
|
||||
let OperandRef { val, layout } = *self;
|
||||
|
||||
let unwrap = |r: Result<V, abi::Scalar>| match r {
|
||||
Ok(v) => v,
|
||||
Err(_) => bug!("OperandRef::build called while fields are missing {self:?}"),
|
||||
};
|
||||
|
||||
let val = match val {
|
||||
OperandValue::ZeroSized => OperandValue::ZeroSized,
|
||||
OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)),
|
||||
OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)),
|
||||
OperandValue::Ref(_) => bug!(),
|
||||
};
|
||||
OperandRef { val, layout }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
|
||||
|
||||
Reference in New Issue
Block a user