rustc_abi/
callconv.rs

1#[cfg(feature = "nightly")]
2use crate::{BackendRepr, FieldsShape, Primitive, Size, TyAbiInterface, TyAndLayout, Variants};
3
4mod reg;
5
6pub use reg::{Reg, RegKind};
7
8/// 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 the
12    /// same way (specified in the `Reg` value).
13    Homogeneous(Reg),
14
15    /// There are no leaf fields at all.
16    NoData,
17}
18
19/// Error from the `homogeneous_aggregate` test function, indicating
20/// there are distinct leaf fields passed in different ways,
21/// or this is uninhabited.
22#[derive(Copy, Clone, Debug)]
23pub struct Heterogeneous;
24
25impl HomogeneousAggregate {
26    /// If this is a homogeneous aggregate, returns the homogeneous
27    /// 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    }
34
35    /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
36    /// the same `struct`. Only succeeds if only one of them has any data,
37    /// or both units are identical.
38    fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {
39        match (self, other) {
40            (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),
41
42            (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {
43                if a != b {
44                    return Err(Heterogeneous);
45                }
46                Ok(self)
47            }
48        }
49    }
50}
51
52#[cfg(feature = "nightly")]
53impl<'a, Ty> TyAndLayout<'a, Ty> {
54    /// Returns `Homogeneous` if this layout is an aggregate containing fields of
55    /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
56    /// special-cased in ABIs.
57    ///
58    /// Note: We generally ignore 1-ZST fields when computing this value (see #56877).
59    ///
60    /// This is public so that it can be used in unit tests, but
61    /// should generally only be relevant to the ABI details of
62    /// specific targets.
63    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
64    where
65        Ty: TyAbiInterface<'a, C> + Copy,
66    {
67        match self.backend_repr {
68            BackendRepr::Uninhabited => Err(Heterogeneous),
69
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            }
78
79            BackendRepr::Vector { .. } => {
80                assert!(!self.is_zst());
81                Ok(HomogeneousAggregate::Homogeneous(Reg {
82                    kind: RegKind::Vector,
83                    size: self.size,
84                }))
85            }
86
87            BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => {
88                // Helper for computing `homogeneous_aggregate`, allowing a custom
89                // starting offset (used below for handling variants).
90                let from_fields_at =
91                    |layout: Self,
92                     start: Size|
93                     -> Result<(HomogeneousAggregate, Size), Heterogeneous> {
94                        let is_union = match layout.fields {
95                            FieldsShape::Primitive => {
96                                unreachable!("aggregates can't have `FieldsShape::Primitive`")
97                            }
98                            FieldsShape::Array { count, .. } => {
99                                assert_eq!(start, Size::ZERO);
100
101                                let result = if count > 0 {
102                                    layout.field(cx, 0).homogeneous_aggregate(cx)?
103                                } else {
104                                    HomogeneousAggregate::NoData
105                                };
106                                return Ok((result, layout.size));
107                            }
108                            FieldsShape::Union(_) => true,
109                            FieldsShape::Arbitrary { .. } => false,
110                        };
111
112                        let mut result = HomogeneousAggregate::NoData;
113                        let mut total = start;
114
115                        for i in 0..layout.fields.count() {
116                            let field = layout.field(cx, i);
117                            if field.is_1zst() {
118                                // No data here and no impact on layout, can be ignored.
119                                // (We might be able to also ignore all aligned ZST but that's less clear.)
120                                continue;
121                            }
122
123                            if !is_union && total != layout.fields.offset(i) {
124                                // This field isn't just after the previous one we considered, abort.
125                                return Err(Heterogeneous);
126                            }
127
128                            result = result.merge(field.homogeneous_aggregate(cx)?)?;
129
130                            // Keep track of the offset (without padding).
131                            let size = field.size;
132                            if is_union {
133                                total = total.max(size);
134                            } else {
135                                total += size;
136                            }
137                        }
138
139                        Ok((result, total))
140                    };
141
142                let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
143
144                match &self.variants {
145                    Variants::Single { .. } | Variants::Empty => {}
146                    Variants::Multiple { variants, .. } => {
147                        // Treat enum variants like union members.
148                        // HACK(eddyb) pretend the `enum` field (discriminant)
149                        // is at the start of every variant (otherwise the gap
150                        // at the start of all variants would disqualify them).
151                        //
152                        // NB: for all tagged `enum`s (which include all non-C-like
153                        // `enum`s with defined FFI representation), this will
154                        // match the homogeneous computation on the equivalent
155                        // `struct { tag; union { variant1; ... } }` and/or
156                        // `union { struct { tag; variant1; } ... }`
157                        // (the offsets of variant fields should be identical
158                        // between the two for either to be a homogeneous aggregate).
159                        let variant_start = total;
160                        for variant_idx in variants.indices() {
161                            let (variant_result, variant_total) =
162                                from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
163
164                            result = result.merge(variant_result)?;
165                            total = total.max(variant_total);
166                        }
167                    }
168                }
169
170                // There needs to be no padding.
171                if total != self.size {
172                    Err(Heterogeneous)
173                } else {
174                    match result {
175                        HomogeneousAggregate::Homogeneous(_) => {
176                            assert_ne!(total, Size::ZERO);
177                        }
178                        HomogeneousAggregate::NoData => {
179                            assert_eq!(total, Size::ZERO);
180                        }
181                    }
182                    Ok(result)
183                }
184            }
185            BackendRepr::Memory { sized: false } => Err(Heterogeneous),
186        }
187    }
188}