rustc_metadata/rmeta/
table.rs

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