| 
 | 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