1#[cfg(feature = "nightly")]2use crate::{BackendRepr, FieldsShape, Primitive, Size, TyAbiInterface, TyAndLayout, Variants};34mod reg;56pub use reg::{Reg, RegKind};78/// Return value from the `homogeneous_aggregate` test function.9#[derive(Copy, Clone, Debug)]10pub enum HomogeneousAggregate {11 /// Yes, all the "leaf fields" of this struct are passed in the12 /// same way (specified in the `Reg` value).13 Homogeneous(Reg),1415 /// There are no leaf fields at all.16 NoData,17}1819/// Error from the `homogeneous_aggregate` test function, indicating20/// there are distinct leaf fields passed in different ways,21/// or this is uninhabited.22#[derive(Copy, Clone, Debug)]23pub struct Heterogeneous;2425impl HomogeneousAggregate {26 /// If this is a homogeneous aggregate, returns the homogeneous27 /// unit, else `None`.28 pub fn unit(self) -> Option<Reg> {29 match self {30 HomogeneousAggregate::Homogeneous(reg) => Some(reg),31 HomogeneousAggregate::NoData => None,32 }33 }3435 /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in36 /// the same `struct`. Only succeeds if only one of them has any data,37 /// or both units are identical.38 #[cfg(feature = "nightly")]39 fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {40 match (self, other) {41 (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),4243 (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {44 if a != b {45 return Err(Heterogeneous);46 }47 Ok(self)48 }49 }50 }51}5253#[cfg(feature = "nightly")]54impl<'a, Ty> TyAndLayout<'a, Ty> {55 /// Returns `Homogeneous` if this layout is an aggregate containing fields of56 /// only a single type (e.g., `(u32, u32)`). Such aggregates are often57 /// special-cased in ABIs.58 ///59 /// Note: We generally ignore 1-ZST fields when computing this value (see #56877).60 ///61 /// This is public so that it can be used in unit tests, but62 /// should generally only be relevant to the ABI details of63 /// specific targets.64 #[tracing::instrument(skip(cx), level = "debug")]65 pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>66 where67 Ty: TyAbiInterface<'a, C> + Copy,68 {69 match self.backend_repr {70 // The primitive for this algorithm.71 BackendRepr::Scalar(scalar) => {72 let kind = match scalar.primitive() {73 Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer,74 Primitive::Float(_) => RegKind::Float,75 };76 Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))77 }7879 BackendRepr::SimdVector { element, count: _ } => {80 assert!(!self.is_zst());8182 Ok(HomogeneousAggregate::Homogeneous(Reg {83 kind: RegKind::Vector { hint_vector_elem: element.primitive() },84 size: self.size,85 }))86 }8788 BackendRepr::SimdScalableVector { .. } => {89 unreachable!("`homogeneous_aggregate` should not be called for scalable vectors")90 }9192 BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => {93 // Helper for computing `homogeneous_aggregate`, allowing a custom94 // starting offset (used below for handling variants).95 let from_fields_at =96 |layout: Self,97 start: Size|98 -> Result<(HomogeneousAggregate, Size), Heterogeneous> {99 let is_union = match layout.fields {100 FieldsShape::Primitive => {101 unreachable!("aggregates can't have `FieldsShape::Primitive`")102 }103 FieldsShape::Array { count, .. } => {104 assert_eq!(start, Size::ZERO);105106 let result = if count > 0 {107 layout.field(cx, 0).homogeneous_aggregate(cx)?108 } else {109 HomogeneousAggregate::NoData110 };111 return Ok((result, layout.size));112 }113 FieldsShape::Union(_) => true,114 FieldsShape::Arbitrary { .. } => false,115 };116117 let mut result = HomogeneousAggregate::NoData;118 let mut total = start;119120 for i in 0..layout.fields.count() {121 let field = layout.field(cx, i);122 if field.is_1zst() {123 // No data here and no impact on layout, can be ignored.124 // (We might be able to also ignore all aligned ZST but that's less clear.)125 continue;126 }127128 if !is_union && total != layout.fields.offset(i) {129 // This field isn't just after the previous one we considered, abort.130 return Err(Heterogeneous);131 }132133 result = result.merge(field.homogeneous_aggregate(cx)?)?;134135 // Keep track of the offset (without padding).136 let size = field.size;137 if is_union {138 total = total.max(size);139 } else {140 total += size;141 }142 }143144 Ok((result, total))145 };146147 let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;148149 match &self.variants {150 Variants::Single { .. } | Variants::Empty => {}151 Variants::Multiple { variants, .. } => {152 // Treat enum variants like union members.153 // HACK(eddyb) pretend the `enum` field (discriminant)154 // is at the start of every variant (otherwise the gap155 // at the start of all variants would disqualify them).156 //157 // NB: for all tagged `enum`s (which include all non-C-like158 // `enum`s with defined FFI representation), this will159 // match the homogeneous computation on the equivalent160 // `struct { tag; union { variant1; ... } }` and/or161 // `union { struct { tag; variant1; } ... }`162 // (the offsets of variant fields should be identical163 // between the two for either to be a homogeneous aggregate).164 let variant_start = total;165 for variant_idx in variants.indices() {166 let (variant_result, variant_total) =167 from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;168169 result = result.merge(variant_result)?;170 total = total.max(variant_total);171 }172 }173 }174175 // There needs to be no padding.176 if total != self.size {177 Err(Heterogeneous)178 } else {179 match result {180 HomogeneousAggregate::Homogeneous(_) => {181 assert_ne!(total, Size::ZERO);182 }183 HomogeneousAggregate::NoData => {184 assert_eq!(total, Size::ZERO);185 }186 }187 Ok(result)188 }189 }190 BackendRepr::Memory { sized: false } => Err(Heterogeneous),191 }192 }193}
Findings
✓ No findings reported for this file.