| #[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. | |
| #[tracing::instrument(skip(cx), level = "debug")] | |
| 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::SimdScalableVector { .. } => { | |
| unreachable!("`homogeneous_aggregate` should not be called for scalable vectors") | |
| } | |
| 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), | |
| } | |
| } | |
| } |