|
| 1 | +use std::num::NonZero; |
| 2 | + |
| 3 | +use rustc_hashes::Hash64; |
| 4 | +use rustc_index::{Idx, IndexVec}; |
| 5 | + |
| 6 | +use crate::{ |
| 7 | + BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants, |
| 8 | +}; |
| 9 | + |
| 10 | +/// "Simple" layout constructors that cannot fail. |
| 11 | +impl<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 | +} |
0 commit comments