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    ty::AssocItemContainer {
226        ( Trait )
227        ( Impl  )
228    }
229}
230
231fixed_size_enum! {
232    MacroKind {
233        ( Attr   )
234        ( Bang   )
235        ( Derive )
236    }
237}
238
239// We directly encode RawDefId because using a `LazyValue` would incur a 50% overhead in the worst case.
240impl FixedSizeEncoding for Option<RawDefId> {
241    type ByteArray = [u8; 8];
242
243    #[inline]
244    fn from_bytes(encoded: &[u8; 8]) -> Self {
245        let (index, krate) = decode_interleaved(encoded);
246        let krate = u32::from_le_bytes(krate);
247        if krate == 0 {
248            return None;
249        }
250        let index = u32::from_le_bytes(index);
251
252        Some(RawDefId { krate: krate - 1, index })
253    }
254
255    #[inline]
256    fn write_to_bytes(self, dest: &mut [u8; 8]) {
257        match self {
258            None => unreachable!(),
259            Some(RawDefId { krate, index }) => {
260                debug_assert!(krate < u32::MAX);
261                // CrateNum is less than `CrateNum::MAX_AS_U32`.
262                let krate = (krate + 1).to_le_bytes();
263                let index = index.to_le_bytes();
264
265                // CrateNum is usually much smaller than the index within the crate, so put it in
266                // the second slot.
267                encode_interleaved(index, krate, dest);
268            }
269        }
270    }
271}
272
273impl FixedSizeEncoding for AttrFlags {
274    type ByteArray = [u8; 1];
275
276    #[inline]
277    fn from_bytes(b: &[u8; 1]) -> Self {
278        AttrFlags::from_bits_truncate(b[0])
279    }
280
281    #[inline]
282    fn write_to_bytes(self, b: &mut [u8; 1]) {
283        debug_assert!(!self.is_default());
284        b[0] = self.bits();
285    }
286}
287
288impl FixedSizeEncoding for bool {
289    type ByteArray = [u8; 1];
290
291    #[inline]
292    fn from_bytes(b: &[u8; 1]) -> Self {
293        b[0] != 0
294    }
295
296    #[inline]
297    fn write_to_bytes(self, b: &mut [u8; 1]) {
298        debug_assert!(!self.is_default());
299        b[0] = self as u8
300    }
301}
302
303// NOTE(eddyb) there could be an impl for `usize`, which would enable a more
304// generic `LazyValue<T>` impl, but in the general case we might not need / want
305// to fit every `usize` in `u32`.
306impl<T> FixedSizeEncoding for Option<LazyValue<T>> {
307    type ByteArray = [u8; 8];
308
309    #[inline]
310    fn from_bytes(b: &[u8; 8]) -> Self {
311        let position = NonZero::new(u64::from_bytes(b) as usize)?;
312        Some(LazyValue::from_position(position))
313    }
314
315    #[inline]
316    fn write_to_bytes(self, b: &mut [u8; 8]) {
317        match self {
318            None => unreachable!(),
319            Some(lazy) => {
320                let position = lazy.position.get();
321                let position: u64 = position.try_into().unwrap();
322                position.write_to_bytes(b)
323            }
324        }
325    }
326}
327
328impl<T> LazyArray<T> {
329    #[inline]
330    fn write_to_bytes_impl(self, dest: &mut [u8; 16]) {
331        let position = (self.position.get() as u64).to_le_bytes();
332        let len = (self.num_elems as u64).to_le_bytes();
333
334        encode_interleaved(position, len, dest)
335    }
336
337    fn from_bytes_impl(position: &[u8; 8], meta: &[u8; 8]) -> Option<LazyArray<T>> {
338        let position = NonZero::new(u64::from_bytes(position) as usize)?;
339        let len = u64::from_bytes(meta) as usize;
340        Some(LazyArray::from_position_and_num_elems(position, len))
341    }
342}
343
344// Interleaving the bytes of the two integers exposes trailing bytes in the first integer
345// to the varint scheme that we use for tables.
346#[inline]
347fn decode_interleaved<const N: usize, const M: usize>(encoded: &[u8; N]) -> ([u8; M], [u8; M]) {
348    assert_eq!(M * 2, N);
349    let mut first = [0u8; M];
350    let mut second = [0u8; M];
351    for i in 0..M {
352        first[i] = encoded[2 * i];
353        second[i] = encoded[2 * i + 1];
354    }
355    (first, second)
356}
357
358// Element width is selected at runtime on a per-table basis by omitting trailing
359// zero bytes in table elements. This works very naturally when table elements are
360// simple numbers but sometimes we have a pair of integers. If naively encoded, the second element
361// would shield the trailing zeroes in the first. Interleaving the bytes exposes trailing zeroes in
362// both to the optimization.
363//
364// Prefer passing a and b such that `b` is usually smaller.
365#[inline]
366fn encode_interleaved<const N: usize, const M: usize>(a: [u8; M], b: [u8; M], dest: &mut [u8; N]) {
367    assert_eq!(M * 2, N);
368    for i in 0..M {
369        dest[2 * i] = a[i];
370        dest[2 * i + 1] = b[i];
371    }
372}
373
374impl<T> FixedSizeEncoding for LazyArray<T> {
375    type ByteArray = [u8; 16];
376
377    #[inline]
378    fn from_bytes(b: &[u8; 16]) -> Self {
379        let (position, meta) = decode_interleaved(b);
380
381        if meta == [0; 8] {
382            return Default::default();
383        }
384        LazyArray::from_bytes_impl(&position, &meta).unwrap()
385    }
386
387    #[inline]
388    fn write_to_bytes(self, b: &mut [u8; 16]) {
389        assert!(!self.is_default());
390        self.write_to_bytes_impl(b)
391    }
392}
393
394impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
395    type ByteArray = [u8; 16];
396
397    #[inline]
398    fn from_bytes(b: &[u8; 16]) -> Self {
399        let (position, meta) = decode_interleaved(b);
400
401        LazyArray::from_bytes_impl(&position, &meta)
402    }
403
404    #[inline]
405    fn write_to_bytes(self, b: &mut [u8; 16]) {
406        match self {
407            None => unreachable!(),
408            Some(lazy) => lazy.write_to_bytes_impl(b),
409        }
410    }
411}
412
413/// Helper for constructing a table's serialization (also see `Table`).
414pub(super) struct TableBuilder<I: Idx, T: FixedSizeEncoding> {
415    width: usize,
416    blocks: IndexVec<I, T::ByteArray>,
417    _marker: PhantomData<T>,
418}
419
420impl<I: Idx, T: FixedSizeEncoding> Default for TableBuilder<I, T> {
421    fn default() -> Self {
422        TableBuilder { width: 0, blocks: Default::default(), _marker: PhantomData }
423    }
424}
425
426impl<I: Idx, const N: usize, T> TableBuilder<I, Option<T>>
427where
428    Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
429{
430    pub(crate) fn set_some(&mut self, i: I, value: T) {
431        self.set(i, Some(value))
432    }
433}
434
435impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBuilder<I, T> {
436    /// Sets the table value if it is not default.
437    /// ATTENTION: For optimization default values are simply ignored by this function, because
438    /// right now metadata tables never need to reset non-default values to default. If such need
439    /// arises in the future then a new method (e.g. `clear` or `reset`) will need to be introduced
440    /// for doing that explicitly.
441    pub(crate) fn set(&mut self, i: I, value: T) {
442        if !value.is_default() {
443            // FIXME(eddyb) investigate more compact encodings for sparse tables.
444            // On the PR @michaelwoerister mentioned:
445            // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
446            // > trick (i.e. divide things into buckets of 32 or 64 items and then
447            // > store bit-masks of which item in each bucket is actually serialized).
448            let block = self.blocks.ensure_contains_elem(i, || [0; N]);
449            value.write_to_bytes(block);
450            if self.width != N {
451                let width = N - trailing_zeros(block);
452                self.width = self.width.max(width);
453            }
454        }
455    }
456
457    pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
458        let pos = buf.position();
459
460        let width = self.width;
461        for block in &self.blocks {
462            buf.write_with(|dest| {
463                *dest = *block;
464                width
465            });
466        }
467
468        LazyTable::from_position_and_encoded_size(
469            NonZero::new(pos).unwrap(),
470            width,
471            self.blocks.len(),
472        )
473    }
474}
475
476fn trailing_zeros(x: &[u8]) -> usize {
477    x.iter().rev().take_while(|b| **b == 0).count()
478}
479
480impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]> + ParameterizedOverTcx>
481    LazyTable<I, T>
482where
483    for<'tcx> T::Value<'tcx>: FixedSizeEncoding<ByteArray = [u8; N]>,
484{
485    /// Given the metadata, extract out the value at a particular index (if any).
486    pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
487        // Access past the end of the table returns a Default
488        if i.index() >= self.len {
489            return Default::default();
490        }
491
492        let width = self.width;
493        let start = self.position.get() + (width * i.index());
494        let end = start + width;
495        let bytes = &metadata.blob()[start..end];
496
497        if let Ok(fixed) = bytes.try_into() {
498            FixedSizeEncoding::from_bytes(fixed)
499        } else {
500            let mut fixed = [0u8; N];
501            fixed[..width].copy_from_slice(bytes);
502            FixedSizeEncoding::from_bytes(&fixed)
503        }
504    }
505
506    /// Size of the table in entries, including possible gaps.
507    pub(super) fn size(&self) -> usize {
508        self.len
509    }
510}