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