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                memory_index: 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            randomization_seed: Hash64::new(0),
29        }
30    }
31
32    pub fn never_type<C: HasDataLayout>(cx: &C) -> Self {
33        let dl = cx.data_layout();
34        // This is also used for uninhabited enums, so we use `Variants::Empty`.
35        LayoutData {
36            variants: Variants::Empty,
37            fields: FieldsShape::Primitive,
38            backend_repr: BackendRepr::Memory { sized: true },
39            largest_niche: None,
40            uninhabited: true,
41            align: AbiAlign::new(dl.i8_align),
42            size: Size::ZERO,
43            max_repr_align: None,
44            unadjusted_abi_align: dl.i8_align,
45            randomization_seed: Hash64::ZERO,
46        }
47    }
48
49    pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
50        let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
51        let size = scalar.size(cx);
52        let align = scalar.align(cx);
53
54        let range = scalar.valid_range(cx);
55
56        // All primitive types for which we don't have subtype coercions should get a distinct seed,
57        // so that types wrapping them can use randomization to arrive at distinct layouts.
58        //
59        // Some type information is already lost at this point, so as an approximation we derive
60        // the seed from what remains. For example on 64-bit targets usize and u64 can no longer
61        // be distinguished.
62        let randomization_seed = size
63            .bytes()
64            .wrapping_add(
65                match scalar.primitive() {
66                    Primitive::Int(_, true) => 1,
67                    Primitive::Int(_, false) => 2,
68                    Primitive::Float(_) => 3,
69                    Primitive::Pointer(_) => 4,
70                } << 32,
71            )
72            // distinguishes references from pointers
73            .wrapping_add((range.start as u64).rotate_right(16))
74            // distinguishes char from u32 and bool from u8
75            .wrapping_add((range.end as u64).rotate_right(16));
76
77        LayoutData {
78            variants: Variants::Single { index: VariantIdx::new(0) },
79            fields: FieldsShape::Primitive,
80            backend_repr: BackendRepr::Scalar(scalar),
81            largest_niche,
82            uninhabited: false,
83            size,
84            align,
85            max_repr_align: None,
86            unadjusted_abi_align: align.abi,
87            randomization_seed: Hash64::new(randomization_seed),
88        }
89    }
90
91    pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self {
92        let dl = cx.data_layout();
93        let b_align = b.align(dl).abi;
94        let align = a.align(dl).abi.max(b_align).max(dl.aggregate_align);
95        let b_offset = a.size(dl).align_to(b_align);
96        let size = (b_offset + b.size(dl)).align_to(align);
97
98        // HACK(nox): We iter on `b` and then `a` because `max_by_key`
99        // returns the last maximum.
100        let largest_niche = Niche::from_scalar(dl, b_offset, b)
101            .into_iter()
102            .chain(Niche::from_scalar(dl, Size::ZERO, a))
103            .max_by_key(|niche| niche.available(dl));
104
105        let combined_seed = a.size(dl).bytes().wrapping_add(b.size(dl).bytes());
106
107        LayoutData {
108            variants: Variants::Single { index: VariantIdx::new(0) },
109            fields: FieldsShape::Arbitrary {
110                offsets: [Size::ZERO, b_offset].into(),
111                memory_index: [0, 1].into(),
112            },
113            backend_repr: BackendRepr::ScalarPair(a, b),
114            largest_niche,
115            uninhabited: false,
116            align: AbiAlign::new(align),
117            size,
118            max_repr_align: None,
119            unadjusted_abi_align: align,
120            randomization_seed: Hash64::new(combined_seed),
121        }
122    }
123
124    /// Returns a dummy layout for an uninhabited variant.
125    ///
126    /// Uninhabited variants get pruned as part of the layout calculation,
127    /// so this can be used after the fact to reconstitute a layout.
128    pub fn uninhabited_variant<C: HasDataLayout>(cx: &C, index: VariantIdx, fields: usize) -> Self {
129        let dl = cx.data_layout();
130        LayoutData {
131            variants: Variants::Single { index },
132            fields: match NonZero::new(fields) {
133                Some(fields) => FieldsShape::Union(fields),
134                None => FieldsShape::Arbitrary {
135                    offsets: IndexVec::new(),
136                    memory_index: IndexVec::new(),
137                },
138            },
139            backend_repr: BackendRepr::Memory { sized: true },
140            largest_niche: None,
141            uninhabited: true,
142            align: AbiAlign::new(dl.i8_align),
143            size: Size::ZERO,
144            max_repr_align: None,
145            unadjusted_abi_align: dl.i8_align,
146            randomization_seed: Hash64::ZERO,
147        }
148    }
149}