mirror of
https://github.com/rust-lang/rust.git
synced 2025-11-11 04:51:46 +00:00
187 lines
7.8 KiB
Rust
187 lines
7.8 KiB
Rust
#[cfg(feature = "nightly")]
|
|
use crate::{BackendRepr, FieldsShape, Primitive, Size, TyAbiInterface, TyAndLayout, Variants};
|
|
|
|
mod reg;
|
|
|
|
pub use reg::{Reg, RegKind};
|
|
|
|
/// Return value from the `homogeneous_aggregate` test function.
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub enum HomogeneousAggregate {
|
|
/// Yes, all the "leaf fields" of this struct are passed in the
|
|
/// same way (specified in the `Reg` value).
|
|
Homogeneous(Reg),
|
|
|
|
/// There are no leaf fields at all.
|
|
NoData,
|
|
}
|
|
|
|
/// Error from the `homogeneous_aggregate` test function, indicating
|
|
/// there are distinct leaf fields passed in different ways,
|
|
/// or this is uninhabited.
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct Heterogeneous;
|
|
|
|
impl HomogeneousAggregate {
|
|
/// If this is a homogeneous aggregate, returns the homogeneous
|
|
/// unit, else `None`.
|
|
pub fn unit(self) -> Option<Reg> {
|
|
match self {
|
|
HomogeneousAggregate::Homogeneous(reg) => Some(reg),
|
|
HomogeneousAggregate::NoData => None,
|
|
}
|
|
}
|
|
|
|
/// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
|
|
/// the same `struct`. Only succeeds if only one of them has any data,
|
|
/// or both units are identical.
|
|
fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {
|
|
match (self, other) {
|
|
(x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),
|
|
|
|
(HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {
|
|
if a != b {
|
|
return Err(Heterogeneous);
|
|
}
|
|
Ok(self)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "nightly")]
|
|
impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|
/// Returns `Homogeneous` if this layout is an aggregate containing fields of
|
|
/// only a single type (e.g., `(u32, u32)`). Such aggregates are often
|
|
/// special-cased in ABIs.
|
|
///
|
|
/// Note: We generally ignore 1-ZST fields when computing this value (see #56877).
|
|
///
|
|
/// This is public so that it can be used in unit tests, but
|
|
/// should generally only be relevant to the ABI details of
|
|
/// specific targets.
|
|
pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
|
|
where
|
|
Ty: TyAbiInterface<'a, C> + Copy,
|
|
{
|
|
match self.backend_repr {
|
|
// The primitive for this algorithm.
|
|
BackendRepr::Scalar(scalar) => {
|
|
let kind = match scalar.primitive() {
|
|
Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer,
|
|
Primitive::Float(_) => RegKind::Float,
|
|
};
|
|
Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
|
|
}
|
|
|
|
BackendRepr::SimdVector { .. } => {
|
|
assert!(!self.is_zst());
|
|
Ok(HomogeneousAggregate::Homogeneous(Reg {
|
|
kind: RegKind::Vector,
|
|
size: self.size,
|
|
}))
|
|
}
|
|
|
|
BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => {
|
|
// Helper for computing `homogeneous_aggregate`, allowing a custom
|
|
// starting offset (used below for handling variants).
|
|
let from_fields_at =
|
|
|layout: Self,
|
|
start: Size|
|
|
-> Result<(HomogeneousAggregate, Size), Heterogeneous> {
|
|
let is_union = match layout.fields {
|
|
FieldsShape::Primitive => {
|
|
unreachable!("aggregates can't have `FieldsShape::Primitive`")
|
|
}
|
|
FieldsShape::Array { count, .. } => {
|
|
assert_eq!(start, Size::ZERO);
|
|
|
|
let result = if count > 0 {
|
|
layout.field(cx, 0).homogeneous_aggregate(cx)?
|
|
} else {
|
|
HomogeneousAggregate::NoData
|
|
};
|
|
return Ok((result, layout.size));
|
|
}
|
|
FieldsShape::Union(_) => true,
|
|
FieldsShape::Arbitrary { .. } => false,
|
|
};
|
|
|
|
let mut result = HomogeneousAggregate::NoData;
|
|
let mut total = start;
|
|
|
|
for i in 0..layout.fields.count() {
|
|
let field = layout.field(cx, i);
|
|
if field.is_1zst() {
|
|
// No data here and no impact on layout, can be ignored.
|
|
// (We might be able to also ignore all aligned ZST but that's less clear.)
|
|
continue;
|
|
}
|
|
|
|
if !is_union && total != layout.fields.offset(i) {
|
|
// This field isn't just after the previous one we considered, abort.
|
|
return Err(Heterogeneous);
|
|
}
|
|
|
|
result = result.merge(field.homogeneous_aggregate(cx)?)?;
|
|
|
|
// Keep track of the offset (without padding).
|
|
let size = field.size;
|
|
if is_union {
|
|
total = total.max(size);
|
|
} else {
|
|
total += size;
|
|
}
|
|
}
|
|
|
|
Ok((result, total))
|
|
};
|
|
|
|
let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
|
|
|
|
match &self.variants {
|
|
Variants::Single { .. } | Variants::Empty => {}
|
|
Variants::Multiple { variants, .. } => {
|
|
// Treat enum variants like union members.
|
|
// HACK(eddyb) pretend the `enum` field (discriminant)
|
|
// is at the start of every variant (otherwise the gap
|
|
// at the start of all variants would disqualify them).
|
|
//
|
|
// NB: for all tagged `enum`s (which include all non-C-like
|
|
// `enum`s with defined FFI representation), this will
|
|
// match the homogeneous computation on the equivalent
|
|
// `struct { tag; union { variant1; ... } }` and/or
|
|
// `union { struct { tag; variant1; } ... }`
|
|
// (the offsets of variant fields should be identical
|
|
// between the two for either to be a homogeneous aggregate).
|
|
let variant_start = total;
|
|
for variant_idx in variants.indices() {
|
|
let (variant_result, variant_total) =
|
|
from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
|
|
|
|
result = result.merge(variant_result)?;
|
|
total = total.max(variant_total);
|
|
}
|
|
}
|
|
}
|
|
|
|
// There needs to be no padding.
|
|
if total != self.size {
|
|
Err(Heterogeneous)
|
|
} else {
|
|
match result {
|
|
HomogeneousAggregate::Homogeneous(_) => {
|
|
assert_ne!(total, Size::ZERO);
|
|
}
|
|
HomogeneousAggregate::NoData => {
|
|
assert_eq!(total, Size::ZERO);
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
}
|
|
BackendRepr::Memory { sized: false } => Err(Heterogeneous),
|
|
}
|
|
}
|
|
}
|