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    /// Loads a value from the side metadata for the given address.
541    /// This method has similar semantics to `store` in Rust atomics.
542    pub fn load_atomic<T: MetadataValue>(&self, data_addr: Address, order: Ordering) -> T {
543        self.side_metadata_access::<true, T, _, _, _>(
544            data_addr,
545            None,
546            || {
547                let meta_addr = address_to_meta_address(self, data_addr);
548                let bits_num_log = self.log_num_of_bits;
549                if bits_num_log < 3 {
550                    let lshift = meta_byte_lshift(self, data_addr);
551                    let mask = meta_byte_mask(self) << lshift;
552                    let byte_val = unsafe { meta_addr.atomic_load::<AtomicU8>(order) };
553                    FromPrimitive::from_u8((byte_val & mask) >> lshift).unwrap()
554                } else {
555                    unsafe { T::load_atomic(meta_addr, order) }
556                }
557            },
558            |_v| {
559                #[cfg(feature = "extreme_assertions")]
560                sanity::verify_load(self, data_addr, _v);
561            },
562        )
563    }
564
565    /// Store the given value to the side metadata for the given address.
566    /// This method has similar semantics to `store` in Rust atomics.
567    pub fn store_atomic<T: MetadataValue>(&self, data_addr: Address, metadata: T, order: Ordering) {
568        self.side_metadata_access::<true, T, _, _, _>(
569            data_addr,
570            Some(metadata),
571            || {
572                let meta_addr = address_to_meta_address(self, data_addr);
573                let bits_num_log = self.log_num_of_bits;
574                if bits_num_log < 3 {
575                    let lshift = meta_byte_lshift(self, data_addr);
576                    let mask = meta_byte_mask(self) << lshift;
577                    let metadata_u8 = metadata.to_u8().unwrap();
578                    let _ = unsafe {
579                        <u8 as MetadataValue>::fetch_update(meta_addr, order, order, |v: u8| {
580                            Some((v & !mask) | (metadata_u8 << lshift))
581                        })
582                    };
583                } else {
584                    unsafe {
585                        T::store_atomic(meta_addr, metadata, order);
586                    }
587                }
588            },
589            |_| {
590                #[cfg(feature = "extreme_assertions")]
591                sanity::verify_store(self, data_addr, metadata);
592            },
593        )
594    }
595
596    /// Non-atomically store zero to the side metadata for the given address.
597    /// This method mainly facilitates clearing multiple metadata specs for the same address in a loop.
598    ///
599    /// # Safety
600    ///
601    /// This is unsafe because:
602    ///
603    /// 1. Concurrent access to this operation is undefined behaviour.
604    /// 2. Interleaving Non-atomic and atomic operations is undefined behaviour.
605    pub unsafe fn set_zero(&self, data_addr: Address) {
606        use num_traits::Zero;
607        match self.log_num_of_bits {
608            0..=3 => self.store(data_addr, u8::zero()),
609            4 => self.store(data_addr, u16::zero()),
610            5 => self.store(data_addr, u32::zero()),
611            6 => self.store(data_addr, u64::zero()),
612            _ => unreachable!(),
613        }
614    }
615
616    /// Atomiccally store zero to the side metadata for the given address.
617    /// This method mainly facilitates clearing multiple metadata specs for the same address in a loop.
618    pub fn set_zero_atomic(&self, data_addr: Address, order: Ordering) {
619        use num_traits::Zero;
620        match self.log_num_of_bits {
621            0..=3 => self.store_atomic(data_addr, u8::zero(), order),
622            4 => self.store_atomic(data_addr, u16::zero(), order),
623            5 => self.store_atomic(data_addr, u32::zero(), order),
624            6 => self.store_atomic(data_addr, u64::zero(), order),
625            _ => unreachable!(),
626        }
627    }
628
629    /// Atomically store one to the side metadata for the data address with the _possible_ side effect of corrupting
630    /// and setting the entire byte in the side metadata to 0xff. This can only be used for side metadata smaller
631    /// than a byte.
632    /// This means it does not only set the side metadata for the data address, and it may also have a side effect of
633    /// corrupting and setting the side metadata for the adjacent data addresses. This method is only intended to be
634    /// used as an optimization to skip masking and setting bits in some scenarios where setting adjancent bits to 1 is benign.
635    ///
636    /// # Safety
637    /// This method _may_ corrupt and set adjacent bits in the side metadata as a side effect. The user must
638    /// make sure that this behavior is correct and must not rely on the side effect of this method to set bits.
639    pub unsafe fn set_raw_byte_atomic(&self, data_addr: Address, order: Ordering) {
640        debug_assert!(self.log_num_of_bits < 3);
641        cfg_if::cfg_if! {
642            if #[cfg(feature = "extreme_assertions")] {
643                // For extreme assertions, we only set 1 to the given address.
644                self.store_atomic::<u8>(data_addr, 1, order)
645            } else {
646                self.side_metadata_access::<false, u8, _, _, _>(
647                    data_addr,
648                    Some(1u8),
649                    || {
650                        let meta_addr = address_to_meta_address(self, data_addr);
651                        u8::store_atomic(meta_addr, 0xffu8, order);
652                    },
653                    |_| {}
654                )
655            }
656        }
657    }
658
659    /// Load the raw byte in the side metadata byte that is mapped to the data address.
660    ///
661    /// # Safety
662    /// This is unsafe because:
663    ///
664    /// 1. Concurrent access to this operation is undefined behaviour.
665    /// 2. Interleaving Non-atomic and atomic operations is undefined behaviour.
666    pub unsafe fn load_raw_byte(&self, data_addr: Address) -> u8 {
667        debug_assert!(self.log_num_of_bits < 3);
668        self.side_metadata_access::<false, u8, _, _, _>(
669            data_addr,
670            None,
671            || {
672                let meta_addr = address_to_meta_address(self, data_addr);
673                meta_addr.load::<u8>()
674            },
675            |_| {},
676        )
677    }
678
679    /// Load the raw word that includes the side metadata byte mapped to the data address.
680    ///
681    /// # Safety
682    /// This is unsafe because:
683    ///
684    /// 1. Concurrent access to this operation is undefined behaviour.
685    /// 2. Interleaving Non-atomic and atomic operations is undefined behaviour.
686    pub unsafe fn load_raw_word(&self, data_addr: Address) -> usize {
687        use crate::util::constants::*;
688        debug_assert!(self.log_num_of_bits < (LOG_BITS_IN_BYTE + LOG_BYTES_IN_ADDRESS) as usize);
689        self.side_metadata_access::<false, usize, _, _, _>(
690            data_addr,
691            None,
692            || {
693                let meta_addr = address_to_meta_address(self, data_addr);
694                let aligned_meta_addr = meta_addr.align_down(BYTES_IN_ADDRESS);
695                aligned_meta_addr.load::<usize>()
696            },
697            |_| {},
698        )
699    }
700
701    /// Stores the new value into the side metadata for the gien address if the current value is the same as the old value.
702    /// This method has similar semantics to `compare_exchange` in Rust atomics.
703    /// The return value is a result indicating whether the new value was written and containing the previous value.
704    /// On success this value is guaranteed to be equal to current.
705    pub fn compare_exchange_atomic<T: MetadataValue>(
706        &self,
707        data_addr: Address,
708        old_metadata: T,
709        new_metadata: T,
710        success_order: Ordering,
711        failure_order: Ordering,
712    ) -> std::result::Result<T, T> {
713        self.side_metadata_access::<true, T, _, _, _>(
714            data_addr,
715            Some(new_metadata),
716            || {
717                let meta_addr = address_to_meta_address(self, data_addr);
718                let bits_num_log = self.log_num_of_bits;
719                if bits_num_log < 3 {
720                    let lshift = meta_byte_lshift(self, data_addr);
721                    let mask = meta_byte_mask(self) << lshift;
722
723                    let real_old_byte = unsafe { meta_addr.atomic_load::<AtomicU8>(success_order) };
724                    let expected_old_byte =
725                        (real_old_byte & !mask) | ((old_metadata.to_u8().unwrap()) << lshift);
726                    let expected_new_byte =
727                        (expected_old_byte & !mask) | ((new_metadata.to_u8().unwrap()) << lshift);
728
729                    unsafe {
730                        meta_addr.compare_exchange::<AtomicU8>(
731                            expected_old_byte,
732                            expected_new_byte,
733                            success_order,
734                            failure_order,
735                        )
736                    }
737                    .map(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
738                    .map_err(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
739                } else {
740                    unsafe {
741                        T::compare_exchange(
742                            meta_addr,
743                            old_metadata,
744                            new_metadata,
745                            success_order,
746                            failure_order,
747                        )
748                    }
749                }
750            },
751            |_res| {
752                #[cfg(feature = "extreme_assertions")]
753                if _res.is_ok() {
754                    sanity::verify_store(self, data_addr, new_metadata);
755                }
756            },
757        )
758    }
759
760    /// This is used to implement fetch_add/sub for bits.
761    /// For fetch_and/or, we don't necessarily need this method. We could directly do fetch_and/or on the u8.
762    fn fetch_ops_on_bits<F: Fn(u8) -> u8>(
763        &self,
764        data_addr: Address,
765        meta_addr: Address,
766        set_order: Ordering,
767        fetch_order: Ordering,
768        update: F,
769    ) -> u8 {
770        let lshift = meta_byte_lshift(self, data_addr);
771        let mask = meta_byte_mask(self) << lshift;
772
773        let old_raw_byte = unsafe {
774            <u8 as MetadataValue>::fetch_update(
775                meta_addr,
776                set_order,
777                fetch_order,
778                |raw_byte: u8| {
779                    let old_val = (raw_byte & mask) >> lshift;
780                    let new_val = update(old_val);
781                    let new_raw_byte = (raw_byte & !mask) | ((new_val << lshift) & mask);
782                    Some(new_raw_byte)
783                },
784            )
785        }
786        .unwrap();
787        (old_raw_byte & mask) >> lshift
788    }
789
790    /// Adds the value to the current value for this side metadata for the given address.
791    /// This method has similar semantics to `fetch_add` in Rust atomics.
792    /// Returns the previous value.
793    pub fn fetch_add_atomic<T: MetadataValue>(
794        &self,
795        data_addr: Address,
796        val: T,
797        order: Ordering,
798    ) -> T {
799        self.side_metadata_access::<true, T, _, _, _>(
800            data_addr,
801            Some(val),
802            || {
803                let meta_addr = address_to_meta_address(self, data_addr);
804                let bits_num_log = self.log_num_of_bits;
805                if bits_num_log < 3 {
806                    FromPrimitive::from_u8(self.fetch_ops_on_bits(
807                        data_addr,
808                        meta_addr,
809                        order,
810                        order,
811                        |x: u8| x.wrapping_add(val.to_u8().unwrap()),
812                    ))
813                    .unwrap()
814                } else {
815                    unsafe { T::fetch_add(meta_addr, val, order) }
816                }
817            },
818            |_old_val| {
819                #[cfg(feature = "extreme_assertions")]
820                sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.wrapping_add(&val))
821            },
822        )
823    }
824
825    /// Subtracts the value from the current value for this side metadata for the given address.
826    /// This method has similar semantics to `fetch_sub` in Rust atomics.
827    /// Returns the previous value.
828    pub fn fetch_sub_atomic<T: MetadataValue>(
829        &self,
830        data_addr: Address,
831        val: T,
832        order: Ordering,
833    ) -> T {
834        self.side_metadata_access::<true, T, _, _, _>(
835            data_addr,
836            Some(val),
837            || {
838                let meta_addr = address_to_meta_address(self, data_addr);
839                if self.log_num_of_bits < 3 {
840                    FromPrimitive::from_u8(self.fetch_ops_on_bits(
841                        data_addr,
842                        meta_addr,
843                        order,
844                        order,
845                        |x: u8| x.wrapping_sub(val.to_u8().unwrap()),
846                    ))
847                    .unwrap()
848                } else {
849                    unsafe { T::fetch_sub(meta_addr, val, order) }
850                }
851            },
852            |_old_val| {
853                #[cfg(feature = "extreme_assertions")]
854                sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.wrapping_sub(&val))
855            },
856        )
857    }
858
859    /// Bitwise 'and' the value with the current value for this side metadata for the given address.
860    /// This method has similar semantics to `fetch_and` in Rust atomics.
861    /// Returns the previous value.
862    pub fn fetch_and_atomic<T: MetadataValue>(
863        &self,
864        data_addr: Address,
865        val: T,
866        order: Ordering,
867    ) -> T {
868        self.side_metadata_access::<true, T, _, _, _>(
869            data_addr,
870            Some(val),
871            || {
872                let meta_addr = address_to_meta_address(self, data_addr);
873                if self.log_num_of_bits < 3 {
874                    let lshift = meta_byte_lshift(self, data_addr);
875                    let mask = meta_byte_mask(self) << lshift;
876                    // We do not need to use fetch_ops_on_bits(), we can just set irrelavent bits to 1, and do fetch_and
877                    let rhs = (val.to_u8().unwrap() << lshift) | !mask;
878                    let old_raw_byte =
879                        unsafe { <u8 as MetadataValue>::fetch_and(meta_addr, rhs, order) };
880                    let old_val = (old_raw_byte & mask) >> lshift;
881                    FromPrimitive::from_u8(old_val).unwrap()
882                } else {
883                    unsafe { T::fetch_and(meta_addr, val, order) }
884                }
885            },
886            |_old_val| {
887                #[cfg(feature = "extreme_assertions")]
888                sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.bitand(val))
889            },
890        )
891    }
892
893    /// Bitwise 'or' the value with the current value for this side metadata for the given address.
894    /// This method has similar semantics to `fetch_or` in Rust atomics.
895    /// Returns the previous value.
896    pub fn fetch_or_atomic<T: MetadataValue>(
897        &self,
898        data_addr: Address,
899        val: T,
900        order: Ordering,
901    ) -> T {
902        self.side_metadata_access::<true, T, _, _, _>(
903            data_addr,
904            Some(val),
905            || {
906                let meta_addr = address_to_meta_address(self, data_addr);
907                if self.log_num_of_bits < 3 {
908                    let lshift = meta_byte_lshift(self, data_addr);
909                    let mask = meta_byte_mask(self) << lshift;
910                    // We do not need to use fetch_ops_on_bits(), we can just set irrelavent bits to 0, and do fetch_or
911                    let rhs = (val.to_u8().unwrap() << lshift) & mask;
912                    let old_raw_byte =
913                        unsafe { <u8 as MetadataValue>::fetch_or(meta_addr, rhs, order) };
914                    let old_val = (old_raw_byte & mask) >> lshift;
915                    FromPrimitive::from_u8(old_val).unwrap()
916                } else {
917                    unsafe { T::fetch_or(meta_addr, val, order) }
918                }
919            },
920            |_old_val| {
921                #[cfg(feature = "extreme_assertions")]
922                sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.bitor(val))
923            },
924        )
925    }
926
927    /// Fetches the value for this side metadata for the given address, and applies a function to it that returns an optional new value.
928    /// This method has similar semantics to `fetch_update` in Rust atomics.
929    /// Returns a Result of Ok(previous_value) if the function returned Some(_), else Err(previous_value).
930    pub fn fetch_update_atomic<T: MetadataValue, F: FnMut(T) -> Option<T> + Copy>(
931        &self,
932        data_addr: Address,
933        set_order: Ordering,
934        fetch_order: Ordering,
935        mut f: F,
936    ) -> std::result::Result<T, T> {
937        self.side_metadata_access::<true, T, _, _, _>(
938            data_addr,
939            None,
940            move || -> std::result::Result<T, T> {
941                let meta_addr = address_to_meta_address(self, data_addr);
942                if self.log_num_of_bits < 3 {
943                    let lshift = meta_byte_lshift(self, data_addr);
944                    let mask = meta_byte_mask(self) << lshift;
945
946                    unsafe {
947                        <u8 as MetadataValue>::fetch_update(
948                            meta_addr,
949                            set_order,
950                            fetch_order,
951                            |raw_byte: u8| {
952                                let old_val = (raw_byte & mask) >> lshift;
953                                f(FromPrimitive::from_u8(old_val).unwrap()).map(|new_val| {
954                                    (raw_byte & !mask)
955                                        | ((new_val.to_u8().unwrap() << lshift) & mask)
956                                })
957                            },
958                        )
959                    }
960                    .map(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
961                    .map_err(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
962                } else {
963                    unsafe { T::fetch_update(meta_addr, set_order, fetch_order, f) }
964                }
965            },
966            |_result| {
967                #[cfg(feature = "extreme_assertions")]
968                if let Ok(old_val) = _result {
969                    sanity::verify_update::<T>(self, data_addr, old_val, f(old_val).unwrap())
970                }
971            },
972        )
973    }
974
975    /// 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),
976    /// and iterates backwards for the given bytes (non inclusive) before the data address.
977    ///
978    /// The data_addr and the corresponding side metadata address may not be mapped. Thus when this function checks the given data address, and
979    /// when it searches back, it needs to check if the address is mapped or not to avoid loading from an unmapped address.
980    ///
981    /// This function returns an address that is aligned to the region of this side metadata (`log_bytes_per_region`), and the side metadata
982    /// for the address is non zero.
983    ///
984    /// # Safety
985    ///
986    /// This function uses non-atomic load for the side metadata. The user needs to make sure
987    /// that there is no other thread that is mutating the side metadata.
988    #[allow(clippy::let_and_return)]
989    pub unsafe fn find_prev_non_zero_value<T: MetadataValue>(
990        &self,
991        data_addr: Address,
992        search_limit_bytes: usize,
993    ) -> Option<Address> {
994        debug_assert!(search_limit_bytes > 0);
995
996        if self.uses_contiguous_side_metadata() {
997            // Contiguous side metadata
998            let result = self.find_prev_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
999            #[cfg(debug_assertions)]
1000            {
1001                // Double check if the implementation is correct
1002                let result2 =
1003                    self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1004                assert_eq!(result, result2, "find_prev_non_zero_value_fast returned a diffrent result from the naive implementation.");
1005            }
1006            result
1007        } else {
1008            // TODO: We should be able to optimize further for this case. However, we need to be careful that the side metadata
1009            // is not contiguous, and we need to skip to the next chunk's side metadata when we search to a different chunk.
1010            // 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.
1011            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.");
1012            self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1013        }
1014    }
1015
1016    fn find_prev_non_zero_value_simple<T: MetadataValue>(
1017        &self,
1018        data_addr: Address,
1019        search_limit_bytes: usize,
1020    ) -> Option<Address> {
1021        let region_bytes = 1 << self.log_bytes_in_region;
1022        // Figure out the range that we need to search.
1023        let start_addr = data_addr.align_down(region_bytes);
1024        let end_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1025
1026        let mmap_granularity = MMAPPER.granularity();
1027        let mut mapped_grain = Address::MAX;
1028
1029        let mut cursor = start_addr;
1030        while cursor >= end_addr {
1031            // We can cache the "is the cursor mapped?" check because MMTk maps metadata at
1032            // chunk-level
1033            if cursor < mapped_grain {
1034                if cursor.is_mapped() {
1035                    mapped_grain = cursor.align_down(mmap_granularity);
1036                } else {
1037                    // We encounter an unmapped address. Just return None.
1038                    return None;
1039                }
1040            }
1041            // If we find non-zero value, just return it.
1042            if !unsafe { self.load::<T>(cursor).is_zero() } {
1043                return Some(cursor);
1044            }
1045            cursor -= region_bytes;
1046        }
1047        None
1048    }
1049
1050    #[allow(clippy::let_and_return)]
1051    fn find_prev_non_zero_value_fast<T: MetadataValue>(
1052        &self,
1053        data_addr: Address,
1054        search_limit_bytes: usize,
1055    ) -> Option<Address> {
1056        debug_assert!(self.uses_contiguous_side_metadata());
1057
1058        // Quick check if the data address is mapped at all.
1059        if !data_addr.is_mapped() {
1060            return None;
1061        }
1062        // Quick check if the current data_addr has a non zero value.
1063        if !unsafe { self.load::<T>(data_addr).is_zero() } {
1064            return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1065        }
1066
1067        // Figure out the start and end data address.
1068        let start_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1069        let end_addr = data_addr;
1070
1071        // Then figure out the start and end metadata address and bits.
1072        // The start bit may not be accurate, as we map any address in the region to the same bit.
1073        // We will filter the result at the end to make sure the found address is in the search range.
1074        let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1075        let start_meta_shift = meta_byte_lshift(self, start_addr);
1076        let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1077        let end_meta_shift = meta_byte_lshift(self, end_addr);
1078
1079        let mut res = None;
1080
1081        let mut visitor = |range: BitByteRange| {
1082            match range {
1083                BitByteRange::Bytes { start, end } => {
1084                    match helpers::find_last_non_zero_bit_in_metadata_bytes(start, end) {
1085                        helpers::FindMetaBitResult::Found { addr, bit } => {
1086                            let (addr, bit) = align_metadata_address(self, addr, bit);
1087                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1088                            // Return true to abort the search. We found the bit.
1089                            true
1090                        }
1091                        // If we see unmapped metadata, we don't need to search any more.
1092                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1093                        // Return false to continue searching.
1094                        helpers::FindMetaBitResult::NotFound => false,
1095                    }
1096                }
1097                BitByteRange::BitsInByte {
1098                    addr,
1099                    bit_start,
1100                    bit_end,
1101                } => {
1102                    match helpers::find_last_non_zero_bit_in_metadata_bits(addr, bit_start, bit_end)
1103                    {
1104                        helpers::FindMetaBitResult::Found { addr, bit } => {
1105                            let (addr, bit) = align_metadata_address(self, addr, bit);
1106                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1107                            // Return true to abort the search. We found the bit.
1108                            true
1109                        }
1110                        // If we see unmapped metadata, we don't need to search any more.
1111                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1112                        // Return false to continue searching.
1113                        helpers::FindMetaBitResult::NotFound => false,
1114                    }
1115                }
1116            }
1117        };
1118
1119        ranges::break_bit_range(
1120            start_meta_addr,
1121            start_meta_shift,
1122            end_meta_addr,
1123            end_meta_shift,
1124            false,
1125            &mut visitor,
1126        );
1127
1128        // We have to filter the result. We search between [start_addr, end_addr). But we actually
1129        // search with metadata bits. It is possible the metadata bit for start_addr is the same bit
1130        // as an address that is before start_addr. E.g. 0x2010f026360 and 0x2010f026361 are mapped
1131        // to the same bit, 0x2010f026361 is the start address and 0x2010f026360 is outside the search range.
1132        res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1133            .filter(|addr| *addr >= start_addr && *addr < end_addr)
1134    }
1135
1136    /// Search forwards for a data address that has a non zero value in the side metadata. The search starts from the given data
1137    /// address (including this address), and iterates forwards for the given bytes (non inclusive) before the data address.
1138    ///
1139    /// The data_addr and the corresponding side metadata address may not be mapped. Thus when this function checks the given data address, and
1140    /// when it searches back, it needs to check if the address is mapped or not to avoid loading from an unmapped address.
1141    ///
1142    /// This function returns an address that is aligned to the region of this side metadata (`log_bytes_per_region`), and the side metadata
1143    /// for the address is non zero.
1144    ///
1145    /// # Safety
1146    ///
1147    /// This function uses non-atomic load for the side metadata. The user needs to make sure
1148    /// that there is no other thread that is mutating the side metadata.
1149    #[allow(clippy::let_and_return)]
1150    pub unsafe fn find_next_non_zero_value<T: MetadataValue>(
1151        &self,
1152        data_addr: Address,
1153        search_limit_bytes: usize,
1154    ) -> Option<Address> {
1155        debug_assert!(search_limit_bytes > 0);
1156
1157        if self.uses_contiguous_side_metadata() {
1158            // Contiguous side metadata
1159            let result = self.find_next_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1160            #[cfg(debug_assertions)]
1161            {
1162                // Double check if the implementation is correct
1163                let result2 =
1164                    self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1165                assert_eq!(
1166                    result,
1167                    result2,
1168                    "find_next_non_zero_value_fast returned a different result from the naive implementation. data_addr {}, search_limit_bytes {}",
1169                    data_addr, search_limit_bytes,
1170                );
1171            }
1172            result
1173        } else {
1174            // TODO: We should be able to optimize further for this case. However, we need to be careful that the side metadata
1175            // is not contiguous, and we need to skip to the next chunk's side metadata when we search to a different chunk.
1176            // 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.
1177            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.");
1178            self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1179        }
1180    }
1181
1182    fn find_next_non_zero_value_simple<T: MetadataValue>(
1183        &self,
1184        data_addr: Address,
1185        search_limit_bytes: usize,
1186    ) -> Option<Address> {
1187        let region_bytes = 1 << self.log_bytes_in_region;
1188        // Figure out the range that we need to search.
1189        let start_addr = data_addr.align_down(region_bytes);
1190        let end_addr = data_addr + search_limit_bytes;
1191
1192        let mmap_granularity = MMAPPER.granularity();
1193        let mut mapped_grain = Address::ZERO;
1194
1195        let mut cursor = start_addr;
1196        while cursor < end_addr {
1197            // We can cache the "is the cursor mapped?" check because MMTk maps metadata at
1198            // chunk-level
1199            if cursor > mapped_grain {
1200                if cursor.is_mapped() {
1201                    mapped_grain = cursor.align_up(mmap_granularity) - 0x1;
1202                } else {
1203                    // We encounter an unmapped address. Just return None.
1204                    return None;
1205                }
1206            }
1207            // If we find non-zero value, just return it.
1208            if !unsafe { self.load::<T>(cursor).is_zero() } {
1209                return Some(cursor);
1210            }
1211            cursor += region_bytes;
1212        }
1213        None
1214    }
1215
1216    fn find_next_non_zero_value_fast<T: MetadataValue>(
1217        &self,
1218        data_addr: Address,
1219        search_limit_bytes: usize,
1220    ) -> Option<Address> {
1221        debug_assert!(self.uses_contiguous_side_metadata());
1222
1223        // Quick check if the data address is mapped at all.
1224        if !data_addr.is_mapped() {
1225            return None;
1226        }
1227        // Quick check if the current data_addr has a non zero value.
1228        if !unsafe { self.load::<T>(data_addr).is_zero() } {
1229            return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1230        }
1231
1232        // Figure out the start and end data address.
1233        let start_addr = data_addr.align_down(1 << self.log_bytes_in_region);
1234        // We need to align the end_address up because the metadata might be stored right at
1235        // the end address otherwise. Our loop in `find_first_non_zero_bit_in_metadata_byte`
1236        // will not load from this end address, resulting in us potentially not finding the
1237        // correct address for the next set bit.
1238        let end_addr = (data_addr + search_limit_bytes).align_up(1 << self.log_bytes_in_region);
1239
1240        // Then figure out the start and end metadata address and bits.
1241        // The start bit may not be accurate, as we map any address in the region to the same bit.
1242        // We will filter the result at the end to make sure the found address is in the search range.
1243        let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1244        let start_meta_shift = meta_byte_lshift(self, start_addr);
1245        let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1246        let end_meta_shift = meta_byte_lshift(self, end_addr);
1247
1248        let mut res = None;
1249
1250        let mut visitor = |range: BitByteRange| {
1251            match range {
1252                BitByteRange::Bytes { start, end } => {
1253                    match helpers::find_first_non_zero_bit_in_metadata_bytes(start, end) {
1254                        helpers::FindMetaBitResult::Found { addr, bit } => {
1255                            let (addr, bit) = align_metadata_address(self, addr, bit);
1256                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1257                            // Return true to abort the search. We found the bit.
1258                            true
1259                        }
1260                        // If we see unmapped metadata, we don't need to search any more.
1261                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1262                        // Return false to continue searching.
1263                        helpers::FindMetaBitResult::NotFound => false,
1264                    }
1265                }
1266                BitByteRange::BitsInByte {
1267                    addr,
1268                    bit_start,
1269                    bit_end,
1270                } => {
1271                    match helpers::find_first_non_zero_bit_in_metadata_bits(
1272                        addr, bit_start, bit_end,
1273                    ) {
1274                        helpers::FindMetaBitResult::Found { addr, bit } => {
1275                            let (addr, bit) = align_metadata_address(self, addr, bit);
1276                            res = Some(contiguous_meta_address_to_address(self, addr, bit));
1277                            // Return true to abort the search. We found the bit.
1278                            true
1279                        }
1280                        // If we see unmapped metadata, we don't need to search any more.
1281                        helpers::FindMetaBitResult::UnmappedMetadata => true,
1282                        // Return false to continue searching.
1283                        helpers::FindMetaBitResult::NotFound => false,
1284                    }
1285                }
1286            }
1287        };
1288
1289        ranges::break_bit_range(
1290            start_meta_addr,
1291            start_meta_shift,
1292            end_meta_addr,
1293            end_meta_shift,
1294            true,
1295            &mut visitor,
1296        );
1297
1298        // We have to filter the result. We search between [start_addr, end_addr). But we actually
1299        // search with metadata bits. It is possible the metadata bit for start_addr is the same bit
1300        // as an address that is before start_addr. E.g. 0x2010f026360 and 0x2010f026361 are mapped
1301        // to the same bit, 0x2010f026361 is the start address and 0x2010f026360 is outside the search range.
1302        res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1303            .filter(|addr| *addr >= start_addr && *addr < end_addr)
1304    }
1305
1306    /// Search for data addresses that have non zero values in the side metadata.  This method is
1307    /// primarily used for heap traversal by scanning the VO bits.
1308    ///
1309    /// This function searches the side metadata for the data address range from `data_start_addr`
1310    /// (inclusive) to `data_end_addr` (exclusive).  The data address range must be fully mapped.
1311    ///
1312    /// For each data region that has non-zero side metadata, `visit_data` is called with the lowest
1313    /// address of that region.  Note that it may not be the original address used to set the
1314    /// metadata bits.
1315    pub fn scan_non_zero_values<T: MetadataValue>(
1316        &self,
1317        data_start_addr: Address,
1318        data_end_addr: Address,
1319        visit_data: &mut impl FnMut(Address),
1320    ) {
1321        if self.uses_contiguous_side_metadata() && self.log_num_of_bits == 0 {
1322            // Contiguous one-bit-per-region side metadata
1323            // TODO: VO bits is one-bit-per-word.  But if we want to scan other metadata (such as
1324            // the forwarding bits which has two bits per word), we will need to refactor the
1325            // algorithm of `scan_non_zero_values_fast`.
1326            self.scan_non_zero_values_fast(data_start_addr, data_end_addr, visit_data);
1327        } else {
1328            // TODO: VO bits are always contiguous.  But if we want to scan other metadata, such as
1329            // side mark bits, we need to refactor `bulk_update_metadata` to support `FnMut`, too,
1330            // and use it to apply `scan_non_zero_values_fast` on each contiguous side metadata
1331            // range.
1332            warn!(
1333                "We are trying to search for non zero bits in a discontiguous side metadata \
1334            or the metadata has more than one bit per region. \
1335                The performance is slow, as MMTk does not optimize for this case."
1336            );
1337            self.scan_non_zero_values_simple::<T>(data_start_addr, data_end_addr, visit_data);
1338        }
1339    }
1340
1341    fn scan_non_zero_values_simple<T: MetadataValue>(
1342        &self,
1343        data_start_addr: Address,
1344        data_end_addr: Address,
1345        visit_data: &mut impl FnMut(Address),
1346    ) {
1347        let region_bytes = 1usize << self.log_bytes_in_region;
1348
1349        let mut cursor = data_start_addr;
1350        while cursor < data_end_addr {
1351            debug_assert!(cursor.is_mapped());
1352
1353            // If we find non-zero value, just call back.
1354            if !unsafe { self.load::<T>(cursor).is_zero() } {
1355                visit_data(cursor);
1356            }
1357            cursor += region_bytes;
1358        }
1359    }
1360
1361    fn scan_non_zero_values_fast(
1362        &self,
1363        data_start_addr: Address,
1364        data_end_addr: Address,
1365        visit_data: &mut impl FnMut(Address),
1366    ) {
1367        debug_assert!(self.uses_contiguous_side_metadata());
1368        debug_assert_eq!(self.log_num_of_bits, 0);
1369
1370        // Then figure out the start and end metadata address and bits.
1371        let start_meta_addr = address_to_contiguous_meta_address(self, data_start_addr);
1372        let start_meta_shift = meta_byte_lshift(self, data_start_addr);
1373        let end_meta_addr = address_to_contiguous_meta_address(self, data_end_addr);
1374        let end_meta_shift = meta_byte_lshift(self, data_end_addr);
1375
1376        let mut visitor = |range| {
1377            match range {
1378                BitByteRange::Bytes { start, end } => {
1379                    helpers::scan_non_zero_bits_in_metadata_bytes(start, end, &mut |addr, bit| {
1380                        visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1381                    });
1382                }
1383                BitByteRange::BitsInByte {
1384                    addr,
1385                    bit_start,
1386                    bit_end,
1387                } => helpers::scan_non_zero_bits_in_metadata_bits(
1388                    addr,
1389                    bit_start,
1390                    bit_end,
1391                    &mut |addr, bit| {
1392                        visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1393                    },
1394                ),
1395            }
1396            false
1397        };
1398
1399        ranges::break_bit_range(
1400            start_meta_addr,
1401            start_meta_shift,
1402            end_meta_addr,
1403            end_meta_shift,
1404            true,
1405            &mut visitor,
1406        );
1407    }
1408}
1409
1410impl fmt::Debug for SideMetadataSpec {
1411    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1412        f.write_fmt(format_args!(
1413            "SideMetadataSpec {} {{ \
1414            **is_global: {:?} \
1415            **offset: 0x{:x} \
1416            **log_num_of_bits: 0x{:x} \
1417            **log_bytes_in_region: 0x{:x} \
1418            }}",
1419            self.name, self.is_global, self.offset, self.log_num_of_bits, self.log_bytes_in_region
1420        ))
1421    }
1422}
1423
1424/// Calculate the offset of the next side metadata spec after the given spec.
1425/// This is used to calculate the offset field in [`crate::util::metadata::side_metadata::SideMetadataSpec`].
1426pub const fn side_metadata_offset_after(spec: &SideMetadataSpec) -> usize {
1427    // Some metadata may be so small that its size is not a multiple of byte size. One example
1428    // is `CHUNK_MARK`. It is one byte per chunk. However, on 32-bit architectures, we allocate
1429    // side metadata per chunk. In that case, it will only occupy one byte. If we do not align
1430    // the upper bound offset up, subsequent local metadata that need to be accessed at, for
1431    // example, word granularity will be misaligned.
1432    // TODO: Currently we align metadata to word size so that it is safe to access the metadata
1433    // one word at a time. In the future, we may allow each metadata to specify its own alignment
1434    // requirement.
1435    raw_align_up(spec.upper_bound_offset(), BYTES_IN_WORD)
1436}
1437
1438/// This struct stores all the side metadata specs for a policy. Generally a policy needs to know its own
1439/// side metadata spec as well as the plan's specs.
1440pub(crate) struct SideMetadataContext {
1441    // For plans
1442    pub global: Vec<SideMetadataSpec>,
1443    // For policies
1444    pub local: Vec<SideMetadataSpec>,
1445}
1446
1447impl SideMetadataContext {
1448    #[allow(clippy::vec_init_then_push)] // allow this, as we conditionally push based on features.
1449    pub fn new_global_specs(specs: &[SideMetadataSpec]) -> Vec<SideMetadataSpec> {
1450        let mut ret = vec![];
1451
1452        #[cfg(feature = "vo_bit")]
1453        ret.push(VO_BIT_SIDE_METADATA_SPEC);
1454
1455        if let Some(spec) = crate::mmtk::SFT_MAP.get_side_metadata() {
1456            if spec.is_global {
1457                ret.push(*spec);
1458            }
1459        }
1460
1461        // Any plan that uses the chunk map needs to reserve the chunk map table.
1462        // As we use either the mark sweep or (non moving) immix as the non moving space,
1463        // and both policies use the chunk map, we just add the chunk map table globally.
1464        ret.push(crate::util::heap::chunk_map::ChunkMap::ALLOC_TABLE);
1465
1466        ret.extend_from_slice(specs);
1467        ret
1468    }
1469
1470    pub fn get_local_specs(&self) -> &[SideMetadataSpec] {
1471        &self.local
1472    }
1473
1474    #[cfg(debug_assertions)]
1475    pub fn assert_metadata_ranges_in_reserved_range(
1476        &self,
1477        start: Address,
1478        size: usize,
1479        space_name: &str,
1480    ) {
1481        let reserved = {
1482            let base = super::layout::global_side_metadata_base_address();
1483            let bytes = super::layout::side_metadata_reserved_bytes();
1484            base..(base + bytes)
1485        };
1486        let check_spec = |spec: &SideMetadataSpec| {
1487            if !spec.uses_contiguous_side_metadata() {
1488                return;
1489            }
1490            let metadata_start = address_to_meta_address(spec, start);
1491            let mmap_start = metadata_start.align_down(BYTES_IN_PAGE);
1492            let metadata_size = data_to_meta_size_round_up(spec, size);
1493            let mmap_end = (metadata_start + metadata_size).align_up(BYTES_IN_PAGE);
1494            debug_assert!(
1495                mmap_start >= reserved.start && mmap_end <= reserved.end,
1496                "Side metadata range for spec {} in space {} is outside reserved range: [{}, {}) vs [{}, {})",
1497                spec.name,
1498                space_name,
1499                mmap_start,
1500                mmap_end,
1501                reserved.start,
1502                reserved.end
1503            );
1504        };
1505        self.global.iter().for_each(check_spec);
1506        self.local.iter().for_each(check_spec);
1507    }
1508
1509    /// Return the pages reserved for side metadata based on the data pages we used.
1510    // We used to use PageAccouting to count pages used in side metadata. However,
1511    // that means we always count pages while we may reserve less than a page each time.
1512    // This could lead to overcount. I think the easier way is to not account
1513    // when we allocate for sidemetadata, but to calculate the side metadata usage based on
1514    // how many data pages we use when reporting.
1515    pub fn calculate_reserved_pages(&self, data_pages: usize) -> usize {
1516        let mut total = 0;
1517        for spec in self.global.iter() {
1518            // This rounds up.  No matter how small `data_pages` is, the side metadata size will be
1519            // at least one page.  This behavior is *intended*.  This over-estimated amount is used
1520            // for triggering GC and resizing the heap.
1521            total += data_to_meta_size_round_up(spec, data_pages);
1522        }
1523        for spec in self.local.iter() {
1524            total += data_to_meta_size_round_up(spec, data_pages);
1525        }
1526        total
1527    }
1528
1529    // ** NOTE: **
1530    //  Regardless of the number of bits in a metadata unit, we always represent its content as a word.
1531
1532    /// Tries to map the required metadata space and returns `true` is successful.
1533    /// This can be called at page granularity.
1534    pub fn try_map_metadata_space(
1535        &self,
1536        start: Address,
1537        size: usize,
1538        space_name: &str,
1539    ) -> MmapResult<()> {
1540        debug!(
1541            "try_map_metadata_space({}, 0x{:x}, {}, {})",
1542            start,
1543            size,
1544            self.global.len(),
1545            self.local.len()
1546        );
1547        // Page aligned
1548        debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1549        debug_assert!(size % BYTES_IN_PAGE == 0);
1550        self.map_metadata_internal(start, size, false, space_name)
1551    }
1552
1553    /// Tries to map the required metadata address range, without reserving swap-space/physical memory for it.
1554    /// This will make sure the address range is exclusive to the caller. This should be called at chunk granularity.
1555    ///
1556    /// NOTE: Accessing addresses in this range will produce a segmentation fault if swap-space is not mapped using the `try_map_metadata_space` function.
1557    pub fn try_map_metadata_address_range(
1558        &self,
1559        start: Address,
1560        size: usize,
1561        name: &str,
1562    ) -> MmapResult<()> {
1563        debug!(
1564            "try_map_metadata_address_range({}, 0x{:x}, {}, {})",
1565            start,
1566            size,
1567            self.global.len(),
1568            self.local.len()
1569        );
1570        // Chunk aligned
1571        debug_assert!(start.is_aligned_to(BYTES_IN_CHUNK));
1572        debug_assert!(size % BYTES_IN_CHUNK == 0);
1573        self.map_metadata_internal(start, size, true, name)
1574    }
1575
1576    /// The internal function to mmap metadata
1577    ///
1578    /// # Arguments
1579    /// * `start` - The starting address of the source data.
1580    /// * `size` - The size of the source data (in bytes).
1581    /// * `no_reserve` - whether to invoke mmap with a noreserve flag (we use this flag to quarantine address range)
1582    /// * `space_name`: The name of the space, used for annotating the mmap.
1583    fn map_metadata_internal(
1584        &self,
1585        start: Address,
1586        size: usize,
1587        no_reserve: bool,
1588        space_name: &str,
1589    ) -> MmapResult<()> {
1590        for spec in self.global.iter() {
1591            let anno = MmapAnnotation::SideMeta {
1592                space: space_name,
1593                meta: spec.name,
1594            };
1595            try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1596        }
1597
1598        #[cfg(target_pointer_width = "32")]
1599        let mut lsize: usize = 0;
1600
1601        for spec in self.local.iter() {
1602            // For local side metadata, we always have to reserve address space for all local
1603            // metadata required by all policies in MMTk to be able to calculate a constant offset
1604            // for each local metadata at compile-time (it's like assigning an ID to each policy).
1605            //
1606            // As the plan is chosen at run-time, we will never know which subset of policies will
1607            // be used during run-time. We can't afford this much address space in 32-bits.
1608            // So, we switch to the chunk-based approach for this specific case.
1609            //
1610            // The global metadata is different in that for each plan, we can calculate its constant
1611            // base addresses at compile-time. Using the chunk-based approach will need the same
1612            // address space size as the current not-chunked approach.
1613            #[cfg(target_pointer_width = "64")]
1614            {
1615                let anno = MmapAnnotation::SideMeta {
1616                    space: space_name,
1617                    meta: spec.name,
1618                };
1619                try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1620            }
1621            #[cfg(target_pointer_width = "32")]
1622            {
1623                lsize += metadata_bytes_per_chunk(spec.log_bytes_in_region, spec.log_num_of_bits);
1624            }
1625        }
1626
1627        #[cfg(target_pointer_width = "32")]
1628        if lsize > 0 {
1629            let max = BYTES_IN_CHUNK >> super::layout::LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO;
1630            debug_assert!(
1631                lsize <= max,
1632                "local side metadata per chunk (0x{:x}) must be less than (0x{:x})",
1633                lsize,
1634                max
1635            );
1636            // We are creating a mmap for all side metadata instead of one specific metadata.  We
1637            // just annotate it as "all" here.
1638            let anno = MmapAnnotation::SideMeta {
1639                space: space_name,
1640                meta: "all",
1641            };
1642            try_map_per_chunk_metadata_space(start, size, lsize, no_reserve, &anno)?;
1643        }
1644
1645        Ok(())
1646    }
1647
1648    /// Unmap the corresponding metadata space or panic.
1649    ///
1650    /// Note-1: This function is only used for test and debug right now.
1651    ///
1652    /// Note-2: This function uses munmap() which works at page granularity.
1653    ///     If the corresponding metadata space's size is not a multiple of page size,
1654    ///     the actual unmapped space will be bigger than what you specify.
1655    #[cfg(test)]
1656    pub fn ensure_unmap_metadata_space(&self, start: Address, size: usize) {
1657        trace!("ensure_unmap_metadata_space({}, 0x{:x})", start, size);
1658        debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1659        debug_assert!(size % BYTES_IN_PAGE == 0);
1660
1661        for spec in self.global.iter() {
1662            ensure_munmap_contiguous_metadata_space(start, size, spec);
1663        }
1664
1665        for spec in self.local.iter() {
1666            #[cfg(target_pointer_width = "64")]
1667            {
1668                ensure_munmap_contiguous_metadata_space(start, size, spec);
1669            }
1670            #[cfg(target_pointer_width = "32")]
1671            {
1672                ensure_munmap_chunked_metadata_space(start, size, spec);
1673            }
1674        }
1675    }
1676}
1677
1678/// A byte array in side-metadata
1679pub struct MetadataByteArrayRef<const ENTRIES: usize> {
1680    #[cfg(feature = "extreme_assertions")]
1681    heap_range_start: Address,
1682    #[cfg(feature = "extreme_assertions")]
1683    spec: SideMetadataSpec,
1684    data: &'static [u8; ENTRIES],
1685}
1686
1687impl<const ENTRIES: usize> MetadataByteArrayRef<ENTRIES> {
1688    /// Get a piece of metadata address range as a byte array.
1689    ///
1690    /// # Arguments
1691    ///
1692    /// * `metadata_spec` - The specification of the target side metadata.
1693    /// * `start` - The starting address of the heap range.
1694    /// * `bytes` - The size of the heap range.
1695    ///
1696    pub fn new(metadata_spec: &SideMetadataSpec, start: Address, bytes: usize) -> Self {
1697        debug_assert_eq!(
1698            metadata_spec.log_num_of_bits, LOG_BITS_IN_BYTE as usize,
1699            "Each heap entry should map to a byte in side-metadata"
1700        );
1701        debug_assert_eq!(
1702            bytes >> metadata_spec.log_bytes_in_region,
1703            ENTRIES,
1704            "Heap range size and MetadataByteArray size does not match"
1705        );
1706        Self {
1707            #[cfg(feature = "extreme_assertions")]
1708            heap_range_start: start,
1709            #[cfg(feature = "extreme_assertions")]
1710            spec: *metadata_spec,
1711            // # Safety
1712            // The metadata memory is assumed to be mapped when accessing.
1713            data: unsafe { &*address_to_meta_address(metadata_spec, start).to_ptr() },
1714        }
1715    }
1716
1717    /// Get the length of the array.
1718    #[allow(clippy::len_without_is_empty)]
1719    pub const fn len(&self) -> usize {
1720        ENTRIES
1721    }
1722
1723    /// Get a byte from the metadata byte array at the given index.
1724    #[allow(clippy::let_and_return)]
1725    pub fn get(&self, index: usize) -> u8 {
1726        #[cfg(feature = "extreme_assertions")]
1727        let _lock = sanity::SANITY_LOCK.lock().unwrap();
1728        let value = self.data[index];
1729        #[cfg(feature = "extreme_assertions")]
1730        {
1731            let data_addr = self.heap_range_start + (index << self.spec.log_bytes_in_region);
1732            sanity::verify_load::<u8>(&self.spec, data_addr, value);
1733        }
1734        value
1735    }
1736}
1737
1738#[cfg(test)]
1739mod tests {
1740    use super::*;
1741    use crate::mmap_anno_test;
1742    use crate::util::metadata::side_metadata::SideMetadataContext;
1743
1744    // offset is not used in these tests.
1745    pub const ZERO_OFFSET: usize = 0;
1746
1747    #[test]
1748    fn calculate_reserved_pages_one_spec() {
1749        // 1 bit per 8 bytes - 1:64
1750        let spec = SideMetadataSpec {
1751            name: "test_spec",
1752            is_global: true,
1753            offset: ZERO_OFFSET,
1754            log_num_of_bits: 0,
1755            log_bytes_in_region: 3,
1756        };
1757        let side_metadata = SideMetadataContext {
1758            global: vec![spec],
1759            local: vec![],
1760        };
1761        assert_eq!(side_metadata.calculate_reserved_pages(0), 0);
1762        assert_eq!(side_metadata.calculate_reserved_pages(63), 1);
1763        assert_eq!(side_metadata.calculate_reserved_pages(64), 1);
1764        assert_eq!(side_metadata.calculate_reserved_pages(65), 2);
1765        assert_eq!(side_metadata.calculate_reserved_pages(1024), 16);
1766    }
1767
1768    #[test]
1769    fn calculate_reserved_pages_multi_specs() {
1770        // 1 bit per 8 bytes - 1:64
1771        let gspec = SideMetadataSpec {
1772            name: "gspec",
1773            is_global: true,
1774            offset: ZERO_OFFSET,
1775            log_num_of_bits: 0,
1776            log_bytes_in_region: 3,
1777        };
1778        // 2 bits per page - 2 / (4k * 8) = 1:16k
1779        let lspec = SideMetadataSpec {
1780            name: "lspec",
1781            is_global: false,
1782            offset: ZERO_OFFSET,
1783            log_num_of_bits: 1,
1784            log_bytes_in_region: 12,
1785        };
1786        let side_metadata = SideMetadataContext {
1787            global: vec![gspec],
1788            local: vec![lspec],
1789        };
1790        assert_eq!(side_metadata.calculate_reserved_pages(1024), 16 + 1);
1791    }
1792
1793    use crate::util::heap::layout::vm_layout;
1794    use crate::util::test_util::{serial_test, with_cleanup};
1795    use paste::paste;
1796
1797    const TEST_LOG_BYTES_IN_REGION: usize = 12;
1798
1799    fn test_side_metadata(
1800        log_bits: usize,
1801        f: impl Fn(&SideMetadataSpec, Address, Address) + std::panic::RefUnwindSafe,
1802    ) {
1803        serial_test(|| {
1804            core_test_initialize_side_metadata();
1805
1806            let spec = SideMetadataSpec {
1807                name: "Test Spec $tname",
1808                is_global: true,
1809                offset: 0,
1810                log_num_of_bits: log_bits,
1811                log_bytes_in_region: TEST_LOG_BYTES_IN_REGION, // page size
1812            };
1813            let context = SideMetadataContext {
1814                global: vec![spec],
1815                local: vec![],
1816            };
1817            let mut sanity = SideMetadataSanity::new();
1818            sanity.verify_metadata_context("TestPolicy", &context);
1819
1820            let data_addr = vm_layout::vm_layout().heap_start;
1821            // Make sure the address is mapped.
1822            crate::MMAPPER
1823                .ensure_mapped(
1824                    data_addr,
1825                    1,
1826                    HugePageSupport::No,
1827                    MmapProtection::ReadWrite,
1828                    mmap_anno_test!(),
1829                )
1830                .unwrap();
1831            let meta_addr = address_to_meta_address(&spec, data_addr);
1832            with_cleanup(
1833                || {
1834                    let mmap_result =
1835                        context.try_map_metadata_space(data_addr, BYTES_IN_PAGE, "test_space");
1836                    assert!(mmap_result.is_ok(), "{:?}", mmap_result);
1837
1838                    f(&spec, data_addr, meta_addr);
1839                },
1840                || {
1841                    // Clear the metadata -- use u64 (max length we support)
1842                    assert!(log_bits <= 6);
1843                    let meta_ptr: *mut u64 = meta_addr.to_mut_ptr();
1844                    unsafe { *meta_ptr = 0 };
1845
1846                    sanity::reset();
1847                },
1848            )
1849        })
1850    }
1851
1852    fn max_value(log_bits: usize) -> u64 {
1853        (0..(1 << log_bits)).fold(0, |accum, x| accum + (1 << x))
1854    }
1855    #[test]
1856    fn test_max_value() {
1857        assert_eq!(max_value(0), 1);
1858        assert_eq!(max_value(1), 0b11);
1859        assert_eq!(max_value(2), 0b1111);
1860        assert_eq!(max_value(3), 255);
1861        assert_eq!(max_value(4), 65535);
1862    }
1863
1864    macro_rules! test_side_metadata_access {
1865        ($tname: ident, $type: ty, $log_bits: expr) => {
1866            paste!{
1867                #[test]
1868                fn [<$tname _load>]() {
1869                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1870                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1871
1872                        // Initial value should be 0
1873                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1874                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
1875
1876                        // Set to max
1877                        let max_value: $type = max_value($log_bits) as _;
1878                        unsafe { spec.store::<$type>(data_addr, max_value); }
1879                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, max_value);
1880                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
1881                        assert_eq!(unsafe { *meta_ptr }, max_value);
1882                    });
1883                }
1884
1885                #[test]
1886                fn [<$tname _store>]() {
1887                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1888                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1889                        let max_value: $type = max_value($log_bits) as _;
1890
1891                        // Set the metadata byte(s) to all 1s
1892                        unsafe { *meta_ptr = <$type>::MAX; }
1893                        // Store 0 to the side metadata
1894                        unsafe { spec.store::<$type>(data_addr, 0); }
1895                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1896                        // Only the affected bits are set to 0
1897                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1898                    });
1899                }
1900
1901                #[test]
1902                fn [<$tname _atomic_store>]() {
1903                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1904                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1905                        let max_value: $type = max_value($log_bits) as _;
1906
1907                        // Set the metadata byte(s) to all 1s
1908                        unsafe { *meta_ptr = <$type>::MAX; }
1909                        // Store 0 to the side metadata
1910                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1911                        assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1912                        // Only the affected bits are set to 0
1913                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1914                    });
1915                }
1916
1917                #[test]
1918                fn [<$tname _compare_exchange_success>]() {
1919                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1920                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1921                        let max_value: $type = max_value($log_bits) as _;
1922                        // Set the metadata byte(s) to all 1s
1923                        unsafe { *meta_ptr = <$type>::MAX; }
1924                        // Store 1 to the side metadata
1925                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1926
1927                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1928                        assert_eq!(old_val, 1);
1929
1930                        let new_val = 0;
1931                        let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
1932                        assert!(res.is_ok());
1933                        assert_eq!(res.unwrap(), old_val, "old vals do not match");
1934
1935                        let after_update = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1936                        assert_eq!(after_update, new_val);
1937                        // Only the affected bits are set to 0
1938                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1939                    });
1940                }
1941
1942                #[test]
1943                fn [<$tname _compare_exchange_fail>]() {
1944                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1945                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1946                        // Set the metadata byte(s) to all 1s
1947                        unsafe { *meta_ptr = <$type>::MAX; }
1948                        // Store 1 to the side metadata
1949                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1950
1951                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1952                        assert_eq!(old_val, 1);
1953
1954                        // make old_val outdated
1955                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1956                        let bits_before_cas = unsafe { *meta_ptr };
1957
1958                        let new_val = 0;
1959                        let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
1960                        assert!(res.is_err());
1961                        assert_eq!(res.err().unwrap(), 0);
1962                        let bits_after_cas = unsafe { *meta_ptr };
1963                        assert_eq!(bits_before_cas, bits_after_cas);
1964                    });
1965                }
1966
1967                #[test]
1968                fn [<$tname _fetch_add_1>]() {
1969                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1970                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1971                        // Set the metadata byte(s) to all 1s
1972                        unsafe { *meta_ptr = <$type>::MAX; }
1973                        // Store 0 to the side metadata
1974                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1975
1976                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1977
1978                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1979                        assert_eq!(old_val_from_fetch, old_val);
1980
1981                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1982                        assert_eq!(new_val, 1);
1983                    });
1984                }
1985
1986                #[test]
1987                fn [<$tname _fetch_add_max>]() {
1988                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1989                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1990                        let max_value: $type = max_value($log_bits) as _;
1991                        // Set the metadata byte(s) to all 1s
1992                        unsafe { *meta_ptr = <$type>::MAX; }
1993                        // Store 0 to the side metadata
1994                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1995
1996                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1997
1998                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
1999                        assert_eq!(old_val_from_fetch, old_val);
2000
2001                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2002                        assert_eq!(new_val, max_value);
2003                    });
2004                }
2005
2006                #[test]
2007                fn [<$tname _fetch_add_overflow>]() {
2008                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2009                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2010                        let max_value: $type = max_value($log_bits) as _;
2011                        // Set the metadata byte(s) to all 1s
2012                        unsafe { *meta_ptr = <$type>::MAX; }
2013                        // Store max to the side metadata
2014                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2015
2016                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2017
2018                        // add 1 to max value will cause overflow and wrap around to 0
2019                        let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2020                        assert_eq!(old_val_from_fetch, old_val);
2021
2022                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2023                        assert_eq!(new_val, 0);
2024                    });
2025                }
2026
2027                #[test]
2028                fn [<$tname _fetch_sub_1>]() {
2029                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2030                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2031                        // Set the metadata byte(s) to all 1s
2032                        unsafe { *meta_ptr = <$type>::MAX; }
2033                        // Store 1 to the side metadata
2034                        spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2035
2036                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2037
2038                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2039                        assert_eq!(old_val_from_fetch, old_val);
2040
2041                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2042                        assert_eq!(new_val, 0);
2043                    });
2044                }
2045
2046                #[test]
2047                fn [<$tname _fetch_sub_max>]() {
2048                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2049                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2050                        let max_value: $type = max_value($log_bits) as _;
2051                        // Set the metadata byte(s) to all 1s
2052                        unsafe { *meta_ptr = <$type>::MAX; }
2053                        // Store max to the side metadata
2054                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2055
2056                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2057
2058                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2059                        assert_eq!(old_val_from_fetch, old_val);
2060
2061                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2062                        assert_eq!(new_val, 0);
2063                    });
2064                }
2065
2066                #[test]
2067                fn [<$tname _fetch_sub_overflow>]() {
2068                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2069                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2070                        let max_value: $type = max_value($log_bits) as _;
2071                        // Set the metadata byte(s) to all 1s
2072                        unsafe { *meta_ptr = <$type>::MAX; }
2073                        // Store 0 to the side metadata
2074                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2075
2076                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2077
2078                        // sub 1 from 0 will cause overflow, and wrap around to max
2079                        let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2080                        assert_eq!(old_val_from_fetch, old_val);
2081
2082                        let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2083                        assert_eq!(new_val, max_value);
2084                    });
2085                }
2086
2087                #[test]
2088                fn [<$tname _fetch_and>]() {
2089                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2090                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2091                        let max_value: $type = max_value($log_bits) as _;
2092                        // Set the metadata byte(s) to all 1s
2093                        unsafe { *meta_ptr = <$type>::MAX; }
2094                        // Store all 1s to the side metadata
2095                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2096
2097                        // max and max should be max
2098                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2099                        let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2100                        assert_eq!(old_val_from_fetch, old_val, "old values do not match");
2101                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value, "load values do not match");
2102                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX, "raw values do not match");
2103
2104                        // max and last_bit_zero should last_bit_zero
2105                        let last_bit_zero = max_value - 1;
2106                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2107                        let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, last_bit_zero, Ordering::SeqCst);
2108                        assert_eq!(old_val_from_fetch, old_val);
2109                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), last_bit_zero);
2110                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX - 1);
2111                    });
2112                }
2113
2114                #[test]
2115                fn [<$tname _fetch_or>]() {
2116                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2117                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2118                        let max_value: $type = max_value($log_bits) as _;
2119                        // Set the metadata byte(s) to all 0s
2120                        unsafe { *meta_ptr = 0; }
2121                        // Store 0 to the side metadata
2122                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2123
2124                        // 0 or 0 should be 0
2125                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2126                        let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2127                        assert_eq!(old_val_from_fetch, old_val);
2128                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2129                        assert_eq!(unsafe { *meta_ptr }, 0);
2130
2131                        // 0 and max should max
2132                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2133                        let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2134                        assert_eq!(old_val_from_fetch, old_val);
2135                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2136                        assert_eq!(unsafe { *meta_ptr }, max_value);
2137                    });
2138                }
2139
2140                #[test]
2141                fn [<$tname _fetch_update_success>]() {
2142                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2143                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2144                        let max_value: $type = max_value($log_bits) as _;
2145                        // Set the metadata byte(s) to all 1s
2146                        unsafe { *meta_ptr = <$type>::MAX; }
2147                        // Store all 1s to the side metadata
2148                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2149
2150                        // update from max to zero
2151                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2152                        let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| Some(0));
2153                        assert!(fetch_res.is_ok());
2154                        assert_eq!(fetch_res.unwrap(), old_val);
2155                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2156                        // Only the affected bits are set to 0
2157                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
2158                    });
2159                }
2160
2161                #[test]
2162                fn [<$tname _fetch_update_fail>]() {
2163                    test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2164                        let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2165                        let max_value: $type = max_value($log_bits) as _;
2166                        // Set the metadata byte(s) to all 1s
2167                        unsafe { *meta_ptr = <$type>::MAX; }
2168                        // Store all 1s to the side metadata
2169                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2170
2171                        // update from max to zero
2172                        let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2173                        let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| None);
2174                        assert!(fetch_res.is_err());
2175                        assert_eq!(fetch_res.err().unwrap(), old_val);
2176                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2177                        // Only the affected bits are set to 0
2178                        assert_eq!(unsafe { *meta_ptr }, <$type>::MAX);
2179                    });
2180                }
2181
2182                #[test]
2183                fn [<$tname _find_prev_non_zero_value_easy>]() {
2184                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2185                        let max_value: $type = max_value($log_bits) as _;
2186                        // Store non zero value at data_addr
2187                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2188
2189                        // Find the value starting from data_addr, at max 8 bytes.
2190                        // We should find data_addr
2191                        let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(data_addr, 8) };
2192                        assert!(res_addr.is_some());
2193                        assert_eq!(res_addr.unwrap(), data_addr);
2194                    });
2195                }
2196
2197                #[test]
2198                fn [<$tname _find_prev_non_zero_value_arbitrary_bytes>]() {
2199                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2200                        let max_value: $type = max_value($log_bits) as _;
2201                        // Store non zero value at data_addr
2202                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2203
2204                        // Start from data_addr, we offset arbitrary length, and search back to find data_addr
2205                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2206                        for len in 1..(test_region*4) {
2207                            let start_addr = data_addr + len;
2208                            // Use len+1, as len is non inclusive.
2209                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2210                            assert!(res_addr.is_some());
2211                            assert_eq!(res_addr.unwrap(), data_addr);
2212                        }
2213                    });
2214                }
2215
2216                #[test]
2217                fn [<$tname _find_prev_non_zero_value_arbitrary_start>]() {
2218                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2219                        let max_value: $type = max_value($log_bits) as _;
2220
2221                        // data_addr has a non-aligned offset
2222                        for offset in 0..7usize {
2223                            // Apply offset and test with the new data addr
2224                            let test_data_addr = data_addr + offset;
2225                            spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2226
2227                            // The return result should be aligned
2228                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(test_data_addr, 4096) };
2229                            assert!(res_addr.is_some());
2230                            assert_eq!(res_addr.unwrap(), data_addr);
2231
2232                            // Clear whatever is set
2233                            spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2234                        }
2235                    });
2236                }
2237
2238                #[test]
2239                fn [<$tname _find_prev_non_zero_value_no_find>]() {
2240                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2241                        // Store zero value at data_addr -- so we won't find anything
2242                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2243
2244                        // Start from data_addr, we offset arbitrary length, and search back
2245                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2246                        for len in 1..(test_region*4) {
2247                            let start_addr = data_addr + len;
2248                            // Use len+1, as len is non inclusive.
2249                            let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2250                            assert!(res_addr.is_none());
2251                        }
2252                    });
2253                }
2254
2255                #[test]
2256                fn [<$tname _find_next_non_zero_value_easy>]() {
2257                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2258                        let max_value: $type = max_value($log_bits) as _;
2259                        // Store non zero value at data_addr
2260                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2261
2262                        // Find the value starting from data_addr, at max 8 bytes.
2263                        // We should find data_addr
2264                        let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(data_addr, 8) };
2265                        assert!(res_addr.is_some());
2266                        assert_eq!(res_addr.unwrap(), data_addr);
2267                    });
2268                }
2269
2270                #[test]
2271                fn [<$tname _find_next_non_zero_value_arbitrary_bytes>]() {
2272                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2273                        let max_value: $type = max_value($log_bits) as _;
2274                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2275
2276                        // Take a data address in the middle since metadata before
2277                        // the start may not be mapped
2278                        let data_addr = data_addr + test_region*4;
2279
2280                        // Store non zero value at data_addr
2281                        spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2282                        assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2283
2284                        // Start from data_addr, we offset arbitrary length, and search forwards to find data_addr
2285                        for len in 1..(test_region*4) {
2286                            let start_addr = data_addr - len;
2287                            // Use len+1, as len is non inclusive.
2288                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2289                            assert!(res_addr.is_some());
2290                            assert_eq!(res_addr.unwrap(), data_addr);
2291                        }
2292                    });
2293                }
2294
2295                #[test]
2296                fn [<$tname _find_next_non_zero_value_arbitrary_start>]() {
2297                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2298                        let max_value: $type = max_value($log_bits) as _;
2299
2300                        // data_addr has a non-aligned offset
2301                        for offset in 0..7usize {
2302                            // Apply offset and test with the new data addr
2303                            let test_data_addr = data_addr + offset;
2304                            spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2305
2306                            // The return result should be aligned
2307                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(test_data_addr, 4096) };
2308                            assert!(res_addr.is_some());
2309                            assert_eq!(res_addr.unwrap(), data_addr);
2310
2311                            // Clear whatever is set
2312                            spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2313                        }
2314                    });
2315                }
2316
2317                #[test]
2318                fn [<$tname _find_next_non_zero_value_no_find>]() {
2319                    test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2320                        // Store zero value at data_addr -- so we won't find anything
2321                        spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2322
2323                        // Start from data_addr, we offset arbitrary length, and search back
2324                        let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2325                        for len in 1..(test_region*4) {
2326                            let start_addr = data_addr - len;
2327                            // Use len+1, as len is non inclusive.
2328                            let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2329                            assert!(res_addr.is_none());
2330                        }
2331                    });
2332                }
2333            }
2334        }
2335    }
2336
2337    test_side_metadata_access!(test_u1, u8, 0);
2338    test_side_metadata_access!(test_u2, u8, 1);
2339    test_side_metadata_access!(test_u4, u8, 2);
2340    test_side_metadata_access!(test_u8, u8, 3);
2341    test_side_metadata_access!(test_u16, u16, 4);
2342    test_side_metadata_access!(test_u32, u32, 5);
2343    test_side_metadata_access!(test_u64, u64, 6);
2344    test_side_metadata_access!(
2345        test_usize,
2346        usize,
2347        if cfg!(target_pointer_width = "64") {
2348            6
2349        } else if cfg!(target_pointer_width = "32") {
2350            5
2351        } else {
2352            unreachable!()
2353        }
2354    );
2355
2356    #[test]
2357    fn test_bulk_update_meta_bits() {
2358        let raw_mem =
2359            unsafe { std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(8, 8).unwrap()) };
2360        let addr = Address::from_mut_ptr(raw_mem);
2361
2362        SideMetadataSpec::set_meta_bits(addr, 0, addr, 4);
2363        assert_eq!(unsafe { addr.load::<u64>() }, 0b1111);
2364
2365        SideMetadataSpec::zero_meta_bits(addr, 1, addr, 3);
2366        assert_eq!(unsafe { addr.load::<u64>() }, 0b1001);
2367
2368        SideMetadataSpec::set_meta_bits(addr, 2, addr, 6);
2369        assert_eq!(unsafe { addr.load::<u64>() }, 0b0011_1101);
2370
2371        SideMetadataSpec::zero_meta_bits(addr, 0, addr + 1usize, 0);
2372        assert_eq!(unsafe { addr.load::<u64>() }, 0b0);
2373
2374        SideMetadataSpec::set_meta_bits(addr, 2, addr + 1usize, 2);
2375        assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1100);
2376
2377        SideMetadataSpec::set_meta_bits(addr, 0, addr + 1usize, 2);
2378        assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1111);
2379    }
2380}