| use std::fmt::{self, Debug}; | |
| use std::num::NonZero; | |
| use std::ops::RangeInclusive; | |
| use serde::Serialize; | |
| use crate::compiler_interface::with; | |
| use crate::mir::FieldIdx; | |
| use crate::target::{MachineInfo, MachineSize as Size}; | |
| use crate::ty::{Align, Ty, VariantIdx, index_impl}; | |
| use crate::{Error, Opaque, ThreadLocalIndex, error}; | |
| /// A function ABI definition. | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub struct FnAbi { | |
| /// The types of each argument. | |
| pub args: Vec<ArgAbi>, | |
| /// The expected return type. | |
| pub ret: ArgAbi, | |
| /// The count of non-variadic arguments. | |
| /// | |
| /// Should only be different from `args.len()` when a function is a C variadic function. | |
| pub fixed_count: u32, | |
| /// The ABI convention. | |
| pub conv: CallConvention, | |
| /// Whether this is a variadic C function, | |
| pub c_variadic: bool, | |
| } | |
| /// Information about the ABI of a function's argument, or return value. | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub struct ArgAbi { | |
| pub ty: Ty, | |
| pub layout: Layout, | |
| pub mode: PassMode, | |
| } | |
| /// How a function argument should be passed in to the target function. | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub enum PassMode { | |
| /// Ignore the argument. | |
| /// | |
| /// The argument is either uninhabited or a ZST. | |
| Ignore, | |
| /// Pass the argument directly. | |
| /// | |
| /// The argument has a layout abi of `Scalar` or `Vector`. | |
| Direct(Opaque), | |
| /// Pass a pair's elements directly in two arguments. | |
| /// | |
| /// The argument has a layout abi of `ScalarPair`. | |
| Pair(Opaque, Opaque), | |
| /// Pass the argument after casting it. | |
| Cast { pad_i32: bool, cast: Opaque }, | |
| /// Pass the argument indirectly via a hidden pointer. | |
| Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool }, | |
| } | |
| /// The layout of a type, alongside the type itself. | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub struct TyAndLayout { | |
| pub ty: Ty, | |
| pub layout: Layout, | |
| } | |
| /// The layout of a type in memory. | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub struct LayoutShape { | |
| /// The fields location within the layout | |
| pub fields: FieldsShape, | |
| /// Encodes information about multi-variant layouts. | |
| /// Even with `Multiple` variants, a layout still has its own fields! Those are then | |
| /// shared between all variants. | |
| /// | |
| /// To access all fields of this layout, both `fields` and the fields of the active variant | |
| /// must be taken into account. | |
| pub variants: VariantsShape, | |
| /// The `abi` defines how this data is passed between functions. | |
| pub abi: ValueAbi, | |
| /// The ABI mandated alignment in bytes. | |
| pub abi_align: Align, | |
| /// The size of this layout in bytes. | |
| pub size: Size, | |
| } | |
| impl LayoutShape { | |
| /// Returns `true` if the layout corresponds to an unsized type. | |
| #[inline] | |
| pub fn is_unsized(&self) -> bool { | |
| self.abi.is_unsized() | |
| } | |
| #[inline] | |
| pub fn is_sized(&self) -> bool { | |
| !self.abi.is_unsized() | |
| } | |
| /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1). | |
| pub fn is_1zst(&self) -> bool { | |
| self.is_sized() && self.size.bits() == 0 && self.abi_align == 1 | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | |
| pub struct Layout(usize, ThreadLocalIndex); | |
| index_impl!(Layout); | |
| impl Layout { | |
| pub fn shape(self) -> LayoutShape { | |
| with(|cx| cx.layout_shape(self)) | |
| } | |
| } | |
| /// Describes how the fields of a type are shaped in memory. | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub enum FieldsShape { | |
| /// Scalar primitives and `!`, which never have fields. | |
| Primitive, | |
| /// All fields start at no offset. The `usize` is the field count. | |
| Union(NonZero<usize>), | |
| /// Array/vector-like placement, with all fields of identical types. | |
| Array { stride: Size, count: u64 }, | |
| /// Struct-like placement, with precomputed offsets. | |
| /// | |
| /// Fields are guaranteed to not overlap, but note that gaps | |
| /// before, between and after all the fields are NOT always | |
| /// padding, and as such their contents may not be discarded. | |
| /// For example, enum variants leave a gap at the start, | |
| /// where the discriminant field in the enum layout goes. | |
| Arbitrary { | |
| /// Offsets for the first byte of each field, | |
| /// ordered to match the source definition order. | |
| /// I.e.: It follows the same order as [super::ty::VariantDef::fields()]. | |
| /// This vector does not go in increasing order. | |
| offsets: Vec<Size>, | |
| }, | |
| } | |
| impl FieldsShape { | |
| pub fn fields_by_offset_order(&self) -> Vec<FieldIdx> { | |
| match self { | |
| FieldsShape::Primitive => vec![], | |
| FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(), | |
| FieldsShape::Arbitrary { offsets, .. } => { | |
| let mut indices = (0..offsets.len()).collect::<Vec<_>>(); | |
| indices.sort_by_key(|idx| offsets[*idx]); | |
| indices | |
| } | |
| } | |
| } | |
| pub fn count(&self) -> usize { | |
| match self { | |
| FieldsShape::Primitive => 0, | |
| FieldsShape::Union(count) => count.get(), | |
| FieldsShape::Array { count, .. } => *count as usize, | |
| FieldsShape::Arbitrary { offsets, .. } => offsets.len(), | |
| } | |
| } | |
| } | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub enum VariantsShape { | |
| /// A type with no valid variants. Must be uninhabited. | |
| Empty, | |
| /// Single enum variants, structs/tuples, unions, and all non-ADTs. | |
| Single { index: VariantIdx }, | |
| /// Enum-likes with more than one inhabited variant: each variant comes with | |
| /// a *discriminant* (usually the same as the variant index but the user can | |
| /// assign explicit discriminant values). That discriminant is encoded | |
| /// as a *tag* on the machine. The layout of each variant is | |
| /// a struct, and they all have space reserved for the tag. | |
| /// For enums, the tag is the sole field of the layout. | |
| Multiple { | |
| tag: Scalar, | |
| tag_encoding: TagEncoding, | |
| tag_field: usize, | |
| variants: Vec<VariantFields>, | |
| }, | |
| } | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub struct VariantFields { | |
| /// Offsets for the first byte of each field, | |
| /// ordered to match the source definition order. | |
| /// I.e.: It follows the same order as [super::ty::VariantDef::fields()]. | |
| /// This vector does not go in increasing order. | |
| pub offsets: Vec<Size>, | |
| } | |
| impl VariantFields { | |
| pub fn fields_by_offset_order(&self) -> Vec<FieldIdx> { | |
| let mut indices = (0..self.offsets.len()).collect::<Vec<_>>(); | |
| indices.sort_by_key(|idx| self.offsets[*idx]); | |
| indices | |
| } | |
| } | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub enum TagEncoding { | |
| /// The tag directly stores the discriminant, but possibly with a smaller layout | |
| /// (so converting the tag to the discriminant can require sign extension). | |
| Direct, | |
| /// Niche (values invalid for a type) encoding the discriminant: | |
| /// Discriminant and variant index coincide. | |
| /// The variant `untagged_variant` contains a niche at an arbitrary | |
| /// offset (field `tag_field` of the enum), which for a variant with | |
| /// discriminant `d` is set to | |
| /// `(d - niche_variants.start).wrapping_add(niche_start)`. | |
| /// | |
| /// For example, `Option<(usize, &T)>` is represented such that | |
| /// `None` has a null pointer for the second tuple field, and | |
| /// `Some` is the identity function (with a non-null reference). | |
| Niche { | |
| untagged_variant: VariantIdx, | |
| niche_variants: RangeInclusive<VariantIdx>, | |
| niche_start: u128, | |
| }, | |
| } | |
| /// How many scalable vectors are in a `ValueAbi::ScalableVector`? | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub struct NumScalableVectors(pub(crate) u8); | |
| /// Describes how values of the type are passed by target ABIs, | |
| /// in terms of categories of C types there are ABI rules for. | |
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub enum ValueAbi { | |
| Scalar(Scalar), | |
| ScalarPair { | |
| a: Scalar, | |
| b: Scalar, | |
| b_offset: Size, | |
| }, | |
| Vector { | |
| element: Scalar, | |
| count: u64, | |
| }, | |
| ScalableVector { | |
| element: Scalar, | |
| count: u64, | |
| number_of_vectors: NumScalableVectors, | |
| }, | |
| Aggregate { | |
| /// If true, the size is exact, otherwise it's only a lower bound. | |
| sized: bool, | |
| }, | |
| } | |
| impl ValueAbi { | |
| /// Returns `true` if the layout corresponds to an unsized type. | |
| pub fn is_unsized(&self) -> bool { | |
| match *self { | |
| ValueAbi::Scalar(_) | |
| | ValueAbi::ScalarPair { .. } | |
| | ValueAbi::Vector { .. } | |
| // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the | |
| // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is | |
| // fully implemented, scalable vectors will remain `Sized`, they just won't be | |
| // `const Sized` - whether `is_unsized` continues to return `false` at that point will | |
| // need to be revisited and will depend on what `is_unsized` is used for. | |
| | ValueAbi::ScalableVector { .. } => false, | |
| ValueAbi::Aggregate { sized } => !sized, | |
| } | |
| } | |
| } | |
| /// Information about one scalar component of a Rust type. | |
| #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)] | |
| pub enum Scalar { | |
| Initialized { | |
| /// The primitive type used to represent this value. | |
| value: Primitive, | |
| /// The range that represents valid values. | |
| /// The range must be valid for the `primitive` size. | |
| valid_range: WrappingRange, | |
| }, | |
| Union { | |
| /// Unions never have niches, so there is no `valid_range`. | |
| /// Even for unions, we need to use the correct registers for the kind of | |
| /// values inside the union, so we keep the `Primitive` type around. | |
| /// It is also used to compute the size of the scalar. | |
| value: Primitive, | |
| }, | |
| } | |
| impl Scalar { | |
| pub fn has_niche(&self, target: &MachineInfo) -> bool { | |
| match self { | |
| Scalar::Initialized { value, valid_range } => { | |
| !valid_range.is_full(value.size(target)).unwrap() | |
| } | |
| Scalar::Union { .. } => false, | |
| } | |
| } | |
| } | |
| /// Fundamental unit of memory access and layout. | |
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)] | |
| pub enum Primitive { | |
| /// The `bool` is the signedness of the `Integer` type. | |
| /// | |
| /// One would think we would not care about such details this low down, | |
| /// but some ABIs are described in terms of C types and ISAs where the | |
| /// integer arithmetic is done on {sign,zero}-extended registers, e.g. | |
| /// a negative integer passed by zero-extension will appear positive in | |
| /// the callee, and most operations on it will produce the wrong values. | |
| Int { | |
| length: IntegerLength, | |
| signed: bool, | |
| }, | |
| Float { | |
| length: FloatLength, | |
| }, | |
| Pointer(AddressSpace), | |
| } | |
| impl Primitive { | |
| pub fn size(self, target: &MachineInfo) -> Size { | |
| match self { | |
| Primitive::Int { length, .. } => Size::from_bits(length.bits()), | |
| Primitive::Float { length } => Size::from_bits(length.bits()), | |
| Primitive::Pointer(_) => target.pointer_width, | |
| } | |
| } | |
| } | |
| /// Enum representing the existing integer lengths. | |
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] | |
| pub enum IntegerLength { | |
| I8, | |
| I16, | |
| I32, | |
| I64, | |
| I128, | |
| } | |
| /// Enum representing the existing float lengths. | |
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] | |
| pub enum FloatLength { | |
| F16, | |
| F32, | |
| F64, | |
| F128, | |
| } | |
| impl IntegerLength { | |
| pub fn bits(self) -> usize { | |
| match self { | |
| IntegerLength::I8 => 8, | |
| IntegerLength::I16 => 16, | |
| IntegerLength::I32 => 32, | |
| IntegerLength::I64 => 64, | |
| IntegerLength::I128 => 128, | |
| } | |
| } | |
| } | |
| impl FloatLength { | |
| pub fn bits(self) -> usize { | |
| match self { | |
| FloatLength::F16 => 16, | |
| FloatLength::F32 => 32, | |
| FloatLength::F64 => 64, | |
| FloatLength::F128 => 128, | |
| } | |
| } | |
| } | |
| /// An identifier that specifies the address space that some operation | |
| /// should operate on. Special address spaces have an effect on code generation, | |
| /// depending on the target and the address spaces it implements. | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] | |
| pub struct AddressSpace(pub u32); | |
| impl AddressSpace { | |
| /// The default address space, corresponding to data space. | |
| pub const DATA: Self = AddressSpace(0); | |
| } | |
| /// Inclusive wrap-around range of valid values (bitwise representation), that is, if | |
| /// start > end, it represents `start..=MAX`, followed by `0..=end`. | |
| /// | |
| /// That is, for an i8 primitive, a range of `254..=2` means following | |
| /// sequence: | |
| /// | |
| /// 254 (-2), 255 (-1), 0, 1, 2 | |
| #[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] | |
| pub struct WrappingRange { | |
| pub start: u128, | |
| pub end: u128, | |
| } | |
| impl WrappingRange { | |
| /// Returns `true` if `size` completely fills the range. | |
| #[inline] | |
| pub fn is_full(&self, size: Size) -> Result<bool, Error> { | |
| let Some(max_value) = size.unsigned_int_max() else { | |
| return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits())); | |
| }; | |
| if self.start <= max_value && self.end <= max_value { | |
| Ok(self.start == (self.end.wrapping_add(1) & max_value)) | |
| } else { | |
| Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits())) | |
| } | |
| } | |
| /// Returns `true` if `v` is contained in the range. | |
| #[inline(always)] | |
| pub fn contains(&self, v: u128) -> bool { | |
| if self.wraps_around() { | |
| self.start <= v || v <= self.end | |
| } else { | |
| self.start <= v && v <= self.end | |
| } | |
| } | |
| /// Returns `true` if the range wraps around. | |
| /// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`. | |
| /// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`. | |
| #[inline] | |
| pub fn wraps_around(&self) -> bool { | |
| self.start > self.end | |
| } | |
| } | |
| impl Debug for WrappingRange { | |
| fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| if self.start > self.end { | |
| write!(fmt, "(..={}) | ({}..)", self.end, self.start)?; | |
| } else { | |
| write!(fmt, "{}..={}", self.start, self.end)?; | |
| } | |
| Ok(()) | |
| } | |
| } | |
| /// General language calling conventions. | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] | |
| pub enum CallConvention { | |
| C, | |
| Rust, | |
| Cold, | |
| PreserveMost, | |
| PreserveAll, | |
| PreserveNone, | |
| Tail, | |
| Custom, | |
| Swift, | |
| // Target-specific calling conventions. | |
| ArmAapcs, | |
| CCmseNonSecureCall, | |
| CCmseNonSecureEntry, | |
| Msp430Intr, | |
| PtxKernel, | |
| GpuKernel, | |
| X86Fastcall, | |
| X86Intr, | |
| X86Stdcall, | |
| X86ThisCall, | |
| X86VectorCall, | |
| X86_64SysV, | |
| X86_64Win64, | |
| AvrInterrupt, | |
| AvrNonBlockingInterrupt, | |
| RiscvInterrupt, | |
| } | |
| #[non_exhaustive] | |
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] | |
| pub struct ReprFlags { | |
| pub is_simd: bool, | |
| pub is_c: bool, | |
| pub is_transparent: bool, | |
| pub is_linear: bool, | |
| } | |
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] | |
| pub enum IntegerType { | |
| /// Pointer-sized integer type, i.e. `isize` and `usize`. | |
| Pointer { | |
| /// Signedness. e.g. `true` for `isize` | |
| is_signed: bool, | |
| }, | |
| /// Fixed-sized integer type, e.g. `i8`, `u32`, `i128`. | |
| Fixed { | |
| /// Length of this integer type. e.g. `IntegerLength::I8` for `u8`. | |
| length: IntegerLength, | |
| /// Signedness. e.g. `false` for `u8` | |
| is_signed: bool, | |
| }, | |
| } | |
| /// Representation options provided by the user | |
| #[non_exhaustive] | |
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] | |
| pub struct ReprOptions { | |
| pub int: Option<IntegerType>, | |
| pub align: Option<Align>, | |
| pub pack: Option<Align>, | |
| pub flags: ReprFlags, | |
| } |