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 #[tracing::instrument(skip(cx), level = "debug")]
64 pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
65 where
66 Ty: TyAbiInterface<'a, C> + Copy,
67 {
68 match self.backend_repr {
69 // The primitive for this algorithm.
70 BackendRepr::Scalar(scalar) => {
71 let kind = match scalar.primitive() {
72 Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer,
73 Primitive::Float(_) => RegKind::Float,
74 };
75 Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
76 }
77
78 BackendRepr::SimdVector { .. } => {
79 assert!(!self.is_zst());
80 Ok(HomogeneousAggregate::Homogeneous(Reg {
81 kind: RegKind::Vector,
82 size: self.size,
83 }))
84 }
85
86 BackendRepr::ScalableVector { .. } => {
87 unreachable!("`homogeneous_aggregate` should not be called for scalable vectors")
88 }
89
90 BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => {
91 // Helper for computing `homogeneous_aggregate`, allowing a custom
92 // starting offset (used below for handling variants).
93 let from_fields_at =
94 |layout: Self,
95 start: Size|
96 -> Result<(HomogeneousAggregate, Size), Heterogeneous> {
97 let is_union = match layout.fields {
98 FieldsShape::Primitive => {
99 unreachable!("aggregates can't have `FieldsShape::Primitive`")
100 }
101 FieldsShape::Array { count, .. } => {
102 assert_eq!(start, Size::ZERO);
103
104 let result = if count > 0 {
105 layout.field(cx, 0).homogeneous_aggregate(cx)?
106 } else {
107 HomogeneousAggregate::NoData
108 };
109 return Ok((result, layout.size));
110 }
111 FieldsShape::Union(_) => true,
112 FieldsShape::Arbitrary { .. } => false,
113 };
114
115 let mut result = HomogeneousAggregate::NoData;
116 let mut total = start;
117
118 for i in 0..layout.fields.count() {
119 let field = layout.field(cx, i);
120 if field.is_1zst() {
121 // No data here and no impact on layout, can be ignored.
122 // (We might be able to also ignore all aligned ZST but that's less clear.)
123 continue;
124 }
125
126 if !is_union && total != layout.fields.offset(i) {
127 // This field isn't just after the previous one we considered, abort.
128 return Err(Heterogeneous);
129 }
130
131 result = result.merge(field.homogeneous_aggregate(cx)?)?;
132
133 // Keep track of the offset (without padding).
134 let size = field.size;
135 if is_union {
136 total = total.max(size);
137 } else {
138 total += size;
139 }
140 }
141
142 Ok((result, total))
143 };
144
145 let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
146
147 match &self.variants {
148 Variants::Single { .. } | Variants::Empty => {}
149 Variants::Multiple { variants, .. } => {
150 // Treat enum variants like union members.
151 // HACK(eddyb) pretend the `enum` field (discriminant)
152 // is at the start of every variant (otherwise the gap
153 // at the start of all variants would disqualify them).
154 //
155 // NB: for all tagged `enum`s (which include all non-C-like
156 // `enum`s with defined FFI representation), this will
157 // match the homogeneous computation on the equivalent
158 // `struct { tag; union { variant1; ... } }` and/or
159 // `union { struct { tag; variant1; } ... }`
160 // (the offsets of variant fields should be identical
161 // between the two for either to be a homogeneous aggregate).
162 let variant_start = total;
163 for variant_idx in variants.indices() {
164 let (variant_result, variant_total) =
165 from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
166
167 result = result.merge(variant_result)?;
168 total = total.max(variant_total);
169 }
170 }
171 }
172
173 // There needs to be no padding.
174 if total != self.size {
175 Err(Heterogeneous)
176 } else {
177 match result {
178 HomogeneousAggregate::Homogeneous(_) => {
179 assert_ne!(total, Size::ZERO);
180 }
181 HomogeneousAggregate::NoData => {
182 assert_eq!(total, Size::ZERO);
183 }
184 }
185 Ok(result)
186 }
187 }
188 BackendRepr::Memory { sized: false } => Err(Heterogeneous),
189 }
190 }
191}