Skip to main content

rustc_abi/layout/
simple.rs

1use std::num::NonZero;
2
3use rustc_hashes::Hash64;
4use rustc_index::{Idx, IndexVec};
5
6use crate::{
7    AbiAlign, BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size,
8    Variants,
9};
10
11/// "Simple" layout constructors that cannot fail.
12impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
13    pub fn unit<C: HasDataLayout>(cx: &C, sized: bool) -> Self {
14        let dl = cx.data_layout();
15        LayoutData {
16            variants: Variants::Single { index: VariantIdx::new(0) },
17            fields: FieldsShape::Arbitrary {
18                offsets: IndexVec::new(),
19                in_memory_order: IndexVec::new(),
20            },
21            backend_repr: BackendRepr::Memory { sized },
22            largest_niche: None,
23            uninhabited: false,
24            align: AbiAlign::new(dl.i8_align),
25            size: Size::ZERO,
26            max_repr_align: None,
27            unadjusted_abi_align: dl.i8_align,
28            repr_c: false,
29            randomization_seed: Hash64::new(0),
30        }
31    }
32
33    pub fn never_type<C: HasDataLayout>(cx: &C) -> Self {
34        let dl = cx.data_layout();
35        // This is also used for uninhabited enums, so we use `Variants::Empty`.
36        LayoutData {
37            variants: Variants::Empty,
38            fields: FieldsShape::Primitive,
39            backend_repr: BackendRepr::Memory { sized: true },
40            largest_niche: None,
41            uninhabited: true,
42            align: AbiAlign::new(dl.i8_align),
43            size: Size::ZERO,
44            max_repr_align: None,
45            unadjusted_abi_align: dl.i8_align,
46            repr_c: false,
47            randomization_seed: Hash64::ZERO,
48        }
49    }
50
51    pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
52        let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
53        let size = scalar.size(cx);
54        let align = scalar.default_align(cx);
55
56        let range = scalar.valid_range(cx);
57
58        // All primitive types for which we don't have subtype coercions should get a distinct seed,
59        // so that types wrapping them can use randomization to arrive at distinct layouts.
60        //
61        // Some type information is already lost at this point, so as an approximation we derive
62        // the seed from what remains. For example on 64-bit targets usize and u64 can no longer
63        // be distinguished.
64        let randomization_seed = size
65            .bytes()
66            .wrapping_add(
67                match scalar.primitive() {
68                    Primitive::Int(_, true) => 1,
69                    Primitive::Int(_, false) => 2,
70                    Primitive::Float(_) => 3,
71                    Primitive::Pointer(_) => 4,
72                } << 32,
73            )
74            // distinguishes references from pointers
75            .wrapping_add((range.start as u64).rotate_right(16))
76            // distinguishes char from u32 and bool from u8
77            .wrapping_add((range.end as u64).rotate_right(16));
78
79        LayoutData {
80            variants: Variants::Single { index: VariantIdx::new(0) },
81            fields: FieldsShape::Primitive,
82            backend_repr: BackendRepr::Scalar(scalar),
83            largest_niche,
84            uninhabited: false,
85            size,
86            align,
87            max_repr_align: None,
88            unadjusted_abi_align: align.abi,
89            repr_c: false,
90            randomization_seed: Hash64::new(randomization_seed),
91        }
92    }
93
94    pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self {
95        let dl = cx.data_layout();
96        let b_align = b.default_align(dl).abi;
97        let align = a.default_align(dl).abi.max(b_align).max(dl.aggregate_align);
98        let b_offset = a.size(dl).align_to(b_align);
99        let size = (b_offset + b.size(dl)).align_to(align);
100
101        // HACK(nox): We iter on `b` and then `a` because `max_by_key`
102        // returns the last maximum.
103        let largest_niche = Niche::from_scalar(dl, b_offset, b)
104            .into_iter()
105            .chain(Niche::from_scalar(dl, Size::ZERO, a))
106            .max_by_key(|niche| niche.available(dl));
107
108        let combined_seed = a.size(dl).bytes().wrapping_add(b.size(dl).bytes());
109
110        LayoutData {
111            variants: Variants::Single { index: VariantIdx::new(0) },
112            fields: FieldsShape::Arbitrary {
113                offsets: [Size::ZERO, b_offset].into(),
114                in_memory_order: [FieldIdx::new(0), FieldIdx::new(1)].into(),
115            },
116            backend_repr: BackendRepr::ScalarPair { a, b, b_offset },
117            largest_niche,
118            uninhabited: false,
119            align: AbiAlign::new(align),
120            size,
121            max_repr_align: None,
122            unadjusted_abi_align: align,
123            repr_c: false,
124            randomization_seed: Hash64::new(combined_seed),
125        }
126    }
127
128    /// Returns a dummy layout for an uninhabited variant.
129    ///
130    /// Uninhabited variants get pruned as part of the layout calculation,
131    /// so this can be used after the fact to reconstitute a layout.
132    pub fn uninhabited_variant<C: HasDataLayout>(cx: &C, index: VariantIdx, fields: usize) -> Self {
133        let dl = cx.data_layout();
134        LayoutData {
135            variants: Variants::Single { index },
136            fields: match NonZero::new(fields) {
137                Some(fields) => FieldsShape::Union(fields),
138                None => FieldsShape::Arbitrary {
139                    offsets: IndexVec::new(),
140                    in_memory_order: IndexVec::new(),
141                },
142            },
143            backend_repr: BackendRepr::Memory { sized: true },
144            largest_niche: None,
145            uninhabited: true,
146            align: AbiAlign::new(dl.i8_align),
147            size: Size::ZERO,
148            max_repr_align: None,
149            unadjusted_abi_align: dl.i8_align,
150            repr_c: false,
151            // Variant layouts never flow back into actual layout computations,
152            // so dummy values are fine here.
153            randomization_seed: Hash64::ZERO,
154        }
155    }
156
157    /// Returns a layout for an inhabited variant.
158    pub fn for_variant(parent: &Self, index: VariantIdx) -> Self {
159        let layout = match &parent.variants {
160            Variants::Multiple { variants, .. } => &variants[index],
161            _ => {
    ::core::panicking::panic_fmt(format_args!("Expected multi-variant layout in `Layout::for_variant`"));
}panic!("Expected multi-variant layout in `Layout::for_variant`"),
162        };
163
164        Self {
165            fields: FieldsShape::Arbitrary {
166                offsets: layout.field_offsets.clone(),
167                in_memory_order: layout.fields_in_memory_order.clone(),
168            },
169            variants: Variants::Single { index },
170            backend_repr: layout.backend_repr,
171            largest_niche: layout.largest_niche,
172            uninhabited: layout.uninhabited,
173            size: layout.size,
174            align: parent.align,
175            max_repr_align: parent.max_repr_align,
176            unadjusted_abi_align: parent.unadjusted_abi_align,
177            repr_c: parent.repr_c,
178            // Variant layouts never flow back into actual layout computations,
179            // so dummy values are fine here.
180            randomization_seed: Hash64::ZERO,
181        }
182    }
183}