|  | 
|  | 1 | +mod abi { | 
|  | 2 | +    pub(crate) use crate::Primitive::*; | 
|  | 3 | +    pub(crate) use crate::Variants; | 
|  | 4 | +} | 
|  | 5 | + | 
|  | 6 | +use rustc_macros::HashStable_Generic; | 
|  | 7 | + | 
|  | 8 | +use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; | 
|  | 9 | + | 
|  | 10 | +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] | 
|  | 11 | +pub enum RegKind { | 
|  | 12 | +    Integer, | 
|  | 13 | +    Float, | 
|  | 14 | +    Vector, | 
|  | 15 | +} | 
|  | 16 | + | 
|  | 17 | +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] | 
|  | 18 | +pub struct Reg { | 
|  | 19 | +    pub kind: RegKind, | 
|  | 20 | +    pub size: Size, | 
|  | 21 | +} | 
|  | 22 | + | 
|  | 23 | +macro_rules! reg_ctor { | 
|  | 24 | +    ($name:ident, $kind:ident, $bits:expr) => { | 
|  | 25 | +        pub fn $name() -> Reg { | 
|  | 26 | +            Reg { kind: RegKind::$kind, size: Size::from_bits($bits) } | 
|  | 27 | +        } | 
|  | 28 | +    }; | 
|  | 29 | +} | 
|  | 30 | + | 
|  | 31 | +impl Reg { | 
|  | 32 | +    reg_ctor!(i8, Integer, 8); | 
|  | 33 | +    reg_ctor!(i16, Integer, 16); | 
|  | 34 | +    reg_ctor!(i32, Integer, 32); | 
|  | 35 | +    reg_ctor!(i64, Integer, 64); | 
|  | 36 | +    reg_ctor!(i128, Integer, 128); | 
|  | 37 | + | 
|  | 38 | +    reg_ctor!(f32, Float, 32); | 
|  | 39 | +    reg_ctor!(f64, Float, 64); | 
|  | 40 | +} | 
|  | 41 | + | 
|  | 42 | +impl Reg { | 
|  | 43 | +    pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { | 
|  | 44 | +        let dl = cx.data_layout(); | 
|  | 45 | +        match self.kind { | 
|  | 46 | +            RegKind::Integer => match self.size.bits() { | 
|  | 47 | +                1 => dl.i1_align.abi, | 
|  | 48 | +                2..=8 => dl.i8_align.abi, | 
|  | 49 | +                9..=16 => dl.i16_align.abi, | 
|  | 50 | +                17..=32 => dl.i32_align.abi, | 
|  | 51 | +                33..=64 => dl.i64_align.abi, | 
|  | 52 | +                65..=128 => dl.i128_align.abi, | 
|  | 53 | +                _ => panic!("unsupported integer: {self:?}"), | 
|  | 54 | +            }, | 
|  | 55 | +            RegKind::Float => match self.size.bits() { | 
|  | 56 | +                16 => dl.f16_align.abi, | 
|  | 57 | +                32 => dl.f32_align.abi, | 
|  | 58 | +                64 => dl.f64_align.abi, | 
|  | 59 | +                128 => dl.f128_align.abi, | 
|  | 60 | +                _ => panic!("unsupported float: {self:?}"), | 
|  | 61 | +            }, | 
|  | 62 | +            RegKind::Vector => dl.vector_align(self.size).abi, | 
|  | 63 | +        } | 
|  | 64 | +    } | 
|  | 65 | +} | 
|  | 66 | + | 
|  | 67 | +/// Return value from the `homogeneous_aggregate` test function. | 
|  | 68 | +#[derive(Copy, Clone, Debug)] | 
|  | 69 | +pub enum HomogeneousAggregate { | 
|  | 70 | +    /// Yes, all the "leaf fields" of this struct are passed in the | 
|  | 71 | +    /// same way (specified in the `Reg` value). | 
|  | 72 | +    Homogeneous(Reg), | 
|  | 73 | + | 
|  | 74 | +    /// There are no leaf fields at all. | 
|  | 75 | +    NoData, | 
|  | 76 | +} | 
|  | 77 | + | 
|  | 78 | +/// Error from the `homogeneous_aggregate` test function, indicating | 
|  | 79 | +/// there are distinct leaf fields passed in different ways, | 
|  | 80 | +/// or this is uninhabited. | 
|  | 81 | +#[derive(Copy, Clone, Debug)] | 
|  | 82 | +pub struct Heterogeneous; | 
|  | 83 | + | 
|  | 84 | +impl HomogeneousAggregate { | 
|  | 85 | +    /// If this is a homogeneous aggregate, returns the homogeneous | 
|  | 86 | +    /// unit, else `None`. | 
|  | 87 | +    pub fn unit(self) -> Option<Reg> { | 
|  | 88 | +        match self { | 
|  | 89 | +            HomogeneousAggregate::Homogeneous(reg) => Some(reg), | 
|  | 90 | +            HomogeneousAggregate::NoData => None, | 
|  | 91 | +        } | 
|  | 92 | +    } | 
|  | 93 | + | 
|  | 94 | +    /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in | 
|  | 95 | +    /// the same `struct`. Only succeeds if only one of them has any data, | 
|  | 96 | +    /// or both units are identical. | 
|  | 97 | +    fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> { | 
|  | 98 | +        match (self, other) { | 
|  | 99 | +            (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x), | 
|  | 100 | + | 
|  | 101 | +            (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => { | 
|  | 102 | +                if a != b { | 
|  | 103 | +                    return Err(Heterogeneous); | 
|  | 104 | +                } | 
|  | 105 | +                Ok(self) | 
|  | 106 | +            } | 
|  | 107 | +        } | 
|  | 108 | +    } | 
|  | 109 | +} | 
|  | 110 | + | 
|  | 111 | +impl<'a, Ty> TyAndLayout<'a, Ty> { | 
|  | 112 | +    /// Returns `true` if this is an aggregate type (including a ScalarPair!) | 
|  | 113 | +    pub fn is_aggregate(&self) -> bool { | 
|  | 114 | +        match self.abi { | 
|  | 115 | +            Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, | 
|  | 116 | +            Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, | 
|  | 117 | +        } | 
|  | 118 | +    } | 
|  | 119 | + | 
|  | 120 | +    /// Returns `Homogeneous` if this layout is an aggregate containing fields of | 
|  | 121 | +    /// only a single type (e.g., `(u32, u32)`). Such aggregates are often | 
|  | 122 | +    /// special-cased in ABIs. | 
|  | 123 | +    /// | 
|  | 124 | +    /// Note: We generally ignore 1-ZST fields when computing this value (see #56877). | 
|  | 125 | +    /// | 
|  | 126 | +    /// This is public so that it can be used in unit tests, but | 
|  | 127 | +    /// should generally only be relevant to the ABI details of | 
|  | 128 | +    /// specific targets. | 
|  | 129 | +    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous> | 
|  | 130 | +    where | 
|  | 131 | +        Ty: TyAbiInterface<'a, C> + Copy, | 
|  | 132 | +    { | 
|  | 133 | +        match self.abi { | 
|  | 134 | +            Abi::Uninhabited => Err(Heterogeneous), | 
|  | 135 | + | 
|  | 136 | +            // The primitive for this algorithm. | 
|  | 137 | +            Abi::Scalar(scalar) => { | 
|  | 138 | +                let kind = match scalar.primitive() { | 
|  | 139 | +                    abi::Int(..) | abi::Pointer(_) => RegKind::Integer, | 
|  | 140 | +                    abi::Float(_) => RegKind::Float, | 
|  | 141 | +                }; | 
|  | 142 | +                Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })) | 
|  | 143 | +            } | 
|  | 144 | + | 
|  | 145 | +            Abi::Vector { .. } => { | 
|  | 146 | +                assert!(!self.is_zst()); | 
|  | 147 | +                Ok(HomogeneousAggregate::Homogeneous(Reg { | 
|  | 148 | +                    kind: RegKind::Vector, | 
|  | 149 | +                    size: self.size, | 
|  | 150 | +                })) | 
|  | 151 | +            } | 
|  | 152 | + | 
|  | 153 | +            Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => { | 
|  | 154 | +                // Helper for computing `homogeneous_aggregate`, allowing a custom | 
|  | 155 | +                // starting offset (used below for handling variants). | 
|  | 156 | +                let from_fields_at = | 
|  | 157 | +                    |layout: Self, | 
|  | 158 | +                     start: Size| | 
|  | 159 | +                     -> Result<(HomogeneousAggregate, Size), Heterogeneous> { | 
|  | 160 | +                        let is_union = match layout.fields { | 
|  | 161 | +                            FieldsShape::Primitive => { | 
|  | 162 | +                                unreachable!("aggregates can't have `FieldsShape::Primitive`") | 
|  | 163 | +                            } | 
|  | 164 | +                            FieldsShape::Array { count, .. } => { | 
|  | 165 | +                                assert_eq!(start, Size::ZERO); | 
|  | 166 | + | 
|  | 167 | +                                let result = if count > 0 { | 
|  | 168 | +                                    layout.field(cx, 0).homogeneous_aggregate(cx)? | 
|  | 169 | +                                } else { | 
|  | 170 | +                                    HomogeneousAggregate::NoData | 
|  | 171 | +                                }; | 
|  | 172 | +                                return Ok((result, layout.size)); | 
|  | 173 | +                            } | 
|  | 174 | +                            FieldsShape::Union(_) => true, | 
|  | 175 | +                            FieldsShape::Arbitrary { .. } => false, | 
|  | 176 | +                        }; | 
|  | 177 | + | 
|  | 178 | +                        let mut result = HomogeneousAggregate::NoData; | 
|  | 179 | +                        let mut total = start; | 
|  | 180 | + | 
|  | 181 | +                        for i in 0..layout.fields.count() { | 
|  | 182 | +                            let field = layout.field(cx, i); | 
|  | 183 | +                            if field.is_1zst() { | 
|  | 184 | +                                // No data here and no impact on layout, can be ignored. | 
|  | 185 | +                                // (We might be able to also ignore all aligned ZST but that's less clear.) | 
|  | 186 | +                                continue; | 
|  | 187 | +                            } | 
|  | 188 | + | 
|  | 189 | +                            if !is_union && total != layout.fields.offset(i) { | 
|  | 190 | +                                // This field isn't just after the previous one we considered, abort. | 
|  | 191 | +                                return Err(Heterogeneous); | 
|  | 192 | +                            } | 
|  | 193 | + | 
|  | 194 | +                            result = result.merge(field.homogeneous_aggregate(cx)?)?; | 
|  | 195 | + | 
|  | 196 | +                            // Keep track of the offset (without padding). | 
|  | 197 | +                            let size = field.size; | 
|  | 198 | +                            if is_union { | 
|  | 199 | +                                total = total.max(size); | 
|  | 200 | +                            } else { | 
|  | 201 | +                                total += size; | 
|  | 202 | +                            } | 
|  | 203 | +                        } | 
|  | 204 | + | 
|  | 205 | +                        Ok((result, total)) | 
|  | 206 | +                    }; | 
|  | 207 | + | 
|  | 208 | +                let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; | 
|  | 209 | + | 
|  | 210 | +                match &self.variants { | 
|  | 211 | +                    abi::Variants::Single { .. } => {} | 
|  | 212 | +                    abi::Variants::Multiple { variants, .. } => { | 
|  | 213 | +                        // Treat enum variants like union members. | 
|  | 214 | +                        // HACK(eddyb) pretend the `enum` field (discriminant) | 
|  | 215 | +                        // is at the start of every variant (otherwise the gap | 
|  | 216 | +                        // at the start of all variants would disqualify them). | 
|  | 217 | +                        // | 
|  | 218 | +                        // NB: for all tagged `enum`s (which include all non-C-like | 
|  | 219 | +                        // `enum`s with defined FFI representation), this will | 
|  | 220 | +                        // match the homogeneous computation on the equivalent | 
|  | 221 | +                        // `struct { tag; union { variant1; ... } }` and/or | 
|  | 222 | +                        // `union { struct { tag; variant1; } ... }` | 
|  | 223 | +                        // (the offsets of variant fields should be identical | 
|  | 224 | +                        // between the two for either to be a homogeneous aggregate). | 
|  | 225 | +                        let variant_start = total; | 
|  | 226 | +                        for variant_idx in variants.indices() { | 
|  | 227 | +                            let (variant_result, variant_total) = | 
|  | 228 | +                                from_fields_at(self.for_variant(cx, variant_idx), variant_start)?; | 
|  | 229 | + | 
|  | 230 | +                            result = result.merge(variant_result)?; | 
|  | 231 | +                            total = total.max(variant_total); | 
|  | 232 | +                        } | 
|  | 233 | +                    } | 
|  | 234 | +                } | 
|  | 235 | + | 
|  | 236 | +                // There needs to be no padding. | 
|  | 237 | +                if total != self.size { | 
|  | 238 | +                    Err(Heterogeneous) | 
|  | 239 | +                } else { | 
|  | 240 | +                    match result { | 
|  | 241 | +                        HomogeneousAggregate::Homogeneous(_) => { | 
|  | 242 | +                            assert_ne!(total, Size::ZERO); | 
|  | 243 | +                        } | 
|  | 244 | +                        HomogeneousAggregate::NoData => { | 
|  | 245 | +                            assert_eq!(total, Size::ZERO); | 
|  | 246 | +                        } | 
|  | 247 | +                    } | 
|  | 248 | +                    Ok(result) | 
|  | 249 | +                } | 
|  | 250 | +            } | 
|  | 251 | +            Abi::Aggregate { sized: false } => Err(Heterogeneous), | 
|  | 252 | +        } | 
|  | 253 | +    } | 
|  | 254 | +} | 
0 commit comments