rustc_abi/layout/
simple.rs

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