mmtk/util/metadata/side_metadata/
global.rs

1use super::*;
2use crate::util::constants::{BYTES_IN_PAGE, BYTES_IN_WORD, LOG_BITS_IN_BYTE};
3use crate::util::conversions::raw_align_up;
4use crate::util::heap::layout::vm_layout::BYTES_IN_CHUNK;
5use crate::util::metadata::metadata_val_traits::*;
6use crate::util::metadata::side_metadata::layout::*;
7#[cfg(feature = "vo_bit")]
8use crate::util::metadata::vo_bit::VO_BIT_SIDE_METADATA_SPEC;
9use crate::util::os::*;
10use crate::util::Address;
11use crate::MMAPPER;
12use num_traits::FromPrimitive;
13use ranges::BitByteRange;
14use std::fmt;
15use std::sync::atomic::{AtomicU8, Ordering};
16
17/// This struct stores the specification of a side metadata bit-set.
18/// It is used as an input to the (inline) functions provided by the side metadata module.
19///
20/// Each plan or policy which uses a metadata bit-set, needs to create an instance of this struct.
21///
22/// For performance reasons, objects of this struct should be constants.
23#[derive(Clone, Copy, PartialEq, Eq, Hash)]
24pub struct SideMetadataSpec {
25    /// The name for this side metadata.
26    pub name: &'static str,
27    /// Is this side metadata global? Local metadata is used by certain spaces,
28    /// while global metadata is used by all the spaces.
29    pub is_global: bool,
30    /// The offset for this side metadata.
31    pub offset: usize,
32    /// Number of bits needed per region. E.g. 0 = 1 bit, 1 = 2 bit.
33    pub log_num_of_bits: usize,
34    /// Number of bytes of the region. E.g. 3 = 8 bytes, 12 = 4096 bytes.
35    pub log_bytes_in_region: usize,
36}
37
38impl SideMetadataSpec {
39    /// Is this spec using contiguous side metadata? If not, it uses chunked side metadata.
40    pub const fn uses_contiguous_side_metadata(&self) -> bool {
41        self.is_global || cfg!(target_pointer_width = "64")
42    }
43
44    /// Is this spec using chunked side metadata? If not, it uses contiguous side metadata.
45    pub const fn uses_chunked_side_metadata(&self) -> bool {
46        !self.uses_contiguous_side_metadata()
47    }
48
49    /// Get the starting address for a spec of contiguous side metadata.
50    pub fn get_starting_address(&self) -> Address {
51        debug_assert!(self.uses_contiguous_side_metadata());
52        let base = global_side_metadata_base_address();
53        base + self.offset
54    }
55
56    /// Get the relative offset for a spec of chunked side metadata.
57    pub const fn get_offset_for_chunked(&self) -> usize {
58        debug_assert!(self.uses_chunked_side_metadata());
59        self.offset
60    }
61
62    /// Return the upperbound offset for the side metadata. The next side metadata should be laid out at this offset.
63    #[cfg(target_pointer_width = "64")]
64    pub const fn upper_bound_offset(&self) -> usize {
65        debug_assert!(self.uses_contiguous_side_metadata());
66        self.offset + metadata_address_range_size(self)
67    }
68
69    /// Return the upperbound offset for the side metadata. The next side metadata should be laid out at this offset.
70    #[cfg(target_pointer_width = "32")]
71    pub const fn upper_bound_offset(&self) -> usize {
72        if self.uses_contiguous_side_metadata() {
73            self.offset + metadata_address_range_size(self)
74        } else {
75            self.offset + metadata_bytes_per_chunk(self.log_bytes_in_region, self.log_num_of_bits)
76        }
77    }
78
79    /// The upper bound address for metadata address computed for this global spec. The computed metadata address
80    /// should never be larger than this address. Otherwise, we are accessing the metadata that is laid out
81    /// after this spec. This spec must be a contiguous side metadata spec (which uses address
82    /// as offset).
83    pub fn upper_bound_address_for_contiguous(&self) -> Address {
84        debug_assert!(self.uses_contiguous_side_metadata());
85        self.get_starting_address() + metadata_address_range_size(self)
86    }
87
88    /// The upper bound address for metadata address computed for this global spec. The computed metadata address
89    /// should never be larger than this address. Otherwise, we are accessing the metadata that is laid out
90    /// after this spec. This spec must be a chunked side metadata spec (which uses relative offset). Only 32 bit local
91    /// side metadata uses chunked metadata.
92    #[cfg(target_pointer_width = "32")]
93    pub fn upper_bound_address_for_chunked(&self, data_addr: Address) -> Address {
94        debug_assert!(self.uses_chunked_side_metadata());
95        address_to_meta_chunk_addr(data_addr) + self.upper_bound_offset()
96    }
97
98    /// Used only for debugging.
99    /// This panics if the required metadata is not mapped
100    #[cfg(debug_assertions)]
101    pub(crate) fn assert_metadata_mapped(&self, data_addr: Address) {
102        let meta_start = address_to_meta_address(self, data_addr).align_down(BYTES_IN_PAGE);
103
104        trace!(
105            "ensure_metadata_is_mapped({}).meta_start({})",
106            data_addr,
107            meta_start
108        );
109
110        OS::panic_if_unmapped(meta_start, BYTES_IN_PAGE);
111    }
112
113    #[cfg(debug_assertions)]
114    pub(crate) fn are_different_metadata_bits(&self, addr1: Address, addr2: Address) -> bool {
115        let a1 = address_to_meta_address(self, addr1);
116        let a2 = address_to_meta_address(self, addr2);
117        let s1 = meta_byte_lshift(self, addr1);
118        let s2 = meta_byte_lshift(self, addr2);
119        (a1, s1) != (a2, s2)
120    }
121
122    /// Used only for debugging.
123    /// * Assert if the given MetadataValue type matches the spec.
124    /// * Assert if the provided value is valid in the spec.
125    #[cfg(debug_assertions)]
126    fn assert_value_type<T: MetadataValue>(&self, val: Option<T>) {
127        let log_b = self.log_num_of_bits;
128        match log_b {
129            _ if log_b < 3 => {
130                assert_eq!(T::LOG2, 3);
131                if let Some(v) = val {
132                    assert!(
133                        v.to_u8().unwrap() < (1 << (1 << log_b)),
134                        "Input value {:?} is invalid for the spec {:?}",
135                        v,
136                        self
137                    );
138                }
139            }
140            3..=6 => assert_eq!(T::LOG2, log_b as u32),
141            _ => unreachable!("side metadata > {}-bits is not supported", 1 << log_b),
142        }
143    }
144
145    /// Check with the mmapper to see if side metadata is mapped for the spec for the data address.
146    pub(crate) fn is_mapped(&self, data_addr: Address) -> bool {
147        use crate::MMAPPER;
148        let meta_addr = address_to_meta_address(self, data_addr);
149        MMAPPER.is_mapped_address(meta_addr)
150    }
151
152    /// This method is used for bulk zeroing side metadata for a data address range.
153    pub(crate) fn zero_meta_bits(
154        meta_start_addr: Address,
155        meta_start_bit: u8,
156        meta_end_addr: Address,
157        meta_end_bit: u8,
158    ) {
159        let mut visitor = |range| {
160            match range {
161                BitByteRange::Bytes { start, end } => {
162                    crate::util::memory::zero(start, end - start);
163                    false
164                }
165                BitByteRange::BitsInByte {
166                    addr,
167                    bit_start,
168                    bit_end,
169                } => {
170                    // we are zeroing selected bit in one byte
171                    // Get a mask that the bits we need to zero are set to zero, and the other bits are 1.
172                    let mask: u8 =
173                        u8::MAX.checked_shl(bit_end as u32).unwrap_or(0) | !(u8::MAX << bit_start);
174                    unsafe { addr.as_ref::<AtomicU8>() }.fetch_and(mask, Ordering::SeqCst);
175                    false
176                }
177            }
178        };
179        ranges::break_bit_range(
180            meta_start_addr,
181            meta_start_bit,
182            meta_end_addr,
183            meta_end_bit,
184            true,
185            &mut visitor,
186        );
187    }
188
189    /// This method is used for bulk setting side metadata for a data address range.
190    pub(crate) fn set_meta_bits(
191        meta_start_addr: Address,
192        meta_start_bit: u8,
193        meta_end_addr: Address,
194        meta_end_bit: u8,
195    ) {
196        let mut visitor = |range| {
197            match range {
198                BitByteRange::Bytes { start, end } => {
199                    crate::util::memory::set(start, 0xff, end - start);
200                    false
201                }
202                BitByteRange::BitsInByte {
203                    addr,
204                    bit_start,
205                    bit_end,
206                } => {
207                    // we are setting selected bits in one byte
208                    // Get a mask that the bits we need to set are 1, and the other bits are 0.
209                    let mask: u8 = !(u8::MAX.checked_shl(bit_end as u32).unwrap_or(0))
210                        & (u8::MAX << bit_start);
211                    unsafe { addr.as_ref::<AtomicU8>() }.fetch_or(mask, Ordering::SeqCst);
212                    false
213                }
214            }
215        };
216        ranges::break_bit_range(
217            meta_start_addr,
218            meta_start_bit,
219            meta_end_addr,
220            meta_end_bit,
221            true,
222            &mut visitor,
223        );
224    }
225
226    /// This method does bulk update for the given data range. It calculates the metadata bits for the given data range,
227    /// and invoke the given method to update the metadata bits.
228    pub(super) fn bulk_update_metadata(
229        &self,
230        start: Address,
231        size: usize,
232        update_meta_bits: &impl Fn(Address, u8, Address, u8),
233    ) {
234        // Update bits for a contiguous side metadata spec. We can simply calculate the data end address, and
235        // calculate the metadata address for the data end.
236        let update_contiguous = |data_start: Address, data_bytes: usize| {
237            if data_bytes == 0 {
238                return;
239            }
240            let meta_start = address_to_meta_address(self, data_start);
241            let meta_start_shift = meta_byte_lshift(self, data_start);
242            let meta_end = address_to_meta_address(self, data_start + data_bytes);
243            let meta_end_shift = meta_byte_lshift(self, data_start + data_bytes);
244            update_meta_bits(meta_start, meta_start_shift, meta_end, meta_end_shift);
245        };
246
247        // Update bits for a discontiguous side metadata spec (chunked metadata). The side metadata for different
248        // chunks are stored in discontiguous memory. For example, Chunk #2 follows Chunk #1, but the side metadata
249        // for Chunk #2 does not immediately follow the side metadata for Chunk #1. So when we bulk update metadata for Chunk #1,
250        // we cannot update up to the metadata address for the Chunk #2 start. Otherwise it may modify unrelated metadata
251        // between the two chunks' metadata.
252        // Instead, we compute how many bytes/bits we need to update.
253        // The data for which the metadata will be updates has to be in the same chunk.
254        #[cfg(target_pointer_width = "32")]
255        let update_discontiguous = |data_start: Address, data_bytes: usize| {
256            use crate::util::constants::BITS_IN_BYTE;
257            if data_bytes == 0 {
258                return;
259            }
260            debug_assert_eq!(
261                data_start.align_down(BYTES_IN_CHUNK),
262                (data_start + data_bytes - 1).align_down(BYTES_IN_CHUNK),
263                "The data to be zeroed in discontiguous specs needs to be in the same chunk"
264            );
265            let meta_start = address_to_meta_address(self, data_start);
266            let meta_start_shift = meta_byte_lshift(self, data_start);
267            // How many bits we need to zero for data_bytes
268            let meta_total_bits = (data_bytes >> self.log_bytes_in_region) << self.log_num_of_bits;
269            let meta_delta_bytes = meta_total_bits >> LOG_BITS_IN_BYTE;
270            let meta_delta_bits: u8 = (meta_total_bits % BITS_IN_BYTE) as u8;
271            // Calculate the end byte/addr and end bit
272            let (meta_end, meta_end_shift) = {
273                let mut end_addr = meta_start + meta_delta_bytes;
274                let mut end_bit = meta_start_shift + meta_delta_bits;
275                if end_bit >= BITS_IN_BYTE as u8 {
276                    end_bit -= BITS_IN_BYTE as u8;
277                    end_addr += 1usize;
278                }
279                (end_addr, end_bit)
280            };
281
282            update_meta_bits(meta_start, meta_start_shift, meta_end, meta_end_shift);
283        };
284
285        if cfg!(target_pointer_width = "64") || self.is_global {
286            update_contiguous(start, size);
287        }
288        #[cfg(target_pointer_width = "32")]
289        if !self.is_global {
290            // per chunk policy-specific metadata for 32-bits targets
291            let chunk_num = ((start + size).align_down(BYTES_IN_CHUNK)
292                - start.align_down(BYTES_IN_CHUNK))
293                / BYTES_IN_CHUNK;
294            if chunk_num == 0 {
295                update_discontiguous(start, size);
296            } else {
297                let second_data_chunk = start.align_up(BYTES_IN_CHUNK);
298                // bzero the first sub-chunk
299                update_discontiguous(start, second_data_chunk - start);
300
301                let last_data_chunk = (start + size).align_down(BYTES_IN_CHUNK);
302                // bzero the last sub-chunk
303                update_discontiguous(last_data_chunk, start + size - last_data_chunk);
304                let mut next_data_chunk = second_data_chunk;
305
306                // bzero all chunks in the middle
307                while next_data_chunk != last_data_chunk {
308                    update_discontiguous(next_data_chunk, BYTES_IN_CHUNK);
309                    next_data_chunk += BYTES_IN_CHUNK;
310                }
311            }
312        }
313    }
314
315    /// Bulk-zero a specific metadata for a memory region. Note that this method is more sophisiticated than a simple memset, especially in the following
316    /// cases:
317    /// * the metadata for the range includes partial bytes (a few bits in the same byte).
318    /// * for 32 bits local side metadata, the side metadata is stored in discontiguous chunks, we will have to bulk zero for each chunk's side metadata.
319    ///
320    /// # Arguments
321    ///
322    /// * `start`: The starting address of a memory region. The side metadata starting from this data address will be zeroed.
323    /// * `size`: The size of the memory region.
324    pub fn bzero_metadata(&self, start: Address, size: usize) {
325        #[cfg(feature = "extreme_assertions")]
326        let _lock = sanity::SANITY_LOCK.lock().unwrap();
327
328        #[cfg(feature = "extreme_assertions")]
329        sanity::verify_bzero(self, start, size);
330
331        self.bulk_update_metadata(start, size, &Self::zero_meta_bits)
332    }
333
334    /// Bulk set a specific metadata for a memory region. Note that this method is more sophisiticated than a simple memset, especially in the following
335    /// cases:
336    /// * the metadata for the range includes partial bytes (a few bits in the same byte).
337    /// * for 32 bits local side metadata, the side metadata is stored in discontiguous chunks, we will have to bulk set for each chunk's side metadata.
338    ///
339    /// # Arguments
340    ///
341    /// * `start`: The starting address of a memory region. The side metadata starting from this data address will be set to all 1s in the bits.
342    /// * `size`: The size of the memory region.
343    pub fn bset_metadata(&self, start: Address, size: usize) {
344        #[cfg(feature = "extreme_assertions")]
345        let _lock = sanity::SANITY_LOCK.lock().unwrap();
346
347        #[cfg(feature = "extreme_assertions")]
348        sanity::verify_bset(self, start, size);
349
350        self.bulk_update_metadata(start, size, &Self::set_meta_bits)
351    }
352
353    /// Bulk copy the `other` side metadata for a memory region to this side metadata.
354    ///
355    /// This function only works for contiguous metadata.
356    /// Curently all global metadata are contiguous.
357    /// It also requires the other metadata to have the same number of bits per region
358    /// and the same region size.
359    ///
360    /// # Arguments
361    ///
362    /// * `start`: The starting address of a memory region.
363    /// * `size`: The size of the memory region.
364    /// * `other`: The other metadata to copy from.
365    pub fn bcopy_metadata_contiguous(&self, start: Address, size: usize, other: &SideMetadataSpec) {
366        #[cfg(feature = "extreme_assertions")]
367        let _lock = sanity::SANITY_LOCK.lock().unwrap();
368
369        #[cfg(feature = "extreme_assertions")]
370        sanity::verify_bcopy(self, start, size, other);
371
372        debug_assert_eq!(other.log_bytes_in_region, self.log_bytes_in_region);
373        debug_assert_eq!(other.log_num_of_bits, self.log_num_of_bits);
374
375        let dst_meta_start_addr = address_to_meta_address(self, start);
376        let dst_meta_start_bit = meta_byte_lshift(self, start);
377        let dst_meta_end_addr = address_to_meta_address(self, start + size);
378        let dst_meta_end_bit = meta_byte_lshift(self, start + size);
379
380        let src_meta_start_addr = address_to_meta_address(other, start);
381        let src_meta_start_bit = meta_byte_lshift(other, start);
382
383        debug_assert_eq!(dst_meta_start_bit, src_meta_start_bit);
384
385        let mut visitor = |range| {
386            match range {
387                BitByteRange::Bytes {
388                    start: dst_start,
389                    end: dst_end,
390                } => unsafe {
391                    let byte_offset = dst_start - dst_meta_start_addr;
392                    let src_start = src_meta_start_addr + byte_offset;
393                    let size = dst_end - dst_start;
394                    std::ptr::copy::<u8>(src_start.to_ptr(), dst_start.to_mut_ptr(), size);
395                    false
396                },
397                BitByteRange::BitsInByte {
398                    addr: dst,
399                    bit_start,
400                    bit_end,
401                } => {
402                    let byte_offset = dst - dst_meta_start_addr;
403                    let src = src_meta_start_addr + byte_offset;
404                    // we are setting selected bits in one byte
405                    let mask: u8 = !(u8::MAX.checked_shl(bit_end as u32).unwrap_or(0))
406                        & (u8::MAX << bit_start); // Get a mask that the bits we need to set are 1, and the other bits are 0.
407                    let old_src = unsafe { src.as_ref::<AtomicU8>() }.load(Ordering::Relaxed);
408                    let old_dst = unsafe { dst.as_ref::<AtomicU8>() }.load(Ordering::Relaxed);
409                    let new = (old_src & mask) | (old_dst & !mask);
410                    unsafe { dst.as_ref::<AtomicU8>() }.store(new, Ordering::Relaxed);
411                    false
412                }
413            }
414        };
415
416        ranges::break_bit_range(
417            dst_meta_start_addr,
418            dst_meta_start_bit,
419            dst_meta_end_addr,
420            dst_meta_end_bit,
421            true,
422            &mut visitor,
423        );
424    }
425
426    /// This is a wrapper method for implementing side metadata access. It does nothing other than
427    /// calling the access function with no overhead, but in debug builds,
428    /// it includes multiple checks to make sure the access is sane.
429    /// * check whether the given value type matches the number of bits for the side metadata.
430    /// * check if the side metadata memory is mapped.
431    /// * check if the side metadata content is correct based on a sanity map (only for extreme assertions).
432    #[allow(unused_variables)] // data_addr/input is not used in release build
433    fn side_metadata_access<
434        const CHECK_VALUE: bool,
435        T: MetadataValue,
436        R: Copy,
437        F: FnOnce() -> R,
438        V: FnOnce(R),
439    >(
440        &self,
441        data_addr: Address,
442        input: Option<T>,
443        access_func: F,
444        verify_func: V,
445    ) -> R {
446        // With extreme assertions, we maintain a sanity table for each side metadata access. For whatever we store in
447        // side metadata, we store in the sanity table. So we can use that table to check if its results are conssitent
448        // with the actual side metadata.
449        // To achieve this, we need to apply a lock when we access side metadata. This will hide some concurrency bugs,
450        // but makes it possible for us to assert our side metadata implementation is correct.
451        #[cfg(feature = "extreme_assertions")]
452        let _lock = sanity::SANITY_LOCK.lock().unwrap();
453
454        // A few checks
455        #[cfg(debug_assertions)]
456        {
457            if CHECK_VALUE {
458                self.assert_value_type::<T>(input);
459            }
460            #[cfg(feature = "extreme_assertions")]
461            self.assert_metadata_mapped(data_addr);
462        }
463
464        // Actual access to the side metadata
465        let ret = access_func();
466
467        // Verifying the side metadata: checks the result with the sanity table, or store some results to the sanity table
468        if CHECK_VALUE {
469            verify_func(ret);
470        }
471
472        ret
473    }
474
475    /// Non-atomic load of metadata.
476    ///
477    /// # Safety
478    ///
479    /// This is unsafe because:
480    ///
481    /// 1. Concurrent access to this operation is undefined behaviour.
482    /// 2. Interleaving Non-atomic and atomic operations is undefined behaviour.
483    pub unsafe fn load<T: MetadataValue>(&self, data_addr: Address) -> T {
484        self.side_metadata_access::<true, T, _, _, _>(
485            data_addr,
486            None,
487            || {
488                let meta_addr = address_to_meta_address(self, data_addr);
489                let bits_num_log = self.log_num_of_bits;
490                if bits_num_log < 3 {
491                    let lshift = meta_byte_lshift(self, data_addr);
492                    let mask = meta_byte_mask(self) << lshift;
493                    let byte_val = meta_addr.load::<u8>();
494
495                    FromPrimitive::from_u8((byte_val & mask) >> lshift).unwrap()
496                } else {
497                    meta_addr.load::<T>()
498                }
499            },
500            |_v| {
501                #[cfg(feature = "extreme_assertions")]
502                sanity::verify_load(self, data_addr, _v);
503            },
504        )
505    }
506
507    /// Non-atomic store of metadata.
508    ///
509    /// # Safety
510    ///
511    /// This is unsafe because:
512    ///
513    /// 1. Concurrent access to this operation is undefined behaviour.
514    /// 2. Interleaving Non-atomic and atomic operations is undefined behaviour.
515    pub unsafe fn store<T: MetadataValue>(&self, data_addr: Address, metadata: T) {
516        self.side_metadata_access::<true, T, _, _, _>(
517            data_addr,
518            Some(metadata),
519            || {
520                let meta_addr = address_to_meta_address(self, data_addr);
521                let bits_num_log = self.log_num_of_bits;
522                if bits_num_log < 3 {
523                    let lshift = meta_byte_lshift(self, data_addr);
524                    let mask = meta_byte_mask(self) << lshift;
525                    let old_val = meta_addr.load::<u8>();
526                    let new_val = (old_val & !mask) | (metadata.to_u8().unwrap() << lshift);
527
528                    meta_addr.store::<u8>(new_val);
529                } else {
530                    meta_addr.store::<T>(metadata);
531                }
532            },
533            |_| {
534                #[cfg(feature = "extreme_assertions")]
535                sanity::verify_store(self, data_addr, metadata);
536            },
537        )
538    }
539
540    /// Non-atomically load a raw byte from the side metadata byte that is mapped to the data address.
541    /// Unlike [`SideMetadataSpec::load`], this always reads a whole byte regardless of the number of
542    /// bits used by this spec, and does not mask/shift out unrelated bits sharing that byte.
543    pub fn load_byte(&self, data_addr: Address) -> u8 {
544        let meta_addr = address_to_meta_address(self, data_addr);
545        unsafe { meta_addr.load::<u8>() }
546    }
547
548    /// Non-atomically store a raw byte to the side metadata byte that is mapped to the data address.
549    ///
550    /// # Safety
551    ///
552    /// This is unsafe because:
553    ///
554    /// 1. Concurrent access to this operation is undefined behaviour.
555    /// 2. Interleaving non-atomic and atomic operations is undefined behaviour.
556    pub unsafe fn store_byte_relaxed(&self, data_addr: Address, byte: u8) {
557        let meta_addr = address_to_meta_address(self, data_addr);
558        meta_addr.store::<u8>(byte);
559    }
560
561    /// Loads a value from the side metadata for the given address.
562    /// This method has similar semantics to `store` in Rust atomics.
563    pub fn load_atomic<T: MetadataValue>(&self, data_addr: Address, order: Ordering) -> T {
564        self.side_metadata_access::<true, T, _, _, _>(
565            data_addr,
566            None,
567            || {
568                let meta_addr = address_to_meta_address(self, data_addr);
569                let bits_num_log = self.log_num_of_bits;
570                if bits_num_log < 3 {
571                    let lshift = meta_byte_lshift(self, data_addr);
572                    let mask = meta_byte_mask(self) << lshift;
573                    let byte_val = unsafe { meta_addr.atomic_load::<AtomicU8>(order) };
574                    FromPrimitive::from_u8((byte_val & mask) >> lshift).unwrap()
575                } else {
576                    unsafe { T::load_atomic(meta_addr, order) }
577                }
578            },
579            |_v| {
580                #[cfg(feature = "extreme_assertions")]
581                sanity::verify_load(self, data_addr, _v);
582            },
583        )
584    }
585
586    /// Store the given value to the side metadata for the given address.
587    /// This method has similar semantics to `store` in Rust atomics.
588    pub fn store_atomic<T: MetadataValue>(&self, data_addr: Address, metadata: T, order: Ordering) {
589        self.side_metadata_access::<true, T, _, _, _>(
590            data_addr,
591            Some(metadata),
592            || {
593                let meta_addr = address_to_meta_address(self, data_addr);
594                let bits_num_log = self.log_num_of_bits;
595                if bits_num_log < 3 {
596                    let lshift = meta_byte_lshift(self, data_addr);
597                    let mask = meta_byte_mask(self) << lshift;
598                    let metadata_u8 = metadata.to_u8().unwrap();
599                    let _ = unsafe {
600                        <u8 as MetadataValue>::fetch_update(meta_addr, order, order, |v: u8| {
601                            Some((v & !mask) | (metadata_u8 << lshift))
602                        })
603                    };
604                } else {
605                    unsafe {
606                        T::store_atomic(meta_addr, metadata, order);
607                    }
608                }
609            },
610            |_| {
611                #[cfg(feature = "extreme_assertions")]
612                sanity::verify_store(self, data_addr, metadata);
613            },
614        )
615    }
616
617    /// Non-atomically store zero to the side metadata for the given address.
618    /// This method mainly facilitates clearing multiple metadata specs for the same address in a loop.
619    ///
620    /// # Safety
621    ///
622    /// This is unsafe because:
623    ///
624    /// 1. Concurrent access to this operation is undefined behaviour.
625    /// 2. Interleaving Non-atomic and atomic operations is undefined behaviour.
626    pub unsafe fn set_zero(&self, data_addr: Address) {
627        use num_traits::Zero;
628        match self.log_num_of_bits {
629            0..=3 => self.store(data_addr, u8::zero()),
630            4 => self.store(data_addr, u16::zero()),
631            5 => self.store(data_addr, u32::zero()),
632            6 => self.store(data_addr, u64::zero()),
633            _ => unreachable!(),
634        }
635    }
636
637    /// Atomiccally store zero to the side metadata for the given address.
638    /// This method mainly facilitates clearing multiple metadata specs for the same address in a loop.
639    pub fn set_zero_atomic(&self, data_addr: Address, order: Ordering) {
640        use num_traits::Zero;
641        match self.log_num_of_bits {
642            0..=3 => self.store_atomic(data_addr, u8::zero(), order),
643            4 => self.store_atomic(data_addr, u16::zero(), order),
644            5 => self.store_atomic(data_addr, u32::zero(), order),
645            6 => self.store_atomic(data_addr, u64::zero(), order),
646            _ => unreachable!(),
647        }
648    }
649
650    /// Atomically store one to the side metadata for the data address with the _possible_ side effect of corrupting
651    /// and setting the entire byte in the side metadata to 0xff. This can only be used for side metadata smaller
652    /// than a byte.
653    /// This means it does not only set the side metadata for the data address, and it may also have a side effect of
654    /// corrupting and setting the side metadata for the adjacent data addresses. This method is only intended to be
655    /// used as an optimization to skip masking and setting bits in some scenarios where setting adjancent bits to 1 is benign.
656    ///
657    /// # Safety
658    /// This method _may_ corrupt and set adjacent bits in the side metadata as a side effect. The user must
659    /// make sure that this behavior is correct and must not rely on the side effect of this method to set bits.
660    pub unsafe fn set_raw_byte_atomic(&self, data_addr: Address, order: Ordering) {
661        debug_assert!(self.log_num_of_bits < 3);
662        cfg_if::cfg_if! {
663            if #[cfg(feature = "extreme_assertions")] {
664                // For extreme assertions, we only set 1 to the given address.
665                self.store_atomic::<u8>(data_addr, 1, order)
666            } else {
667                self.side_metadata_access::<false, u8, _, _, _>(
668                    data_addr,
669                    Some(1u8),
670                    || {
671                        let meta_addr = address_to_meta_address(self, data_addr);
672                        u8::store_atomic(meta_addr, 0xffu8, order);
673                    },
674                    |_| {}
675                )
676            }
677        }
678    }
679
680    /// Load the raw byte in the side metadata byte that is mapped to the data address.
681    ///
682    /// # Safety
683    /// This is unsafe because:
684    ///
685    /// 1. Concurrent access to this operation is undefined behaviour.
686    /// 2. Interleaving Non-atomic and atomic operations is undefined behaviour.
687    pub unsafe fn load_raw_byte(&self, data_addr: Address) -> u8 {
688        debug_assert!(self.log_num_of_bits < 3);
689        self.side_metadata_access::<false, u8, _, _, _>(
690            data_addr,
691            None,
692            || {
693                let meta_addr = address_to_meta_address(self, data_addr);
694                meta_addr.load::<u8>()
695            },
696            |_| {},
697        )
698    }
699
700    /// Load the raw word that includes the side metadata byte mapped to the data address.
701    ///
702    /// # Safety
703    /// This is unsafe because:
704    ///
705    /// 1. Concurrent access to this operation is undefined behaviour.
706    /// 2. Interleaving Non-atomic and atomic operations is undefined behaviour.
707    pub unsafe fn load_raw_word(&self, data_addr: Address) -> usize {
708        use crate::util::constants::*;
709        debug_assert!(self.log_num_of_bits < (LOG_BITS_IN_BYTE + LOG_BYTES_IN_ADDRESS) as usize);
710        self.side_metadata_access::<false, usize, _, _, _>(
711            data_addr,
712            None,
713            || {
714                let meta_addr = address_to_meta_address(self, data_addr);
715                let aligned_meta_addr = meta_addr.align_down(BYTES_IN_ADDRESS);
716                aligned_meta_addr.load::<usize>()
717            },
718            |_| {},
719        )
720    }
721
722    /// Stores the new value into the side metadata for the gien address if the current value is the same as the old value.
723    /// This method has similar semantics to `compare_exchange` in Rust atomics.
724    /// The return value is a result indicating whether the new value was written and containing the previous value.
725    /// On success this value is guaranteed to be equal to current.
726    pub fn compare_exchange_atomic<T: MetadataValue>(
727        &self,
728        data_addr: Address,
729        old_metadata: T,
730        new_metadata: T,
731        success_order: Ordering,
732        failure_order: Ordering,
733    ) -> std::result::Result<T, T> {
734        self.side_metadata_access::<true, T, _, _, _>(
735            data_addr,
736            Some(new_metadata),
737            || {
738                let meta_addr = address_to_meta_address(self, data_addr);
739                let bits_num_log = self.log_num_of_bits;
740                if bits_num_log < 3 {
741                    let lshift = meta_byte_lshift(self, data_addr);
742                    let mask = meta_byte_mask(self) << lshift;
743
744                    let real_old_byte = unsafe { meta_addr.atomic_load::<AtomicU8>(success_order) };
745                    let expected_old_byte =
746                        (real_old_byte & !mask) | ((old_metadata.to_u8().unwrap()) << lshift);
747                    let expected_new_byte =
748                        (expected_old_byte & !mask) | ((new_metadata.to_u8().unwrap()) << lshift);
749
750                    unsafe {
751                        meta_addr.compare_exchange::<AtomicU8>(
752                            expected_old_byte,
753                            expected_new_byte,
754                            success_order,
755                            failure_order,
756                        )
757                    }
758                    .map(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
759                    .map_err(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
760                } else {
761                    unsafe {
762                        T::compare_exchange(
763                            meta_addr,
764                            old_metadata,
765                            new_metadata,
766                            success_order,
767                            failure_order,
768                        )
769                    }
770                }
771            },
772            |_res| {
773                #[cfg(feature = "extreme_assertions")]
774                if _res.is_ok() {
775                    sanity::verify_store(self, data_addr, new_metadata);
776                }
777            },
778        )
779    }
780
781    /// This is used to implement fetch_add/sub for bits.
782    /// For fetch_and/or, we don't necessarily need this method. We could directly do fetch_and/or on the u8.
783    fn fetch_ops_on_bits<F: Fn(u8) -> u8>(
784        &self,
785        data_addr: Address,
786        meta_addr: Address,
787        set_order: Ordering,
788        fetch_order: Ordering,
789        update: F,
790    ) -> u8 {
791        let lshift = meta_byte_lshift(self, data_addr);
792        let mask = meta_byte_mask(self) << lshift;
793
794        let old_raw_byte = unsafe {
795            <u8 as MetadataValue>::fetch_update(
796                meta_addr,
797                set_order,
798                fetch_order,
799                |raw_byte: u8| {
800                    let old_val = (raw_byte & mask) >> lshift;
801                    let new_val = update(old_val);
802                    let new_raw_byte = (raw_byte & !mask) | ((new_val << lshift) & mask);
803                    Some(new_raw_byte)
804                },
805            )
806        }
807        .unwrap();
808        (old_raw_byte & mask) >> lshift
809    }
810
811    /// Adds the value to the current value for this side metadata for the given address.
812    /// This method has similar semantics to `fetch_add` in Rust atomics.
813    /// Returns the previous value.
814    pub fn fetch_add_atomic<T: MetadataValue>(
815        &self,
816        data_addr: Address,
817        val: T,
818        order: Ordering,
819    ) -> T {
820        self.side_metadata_access::<true, T, _, _, _>(
821            data_addr,
822            Some(val),
823            || {
824                let meta_addr = address_to_meta_address(self, data_addr);
825                let bits_num_log = self.log_num_of_bits;
826                if bits_num_log < 3 {
827                    FromPrimitive::from_u8(self.fetch_ops_on_bits(
828                        data_addr,
829                        meta_addr,
830                        order,
831                        order,
832                        |x: u8| x.wrapping_add(val.to_u8().unwrap()),
833                    ))
834                    .unwrap()
835                } else {
836                    unsafe { T::fetch_add(meta_addr, val, order) }
837                }
838            },
839            |_old_val| {
840                #[cfg(feature = "extreme_assertions")]
841                sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.wrapping_add(&val))
842            },
843        )
844    }
845
846    /// Subtracts the value from the current value for this side metadata for the given address.
847    /// This method has similar semantics to `fetch_sub` in Rust atomics.
848    /// Returns the previous value.
849    pub fn fetch_sub_atomic<T: MetadataValue>(
850        &self,
851        data_addr: Address,
852        val: T,
853        order: Ordering,
854    ) -> T {
855        self.side_metadata_access::<true, T, _, _, _>(
856            data_addr,
857            Some(val),
858            || {
859                let meta_addr = address_to_meta_address(self, data_addr);
860                if self.log_num_of_bits < 3 {
861                    FromPrimitive::from_u8(self.fetch_ops_on_bits(
862                        data_addr,
863                        meta_addr,
864                        order,
865                        order,
866                        |x: u8| x.wrapping_sub(val.to_u8().unwrap()),
867                    ))
868                    .unwrap()
869                } else {
870                    unsafe { T::fetch_sub(meta_addr, val, order) }
871                }
872            },
873            |_old_val| {
874                #[cfg(feature = "extreme_assertions")]
875                sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.wrapping_sub(&val))
876            },
877        )
878    }
879
880    /// Bitwise 'and' the value with the current value for this side metadata for the given address.
881    /// This method has similar semantics to `fetch_and` in Rust atomics.
882    /// Returns the previous value.
883    pub fn fetch_and_atomic<T: MetadataValue>(
884        &self,
885        data_addr: Address,
886        val: T,
887        order: Ordering,
888    ) -> T {
889        self.side_metadata_access::<true, T, _, _, _>(
890            data_addr,
891            Some(val),
892            || {
893                let meta_addr = address_to_meta_address(self, data_addr);
894                if self.log_num_of_bits < 3 {
895                    let lshift = meta_byte_lshift(self, data_addr);
896                    let mask = meta_byte_mask(self) << lshift;
897                    // We do not need to use fetch_ops_on_bits(), we can just set irrelavent bits to 1, and do fetch_and
898                    let rhs = (val.to_u8().unwrap() << lshift) | !mask;
899                    let old_raw_byte =
900                        unsafe { <u8 as MetadataValue>::fetch_and(meta_addr, rhs, order) };
901                    let old_val = (old_raw_byte & mask) >> lshift;
902                    FromPrimitive::from_u8(old_val).unwrap()
903                } else {
904                    unsafe { T::fetch_and(meta_addr, val, order) }
905                }
906            },
907            |_old_val| {
908                #[cfg(feature = "extreme_assertions")]
909                sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.bitand(val))
910            },
911        )
912    }
913
914    /// Bitwise 'or' the value with the current value for this side metadata for the given address.
915    /// This method has similar semantics to `fetch_or` in Rust atomics.
916    /// Returns the previous value.
917    pub fn fetch_or_atomic<T: MetadataValue>(
918        &self,
919        data_addr: Address,
920        val: T,
921        order: Ordering,
922    ) -> T {
923        self.side_metadata_access::<true, T, _, _, _>(
924            data_addr,
925            Some(val),
926            || {
927                let meta_addr = address_to_meta_address(self, data_addr);
928                if self.log_num_of_bits < 3 {
929                    let lshift = meta_byte_lshift(self, data_addr);
930                    let mask = meta_byte_mask(self) << lshift;
931                    // We do not need to use fetch_ops_on_bits(), we can just set irrelavent bits to 0, and do fetch_or
932                    let rhs = (val.to_u8().unwrap() << lshift) & mask;
933                    let old_raw_byte =
934                        unsafe { <u8 as MetadataValue>::fetch_or(meta_addr, rhs, order) };
935                    let old_val = (old_raw_byte & mask) >> lshift;
936                    FromPrimitive::from_u8(old_val).unwrap()
937                } else {
938                    unsafe { T::fetch_or(meta_addr, val, order) }
939                }
940            },
941            |_old_val| {
942                #[cfg(feature = "extreme_assertions")]
943                sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.bitor(val))
944            },
945        )
946    }
947
948    /// Fetches the value for this side metadata for the given address, and applies a function to it that returns an optional new value.
949    /// This method has similar semantics to `fetch_update` in Rust atomics.
950    /// Returns a Result of Ok(previous_value) if the function returned Some(_), else Err(previous_value).
951    pub fn fetch_update_atomic<T: MetadataValue, F: FnMut(T) -> Option<T>>(
952        &self,
953        data_addr: Address,
954        set_order: Ordering,
955        fetch_order: Ordering,
956        mut f: F,
957    ) -> std::result::Result<T, T> {
958        // `f` may have side effects (e.g. it may capture and mutate local state), so under
959        // `extreme_assertions` we must not call it a second time just to recompute the new
960        // value for the sanity check. Instead, stash the new value computed during the actual
961        // update here, and have the verify closure read it back.
962        #[cfg(feature = "extreme_assertions")]
963        let last_new_val: std::cell::Cell<Option<T>> = std::cell::Cell::new(None);
964        #[cfg(feature = "extreme_assertions")]
965        let last_new_val_ref = &last_new_val;
966        self.side_metadata_access::<true, T, _, _, _>(
967            data_addr,
968            None,
969            move || -> std::result::Result<T, T> {
970                let meta_addr = address_to_meta_address(self, data_addr);
971                if self.log_num_of_bits < 3 {
972                    let lshift = meta_byte_lshift(self, data_addr);
973                    let mask = meta_byte_mask(self) << lshift;
974
975                    unsafe {
976                        <u8 as MetadataValue>::fetch_update(
977                            meta_addr,
978                            set_order,
979                            fetch_order,
980                            |raw_byte: u8| {
981                                let old_val = (raw_byte & mask) >> lshift;
982                                let new_val = f(FromPrimitive::from_u8(old_val).unwrap());
983                                #[cfg(feature = "extreme_assertions")]
984                                last_new_val_ref.set(new_val);
985                                new_val.map(|new_val| {
986                                    (raw_byte & !mask)
987                                        | ((new_val.to_u8().unwrap() << lshift) & mask)
988                                })
989                            },
990                        )
991                    }
992                    .map(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
993                    .map_err(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
994                } else {
995                    unsafe {
996                        T::fetch_update(meta_addr, set_order, fetch_order, |old_val| {
997                            let new_val = f(old_val);
998                            #[cfg(feature = "extreme_assertions")]
999                            last_new_val_ref.set(new_val);
1000                            new_val
1001                        })
1002                    }
1003                }
1004            },
1005            |_result| {
1006                #[cfg(feature = "extreme_assertions")]
1007                if let Ok(old_val) = _result {
1008                    sanity::verify_update::<T>(
1009                        self,
1010                        data_addr,
1011                        old_val,
1012                        last_new_val_ref.get().unwrap(),
1013                    )
1014                }
1015            },
1016        )
1017    }
1018
1019    /// Search for a data address that has a non zero value in the side metadata. The search starts from the given data address (including this address),
1020    /// and iterates backwards for the given bytes (non inclusive) before the data address.
1021    ///
1022    /// The side metadata for the searched range may not be mapped. Thus when this function checks the given data address, and
1023    /// when it searches back, it needs to check if the side metadata is mapped or not to avoid loading from an unmapped address.
1024    /// Note that this function checks whether the side metadata is mapped, rather than whether the data address is mapped.
1025    /// Some spaces (e.g. `MallocSpace`) have objects in memory that is not mapped by MMTk, but MMTk still maps side metadata for them.
1026    ///
1027    /// This function returns an address that is aligned to the region of this side metadata (`log_bytes_per_region`), and the side metadata
1028    /// for the address is non zero.
1029    ///
1030    /// # Safety
1031    ///
1032    /// This function uses non-atomic load for the side metadata. The user needs to make sure
1033    /// that there is no other thread that is mutating the side metadata.
1034    #[allow(clippy::let_and_return)]
1035    pub unsafe fn find_prev_non_zero_value<T: MetadataValue>(
1036        &self,
1037        data_addr: Address,
1038        search_limit_bytes: usize,
1039    ) -> Option<Address> {
1040        debug_assert!(search_limit_bytes > 0);
1041
1042        if self.uses_contiguous_side_metadata() {
1043            // Contiguous side metadata
1044            let result = self.find_prev_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1045            #[cfg(debug_assertions)]
1046            {
1047                // Double check if the implementation is correct
1048                let result2 =
1049                    self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1050                assert_eq!(result, result2, "find_prev_non_zero_value_fast returned a diffrent result from the naive implementation.");
1051            }
1052            result
1053        } else {
1054            // TODO: We should be able to optimize further for this case. However, we need to be careful that the side metadata
1055            // is not contiguous, and we need to skip to the next chunk's side metadata when we search to a different chunk.
1056            // This won't be used for VO bit, as VO bit is global and is always contiguous. So for now, I am not bothered to do it.
1057            warn!("We are trying to search non zero bits in an discontiguous side metadata. The performance is slow, as MMTk does not optimize for this case.");
1058            self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1059        }
1060    }
1061
1062    fn find_prev_non_zero_value_simple<T: MetadataValue>(
1063        &self,
1064        data_addr: Address,
1065        search_limit_bytes: usize,
1066    ) -> Option<Address> {
1067        let region_bytes = 1 << self.log_bytes_in_region;
1068        // Figure out the range that we need to search.
1069        let start_addr = data_addr.align_down(region_bytes);
1070        let end_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1071
1072        let mmap_granularity = MMAPPER.granularity();
1073        let mut mapped_grain = Address::MAX;
1074
1075        let mut cursor = start_addr;
1076        while cursor >= end_addr {
1077            // We can cache the "is the side metadata for the cursor mapped?" check because MMTk
1078            // maps metadata at chunk-level
1079            if cursor < mapped_grain {
1080                if self.is_mapped(cursor) {
1081                    mapped_grain = cursor.align_down(mmap_granularity);
1082                } else {
1083                    // We encounter an unmapped side metadata. Just return None.
1084                    return None;
1085                }
1086            }
1087            // If we find non-zero value, just return it.
1088            if !unsafe { self.load::<T>(cursor).is_zero() } {
1089                return Some(cursor);
1090            }
1091            cursor -= region_bytes;
1092        }
1093        None
1094    }
1095
1096    #[allow(clippy::let_and_return)]
1097    fn find_prev_non_zero_value_fast<T: MetadataValue>(
1098        &self,
1099        data_addr: Address,
1100        search_limit_bytes: usize,
1101    ) -> Option<Address> {
1102        debug_assert!(self.uses_contiguous_side_metadata());
1103
1104        // Quick check if the side metadata for the data address is mapped at all.
1105        if !self.is_mapped(data_addr) {
1106            return None;
1107        }
1108        // Quick check if the current data_addr has a non zero value.
1109        if !unsafe { self.load::<T>(data_addr).is_zero() } {
1110            return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1111        }
1112
1113        // Figure out the start and end data address.
1114        let start_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1115        let end_addr = data_addr;
1116
1117        // Then figure out the start and end metadata address and bits.
1118        // The start bit may not be accurate, as we map any address in the region to the same bit.
1119        // We will filter the result at the end to make sure the found address is in the search range.
1120        let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1121        let start_meta_shift = meta_byte_lshift(self, start_addr);
1122        let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1123        let end_meta_shift = meta_byte_lshift(self, end_addr);
1124
1125        let mut res = None;
1126
1127        let mut visitor = |range: BitByteRange| {
1128            match range {
1129                BitByteRange::Bytes { start, end } => {
1130                    match helpers::find_last_non_zero_bit_in_metadata_bytes(start, end) {
1131                        helpers::FindMetaBitResult::Found { addr, bit } => {
1132                            let (addr, bit) = align_metadata_address(self, addr, bit);
1133                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1134                            // Return true to abort the search. We found the bit.
1135                            true
1136                        }
1137                        // If we see unmapped metadata, we don't need to search any more.
1138                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1139                        // Return false to continue searching.
1140                        helpers::FindMetaBitResult::NotFound => false,
1141                    }
1142                }
1143                BitByteRange::BitsInByte {
1144                    addr,
1145                    bit_start,
1146                    bit_end,
1147                } => {
1148                    match helpers::find_last_non_zero_bit_in_metadata_bits(addr, bit_start, bit_end)
1149                    {
1150                        helpers::FindMetaBitResult::Found { addr, bit } => {
1151                            let (addr, bit) = align_metadata_address(self, addr, bit);
1152                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1153                            // Return true to abort the search. We found the bit.
1154                            true
1155                        }
1156                        // If we see unmapped metadata, we don't need to search any more.
1157                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1158                        // Return false to continue searching.
1159                        helpers::FindMetaBitResult::NotFound => false,
1160                    }
1161                }
1162            }
1163        };
1164
1165        ranges::break_bit_range(
1166            start_meta_addr,
1167            start_meta_shift,
1168            end_meta_addr,
1169            end_meta_shift,
1170            false,
1171            &mut visitor,
1172        );
1173
1174        // We have to filter the result. We search between [start_addr, end_addr). But we actually
1175        // search with metadata bits. It is possible the metadata bit for start_addr is the same bit
1176        // as an address that is before start_addr. E.g. 0x2010f026360 and 0x2010f026361 are mapped
1177        // to the same bit, 0x2010f026361 is the start address and 0x2010f026360 is outside the search range.
1178        res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1179            .filter(|addr| *addr >= start_addr && *addr < end_addr)
1180    }
1181
1182    /// Search forwards for a data address that has a non zero value in the side metadata. The search starts from the given data
1183    /// address (including this address), and iterates forwards for the given bytes (non inclusive) before the data address.
1184    ///
1185    /// The side metadata for the searched range may not be mapped. Thus when this function checks the given data address, and
1186    /// when it searches forwards, it needs to check if the side metadata is mapped or not to avoid loading from an unmapped address.
1187    /// Note that this function checks whether the side metadata is mapped, rather than whether the data address is mapped.
1188    /// Some spaces (e.g. `MallocSpace`) have objects in memory that is not mapped by MMTk, but MMTk still maps side metadata for them.
1189    ///
1190    /// This function returns an address that is aligned to the region of this side metadata (`log_bytes_per_region`), and the side metadata
1191    /// for the address is non zero.
1192    ///
1193    /// # Safety
1194    ///
1195    /// This function uses non-atomic load for the side metadata. The user needs to make sure
1196    /// that there is no other thread that is mutating the side metadata.
1197    #[allow(clippy::let_and_return)]
1198    pub unsafe fn find_next_non_zero_value<T: MetadataValue>(
1199        &self,
1200        data_addr: Address,
1201        search_limit_bytes: usize,
1202    ) -> Option<Address> {
1203        debug_assert!(search_limit_bytes > 0);
1204
1205        if self.uses_contiguous_side_metadata() {
1206            // Contiguous side metadata
1207            let result = self.find_next_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1208            #[cfg(debug_assertions)]
1209            {
1210                // Double check if the implementation is correct
1211                let result2 =
1212                    self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1213                assert_eq!(
1214                    result,
1215                    result2,
1216                    "find_next_non_zero_value_fast returned a different result from the naive implementation. data_addr {}, search_limit_bytes {}",
1217                    data_addr, search_limit_bytes,
1218                );
1219            }
1220            result
1221        } else {
1222            // TODO: We should be able to optimize further for this case. However, we need to be careful that the side metadata
1223            // is not contiguous, and we need to skip to the next chunk's side metadata when we search to a different chunk.
1224            // This won't be used for VO bit, as VO bit is global and is always contiguous. So for now, I am not bothered to do it.
1225            warn!("We are trying to search non zero bits in an discontiguous side metadata. The performance is slow, as MMTk does not optimize for this case.");
1226            self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1227        }
1228    }
1229
1230    fn find_next_non_zero_value_simple<T: MetadataValue>(
1231        &self,
1232        data_addr: Address,
1233        search_limit_bytes: usize,
1234    ) -> Option<Address> {
1235        let region_bytes = 1 << self.log_bytes_in_region;
1236        // Figure out the range that we need to search.
1237        let start_addr = data_addr.align_down(region_bytes);
1238        let end_addr = data_addr + search_limit_bytes;
1239
1240        let mmap_granularity = MMAPPER.granularity();
1241        let mut mapped_grain = Address::ZERO;
1242
1243        let mut cursor = start_addr;
1244        while cursor < end_addr {
1245            // We can cache the "is the side metadata for the cursor mapped?" check because MMTk
1246            // maps metadata at chunk-level
1247            if cursor > mapped_grain {
1248                if self.is_mapped(cursor) {
1249                    mapped_grain = cursor.align_up(mmap_granularity) - 0x1;
1250                } else {
1251                    // We encounter an unmapped side metadata. Just return None.
1252                    return None;
1253                }
1254            }
1255            // If we find non-zero value, just return it.
1256            if !unsafe { self.load::<T>(cursor).is_zero() } {
1257                return Some(cursor);
1258            }
1259            cursor += region_bytes;
1260        }
1261        None
1262    }
1263
1264    fn find_next_non_zero_value_fast<T: MetadataValue>(
1265        &self,
1266        data_addr: Address,
1267        search_limit_bytes: usize,
1268    ) -> Option<Address> {
1269        debug_assert!(self.uses_contiguous_side_metadata());
1270
1271        // Quick check if the side metadata for the data address is mapped at all.
1272        if !self.is_mapped(data_addr) {
1273            return None;
1274        }
1275        // Quick check if the current data_addr has a non zero value.
1276        if !unsafe { self.load::<T>(data_addr).is_zero() } {
1277            return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1278        }
1279
1280        // Figure out the start and end data address.
1281        let start_addr = data_addr.align_down(1 << self.log_bytes_in_region);
1282        // We need to align the end_address up because the metadata might be stored right at
1283        // the end address otherwise. Our loop in `find_first_non_zero_bit_in_metadata_byte`
1284        // will not load from this end address, resulting in us potentially not finding the
1285        // correct address for the next set bit.
1286        let end_addr = (data_addr + search_limit_bytes).align_up(1 << self.log_bytes_in_region);
1287
1288        // Then figure out the start and end metadata address and bits.
1289        // The start bit may not be accurate, as we map any address in the region to the same bit.
1290        // We will filter the result at the end to make sure the found address is in the search range.
1291        let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1292        let start_meta_shift = meta_byte_lshift(self, start_addr);
1293        let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1294        let end_meta_shift = meta_byte_lshift(self, end_addr);
1295
1296        let mut res = None;
1297
1298        let mut visitor = |range: BitByteRange| {
1299            match range {
1300                BitByteRange::Bytes { start, end } => {
1301                    match helpers::find_first_non_zero_bit_in_metadata_bytes(start, end) {
1302                        helpers::FindMetaBitResult::Found { addr, bit } => {
1303                            let (addr, bit) = align_metadata_address(self, addr, bit);
1304                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1305                            // Return true to abort the search. We found the bit.
1306                            true
1307                        }
1308                        // If we see unmapped metadata, we don't need to search any more.
1309                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1310                        // Return false to continue searching.
1311                        helpers::FindMetaBitResult::NotFound => false,
1312                    }
1313                }
1314                BitByteRange::BitsInByte {
1315                    addr,
1316                    bit_start,
1317                    bit_end,
1318                } => {
1319                    match helpers::find_first_non_zero_bit_in_metadata_bits(
1320                        addr, bit_start, bit_end,
1321                    ) {
1322                        helpers::FindMetaBitResult::Found { addr, bit } => {
1323                            let (addr, bit) = align_metadata_address(self, addr, bit);
1324                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1325                            // Return true to abort the search. We found the bit.
1326                            true
1327                        }
1328                        // If we see unmapped metadata, we don't need to search any more.
1329                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1330                        // Return false to continue searching.
1331                        helpers::FindMetaBitResult::NotFound => false,
1332                    }
1333                }
1334            }
1335        };
1336
1337        ranges::break_bit_range(
1338            start_meta_addr,
1339            start_meta_shift,
1340            end_meta_addr,
1341            end_meta_shift,
1342            true,
1343            &mut visitor,
1344        );
1345
1346        // We have to filter the result. We search between [start_addr, end_addr). But we actually
1347        // search with metadata bits. It is possible the metadata bit for start_addr is the same bit
1348        // as an address that is before start_addr. E.g. 0x2010f026360 and 0x2010f026361 are mapped
1349        // to the same bit, 0x2010f026361 is the start address and 0x2010f026360 is outside the search range.
1350        res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1351            .filter(|addr| *addr >= start_addr && *addr < end_addr)
1352    }
1353
1354    /// Search for data addresses that have non zero values in the side metadata.  This method is
1355    /// primarily used for heap traversal by scanning the VO bits.
1356    ///
1357    /// This function searches the side metadata for the data address range from `data_start_addr`
1358    /// (inclusive) to `data_end_addr` (exclusive).  The data address range must be fully mapped.
1359    ///
1360    /// For each data region that has non-zero side metadata, `visit_data` is called with the lowest
1361    /// address of that region.  Note that it may not be the original address used to set the
1362    /// metadata bits.
1363    pub fn scan_non_zero_values<T: MetadataValue>(
1364        &self,
1365        data_start_addr: Address,
1366        data_end_addr: Address,
1367        visit_data: &mut impl FnMut(Address),
1368    ) {
1369        if self.uses_contiguous_side_metadata() && self.log_num_of_bits == 0 {
1370            // Contiguous one-bit-per-region side metadata
1371            // TODO: VO bits is one-bit-per-word.  But if we want to scan other metadata (such as
1372            // the forwarding bits which has two bits per word), we will need to refactor the
1373            // algorithm of `scan_non_zero_values_fast`.
1374            self.scan_non_zero_values_fast(data_start_addr, data_end_addr, visit_data);
1375        } else {
1376            // TODO: VO bits are always contiguous.  But if we want to scan other metadata, such as
1377            // side mark bits, we need to refactor `bulk_update_metadata` to support `FnMut`, too,
1378            // and use it to apply `scan_non_zero_values_fast` on each contiguous side metadata
1379            // range.
1380            warn!(
1381                "We are trying to search for non zero bits in a discontiguous side metadata \
1382            or the metadata has more than one bit per region. \
1383                The performance is slow, as MMTk does not optimize for this case."
1384            );
1385            self.scan_non_zero_values_simple::<T>(data_start_addr, data_end_addr, visit_data);
1386        }
1387    }
1388
1389    fn scan_non_zero_values_simple<T: MetadataValue>(
1390        &self,
1391        data_start_addr: Address,
1392        data_end_addr: Address,
1393        visit_data: &mut impl FnMut(Address),
1394    ) {
1395        let region_bytes = 1usize << self.log_bytes_in_region;
1396
1397        let mut cursor = data_start_addr;
1398        while cursor < data_end_addr {
1399            debug_assert!(self.is_mapped(cursor));
1400
1401            // If we find non-zero value, just call back.
1402            if !unsafe { self.load::<T>(cursor).is_zero() } {
1403                visit_data(cursor);
1404            }
1405            cursor += region_bytes;
1406        }
1407    }
1408
1409    fn scan_non_zero_values_fast(
1410        &self,
1411        data_start_addr: Address,
1412        data_end_addr: Address,
1413        visit_data: &mut impl FnMut(Address),
1414    ) {
1415        debug_assert!(self.uses_contiguous_side_metadata());
1416        debug_assert_eq!(self.log_num_of_bits, 0);
1417
1418        // Then figure out the start and end metadata address and bits.
1419        let start_meta_addr = address_to_contiguous_meta_address(self, data_start_addr);
1420        let start_meta_shift = meta_byte_lshift(self, data_start_addr);
1421        let end_meta_addr = address_to_contiguous_meta_address(self, data_end_addr);
1422        let end_meta_shift = meta_byte_lshift(self, data_end_addr);
1423
1424        let mut visitor = |range| {
1425            match range {
1426                BitByteRange::Bytes { start, end } => {
1427                    helpers::scan_non_zero_bits_in_metadata_bytes(start, end, &mut |addr, bit| {
1428                        visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1429                    });
1430                }
1431                BitByteRange::BitsInByte {
1432                    addr,
1433                    bit_start,
1434                    bit_end,
1435                } => helpers::scan_non_zero_bits_in_metadata_bits(
1436                    addr,
1437                    bit_start,
1438                    bit_end,
1439                    &mut |addr, bit| {
1440                        visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1441                    },
1442                ),
1443            }
1444            false
1445        };
1446
1447        ranges::break_bit_range(
1448            start_meta_addr,
1449            start_meta_shift,
1450            end_meta_addr,
1451            end_meta_shift,
1452            true,
1453            &mut visitor,
1454        );
1455    }
1456}
1457
1458impl fmt::Debug for SideMetadataSpec {
1459    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1460        f.write_fmt(format_args!(
1461            "SideMetadataSpec {} {{ \
1462            **is_global: {:?} \
1463            **offset: 0x{:x} \
1464            **log_num_of_bits: 0x{:x} \
1465            **log_bytes_in_region: 0x{:x} \
1466            }}",
1467            self.name, self.is_global, self.offset, self.log_num_of_bits, self.log_bytes_in_region
1468        ))
1469    }
1470}
1471
1472/// Calculate the offset of the next side metadata spec after the given spec.
1473/// This is used to calculate the offset field in [`crate::util::metadata::side_metadata::SideMetadataSpec`].
1474pub const fn side_metadata_offset_after(spec: &SideMetadataSpec) -> usize {
1475    // Some metadata may be so small that its size is not a multiple of byte size. One example
1476    // is `CHUNK_MARK`. It is one byte per chunk. However, on 32-bit architectures, we allocate
1477    // side metadata per chunk. In that case, it will only occupy one byte. If we do not align
1478    // the upper bound offset up, subsequent local metadata that need to be accessed at, for
1479    // example, word granularity will be misaligned.
1480    // TODO: Currently we align metadata to word size so that it is safe to access the metadata
1481    // one word at a time. In the future, we may allow each metadata to specify its own alignment
1482    // requirement.
1483    raw_align_up(spec.upper_bound_offset(), BYTES_IN_WORD)
1484}
1485
1486/// This struct stores all the side metadata specs for a policy. Generally a policy needs to know its own
1487/// side metadata spec as well as the plan's specs.
1488pub(crate) struct SideMetadataContext {
1489    // For plans
1490    pub global: Vec<SideMetadataSpec>,
1491    // For policies
1492    pub local: Vec<SideMetadataSpec>,
1493}
1494
1495impl SideMetadataContext {
1496    #[allow(clippy::vec_init_then_push)] // allow this, as we conditionally push based on features.
1497    pub fn new_global_specs(specs: &[SideMetadataSpec]) -> Vec<SideMetadataSpec> {
1498        let mut ret = vec![];
1499
1500        #[cfg(feature = "vo_bit")]
1501        ret.push(VO_BIT_SIDE_METADATA_SPEC);
1502
1503        if let Some(spec) = crate::mmtk::SFT_MAP.get_side_metadata() {
1504            if spec.is_global {
1505                ret.push(*spec);
1506            }
1507        }
1508
1509        // Any plan that uses the chunk map needs to reserve the chunk map table.
1510        // As we use either the mark sweep or (non moving) immix as the non moving space,
1511        // and both policies use the chunk map, we just add the chunk map table globally.
1512        ret.push(crate::util::heap::chunk_map::ChunkMap::ALLOC_TABLE);
1513
1514        ret.extend_from_slice(specs);
1515        ret
1516    }
1517
1518    pub fn get_local_specs(&self) -> &[SideMetadataSpec] {
1519        &self.local
1520    }
1521
1522    #[cfg(debug_assertions)]
1523    pub fn assert_metadata_ranges_in_reserved_range(
1524        &self,
1525        start: Address,
1526        size: usize,
1527        space_name: &str,
1528    ) {
1529        let reserved = {
1530            let base = super::layout::global_side_metadata_base_address();
1531            let bytes = super::layout::side_metadata_reserved_bytes();
1532            base..(base + bytes)
1533        };
1534        let check_spec = |spec: &SideMetadataSpec| {
1535            if !spec.uses_contiguous_side_metadata() {
1536                return;
1537            }
1538            let metadata_start = address_to_meta_address(spec, start);
1539            let mmap_start = metadata_start.align_down(BYTES_IN_PAGE);
1540            let metadata_size = data_to_meta_size_round_up(spec, size);
1541            let mmap_end = (metadata_start + metadata_size).align_up(BYTES_IN_PAGE);
1542            debug_assert!(
1543                mmap_start >= reserved.start && mmap_end <= reserved.end,
1544                "Side metadata range for spec {} in space {} is outside reserved range: [{}, {}) vs [{}, {})",
1545                spec.name,
1546                space_name,
1547                mmap_start,
1548                mmap_end,
1549                reserved.start,
1550                reserved.end
1551            );
1552        };
1553        self.global.iter().for_each(check_spec);
1554        self.local.iter().for_each(check_spec);
1555    }
1556
1557    /// Return the pages reserved for side metadata based on the data pages we used.
1558    // We used to use PageAccouting to count pages used in side metadata. However,
1559    // that means we always count pages while we may reserve less than a page each time.
1560    // This could lead to overcount. I think the easier way is to not account
1561    // when we allocate for sidemetadata, but to calculate the side metadata usage based on
1562    // how many data pages we use when reporting.
1563    pub fn calculate_reserved_pages(&self, data_pages: usize) -> usize {
1564        let mut total = 0;
1565        for spec in self.global.iter() {
1566            // This rounds up.  No matter how small `data_pages` is, the side metadata size will be
1567            // at least one page.  This behavior is *intended*.  This over-estimated amount is used
1568            // for triggering GC and resizing the heap.
1569            total += data_to_meta_size_round_up(spec, data_pages);
1570        }
1571        for spec in self.local.iter() {
1572            total += data_to_meta_size_round_up(spec, data_pages);
1573        }
1574        total
1575    }
1576
1577    // ** NOTE: **
1578    //  Regardless of the number of bits in a metadata unit, we always represent its content as a word.
1579
1580    /// Tries to map the required metadata space and returns `true` is successful.
1581    /// This can be called at page granularity.
1582    pub fn try_map_metadata_space(
1583        &self,
1584        start: Address,
1585        size: usize,
1586        space_name: &str,
1587    ) -> MmapResult<()> {
1588        debug!(
1589            "try_map_metadata_space({}, 0x{:x}, {}, {})",
1590            start,
1591            size,
1592            self.global.len(),
1593            self.local.len()
1594        );
1595        // Page aligned
1596        debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1597        debug_assert!(size % BYTES_IN_PAGE == 0);
1598        self.map_metadata_internal(start, size, false, space_name)
1599    }
1600
1601    /// Tries to map the required metadata address range, without reserving swap-space/physical memory for it.
1602    /// This will make sure the address range is exclusive to the caller. This should be called at chunk granularity.
1603    ///
1604    /// NOTE: Accessing addresses in this range will produce a segmentation fault if swap-space is not mapped using the `try_map_metadata_space` function.
1605    pub fn try_map_metadata_address_range(
1606        &self,
1607        start: Address,
1608        size: usize,
1609        name: &str,
1610    ) -> MmapResult<()> {
1611        debug!(
1612            "try_map_metadata_address_range({}, 0x{:x}, {}, {})",
1613            start,
1614            size,
1615            self.global.len(),
1616            self.local.len()
1617        );
1618        // Chunk aligned
1619        debug_assert!(start.is_aligned_to(BYTES_IN_CHUNK));
1620        debug_assert!(size % BYTES_IN_CHUNK == 0);
1621        self.map_metadata_internal(start, size, true, name)
1622    }
1623
1624    /// The internal function to mmap metadata
1625    ///
1626    /// # Arguments
1627    /// * `start` - The starting address of the source data.
1628    /// * `size` - The size of the source data (in bytes).
1629    /// * `no_reserve` - whether to invoke mmap with a noreserve flag (we use this flag to quarantine address range)
1630    /// * `space_name`: The name of the space, used for annotating the mmap.
1631    fn map_metadata_internal(
1632        &self,
1633        start: Address,
1634        size: usize,
1635        no_reserve: bool,
1636        space_name: &str,
1637    ) -> MmapResult<()> {
1638        for spec in self.global.iter() {
1639            let anno = MmapAnnotation::SideMeta {
1640                space: space_name,
1641                meta: spec.name,
1642            };
1643            try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1644        }
1645
1646        #[cfg(target_pointer_width = "32")]
1647        let mut lsize: usize = 0;
1648
1649        for spec in self.local.iter() {
1650            // For local side metadata, we always have to reserve address space for all local
1651            // metadata required by all policies in MMTk to be able to calculate a constant offset
1652            // for each local metadata at compile-time (it's like assigning an ID to each policy).
1653            //
1654            // As the plan is chosen at run-time, we will never know which subset of policies will
1655            // be used during run-time. We can't afford this much address space in 32-bits.
1656            // So, we switch to the chunk-based approach for this specific case.
1657            //
1658            // The global metadata is different in that for each plan, we can calculate its constant
1659            // base addresses at compile-time. Using the chunk-based approach will need the same
1660            // address space size as the current not-chunked approach.
1661            #[cfg(target_pointer_width = "64")]
1662            {
1663                let anno = MmapAnnotation::SideMeta {
1664                    space: space_name,
1665                    meta: spec.name,
1666                };
1667                try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1668            }
1669            #[cfg(target_pointer_width = "32")]
1670            {
1671                lsize += metadata_bytes_per_chunk(spec.log_bytes_in_region, spec.log_num_of_bits);
1672            }
1673        }
1674
1675        #[cfg(target_pointer_width = "32")]
1676        if lsize > 0 {
1677            let max = BYTES_IN_CHUNK >> super::layout::LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO;
1678            debug_assert!(
1679                lsize <= max,
1680                "local side metadata per chunk (0x{:x}) must be less than (0x{:x})",
1681                lsize,
1682                max
1683            );
1684            // We are creating a mmap for all side metadata instead of one specific metadata.  We
1685            // just annotate it as "all" here.
1686            let anno = MmapAnnotation::SideMeta {
1687                space: space_name,
1688                meta: "all",
1689            };
1690            try_map_per_chunk_metadata_space(start, size, lsize, no_reserve, &anno)?;
1691        }
1692
1693        Ok(())
1694    }
1695
1696    /// Unmap the corresponding metadata space or panic.
1697    ///
1698    /// Note-1: This function is only used for test and debug right now.
1699    ///
1700    /// Note-2: This function uses munmap() which works at page granularity.
1701    ///     If the corresponding metadata space's size is not a multiple of page size,
1702    ///     the actual unmapped space will be bigger than what you specify.
1703    #[cfg(test)]
1704    pub fn ensure_unmap_metadata_space(&self, start: Address, size: usize) {
1705        trace!("ensure_unmap_metadata_space({}, 0x{:x})", start, size);
1706        debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1707        debug_assert!(size % BYTES_IN_PAGE == 0);
1708
1709        for spec in self.global.iter() {
1710            ensure_munmap_contiguous_metadata_space(start, size, spec);
1711        }
1712
1713        for spec in self.local.iter() {
1714            #[cfg(target_pointer_width = "64")]
1715            {
1716                ensure_munmap_contiguous_metadata_space(start, size, spec);
1717            }
1718            #[cfg(target_pointer_width = "32")]
1719            {
1720                ensure_munmap_chunked_metadata_space(start, size, spec);
1721            }
1722        }
1723    }
1724}
1725
1726/// A byte array in side-metadata
1727pub struct MetadataByteArrayRef<const ENTRIES: usize> {
1728    #[cfg(feature = "extreme_assertions")]
1729    heap_range_start: Address,
1730    #[cfg(feature = "extreme_assertions")]
1731    spec: SideMetadataSpec,
1732    data: &'static [u8; ENTRIES],
1733}
1734
1735impl<const ENTRIES: usize> MetadataByteArrayRef<ENTRIES> {
1736    /// Get a piece of metadata address range as a byte array.
1737    ///
1738    /// # Arguments
1739    ///
1740    /// * `metadata_spec` - The specification of the target side metadata.
1741    /// * `start` - The starting address of the heap range.
1742    /// * `bytes` - The size of the heap range.
1743    ///
1744    pub fn new(metadata_spec: &SideMetadataSpec, start: Address, bytes: usize) -> Self {
1745        debug_assert_eq!(
1746            metadata_spec.log_num_of_bits, LOG_BITS_IN_BYTE as usize,
1747            "Each heap entry should map to a byte in side-metadata"
1748        );
1749        debug_assert_eq!(
1750            bytes >> metadata_spec.log_bytes_in_region,
1751            ENTRIES,
1752            "Heap range size and MetadataByteArray size does not match"
1753        );
1754        Self {
1755            #[cfg(feature = "extreme_assertions")]
1756            heap_range_start: start,
1757            #[cfg(feature = "extreme_assertions")]
1758            spec: *metadata_spec,
1759            // # Safety
1760            // The metadata memory is assumed to be mapped when accessing.
1761            data: unsafe { &*address_to_meta_address(metadata_spec, start).to_ptr() },
1762        }
1763    }
1764
1765    /// Get the length of the array.
1766    #[allow(clippy::len_without_is_empty)]
1767    pub const fn len(&self) -> usize {
1768        ENTRIES
1769    }
1770
1771    /// Get a byte from the metadata byte array at the given index.
1772    #[allow(clippy::let_and_return)]
1773    pub fn get(&self, index: usize) -> u8 {
1774        #[cfg(feature = "extreme_assertions")]
1775        let _lock = sanity::SANITY_LOCK.lock().unwrap();
1776        let value = self.data[index];
1777        #[cfg(feature = "extreme_assertions")]
1778        {
1779            let data_addr = self.heap_range_start + (index << self.spec.log_bytes_in_region);
1780            sanity::verify_load::<u8>(&self.spec, data_addr, value);
1781        }
1782        value
1783    }
1784}
1785
1786#[cfg(test)]
1787mod tests {
1788    use super::*;
1789    use crate::mmap_anno_test;
1790    use crate::util::metadata::side_metadata::SideMetadataContext;
1791
1792    // offset is not used in these tests.
1793    pub const ZERO_OFFSET: usize = 0;
1794
1795    #[test]
1796    fn calculate_reserved_pages_one_spec() {
1797        // 1 bit per 8 bytes - 1:64
1798        let spec = SideMetadataSpec {
1799            name: "test_spec",
1800            is_global: true,
1801            offset: ZERO_OFFSET,
1802            log_num_of_bits: 0,
1803            log_bytes_in_region: 3,
1804        };
1805        let side_metadata = SideMetadataContext {
1806            global: vec![spec],
1807            local: vec![],
1808        };
1809        assert_eq!(side_metadata.calculate_reserved_pages(0), 0);
1810        assert_eq!(side_metadata.calculate_reserved_pages(63), 1);
1811        assert_eq!(side_metadata.calculate_reserved_pages(64), 1);
1812        assert_eq!(side_metadata.calculate_reserved_pages(65), 2);
1813        assert_eq!(side_metadata.calculate_reserved_pages(1024), 16);
1814    }
1815
1816    #[test]
1817    fn calculate_reserved_pages_multi_specs() {
1818        // 1 bit per 8 bytes - 1:64
1819        let gspec = SideMetadataSpec {
1820            name: "gspec",
1821            is_global: true,
1822            offset: ZERO_OFFSET,
1823            log_num_of_bits: 0,
1824            log_bytes_in_region: 3,
1825        };
1826        // 2 bits per page - 2 / (4k * 8) = 1:16k
1827        let lspec = SideMetadataSpec {
1828            name: "lspec",
1829            is_global: false,
1830            offset: ZERO_OFFSET,
1831            log_num_of_bits: 1,
1832            log_bytes_in_region: 12,
1833        };
1834        let side_metadata = SideMetadataContext {
1835            global: vec![gspec],
1836            local: vec![lspec],
1837        };
1838        assert_eq!(side_metadata.calculate_reserved_pages(1024), 16 + 1);
1839    }
1840
1841    use crate::util::heap::layout::vm_layout;
1842    use crate::util::test_util::{serial_test, with_cleanup};
1843    use paste::paste;
1844
1845    const TEST_LOG_BYTES_IN_REGION: usize = 12;
1846
1847    fn test_side_metadata(
1848        log_bits: usize,
1849        f: impl Fn(&SideMetadataSpec, Address, Address) + std::panic::RefUnwindSafe,
1850    ) {
1851        serial_test(|| {
1852            core_test_initialize_side_metadata();
1853
1854            let spec = SideMetadataSpec {
1855                name: "Test Spec $tname",
1856                is_global: true,
1857                offset: 0,
1858                log_num_of_bits: log_bits,
1859                log_bytes_in_region: TEST_LOG_BYTES_IN_REGION, // page size
1860            };
1861            let context = SideMetadataContext {
1862                global: vec![spec],
1863                local: vec![],
1864            };
1865            let mut sanity = SideMetadataSanity::new();
1866            sanity.verify_metadata_context("TestPolicy", &context);
1867
1868            let data_addr = vm_layout::vm_layout().heap_start;
1869            // Make sure the address is mapped.
1870            crate::MMAPPER
1871                .ensure_mapped(
1872                    data_addr,
1873                    1,
1874                    HugePageSupport::No,
1875                    MmapProtection::ReadWrite,
1876                    mmap_anno_test!(),
1877                )
1878                .unwrap();
1879            let meta_addr = address_to_meta_address(&spec, data_addr);
1880            with_cleanup(
1881                || {
1882                    let mmap_result =
1883                        context.try_map_metadata_space(data_addr, BYTES_IN_PAGE, "test_space");
1884                    assert!(mmap_result.is_ok(), "{:?}", mmap_result);
1885
1886                    f(&spec, data_addr, meta_addr);
1887                },
1888                || {
1889                    // Clear the metadata -- use u64 (max length we support)
1890                    assert!(log_bits <= 6);
1891                    let meta_ptr: *mut u64 = meta_addr.to_mut_ptr();
1892                    unsafe { *meta_ptr = 0 };
1893
1894                    sanity::reset();
1895                },
1896            )
1897        })
1898    }
1899
1900    fn max_value(log_bits: usize) -> u64 {
1901        (0..(1 << log_bits)).fold(0, |accum, x| accum + (1 << x))
1902    }
1903    #[test]
1904    fn test_max_value() {
1905        assert_eq!(max_value(0), 1);
1906        assert_eq!(max_value(1), 0b11);
1907        assert_eq!(max_value(2), 0b1111);
1908        assert_eq!(max_value(3), 255);
1909        assert_eq!(max_value(4), 65535);
1910    }
1911
1912    macro_rules! test_side_metadata_access {
1913        ($tname: ident, $type: ty, $log_bits: expr) => {
1914            paste!{
1915                #[test]
1916                fn [<$tname _load>]() {
1917                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1918                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1919
1920                        // Initial value should be 0
1921                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1922                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
1923
1924                        // Set to max
1925                        let max_value: $type = max_value($log_bits) as _;
1926                        unsafe { spec.store::<$type>(data_addr, max_value); }
1927                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, max_value);
1928                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
1929                        assert_eq!(unsafe { *meta_ptr }, max_value);
1930                    });
1931                }
1932
1933                #[test]
1934                fn [<$tname _store>]() {
1935                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1936                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1937                        let max_value: $type = max_value($log_bits) as _;
1938
1939                        // Set the metadata byte(s) to all 1s
1940                        unsafe { *meta_ptr = <$type>::MAX; }
1941                        // Store 0 to the side metadata
1942                        unsafe { spec.store::<$type>(data_addr, 0); }
1943                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1944                        // Only the affected bits are set to 0
1945                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1946                    });
1947                }
1948
1949                #[test]
1950                fn [<$tname _atomic_store>]() {
1951                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1952                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1953                        let max_value: $type = max_value($log_bits) as _;
1954
1955                        // Set the metadata byte(s) to all 1s
1956                        unsafe { *meta_ptr = <$type>::MAX; }
1957                        // Store 0 to the side metadata
1958                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1959                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1960                        // Only the affected bits are set to 0
1961                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1962                    });
1963                }
1964
1965                #[test]
1966                fn [<$tname _compare_exchange_success>]() {
1967                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1968                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1969                        let max_value: $type = max_value($log_bits) as _;
1970                        // Set the metadata byte(s) to all 1s
1971                        unsafe { *meta_ptr = <$type>::MAX; }
1972                        // Store 1 to the side metadata
1973                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1974
1975                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1976                        assert_eq!(old_val, 1);
1977
1978                        let new_val = 0;
1979                        let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
1980                        assert!(res.is_ok());
1981                        assert_eq!(res.unwrap(), old_val, "old vals do not match");
1982
1983                        let after_update = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1984                        assert_eq!(after_update, new_val);
1985                        // Only the affected bits are set to 0
1986                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1987                    });
1988                }
1989
1990                #[test]
1991                fn [<$tname _compare_exchange_fail>]() {
1992                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1993                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1994                        // Set the metadata byte(s) to all 1s
1995                        unsafe { *meta_ptr = <$type>::MAX; }
1996                        // Store 1 to the side metadata
1997                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1998
1999                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2000                        assert_eq!(old_val, 1);
2001
2002                        // make old_val outdated
2003                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2004                        let bits_before_cas = unsafe { *meta_ptr };
2005
2006                        let new_val = 0;
2007                        let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
2008                        assert!(res.is_err());
2009                        assert_eq!(res.err().unwrap(), 0);
2010                        let bits_after_cas = unsafe { *meta_ptr };
2011                        assert_eq!(bits_before_cas, bits_after_cas);
2012                    });
2013                }
2014
2015                #[test]
2016                fn [<$tname _fetch_add_1>]() {
2017                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2018                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2019                        // Set the metadata byte(s) to all 1s
2020                        unsafe { *meta_ptr = <$type>::MAX; }
2021                        // Store 0 to the side metadata
2022                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2023
2024                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2025
2026                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2027                        assert_eq!(old_val_from_fetch, old_val);
2028
2029                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2030                        assert_eq!(new_val, 1);
2031                    });
2032                }
2033
2034                #[test]
2035                fn [<$tname _fetch_add_max>]() {
2036                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2037                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2038                        let max_value: $type = max_value($log_bits) as _;
2039                        // Set the metadata byte(s) to all 1s
2040                        unsafe { *meta_ptr = <$type>::MAX; }
2041                        // Store 0 to the side metadata
2042                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2043
2044                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2045
2046                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2047                        assert_eq!(old_val_from_fetch, old_val);
2048
2049                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2050                        assert_eq!(new_val, max_value);
2051                    });
2052                }
2053
2054                #[test]
2055                fn [<$tname _fetch_add_overflow>]() {
2056                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2057                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2058                        let max_value: $type = max_value($log_bits) as _;
2059                        // Set the metadata byte(s) to all 1s
2060                        unsafe { *meta_ptr = <$type>::MAX; }
2061                        // Store max to the side metadata
2062                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2063
2064                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2065
2066                        // add 1 to max value will cause overflow and wrap around to 0
2067                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2068                        assert_eq!(old_val_from_fetch, old_val);
2069
2070                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2071                        assert_eq!(new_val, 0);
2072                    });
2073                }
2074
2075                #[test]
2076                fn [<$tname _fetch_sub_1>]() {
2077                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2078                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2079                        // Set the metadata byte(s) to all 1s
2080                        unsafe { *meta_ptr = <$type>::MAX; }
2081                        // Store 1 to the side metadata
2082                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2083
2084                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2085
2086                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2087                        assert_eq!(old_val_from_fetch, old_val);
2088
2089                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2090                        assert_eq!(new_val, 0);
2091                    });
2092                }
2093
2094                #[test]
2095                fn [<$tname _fetch_sub_max>]() {
2096                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2097                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2098                        let max_value: $type = max_value($log_bits) as _;
2099                        // Set the metadata byte(s) to all 1s
2100                        unsafe { *meta_ptr = <$type>::MAX; }
2101                        // Store max to the side metadata
2102                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2103
2104                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2105
2106                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2107                        assert_eq!(old_val_from_fetch, old_val);
2108
2109                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2110                        assert_eq!(new_val, 0);
2111                    });
2112                }
2113
2114                #[test]
2115                fn [<$tname _fetch_sub_overflow>]() {
2116                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2117                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2118                        let max_value: $type = max_value($log_bits) as _;
2119                        // Set the metadata byte(s) to all 1s
2120                        unsafe { *meta_ptr = <$type>::MAX; }
2121                        // Store 0 to the side metadata
2122                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2123
2124                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2125
2126                        // sub 1 from 0 will cause overflow, and wrap around to max
2127                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2128                        assert_eq!(old_val_from_fetch, old_val);
2129
2130                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2131                        assert_eq!(new_val, max_value);
2132                    });
2133                }
2134
2135                #[test]
2136                fn [<$tname _fetch_and>]() {
2137                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2138                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2139                        let max_value: $type = max_value($log_bits) as _;
2140                        // Set the metadata byte(s) to all 1s
2141                        unsafe { *meta_ptr = <$type>::MAX; }
2142                        // Store all 1s to the side metadata
2143                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2144
2145                        // max and max should be max
2146                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2147                        let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2148                        assert_eq!(old_val_from_fetch, old_val, "old values do not match");
2149                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value, "load values do not match");
2150                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX, "raw values do not match");
2151
2152                        // max and last_bit_zero should last_bit_zero
2153                        let last_bit_zero = max_value - 1;
2154                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2155                        let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, last_bit_zero, Ordering::SeqCst);
2156                        assert_eq!(old_val_from_fetch, old_val);
2157                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), last_bit_zero);
2158                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX - 1);
2159                    });
2160                }
2161
2162                #[test]
2163                fn [<$tname _fetch_or>]() {
2164                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2165                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2166                        let max_value: $type = max_value($log_bits) as _;
2167                        // Set the metadata byte(s) to all 0s
2168                        unsafe { *meta_ptr = 0; }
2169                        // Store 0 to the side metadata
2170                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2171
2172                        // 0 or 0 should be 0
2173                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2174                        let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2175                        assert_eq!(old_val_from_fetch, old_val);
2176                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2177                        assert_eq!(unsafe { *meta_ptr }, 0);
2178
2179                        // 0 and max should max
2180                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2181                        let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2182                        assert_eq!(old_val_from_fetch, old_val);
2183                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2184                        assert_eq!(unsafe { *meta_ptr }, max_value);
2185                    });
2186                }
2187
2188                #[test]
2189                fn [<$tname _fetch_update_success>]() {
2190                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2191                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2192                        let max_value: $type = max_value($log_bits) as _;
2193                        // Set the metadata byte(s) to all 1s
2194                        unsafe { *meta_ptr = <$type>::MAX; }
2195                        // Store all 1s to the side metadata
2196                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2197
2198                        // update from max to zero
2199                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2200                        let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| Some(0));
2201                        assert!(fetch_res.is_ok());
2202                        assert_eq!(fetch_res.unwrap(), old_val);
2203                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2204                        // Only the affected bits are set to 0
2205                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
2206                    });
2207                }
2208
2209                #[test]
2210                fn [<$tname _fetch_update_fail>]() {
2211                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2212                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2213                        let max_value: $type = max_value($log_bits) as _;
2214                        // Set the metadata byte(s) to all 1s
2215                        unsafe { *meta_ptr = <$type>::MAX; }
2216                        // Store all 1s to the side metadata
2217                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2218
2219                        // update from max to zero
2220                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2221                        let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| None);
2222                        assert!(fetch_res.is_err());
2223                        assert_eq!(fetch_res.err().unwrap(), old_val);
2224                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2225                        // Only the affected bits are set to 0
2226                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX);
2227                    });
2228                }
2229
2230                #[test]
2231                fn [<$tname _find_prev_non_zero_value_easy>]() {
2232                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2233                        let max_value: $type = max_value($log_bits) as _;
2234                        // Store non zero value at data_addr
2235                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2236
2237                        // Find the value starting from data_addr, at max 8 bytes.
2238                        // We should find data_addr
2239                        let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(data_addr, 8) };
2240                        assert!(res_addr.is_some());
2241                        assert_eq!(res_addr.unwrap(), data_addr);
2242                    });
2243                }
2244
2245                #[test]
2246                fn [<$tname _find_prev_non_zero_value_arbitrary_bytes>]() {
2247                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2248                        let max_value: $type = max_value($log_bits) as _;
2249                        // Store non zero value at data_addr
2250                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2251
2252                        // Start from data_addr, we offset arbitrary length, and search back to find data_addr
2253                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2254                        for len in 1..(test_region*4) {
2255                            let start_addr = data_addr + len;
2256                            // Use len+1, as len is non inclusive.
2257                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2258                            assert!(res_addr.is_some());
2259                            assert_eq!(res_addr.unwrap(), data_addr);
2260                        }
2261                    });
2262                }
2263
2264                #[test]
2265                fn [<$tname _find_prev_non_zero_value_arbitrary_start>]() {
2266                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2267                        let max_value: $type = max_value($log_bits) as _;
2268
2269                        // data_addr has a non-aligned offset
2270                        for offset in 0..7usize {
2271                            // Apply offset and test with the new data addr
2272                            let test_data_addr = data_addr + offset;
2273                            spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2274
2275                            // The return result should be aligned
2276                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(test_data_addr, 4096) };
2277                            assert!(res_addr.is_some());
2278                            assert_eq!(res_addr.unwrap(), data_addr);
2279
2280                            // Clear whatever is set
2281                            spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2282                        }
2283                    });
2284                }
2285
2286                #[test]
2287                fn [<$tname _find_prev_non_zero_value_no_find>]() {
2288                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2289                        // Store zero value at data_addr -- so we won't find anything
2290                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2291
2292                        // Start from data_addr, we offset arbitrary length, and search back
2293                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2294                        for len in 1..(test_region*4) {
2295                            let start_addr = data_addr + len;
2296                            // Use len+1, as len is non inclusive.
2297                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2298                            assert!(res_addr.is_none());
2299                        }
2300                    });
2301                }
2302
2303                #[test]
2304                fn [<$tname _find_next_non_zero_value_easy>]() {
2305                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2306                        let max_value: $type = max_value($log_bits) as _;
2307                        // Store non zero value at data_addr
2308                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2309
2310                        // Find the value starting from data_addr, at max 8 bytes.
2311                        // We should find data_addr
2312                        let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(data_addr, 8) };
2313                        assert!(res_addr.is_some());
2314                        assert_eq!(res_addr.unwrap(), data_addr);
2315                    });
2316                }
2317
2318                #[test]
2319                fn [<$tname _find_next_non_zero_value_arbitrary_bytes>]() {
2320                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2321                        let max_value: $type = max_value($log_bits) as _;
2322                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2323
2324                        // Take a data address in the middle since metadata before
2325                        // the start may not be mapped
2326                        let data_addr = data_addr + test_region*4;
2327
2328                        // Store non zero value at data_addr
2329                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2330                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2331
2332                        // Start from data_addr, we offset arbitrary length, and search forwards to find data_addr
2333                        for len in 1..(test_region*4) {
2334                            let start_addr = data_addr - len;
2335                            // Use len+1, as len is non inclusive.
2336                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2337                            assert!(res_addr.is_some());
2338                            assert_eq!(res_addr.unwrap(), data_addr);
2339                        }
2340                    });
2341                }
2342
2343                #[test]
2344                fn [<$tname _find_next_non_zero_value_arbitrary_start>]() {
2345                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2346                        let max_value: $type = max_value($log_bits) as _;
2347
2348                        // data_addr has a non-aligned offset
2349                        for offset in 0..7usize {
2350                            // Apply offset and test with the new data addr
2351                            let test_data_addr = data_addr + offset;
2352                            spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2353
2354                            // The return result should be aligned
2355                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(test_data_addr, 4096) };
2356                            assert!(res_addr.is_some());
2357                            assert_eq!(res_addr.unwrap(), data_addr);
2358
2359                            // Clear whatever is set
2360                            spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2361                        }
2362                    });
2363                }
2364
2365                #[test]
2366                fn [<$tname _find_next_non_zero_value_no_find>]() {
2367                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2368                        // Store zero value at data_addr -- so we won't find anything
2369                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2370
2371                        // Start from data_addr, we offset arbitrary length, and search back
2372                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2373                        for len in 1..(test_region*4) {
2374                            let start_addr = data_addr - len;
2375                            // Use len+1, as len is non inclusive.
2376                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2377                            assert!(res_addr.is_none());
2378                        }
2379                    });
2380                }
2381            }
2382        }
2383    }
2384
2385    test_side_metadata_access!(test_u1, u8, 0);
2386    test_side_metadata_access!(test_u2, u8, 1);
2387    test_side_metadata_access!(test_u4, u8, 2);
2388    test_side_metadata_access!(test_u8, u8, 3);
2389    test_side_metadata_access!(test_u16, u16, 4);
2390    test_side_metadata_access!(test_u32, u32, 5);
2391    test_side_metadata_access!(test_u64, u64, 6);
2392    test_side_metadata_access!(
2393        test_usize,
2394        usize,
2395        if cfg!(target_pointer_width = "64") {
2396            6
2397        } else if cfg!(target_pointer_width = "32") {
2398            5
2399        } else {
2400            unreachable!()
2401        }
2402    );
2403
2404    #[test]
2405    fn test_bulk_update_meta_bits() {
2406        let raw_mem =
2407            unsafe { std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(8, 8).unwrap()) };
2408        let addr = Address::from_mut_ptr(raw_mem);
2409
2410        SideMetadataSpec::set_meta_bits(addr, 0, addr, 4);
2411        assert_eq!(unsafe { addr.load::<u64>() }, 0b1111);
2412
2413        SideMetadataSpec::zero_meta_bits(addr, 1, addr, 3);
2414        assert_eq!(unsafe { addr.load::<u64>() }, 0b1001);
2415
2416        SideMetadataSpec::set_meta_bits(addr, 2, addr, 6);
2417        assert_eq!(unsafe { addr.load::<u64>() }, 0b0011_1101);
2418
2419        SideMetadataSpec::zero_meta_bits(addr, 0, addr + 1usize, 0);
2420        assert_eq!(unsafe { addr.load::<u64>() }, 0b0);
2421
2422        SideMetadataSpec::set_meta_bits(addr, 2, addr + 1usize, 2);
2423        assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1100);
2424
2425        SideMetadataSpec::set_meta_bits(addr, 0, addr + 1usize, 2);
2426        assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1111);
2427    }
2428}