mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
456 lines
14 KiB
Rust
456 lines
14 KiB
Rust
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, IndexedVal, Ty, VariantIdx};
|
|
use crate::{Error, Opaque, 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, Serialize)]
|
|
pub struct Layout(usize);
|
|
|
|
impl Layout {
|
|
pub fn shape(self) -> LayoutShape {
|
|
with(|cx| cx.layout_shape(self))
|
|
}
|
|
}
|
|
|
|
impl IndexedVal for Layout {
|
|
fn to_val(index: usize) -> Self {
|
|
Layout(index)
|
|
}
|
|
fn to_index(&self) -> usize {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
/// 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 [crate::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 {
|
|
/// 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<LayoutShape>,
|
|
},
|
|
}
|
|
|
|
#[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,
|
|
},
|
|
}
|
|
|
|
/// 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 {
|
|
Uninhabited,
|
|
Scalar(Scalar),
|
|
ScalarPair(Scalar, Scalar),
|
|
Vector {
|
|
element: Scalar,
|
|
count: u64,
|
|
},
|
|
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::Uninhabited
|
|
| ValueAbi::Scalar(_)
|
|
| ValueAbi::ScalarPair(..)
|
|
| ValueAbi::Vector { .. } => 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,
|
|
|
|
// Target-specific calling conventions.
|
|
ArmAapcs,
|
|
CCmseNonSecureCall,
|
|
CCmseNonSecureEntry,
|
|
|
|
Msp430Intr,
|
|
|
|
PtxKernel,
|
|
|
|
X86Fastcall,
|
|
X86Intr,
|
|
X86Stdcall,
|
|
X86ThisCall,
|
|
X86VectorCall,
|
|
|
|
X86_64SysV,
|
|
X86_64Win64,
|
|
|
|
AvrInterrupt,
|
|
AvrNonBlockingInterrupt,
|
|
|
|
RiscvInterrupt,
|
|
}
|