#![cfg_attr(feature = "nightly", allow(internal_features))]#![cfg_attr(feature = "nightly", feature(step_trait))]
#[macro_use]
use alloc::vec::Vec;
use alloc::string::String;
use alloc::string::ToString;
use core::cmp::min;
use core::fmt;
use core::num::{NonZero, ParseIntError};
use core::ops::{Add, AddAssign, Deref, Mul, Sub};
use core::range::RangeInclusive;
use core::str::FromStr;
use bitflags::bitflags;
#[cfg(feature = "nightly")]
use crate::rustc_data_structures::stable_hash::StableOrd;
#[cfg(feature = "nightly")]
use crate::rustc_error_messages::{DiagArgValue, IntoDiagArg};
#[cfg(feature = "nightly")]
use crate::rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, msg};
use crate::rustc_hashes::Hash64;
use crate::rustc_index::{Idx, IndexSlice, IndexVec};
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash};
#[cfg(feature = "nightly")]
use crate::rustc_span::{Symbol, sym};
mod callconv;
mod canon_abi;
mod extern_abi;
mod layout;
#[cfg(test)]
mod tests;
mod wrapping_range;
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
#[cfg(feature = "nightly")]
pub use extern_abi::CVariadicStatus;
pub use extern_abi::{ExternAbi, all_names};
pub use layout::{FIRST_VARIANT, FieldIdx, LayoutCalculator, LayoutCalculatorError, VariantIdx};
#[cfg(feature = "nightly")]
pub use layout::{Layout, TyAbiInterface, TyAndLayout};
pub use wrapping_range::WrappingRange;
#[derive(Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))]
pub struct ReprFlags(u8);
bitflags! {
impl ReprFlags: u8 {
const IS_C = 1 << 0;
const IS_SIMD = 1 << 1;
const IS_TRANSPARENT = 1 << 2;
const IS_LINEAR = 1 << 3;
const RANDOMIZE_LAYOUT = 1 << 4;
const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5;
const IS_SCALABLE = 1 << 6;
const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits()
| ReprFlags::IS_SIMD.bits()
| ReprFlags::IS_SCALABLE.bits()
| ReprFlags::IS_LINEAR.bits();
const ABI_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits();
}
}
impl core::fmt::Debug for ReprFlags {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
bitflags::parser::to_writer(self, f)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))]
pub enum IntegerType {
Pointer(bool),
Fixed(Integer, bool),
}
impl IntegerType {
pub fn is_signed(&self) -> bool {
match self {
IntegerType::Pointer(b) => *b,
IntegerType::Fixed(_, b) => *b,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))]
pub enum ScalableElt {
ElementCount(u16),
Container,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))]
pub struct ReprOptions {
pub int: Option<IntegerType>,
pub align: Option<Align>,
pub pack: Option<Align>,
pub flags: ReprFlags,
pub scalable: Option<ScalableElt>,
pub field_shuffle_seed: Hash64,
}
impl ReprOptions {
#[inline]
pub fn simd(&self) -> bool {
self.flags.contains(ReprFlags::IS_SIMD)
}
#[inline]
pub fn scalable(&self) -> bool {
self.flags.contains(ReprFlags::IS_SCALABLE)
}
#[inline]
pub fn c(&self) -> bool {
self.flags.contains(ReprFlags::IS_C)
}
#[inline]
pub fn rust(&self) -> bool {
!self.c() & !self.simd() & !self.scalable() & !self.transparent()
}
#[inline]
pub fn packed(&self) -> bool {
self.pack.is_some()
}
#[inline]
pub fn transparent(&self) -> bool {
self.flags.contains(ReprFlags::IS_TRANSPARENT)
}
#[inline]
pub fn linear(&self) -> bool {
self.flags.contains(ReprFlags::IS_LINEAR)
}
pub fn discr_type(&self) -> IntegerType {
self.int.unwrap_or(IntegerType::Pointer(true))
}
pub fn inhibit_enum_layout_opt(&self) -> bool {
self.c() || self.int.is_some()
}
pub fn inhibit_newtype_abi_optimization(&self) -> bool {
self.flags.intersects(ReprFlags::ABI_UNOPTIMIZABLE)
}
pub fn inhibit_struct_field_reordering(&self) -> bool {
self.flags.intersects(ReprFlags::FIELD_ORDER_UNOPTIMIZABLE) || self.int.is_some()
}
pub fn can_randomize_type_layout(&self) -> bool {
!self.inhibit_struct_field_reordering() && self.flags.contains(ReprFlags::RANDOMIZE_LAYOUT)
}
pub fn inhibits_union_abi_opt(&self) -> bool {
self.c()
}
}
pub const MAX_SIMD_LANES: u16 = 1 << 0xF;
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))]
pub struct BackendLaneCount(NonZero<u16>);
impl BackendLaneCount {
pub fn new<N>(count: u64) -> Result<Self, LayoutCalculatorError<N>> {
let Ok(count @ ..=MAX_SIMD_LANES) = u16::try_from(count) else {
return Err(LayoutCalculatorError::OversizedSimdType {
max_lanes: crate::rustc_abi::MAX_SIMD_LANES.into(),
});
};
if let Some(count) = NonZero::new(count) {
Ok(BackendLaneCount(count))
} else {
Err(LayoutCalculatorError::ZeroLengthSimdType)
}
}
#[inline]
pub fn is_power_of_two(self) -> bool {
self.0.is_power_of_two()
}
#[inline]
pub fn as_u64(self) -> u64 {
self.0.get().into()
}
#[inline]
pub fn as_u32(self) -> u32 {
self.0.get().into()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct PointerSpec {
pointer_size: Size,
pointer_align: Align,
pointer_offset: Size,
_is_fat: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub struct TargetDataLayout {
pub endian: Endian,
pub i1_align: Align,
pub i8_align: Align,
pub i16_align: Align,
pub i32_align: Align,
pub i64_align: Align,
pub i128_align: Align,
pub f16_align: Align,
pub f32_align: Align,
pub f64_align: Align,
pub f128_align: Align,
pub aggregate_align: Align,
pub vector_align: Vec<(Size, Align)>,
pub default_address_space: AddressSpace,
pub default_address_space_pointer_spec: PointerSpec,
address_space_info: Vec<(AddressSpace, PointerSpec)>,
pub instruction_address_space: AddressSpace,
pub c_enum_min_size: Integer,
}
impl Default for TargetDataLayout {
fn default() -> TargetDataLayout {
let align = |bits| Align::from_bits(bits).unwrap();
TargetDataLayout {
endian: Endian::Big,
i1_align: align(8),
i8_align: align(8),
i16_align: align(16),
i32_align: align(32),
i64_align: align(32),
i128_align: align(32),
f16_align: align(16),
f32_align: align(32),
f64_align: align(64),
f128_align: align(128),
aggregate_align: align(8),
vector_align: vec![
(Size::from_bits(64), align(64)),
(Size::from_bits(128), align(128)),
],
default_address_space: AddressSpace::ZERO,
default_address_space_pointer_spec: PointerSpec {
pointer_size: Size::from_bits(64),
pointer_align: align(64),
pointer_offset: Size::from_bits(64),
_is_fat: false,
},
address_space_info: vec![],
instruction_address_space: AddressSpace::ZERO,
c_enum_min_size: Integer::I32,
}
}
}
pub enum TargetDataLayoutError<'a> {
InvalidAddressSpace { addr_space: &'a str, cause: &'a str, err: ParseIntError },
InvalidBits { kind: &'a str, bit: &'a str, cause: &'a str, err: ParseIntError },
MissingAlignment { cause: &'a str },
InvalidAlignment { cause: &'a str, err: AlignFromBytesError },
InconsistentTargetArchitecture { dl: &'a str, target: &'a str },
InconsistentTargetPointerWidth { pointer_size: u64, target: u16 },
InvalidBitsSize { err: String },
UnknownPointerSpecification { err: String },
}
#[cfg(feature = "nightly")]
impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetDataLayoutError<'_> {
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
match self {
TargetDataLayoutError::InvalidAddressSpace { addr_space, err, cause } => {
Diag::new(dcx, level, msg!("invalid address space `{$addr_space}` for `{$cause}` in \"data-layout\": {$err}"))
.with_arg("addr_space", addr_space)
.with_arg("cause", cause)
.with_arg("err", err)
}
TargetDataLayoutError::InvalidBits { kind, bit, cause, err } => {
Diag::new(dcx, level, msg!("invalid {$kind} `{$bit}` for `{$cause}` in \"data-layout\": {$err}"))
.with_arg("kind", kind)
.with_arg("bit", bit)
.with_arg("cause", cause)
.with_arg("err", err)
}
TargetDataLayoutError::MissingAlignment { cause } => {
Diag::new(dcx, level, msg!("missing alignment for `{$cause}` in \"data-layout\""))
.with_arg("cause", cause)
}
TargetDataLayoutError::InvalidAlignment { cause, err } => {
Diag::new(dcx, level, msg!("invalid alignment for `{$cause}` in \"data-layout\": {$err}"))
.with_arg("cause", cause)
.with_arg("err", err.to_string())
}
TargetDataLayoutError::InconsistentTargetArchitecture { dl, target } => {
Diag::new(dcx, level, msg!("inconsistent target specification: \"data-layout\" claims architecture is {$dl}-endian, while \"target-endian\" is `{$target}`"))
.with_arg("dl", dl).with_arg("target", target)
}
TargetDataLayoutError::InconsistentTargetPointerWidth { pointer_size, target } => {
Diag::new(dcx, level, msg!("inconsistent target specification: \"data-layout\" claims pointers are {$pointer_size}-bit, while \"target-pointer-width\" is `{$target}`"))
.with_arg("pointer_size", pointer_size).with_arg("target", target)
}
TargetDataLayoutError::InvalidBitsSize { err } => {
Diag::new(dcx, level, msg!("{$err}")).with_arg("err", err)
}
TargetDataLayoutError::UnknownPointerSpecification { err } => {
Diag::new(dcx, level, msg!("unknown pointer specification `{$err}` in datalayout string"))
.with_arg("err", err)
}
}
}
}
impl TargetDataLayout {
pub fn parse_from_llvm_datalayout_string<'a>(
input: &'a str,
default_address_space: AddressSpace,
) -> Result<TargetDataLayout, TargetDataLayoutError<'a>> {
let parse_address_space = |s: &'a str, cause: &'a str| {
s.parse::<u32>().map(AddressSpace).map_err(|err| {
TargetDataLayoutError::InvalidAddressSpace { addr_space: s, cause, err }
})
};
let parse_bits = |s: &'a str, kind: &'a str, cause: &'a str| {
s.parse::<u64>().map_err(|err| TargetDataLayoutError::InvalidBits {
kind,
bit: s,
cause,
err,
})
};
let parse_size =
|s: &'a str, cause: &'a str| parse_bits(s, "size", cause).map(Size::from_bits);
let parse_align_str = |s: &'a str, cause: &'a str| {
let align_from_bits = |bits| {
Align::from_bits(bits)
.map_err(|err| TargetDataLayoutError::InvalidAlignment { cause, err })
};
let abi = parse_bits(s, "alignment", cause)?;
Ok(align_from_bits(abi)?)
};
let parse_align_seq = |s: &[&'a str], cause: &'a str| {
if s.is_empty() {
return Err(TargetDataLayoutError::MissingAlignment { cause });
}
parse_align_str(s[0], cause)
};
let mut dl = TargetDataLayout::default();
dl.default_address_space = default_address_space;
let mut i128_align_src = 64;
for spec in input.split('-') {
let spec_parts = spec.split(':').collect::<Vec<_>>();
match &*spec_parts {
["e"] => dl.endian = Endian::Little,
["E"] => dl.endian = Endian::Big,
[p] if p.starts_with('P') => {
dl.instruction_address_space = parse_address_space(&p[1..], "P")?
}
["a", a @ ..] => dl.aggregate_align = parse_align_seq(a, "a")?,
["f16", a @ ..] => dl.f16_align = parse_align_seq(a, "f16")?,
["f32", a @ ..] => dl.f32_align = parse_align_seq(a, "f32")?,
["f64", a @ ..] => dl.f64_align = parse_align_seq(a, "f64")?,
["f128", a @ ..] => dl.f128_align = parse_align_seq(a, "f128")?,
[p, s, a @ ..] if p.starts_with("p") => {
let mut p = p.strip_prefix('p').unwrap();
let mut _is_fat = false;
if p.starts_with('f') {
p = p.strip_prefix('f').unwrap();
_is_fat = true;
}
if p.starts_with(char::is_alphabetic) {
return Err(TargetDataLayoutError::UnknownPointerSpecification {
err: p.to_string(),
});
}
let addr_space = if !p.is_empty() {
parse_address_space(p, "p-")?
} else {
AddressSpace::ZERO
};
let pointer_size = parse_size(s, "p-")?;
let pointer_align = parse_align_seq(a, "p-")?;
let info = PointerSpec {
pointer_offset: pointer_size,
pointer_size,
pointer_align,
_is_fat,
};
if addr_space == default_address_space {
dl.default_address_space_pointer_spec = info;
} else {
match dl.address_space_info.iter_mut().find(|(a, _)| *a == addr_space) {
Some(e) => e.1 = info,
None => {
dl.address_space_info.push((addr_space, info));
}
}
}
}
[p, s, a, _pr, i] if p.starts_with("p") => {
let mut p = p.strip_prefix('p').unwrap();
let mut _is_fat = false;
if p.starts_with('f') {
p = p.strip_prefix('f').unwrap();
_is_fat = true;
}
if p.starts_with(char::is_alphabetic) {
return Err(TargetDataLayoutError::UnknownPointerSpecification {
err: p.to_string(),
});
}
let addr_space = if !p.is_empty() {
parse_address_space(p, "p")?
} else {
AddressSpace::ZERO
};
let info = PointerSpec {
pointer_size: parse_size(s, "p-")?,
pointer_align: parse_align_str(a, "p-")?,
pointer_offset: parse_size(i, "p-")?,
_is_fat,
};
if addr_space == default_address_space {
dl.default_address_space_pointer_spec = info;
} else {
match dl.address_space_info.iter_mut().find(|(a, _)| *a == addr_space) {
Some(e) => e.1 = info,
None => {
dl.address_space_info.push((addr_space, info));
}
}
}
}
[s, a @ ..] if s.starts_with('i') => {
let Ok(bits) = s[1..].parse::<u64>() else {
parse_size(&s[1..], "i")?; continue;
};
let a = parse_align_seq(a, s)?;
match bits {
1 => dl.i1_align = a,
8 => dl.i8_align = a,
16 => dl.i16_align = a,
32 => dl.i32_align = a,
64 => dl.i64_align = a,
_ => {}
}
if bits >= i128_align_src && bits <= 128 {
i128_align_src = bits;
dl.i128_align = a;
}
}
[s, a @ ..] if s.starts_with('v') => {
let v_size = parse_size(&s[1..], "v")?;
let a = parse_align_seq(a, s)?;
if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
v.1 = a;
continue;
}
dl.vector_align.push((v_size, a));
}
_ => {} }
}
if (dl.instruction_address_space != dl.default_address_space)
&& dl
.address_space_info
.iter()
.find(|(a, _)| *a == dl.instruction_address_space)
.is_none()
{
dl.address_space_info.push((
dl.instruction_address_space,
dl.default_address_space_pointer_spec.clone(),
));
}
Ok(dl)
}
#[inline]
pub fn obj_size_bound(&self) -> u64 {
match self.pointer_size().bits() {
16 => 1 << 15,
32 => 1 << 31,
64 => 1 << 61,
bits => panic!("obj_size_bound: unknown pointer bit size {bits}"),
}
}
#[inline]
pub fn obj_size_bound_in(&self, address_space: AddressSpace) -> u64 {
match self.pointer_size_in(address_space).bits() {
16 => 1 << 15,
32 => 1 << 31,
64 => 1 << 61,
bits => panic!("obj_size_bound: unknown pointer bit size {bits}"),
}
}
#[inline]
pub fn ptr_sized_integer(&self) -> Integer {
use Integer::*;
match self.pointer_offset().bits() {
16 => I16,
32 => I32,
64 => I64,
bits => panic!("ptr_sized_integer: unknown pointer bit size {bits}"),
}
}
#[inline]
pub fn ptr_sized_integer_in(&self, address_space: AddressSpace) -> Integer {
use Integer::*;
match self.pointer_offset_in(address_space).bits() {
16 => I16,
32 => I32,
64 => I64,
bits => panic!("ptr_sized_integer: unknown pointer bit size {bits}"),
}
}
#[inline]
fn c_vector_align(&self, vec_size: Size) -> Option<Align> {
self.vector_align
.iter()
.find(|(size, _align)| *size == vec_size)
.map(|(_size, align)| *align)
}
#[inline]
pub fn rust_vector_align(&self, vec_size: Size) -> Align {
self.c_vector_align(vec_size)
.unwrap_or(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap())
}
#[inline]
pub fn pointer_size(&self) -> Size {
self.default_address_space_pointer_spec.pointer_size
}
#[inline]
pub fn pointer_size_in(&self, c: AddressSpace) -> Size {
if c == self.default_address_space {
return self.default_address_space_pointer_spec.pointer_size;
}
if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) {
e.1.pointer_size
} else {
panic!("Use of unknown address space {c:?}");
}
}
#[inline]
pub fn pointer_offset(&self) -> Size {
self.default_address_space_pointer_spec.pointer_offset
}
#[inline]
pub fn pointer_offset_in(&self, c: AddressSpace) -> Size {
if c == self.default_address_space {
return self.default_address_space_pointer_spec.pointer_offset;
}
if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) {
e.1.pointer_offset
} else {
panic!("Use of unknown address space {c:?}");
}
}
#[inline]
pub fn pointer_align(&self) -> AbiAlign {
AbiAlign::new(self.default_address_space_pointer_spec.pointer_align)
}
#[inline]
pub fn pointer_align_in(&self, c: AddressSpace) -> AbiAlign {
AbiAlign::new(if c == self.default_address_space {
self.default_address_space_pointer_spec.pointer_align
} else if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) {
e.1.pointer_align
} else {
panic!("Use of unknown address space {c:?}");
})
}
}
pub trait HasDataLayout {
fn data_layout(&self) -> &TargetDataLayout;
}
impl HasDataLayout for TargetDataLayout {
#[inline]
fn data_layout(&self) -> &TargetDataLayout {
self
}
}
impl HasDataLayout for &TargetDataLayout {
#[inline]
fn data_layout(&self) -> &TargetDataLayout {
(**self).data_layout()
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Endian {
Little,
Big,
}
impl Endian {
pub fn as_str(&self) -> &'static str {
match self {
Self::Little => "little",
Self::Big => "big",
}
}
#[cfg(feature = "nightly")]
pub fn desc_symbol(&self) -> Symbol {
match self {
Self::Little => sym::little,
Self::Big => sym::big,
}
}
}
impl fmt::Debug for Endian {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Endian {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"little" => Ok(Self::Little),
"big" => Ok(Self::Big),
_ => Err(format!(r#"unknown endian: "{s}""#)),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))]
pub struct Size {
raw: u64,
}
#[cfg(feature = "nightly")]
impl StableOrd for Size {
const CAN_USE_UNSTABLE_SORT: bool = true;
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl fmt::Debug for Size {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Size({} bytes)", self.bytes())
}
}
impl Size {
pub const ZERO: Size = Size { raw: 0 };
pub fn from_bits(bits: impl TryInto<u64>) -> Size {
let bits = bits.try_into().ok().unwrap();
Size { raw: bits.div_ceil(8) }
}
#[inline]
pub fn from_bytes(bytes: impl TryInto<u64>) -> Size {
let bytes: u64 = bytes.try_into().ok().unwrap();
Size { raw: bytes }
}
#[inline]
pub fn bytes(self) -> u64 {
self.raw
}
#[inline]
pub fn bytes_usize(self) -> usize {
self.bytes().try_into().unwrap()
}
#[inline]
pub fn bits(self) -> u64 {
#[cold]
fn overflow(bytes: u64) -> ! {
panic!("Size::bits: {bytes} bytes in bits doesn't fit in u64")
}
self.bytes().checked_mul(8).unwrap_or_else(|| overflow(self.bytes()))
}
#[inline]
pub fn bits_usize(self) -> usize {
self.bits().try_into().unwrap()
}
#[inline]
pub fn align_to(self, align: Align) -> Size {
let mask = align.bytes() - 1;
Size::from_bytes((self.bytes() + mask) & !mask)
}
#[inline]
pub fn is_aligned(self, align: Align) -> bool {
let mask = align.bytes() - 1;
self.bytes() & mask == 0
}
#[inline]
pub fn checked_add<C: HasDataLayout>(self, offset: Size, cx: &C) -> Option<Size> {
let dl = cx.data_layout();
let bytes = self.bytes().checked_add(offset.bytes())?;
if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None }
}
#[inline]
pub fn checked_mul<C: HasDataLayout>(self, count: u64, cx: &C) -> Option<Size> {
let dl = cx.data_layout();
let bytes = self.bytes().checked_mul(count)?;
if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None }
}
#[inline]
pub fn sign_extend(self, value: u128) -> i128 {
let size = self.bits();
if size == 0 {
return 0;
}
let shift = 128 - size;
((value << shift) as i128) >> shift
}
#[inline]
pub fn truncate(self, value: u128) -> u128 {
let size = self.bits();
if size == 0 {
return 0;
}
let shift = 128 - size;
(value << shift) >> shift
}
#[inline]
pub fn signed_int_min(&self) -> i128 {
self.sign_extend(1_u128 << (self.bits() - 1))
}
#[inline]
pub fn signed_int_max(&self) -> i128 {
i128::MAX >> (128 - self.bits())
}
#[inline]
pub fn unsigned_int_max(&self) -> u128 {
u128::MAX >> (128 - self.bits())
}
}
impl Add for Size {
type Output = Size;
#[inline]
fn add(self, other: Size) -> Size {
Size::from_bytes(self.bytes().checked_add(other.bytes()).unwrap_or_else(|| {
panic!("Size::add: {} + {} doesn't fit in u64", self.bytes(), other.bytes())
}))
}
}
impl Sub for Size {
type Output = Size;
#[inline]
fn sub(self, other: Size) -> Size {
Size::from_bytes(self.bytes().checked_sub(other.bytes()).unwrap_or_else(|| {
panic!("Size::sub: {} - {} would result in negative size", self.bytes(), other.bytes())
}))
}
}
impl Mul<Size> for u64 {
type Output = Size;
#[inline]
fn mul(self, size: Size) -> Size {
size * self
}
}
impl Mul<u64> for Size {
type Output = Size;
#[inline]
fn mul(self, count: u64) -> Size {
match self.bytes().checked_mul(count) {
Some(bytes) => Size::from_bytes(bytes),
None => panic!("Size::mul: {} * {} doesn't fit in u64", self.bytes(), count),
}
}
}
impl AddAssign for Size {
#[inline]
fn add_assign(&mut self, other: Size) {
*self = *self + other;
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))]
pub struct Align {
pow2: u8,
}
impl fmt::Debug for Align {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Align({} bytes)", self.bytes())
}
}
#[derive(Clone, Copy)]
pub enum AlignFromBytesError {
NotPowerOfTwo(u64),
TooLarge(u64),
}
impl fmt::Debug for AlignFromBytesError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for AlignFromBytesError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AlignFromBytesError::NotPowerOfTwo(align) => write!(f, "{align} is not a power of 2"),
AlignFromBytesError::TooLarge(align) => write!(f, "{align} is too large"),
}
}
}
impl Align {
pub const ONE: Align = Align { pow2: 0 };
pub const EIGHT: Align = Align { pow2: 3 };
pub const MAX: Align = Align { pow2: 29 };
#[inline]
pub fn max_for_target(tdl: &TargetDataLayout) -> Align {
let pointer_bits = u8::try_from(tdl.pointer_size().bits()).unwrap();
min(Align { pow2: pointer_bits - 1 }, Align::MAX)
}
#[inline]
pub fn from_bits(bits: u64) -> Result<Align, AlignFromBytesError> {
Align::from_bytes(Size::from_bits(bits).bytes())
}
#[inline]
pub const fn from_bytes(align: u64) -> Result<Align, AlignFromBytesError> {
if align == 0 {
return Ok(Align::ONE);
}
#[cold]
const fn not_power_of_2(align: u64) -> AlignFromBytesError {
AlignFromBytesError::NotPowerOfTwo(align)
}
#[cold]
const fn too_large(align: u64) -> AlignFromBytesError {
AlignFromBytesError::TooLarge(align)
}
let tz = align.trailing_zeros();
if align != (1 << tz) {
return Err(not_power_of_2(align));
}
let pow2 = tz as u8;
if pow2 > Self::MAX.pow2 {
return Err(too_large(align));
}
Ok(Align { pow2 })
}
#[inline]
pub const fn bytes(self) -> u64 {
1 << self.pow2
}
#[inline]
pub fn bytes_usize(self) -> usize {
self.bytes().try_into().unwrap()
}
#[inline]
pub const fn bits(self) -> u64 {
self.bytes() * 8
}
#[inline]
pub fn bits_usize(self) -> usize {
self.bits().try_into().unwrap()
}
#[inline]
pub fn max_aligned_factor(size: Size) -> Align {
Align { pow2: size.bytes().trailing_zeros() as u8 }
}
#[inline]
pub fn restrict_for_offset(self, size: Size) -> Align {
self.min(Align::max_aligned_factor(size))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub struct AbiAlign {
pub abi: Align,
}
impl AbiAlign {
#[inline]
pub fn new(align: Align) -> AbiAlign {
AbiAlign { abi: align }
}
#[inline]
pub fn min(self, other: AbiAlign) -> AbiAlign {
AbiAlign { abi: self.abi.min(other.abi) }
}
#[inline]
pub fn max(self, other: AbiAlign) -> AbiAlign {
AbiAlign { abi: self.abi.max(other.abi) }
}
}
impl Deref for AbiAlign {
type Target = Align;
fn deref(&self) -> &Self::Target {
&self.abi
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash))]
pub enum Integer {
I8,
I16,
I32,
I64,
I128,
}
impl Integer {
pub fn int_ty_str(self) -> &'static str {
use Integer::*;
match self {
I8 => "i8",
I16 => "i16",
I32 => "i32",
I64 => "i64",
I128 => "i128",
}
}
pub fn uint_ty_str(self) -> &'static str {
use Integer::*;
match self {
I8 => "u8",
I16 => "u16",
I32 => "u32",
I64 => "u64",
I128 => "u128",
}
}
#[inline]
pub fn size(self) -> Size {
use Integer::*;
match self {
I8 => Size::from_bytes(1),
I16 => Size::from_bytes(2),
I32 => Size::from_bytes(4),
I64 => Size::from_bytes(8),
I128 => Size::from_bytes(16),
}
}
pub fn from_attr<C: HasDataLayout>(cx: &C, ity: IntegerType) -> Integer {
let dl = cx.data_layout();
match ity {
IntegerType::Pointer(_) => dl.ptr_sized_integer(),
IntegerType::Fixed(x, _) => x,
}
}
pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAlign {
use Integer::*;
let dl = cx.data_layout();
AbiAlign::new(match self {
I8 => dl.i8_align,
I16 => dl.i16_align,
I32 => dl.i32_align,
I64 => dl.i64_align,
I128 => dl.i128_align,
})
}
#[inline]
pub fn signed_max(self) -> i128 {
use Integer::*;
match self {
I8 => i8::MAX as i128,
I16 => i16::MAX as i128,
I32 => i32::MAX as i128,
I64 => i64::MAX as i128,
I128 => i128::MAX,
}
}
#[inline]
pub fn signed_min(self) -> i128 {
use Integer::*;
match self {
I8 => i8::MIN as i128,
I16 => i16::MIN as i128,
I32 => i32::MIN as i128,
I64 => i64::MIN as i128,
I128 => i128::MIN,
}
}
#[inline]
pub fn fit_signed(x: i128) -> Integer {
use Integer::*;
match x {
-0x0000_0000_0000_0080..=0x0000_0000_0000_007f => I8,
-0x0000_0000_0000_8000..=0x0000_0000_0000_7fff => I16,
-0x0000_0000_8000_0000..=0x0000_0000_7fff_ffff => I32,
-0x8000_0000_0000_0000..=0x7fff_ffff_ffff_ffff => I64,
_ => I128,
}
}
#[inline]
pub fn fit_unsigned(x: u128) -> Integer {
use Integer::*;
match x {
0..=0x0000_0000_0000_00ff => I8,
0..=0x0000_0000_0000_ffff => I16,
0..=0x0000_0000_ffff_ffff => I32,
0..=0xffff_ffff_ffff_ffff => I64,
_ => I128,
}
}
pub fn for_align<C: HasDataLayout>(cx: &C, wanted: Align) -> Option<Integer> {
use Integer::*;
let dl = cx.data_layout();
[I8, I16, I32, I64, I128].into_iter().find(|&candidate| {
wanted == candidate.align(dl).abi && wanted.bytes() == candidate.size().bytes()
})
}
pub fn approximate_align<C: HasDataLayout>(cx: &C, wanted: Align) -> Integer {
use Integer::*;
let dl = cx.data_layout();
for candidate in [I64, I32, I16] {
if wanted >= candidate.align(dl).abi && wanted.bytes() >= candidate.size().bytes() {
return candidate;
}
}
I8
}
#[inline]
pub fn from_size(size: Size) -> Result<Self, String> {
match size.bits() {
8 => Ok(Integer::I8),
16 => Ok(Integer::I16),
32 => Ok(Integer::I32),
64 => Ok(Integer::I64),
128 => Ok(Integer::I128),
_ => Err(format!("rust does not support integers with {} bits", size.bits())),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub enum Float {
F16,
F32,
F64,
F128,
}
impl Float {
pub fn size(self) -> Size {
use Float::*;
match self {
F16 => Size::from_bits(16),
F32 => Size::from_bits(32),
F64 => Size::from_bits(64),
F128 => Size::from_bits(128),
}
}
pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAlign {
use Float::*;
let dl = cx.data_layout();
AbiAlign::new(match self {
F16 => dl.f16_align,
F32 => dl.f32_align,
F64 => dl.f64_align,
F128 => dl.f128_align,
})
}
pub fn ty_str(self) -> &'static str {
use Float::*;
match self {
F16 => "f16",
F32 => "f32",
F64 => "f64",
F128 => "f128",
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub enum Primitive {
Int(Integer, bool),
Float(Float),
Pointer(AddressSpace),
}
impl fmt::Debug for Primitive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match *self {
Primitive::Int(integer, is_signed) => {
if is_signed {
integer.int_ty_str()
} else {
integer.uint_ty_str()
}
}
Primitive::Float(float) => float.ty_str(),
Primitive::Pointer(addr_space) => {
if addr_space == AddressSpace::ZERO {
"pointer"
} else {
return write!(f, "pointer({addr_space:?})");
}
}
};
f.write_str(name)
}
}
impl Primitive {
pub fn size<C: HasDataLayout>(self, cx: &C) -> Size {
use Primitive::*;
let dl = cx.data_layout();
match self {
Int(i, _) => i.size(),
Float(f) => f.size(),
Pointer(a) => dl.pointer_size_in(a),
}
}
pub fn default_align<C: HasDataLayout>(self, cx: &C) -> AbiAlign {
use Primitive::*;
let dl = cx.data_layout();
match self {
Int(i, _) => i.align(dl),
Float(f) => f.align(dl),
Pointer(a) => dl.pointer_align_in(a),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub enum Scalar {
Initialized {
value: Primitive,
valid_range: WrappingRange,
},
Union {
value: Primitive,
},
}
impl fmt::Debug for Scalar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Scalar::Initialized { value, valid_range } => {
let (size, is_signed) = match *value {
Primitive::Int(integer, is_signed) => (integer.size(), is_signed),
Primitive::Float(float) => (float.size(), false),
Primitive::Pointer(_) => (Size::from_bits(128), false),
};
write!(f, "{value:?} is {:?}", valid_range.debug_as(size, is_signed))
}
Scalar::Union { value } => {
write!(f, "union {value:?}")
}
}
}
}
impl Scalar {
#[inline]
pub fn is_bool(&self) -> bool {
use Integer::*;
matches!(
self,
Scalar::Initialized {
value: Primitive::Int(I8, false),
valid_range: WrappingRange { start: 0, end: 1 }
}
)
}
pub fn primitive(&self) -> Primitive {
match *self {
Scalar::Initialized { value, .. } | Scalar::Union { value } => value,
}
}
pub fn default_align(self, cx: &impl HasDataLayout) -> AbiAlign {
self.primitive().default_align(cx)
}
pub fn size(self, cx: &impl HasDataLayout) -> Size {
self.primitive().size(cx)
}
#[inline]
pub fn to_union(&self) -> Self {
Self::Union { value: self.primitive() }
}
#[inline]
pub fn valid_range(&self, cx: &impl HasDataLayout) -> WrappingRange {
match *self {
Scalar::Initialized { valid_range, .. } => valid_range,
Scalar::Union { value } => WrappingRange::full(value.size(cx)),
}
}
#[inline]
pub fn valid_range_mut(&mut self) -> &mut WrappingRange {
match self {
Scalar::Initialized { valid_range, .. } => valid_range,
Scalar::Union { .. } => panic!("cannot change the valid range of a union"),
}
}
#[inline]
pub fn is_always_valid<C: HasDataLayout>(&self, cx: &C) -> bool {
match *self {
Scalar::Initialized { valid_range, .. } => valid_range.is_full_for(self.size(cx)),
Scalar::Union { .. } => true,
}
}
#[inline]
pub fn is_uninit_valid(&self) -> bool {
match *self {
Scalar::Initialized { .. } => false,
Scalar::Union { .. } => true,
}
}
#[inline]
pub fn is_signed(&self) -> bool {
match self.primitive() {
Primitive::Int(_, signed) => signed,
_ => false,
}
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub enum FieldsShape<FieldIdx: Idx> {
Primitive,
Union(NonZero<usize>),
Array { stride: Size, count: u64 },
Arbitrary {
offsets: IndexVec<FieldIdx, Size>,
in_memory_order: IndexVec<u32, FieldIdx>,
},
}
impl<FieldIdx: Idx> FieldsShape<FieldIdx> {
#[inline]
pub fn count(&self) -> usize {
match *self {
FieldsShape::Primitive => 0,
FieldsShape::Union(count) => count.get(),
FieldsShape::Array { count, .. } => count.try_into().unwrap(),
FieldsShape::Arbitrary { ref offsets, .. } => offsets.len(),
}
}
#[inline]
pub fn offset(&self, i: usize) -> Size {
match *self {
FieldsShape::Primitive => {
unreachable!("FieldsShape::offset: `Primitive`s have no fields")
}
FieldsShape::Union(count) => {
assert!(i < count.get(), "tried to access field {i} of union with {count} fields");
Size::ZERO
}
FieldsShape::Array { stride, count } => {
let i = u64::try_from(i).unwrap();
assert!(i < count, "tried to access field {i} of array with {count} fields");
stride * i
}
FieldsShape::Arbitrary { ref offsets, .. } => offsets[FieldIdx::new(i)],
}
}
#[inline]
pub fn index_by_increasing_offset(&self) -> impl ExactSizeIterator<Item = usize> {
let pseudofield_count = if let FieldsShape::Primitive = self { 1 } else { self.count() };
(0..pseudofield_count).map(move |i| match self {
FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i,
FieldsShape::Arbitrary { in_memory_order, .. } => in_memory_order[i as u32].index(),
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub struct AddressSpace(pub u32);
impl AddressSpace {
pub const ZERO: Self = AddressSpace(0);
pub const GPU_WORKGROUP: Self = AddressSpace(3);
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub struct NumScalableVectors(pub u8);
impl NumScalableVectors {
pub fn for_non_tuple() -> Self {
NumScalableVectors(1)
}
pub fn from_field_count(count: usize) -> Option<Self> {
match count {
2..8 => Some(NumScalableVectors(count as u8)),
_ => None,
}
}
}
#[cfg(feature = "nightly")]
impl IntoDiagArg for NumScalableVectors {
fn into_diag_arg(self, _: &mut crate::rustc_error_messages::LongTyPath) -> DiagArgValue {
DiagArgValue::Str(alloc::borrow::Cow::Borrowed(match self.0 {
0 => panic!("`NumScalableVectors(0)` is illformed"),
1 => "one",
2 => "two",
3 => "three",
4 => "four",
5 => "five",
6 => "six",
7 => "seven",
8 => "eight",
_ => panic!("`NumScalableVectors(N)` for N>8 is illformed"),
}))
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub enum BackendRepr {
Scalar(Scalar),
ScalarPair {
a: Scalar,
b: Scalar,
b_offset: Size,
},
SimdScalableVector {
element: Scalar,
count: BackendLaneCount,
number_of_vectors: NumScalableVectors,
},
SimdVector {
element: Scalar,
count: BackendLaneCount,
},
Memory {
sized: bool,
},
}
impl BackendRepr {
#[inline]
pub fn is_unsized(&self) -> bool {
match *self {
BackendRepr::Scalar(_)
| BackendRepr::ScalarPair { .. }
| BackendRepr::SimdScalableVector { .. }
| BackendRepr::SimdVector { .. } => false,
BackendRepr::Memory { sized } => !sized,
}
}
#[inline]
pub fn is_sized(&self) -> bool {
!self.is_unsized()
}
#[inline]
pub fn is_signed(&self) -> bool {
match self {
BackendRepr::Scalar(scal) => scal.is_signed(),
_ => panic!("`is_signed` on non-scalar ABI {self:?}"),
}
}
#[inline]
pub fn is_scalar(&self) -> bool {
matches!(*self, BackendRepr::Scalar(_))
}
#[inline]
pub fn is_scalar_or_simd(&self) -> bool {
matches!(
*self,
BackendRepr::Scalar(_)
| BackendRepr::SimdVector { .. }
| BackendRepr::SimdScalableVector { .. }
)
}
#[inline]
pub fn is_bool(&self) -> bool {
matches!(*self, BackendRepr::Scalar(s) if s.is_bool())
}
pub fn scalar_platform_align<C: HasDataLayout>(&self, cx: &C) -> Option<Align> {
match *self {
BackendRepr::Scalar(s) => Some(s.default_align(cx).abi),
BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => {
Some(s1.default_align(cx).max(s2.default_align(cx)).abi)
}
BackendRepr::SimdVector { .. }
| BackendRepr::Memory { .. }
| BackendRepr::SimdScalableVector { .. } => None,
}
}
pub fn scalar_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
match *self {
BackendRepr::Scalar(s) => Some(s.size(cx)),
BackendRepr::ScalarPair { a: _, b: s2, b_offset: field2_offset } => {
let size = (field2_offset + s2.size(cx)).align_to(
self.scalar_platform_align(cx)
.unwrap(),
);
Some(size)
}
BackendRepr::SimdVector { .. }
| BackendRepr::Memory { .. }
| BackendRepr::SimdScalableVector { .. } => None,
}
}
pub fn to_union(&self) -> Self {
match *self {
BackendRepr::Scalar(s) => BackendRepr::Scalar(s.to_union()),
BackendRepr::ScalarPair { a: s1, b: s2, b_offset } => {
BackendRepr::ScalarPair { a: s1.to_union(), b: s2.to_union(), b_offset }
}
BackendRepr::SimdVector { element, count } => {
BackendRepr::SimdVector { element: element.to_union(), count }
}
BackendRepr::Memory { .. } => BackendRepr::Memory { sized: true },
BackendRepr::SimdScalableVector { element, count, number_of_vectors } => {
BackendRepr::SimdScalableVector {
element: element.to_union(),
count,
number_of_vectors,
}
}
}
}
pub fn eq_up_to_validity(&self, other: &Self) -> bool {
match (self, other) {
(BackendRepr::Scalar(l), BackendRepr::Scalar(r)) => l.primitive() == r.primitive(),
(
BackendRepr::SimdVector { element: element_l, count: count_l },
BackendRepr::SimdVector { element: element_r, count: count_r },
) => element_l.primitive() == element_r.primitive() && count_l == count_r,
(
BackendRepr::ScalarPair { a: l1, b: l2, b_offset: l_offset },
BackendRepr::ScalarPair { a: r1, b: r2, b_offset: r_offset },
) => {
l1.primitive() == r1.primitive()
&& l2.primitive() == r2.primitive()
&& l_offset == r_offset
}
_ => self == other,
}
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
Empty,
Single {
index: VariantIdx,
},
Multiple {
tag: Scalar,
tag_encoding: TagEncoding<VariantIdx>,
tag_field: FieldIdx,
variants: IndexVec<VariantIdx, VariantLayout<FieldIdx>>,
},
}
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub enum TagEncoding<VariantIdx: Idx> {
Direct,
Niche {
untagged_variant: VariantIdx,
niche_variants: RangeInclusive<VariantIdx>,
niche_start: u128,
},
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub struct Niche {
pub offset: Size,
pub value: Primitive,
pub valid_range: WrappingRange,
}
impl Niche {
pub fn from_scalar<C: HasDataLayout>(cx: &C, offset: Size, scalar: Scalar) -> Option<Self> {
let Scalar::Initialized { value, valid_range } = scalar else { return None };
let niche = Niche { offset, value, valid_range };
if niche.available(cx) > 0 { Some(niche) } else { None }
}
pub fn available<C: HasDataLayout>(&self, cx: &C) -> u128 {
let Self { value, valid_range: v, .. } = *self;
let size = value.size(cx);
assert!(size.bits() <= 128);
let max_value = size.unsigned_int_max();
let niche = v.end.wrapping_add(1)..v.start;
niche.end.wrapping_sub(niche.start) & max_value
}
pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> {
assert!(count > 0);
let Self { value, valid_range: v, .. } = *self;
let size = value.size(cx);
assert!(size.bits() <= 128);
let max_value = size.unsigned_int_max();
let available = v.start.wrapping_sub(v.end).wrapping_sub(1) & max_value;
if count > available {
return None;
}
let move_start = |v: WrappingRange| {
let start = v.start.wrapping_sub(count) & max_value;
Some((start, Scalar::Initialized { value, valid_range: v.with_start(start) }))
};
let move_end = |v: WrappingRange| {
let start = v.end.wrapping_add(1) & max_value;
let end = v.end.wrapping_add(count) & max_value;
Some((start, Scalar::Initialized { value, valid_range: v.with_end(end) }))
};
let distance_end_zero = max_value - v.end;
if count == 1 && v != (WrappingRange { start: 0, end: 1 }) {
let next_up = size.sign_extend(v.end.wrapping_add(1)).unsigned_abs();
let next_down = size.sign_extend(v.start.wrapping_sub(1)).unsigned_abs();
if next_down <= next_up { move_start(v) } else { move_end(v) }
} else if v.start > v.end {
move_end(v)
} else if v.start <= distance_end_zero {
if count <= v.start {
move_start(v)
} else {
move_end(v)
}
} else {
let end = v.end.wrapping_add(count) & max_value;
let overshot_zero = (1..=v.end).contains(&end);
if overshot_zero {
move_start(v)
} else {
move_end(v)
}
}
}
}
#[derive(PartialEq, Eq, Hash, Clone)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
pub fields: FieldsShape<FieldIdx>,
pub variants: Variants<FieldIdx, VariantIdx>,
pub backend_repr: BackendRepr,
pub largest_niche: Option<Niche>,
pub uninhabited: bool,
pub align: AbiAlign,
pub size: Size,
pub max_repr_align: Option<Align>,
pub unadjusted_abi_align: Align,
pub randomization_seed: Hash64,
}
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
pub fn is_aggregate(&self) -> bool {
match self.backend_repr {
BackendRepr::Scalar(_)
| BackendRepr::SimdVector { .. }
| BackendRepr::SimdScalableVector { .. } => false,
BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => true,
}
}
pub fn is_uninhabited(&self) -> bool {
self.uninhabited
}
pub fn is_variant_uninhabited(&self, variant: VariantIdx) -> bool {
match self.variants {
Variants::Empty => true,
Variants::Single { index } => variant != index || self.uninhabited,
Variants::Multiple { ref variants, .. } => {
variants.get(variant).map(|v| v.uninhabited).unwrap_or(true)
}
}
}
}
impl<FieldIdx: Idx, VariantIdx: Idx> fmt::Debug for LayoutData<FieldIdx, VariantIdx>
where
FieldsShape<FieldIdx>: fmt::Debug,
Variants<FieldIdx, VariantIdx>: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let LayoutData {
size,
align,
backend_repr,
fields,
largest_niche,
uninhabited,
variants,
max_repr_align,
unadjusted_abi_align,
randomization_seed,
} = self;
f.debug_struct("Layout")
.field("size", size)
.field("align", align)
.field("backend_repr", backend_repr)
.field("fields", fields)
.field("largest_niche", largest_niche)
.field("uninhabited", uninhabited)
.field("variants", variants)
.field("max_repr_align", max_repr_align)
.field("unadjusted_abi_align", unadjusted_abi_align)
.field("randomization_seed", randomization_seed)
.finish()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum PointerKind {
SharedRef { frozen: bool },
MutableRef { unpin: bool },
Box { unpin: bool, global: bool },
}
#[derive(Copy, Clone, Debug)]
pub struct PointeeInfo {
pub safe: Option<PointerKind>,
pub size: Size,
pub align: Align,
}
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
#[inline]
pub fn is_unsized(&self) -> bool {
self.backend_repr.is_unsized()
}
#[inline]
pub fn is_sized(&self) -> bool {
self.backend_repr.is_sized()
}
pub fn is_1zst(&self) -> bool {
self.is_sized() && self.size.bytes() == 0 && self.align.bytes() == 1
}
pub fn is_scalable_vector(&self) -> bool {
matches!(self.backend_repr, BackendRepr::SimdScalableVector { .. })
}
pub fn scalable_vector_element_count(&self) -> Option<BackendLaneCount> {
match self.backend_repr {
BackendRepr::SimdScalableVector { count, .. } => Some(count),
_ => None,
}
}
pub fn is_zst(&self) -> bool {
match self.backend_repr {
BackendRepr::Scalar(_)
| BackendRepr::ScalarPair { .. }
| BackendRepr::SimdScalableVector { .. }
| BackendRepr::SimdVector { .. } => false,
BackendRepr::Memory { sized } => sized && self.size.bytes() == 0,
}
}
#[inline]
pub fn is_ssa_standalone(&self) -> bool {
match self.backend_repr {
BackendRepr::Memory { .. } => self.is_zst(),
BackendRepr::Scalar(..)
| BackendRepr::ScalarPair { .. }
| BackendRepr::SimdVector { .. }
| BackendRepr::SimdScalableVector { .. } => true,
}
}
pub fn eq_abi(&self, other: &Self) -> bool {
self.size == other.size
&& self.is_sized() == other.is_sized()
&& self.backend_repr.eq_up_to_validity(&other.backend_repr)
&& self.backend_repr.is_bool() == other.backend_repr.is_bool()
&& self.align.abi == other.align.abi
&& self.max_repr_align == other.max_repr_align
&& self.unadjusted_abi_align == other.unadjusted_abi_align
}
}
#[derive(Copy, Clone, Debug)]
pub enum StructKind {
AlwaysSized,
MaybeUnsized,
Prefixed(Size, Align),
}
#[derive(Clone, Debug)]
pub enum AbiFromStrErr {
Unknown,
NoExplicitUnwind,
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
#[cfg_attr(feature = "nightly", derive(StableHash))]
pub struct VariantLayout<FieldIdx: Idx> {
pub size: Size,
pub backend_repr: BackendRepr,
pub field_offsets: IndexVec<FieldIdx, Size>,
fields_in_memory_order: IndexVec<u32, FieldIdx>,
largest_niche: Option<Niche>,
uninhabited: bool,
}
impl<FieldIdx: Idx> VariantLayout<FieldIdx> {
pub fn from_layout(layout: LayoutData<FieldIdx, impl Idx>) -> Self {
let FieldsShape::Arbitrary { offsets, in_memory_order } = layout.fields else {
panic!("Layout of fields should be Arbitrary for variants");
};
Self {
size: layout.size,
backend_repr: layout.backend_repr,
field_offsets: offsets,
fields_in_memory_order: in_memory_order,
largest_niche: layout.largest_niche,
uninhabited: layout.uninhabited,
}
}
pub fn is_uninhabited(&self) -> bool {
self.uninhabited
}
pub fn has_fields(&self) -> bool {
self.field_offsets.len() > 0
}
}