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 (page).
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 data_addr and the corresponding side metadata address 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 address is mapped or not to avoid loading from an unmapped address.
1024    ///
1025    /// This function returns an address that is aligned to the region of this side metadata (`log_bytes_per_region`), and the side metadata
1026    /// for the address is non zero.
1027    ///
1028    /// # Safety
1029    ///
1030    /// This function uses non-atomic load for the side metadata. The user needs to make sure
1031    /// that there is no other thread that is mutating the side metadata.
1032    #[allow(clippy::let_and_return)]
1033    pub unsafe fn find_prev_non_zero_value<T: MetadataValue>(
1034        &self,
1035        data_addr: Address,
1036        search_limit_bytes: usize,
1037    ) -> Option<Address> {
1038        debug_assert!(search_limit_bytes > 0);
1039
1040        if self.uses_contiguous_side_metadata() {
1041            // Contiguous side metadata
1042            let result = self.find_prev_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1043            #[cfg(debug_assertions)]
1044            {
1045                // Double check if the implementation is correct
1046                let result2 =
1047                    self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1048                assert_eq!(result, result2, "find_prev_non_zero_value_fast returned a diffrent result from the naive implementation.");
1049            }
1050            result
1051        } else {
1052            // TODO: We should be able to optimize further for this case. However, we need to be careful that the side metadata
1053            // is not contiguous, and we need to skip to the next chunk's side metadata when we search to a different chunk.
1054            // 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.
1055            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.");
1056            self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1057        }
1058    }
1059
1060    fn find_prev_non_zero_value_simple<T: MetadataValue>(
1061        &self,
1062        data_addr: Address,
1063        search_limit_bytes: usize,
1064    ) -> Option<Address> {
1065        let region_bytes = 1 << self.log_bytes_in_region;
1066        // Figure out the range that we need to search.
1067        let start_addr = data_addr.align_down(region_bytes);
1068        let end_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1069
1070        let mmap_granularity = MMAPPER.granularity();
1071        let mut mapped_grain = Address::MAX;
1072
1073        let mut cursor = start_addr;
1074        while cursor >= end_addr {
1075            // We can cache the "is the cursor mapped?" check because MMTk maps metadata at
1076            // chunk-level
1077            if cursor < mapped_grain {
1078                if cursor.is_mapped() {
1079                    mapped_grain = cursor.align_down(mmap_granularity);
1080                } else {
1081                    // We encounter an unmapped address. Just return None.
1082                    return None;
1083                }
1084            }
1085            // If we find non-zero value, just return it.
1086            if !unsafe { self.load::<T>(cursor).is_zero() } {
1087                return Some(cursor);
1088            }
1089            cursor -= region_bytes;
1090        }
1091        None
1092    }
1093
1094    #[allow(clippy::let_and_return)]
1095    fn find_prev_non_zero_value_fast<T: MetadataValue>(
1096        &self,
1097        data_addr: Address,
1098        search_limit_bytes: usize,
1099    ) -> Option<Address> {
1100        debug_assert!(self.uses_contiguous_side_metadata());
1101
1102        // Quick check if the data address is mapped at all.
1103        if !data_addr.is_mapped() {
1104            return None;
1105        }
1106        // Quick check if the current data_addr has a non zero value.
1107        if !unsafe { self.load::<T>(data_addr).is_zero() } {
1108            return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1109        }
1110
1111        // Figure out the start and end data address.
1112        let start_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1113        let end_addr = data_addr;
1114
1115        // Then figure out the start and end metadata address and bits.
1116        // The start bit may not be accurate, as we map any address in the region to the same bit.
1117        // We will filter the result at the end to make sure the found address is in the search range.
1118        let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1119        let start_meta_shift = meta_byte_lshift(self, start_addr);
1120        let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1121        let end_meta_shift = meta_byte_lshift(self, end_addr);
1122
1123        let mut res = None;
1124
1125        let mut visitor = |range: BitByteRange| {
1126            match range {
1127                BitByteRange::Bytes { start, end } => {
1128                    match helpers::find_last_non_zero_bit_in_metadata_bytes(start, end) {
1129                        helpers::FindMetaBitResult::Found { addr, bit } => {
1130                            let (addr, bit) = align_metadata_address(self, addr, bit);
1131                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1132                            // Return true to abort the search. We found the bit.
1133                            true
1134                        }
1135                        // If we see unmapped metadata, we don't need to search any more.
1136                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1137                        // Return false to continue searching.
1138                        helpers::FindMetaBitResult::NotFound => false,
1139                    }
1140                }
1141                BitByteRange::BitsInByte {
1142                    addr,
1143                    bit_start,
1144                    bit_end,
1145                } => {
1146                    match helpers::find_last_non_zero_bit_in_metadata_bits(addr, bit_start, bit_end)
1147                    {
1148                        helpers::FindMetaBitResult::Found { addr, bit } => {
1149                            let (addr, bit) = align_metadata_address(self, addr, bit);
1150                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1151                            // Return true to abort the search. We found the bit.
1152                            true
1153                        }
1154                        // If we see unmapped metadata, we don't need to search any more.
1155                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1156                        // Return false to continue searching.
1157                        helpers::FindMetaBitResult::NotFound => false,
1158                    }
1159                }
1160            }
1161        };
1162
1163        ranges::break_bit_range(
1164            start_meta_addr,
1165            start_meta_shift,
1166            end_meta_addr,
1167            end_meta_shift,
1168            false,
1169            &mut visitor,
1170        );
1171
1172        // We have to filter the result. We search between [start_addr, end_addr). But we actually
1173        // search with metadata bits. It is possible the metadata bit for start_addr is the same bit
1174        // as an address that is before start_addr. E.g. 0x2010f026360 and 0x2010f026361 are mapped
1175        // to the same bit, 0x2010f026361 is the start address and 0x2010f026360 is outside the search range.
1176        res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1177            .filter(|addr| *addr >= start_addr && *addr < end_addr)
1178    }
1179
1180    /// Search forwards for a data address that has a non zero value in the side metadata. The search starts from the given data
1181    /// address (including this address), and iterates forwards for the given bytes (non inclusive) before the data address.
1182    ///
1183    /// The data_addr and the corresponding side metadata address may not be mapped. Thus when this function checks the given data address, and
1184    /// when it searches back, it needs to check if the address is mapped or not to avoid loading from an unmapped address.
1185    ///
1186    /// This function returns an address that is aligned to the region of this side metadata (`log_bytes_per_region`), and the side metadata
1187    /// for the address is non zero.
1188    ///
1189    /// # Safety
1190    ///
1191    /// This function uses non-atomic load for the side metadata. The user needs to make sure
1192    /// that there is no other thread that is mutating the side metadata.
1193    #[allow(clippy::let_and_return)]
1194    pub unsafe fn find_next_non_zero_value<T: MetadataValue>(
1195        &self,
1196        data_addr: Address,
1197        search_limit_bytes: usize,
1198    ) -> Option<Address> {
1199        debug_assert!(search_limit_bytes > 0);
1200
1201        if self.uses_contiguous_side_metadata() {
1202            // Contiguous side metadata
1203            let result = self.find_next_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1204            #[cfg(debug_assertions)]
1205            {
1206                // Double check if the implementation is correct
1207                let result2 =
1208                    self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1209                assert_eq!(
1210                    result,
1211                    result2,
1212                    "find_next_non_zero_value_fast returned a different result from the naive implementation. data_addr {}, search_limit_bytes {}",
1213                    data_addr, search_limit_bytes,
1214                );
1215            }
1216            result
1217        } else {
1218            // TODO: We should be able to optimize further for this case. However, we need to be careful that the side metadata
1219            // is not contiguous, and we need to skip to the next chunk's side metadata when we search to a different chunk.
1220            // 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.
1221            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.");
1222            self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1223        }
1224    }
1225
1226    fn find_next_non_zero_value_simple<T: MetadataValue>(
1227        &self,
1228        data_addr: Address,
1229        search_limit_bytes: usize,
1230    ) -> Option<Address> {
1231        let region_bytes = 1 << self.log_bytes_in_region;
1232        // Figure out the range that we need to search.
1233        let start_addr = data_addr.align_down(region_bytes);
1234        let end_addr = data_addr + search_limit_bytes;
1235
1236        let mmap_granularity = MMAPPER.granularity();
1237        let mut mapped_grain = Address::ZERO;
1238
1239        let mut cursor = start_addr;
1240        while cursor < end_addr {
1241            // We can cache the "is the cursor mapped?" check because MMTk maps metadata at
1242            // chunk-level
1243            if cursor > mapped_grain {
1244                if cursor.is_mapped() {
1245                    mapped_grain = cursor.align_up(mmap_granularity) - 0x1;
1246                } else {
1247                    // We encounter an unmapped address. Just return None.
1248                    return None;
1249                }
1250            }
1251            // If we find non-zero value, just return it.
1252            if !unsafe { self.load::<T>(cursor).is_zero() } {
1253                return Some(cursor);
1254            }
1255            cursor += region_bytes;
1256        }
1257        None
1258    }
1259
1260    fn find_next_non_zero_value_fast<T: MetadataValue>(
1261        &self,
1262        data_addr: Address,
1263        search_limit_bytes: usize,
1264    ) -> Option<Address> {
1265        debug_assert!(self.uses_contiguous_side_metadata());
1266
1267        // Quick check if the data address is mapped at all.
1268        if !data_addr.is_mapped() {
1269            return None;
1270        }
1271        // Quick check if the current data_addr has a non zero value.
1272        if !unsafe { self.load::<T>(data_addr).is_zero() } {
1273            return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1274        }
1275
1276        // Figure out the start and end data address.
1277        let start_addr = data_addr.align_down(1 << self.log_bytes_in_region);
1278        // We need to align the end_address up because the metadata might be stored right at
1279        // the end address otherwise. Our loop in `find_first_non_zero_bit_in_metadata_byte`
1280        // will not load from this end address, resulting in us potentially not finding the
1281        // correct address for the next set bit.
1282        let end_addr = (data_addr + search_limit_bytes).align_up(1 << self.log_bytes_in_region);
1283
1284        // Then figure out the start and end metadata address and bits.
1285        // The start bit may not be accurate, as we map any address in the region to the same bit.
1286        // We will filter the result at the end to make sure the found address is in the search range.
1287        let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1288        let start_meta_shift = meta_byte_lshift(self, start_addr);
1289        let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1290        let end_meta_shift = meta_byte_lshift(self, end_addr);
1291
1292        let mut res = None;
1293
1294        let mut visitor = |range: BitByteRange| {
1295            match range {
1296                BitByteRange::Bytes { start, end } => {
1297                    match helpers::find_first_non_zero_bit_in_metadata_bytes(start, end) {
1298                        helpers::FindMetaBitResult::Found { addr, bit } => {
1299                            let (addr, bit) = align_metadata_address(self, addr, bit);
1300                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1301                            // Return true to abort the search. We found the bit.
1302                            true
1303                        }
1304                        // If we see unmapped metadata, we don't need to search any more.
1305                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1306                        // Return false to continue searching.
1307                        helpers::FindMetaBitResult::NotFound => false,
1308                    }
1309                }
1310                BitByteRange::BitsInByte {
1311                    addr,
1312                    bit_start,
1313                    bit_end,
1314                } => {
1315                    match helpers::find_first_non_zero_bit_in_metadata_bits(
1316                        addr, bit_start, bit_end,
1317                    ) {
1318                        helpers::FindMetaBitResult::Found { addr, bit } => {
1319                            let (addr, bit) = align_metadata_address(self, addr, bit);
1320                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1321                            // Return true to abort the search. We found the bit.
1322                            true
1323                        }
1324                        // If we see unmapped metadata, we don't need to search any more.
1325                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1326                        // Return false to continue searching.
1327                        helpers::FindMetaBitResult::NotFound => false,
1328                    }
1329                }
1330            }
1331        };
1332
1333        ranges::break_bit_range(
1334            start_meta_addr,
1335            start_meta_shift,
1336            end_meta_addr,
1337            end_meta_shift,
1338            true,
1339            &mut visitor,
1340        );
1341
1342        // We have to filter the result. We search between [start_addr, end_addr). But we actually
1343        // search with metadata bits. It is possible the metadata bit for start_addr is the same bit
1344        // as an address that is before start_addr. E.g. 0x2010f026360 and 0x2010f026361 are mapped
1345        // to the same bit, 0x2010f026361 is the start address and 0x2010f026360 is outside the search range.
1346        res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1347            .filter(|addr| *addr >= start_addr && *addr < end_addr)
1348    }
1349
1350    /// Search for data addresses that have non zero values in the side metadata.  This method is
1351    /// primarily used for heap traversal by scanning the VO bits.
1352    ///
1353    /// This function searches the side metadata for the data address range from `data_start_addr`
1354    /// (inclusive) to `data_end_addr` (exclusive).  The data address range must be fully mapped.
1355    ///
1356    /// For each data region that has non-zero side metadata, `visit_data` is called with the lowest
1357    /// address of that region.  Note that it may not be the original address used to set the
1358    /// metadata bits.
1359    pub fn scan_non_zero_values<T: MetadataValue>(
1360        &self,
1361        data_start_addr: Address,
1362        data_end_addr: Address,
1363        visit_data: &mut impl FnMut(Address),
1364    ) {
1365        if self.uses_contiguous_side_metadata() && self.log_num_of_bits == 0 {
1366            // Contiguous one-bit-per-region side metadata
1367            // TODO: VO bits is one-bit-per-word.  But if we want to scan other metadata (such as
1368            // the forwarding bits which has two bits per word), we will need to refactor the
1369            // algorithm of `scan_non_zero_values_fast`.
1370            self.scan_non_zero_values_fast(data_start_addr, data_end_addr, visit_data);
1371        } else {
1372            // TODO: VO bits are always contiguous.  But if we want to scan other metadata, such as
1373            // side mark bits, we need to refactor `bulk_update_metadata` to support `FnMut`, too,
1374            // and use it to apply `scan_non_zero_values_fast` on each contiguous side metadata
1375            // range.
1376            warn!(
1377                "We are trying to search for non zero bits in a discontiguous side metadata \
1378            or the metadata has more than one bit per region. \
1379                The performance is slow, as MMTk does not optimize for this case."
1380            );
1381            self.scan_non_zero_values_simple::<T>(data_start_addr, data_end_addr, visit_data);
1382        }
1383    }
1384
1385    fn scan_non_zero_values_simple<T: MetadataValue>(
1386        &self,
1387        data_start_addr: Address,
1388        data_end_addr: Address,
1389        visit_data: &mut impl FnMut(Address),
1390    ) {
1391        let region_bytes = 1usize << self.log_bytes_in_region;
1392
1393        let mut cursor = data_start_addr;
1394        while cursor < data_end_addr {
1395            debug_assert!(cursor.is_mapped());
1396
1397            // If we find non-zero value, just call back.
1398            if !unsafe { self.load::<T>(cursor).is_zero() } {
1399                visit_data(cursor);
1400            }
1401            cursor += region_bytes;
1402        }
1403    }
1404
1405    fn scan_non_zero_values_fast(
1406        &self,
1407        data_start_addr: Address,
1408        data_end_addr: Address,
1409        visit_data: &mut impl FnMut(Address),
1410    ) {
1411        debug_assert!(self.uses_contiguous_side_metadata());
1412        debug_assert_eq!(self.log_num_of_bits, 0);
1413
1414        // Then figure out the start and end metadata address and bits.
1415        let start_meta_addr = address_to_contiguous_meta_address(self, data_start_addr);
1416        let start_meta_shift = meta_byte_lshift(self, data_start_addr);
1417        let end_meta_addr = address_to_contiguous_meta_address(self, data_end_addr);
1418        let end_meta_shift = meta_byte_lshift(self, data_end_addr);
1419
1420        let mut visitor = |range| {
1421            match range {
1422                BitByteRange::Bytes { start, end } => {
1423                    helpers::scan_non_zero_bits_in_metadata_bytes(start, end, &mut |addr, bit| {
1424                        visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1425                    });
1426                }
1427                BitByteRange::BitsInByte {
1428                    addr,
1429                    bit_start,
1430                    bit_end,
1431                } => helpers::scan_non_zero_bits_in_metadata_bits(
1432                    addr,
1433                    bit_start,
1434                    bit_end,
1435                    &mut |addr, bit| {
1436                        visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1437                    },
1438                ),
1439            }
1440            false
1441        };
1442
1443        ranges::break_bit_range(
1444            start_meta_addr,
1445            start_meta_shift,
1446            end_meta_addr,
1447            end_meta_shift,
1448            true,
1449            &mut visitor,
1450        );
1451    }
1452}
1453
1454impl fmt::Debug for SideMetadataSpec {
1455    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1456        f.write_fmt(format_args!(
1457            "SideMetadataSpec {} {{ \
1458            **is_global: {:?} \
1459            **offset: 0x{:x} \
1460            **log_num_of_bits: 0x{:x} \
1461            **log_bytes_in_region: 0x{:x} \
1462            }}",
1463            self.name, self.is_global, self.offset, self.log_num_of_bits, self.log_bytes_in_region
1464        ))
1465    }
1466}
1467
1468/// Calculate the offset of the next side metadata spec after the given spec.
1469/// This is used to calculate the offset field in [`crate::util::metadata::side_metadata::SideMetadataSpec`].
1470pub const fn side_metadata_offset_after(spec: &SideMetadataSpec) -> usize {
1471    // Some metadata may be so small that its size is not a multiple of byte size. One example
1472    // is `CHUNK_MARK`. It is one byte per chunk. However, on 32-bit architectures, we allocate
1473    // side metadata per chunk. In that case, it will only occupy one byte. If we do not align
1474    // the upper bound offset up, subsequent local metadata that need to be accessed at, for
1475    // example, word granularity will be misaligned.
1476    // TODO: Currently we align metadata to word size so that it is safe to access the metadata
1477    // one word at a time. In the future, we may allow each metadata to specify its own alignment
1478    // requirement.
1479    raw_align_up(spec.upper_bound_offset(), BYTES_IN_WORD)
1480}
1481
1482/// This struct stores all the side metadata specs for a policy. Generally a policy needs to know its own
1483/// side metadata spec as well as the plan's specs.
1484pub(crate) struct SideMetadataContext {
1485    // For plans
1486    pub global: Vec<SideMetadataSpec>,
1487    // For policies
1488    pub local: Vec<SideMetadataSpec>,
1489}
1490
1491impl SideMetadataContext {
1492    #[allow(clippy::vec_init_then_push)] // allow this, as we conditionally push based on features.
1493    pub fn new_global_specs(specs: &[SideMetadataSpec]) -> Vec<SideMetadataSpec> {
1494        let mut ret = vec![];
1495
1496        #[cfg(feature = "vo_bit")]
1497        ret.push(VO_BIT_SIDE_METADATA_SPEC);
1498
1499        if let Some(spec) = crate::mmtk::SFT_MAP.get_side_metadata() {
1500            if spec.is_global {
1501                ret.push(*spec);
1502            }
1503        }
1504
1505        // Any plan that uses the chunk map needs to reserve the chunk map table.
1506        // As we use either the mark sweep or (non moving) immix as the non moving space,
1507        // and both policies use the chunk map, we just add the chunk map table globally.
1508        ret.push(crate::util::heap::chunk_map::ChunkMap::ALLOC_TABLE);
1509
1510        ret.extend_from_slice(specs);
1511        ret
1512    }
1513
1514    pub fn get_local_specs(&self) -> &[SideMetadataSpec] {
1515        &self.local
1516    }
1517
1518    #[cfg(debug_assertions)]
1519    pub fn assert_metadata_ranges_in_reserved_range(
1520        &self,
1521        start: Address,
1522        size: usize,
1523        space_name: &str,
1524    ) {
1525        let reserved = {
1526            let base = super::layout::global_side_metadata_base_address();
1527            let bytes = super::layout::side_metadata_reserved_bytes();
1528            base..(base + bytes)
1529        };
1530        let check_spec = |spec: &SideMetadataSpec| {
1531            if !spec.uses_contiguous_side_metadata() {
1532                return;
1533            }
1534            let metadata_start = address_to_meta_address(spec, start);
1535            let mmap_start = metadata_start.align_down(BYTES_IN_PAGE);
1536            let metadata_size = data_to_meta_size_round_up(spec, size);
1537            let mmap_end = (metadata_start + metadata_size).align_up(BYTES_IN_PAGE);
1538            debug_assert!(
1539                mmap_start >= reserved.start && mmap_end <= reserved.end,
1540                "Side metadata range for spec {} in space {} is outside reserved range: [{}, {}) vs [{}, {})",
1541                spec.name,
1542                space_name,
1543                mmap_start,
1544                mmap_end,
1545                reserved.start,
1546                reserved.end
1547            );
1548        };
1549        self.global.iter().for_each(check_spec);
1550        self.local.iter().for_each(check_spec);
1551    }
1552
1553    /// Return the pages reserved for side metadata based on the data pages we used.
1554    // We used to use PageAccouting to count pages used in side metadata. However,
1555    // that means we always count pages while we may reserve less than a page each time.
1556    // This could lead to overcount. I think the easier way is to not account
1557    // when we allocate for sidemetadata, but to calculate the side metadata usage based on
1558    // how many data pages we use when reporting.
1559    pub fn calculate_reserved_pages(&self, data_pages: usize) -> usize {
1560        let mut total = 0;
1561        for spec in self.global.iter() {
1562            // This rounds up.  No matter how small `data_pages` is, the side metadata size will be
1563            // at least one page.  This behavior is *intended*.  This over-estimated amount is used
1564            // for triggering GC and resizing the heap.
1565            total += data_to_meta_size_round_up(spec, data_pages);
1566        }
1567        for spec in self.local.iter() {
1568            total += data_to_meta_size_round_up(spec, data_pages);
1569        }
1570        total
1571    }
1572
1573    // ** NOTE: **
1574    //  Regardless of the number of bits in a metadata unit, we always represent its content as a word.
1575
1576    /// Tries to map the required metadata space and returns `true` is successful.
1577    /// This can be called at page granularity.
1578    pub fn try_map_metadata_space(
1579        &self,
1580        start: Address,
1581        size: usize,
1582        space_name: &str,
1583    ) -> MmapResult<()> {
1584        debug!(
1585            "try_map_metadata_space({}, 0x{:x}, {}, {})",
1586            start,
1587            size,
1588            self.global.len(),
1589            self.local.len()
1590        );
1591        // Page aligned
1592        debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1593        debug_assert!(size % BYTES_IN_PAGE == 0);
1594        self.map_metadata_internal(start, size, false, space_name)
1595    }
1596
1597    /// Tries to map the required metadata address range, without reserving swap-space/physical memory for it.
1598    /// This will make sure the address range is exclusive to the caller. This should be called at chunk granularity.
1599    ///
1600    /// NOTE: Accessing addresses in this range will produce a segmentation fault if swap-space is not mapped using the `try_map_metadata_space` function.
1601    pub fn try_map_metadata_address_range(
1602        &self,
1603        start: Address,
1604        size: usize,
1605        name: &str,
1606    ) -> MmapResult<()> {
1607        debug!(
1608            "try_map_metadata_address_range({}, 0x{:x}, {}, {})",
1609            start,
1610            size,
1611            self.global.len(),
1612            self.local.len()
1613        );
1614        // Chunk aligned
1615        debug_assert!(start.is_aligned_to(BYTES_IN_CHUNK));
1616        debug_assert!(size % BYTES_IN_CHUNK == 0);
1617        self.map_metadata_internal(start, size, true, name)
1618    }
1619
1620    /// The internal function to mmap metadata
1621    ///
1622    /// # Arguments
1623    /// * `start` - The starting address of the source data.
1624    /// * `size` - The size of the source data (in bytes).
1625    /// * `no_reserve` - whether to invoke mmap with a noreserve flag (we use this flag to quarantine address range)
1626    /// * `space_name`: The name of the space, used for annotating the mmap.
1627    fn map_metadata_internal(
1628        &self,
1629        start: Address,
1630        size: usize,
1631        no_reserve: bool,
1632        space_name: &str,
1633    ) -> MmapResult<()> {
1634        for spec in self.global.iter() {
1635            let anno = MmapAnnotation::SideMeta {
1636                space: space_name,
1637                meta: spec.name,
1638            };
1639            try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1640        }
1641
1642        #[cfg(target_pointer_width = "32")]
1643        let mut lsize: usize = 0;
1644
1645        for spec in self.local.iter() {
1646            // For local side metadata, we always have to reserve address space for all local
1647            // metadata required by all policies in MMTk to be able to calculate a constant offset
1648            // for each local metadata at compile-time (it's like assigning an ID to each policy).
1649            //
1650            // As the plan is chosen at run-time, we will never know which subset of policies will
1651            // be used during run-time. We can't afford this much address space in 32-bits.
1652            // So, we switch to the chunk-based approach for this specific case.
1653            //
1654            // The global metadata is different in that for each plan, we can calculate its constant
1655            // base addresses at compile-time. Using the chunk-based approach will need the same
1656            // address space size as the current not-chunked approach.
1657            #[cfg(target_pointer_width = "64")]
1658            {
1659                let anno = MmapAnnotation::SideMeta {
1660                    space: space_name,
1661                    meta: spec.name,
1662                };
1663                try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1664            }
1665            #[cfg(target_pointer_width = "32")]
1666            {
1667                lsize += metadata_bytes_per_chunk(spec.log_bytes_in_region, spec.log_num_of_bits);
1668            }
1669        }
1670
1671        #[cfg(target_pointer_width = "32")]
1672        if lsize > 0 {
1673            let max = BYTES_IN_CHUNK >> super::layout::LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO;
1674            debug_assert!(
1675                lsize <= max,
1676                "local side metadata per chunk (0x{:x}) must be less than (0x{:x})",
1677                lsize,
1678                max
1679            );
1680            // We are creating a mmap for all side metadata instead of one specific metadata.  We
1681            // just annotate it as "all" here.
1682            let anno = MmapAnnotation::SideMeta {
1683                space: space_name,
1684                meta: "all",
1685            };
1686            try_map_per_chunk_metadata_space(start, size, lsize, no_reserve, &anno)?;
1687        }
1688
1689        Ok(())
1690    }
1691
1692    /// Unmap the corresponding metadata space or panic.
1693    ///
1694    /// Note-1: This function is only used for test and debug right now.
1695    ///
1696    /// Note-2: This function uses munmap() which works at page granularity.
1697    ///     If the corresponding metadata space's size is not a multiple of page size,
1698    ///     the actual unmapped space will be bigger than what you specify.
1699    #[cfg(test)]
1700    pub fn ensure_unmap_metadata_space(&self, start: Address, size: usize) {
1701        trace!("ensure_unmap_metadata_space({}, 0x{:x})", start, size);
1702        debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1703        debug_assert!(size % BYTES_IN_PAGE == 0);
1704
1705        for spec in self.global.iter() {
1706            ensure_munmap_contiguous_metadata_space(start, size, spec);
1707        }
1708
1709        for spec in self.local.iter() {
1710            #[cfg(target_pointer_width = "64")]
1711            {
1712                ensure_munmap_contiguous_metadata_space(start, size, spec);
1713            }
1714            #[cfg(target_pointer_width = "32")]
1715            {
1716                ensure_munmap_chunked_metadata_space(start, size, spec);
1717            }
1718        }
1719    }
1720}
1721
1722/// A byte array in side-metadata
1723pub struct MetadataByteArrayRef<const ENTRIES: usize> {
1724    #[cfg(feature = "extreme_assertions")]
1725    heap_range_start: Address,
1726    #[cfg(feature = "extreme_assertions")]
1727    spec: SideMetadataSpec,
1728    data: &'static [u8; ENTRIES],
1729}
1730
1731impl<const ENTRIES: usize> MetadataByteArrayRef<ENTRIES> {
1732    /// Get a piece of metadata address range as a byte array.
1733    ///
1734    /// # Arguments
1735    ///
1736    /// * `metadata_spec` - The specification of the target side metadata.
1737    /// * `start` - The starting address of the heap range.
1738    /// * `bytes` - The size of the heap range.
1739    ///
1740    pub fn new(metadata_spec: &SideMetadataSpec, start: Address, bytes: usize) -> Self {
1741        debug_assert_eq!(
1742            metadata_spec.log_num_of_bits, LOG_BITS_IN_BYTE as usize,
1743            "Each heap entry should map to a byte in side-metadata"
1744        );
1745        debug_assert_eq!(
1746            bytes >> metadata_spec.log_bytes_in_region,
1747            ENTRIES,
1748            "Heap range size and MetadataByteArray size does not match"
1749        );
1750        Self {
1751            #[cfg(feature = "extreme_assertions")]
1752            heap_range_start: start,
1753            #[cfg(feature = "extreme_assertions")]
1754            spec: *metadata_spec,
1755            // # Safety
1756            // The metadata memory is assumed to be mapped when accessing.
1757            data: unsafe { &*address_to_meta_address(metadata_spec, start).to_ptr() },
1758        }
1759    }
1760
1761    /// Get the length of the array.
1762    #[allow(clippy::len_without_is_empty)]
1763    pub const fn len(&self) -> usize {
1764        ENTRIES
1765    }
1766
1767    /// Get a byte from the metadata byte array at the given index.
1768    #[allow(clippy::let_and_return)]
1769    pub fn get(&self, index: usize) -> u8 {
1770        #[cfg(feature = "extreme_assertions")]
1771        let _lock = sanity::SANITY_LOCK.lock().unwrap();
1772        let value = self.data[index];
1773        #[cfg(feature = "extreme_assertions")]
1774        {
1775            let data_addr = self.heap_range_start + (index << self.spec.log_bytes_in_region);
1776            sanity::verify_load::<u8>(&self.spec, data_addr, value);
1777        }
1778        value
1779    }
1780}
1781
1782#[cfg(test)]
1783mod tests {
1784    use super::*;
1785    use crate::mmap_anno_test;
1786    use crate::util::metadata::side_metadata::SideMetadataContext;
1787
1788    // offset is not used in these tests.
1789    pub const ZERO_OFFSET: usize = 0;
1790
1791    #[test]
1792    fn calculate_reserved_pages_one_spec() {
1793        // 1 bit per 8 bytes - 1:64
1794        let spec = SideMetadataSpec {
1795            name: "test_spec",
1796            is_global: true,
1797            offset: ZERO_OFFSET,
1798            log_num_of_bits: 0,
1799            log_bytes_in_region: 3,
1800        };
1801        let side_metadata = SideMetadataContext {
1802            global: vec![spec],
1803            local: vec![],
1804        };
1805        assert_eq!(side_metadata.calculate_reserved_pages(0), 0);
1806        assert_eq!(side_metadata.calculate_reserved_pages(63), 1);
1807        assert_eq!(side_metadata.calculate_reserved_pages(64), 1);
1808        assert_eq!(side_metadata.calculate_reserved_pages(65), 2);
1809        assert_eq!(side_metadata.calculate_reserved_pages(1024), 16);
1810    }
1811
1812    #[test]
1813    fn calculate_reserved_pages_multi_specs() {
1814        // 1 bit per 8 bytes - 1:64
1815        let gspec = SideMetadataSpec {
1816            name: "gspec",
1817            is_global: true,
1818            offset: ZERO_OFFSET,
1819            log_num_of_bits: 0,
1820            log_bytes_in_region: 3,
1821        };
1822        // 2 bits per page - 2 / (4k * 8) = 1:16k
1823        let lspec = SideMetadataSpec {
1824            name: "lspec",
1825            is_global: false,
1826            offset: ZERO_OFFSET,
1827            log_num_of_bits: 1,
1828            log_bytes_in_region: 12,
1829        };
1830        let side_metadata = SideMetadataContext {
1831            global: vec![gspec],
1832            local: vec![lspec],
1833        };
1834        assert_eq!(side_metadata.calculate_reserved_pages(1024), 16 + 1);
1835    }
1836
1837    use crate::util::heap::layout::vm_layout;
1838    use crate::util::test_util::{serial_test, with_cleanup};
1839    use paste::paste;
1840
1841    const TEST_LOG_BYTES_IN_REGION: usize = 12;
1842
1843    fn test_side_metadata(
1844        log_bits: usize,
1845        f: impl Fn(&SideMetadataSpec, Address, Address) + std::panic::RefUnwindSafe,
1846    ) {
1847        serial_test(|| {
1848            core_test_initialize_side_metadata();
1849
1850            let spec = SideMetadataSpec {
1851                name: "Test Spec $tname",
1852                is_global: true,
1853                offset: 0,
1854                log_num_of_bits: log_bits,
1855                log_bytes_in_region: TEST_LOG_BYTES_IN_REGION, // page size
1856            };
1857            let context = SideMetadataContext {
1858                global: vec![spec],
1859                local: vec![],
1860            };
1861            let mut sanity = SideMetadataSanity::new();
1862            sanity.verify_metadata_context("TestPolicy", &context);
1863
1864            let data_addr = vm_layout::vm_layout().heap_start;
1865            // Make sure the address is mapped.
1866            crate::MMAPPER
1867                .ensure_mapped(
1868                    data_addr,
1869                    1,
1870                    HugePageSupport::No,
1871                    MmapProtection::ReadWrite,
1872                    mmap_anno_test!(),
1873                )
1874                .unwrap();
1875            let meta_addr = address_to_meta_address(&spec, data_addr);
1876            with_cleanup(
1877                || {
1878                    let mmap_result =
1879                        context.try_map_metadata_space(data_addr, BYTES_IN_PAGE, "test_space");
1880                    assert!(mmap_result.is_ok(), "{:?}", mmap_result);
1881
1882                    f(&spec, data_addr, meta_addr);
1883                },
1884                || {
1885                    // Clear the metadata -- use u64 (max length we support)
1886                    assert!(log_bits <= 6);
1887                    let meta_ptr: *mut u64 = meta_addr.to_mut_ptr();
1888                    unsafe { *meta_ptr = 0 };
1889
1890                    sanity::reset();
1891                },
1892            )
1893        })
1894    }
1895
1896    fn max_value(log_bits: usize) -> u64 {
1897        (0..(1 << log_bits)).fold(0, |accum, x| accum + (1 << x))
1898    }
1899    #[test]
1900    fn test_max_value() {
1901        assert_eq!(max_value(0), 1);
1902        assert_eq!(max_value(1), 0b11);
1903        assert_eq!(max_value(2), 0b1111);
1904        assert_eq!(max_value(3), 255);
1905        assert_eq!(max_value(4), 65535);
1906    }
1907
1908    macro_rules! test_side_metadata_access {
1909        ($tname: ident, $type: ty, $log_bits: expr) => {
1910            paste!{
1911                #[test]
1912                fn [<$tname _load>]() {
1913                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1914                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1915
1916                        // Initial value should be 0
1917                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1918                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
1919
1920                        // Set to max
1921                        let max_value: $type = max_value($log_bits) as _;
1922                        unsafe { spec.store::<$type>(data_addr, max_value); }
1923                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, max_value);
1924                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
1925                        assert_eq!(unsafe { *meta_ptr }, max_value);
1926                    });
1927                }
1928
1929                #[test]
1930                fn [<$tname _store>]() {
1931                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1932                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1933                        let max_value: $type = max_value($log_bits) as _;
1934
1935                        // Set the metadata byte(s) to all 1s
1936                        unsafe { *meta_ptr = <$type>::MAX; }
1937                        // Store 0 to the side metadata
1938                        unsafe { spec.store::<$type>(data_addr, 0); }
1939                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1940                        // Only the affected bits are set to 0
1941                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1942                    });
1943                }
1944
1945                #[test]
1946                fn [<$tname _atomic_store>]() {
1947                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1948                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1949                        let max_value: $type = max_value($log_bits) as _;
1950
1951                        // Set the metadata byte(s) to all 1s
1952                        unsafe { *meta_ptr = <$type>::MAX; }
1953                        // Store 0 to the side metadata
1954                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1955                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1956                        // Only the affected bits are set to 0
1957                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1958                    });
1959                }
1960
1961                #[test]
1962                fn [<$tname _compare_exchange_success>]() {
1963                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1964                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1965                        let max_value: $type = max_value($log_bits) as _;
1966                        // Set the metadata byte(s) to all 1s
1967                        unsafe { *meta_ptr = <$type>::MAX; }
1968                        // Store 1 to the side metadata
1969                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1970
1971                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1972                        assert_eq!(old_val, 1);
1973
1974                        let new_val = 0;
1975                        let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
1976                        assert!(res.is_ok());
1977                        assert_eq!(res.unwrap(), old_val, "old vals do not match");
1978
1979                        let after_update = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1980                        assert_eq!(after_update, new_val);
1981                        // Only the affected bits are set to 0
1982                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1983                    });
1984                }
1985
1986                #[test]
1987                fn [<$tname _compare_exchange_fail>]() {
1988                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1989                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1990                        // Set the metadata byte(s) to all 1s
1991                        unsafe { *meta_ptr = <$type>::MAX; }
1992                        // Store 1 to the side metadata
1993                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1994
1995                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1996                        assert_eq!(old_val, 1);
1997
1998                        // make old_val outdated
1999                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2000                        let bits_before_cas = unsafe { *meta_ptr };
2001
2002                        let new_val = 0;
2003                        let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
2004                        assert!(res.is_err());
2005                        assert_eq!(res.err().unwrap(), 0);
2006                        let bits_after_cas = unsafe { *meta_ptr };
2007                        assert_eq!(bits_before_cas, bits_after_cas);
2008                    });
2009                }
2010
2011                #[test]
2012                fn [<$tname _fetch_add_1>]() {
2013                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2014                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2015                        // Set the metadata byte(s) to all 1s
2016                        unsafe { *meta_ptr = <$type>::MAX; }
2017                        // Store 0 to the side metadata
2018                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2019
2020                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2021
2022                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2023                        assert_eq!(old_val_from_fetch, old_val);
2024
2025                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2026                        assert_eq!(new_val, 1);
2027                    });
2028                }
2029
2030                #[test]
2031                fn [<$tname _fetch_add_max>]() {
2032                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2033                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2034                        let max_value: $type = max_value($log_bits) as _;
2035                        // Set the metadata byte(s) to all 1s
2036                        unsafe { *meta_ptr = <$type>::MAX; }
2037                        // Store 0 to the side metadata
2038                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2039
2040                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2041
2042                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2043                        assert_eq!(old_val_from_fetch, old_val);
2044
2045                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2046                        assert_eq!(new_val, max_value);
2047                    });
2048                }
2049
2050                #[test]
2051                fn [<$tname _fetch_add_overflow>]() {
2052                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2053                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2054                        let max_value: $type = max_value($log_bits) as _;
2055                        // Set the metadata byte(s) to all 1s
2056                        unsafe { *meta_ptr = <$type>::MAX; }
2057                        // Store max to the side metadata
2058                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2059
2060                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2061
2062                        // add 1 to max value will cause overflow and wrap around to 0
2063                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2064                        assert_eq!(old_val_from_fetch, old_val);
2065
2066                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2067                        assert_eq!(new_val, 0);
2068                    });
2069                }
2070
2071                #[test]
2072                fn [<$tname _fetch_sub_1>]() {
2073                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2074                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2075                        // Set the metadata byte(s) to all 1s
2076                        unsafe { *meta_ptr = <$type>::MAX; }
2077                        // Store 1 to the side metadata
2078                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2079
2080                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2081
2082                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2083                        assert_eq!(old_val_from_fetch, old_val);
2084
2085                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2086                        assert_eq!(new_val, 0);
2087                    });
2088                }
2089
2090                #[test]
2091                fn [<$tname _fetch_sub_max>]() {
2092                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2093                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2094                        let max_value: $type = max_value($log_bits) as _;
2095                        // Set the metadata byte(s) to all 1s
2096                        unsafe { *meta_ptr = <$type>::MAX; }
2097                        // Store max to the side metadata
2098                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2099
2100                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2101
2102                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2103                        assert_eq!(old_val_from_fetch, old_val);
2104
2105                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2106                        assert_eq!(new_val, 0);
2107                    });
2108                }
2109
2110                #[test]
2111                fn [<$tname _fetch_sub_overflow>]() {
2112                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2113                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2114                        let max_value: $type = max_value($log_bits) as _;
2115                        // Set the metadata byte(s) to all 1s
2116                        unsafe { *meta_ptr = <$type>::MAX; }
2117                        // Store 0 to the side metadata
2118                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2119
2120                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2121
2122                        // sub 1 from 0 will cause overflow, and wrap around to max
2123                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2124                        assert_eq!(old_val_from_fetch, old_val);
2125
2126                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2127                        assert_eq!(new_val, max_value);
2128                    });
2129                }
2130
2131                #[test]
2132                fn [<$tname _fetch_and>]() {
2133                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2134                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2135                        let max_value: $type = max_value($log_bits) as _;
2136                        // Set the metadata byte(s) to all 1s
2137                        unsafe { *meta_ptr = <$type>::MAX; }
2138                        // Store all 1s to the side metadata
2139                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2140
2141                        // max and max should be max
2142                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2143                        let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2144                        assert_eq!(old_val_from_fetch, old_val, "old values do not match");
2145                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value, "load values do not match");
2146                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX, "raw values do not match");
2147
2148                        // max and last_bit_zero should last_bit_zero
2149                        let last_bit_zero = max_value - 1;
2150                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2151                        let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, last_bit_zero, Ordering::SeqCst);
2152                        assert_eq!(old_val_from_fetch, old_val);
2153                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), last_bit_zero);
2154                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX - 1);
2155                    });
2156                }
2157
2158                #[test]
2159                fn [<$tname _fetch_or>]() {
2160                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2161                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2162                        let max_value: $type = max_value($log_bits) as _;
2163                        // Set the metadata byte(s) to all 0s
2164                        unsafe { *meta_ptr = 0; }
2165                        // Store 0 to the side metadata
2166                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2167
2168                        // 0 or 0 should be 0
2169                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2170                        let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2171                        assert_eq!(old_val_from_fetch, old_val);
2172                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2173                        assert_eq!(unsafe { *meta_ptr }, 0);
2174
2175                        // 0 and max should max
2176                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2177                        let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2178                        assert_eq!(old_val_from_fetch, old_val);
2179                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2180                        assert_eq!(unsafe { *meta_ptr }, max_value);
2181                    });
2182                }
2183
2184                #[test]
2185                fn [<$tname _fetch_update_success>]() {
2186                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2187                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2188                        let max_value: $type = max_value($log_bits) as _;
2189                        // Set the metadata byte(s) to all 1s
2190                        unsafe { *meta_ptr = <$type>::MAX; }
2191                        // Store all 1s to the side metadata
2192                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2193
2194                        // update from max to zero
2195                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2196                        let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| Some(0));
2197                        assert!(fetch_res.is_ok());
2198                        assert_eq!(fetch_res.unwrap(), old_val);
2199                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2200                        // Only the affected bits are set to 0
2201                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
2202                    });
2203                }
2204
2205                #[test]
2206                fn [<$tname _fetch_update_fail>]() {
2207                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2208                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2209                        let max_value: $type = max_value($log_bits) as _;
2210                        // Set the metadata byte(s) to all 1s
2211                        unsafe { *meta_ptr = <$type>::MAX; }
2212                        // Store all 1s to the side metadata
2213                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2214
2215                        // update from max to zero
2216                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2217                        let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| None);
2218                        assert!(fetch_res.is_err());
2219                        assert_eq!(fetch_res.err().unwrap(), old_val);
2220                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2221                        // Only the affected bits are set to 0
2222                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX);
2223                    });
2224                }
2225
2226                #[test]
2227                fn [<$tname _find_prev_non_zero_value_easy>]() {
2228                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2229                        let max_value: $type = max_value($log_bits) as _;
2230                        // Store non zero value at data_addr
2231                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2232
2233                        // Find the value starting from data_addr, at max 8 bytes.
2234                        // We should find data_addr
2235                        let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(data_addr, 8) };
2236                        assert!(res_addr.is_some());
2237                        assert_eq!(res_addr.unwrap(), data_addr);
2238                    });
2239                }
2240
2241                #[test]
2242                fn [<$tname _find_prev_non_zero_value_arbitrary_bytes>]() {
2243                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2244                        let max_value: $type = max_value($log_bits) as _;
2245                        // Store non zero value at data_addr
2246                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2247
2248                        // Start from data_addr, we offset arbitrary length, and search back to find data_addr
2249                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2250                        for len in 1..(test_region*4) {
2251                            let start_addr = data_addr + len;
2252                            // Use len+1, as len is non inclusive.
2253                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2254                            assert!(res_addr.is_some());
2255                            assert_eq!(res_addr.unwrap(), data_addr);
2256                        }
2257                    });
2258                }
2259
2260                #[test]
2261                fn [<$tname _find_prev_non_zero_value_arbitrary_start>]() {
2262                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2263                        let max_value: $type = max_value($log_bits) as _;
2264
2265                        // data_addr has a non-aligned offset
2266                        for offset in 0..7usize {
2267                            // Apply offset and test with the new data addr
2268                            let test_data_addr = data_addr + offset;
2269                            spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2270
2271                            // The return result should be aligned
2272                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(test_data_addr, 4096) };
2273                            assert!(res_addr.is_some());
2274                            assert_eq!(res_addr.unwrap(), data_addr);
2275
2276                            // Clear whatever is set
2277                            spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2278                        }
2279                    });
2280                }
2281
2282                #[test]
2283                fn [<$tname _find_prev_non_zero_value_no_find>]() {
2284                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2285                        // Store zero value at data_addr -- so we won't find anything
2286                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2287
2288                        // Start from data_addr, we offset arbitrary length, and search back
2289                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2290                        for len in 1..(test_region*4) {
2291                            let start_addr = data_addr + len;
2292                            // Use len+1, as len is non inclusive.
2293                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2294                            assert!(res_addr.is_none());
2295                        }
2296                    });
2297                }
2298
2299                #[test]
2300                fn [<$tname _find_next_non_zero_value_easy>]() {
2301                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2302                        let max_value: $type = max_value($log_bits) as _;
2303                        // Store non zero value at data_addr
2304                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2305
2306                        // Find the value starting from data_addr, at max 8 bytes.
2307                        // We should find data_addr
2308                        let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(data_addr, 8) };
2309                        assert!(res_addr.is_some());
2310                        assert_eq!(res_addr.unwrap(), data_addr);
2311                    });
2312                }
2313
2314                #[test]
2315                fn [<$tname _find_next_non_zero_value_arbitrary_bytes>]() {
2316                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2317                        let max_value: $type = max_value($log_bits) as _;
2318                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2319
2320                        // Take a data address in the middle since metadata before
2321                        // the start may not be mapped
2322                        let data_addr = data_addr + test_region*4;
2323
2324                        // Store non zero value at data_addr
2325                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2326                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2327
2328                        // Start from data_addr, we offset arbitrary length, and search forwards to find data_addr
2329                        for len in 1..(test_region*4) {
2330                            let start_addr = data_addr - len;
2331                            // Use len+1, as len is non inclusive.
2332                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2333                            assert!(res_addr.is_some());
2334                            assert_eq!(res_addr.unwrap(), data_addr);
2335                        }
2336                    });
2337                }
2338
2339                #[test]
2340                fn [<$tname _find_next_non_zero_value_arbitrary_start>]() {
2341                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2342                        let max_value: $type = max_value($log_bits) as _;
2343
2344                        // data_addr has a non-aligned offset
2345                        for offset in 0..7usize {
2346                            // Apply offset and test with the new data addr
2347                            let test_data_addr = data_addr + offset;
2348                            spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2349
2350                            // The return result should be aligned
2351                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(test_data_addr, 4096) };
2352                            assert!(res_addr.is_some());
2353                            assert_eq!(res_addr.unwrap(), data_addr);
2354
2355                            // Clear whatever is set
2356                            spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2357                        }
2358                    });
2359                }
2360
2361                #[test]
2362                fn [<$tname _find_next_non_zero_value_no_find>]() {
2363                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2364                        // Store zero value at data_addr -- so we won't find anything
2365                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2366
2367                        // Start from data_addr, we offset arbitrary length, and search back
2368                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2369                        for len in 1..(test_region*4) {
2370                            let start_addr = data_addr - len;
2371                            // Use len+1, as len is non inclusive.
2372                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2373                            assert!(res_addr.is_none());
2374                        }
2375                    });
2376                }
2377            }
2378        }
2379    }
2380
2381    test_side_metadata_access!(test_u1, u8, 0);
2382    test_side_metadata_access!(test_u2, u8, 1);
2383    test_side_metadata_access!(test_u4, u8, 2);
2384    test_side_metadata_access!(test_u8, u8, 3);
2385    test_side_metadata_access!(test_u16, u16, 4);
2386    test_side_metadata_access!(test_u32, u32, 5);
2387    test_side_metadata_access!(test_u64, u64, 6);
2388    test_side_metadata_access!(
2389        test_usize,
2390        usize,
2391        if cfg!(target_pointer_width = "64") {
2392            6
2393        } else if cfg!(target_pointer_width = "32") {
2394            5
2395        } else {
2396            unreachable!()
2397        }
2398    );
2399
2400    #[test]
2401    fn test_bulk_update_meta_bits() {
2402        let raw_mem =
2403            unsafe { std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(8, 8).unwrap()) };
2404        let addr = Address::from_mut_ptr(raw_mem);
2405
2406        SideMetadataSpec::set_meta_bits(addr, 0, addr, 4);
2407        assert_eq!(unsafe { addr.load::<u64>() }, 0b1111);
2408
2409        SideMetadataSpec::zero_meta_bits(addr, 1, addr, 3);
2410        assert_eq!(unsafe { addr.load::<u64>() }, 0b1001);
2411
2412        SideMetadataSpec::set_meta_bits(addr, 2, addr, 6);
2413        assert_eq!(unsafe { addr.load::<u64>() }, 0b0011_1101);
2414
2415        SideMetadataSpec::zero_meta_bits(addr, 0, addr + 1usize, 0);
2416        assert_eq!(unsafe { addr.load::<u64>() }, 0b0);
2417
2418        SideMetadataSpec::set_meta_bits(addr, 2, addr + 1usize, 2);
2419        assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1100);
2420
2421        SideMetadataSpec::set_meta_bits(addr, 0, addr + 1usize, 2);
2422        assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1111);
2423    }
2424}