mmtk/util/metadata/
header_metadata.rs

1//! This module provides a default implementation of the access functions for in-header metadata.
2
3use atomic::Ordering;
4use std::fmt;
5use std::sync::atomic::AtomicU8;
6
7use crate::util::constants::{BITS_IN_BYTE, LOG_BITS_IN_BYTE};
8use crate::util::metadata::metadata_val_traits::*;
9use crate::util::Address;
10use num_traits::FromPrimitive;
11
12const LOG_BITS_IN_U16: usize = 4;
13const BITS_IN_U16: usize = 1 << LOG_BITS_IN_U16;
14const LOG_BITS_IN_U32: usize = 5;
15const BITS_IN_U32: usize = 1 << LOG_BITS_IN_U32;
16const LOG_BITS_IN_U64: usize = 6;
17const BITS_IN_U64: usize = 1 << LOG_BITS_IN_U64;
18
19/// This struct stores the specification of a header metadata bit-set.
20/// It supports either bits metadata of 1-7 bits in the same byte, or u8/u16/u32/u64 at an offset of their natural alignment.
21///
22/// For performance reasons, objects of this struct should be constants.
23#[derive(Clone, Copy, PartialEq, Eq, Hash)]
24pub struct HeaderMetadataSpec {
25    /// `bit_offset` is the index of the starting bit from which the data should be read or written.
26    /// It is counted from the right (least significant bit) of the byte.
27    /// Positive values refer to the bit positions within the current byte, starting with 0 for the
28    /// least significant bit (rightmost) up to 7 for the most significant bit (leftmost).
29    /// Negative values are used to refer to bit positions in the previous bytes, where -1 indicates
30    /// the most significant bit (leftmost) of the byte immediately before the current one.
31    pub bit_offset: isize,
32    /// `num_of_bits` specifies the number of consecutive bits to be read or written starting from the `bit_offset`.
33    /// This value is used to define the size of the data field in bits. For instance, if `num_of_bits` is set to 1,
34    /// only a single bit is considered, whereas a value of 8 would indicate a full byte.
35    /// This field must be a positive integer and typically should not exceed the size of the data type that
36    /// will hold the extracted value (for example, 8 bits for a `u8`, 16 bits for a `u16`, etc.).
37    /// The `num_of_bits` together with the `bit_offset` enables the extraction of bit fields of arbitrary
38    /// length and position, facilitating bit-level data manipulation.
39    pub num_of_bits: usize,
40}
41
42impl HeaderMetadataSpec {
43    /// We only allow mask for u8/u16/u32/u64/usize. If a mask is used with a spec that does not allow it, this method will panic.
44    ///
45    /// We allow using mask for certain operations. The reason for mask is that for header metadata, we may have overlapping metadata specs. For example,
46    /// a forwarding pointer is pointer-size, but its last 2 bits could be used as forwarding bits. In that case, all accesses to the forwarding pointer
47    /// spec should be used with a mask to make sure that we exclude the forwarding bits.
48    #[cfg(debug_assertions)]
49    fn assert_mask<T: MetadataValue>(&self, mask: Option<T>) {
50        debug_assert!(mask.is_none() || self.num_of_bits >= 8, "optional_mask is only supported for 8X-bits in-header metadata. Problematic MetadataSpec: ({:?})", self);
51    }
52
53    /// Assert if this is a valid spec.
54    #[cfg(debug_assertions)]
55    fn assert_spec<T: MetadataValue>(&self) {
56        if self.num_of_bits == 0 {
57            panic!("Metadata of 0 bits is not allowed.");
58        } else if self.num_of_bits < 8 {
59            debug_assert!(
60                (self.bit_offset >> LOG_BITS_IN_BYTE)
61                    == ((self.bit_offset + self.num_of_bits as isize - 1) >> LOG_BITS_IN_BYTE),
62                "Metadata << 8-bits: ({:?}) stretches over two bytes!",
63                self
64            );
65        } else if self.num_of_bits >= 8 && self.num_of_bits <= 64 {
66            debug_assert!(
67                self.bit_offset.trailing_zeros() >= T::LOG2,
68                "{:?}: bit_offset must be aligned to {}",
69                self,
70                1 << T::LOG2
71            );
72        } else {
73            // num_of_bits larger than 64
74            unreachable!("Metadata that is larger than 64-bits is not supported")
75        }
76    }
77
78    fn byte_offset(&self) -> isize {
79        self.bit_offset >> LOG_BITS_IN_BYTE
80    }
81
82    fn meta_addr(&self, header: Address) -> Address {
83        header + self.byte_offset()
84    }
85
86    // Some common methods for header metadata that is smaller than 1 byte.
87
88    /// Get the bit shift (the bit distance from the lowest bit to the bits location defined in the spec),
89    /// and the mask (used to extract value for the bits defined in the spec).
90    fn get_shift_and_mask_for_bits(&self) -> (isize, u8) {
91        debug_assert!(self.num_of_bits < BITS_IN_BYTE);
92        let byte_offset = self.byte_offset();
93        let bit_shift = self.bit_offset - (byte_offset << LOG_BITS_IN_BYTE);
94        let mask = ((1u8 << self.num_of_bits) - 1) << bit_shift;
95        (bit_shift, mask)
96    }
97
98    /// Extract bits from a raw byte, and put it to the lowest bits.
99    fn get_bits_from_u8(&self, raw_byte: u8) -> u8 {
100        debug_assert!(self.num_of_bits < BITS_IN_BYTE);
101        let (bit_shift, mask) = self.get_shift_and_mask_for_bits();
102        (raw_byte & mask) >> bit_shift
103    }
104
105    /// Set bits to a raw byte. `set_val` has the valid value in its lowest bits.
106    fn set_bits_to_u8(&self, raw_byte: u8, set_val: u8) -> u8 {
107        debug_assert!(self.num_of_bits < BITS_IN_BYTE);
108        debug_assert!(
109            set_val < (1 << self.num_of_bits),
110            "{:b} exceeds the maximum value of {} bits in the spec",
111            set_val,
112            self.num_of_bits
113        );
114        let (bit_shift, mask) = self.get_shift_and_mask_for_bits();
115        (raw_byte & !mask) | (set_val << bit_shift)
116    }
117
118    /// Truncate a value based on the spec.
119    fn truncate_bits_in_u8(&self, val: u8) -> u8 {
120        debug_assert!(self.num_of_bits < BITS_IN_BYTE);
121        val & ((1 << self.num_of_bits) - 1)
122    }
123
124    /// This function provides a default implementation for the `load_metadata` method from the `ObjectModel` trait.
125    ///
126    /// # Safety
127    /// This is a non-atomic load, thus not thread-safe.
128    pub unsafe fn load<T: MetadataValue>(&self, header: Address, optional_mask: Option<T>) -> T {
129        self.load_inner::<T>(header, optional_mask, None)
130    }
131
132    /// This function provides a default implementation for the `load_metadata_atomic` method from the `ObjectModel` trait.
133    pub fn load_atomic<T: MetadataValue>(
134        &self,
135        header: Address,
136        optional_mask: Option<T>,
137        ordering: Ordering,
138    ) -> T {
139        self.load_inner::<T>(header, optional_mask, Some(ordering))
140    }
141
142    fn load_inner<T: MetadataValue>(
143        &self,
144        header: Address,
145        optional_mask: Option<T>,
146        atomic_ordering: Option<Ordering>,
147    ) -> T {
148        #[cfg(debug_assertions)]
149        {
150            self.assert_mask::<T>(optional_mask);
151            self.assert_spec::<T>();
152        }
153
154        // metadata smaller than 8-bits is special in that more than one metadata value may be included in one AtomicU8 operation, and extra shift and mask is required
155        let res: T = if self.num_of_bits < 8 {
156            let byte_val = unsafe {
157                if let Some(order) = atomic_ordering {
158                    (self.meta_addr(header)).atomic_load::<AtomicU8>(order)
159                } else {
160                    (self.meta_addr(header)).load::<u8>()
161                }
162            };
163
164            FromPrimitive::from_u8(self.get_bits_from_u8(byte_val)).unwrap()
165        } else {
166            unsafe {
167                if let Some(order) = atomic_ordering {
168                    T::load_atomic(self.meta_addr(header), order)
169                } else {
170                    (self.meta_addr(header)).load::<T>()
171                }
172            }
173        };
174
175        if let Some(mask) = optional_mask {
176            res.bitand(mask)
177        } else {
178            res
179        }
180    }
181
182    /// This function provides a default implementation for the `store_metadata` method from the `ObjectModel` trait.
183    ///
184    /// Note: this function does compare-and-swap in a busy loop. So, unlike `compare_exchange_metadata`, this operation will always success.
185    ///
186    /// # Safety
187    /// This is a non-atomic store, thus not thread-safe.
188    pub unsafe fn store<T: MetadataValue>(
189        &self,
190        header: Address,
191        val: T,
192        optional_mask: Option<T>,
193    ) {
194        self.store_inner::<T>(header, val, optional_mask, None)
195    }
196
197    /// This function provides a default implementation for the `store_metadata_atomic` method from the `ObjectModel` trait.
198    ///
199    /// Note: this function does compare-and-swap in a busy loop. So, unlike `compare_exchange_metadata`, this operation will always success.
200    pub fn store_atomic<T: MetadataValue>(
201        &self,
202        header: Address,
203        val: T,
204        optional_mask: Option<T>,
205        ordering: Ordering,
206    ) {
207        self.store_inner::<T>(header, val, optional_mask, Some(ordering))
208    }
209
210    fn store_inner<T: MetadataValue>(
211        &self,
212        header: Address,
213        val: T,
214        optional_mask: Option<T>,
215        atomic_ordering: Option<Ordering>,
216    ) {
217        #[cfg(debug_assertions)]
218        {
219            self.assert_mask::<T>(optional_mask);
220            self.assert_spec::<T>();
221        }
222
223        // metadata smaller than 8-bits is special in that more than one metadata value may be included in one AtomicU8 operation, and extra shift and mask, and compare_exchange is required
224        if self.num_of_bits < 8 {
225            let val_u8 = val.to_u8().unwrap();
226            let byte_addr = self.meta_addr(header);
227            if let Some(order) = atomic_ordering {
228                let _ = unsafe {
229                    <u8 as MetadataValue>::fetch_update(byte_addr, order, order, |old_val: u8| {
230                        Some(self.set_bits_to_u8(old_val, val_u8))
231                    })
232                };
233            } else {
234                unsafe {
235                    let old_byte_val = byte_addr.load::<u8>();
236                    let new_byte_val = self.set_bits_to_u8(old_byte_val, val_u8);
237                    byte_addr.store::<u8>(new_byte_val);
238                }
239            }
240        } else {
241            let addr = self.meta_addr(header);
242            unsafe {
243                if let Some(order) = atomic_ordering {
244                    // if the optional mask is provided (e.g. for forwarding pointer), we need to use compare_exchange
245                    if let Some(mask) = optional_mask {
246                        let _ = T::fetch_update(addr, order, order, |old_val: T| {
247                            Some(old_val.bitand(mask.inv()).bitor(val.bitand(mask)))
248                        });
249                    } else {
250                        T::store_atomic(addr, val, order);
251                    }
252                } else {
253                    let val = if let Some(mask) = optional_mask {
254                        let old_val = T::load(addr);
255                        old_val.bitand(mask.inv()).bitor(val.bitand(mask))
256                    } else {
257                        val
258                    };
259                    T::store(addr, val);
260                }
261            }
262        }
263    }
264
265    /// This function provides a default implementation for the `compare_exchange_metadata` method from the `ObjectModel` trait.
266    ///
267    /// Note: this function only does fetch and exclusive store once, without any busy waiting in a loop.
268    pub fn compare_exchange<T: MetadataValue>(
269        &self,
270        header: Address,
271        old_metadata: T,
272        new_metadata: T,
273        optional_mask: Option<T>,
274        success_order: Ordering,
275        failure_order: Ordering,
276    ) -> Result<T, T> {
277        #[cfg(debug_assertions)]
278        self.assert_spec::<T>();
279        // metadata smaller than 8-bits is special in that more than one metadata value may be included in one AtomicU8 operation, and extra shift and mask is required
280        if self.num_of_bits < 8 {
281            let byte_addr = self.meta_addr(header);
282            unsafe {
283                let real_old_byte = byte_addr.atomic_load::<AtomicU8>(success_order);
284                let expected_old_byte =
285                    self.set_bits_to_u8(real_old_byte, old_metadata.to_u8().unwrap());
286                let expected_new_byte =
287                    self.set_bits_to_u8(expected_old_byte, new_metadata.to_u8().unwrap());
288                byte_addr
289                    .compare_exchange::<AtomicU8>(
290                        expected_old_byte,
291                        expected_new_byte,
292                        success_order,
293                        failure_order,
294                    )
295                    .map(|x| FromPrimitive::from_u8(x).unwrap())
296                    .map_err(|x| FromPrimitive::from_u8(x).unwrap())
297            }
298        } else {
299            let addr = self.meta_addr(header);
300            let (old_metadata, new_metadata) = if let Some(mask) = optional_mask {
301                let old_byte = unsafe { T::load_atomic(addr, success_order) };
302                let expected_new_byte = old_byte.bitand(mask.inv()).bitor(new_metadata);
303                let expected_old_byte = old_byte.bitand(mask.inv()).bitor(old_metadata);
304                (expected_old_byte, expected_new_byte)
305            } else {
306                (old_metadata, new_metadata)
307            };
308
309            unsafe {
310                T::compare_exchange(
311                    addr,
312                    old_metadata,
313                    new_metadata,
314                    success_order,
315                    failure_order,
316                )
317            }
318        }
319    }
320
321    /// Inner method for fetch_add/sub on bits.
322    /// For fetch_and/or, we don't necessarily need this method. We could directly do fetch_and/or on the u8.
323    fn fetch_ops_on_bits<F: Fn(u8) -> u8>(
324        &self,
325        header: Address,
326        set_order: Ordering,
327        fetch_order: Ordering,
328        update: F,
329    ) -> u8 {
330        let byte_addr = self.meta_addr(header);
331        let old_raw_byte = unsafe {
332            <u8 as MetadataValue>::fetch_update(
333                byte_addr,
334                set_order,
335                fetch_order,
336                |raw_byte: u8| {
337                    let old_metadata = self.get_bits_from_u8(raw_byte);
338                    let new_metadata = self.truncate_bits_in_u8(update(old_metadata));
339                    let new_byte = self.set_bits_to_u8(raw_byte, new_metadata);
340                    Some(new_byte)
341                },
342            )
343        }
344        .unwrap();
345        self.get_bits_from_u8(old_raw_byte)
346    }
347
348    /// This function provides a default implementation for the `fetch_add` method from the `ObjectModel` trait.
349    pub fn fetch_add<T: MetadataValue>(&self, header: Address, val: T, order: Ordering) -> T {
350        #[cfg(debug_assertions)]
351        self.assert_spec::<T>();
352        if self.num_of_bits < 8 {
353            FromPrimitive::from_u8(self.fetch_ops_on_bits(header, order, order, |x: u8| {
354                x.wrapping_add(val.to_u8().unwrap())
355            }))
356            .unwrap()
357        } else {
358            unsafe { T::fetch_add(self.meta_addr(header), val, order) }
359        }
360    }
361
362    /// This function provides a default implementation for the `fetch_sub` method from the `ObjectModel` trait.
363    pub fn fetch_sub<T: MetadataValue>(&self, header: Address, val: T, order: Ordering) -> T {
364        #[cfg(debug_assertions)]
365        self.assert_spec::<T>();
366        if self.num_of_bits < 8 {
367            FromPrimitive::from_u8(self.fetch_ops_on_bits(header, order, order, |x: u8| {
368                x.wrapping_sub(val.to_u8().unwrap())
369            }))
370            .unwrap()
371        } else {
372            unsafe { T::fetch_sub(self.meta_addr(header), val, order) }
373        }
374    }
375
376    /// This function provides a default implementation for the `fetch_and` method from the `ObjectModel` trait.
377    pub fn fetch_and<T: MetadataValue>(&self, header: Address, val: T, order: Ordering) -> T {
378        #[cfg(debug_assertions)]
379        self.assert_spec::<T>();
380        if self.num_of_bits < 8 {
381            let (lshift, mask) = self.get_shift_and_mask_for_bits();
382            let new_val = (val.to_u8().unwrap() << lshift) | !mask;
383            // We do not need to use fetch_ops_on_bits(), we can just set irrelavent bits to 1, and do fetch_and
384            let old_raw_byte =
385                unsafe { <u8 as MetadataValue>::fetch_and(self.meta_addr(header), new_val, order) };
386            let old_val = self.get_bits_from_u8(old_raw_byte);
387            FromPrimitive::from_u8(old_val).unwrap()
388        } else {
389            unsafe { T::fetch_and(self.meta_addr(header), val, order) }
390        }
391    }
392
393    /// This function provides a default implementation for the `fetch_or` method from the `ObjectModel` trait.
394    pub fn fetch_or<T: MetadataValue>(&self, header: Address, val: T, order: Ordering) -> T {
395        #[cfg(debug_assertions)]
396        self.assert_spec::<T>();
397        if self.num_of_bits < 8 {
398            let (lshift, mask) = self.get_shift_and_mask_for_bits();
399            let new_val = (val.to_u8().unwrap() << lshift) & mask;
400            // We do not need to use fetch_ops_on_bits(), we can just set irrelavent bits to 0, and do fetch_or
401            let old_raw_byte =
402                unsafe { <u8 as MetadataValue>::fetch_or(self.meta_addr(header), new_val, order) };
403            let old_val = self.get_bits_from_u8(old_raw_byte);
404            FromPrimitive::from_u8(old_val).unwrap()
405        } else {
406            unsafe { T::fetch_or(self.meta_addr(header), val, order) }
407        }
408    }
409
410    /// This function provides a default implementation for the `fetch_update` method from the `ObjectModel` trait.
411    /// The semantics is the same as Rust's `fetch_update` on atomic types.
412    pub fn fetch_update<T: MetadataValue, F: FnMut(T) -> Option<T> + Copy>(
413        &self,
414        header: Address,
415        set_order: Ordering,
416        fetch_order: Ordering,
417        mut f: F,
418    ) -> std::result::Result<T, T> {
419        #[cfg(debug_assertions)]
420        self.assert_spec::<T>();
421        if self.num_of_bits < 8 {
422            let byte_addr = self.meta_addr(header);
423            unsafe {
424                <u8 as MetadataValue>::fetch_update(
425                    byte_addr,
426                    set_order,
427                    fetch_order,
428                    |raw_byte: u8| {
429                        let old_metadata = self.get_bits_from_u8(raw_byte);
430                        f(FromPrimitive::from_u8(old_metadata).unwrap()).map(|new_val| {
431                            let new_metadata = self.truncate_bits_in_u8(new_val.to_u8().unwrap());
432                            self.set_bits_to_u8(raw_byte, new_metadata)
433                        })
434                    },
435                )
436            }
437            .map(|raw_byte| FromPrimitive::from_u8(self.get_bits_from_u8(raw_byte)).unwrap())
438            .map_err(|raw_byte| FromPrimitive::from_u8(self.get_bits_from_u8(raw_byte)).unwrap())
439        } else {
440            unsafe { T::fetch_update(self.meta_addr(header), set_order, fetch_order, f) }
441        }
442    }
443}
444
445impl fmt::Debug for HeaderMetadataSpec {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        f.write_fmt(format_args!(
448            "HeaderMetadataSpec {{ \
449            **bit_offset: 0x{:x} \
450            **num_of_bits: 0x{:x} \
451            }}",
452            self.bit_offset, self.num_of_bits
453        ))
454    }
455}
456
457#[cfg(all(test, debug_assertions))]
458mod tests {
459    use super::*;
460    use crate::util::address::Address;
461
462    #[test]
463    fn test_valid_specs() {
464        let spec = HeaderMetadataSpec {
465            bit_offset: 0,
466            num_of_bits: 1,
467        };
468        spec.assert_spec::<u8>();
469
470        let spec = HeaderMetadataSpec {
471            bit_offset: 99,
472            num_of_bits: 1,
473        };
474        spec.assert_spec::<u8>();
475
476        let spec = HeaderMetadataSpec {
477            bit_offset: 0,
478            num_of_bits: 8,
479        };
480        spec.assert_spec::<u8>();
481
482        let spec = HeaderMetadataSpec {
483            bit_offset: 8,
484            num_of_bits: 8,
485        };
486        spec.assert_spec::<u8>();
487
488        let spec = HeaderMetadataSpec {
489            bit_offset: 32,
490            num_of_bits: 8,
491        };
492        spec.assert_spec::<u8>();
493    }
494
495    #[test]
496    #[should_panic]
497    fn test_spec_at_unaligned_offset() {
498        let spec = HeaderMetadataSpec {
499            bit_offset: 8,
500            num_of_bits: 16,
501        };
502        spec.assert_spec::<u16>();
503    }
504
505    #[test]
506    #[should_panic]
507    fn test_bits_spec_across_byte() {
508        // bits across byte boundary
509        let spec = HeaderMetadataSpec {
510            bit_offset: 7,
511            num_of_bits: 2,
512        };
513        spec.assert_spec::<u8>();
514    }
515
516    #[test]
517    fn test_negative_bit_offset() {
518        let spec = HeaderMetadataSpec {
519            bit_offset: -1,
520            num_of_bits: 1,
521        };
522        spec.assert_spec::<u8>();
523        assert_eq!(spec.get_shift_and_mask_for_bits(), (7, 0b1000_0000));
524        assert_eq!(spec.byte_offset(), -1);
525        assert_eq!(spec.get_bits_from_u8(0b1000_0000), 1);
526        assert_eq!(spec.get_bits_from_u8(0b0111_1111), 0);
527
528        let spec = HeaderMetadataSpec {
529            bit_offset: -2,
530            num_of_bits: 1,
531        };
532        spec.assert_spec::<u8>();
533        assert_eq!(spec.get_shift_and_mask_for_bits(), (6, 0b0100_0000));
534        assert_eq!(spec.byte_offset(), -1);
535        assert_eq!(spec.get_bits_from_u8(0b0100_0000), 1);
536        assert_eq!(spec.get_bits_from_u8(0b1011_1111), 0);
537
538        let spec = HeaderMetadataSpec {
539            bit_offset: -7,
540            num_of_bits: 1,
541        };
542        spec.assert_spec::<u8>();
543        assert_eq!(spec.get_shift_and_mask_for_bits(), (1, 0b0000_0010));
544        assert_eq!(spec.byte_offset(), -1);
545        assert_eq!(spec.get_bits_from_u8(0b0000_0010), 1);
546        assert_eq!(spec.get_bits_from_u8(0b1111_1101), 0);
547
548        let spec = HeaderMetadataSpec {
549            bit_offset: -8,
550            num_of_bits: 1,
551        };
552        spec.assert_spec::<u8>();
553        assert_eq!(spec.get_shift_and_mask_for_bits(), (0, 0b0000_0001));
554        assert_eq!(spec.byte_offset(), -1);
555        assert_eq!(spec.get_bits_from_u8(0b0000_0001), 1);
556        assert_eq!(spec.get_bits_from_u8(0b1111_1110), 0);
557
558        let spec = HeaderMetadataSpec {
559            bit_offset: -9,
560            num_of_bits: 1,
561        };
562        spec.assert_spec::<u8>();
563        assert_eq!(spec.get_shift_and_mask_for_bits(), (7, 0b1000_0000));
564        assert_eq!(spec.byte_offset(), -2);
565        assert_eq!(spec.get_bits_from_u8(0b1000_0000), 1);
566        assert_eq!(spec.get_bits_from_u8(0b0111_1111), 0);
567    }
568
569    #[test]
570    fn test_get_bits_from_u8() {
571        // 1 bit
572        let spec = HeaderMetadataSpec {
573            bit_offset: 0,
574            num_of_bits: 1,
575        };
576        assert_eq!(spec.get_shift_and_mask_for_bits(), (0, 0b1));
577        assert_eq!(spec.byte_offset(), 0);
578        assert_eq!(spec.get_bits_from_u8(0b0000_0001), 1);
579        assert_eq!(spec.get_bits_from_u8(0b1111_1110), 0);
580
581        let spec = HeaderMetadataSpec {
582            bit_offset: 1,
583            num_of_bits: 1,
584        };
585        assert_eq!(spec.get_shift_and_mask_for_bits(), (1, 0b10));
586        assert_eq!(spec.get_bits_from_u8(0b0000_0010), 1);
587        assert_eq!(spec.get_bits_from_u8(0b1111_1101), 0);
588
589        let spec = HeaderMetadataSpec {
590            bit_offset: 7,
591            num_of_bits: 1,
592        };
593        assert_eq!(spec.get_shift_and_mask_for_bits(), (7, 0b1000_0000));
594        assert_eq!(spec.get_bits_from_u8(0b1000_0000), 1);
595        assert_eq!(spec.get_bits_from_u8(0b0111_1111), 0);
596
597        // 1 bit in the next byte
598        let spec = HeaderMetadataSpec {
599            bit_offset: 8,
600            num_of_bits: 1,
601        };
602        assert_eq!(spec.get_shift_and_mask_for_bits(), (0, 0b1));
603        assert_eq!(spec.get_bits_from_u8(0b0000_0001), 1);
604        assert_eq!(spec.get_bits_from_u8(0b1111_1110), 0);
605
606        // 2 bits
607        let spec = HeaderMetadataSpec {
608            bit_offset: 0,
609            num_of_bits: 2,
610        };
611        assert_eq!(spec.get_shift_and_mask_for_bits(), (0, 0b11));
612        assert_eq!(spec.get_bits_from_u8(0b0000_0011), 0b11);
613        assert_eq!(spec.get_bits_from_u8(0b0000_0010), 0b10);
614        assert_eq!(spec.get_bits_from_u8(0b1111_1110), 0b10);
615
616        let spec = HeaderMetadataSpec {
617            bit_offset: 6,
618            num_of_bits: 2,
619        };
620        assert_eq!(spec.get_shift_and_mask_for_bits(), (6, 0b1100_0000));
621        assert_eq!(spec.get_bits_from_u8(0b1100_0000), 0b11);
622        assert_eq!(spec.get_bits_from_u8(0b1000_0000), 0b10);
623        assert_eq!(spec.get_bits_from_u8(0b1011_1111), 0b10);
624
625        // 2 bits in the next byte
626        let spec = HeaderMetadataSpec {
627            bit_offset: 8,
628            num_of_bits: 2,
629        };
630        assert_eq!(spec.get_shift_and_mask_for_bits(), (0, 0b0000_0011));
631        assert_eq!(spec.get_bits_from_u8(0b0000_0011), 0b11);
632        assert_eq!(spec.get_bits_from_u8(0b0000_0010), 0b10);
633        assert_eq!(spec.get_bits_from_u8(0b1111_1110), 0b10);
634    }
635
636    #[test]
637    fn test_set_bits_to_u8() {
638        // 1 bit
639        let spec = HeaderMetadataSpec {
640            bit_offset: 0,
641            num_of_bits: 1,
642        };
643        assert_eq!(spec.set_bits_to_u8(0b0000_0000, 1), 0b0000_0001);
644        assert_eq!(spec.set_bits_to_u8(0b1111_1111, 1), 0b1111_1111);
645        assert_eq!(spec.set_bits_to_u8(0b1111_1111, 0), 0b1111_1110);
646
647        let spec = HeaderMetadataSpec {
648            bit_offset: 1,
649            num_of_bits: 1,
650        };
651        assert_eq!(spec.set_bits_to_u8(0b0000_0000, 1), 0b0000_0010);
652        assert_eq!(spec.set_bits_to_u8(0b1111_1111, 1), 0b1111_1111);
653        assert_eq!(spec.set_bits_to_u8(0b1111_1111, 0), 0b1111_1101);
654
655        // 2 bit
656        let spec = HeaderMetadataSpec {
657            bit_offset: 0,
658            num_of_bits: 2,
659        };
660        assert_eq!(spec.set_bits_to_u8(0b0000_0000, 0b11), 0b0000_0011);
661        assert_eq!(spec.set_bits_to_u8(0b1111_1111, 0b11), 0b1111_1111);
662        assert_eq!(spec.set_bits_to_u8(0b1111_1111, 0b10), 0b1111_1110);
663        assert_eq!(spec.set_bits_to_u8(0b1111_1111, 0b01), 0b1111_1101);
664        assert_eq!(spec.set_bits_to_u8(0b1111_1111, 0b00), 0b1111_1100);
665    }
666
667    #[test]
668    #[should_panic]
669    fn test_set_bits_to_u8_exceeds_bits() {
670        let spec = HeaderMetadataSpec {
671            bit_offset: 0,
672            num_of_bits: 1,
673        };
674        spec.set_bits_to_u8(0, 0b11);
675    }
676
677    use paste::paste;
678
679    macro_rules! impl_with_object {
680        ($type: ty) => {
681            paste!{
682                fn [<with_ $type _obj>]<F>(f: F) where F: FnOnce(Address, *mut $type) + std::panic::UnwindSafe {
683                    // Allocate a tuple that can hold 3 integers
684                    let ty_size = ($type::BITS >> LOG_BITS_IN_BYTE) as usize;
685                    let layout = std::alloc::Layout::from_size_align(ty_size * 3, ty_size).unwrap();
686                    let (obj, ptr) = {
687                        let ptr_raw: *mut $type = unsafe { std::alloc::alloc_zeroed(layout) as *mut $type };
688                        // Use the mid one for testing, as we can use offset to access the other integers.
689                        let ptr_mid: *mut $type = unsafe { ptr_raw.offset(1) };
690                        // Make sure they are all empty
691                        assert_eq!(unsafe { *(ptr_mid.offset(-1)) }, 0, "memory at offset -1 is not zero");
692                        assert_eq!(unsafe { *ptr_mid }, 0, "memory at offset 0 is not zero");
693                        assert_eq!(unsafe { *(ptr_mid.offset(1)) }, 0, "memory at offset 1 is not zero");
694                        (Address::from_ptr(ptr_mid), ptr_mid)
695                    };
696                    crate::util::test_util::with_cleanup(
697                        || f(obj, ptr),
698                        || {
699                            unsafe { std::alloc::dealloc(ptr.offset(-1) as *mut u8, layout); }
700                        }
701                    )
702                }
703            }
704        }
705    }
706
707    impl_with_object!(u8);
708    impl_with_object!(u16);
709    impl_with_object!(u32);
710    impl_with_object!(u64);
711    impl_with_object!(usize);
712
713    fn max_value(n_bits: usize) -> u64 {
714        (0..n_bits).fold(0, |accum, x| accum + (1 << x))
715    }
716
717    macro_rules! test_header_metadata_access {
718        ($tname: ident, $type: ty, $num_of_bits: expr) => {
719            paste!{
720                #[test]
721                fn [<$tname _load>]() {
722                    [<with_ $type _obj>](|obj, ptr| {
723                        let spec = HeaderMetadataSpec { bit_offset: 0, num_of_bits: $num_of_bits };
724                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
725                        let max_value = max_value($num_of_bits) as $type;
726                        unsafe { *ptr = max_value };
727                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
728                    });
729                }
730
731                #[test]
732                fn [<$tname _load_atomic>]() {
733                    [<with_ $type _obj>](|obj, ptr| {
734                        let spec = HeaderMetadataSpec { bit_offset: 0, num_of_bits: $num_of_bits };
735                        assert_eq!(spec.load_atomic::<$type>(obj, None, Ordering::SeqCst), 0);
736                        let max_value = max_value($num_of_bits) as $type;
737                        unsafe { *ptr = max_value };
738                        assert_eq!(spec.load_atomic::<$type>(obj, None, Ordering::SeqCst), max_value);
739                    });
740                }
741
742                #[test]
743                fn [<$tname _load_next>]() {
744                    [<with_ $type _obj>](|obj, ptr| {
745                        let spec = HeaderMetadataSpec { bit_offset: $num_of_bits, num_of_bits: $num_of_bits };
746                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
747                        let max_value = max_value($num_of_bits) as $type;
748                        if $num_of_bits < 8 {
749                            unsafe { *ptr = max_value << spec.bit_offset}
750                        } else {
751                            unsafe { *(ptr.offset(1)) = max_value };
752                        }
753                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
754                    });
755                }
756
757                #[test]
758                fn [<$tname _load_prev>]() {
759                    [<with_ $type _obj>](|obj, ptr| {
760                        let spec = HeaderMetadataSpec { bit_offset: -$num_of_bits, num_of_bits: $num_of_bits };
761                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
762                        let max_value = max_value($num_of_bits) as $type;
763                        if $num_of_bits < 8 {
764                            unsafe { *(ptr.offset(-1)) = max_value << (BITS_IN_BYTE as isize + spec.bit_offset)}
765                        } else {
766                            unsafe { *(ptr.offset(-1)) = max_value };
767                        }
768                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
769                    });
770                }
771
772                #[test]
773                fn [<$tname _load_mask>]() {
774                    [<with_ $type _obj>](|obj, ptr| {
775                        // The test only runs for metadata no smaller than 1 byte
776                        if $num_of_bits < 8 {
777                            return;
778                        }
779
780                        let spec = HeaderMetadataSpec { bit_offset: 0, num_of_bits: $num_of_bits };
781                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
782                        let max_value = max_value($num_of_bits) as $type;
783                        unsafe { *ptr = max_value };
784                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
785                        assert_eq!(unsafe { spec.load::<$type>(obj, Some(0)) }, 0);
786                        assert_eq!(unsafe { spec.load::<$type>(obj, Some(0b101)) }, 0b101);
787                    });
788                }
789
790                #[test]
791                fn [<$tname _store>]() {
792                    [<with_ $type _obj>](|obj, ptr| {
793                        let spec = HeaderMetadataSpec { bit_offset: 0, num_of_bits: $num_of_bits };
794                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
795                        let max_value = max_value($num_of_bits) as $type;
796                        unsafe { spec.store::<$type>(obj, max_value, None) };
797                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
798                        assert_eq!(unsafe { *ptr }, max_value);
799                    });
800                }
801
802                #[test]
803                fn [<$tname _store_atomic>]() {
804                    [<with_ $type _obj>](|obj, ptr| {
805                        let spec = HeaderMetadataSpec { bit_offset: 0, num_of_bits: $num_of_bits };
806                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
807                        let max_value = max_value($num_of_bits) as $type;
808                        spec.store_atomic::<$type>(obj, max_value, None, Ordering::SeqCst);
809                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
810                        assert_eq!(unsafe { *ptr }, max_value);
811                    });
812                }
813
814                #[test]
815                fn [<$tname _store_next>]() {
816                    [<with_ $type _obj>](|obj, ptr| {
817                        let spec = HeaderMetadataSpec { bit_offset: $num_of_bits, num_of_bits: $num_of_bits };
818                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
819                        let max_value = max_value($num_of_bits) as $type;
820                        unsafe { spec.store::<$type>(obj, max_value, None) };
821                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
822                        if $num_of_bits < 8 {
823                            assert_eq!(unsafe { *ptr }, max_value << spec.bit_offset);
824                        } else {
825                            assert_eq!(unsafe { *(ptr.offset(1)) }, max_value);
826                        }
827                    });
828                }
829
830                #[test]
831                fn [<$tname _store_prev>]() {
832                    [<with_ $type _obj>](|obj, ptr| {
833                        let spec = HeaderMetadataSpec { bit_offset: -$num_of_bits, num_of_bits: $num_of_bits };
834                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
835                        let max_value = max_value($num_of_bits) as $type;
836                        unsafe { spec.store::<$type>(obj, max_value, None) };
837                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
838                        if $num_of_bits < 8 {
839                            assert_eq!(unsafe { *ptr.offset(-1) }, max_value << (BITS_IN_BYTE as isize + spec.bit_offset));
840                        } else {
841                            assert_eq!(unsafe { *(ptr.offset(-1)) }, max_value);
842                        }
843                    });
844                }
845
846                #[test]
847                fn [<$tname _store_mask>]() {
848                    [<with_ $type _obj>](|obj, ptr| {
849                        // The test only runs for metadata no smaller than 1 byte
850                        if $num_of_bits < 8 {
851                            return;
852                        }
853
854                        let spec = HeaderMetadataSpec { bit_offset: 0, num_of_bits: $num_of_bits };
855                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
856                        let max_value = max_value($num_of_bits) as $type;
857
858                        // set to max with mask of all 1s
859                        unsafe { spec.store::<$type>(obj, max_value, Some(max_value)) };
860                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
861
862                        // set to 0
863                        unsafe { spec.store::<$type>(obj, 0, None) };
864
865                        // set to max with mask of 1 bit
866                        unsafe { spec.store::<$type>(obj, max_value, Some(0b10)) };
867                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0b10);
868                        assert_eq!(unsafe { *ptr }, 0b10);
869                    });
870                }
871
872                #[test]
873                fn [<$tname _compare_exchange_success>]() {
874                    [<with_ $type _obj>](|obj, _| {
875                        let spec = HeaderMetadataSpec { bit_offset: 0, num_of_bits: $num_of_bits };
876                        let old_val = unsafe { spec.load::<$type>(obj, None) };
877                        assert_eq!(old_val, 0);
878
879                        let max_value = max_value($num_of_bits) as $type;
880                        let res = spec.compare_exchange::<$type>(obj, old_val, max_value, None, Ordering::SeqCst, Ordering::SeqCst);
881                        assert!(res.is_ok());
882                        assert_eq!(res.unwrap(), old_val);
883                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
884                    })
885                }
886
887                #[test]
888                fn [<$tname _compare_exchange_fail>]() {
889                    [<with_ $type _obj>](|obj, _| {
890                        let spec = HeaderMetadataSpec { bit_offset: 0, num_of_bits: $num_of_bits };
891                        let old_val = unsafe { spec.load::<$type>(obj, None) };
892                        assert_eq!(old_val, 0);
893
894                        // Change the value
895                        unsafe { spec.store::<$type>(obj, 1, None) };
896
897                        let max_value = max_value($num_of_bits) as $type;
898                        let res = spec.compare_exchange::<$type>(obj, old_val, max_value, None, Ordering::SeqCst, Ordering::SeqCst);
899                        assert!(res.is_err());
900                        assert_eq!(res.err().unwrap(), 1);
901                        assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 1);
902                    })
903                }
904
905                #[test]
906                fn [<$tname _fetch_add>]() {
907                    [<with_ $type _obj>](|obj, _| {
908                        for bit_offset in (0isize..($type::BITS as isize)).step_by($num_of_bits) {
909                            let spec = HeaderMetadataSpec { bit_offset, num_of_bits: $num_of_bits };
910                            let max_value = max_value($num_of_bits) as $type;
911
912                            let old_val = unsafe { spec.load::<$type>(obj, None) };
913                            assert_eq!(old_val, 0);
914
915                            let old_val_from_fetch = spec.fetch_add::<$type>(obj, max_value, Ordering::SeqCst);
916                            assert_eq!(old_val, old_val_from_fetch);
917                            assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
918                        }
919                    })
920                }
921
922                #[test]
923                fn [<$tname _fetch_add_overflow>]() {
924                    [<with_ $type _obj>](|obj, ptr| {
925                        for bit_offset in (0isize..($type::BITS as isize)).step_by($num_of_bits) {
926                            let spec = HeaderMetadataSpec { bit_offset, num_of_bits: $num_of_bits };
927                            let max_value = max_value($num_of_bits) as $type;
928
929                            unsafe { spec.store::<$type>(obj, max_value, None) };
930                            let old_val = unsafe { spec.load::<$type>(obj, None) };
931
932                            // add 1 will cause overflow
933                            let old_val_from_fetch = spec.fetch_add::<$type>(obj, 1, Ordering::SeqCst);
934                            assert_eq!(old_val, old_val_from_fetch);
935                            assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
936                            assert_eq!(unsafe { *ptr }, 0); // we should not accidentally affect other bits
937                        }
938                    })
939                }
940
941                #[test]
942                fn [<$tname _fetch_sub>]() {
943                    [<with_ $type _obj>](|obj, _| {
944                        for bit_offset in (0isize..($type::BITS as isize)).step_by($num_of_bits) {
945                            let spec = HeaderMetadataSpec { bit_offset, num_of_bits: $num_of_bits };
946
947                            unsafe { spec.store::<$type>(obj, 1, None) };
948                            let old_val = unsafe { spec.load::<$type>(obj, None) };
949                            assert_eq!(old_val, 1);
950
951                            let old_val_from_fetch = spec.fetch_sub::<$type>(obj, 1, Ordering::SeqCst);
952                            assert_eq!(old_val, old_val_from_fetch);
953                            assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
954                        }
955                    })
956                }
957
958                #[test]
959                fn [<$tname _fetch_sub_overflow>]() {
960                    [<with_ $type _obj>](|obj, _| {
961                        for bit_offset in (0isize..($type::BITS as isize)).step_by($num_of_bits) {
962                            let spec = HeaderMetadataSpec { bit_offset, num_of_bits: $num_of_bits };
963                            let max_value = max_value($num_of_bits) as $type;
964
965                            let old_val = unsafe { spec.load::<$type>(obj, None) };
966                            assert_eq!(old_val, 0);
967
968                            let old_val_from_fetch = spec.fetch_sub::<$type>(obj, 1, Ordering::SeqCst);
969                            assert_eq!(old_val, old_val_from_fetch);
970                            assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
971                        }
972                    })
973                }
974
975                #[test]
976                fn [<$tname _fetch_and>]() {
977                    [<with_ $type _obj>](|obj, _| {
978                        for bit_offset in (0isize..($type::BITS as isize)).step_by($num_of_bits) {
979                            let spec = HeaderMetadataSpec { bit_offset, num_of_bits: $num_of_bits };
980                            let max_value = max_value($num_of_bits) as $type;
981
982                            let old_val = unsafe { spec.load::<$type>(obj, None) };
983                            assert_eq!(old_val, 0);
984
985                            let old_val_from_fetch = spec.fetch_and::<$type>(obj, max_value, Ordering::SeqCst);
986                            assert_eq!(old_val, old_val_from_fetch);
987                            assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
988                        }
989                    })
990                }
991
992                #[test]
993                fn [<$tname _fetch_or>]() {
994                    [<with_ $type _obj>](|obj, _| {
995                        for bit_offset in (0isize..($type::BITS as isize)).step_by($num_of_bits) {
996                            let spec = HeaderMetadataSpec { bit_offset, num_of_bits: $num_of_bits };
997                            let max_value = max_value($num_of_bits) as $type;
998
999                            let old_val = unsafe { spec.load::<$type>(obj, None) };
1000                            assert_eq!(old_val, 0);
1001
1002                            let old_val_from_fetch = spec.fetch_or::<$type>(obj, max_value, Ordering::SeqCst);
1003                            assert_eq!(old_val, old_val_from_fetch);
1004                            assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
1005                        }
1006                    })
1007                }
1008
1009                #[test]
1010                fn [<$tname _fetch_update_success>]() {
1011                    [<with_ $type _obj>](|obj, _| {
1012                        for bit_offset in (0isize..($type::BITS as isize)).step_by($num_of_bits) {
1013                            let spec = HeaderMetadataSpec { bit_offset, num_of_bits: $num_of_bits };
1014                            let max_value = max_value($num_of_bits) as $type;
1015
1016                            let old_val = unsafe { spec.load::<$type>(obj, None) };
1017                            assert_eq!(old_val, 0);
1018
1019                            let update_res = spec.fetch_update(obj, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| Some(max_value));
1020                            assert!(update_res.is_ok());
1021                            assert_eq!(old_val, update_res.unwrap());
1022                            assert_eq!(unsafe { spec.load::<$type>(obj, None) }, max_value);
1023                        }
1024                    })
1025                }
1026
1027                #[test]
1028                fn [<$tname _fetch_update_fail>]() {
1029                    [<with_ $type _obj>](|obj, _| {
1030                        for bit_offset in (0isize..($type::BITS as isize)).step_by($num_of_bits) {
1031                            let spec = HeaderMetadataSpec { bit_offset, num_of_bits: $num_of_bits };
1032
1033                            let old_val = unsafe { spec.load::<$type>(obj, None) };
1034                            assert_eq!(old_val, 0);
1035
1036                            let update_res = spec.fetch_update(obj, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| None);
1037                            assert!(update_res.is_err());
1038                            assert_eq!(old_val, update_res.err().unwrap());
1039                            assert_eq!(unsafe { spec.load::<$type>(obj, None) }, 0);
1040                        }
1041                    })
1042                }
1043            }
1044        }
1045    }
1046
1047    test_header_metadata_access!(test_u1, u8, 1);
1048    test_header_metadata_access!(test_u2, u8, 2);
1049    test_header_metadata_access!(test_u4, u8, 4);
1050    test_header_metadata_access!(test_u8, u8, 8);
1051    test_header_metadata_access!(test_u16, u16, 16);
1052    test_header_metadata_access!(test_u32, u32, 32);
1053    test_header_metadata_access!(test_u64, u64, 64);
1054    test_header_metadata_access!(
1055        test_usize,
1056        usize,
1057        if cfg!(target_pointer_width = "64") {
1058            64
1059        } else if cfg!(target_pointer_width = "32") {
1060            32
1061        } else {
1062            unreachable!()
1063        }
1064    );
1065}