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