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}