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}