diff options
Diffstat (limited to 'compiler/rustc_abi/src')
| -rw-r--r-- | compiler/rustc_abi/src/callconv.rs | 254 | ||||
| -rw-r--r-- | compiler/rustc_abi/src/layout.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_abi/src/layout/ty.rs | 267 | ||||
| -rw-r--r-- | compiler/rustc_abi/src/lib.rs | 8 |
4 files changed, 532 insertions, 1 deletions
diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs new file mode 100644 index 00000000000..2ecac8a9df1 --- /dev/null +++ b/compiler/rustc_abi/src/callconv.rs @@ -0,0 +1,254 @@ +mod abi { + pub(crate) use crate::Primitive::*; + pub(crate) use crate::Variants; +} + +use rustc_macros::HashStable_Generic; + +use crate::{Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum RegKind { + Integer, + Float, + Vector, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub struct Reg { + pub kind: RegKind, + pub size: Size, +} + +macro_rules! reg_ctor { + ($name:ident, $kind:ident, $bits:expr) => { + pub fn $name() -> Reg { + Reg { kind: RegKind::$kind, size: Size::from_bits($bits) } + } + }; +} + +impl Reg { + reg_ctor!(i8, Integer, 8); + reg_ctor!(i16, Integer, 16); + reg_ctor!(i32, Integer, 32); + reg_ctor!(i64, Integer, 64); + reg_ctor!(i128, Integer, 128); + + reg_ctor!(f32, Float, 32); + reg_ctor!(f64, Float, 64); +} + +impl Reg { + pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { + let dl = cx.data_layout(); + match self.kind { + RegKind::Integer => match self.size.bits() { + 1 => dl.i1_align.abi, + 2..=8 => dl.i8_align.abi, + 9..=16 => dl.i16_align.abi, + 17..=32 => dl.i32_align.abi, + 33..=64 => dl.i64_align.abi, + 65..=128 => dl.i128_align.abi, + _ => panic!("unsupported integer: {self:?}"), + }, + RegKind::Float => match self.size.bits() { + 16 => dl.f16_align.abi, + 32 => dl.f32_align.abi, + 64 => dl.f64_align.abi, + 128 => dl.f128_align.abi, + _ => panic!("unsupported float: {self:?}"), + }, + RegKind::Vector => dl.vector_align(self.size).abi, + } + } +} + +/// Return value from the `homogeneous_aggregate` test function. +#[derive(Copy, Clone, Debug)] +pub enum HomogeneousAggregate { + /// Yes, all the "leaf fields" of this struct are passed in the + /// same way (specified in the `Reg` value). + Homogeneous(Reg), + + /// There are no leaf fields at all. + NoData, +} + +/// Error from the `homogeneous_aggregate` test function, indicating +/// there are distinct leaf fields passed in different ways, +/// or this is uninhabited. +#[derive(Copy, Clone, Debug)] +pub struct Heterogeneous; + +impl HomogeneousAggregate { + /// If this is a homogeneous aggregate, returns the homogeneous + /// unit, else `None`. + pub fn unit(self) -> Option<Reg> { + match self { + HomogeneousAggregate::Homogeneous(reg) => Some(reg), + HomogeneousAggregate::NoData => None, + } + } + + /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in + /// the same `struct`. Only succeeds if only one of them has any data, + /// or both units are identical. + fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> { + match (self, other) { + (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x), + + (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => { + if a != b { + return Err(Heterogeneous); + } + Ok(self) + } + } + } +} + +impl<'a, Ty> TyAndLayout<'a, Ty> { + /// Returns `true` if this is an aggregate type (including a ScalarPair!) + pub fn is_aggregate(&self) -> bool { + match self.abi { + Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, + Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, + } + } + + /// Returns `Homogeneous` if this layout is an aggregate containing fields of + /// only a single type (e.g., `(u32, u32)`). Such aggregates are often + /// special-cased in ABIs. + /// + /// Note: We generally ignore 1-ZST fields when computing this value (see #56877). + /// + /// This is public so that it can be used in unit tests, but + /// should generally only be relevant to the ABI details of + /// specific targets. + pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous> + where + Ty: TyAbiInterface<'a, C> + Copy, + { + match self.abi { + Abi::Uninhabited => Err(Heterogeneous), + + // The primitive for this algorithm. + Abi::Scalar(scalar) => { + let kind = match scalar.primitive() { + abi::Int(..) | abi::Pointer(_) => RegKind::Integer, + abi::Float(_) => RegKind::Float, + }; + Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })) + } + + Abi::Vector { .. } => { + assert!(!self.is_zst()); + Ok(HomogeneousAggregate::Homogeneous(Reg { + kind: RegKind::Vector, + size: self.size, + })) + } + + Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => { + // Helper for computing `homogeneous_aggregate`, allowing a custom + // starting offset (used below for handling variants). + let from_fields_at = + |layout: Self, + start: Size| + -> Result<(HomogeneousAggregate, Size), Heterogeneous> { + let is_union = match layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Array { count, .. } => { + assert_eq!(start, Size::ZERO); + + let result = if count > 0 { + layout.field(cx, 0).homogeneous_aggregate(cx)? + } else { + HomogeneousAggregate::NoData + }; + return Ok((result, layout.size)); + } + FieldsShape::Union(_) => true, + FieldsShape::Arbitrary { .. } => false, + }; + + let mut result = HomogeneousAggregate::NoData; + let mut total = start; + + for i in 0..layout.fields.count() { + let field = layout.field(cx, i); + if field.is_1zst() { + // No data here and no impact on layout, can be ignored. + // (We might be able to also ignore all aligned ZST but that's less clear.) + continue; + } + + if !is_union && total != layout.fields.offset(i) { + // This field isn't just after the previous one we considered, abort. + return Err(Heterogeneous); + } + + result = result.merge(field.homogeneous_aggregate(cx)?)?; + + // Keep track of the offset (without padding). + let size = field.size; + if is_union { + total = total.max(size); + } else { + total += size; + } + } + + Ok((result, total)) + }; + + let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; + + match &self.variants { + abi::Variants::Single { .. } => {} + abi::Variants::Multiple { variants, .. } => { + // Treat enum variants like union members. + // HACK(eddyb) pretend the `enum` field (discriminant) + // is at the start of every variant (otherwise the gap + // at the start of all variants would disqualify them). + // + // NB: for all tagged `enum`s (which include all non-C-like + // `enum`s with defined FFI representation), this will + // match the homogeneous computation on the equivalent + // `struct { tag; union { variant1; ... } }` and/or + // `union { struct { tag; variant1; } ... }` + // (the offsets of variant fields should be identical + // between the two for either to be a homogeneous aggregate). + let variant_start = total; + for variant_idx in variants.indices() { + let (variant_result, variant_total) = + from_fields_at(self.for_variant(cx, variant_idx), variant_start)?; + + result = result.merge(variant_result)?; + total = total.max(variant_total); + } + } + } + + // There needs to be no padding. + if total != self.size { + Err(Heterogeneous) + } else { + match result { + HomogeneousAggregate::Homogeneous(_) => { + assert_ne!(total, Size::ZERO); + } + HomogeneousAggregate::NoData => { + assert_eq!(total, Size::ZERO); + } + } + Ok(result) + } + } + Abi::Aggregate { sized: false } => Err(Heterogeneous), + } + } +} diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 620a051eeb7..6e1299944a0 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -11,6 +11,10 @@ use crate::{ Variants, WrappingRange, }; +mod ty; + +pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; + // A variant is absent if it's uninhabited and only has ZST fields. // Present uninhabited variants only require space for their fields, // but *not* an encoding of the discriminant (e.g., a tag value). diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs new file mode 100644 index 00000000000..c6812c4d4c0 --- /dev/null +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -0,0 +1,267 @@ +use std::fmt; +use std::ops::Deref; + +use Float::*; +use Primitive::*; +use rustc_data_structures::intern::Interned; +use rustc_macros::HashStable_Generic; + +// Explicitly import `Float` to avoid ambiguity with `Primitive::Float`. +use crate::{Float, *}; + +rustc_index::newtype_index! { + /// The *source-order* index of a field in a variant. + /// + /// This is how most code after type checking refers to fields, rather than + /// using names (as names have hygiene complications and more complex lookup). + /// + /// Particularly for `repr(Rust)` types, this may not be the same as *layout* order. + /// (It is for `repr(C)` `struct`s, however.) + /// + /// For example, in the following types, + /// ```rust + /// # enum Never {} + /// # #[repr(u16)] + /// enum Demo1 { + /// Variant0 { a: Never, b: i32 } = 100, + /// Variant1 { c: u8, d: u64 } = 10, + /// } + /// struct Demo2 { e: u8, f: u16, g: u8 } + /// ``` + /// `b` is `FieldIdx(1)` in `VariantIdx(0)`, + /// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and + /// `f` is `FieldIdx(1)` in `VariantIdx(0)`. + #[derive(HashStable_Generic)] + #[encodable] + #[orderable] + pub struct FieldIdx {} +} + +rustc_index::newtype_index! { + /// The *source-order* index of a variant in a type. + /// + /// For enums, these are always `0..variant_count`, regardless of any + /// custom discriminants that may have been defined, and including any + /// variants that may end up uninhabited due to field types. (Some of the + /// variants may not be present in a monomorphized ABI [`Variants`], but + /// those skipped variants are always counted when determining the *index*.) + /// + /// `struct`s, `tuples`, and `unions`s are considered to have a single variant + /// with variant index zero, aka [`FIRST_VARIANT`]. + #[derive(HashStable_Generic)] + #[encodable] + #[orderable] + pub struct VariantIdx { + /// Equivalent to `VariantIdx(0)`. + const FIRST_VARIANT = 0; + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)] +#[rustc_pass_by_value] +pub struct Layout<'a>(pub Interned<'a, LayoutS<FieldIdx, VariantIdx>>); + +impl<'a> fmt::Debug for Layout<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // See comment on `<LayoutS as Debug>::fmt` above. + self.0.0.fmt(f) + } +} + +impl<'a> Deref for Layout<'a> { + type Target = &'a LayoutS<FieldIdx, VariantIdx>; + fn deref(&self) -> &&'a LayoutS<FieldIdx, VariantIdx> { + &self.0.0 + } +} + +impl<'a> Layout<'a> { + pub fn fields(self) -> &'a FieldsShape<FieldIdx> { + &self.0.0.fields + } + + pub fn variants(self) -> &'a Variants<FieldIdx, VariantIdx> { + &self.0.0.variants + } + + pub fn abi(self) -> Abi { + self.0.0.abi + } + + pub fn largest_niche(self) -> Option<Niche> { + self.0.0.largest_niche + } + + pub fn align(self) -> AbiAndPrefAlign { + self.0.0.align + } + + pub fn size(self) -> Size { + self.0.0.size + } + + pub fn max_repr_align(self) -> Option<Align> { + self.0.0.max_repr_align + } + + pub fn unadjusted_abi_align(self) -> Align { + self.0.0.unadjusted_abi_align + } + + /// Whether the layout is from a type that implements [`std::marker::PointerLike`]. + /// + /// Currently, that means that the type is pointer-sized, pointer-aligned, + /// and has a initialized (non-union), scalar ABI. + pub fn is_pointer_like(self, data_layout: &TargetDataLayout) -> bool { + self.size() == data_layout.pointer_size + && self.align().abi == data_layout.pointer_align.abi + && matches!(self.abi(), Abi::Scalar(Scalar::Initialized { .. })) + } +} + +/// The layout of a type, alongside the type itself. +/// Provides various type traversal APIs (e.g., recursing into fields). +/// +/// Note that the layout is NOT guaranteed to always be identical +/// to that obtained from `layout_of(ty)`, as we need to produce +/// layouts for which Rust types do not exist, such as enum variants +/// or synthetic fields of enums (i.e., discriminants) and wide pointers. +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)] +pub struct TyAndLayout<'a, Ty> { + pub ty: Ty, + pub layout: Layout<'a>, +} + +impl<'a, Ty: fmt::Display> fmt::Debug for TyAndLayout<'a, Ty> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Print the type in a readable way, not its debug representation. + f.debug_struct("TyAndLayout") + .field("ty", &format_args!("{}", self.ty)) + .field("layout", &self.layout) + .finish() + } +} + +impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { + type Target = &'a LayoutS<FieldIdx, VariantIdx>; + fn deref(&self) -> &&'a LayoutS<FieldIdx, VariantIdx> { + &self.layout.0.0 + } +} + +/// Trait that needs to be implemented by the higher-level type representation +/// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality. +pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug { + fn ty_and_layout_for_variant( + this: TyAndLayout<'a, Self>, + cx: &C, + variant_index: VariantIdx, + ) -> TyAndLayout<'a, Self>; + fn ty_and_layout_field(this: TyAndLayout<'a, Self>, cx: &C, i: usize) -> TyAndLayout<'a, Self>; + fn ty_and_layout_pointee_info_at( + this: TyAndLayout<'a, Self>, + cx: &C, + offset: Size, + ) -> Option<PointeeInfo>; + fn is_adt(this: TyAndLayout<'a, Self>) -> bool; + fn is_never(this: TyAndLayout<'a, Self>) -> bool; + fn is_tuple(this: TyAndLayout<'a, Self>) -> bool; + fn is_unit(this: TyAndLayout<'a, Self>) -> bool; + fn is_transparent(this: TyAndLayout<'a, Self>) -> bool; +} + +impl<'a, Ty> TyAndLayout<'a, Ty> { + pub fn for_variant<C>(self, cx: &C, variant_index: VariantIdx) -> Self + where + Ty: TyAbiInterface<'a, C>, + { + Ty::ty_and_layout_for_variant(self, cx, variant_index) + } + + pub fn field<C>(self, cx: &C, i: usize) -> Self + where + Ty: TyAbiInterface<'a, C>, + { + Ty::ty_and_layout_field(self, cx, i) + } + + pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo> + where + Ty: TyAbiInterface<'a, C>, + { + Ty::ty_and_layout_pointee_info_at(self, cx, offset) + } + + pub fn is_single_fp_element<C>(self, cx: &C) -> bool + where + Ty: TyAbiInterface<'a, C>, + C: HasDataLayout, + { + match self.abi { + Abi::Scalar(scalar) => matches!(scalar.primitive(), Float(F32 | F64)), + Abi::Aggregate { .. } => { + if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 { + self.field(cx, 0).is_single_fp_element(cx) + } else { + false + } + } + _ => false, + } + } + + pub fn is_adt<C>(self) -> bool + where + Ty: TyAbiInterface<'a, C>, + { + Ty::is_adt(self) + } + + pub fn is_never<C>(self) -> bool + where + Ty: TyAbiInterface<'a, C>, + { + Ty::is_never(self) + } + + pub fn is_tuple<C>(self) -> bool + where + Ty: TyAbiInterface<'a, C>, + { + Ty::is_tuple(self) + } + + pub fn is_unit<C>(self) -> bool + where + Ty: TyAbiInterface<'a, C>, + { + Ty::is_unit(self) + } + + pub fn is_transparent<C>(self) -> bool + where + Ty: TyAbiInterface<'a, C>, + { + Ty::is_transparent(self) + } + + /// Finds the one field that is not a 1-ZST. + /// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields. + pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)> + where + Ty: TyAbiInterface<'a, C> + Copy, + { + let mut found = None; + for field_idx in 0..self.fields.count() { + let field = self.field(cx, field_idx); + if field.is_1zst() { + continue; + } + if found.is_some() { + // More than one non-1-ZST field. + return None; + } + found = Some((field_idx, field)); + } + found + } +} diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index fa7c98a7fa0..84d756b6d51 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1,6 +1,7 @@ // tidy-alphabetical-start #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", doc(rust_logo))] +#![cfg_attr(feature = "nightly", feature(rustc_attrs))] #![cfg_attr(feature = "nightly", feature(rustdoc_internals))] #![cfg_attr(feature = "nightly", feature(step_trait))] #![warn(unreachable_pub)] @@ -22,11 +23,16 @@ use rustc_macros::HashStable_Generic; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_Generic, Encodable_Generic}; +mod callconv; mod layout; #[cfg(test)] mod tests; -pub use layout::{LayoutCalculator, LayoutCalculatorError}; +pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind}; +pub use layout::{ + FIRST_VARIANT, FieldIdx, Layout, LayoutCalculator, LayoutCalculatorError, TyAbiInterface, + TyAndLayout, VariantIdx, +}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro |
