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