| use std::num::NonZero; | |
| use rustc_hashes::Hash64; | |
| use rustc_index::{Idx, IndexVec}; | |
| use crate::{ | |
| AbiAlign, BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, | |
| Variants, | |
| }; | |
| /// "Simple" layout constructors that cannot fail. | |
| impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { | |
| pub fn unit<C: HasDataLayout>(cx: &C, sized: bool) -> Self { | |
| let dl = cx.data_layout(); | |
| LayoutData { | |
| variants: Variants::Single { index: VariantIdx::new(0) }, | |
| fields: FieldsShape::Arbitrary { | |
| offsets: IndexVec::new(), | |
| in_memory_order: IndexVec::new(), | |
| }, | |
| backend_repr: BackendRepr::Memory { sized }, | |
| largest_niche: None, | |
| uninhabited: false, | |
| align: AbiAlign::new(dl.i8_align), | |
| size: Size::ZERO, | |
| max_repr_align: None, | |
| unadjusted_abi_align: dl.i8_align, | |
| randomization_seed: Hash64::new(0), | |
| } | |
| } | |
| pub fn never_type<C: HasDataLayout>(cx: &C) -> Self { | |
| let dl = cx.data_layout(); | |
| // This is also used for uninhabited enums, so we use `Variants::Empty`. | |
| LayoutData { | |
| variants: Variants::Empty, | |
| fields: FieldsShape::Primitive, | |
| backend_repr: BackendRepr::Memory { sized: true }, | |
| largest_niche: None, | |
| uninhabited: true, | |
| align: AbiAlign::new(dl.i8_align), | |
| size: Size::ZERO, | |
| max_repr_align: None, | |
| unadjusted_abi_align: dl.i8_align, | |
| randomization_seed: Hash64::ZERO, | |
| } | |
| } | |
| pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self { | |
| let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); | |
| let size = scalar.size(cx); | |
| let align = scalar.align(cx); | |
| let range = scalar.valid_range(cx); | |
| // All primitive types for which we don't have subtype coercions should get a distinct seed, | |
| // so that types wrapping them can use randomization to arrive at distinct layouts. | |
| // | |
| // Some type information is already lost at this point, so as an approximation we derive | |
| // the seed from what remains. For example on 64-bit targets usize and u64 can no longer | |
| // be distinguished. | |
| let randomization_seed = size | |
| .bytes() | |
| .wrapping_add( | |
| match scalar.primitive() { | |
| Primitive::Int(_, true) => 1, | |
| Primitive::Int(_, false) => 2, | |
| Primitive::Float(_) => 3, | |
| Primitive::Pointer(_) => 4, | |
| } << 32, | |
| ) | |
| // distinguishes references from pointers | |
| .wrapping_add((range.start as u64).rotate_right(16)) | |
| // distinguishes char from u32 and bool from u8 | |
| .wrapping_add((range.end as u64).rotate_right(16)); | |
| LayoutData { | |
| variants: Variants::Single { index: VariantIdx::new(0) }, | |
| fields: FieldsShape::Primitive, | |
| backend_repr: BackendRepr::Scalar(scalar), | |
| largest_niche, | |
| uninhabited: false, | |
| size, | |
| align, | |
| max_repr_align: None, | |
| unadjusted_abi_align: align.abi, | |
| randomization_seed: Hash64::new(randomization_seed), | |
| } | |
| } | |
| pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self { | |
| let dl = cx.data_layout(); | |
| let b_align = b.align(dl).abi; | |
| let align = a.align(dl).abi.max(b_align).max(dl.aggregate_align); | |
| let b_offset = a.size(dl).align_to(b_align); | |
| let size = (b_offset + b.size(dl)).align_to(align); | |
| // HACK(nox): We iter on `b` and then `a` because `max_by_key` | |
| // returns the last maximum. | |
| let largest_niche = Niche::from_scalar(dl, b_offset, b) | |
| .into_iter() | |
| .chain(Niche::from_scalar(dl, Size::ZERO, a)) | |
| .max_by_key(|niche| niche.available(dl)); | |
| let combined_seed = a.size(dl).bytes().wrapping_add(b.size(dl).bytes()); | |
| LayoutData { | |
| variants: Variants::Single { index: VariantIdx::new(0) }, | |
| fields: FieldsShape::Arbitrary { | |
| offsets: [Size::ZERO, b_offset].into(), | |
| in_memory_order: [FieldIdx::new(0), FieldIdx::new(1)].into(), | |
| }, | |
| backend_repr: BackendRepr::ScalarPair(a, b), | |
| largest_niche, | |
| uninhabited: false, | |
| align: AbiAlign::new(align), | |
| size, | |
| max_repr_align: None, | |
| unadjusted_abi_align: align, | |
| randomization_seed: Hash64::new(combined_seed), | |
| } | |
| } | |
| /// Returns a dummy layout for an uninhabited variant. | |
| /// | |
| /// Uninhabited variants get pruned as part of the layout calculation, | |
| /// so this can be used after the fact to reconstitute a layout. | |
| pub fn uninhabited_variant<C: HasDataLayout>(cx: &C, index: VariantIdx, fields: usize) -> Self { | |
| let dl = cx.data_layout(); | |
| LayoutData { | |
| variants: Variants::Single { index }, | |
| fields: match NonZero::new(fields) { | |
| Some(fields) => FieldsShape::Union(fields), | |
| None => FieldsShape::Arbitrary { | |
| offsets: IndexVec::new(), | |
| in_memory_order: IndexVec::new(), | |
| }, | |
| }, | |
| backend_repr: BackendRepr::Memory { sized: true }, | |
| largest_niche: None, | |
| uninhabited: true, | |
| align: AbiAlign::new(dl.i8_align), | |
| size: Size::ZERO, | |
| max_repr_align: None, | |
| unadjusted_abi_align: dl.i8_align, | |
| randomization_seed: Hash64::ZERO, | |
| } | |
| } | |
| } |