rustc_metadata/rmeta/
table.rs

1use rustc_hir::def::CtorOf;
2use rustc_index::Idx;
3
4use crate::rmeta::*;
5
6pub(super) trait IsDefault: Default {
7    fn is_default(&self) -> bool;
8}
9
10impl<T> IsDefault for Option<T> {
11    fn is_default(&self) -> bool {
12        self.is_none()
13    }
14}
15
16impl IsDefault for AttrFlags {
17    fn is_default(&self) -> bool {
18        self.is_empty()
19    }
20}
21
22impl IsDefault for bool {
23    fn is_default(&self) -> bool {
24        !self
25    }
26}
27
28impl IsDefault for u32 {
29    fn is_default(&self) -> bool {
30        *self == 0
31    }
32}
33
34impl IsDefault for u64 {
35    fn is_default(&self) -> bool {
36        *self == 0
37    }
38}
39
40impl<T> IsDefault for LazyArray<T> {
41    fn is_default(&self) -> bool {
42        self.num_elems == 0
43    }
44}
45
46impl IsDefault for UnusedGenericParams {
47    fn is_default(&self) -> bool {
48        // UnusedGenericParams encodes the *un*usedness as a bitset.
49        // This means that 0 corresponds to all bits used, which is indeed the default.
50        let is_default = self.bits() == 0;
51        debug_assert_eq!(is_default, self.all_used());
52        is_default
53    }
54}
55
56/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
57/// Used mainly for Lazy positions and lengths.
58/// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
59/// but this has no impact on safety.
60pub(super) trait FixedSizeEncoding: IsDefault {
61    /// This should be `[u8; BYTE_LEN]`;
62    /// Cannot use an associated `const BYTE_LEN: usize` instead due to const eval limitations.
63    type ByteArray;
64
65    fn from_bytes(b: &Self::ByteArray) -> Self;
66    fn write_to_bytes(self, b: &mut Self::ByteArray);
67}
68
69impl FixedSizeEncoding for u64 {
70    type ByteArray = [u8; 8];
71
72    #[inline]
73    fn from_bytes(b: &[u8; 8]) -> Self {
74        Self::from_le_bytes(*b)
75    }
76
77    #[inline]
78    fn write_to_bytes(self, b: &mut [u8; 8]) {
79        *b = self.to_le_bytes();
80    }
81}
82
83macro_rules! fixed_size_enum {
84    ($ty:ty { $(($($pat:tt)*))* } $( unreachable { $(($($upat:tt)*))+ } )?) => {
85        impl FixedSizeEncoding for Option<$ty> {
86            type ByteArray = [u8;1];
87
88            #[inline]
89            fn from_bytes(b: &[u8;1]) -> Self {
90                use $ty::*;
91                if b[0] == 0 {
92                    return None;
93                }
94                match b[0] - 1 {
95                    $(${index()} => Some($($pat)*),)*
96                    _ => panic!("Unexpected {} code: {:?}", stringify!($ty), b[0]),
97                }
98            }
99
100            #[inline]
101            fn write_to_bytes(self, b: &mut [u8;1]) {
102                use $ty::*;
103                b[0] = match self {
104                    None => unreachable!(),
105                    $(Some($($pat)*) => 1 + ${index()},)*
106                    $(Some($($($upat)*)|+) => unreachable!(),)?
107                }
108            }
109        }
110    }
111}
112
113// Workaround; need const traits to construct bitflags in a const
114macro_rules! const_macro_kinds {
115    ($($name:ident),+$(,)?) => (MacroKinds::from_bits_truncate($(MacroKinds::$name.bits())|+))
116}
117const MACRO_KINDS_ATTR_BANG: MacroKinds = const_macro_kinds!(ATTR, BANG);
118const MACRO_KINDS_DERIVE_BANG: MacroKinds = const_macro_kinds!(DERIVE, BANG);
119const MACRO_KINDS_DERIVE_ATTR: MacroKinds = const_macro_kinds!(DERIVE, ATTR);
120const MACRO_KINDS_DERIVE_ATTR_BANG: MacroKinds = const_macro_kinds!(DERIVE, ATTR, BANG);
121// Ensure that we get a compilation error if MacroKinds gets extended without updating metadata.
122const _: () = assert!(MACRO_KINDS_DERIVE_ATTR_BANG.is_all());
123
124fixed_size_enum! {
125    DefKind {
126        ( Mod                                      )
127        ( Struct                                   )
128        ( Union                                    )
129        ( Enum                                     )
130        ( Variant                                  )
131        ( Trait                                    )
132        ( TyAlias                                  )
133        ( ForeignTy                                )
134        ( TraitAlias                               )
135        ( AssocTy                                  )
136        ( TyParam                                  )
137        ( Fn                                       )
138        ( Const                                    )
139        ( ConstParam                               )
140        ( AssocFn                                  )
141        ( AssocConst                               )
142        ( ExternCrate                              )
143        ( Use                                      )
144        ( ForeignMod                               )
145        ( AnonConst                                )
146        ( InlineConst                              )
147        ( OpaqueTy                                 )
148        ( Field                                    )
149        ( LifetimeParam                            )
150        ( GlobalAsm                                )
151        ( Impl { of_trait: false }                 )
152        ( Impl { of_trait: true }                  )
153        ( Closure                                  )
154        ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Not, nested: false } )
155        ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Not, nested: false } )
156        ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Mut, nested: false } )
157        ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Mut, nested: false } )
158        ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Not, nested: true } )
159        ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Not, nested: true } )
160        ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Mut, nested: true } )
161        ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Mut, nested: true } )
162        ( Ctor(CtorOf::Struct, CtorKind::Fn)       )
163        ( Ctor(CtorOf::Struct, CtorKind::Const)    )
164        ( Ctor(CtorOf::Variant, CtorKind::Fn)      )
165        ( Ctor(CtorOf::Variant, CtorKind::Const)   )
166        ( Macro(MacroKinds::BANG)                  )
167        ( Macro(MacroKinds::ATTR)                  )
168        ( Macro(MacroKinds::DERIVE)                )
169        ( Macro(MACRO_KINDS_ATTR_BANG)             )
170        ( Macro(MACRO_KINDS_DERIVE_ATTR)           )
171        ( Macro(MACRO_KINDS_DERIVE_BANG)           )
172        ( Macro(MACRO_KINDS_DERIVE_ATTR_BANG)      )
173        ( SyntheticCoroutineBody                   )
174    } unreachable {
175        ( Macro(_)                                 )
176    }
177}
178
179fixed_size_enum! {
180    hir::Constness {
181        ( NotConst )
182        ( Const    )
183    }
184}
185
186fixed_size_enum! {
187    hir::Defaultness {
188        ( Final                        )
189        ( Default { has_value: false } )
190        ( Default { has_value: true }  )
191    }
192}
193
194fixed_size_enum! {
195    hir::Safety {
196        ( Unsafe )
197        ( Safe   )
198    }
199}
200
201fixed_size_enum! {
202    ty::Asyncness {
203        ( Yes )
204        ( No  )
205    }
206}
207
208fixed_size_enum! {
209    hir::CoroutineKind {
210        ( Coroutine(hir::Movability::Movable)                                          )
211        ( Coroutine(hir::Movability::Static)                                           )
212        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Block)        )
213        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Fn)           )
214        ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Closure)      )
215        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Block)      )
216        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Fn)         )
217        ( Desugared(hir::CoroutineDesugaring::Async, hir::CoroutineSource::Closure)    )
218        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Block)   )
219        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Fn)      )
220        ( Desugared(hir::CoroutineDesugaring::AsyncGen, hir::CoroutineSource::Closure) )
221    }
222}
223
224fixed_size_enum! {
225    MacroKind {
226        ( Attr   )
227        ( Bang   )
228        ( Derive )
229    }
230}
231
232// We directly encode RawDefId because using a `LazyValue` would incur a 50% overhead in the worst case.
233impl FixedSizeEncoding for Option<RawDefId> {
234    type ByteArray = [u8; 8];
235
236    #[inline]
237    fn from_bytes(encoded: &[u8; 8]) -> Self {
238        let (index, krate) = decode_interleaved(encoded);
239        let krate = u32::from_le_bytes(krate);
240        if krate == 0 {
241            return None;
242        }
243        let index = u32::from_le_bytes(index);
244
245        Some(RawDefId { krate: krate - 1, index })
246    }
247
248    #[inline]
249    fn write_to_bytes(self, dest: &mut [u8; 8]) {
250        match self {
251            None => unreachable!(),
252            Some(RawDefId { krate, index }) => {
253                debug_assert!(krate < u32::MAX);
254                // CrateNum is less than `CrateNum::MAX_AS_U32`.
255                let krate = (krate + 1).to_le_bytes();
256                let index = index.to_le_bytes();
257
258                // CrateNum is usually much smaller than the index within the crate, so put it in
259                // the second slot.
260                encode_interleaved(index, krate, dest);
261            }
262        }
263    }
264}
265
266impl FixedSizeEncoding for AttrFlags {
267    type ByteArray = [u8; 1];
268
269    #[inline]
270    fn from_bytes(b: &[u8; 1]) -> Self {
271        AttrFlags::from_bits_truncate(b[0])
272    }
273
274    #[inline]
275    fn write_to_bytes(self, b: &mut [u8; 1]) {
276        debug_assert!(!self.is_default());
277        b[0] = self.bits();
278    }
279}
280
281impl FixedSizeEncoding for bool {
282    type ByteArray = [u8; 1];
283
284    #[inline]
285    fn from_bytes(b: &[u8; 1]) -> Self {
286        b[0] != 0
287    }
288
289    #[inline]
290    fn write_to_bytes(self, b: &mut [u8; 1]) {
291        debug_assert!(!self.is_default());
292        b[0] = self as u8
293    }
294}
295
296// NOTE(eddyb) there could be an impl for `usize`, which would enable a more
297// generic `LazyValue<T>` impl, but in the general case we might not need / want
298// to fit every `usize` in `u32`.
299impl<T> FixedSizeEncoding for Option<LazyValue<T>> {
300    type ByteArray = [u8; 8];
301
302    #[inline]
303    fn from_bytes(b: &[u8; 8]) -> Self {
304        let position = NonZero::new(u64::from_bytes(b) as usize)?;
305        Some(LazyValue::from_position(position))
306    }
307
308    #[inline]
309    fn write_to_bytes(self, b: &mut [u8; 8]) {
310        match self {
311            None => unreachable!(),
312            Some(lazy) => {
313                let position = lazy.position.get();
314                let position: u64 = position.try_into().unwrap();
315                position.write_to_bytes(b)
316            }
317        }
318    }
319}
320
321impl<T> LazyArray<T> {
322    #[inline]
323    fn write_to_bytes_impl(self, dest: &mut [u8; 16]) {
324        let position = (self.position.get() as u64).to_le_bytes();
325        let len = (self.num_elems as u64).to_le_bytes();
326
327        encode_interleaved(position, len, dest)
328    }
329
330    fn from_bytes_impl(position: &[u8; 8], meta: &[u8; 8]) -> Option<LazyArray<T>> {
331        let position = NonZero::new(u64::from_bytes(position) as usize)?;
332        let len = u64::from_bytes(meta) as usize;
333        Some(LazyArray::from_position_and_num_elems(position, len))
334    }
335}
336
337// Interleaving the bytes of the two integers exposes trailing bytes in the first integer
338// to the varint scheme that we use for tables.
339#[inline]
340fn decode_interleaved<const N: usize, const M: usize>(encoded: &[u8; N]) -> ([u8; M], [u8; M]) {
341    assert_eq!(M * 2, N);
342    let mut first = [0u8; M];
343    let mut second = [0u8; M];
344    for i in 0..M {
345        first[i] = encoded[2 * i];
346        second[i] = encoded[2 * i + 1];
347    }
348    (first, second)
349}
350
351// Element width is selected at runtime on a per-table basis by omitting trailing
352// zero bytes in table elements. This works very naturally when table elements are
353// simple numbers but sometimes we have a pair of integers. If naively encoded, the second element
354// would shield the trailing zeroes in the first. Interleaving the bytes exposes trailing zeroes in
355// both to the optimization.
356//
357// Prefer passing a and b such that `b` is usually smaller.
358#[inline]
359fn encode_interleaved<const N: usize, const M: usize>(a: [u8; M], b: [u8; M], dest: &mut [u8; N]) {
360    assert_eq!(M * 2, N);
361    for i in 0..M {
362        dest[2 * i] = a[i];
363        dest[2 * i + 1] = b[i];
364    }
365}
366
367impl<T> FixedSizeEncoding for LazyArray<T> {
368    type ByteArray = [u8; 16];
369
370    #[inline]
371    fn from_bytes(b: &[u8; 16]) -> Self {
372        let (position, meta) = decode_interleaved(b);
373
374        if meta == [0; 8] {
375            return Default::default();
376        }
377        LazyArray::from_bytes_impl(&position, &meta).unwrap()
378    }
379
380    #[inline]
381    fn write_to_bytes(self, b: &mut [u8; 16]) {
382        assert!(!self.is_default());
383        self.write_to_bytes_impl(b)
384    }
385}
386
387impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
388    type ByteArray = [u8; 16];
389
390    #[inline]
391    fn from_bytes(b: &[u8; 16]) -> Self {
392        let (position, meta) = decode_interleaved(b);
393
394        LazyArray::from_bytes_impl(&position, &meta)
395    }
396
397    #[inline]
398    fn write_to_bytes(self, b: &mut [u8; 16]) {
399        match self {
400            None => unreachable!(),
401            Some(lazy) => lazy.write_to_bytes_impl(b),
402        }
403    }
404}
405
406/// Helper for constructing a table's serialization (also see `Table`).
407pub(super) struct TableBuilder<I: Idx, T: FixedSizeEncoding> {
408    width: usize,
409    blocks: IndexVec<I, T::ByteArray>,
410    _marker: PhantomData<T>,
411}
412
413impl<I: Idx, T: FixedSizeEncoding> Default for TableBuilder<I, T> {
414    fn default() -> Self {
415        TableBuilder { width: 0, blocks: Default::default(), _marker: PhantomData }
416    }
417}
418
419impl<I: Idx, const N: usize, T> TableBuilder<I, Option<T>>
420where
421    Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
422{
423    pub(crate) fn set_some(&mut self, i: I, value: T) {
424        self.set(i, Some(value))
425    }
426}
427
428impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBuilder<I, T> {
429    /// Sets the table value if it is not default.
430    /// ATTENTION: For optimization default values are simply ignored by this function, because
431    /// right now metadata tables never need to reset non-default values to default. If such need
432    /// arises in the future then a new method (e.g. `clear` or `reset`) will need to be introduced
433    /// for doing that explicitly.
434    pub(crate) fn set(&mut self, i: I, value: T) {
435        if !value.is_default() {
436            // FIXME(eddyb) investigate more compact encodings for sparse tables.
437            // On the PR @michaelwoerister mentioned:
438            // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
439            // > trick (i.e. divide things into buckets of 32 or 64 items and then
440            // > store bit-masks of which item in each bucket is actually serialized).
441            let block = self.blocks.ensure_contains_elem(i, || [0; N]);
442            value.write_to_bytes(block);
443            if self.width != N {
444                let width = N - trailing_zeros(block);
445                self.width = self.width.max(width);
446            }
447        }
448    }
449
450    pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
451        let pos = buf.position();
452
453        let width = self.width;
454        for block in &self.blocks {
455            buf.write_with(|dest| {
456                *dest = *block;
457                width
458            });
459        }
460
461        LazyTable::from_position_and_encoded_size(
462            NonZero::new(pos).unwrap(),
463            width,
464            self.blocks.len(),
465        )
466    }
467}
468
469fn trailing_zeros(x: &[u8]) -> usize {
470    x.iter().rev().take_while(|b| **b == 0).count()
471}
472
473impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]> + ParameterizedOverTcx>
474    LazyTable<I, T>
475where
476    for<'tcx> T::Value<'tcx>: FixedSizeEncoding<ByteArray = [u8; N]>,
477{
478    /// Given the metadata, extract out the value at a particular index (if any).
479    pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
480        // Access past the end of the table returns a Default
481        if i.index() >= self.len {
482            return Default::default();
483        }
484
485        let width = self.width;
486        let start = self.position.get() + (width * i.index());
487        let end = start + width;
488        let bytes = &metadata.blob()[start..end];
489
490        if let Ok(fixed) = bytes.try_into() {
491            FixedSizeEncoding::from_bytes(fixed)
492        } else {
493            let mut fixed = [0u8; N];
494            fixed[..width].copy_from_slice(bytes);
495            FixedSizeEncoding::from_bytes(&fixed)
496        }
497    }
498
499    /// Size of the table in entries, including possible gaps.
500    pub(super) fn size(&self) -> usize {
501        self.len
502    }
503}