compiler: Wire {TyAnd,}Layout
into rustc_abi
· rust-lang/rust@1072190 (original) (raw)
``
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,kind: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 {
`
``
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(&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
`+
}
`