rustc_smir/stable_mir/
abi.rs

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