stable_mir/abi.rs
1use std::fmt::{self, Debug};
2use std::num::NonZero;
3use std::ops::RangeInclusive;
4
5use serde::Serialize;
6
7use crate::compiler_interface::with;
8use crate::mir::FieldIdx;
9use crate::target::{MachineInfo, MachineSize as Size};
10use crate::ty::{Align, IndexedVal, Ty, VariantIdx};
11use crate::{Error, Opaque, error};
12
13/// A function ABI definition.
14#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
15pub struct FnAbi {
16 /// The types of each argument.
17 pub args: Vec<ArgAbi>,
18
19 /// The expected return type.
20 pub ret: ArgAbi,
21
22 /// The count of non-variadic arguments.
23 ///
24 /// Should only be different from `args.len()` when a function is a C variadic function.
25 pub fixed_count: u32,
26
27 /// The ABI convention.
28 pub conv: CallConvention,
29
30 /// Whether this is a variadic C function,
31 pub c_variadic: bool,
32}
33
34/// Information about the ABI of a function's argument, or return value.
35#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
36pub struct ArgAbi {
37 pub ty: Ty,
38 pub layout: Layout,
39 pub mode: PassMode,
40}
41
42/// How a function argument should be passed in to the target function.
43#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
44pub enum PassMode {
45 /// Ignore the argument.
46 ///
47 /// The argument is either uninhabited or a ZST.
48 Ignore,
49 /// Pass the argument directly.
50 ///
51 /// The argument has a layout abi of `Scalar` or `Vector`.
52 Direct(Opaque),
53 /// Pass a pair's elements directly in two arguments.
54 ///
55 /// The argument has a layout abi of `ScalarPair`.
56 Pair(Opaque, Opaque),
57 /// Pass the argument after casting it.
58 Cast { pad_i32: bool, cast: Opaque },
59 /// Pass the argument indirectly via a hidden pointer.
60 Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool },
61}
62
63/// The layout of a type, alongside the type itself.
64#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
65pub struct TyAndLayout {
66 pub ty: Ty,
67 pub layout: Layout,
68}
69
70/// The layout of a type in memory.
71#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
72pub struct LayoutShape {
73 /// The fields location within the layout
74 pub fields: FieldsShape,
75
76 /// Encodes information about multi-variant layouts.
77 /// Even with `Multiple` variants, a layout still has its own fields! Those are then
78 /// shared between all variants.
79 ///
80 /// To access all fields of this layout, both `fields` and the fields of the active variant
81 /// must be taken into account.
82 pub variants: VariantsShape,
83
84 /// The `abi` defines how this data is passed between functions.
85 pub abi: ValueAbi,
86
87 /// The ABI mandated alignment in bytes.
88 pub abi_align: Align,
89
90 /// The size of this layout in bytes.
91 pub size: Size,
92}
93
94impl LayoutShape {
95 /// Returns `true` if the layout corresponds to an unsized type.
96 #[inline]
97 pub fn is_unsized(&self) -> bool {
98 self.abi.is_unsized()
99 }
100
101 #[inline]
102 pub fn is_sized(&self) -> bool {
103 !self.abi.is_unsized()
104 }
105
106 /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1).
107 pub fn is_1zst(&self) -> bool {
108 self.is_sized() && self.size.bits() == 0 && self.abi_align == 1
109 }
110}
111
112#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
113pub struct Layout(usize);
114
115impl Layout {
116 pub fn shape(self) -> LayoutShape {
117 with(|cx| cx.layout_shape(self))
118 }
119}
120
121impl IndexedVal for Layout {
122 fn to_val(index: usize) -> Self {
123 Layout(index)
124 }
125 fn to_index(&self) -> usize {
126 self.0
127 }
128}
129
130/// Describes how the fields of a type are shaped in memory.
131#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
132pub enum FieldsShape {
133 /// Scalar primitives and `!`, which never have fields.
134 Primitive,
135
136 /// All fields start at no offset. The `usize` is the field count.
137 Union(NonZero<usize>),
138
139 /// Array/vector-like placement, with all fields of identical types.
140 Array { stride: Size, count: u64 },
141
142 /// Struct-like placement, with precomputed offsets.
143 ///
144 /// Fields are guaranteed to not overlap, but note that gaps
145 /// before, between and after all the fields are NOT always
146 /// padding, and as such their contents may not be discarded.
147 /// For example, enum variants leave a gap at the start,
148 /// where the discriminant field in the enum layout goes.
149 Arbitrary {
150 /// Offsets for the first byte of each field,
151 /// ordered to match the source definition order.
152 /// I.e.: It follows the same order as [crate::ty::VariantDef::fields()].
153 /// This vector does not go in increasing order.
154 offsets: Vec<Size>,
155 },
156}
157
158impl FieldsShape {
159 pub fn fields_by_offset_order(&self) -> Vec<FieldIdx> {
160 match self {
161 FieldsShape::Primitive => vec![],
162 FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(),
163 FieldsShape::Arbitrary { offsets, .. } => {
164 let mut indices = (0..offsets.len()).collect::<Vec<_>>();
165 indices.sort_by_key(|idx| offsets[*idx]);
166 indices
167 }
168 }
169 }
170
171 pub fn count(&self) -> usize {
172 match self {
173 FieldsShape::Primitive => 0,
174 FieldsShape::Union(count) => count.get(),
175 FieldsShape::Array { count, .. } => *count as usize,
176 FieldsShape::Arbitrary { offsets, .. } => offsets.len(),
177 }
178 }
179}
180
181#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
182pub enum VariantsShape {
183 /// A type with no valid variants. Must be uninhabited.
184 Empty,
185
186 /// Single enum variants, structs/tuples, unions, and all non-ADTs.
187 Single { index: VariantIdx },
188
189 /// Enum-likes with more than one inhabited variant: each variant comes with
190 /// a *discriminant* (usually the same as the variant index but the user can
191 /// assign explicit discriminant values). That discriminant is encoded
192 /// as a *tag* on the machine. The layout of each variant is
193 /// a struct, and they all have space reserved for the tag.
194 /// For enums, the tag is the sole field of the layout.
195 Multiple {
196 tag: Scalar,
197 tag_encoding: TagEncoding,
198 tag_field: usize,
199 variants: Vec<LayoutShape>,
200 },
201}
202
203#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
204pub enum TagEncoding {
205 /// The tag directly stores the discriminant, but possibly with a smaller layout
206 /// (so converting the tag to the discriminant can require sign extension).
207 Direct,
208
209 /// Niche (values invalid for a type) encoding the discriminant:
210 /// Discriminant and variant index coincide.
211 /// The variant `untagged_variant` contains a niche at an arbitrary
212 /// offset (field `tag_field` of the enum), which for a variant with
213 /// discriminant `d` is set to
214 /// `(d - niche_variants.start).wrapping_add(niche_start)`.
215 ///
216 /// For example, `Option<(usize, &T)>` is represented such that
217 /// `None` has a null pointer for the second tuple field, and
218 /// `Some` is the identity function (with a non-null reference).
219 Niche {
220 untagged_variant: VariantIdx,
221 niche_variants: RangeInclusive<VariantIdx>,
222 niche_start: u128,
223 },
224}
225
226/// Describes how values of the type are passed by target ABIs,
227/// in terms of categories of C types there are ABI rules for.
228#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
229pub enum ValueAbi {
230 Scalar(Scalar),
231 ScalarPair(Scalar, Scalar),
232 Vector {
233 element: Scalar,
234 count: u64,
235 },
236 Aggregate {
237 /// If true, the size is exact, otherwise it's only a lower bound.
238 sized: bool,
239 },
240}
241
242impl ValueAbi {
243 /// Returns `true` if the layout corresponds to an unsized type.
244 pub fn is_unsized(&self) -> bool {
245 match *self {
246 ValueAbi::Scalar(_) | ValueAbi::ScalarPair(..) | ValueAbi::Vector { .. } => false,
247 ValueAbi::Aggregate { sized } => !sized,
248 }
249 }
250}
251
252/// Information about one scalar component of a Rust type.
253#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)]
254pub enum Scalar {
255 Initialized {
256 /// The primitive type used to represent this value.
257 value: Primitive,
258 /// The range that represents valid values.
259 /// The range must be valid for the `primitive` size.
260 valid_range: WrappingRange,
261 },
262 Union {
263 /// Unions never have niches, so there is no `valid_range`.
264 /// Even for unions, we need to use the correct registers for the kind of
265 /// values inside the union, so we keep the `Primitive` type around.
266 /// It is also used to compute the size of the scalar.
267 value: Primitive,
268 },
269}
270
271impl Scalar {
272 pub fn has_niche(&self, target: &MachineInfo) -> bool {
273 match self {
274 Scalar::Initialized { value, valid_range } => {
275 !valid_range.is_full(value.size(target)).unwrap()
276 }
277 Scalar::Union { .. } => false,
278 }
279 }
280}
281
282/// Fundamental unit of memory access and layout.
283#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)]
284pub enum Primitive {
285 /// The `bool` is the signedness of the `Integer` type.
286 ///
287 /// One would think we would not care about such details this low down,
288 /// but some ABIs are described in terms of C types and ISAs where the
289 /// integer arithmetic is done on {sign,zero}-extended registers, e.g.
290 /// a negative integer passed by zero-extension will appear positive in
291 /// the callee, and most operations on it will produce the wrong values.
292 Int {
293 length: IntegerLength,
294 signed: bool,
295 },
296 Float {
297 length: FloatLength,
298 },
299 Pointer(AddressSpace),
300}
301
302impl Primitive {
303 pub fn size(self, target: &MachineInfo) -> Size {
304 match self {
305 Primitive::Int { length, .. } => Size::from_bits(length.bits()),
306 Primitive::Float { length } => Size::from_bits(length.bits()),
307 Primitive::Pointer(_) => target.pointer_width,
308 }
309 }
310}
311
312/// Enum representing the existing integer lengths.
313#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
314pub enum IntegerLength {
315 I8,
316 I16,
317 I32,
318 I64,
319 I128,
320}
321
322/// Enum representing the existing float lengths.
323#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
324pub enum FloatLength {
325 F16,
326 F32,
327 F64,
328 F128,
329}
330
331impl IntegerLength {
332 pub fn bits(self) -> usize {
333 match self {
334 IntegerLength::I8 => 8,
335 IntegerLength::I16 => 16,
336 IntegerLength::I32 => 32,
337 IntegerLength::I64 => 64,
338 IntegerLength::I128 => 128,
339 }
340 }
341}
342
343impl FloatLength {
344 pub fn bits(self) -> usize {
345 match self {
346 FloatLength::F16 => 16,
347 FloatLength::F32 => 32,
348 FloatLength::F64 => 64,
349 FloatLength::F128 => 128,
350 }
351 }
352}
353
354/// An identifier that specifies the address space that some operation
355/// should operate on. Special address spaces have an effect on code generation,
356/// depending on the target and the address spaces it implements.
357#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
358pub struct AddressSpace(pub u32);
359
360impl AddressSpace {
361 /// The default address space, corresponding to data space.
362 pub const DATA: Self = AddressSpace(0);
363}
364
365/// Inclusive wrap-around range of valid values (bitwise representation), that is, if
366/// start > end, it represents `start..=MAX`, followed by `0..=end`.
367///
368/// That is, for an i8 primitive, a range of `254..=2` means following
369/// sequence:
370///
371/// 254 (-2), 255 (-1), 0, 1, 2
372#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)]
373pub struct WrappingRange {
374 pub start: u128,
375 pub end: u128,
376}
377
378impl WrappingRange {
379 /// Returns `true` if `size` completely fills the range.
380 #[inline]
381 pub fn is_full(&self, size: Size) -> Result<bool, Error> {
382 let Some(max_value) = size.unsigned_int_max() else {
383 return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits()));
384 };
385 if self.start <= max_value && self.end <= max_value {
386 Ok(self.start == (self.end.wrapping_add(1) & max_value))
387 } else {
388 Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits()))
389 }
390 }
391
392 /// Returns `true` if `v` is contained in the range.
393 #[inline(always)]
394 pub fn contains(&self, v: u128) -> bool {
395 if self.wraps_around() {
396 self.start <= v || v <= self.end
397 } else {
398 self.start <= v && v <= self.end
399 }
400 }
401
402 /// Returns `true` if the range wraps around.
403 /// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`.
404 /// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`.
405 #[inline]
406 pub fn wraps_around(&self) -> bool {
407 self.start > self.end
408 }
409}
410
411impl Debug for WrappingRange {
412 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
413 if self.start > self.end {
414 write!(fmt, "(..={}) | ({}..)", self.end, self.start)?;
415 } else {
416 write!(fmt, "{}..={}", self.start, self.end)?;
417 }
418 Ok(())
419 }
420}
421
422/// General language calling conventions.
423#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
424pub enum CallConvention {
425 C,
426 Rust,
427
428 Cold,
429 PreserveMost,
430 PreserveAll,
431
432 // Target-specific calling conventions.
433 ArmAapcs,
434 CCmseNonSecureCall,
435 CCmseNonSecureEntry,
436
437 Msp430Intr,
438
439 PtxKernel,
440
441 GpuKernel,
442
443 X86Fastcall,
444 X86Intr,
445 X86Stdcall,
446 X86ThisCall,
447 X86VectorCall,
448
449 X86_64SysV,
450 X86_64Win64,
451
452 AvrInterrupt,
453 AvrNonBlockingInterrupt,
454
455 RiscvInterrupt,
456}