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#[derive(Clone, Copy, PartialEq, Eq, Hash)]
24pub struct SideMetadataSpec {
25 pub name: &'static str,
27 pub is_global: bool,
30 pub offset: usize,
32 pub log_num_of_bits: usize,
34 pub log_bytes_in_region: usize,
36}
37
38impl SideMetadataSpec {
39 pub const fn uses_contiguous_side_metadata(&self) -> bool {
41 self.is_global || cfg!(target_pointer_width = "64")
42 }
43
44 pub const fn uses_chunked_side_metadata(&self) -> bool {
46 !self.uses_contiguous_side_metadata()
47 }
48
49 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 pub const fn get_offset_for_chunked(&self) -> usize {
58 debug_assert!(self.uses_chunked_side_metadata());
59 self.offset
60 }
61
62 #[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 #[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 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 #[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 #[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 #[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 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 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 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 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 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 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 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 #[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 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 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 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 update_discontiguous(start, second_data_chunk - start);
300
301 let last_data_chunk = (start + size).align_down(BYTES_IN_CHUNK);
302 update_discontiguous(last_data_chunk, start + size - last_data_chunk);
304 let mut next_data_chunk = second_data_chunk;
305
306 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 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 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 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 let mask: u8 = !(u8::MAX.checked_shl(bit_end as u32).unwrap_or(0))
406 & (u8::MAX << bit_start); 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 #[allow(unused_variables)] 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 #[cfg(feature = "extreme_assertions")]
452 let _lock = sanity::SANITY_LOCK.lock().unwrap();
453
454 #[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 let ret = access_func();
466
467 if CHECK_VALUE {
469 verify_func(ret);
470 }
471
472 ret
473 }
474
475 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 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 pub fn load_byte(&self, data_addr: Address) -> u8 {
544 let meta_addr = address_to_meta_address(self, data_addr);
545 unsafe { meta_addr.load::<u8>() }
546 }
547
548 pub unsafe fn store_byte_relaxed(&self, data_addr: Address, byte: u8) {
557 let meta_addr = address_to_meta_address(self, data_addr);
558 meta_addr.store::<u8>(byte);
559 }
560
561 pub fn load_atomic<T: MetadataValue>(&self, data_addr: Address, order: Ordering) -> T {
564 self.side_metadata_access::<true, T, _, _, _>(
565 data_addr,
566 None,
567 || {
568 let meta_addr = address_to_meta_address(self, data_addr);
569 let bits_num_log = self.log_num_of_bits;
570 if bits_num_log < 3 {
571 let lshift = meta_byte_lshift(self, data_addr);
572 let mask = meta_byte_mask(self) << lshift;
573 let byte_val = unsafe { meta_addr.atomic_load::<AtomicU8>(order) };
574 FromPrimitive::from_u8((byte_val & mask) >> lshift).unwrap()
575 } else {
576 unsafe { T::load_atomic(meta_addr, order) }
577 }
578 },
579 |_v| {
580 #[cfg(feature = "extreme_assertions")]
581 sanity::verify_load(self, data_addr, _v);
582 },
583 )
584 }
585
586 pub fn store_atomic<T: MetadataValue>(&self, data_addr: Address, metadata: T, order: Ordering) {
589 self.side_metadata_access::<true, T, _, _, _>(
590 data_addr,
591 Some(metadata),
592 || {
593 let meta_addr = address_to_meta_address(self, data_addr);
594 let bits_num_log = self.log_num_of_bits;
595 if bits_num_log < 3 {
596 let lshift = meta_byte_lshift(self, data_addr);
597 let mask = meta_byte_mask(self) << lshift;
598 let metadata_u8 = metadata.to_u8().unwrap();
599 let _ = unsafe {
600 <u8 as MetadataValue>::fetch_update(meta_addr, order, order, |v: u8| {
601 Some((v & !mask) | (metadata_u8 << lshift))
602 })
603 };
604 } else {
605 unsafe {
606 T::store_atomic(meta_addr, metadata, order);
607 }
608 }
609 },
610 |_| {
611 #[cfg(feature = "extreme_assertions")]
612 sanity::verify_store(self, data_addr, metadata);
613 },
614 )
615 }
616
617 pub unsafe fn set_zero(&self, data_addr: Address) {
627 use num_traits::Zero;
628 match self.log_num_of_bits {
629 0..=3 => self.store(data_addr, u8::zero()),
630 4 => self.store(data_addr, u16::zero()),
631 5 => self.store(data_addr, u32::zero()),
632 6 => self.store(data_addr, u64::zero()),
633 _ => unreachable!(),
634 }
635 }
636
637 pub fn set_zero_atomic(&self, data_addr: Address, order: Ordering) {
640 use num_traits::Zero;
641 match self.log_num_of_bits {
642 0..=3 => self.store_atomic(data_addr, u8::zero(), order),
643 4 => self.store_atomic(data_addr, u16::zero(), order),
644 5 => self.store_atomic(data_addr, u32::zero(), order),
645 6 => self.store_atomic(data_addr, u64::zero(), order),
646 _ => unreachable!(),
647 }
648 }
649
650 pub unsafe fn set_raw_byte_atomic(&self, data_addr: Address, order: Ordering) {
661 debug_assert!(self.log_num_of_bits < 3);
662 cfg_if::cfg_if! {
663 if #[cfg(feature = "extreme_assertions")] {
664 self.store_atomic::<u8>(data_addr, 1, order)
666 } else {
667 self.side_metadata_access::<false, u8, _, _, _>(
668 data_addr,
669 Some(1u8),
670 || {
671 let meta_addr = address_to_meta_address(self, data_addr);
672 u8::store_atomic(meta_addr, 0xffu8, order);
673 },
674 |_| {}
675 )
676 }
677 }
678 }
679
680 pub unsafe fn load_raw_byte(&self, data_addr: Address) -> u8 {
688 debug_assert!(self.log_num_of_bits < 3);
689 self.side_metadata_access::<false, u8, _, _, _>(
690 data_addr,
691 None,
692 || {
693 let meta_addr = address_to_meta_address(self, data_addr);
694 meta_addr.load::<u8>()
695 },
696 |_| {},
697 )
698 }
699
700 pub unsafe fn load_raw_word(&self, data_addr: Address) -> usize {
708 use crate::util::constants::*;
709 debug_assert!(self.log_num_of_bits < (LOG_BITS_IN_BYTE + LOG_BYTES_IN_ADDRESS) as usize);
710 self.side_metadata_access::<false, usize, _, _, _>(
711 data_addr,
712 None,
713 || {
714 let meta_addr = address_to_meta_address(self, data_addr);
715 let aligned_meta_addr = meta_addr.align_down(BYTES_IN_ADDRESS);
716 aligned_meta_addr.load::<usize>()
717 },
718 |_| {},
719 )
720 }
721
722 pub fn compare_exchange_atomic<T: MetadataValue>(
727 &self,
728 data_addr: Address,
729 old_metadata: T,
730 new_metadata: T,
731 success_order: Ordering,
732 failure_order: Ordering,
733 ) -> std::result::Result<T, T> {
734 self.side_metadata_access::<true, T, _, _, _>(
735 data_addr,
736 Some(new_metadata),
737 || {
738 let meta_addr = address_to_meta_address(self, data_addr);
739 let bits_num_log = self.log_num_of_bits;
740 if bits_num_log < 3 {
741 let lshift = meta_byte_lshift(self, data_addr);
742 let mask = meta_byte_mask(self) << lshift;
743
744 let real_old_byte = unsafe { meta_addr.atomic_load::<AtomicU8>(success_order) };
745 let expected_old_byte =
746 (real_old_byte & !mask) | ((old_metadata.to_u8().unwrap()) << lshift);
747 let expected_new_byte =
748 (expected_old_byte & !mask) | ((new_metadata.to_u8().unwrap()) << lshift);
749
750 unsafe {
751 meta_addr.compare_exchange::<AtomicU8>(
752 expected_old_byte,
753 expected_new_byte,
754 success_order,
755 failure_order,
756 )
757 }
758 .map(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
759 .map_err(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
760 } else {
761 unsafe {
762 T::compare_exchange(
763 meta_addr,
764 old_metadata,
765 new_metadata,
766 success_order,
767 failure_order,
768 )
769 }
770 }
771 },
772 |_res| {
773 #[cfg(feature = "extreme_assertions")]
774 if _res.is_ok() {
775 sanity::verify_store(self, data_addr, new_metadata);
776 }
777 },
778 )
779 }
780
781 fn fetch_ops_on_bits<F: Fn(u8) -> u8>(
784 &self,
785 data_addr: Address,
786 meta_addr: Address,
787 set_order: Ordering,
788 fetch_order: Ordering,
789 update: F,
790 ) -> u8 {
791 let lshift = meta_byte_lshift(self, data_addr);
792 let mask = meta_byte_mask(self) << lshift;
793
794 let old_raw_byte = unsafe {
795 <u8 as MetadataValue>::fetch_update(
796 meta_addr,
797 set_order,
798 fetch_order,
799 |raw_byte: u8| {
800 let old_val = (raw_byte & mask) >> lshift;
801 let new_val = update(old_val);
802 let new_raw_byte = (raw_byte & !mask) | ((new_val << lshift) & mask);
803 Some(new_raw_byte)
804 },
805 )
806 }
807 .unwrap();
808 (old_raw_byte & mask) >> lshift
809 }
810
811 pub fn fetch_add_atomic<T: MetadataValue>(
815 &self,
816 data_addr: Address,
817 val: T,
818 order: Ordering,
819 ) -> T {
820 self.side_metadata_access::<true, T, _, _, _>(
821 data_addr,
822 Some(val),
823 || {
824 let meta_addr = address_to_meta_address(self, data_addr);
825 let bits_num_log = self.log_num_of_bits;
826 if bits_num_log < 3 {
827 FromPrimitive::from_u8(self.fetch_ops_on_bits(
828 data_addr,
829 meta_addr,
830 order,
831 order,
832 |x: u8| x.wrapping_add(val.to_u8().unwrap()),
833 ))
834 .unwrap()
835 } else {
836 unsafe { T::fetch_add(meta_addr, val, order) }
837 }
838 },
839 |_old_val| {
840 #[cfg(feature = "extreme_assertions")]
841 sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.wrapping_add(&val))
842 },
843 )
844 }
845
846 pub fn fetch_sub_atomic<T: MetadataValue>(
850 &self,
851 data_addr: Address,
852 val: T,
853 order: Ordering,
854 ) -> T {
855 self.side_metadata_access::<true, T, _, _, _>(
856 data_addr,
857 Some(val),
858 || {
859 let meta_addr = address_to_meta_address(self, data_addr);
860 if self.log_num_of_bits < 3 {
861 FromPrimitive::from_u8(self.fetch_ops_on_bits(
862 data_addr,
863 meta_addr,
864 order,
865 order,
866 |x: u8| x.wrapping_sub(val.to_u8().unwrap()),
867 ))
868 .unwrap()
869 } else {
870 unsafe { T::fetch_sub(meta_addr, val, order) }
871 }
872 },
873 |_old_val| {
874 #[cfg(feature = "extreme_assertions")]
875 sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.wrapping_sub(&val))
876 },
877 )
878 }
879
880 pub fn fetch_and_atomic<T: MetadataValue>(
884 &self,
885 data_addr: Address,
886 val: T,
887 order: Ordering,
888 ) -> T {
889 self.side_metadata_access::<true, T, _, _, _>(
890 data_addr,
891 Some(val),
892 || {
893 let meta_addr = address_to_meta_address(self, data_addr);
894 if self.log_num_of_bits < 3 {
895 let lshift = meta_byte_lshift(self, data_addr);
896 let mask = meta_byte_mask(self) << lshift;
897 let rhs = (val.to_u8().unwrap() << lshift) | !mask;
899 let old_raw_byte =
900 unsafe { <u8 as MetadataValue>::fetch_and(meta_addr, rhs, order) };
901 let old_val = (old_raw_byte & mask) >> lshift;
902 FromPrimitive::from_u8(old_val).unwrap()
903 } else {
904 unsafe { T::fetch_and(meta_addr, val, order) }
905 }
906 },
907 |_old_val| {
908 #[cfg(feature = "extreme_assertions")]
909 sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.bitand(val))
910 },
911 )
912 }
913
914 pub fn fetch_or_atomic<T: MetadataValue>(
918 &self,
919 data_addr: Address,
920 val: T,
921 order: Ordering,
922 ) -> T {
923 self.side_metadata_access::<true, T, _, _, _>(
924 data_addr,
925 Some(val),
926 || {
927 let meta_addr = address_to_meta_address(self, data_addr);
928 if self.log_num_of_bits < 3 {
929 let lshift = meta_byte_lshift(self, data_addr);
930 let mask = meta_byte_mask(self) << lshift;
931 let rhs = (val.to_u8().unwrap() << lshift) & mask;
933 let old_raw_byte =
934 unsafe { <u8 as MetadataValue>::fetch_or(meta_addr, rhs, order) };
935 let old_val = (old_raw_byte & mask) >> lshift;
936 FromPrimitive::from_u8(old_val).unwrap()
937 } else {
938 unsafe { T::fetch_or(meta_addr, val, order) }
939 }
940 },
941 |_old_val| {
942 #[cfg(feature = "extreme_assertions")]
943 sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.bitor(val))
944 },
945 )
946 }
947
948 pub fn fetch_update_atomic<T: MetadataValue, F: FnMut(T) -> Option<T>>(
952 &self,
953 data_addr: Address,
954 set_order: Ordering,
955 fetch_order: Ordering,
956 mut f: F,
957 ) -> std::result::Result<T, T> {
958 #[cfg(feature = "extreme_assertions")]
963 let last_new_val: std::cell::Cell<Option<T>> = std::cell::Cell::new(None);
964 #[cfg(feature = "extreme_assertions")]
965 let last_new_val_ref = &last_new_val;
966 self.side_metadata_access::<true, T, _, _, _>(
967 data_addr,
968 None,
969 move || -> std::result::Result<T, T> {
970 let meta_addr = address_to_meta_address(self, data_addr);
971 if self.log_num_of_bits < 3 {
972 let lshift = meta_byte_lshift(self, data_addr);
973 let mask = meta_byte_mask(self) << lshift;
974
975 unsafe {
976 <u8 as MetadataValue>::fetch_update(
977 meta_addr,
978 set_order,
979 fetch_order,
980 |raw_byte: u8| {
981 let old_val = (raw_byte & mask) >> lshift;
982 let new_val = f(FromPrimitive::from_u8(old_val).unwrap());
983 #[cfg(feature = "extreme_assertions")]
984 last_new_val_ref.set(new_val);
985 new_val.map(|new_val| {
986 (raw_byte & !mask)
987 | ((new_val.to_u8().unwrap() << lshift) & mask)
988 })
989 },
990 )
991 }
992 .map(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
993 .map_err(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
994 } else {
995 unsafe {
996 T::fetch_update(meta_addr, set_order, fetch_order, |old_val| {
997 let new_val = f(old_val);
998 #[cfg(feature = "extreme_assertions")]
999 last_new_val_ref.set(new_val);
1000 new_val
1001 })
1002 }
1003 }
1004 },
1005 |_result| {
1006 #[cfg(feature = "extreme_assertions")]
1007 if let Ok(old_val) = _result {
1008 sanity::verify_update::<T>(
1009 self,
1010 data_addr,
1011 old_val,
1012 last_new_val_ref.get().unwrap(),
1013 )
1014 }
1015 },
1016 )
1017 }
1018
1019 #[allow(clippy::let_and_return)]
1035 pub unsafe fn find_prev_non_zero_value<T: MetadataValue>(
1036 &self,
1037 data_addr: Address,
1038 search_limit_bytes: usize,
1039 ) -> Option<Address> {
1040 debug_assert!(search_limit_bytes > 0);
1041
1042 if self.uses_contiguous_side_metadata() {
1043 let result = self.find_prev_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1045 #[cfg(debug_assertions)]
1046 {
1047 let result2 =
1049 self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1050 assert_eq!(result, result2, "find_prev_non_zero_value_fast returned a diffrent result from the naive implementation.");
1051 }
1052 result
1053 } else {
1054 warn!("We are trying to search non zero bits in an discontiguous side metadata. The performance is slow, as MMTk does not optimize for this case.");
1058 self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1059 }
1060 }
1061
1062 fn find_prev_non_zero_value_simple<T: MetadataValue>(
1063 &self,
1064 data_addr: Address,
1065 search_limit_bytes: usize,
1066 ) -> Option<Address> {
1067 let region_bytes = 1 << self.log_bytes_in_region;
1068 let start_addr = data_addr.align_down(region_bytes);
1070 let end_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1071
1072 let mmap_granularity = MMAPPER.granularity();
1073 let mut mapped_grain = Address::MAX;
1074
1075 let mut cursor = start_addr;
1076 while cursor >= end_addr {
1077 if cursor < mapped_grain {
1080 if self.is_mapped(cursor) {
1081 mapped_grain = cursor.align_down(mmap_granularity);
1082 } else {
1083 return None;
1085 }
1086 }
1087 if !unsafe { self.load::<T>(cursor).is_zero() } {
1089 return Some(cursor);
1090 }
1091 cursor -= region_bytes;
1092 }
1093 None
1094 }
1095
1096 #[allow(clippy::let_and_return)]
1097 fn find_prev_non_zero_value_fast<T: MetadataValue>(
1098 &self,
1099 data_addr: Address,
1100 search_limit_bytes: usize,
1101 ) -> Option<Address> {
1102 debug_assert!(self.uses_contiguous_side_metadata());
1103
1104 if !self.is_mapped(data_addr) {
1106 return None;
1107 }
1108 if !unsafe { self.load::<T>(data_addr).is_zero() } {
1110 return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1111 }
1112
1113 let start_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1115 let end_addr = data_addr;
1116
1117 let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1121 let start_meta_shift = meta_byte_lshift(self, start_addr);
1122 let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1123 let end_meta_shift = meta_byte_lshift(self, end_addr);
1124
1125 let mut res = None;
1126
1127 let mut visitor = |range: BitByteRange| {
1128 match range {
1129 BitByteRange::Bytes { start, end } => {
1130 match helpers::find_last_non_zero_bit_in_metadata_bytes(start, end) {
1131 helpers::FindMetaBitResult::Found { addr, bit } => {
1132 let (addr, bit) = align_metadata_address(self, addr, bit);
1133 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1134 true
1136 }
1137 helpers::FindMetaBitResult::UnmappedMetadata => true,
1139 helpers::FindMetaBitResult::NotFound => false,
1141 }
1142 }
1143 BitByteRange::BitsInByte {
1144 addr,
1145 bit_start,
1146 bit_end,
1147 } => {
1148 match helpers::find_last_non_zero_bit_in_metadata_bits(addr, bit_start, bit_end)
1149 {
1150 helpers::FindMetaBitResult::Found { addr, bit } => {
1151 let (addr, bit) = align_metadata_address(self, addr, bit);
1152 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1153 true
1155 }
1156 helpers::FindMetaBitResult::UnmappedMetadata => true,
1158 helpers::FindMetaBitResult::NotFound => false,
1160 }
1161 }
1162 }
1163 };
1164
1165 ranges::break_bit_range(
1166 start_meta_addr,
1167 start_meta_shift,
1168 end_meta_addr,
1169 end_meta_shift,
1170 false,
1171 &mut visitor,
1172 );
1173
1174 res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1179 .filter(|addr| *addr >= start_addr && *addr < end_addr)
1180 }
1181
1182 #[allow(clippy::let_and_return)]
1198 pub unsafe fn find_next_non_zero_value<T: MetadataValue>(
1199 &self,
1200 data_addr: Address,
1201 search_limit_bytes: usize,
1202 ) -> Option<Address> {
1203 debug_assert!(search_limit_bytes > 0);
1204
1205 if self.uses_contiguous_side_metadata() {
1206 let result = self.find_next_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1208 #[cfg(debug_assertions)]
1209 {
1210 let result2 =
1212 self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1213 assert_eq!(
1214 result,
1215 result2,
1216 "find_next_non_zero_value_fast returned a different result from the naive implementation. data_addr {}, search_limit_bytes {}",
1217 data_addr, search_limit_bytes,
1218 );
1219 }
1220 result
1221 } else {
1222 warn!("We are trying to search non zero bits in an discontiguous side metadata. The performance is slow, as MMTk does not optimize for this case.");
1226 self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1227 }
1228 }
1229
1230 fn find_next_non_zero_value_simple<T: MetadataValue>(
1231 &self,
1232 data_addr: Address,
1233 search_limit_bytes: usize,
1234 ) -> Option<Address> {
1235 let region_bytes = 1 << self.log_bytes_in_region;
1236 let start_addr = data_addr.align_down(region_bytes);
1238 let end_addr = data_addr + search_limit_bytes;
1239
1240 let mmap_granularity = MMAPPER.granularity();
1241 let mut mapped_grain = Address::ZERO;
1242
1243 let mut cursor = start_addr;
1244 while cursor < end_addr {
1245 if cursor > mapped_grain {
1248 if self.is_mapped(cursor) {
1249 mapped_grain = cursor.align_up(mmap_granularity) - 0x1;
1250 } else {
1251 return None;
1253 }
1254 }
1255 if !unsafe { self.load::<T>(cursor).is_zero() } {
1257 return Some(cursor);
1258 }
1259 cursor += region_bytes;
1260 }
1261 None
1262 }
1263
1264 fn find_next_non_zero_value_fast<T: MetadataValue>(
1265 &self,
1266 data_addr: Address,
1267 search_limit_bytes: usize,
1268 ) -> Option<Address> {
1269 debug_assert!(self.uses_contiguous_side_metadata());
1270
1271 if !self.is_mapped(data_addr) {
1273 return None;
1274 }
1275 if !unsafe { self.load::<T>(data_addr).is_zero() } {
1277 return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1278 }
1279
1280 let start_addr = data_addr.align_down(1 << self.log_bytes_in_region);
1282 let end_addr = (data_addr + search_limit_bytes).align_up(1 << self.log_bytes_in_region);
1287
1288 let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1292 let start_meta_shift = meta_byte_lshift(self, start_addr);
1293 let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1294 let end_meta_shift = meta_byte_lshift(self, end_addr);
1295
1296 let mut res = None;
1297
1298 let mut visitor = |range: BitByteRange| {
1299 match range {
1300 BitByteRange::Bytes { start, end } => {
1301 match helpers::find_first_non_zero_bit_in_metadata_bytes(start, end) {
1302 helpers::FindMetaBitResult::Found { addr, bit } => {
1303 let (addr, bit) = align_metadata_address(self, addr, bit);
1304 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1305 true
1307 }
1308 helpers::FindMetaBitResult::UnmappedMetadata => true,
1310 helpers::FindMetaBitResult::NotFound => false,
1312 }
1313 }
1314 BitByteRange::BitsInByte {
1315 addr,
1316 bit_start,
1317 bit_end,
1318 } => {
1319 match helpers::find_first_non_zero_bit_in_metadata_bits(
1320 addr, bit_start, bit_end,
1321 ) {
1322 helpers::FindMetaBitResult::Found { addr, bit } => {
1323 let (addr, bit) = align_metadata_address(self, addr, bit);
1324 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1325 true
1327 }
1328 helpers::FindMetaBitResult::UnmappedMetadata => true,
1330 helpers::FindMetaBitResult::NotFound => false,
1332 }
1333 }
1334 }
1335 };
1336
1337 ranges::break_bit_range(
1338 start_meta_addr,
1339 start_meta_shift,
1340 end_meta_addr,
1341 end_meta_shift,
1342 true,
1343 &mut visitor,
1344 );
1345
1346 res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1351 .filter(|addr| *addr >= start_addr && *addr < end_addr)
1352 }
1353
1354 pub fn scan_non_zero_values<T: MetadataValue>(
1364 &self,
1365 data_start_addr: Address,
1366 data_end_addr: Address,
1367 visit_data: &mut impl FnMut(Address),
1368 ) {
1369 if self.uses_contiguous_side_metadata() && self.log_num_of_bits == 0 {
1370 self.scan_non_zero_values_fast(data_start_addr, data_end_addr, visit_data);
1375 } else {
1376 warn!(
1381 "We are trying to search for non zero bits in a discontiguous side metadata \
1382 or the metadata has more than one bit per region. \
1383 The performance is slow, as MMTk does not optimize for this case."
1384 );
1385 self.scan_non_zero_values_simple::<T>(data_start_addr, data_end_addr, visit_data);
1386 }
1387 }
1388
1389 fn scan_non_zero_values_simple<T: MetadataValue>(
1390 &self,
1391 data_start_addr: Address,
1392 data_end_addr: Address,
1393 visit_data: &mut impl FnMut(Address),
1394 ) {
1395 let region_bytes = 1usize << self.log_bytes_in_region;
1396
1397 let mut cursor = data_start_addr;
1398 while cursor < data_end_addr {
1399 debug_assert!(self.is_mapped(cursor));
1400
1401 if !unsafe { self.load::<T>(cursor).is_zero() } {
1403 visit_data(cursor);
1404 }
1405 cursor += region_bytes;
1406 }
1407 }
1408
1409 fn scan_non_zero_values_fast(
1410 &self,
1411 data_start_addr: Address,
1412 data_end_addr: Address,
1413 visit_data: &mut impl FnMut(Address),
1414 ) {
1415 debug_assert!(self.uses_contiguous_side_metadata());
1416 debug_assert_eq!(self.log_num_of_bits, 0);
1417
1418 let start_meta_addr = address_to_contiguous_meta_address(self, data_start_addr);
1420 let start_meta_shift = meta_byte_lshift(self, data_start_addr);
1421 let end_meta_addr = address_to_contiguous_meta_address(self, data_end_addr);
1422 let end_meta_shift = meta_byte_lshift(self, data_end_addr);
1423
1424 let mut visitor = |range| {
1425 match range {
1426 BitByteRange::Bytes { start, end } => {
1427 helpers::scan_non_zero_bits_in_metadata_bytes(start, end, &mut |addr, bit| {
1428 visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1429 });
1430 }
1431 BitByteRange::BitsInByte {
1432 addr,
1433 bit_start,
1434 bit_end,
1435 } => helpers::scan_non_zero_bits_in_metadata_bits(
1436 addr,
1437 bit_start,
1438 bit_end,
1439 &mut |addr, bit| {
1440 visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1441 },
1442 ),
1443 }
1444 false
1445 };
1446
1447 ranges::break_bit_range(
1448 start_meta_addr,
1449 start_meta_shift,
1450 end_meta_addr,
1451 end_meta_shift,
1452 true,
1453 &mut visitor,
1454 );
1455 }
1456}
1457
1458impl fmt::Debug for SideMetadataSpec {
1459 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1460 f.write_fmt(format_args!(
1461 "SideMetadataSpec {} {{ \
1462 **is_global: {:?} \
1463 **offset: 0x{:x} \
1464 **log_num_of_bits: 0x{:x} \
1465 **log_bytes_in_region: 0x{:x} \
1466 }}",
1467 self.name, self.is_global, self.offset, self.log_num_of_bits, self.log_bytes_in_region
1468 ))
1469 }
1470}
1471
1472pub const fn side_metadata_offset_after(spec: &SideMetadataSpec) -> usize {
1475 raw_align_up(spec.upper_bound_offset(), BYTES_IN_WORD)
1484}
1485
1486pub(crate) struct SideMetadataContext {
1489 pub global: Vec<SideMetadataSpec>,
1491 pub local: Vec<SideMetadataSpec>,
1493}
1494
1495impl SideMetadataContext {
1496 #[allow(clippy::vec_init_then_push)] pub fn new_global_specs(specs: &[SideMetadataSpec]) -> Vec<SideMetadataSpec> {
1498 let mut ret = vec![];
1499
1500 #[cfg(feature = "vo_bit")]
1501 ret.push(VO_BIT_SIDE_METADATA_SPEC);
1502
1503 if let Some(spec) = crate::mmtk::SFT_MAP.get_side_metadata() {
1504 if spec.is_global {
1505 ret.push(*spec);
1506 }
1507 }
1508
1509 ret.push(crate::util::heap::chunk_map::ChunkMap::ALLOC_TABLE);
1513
1514 ret.extend_from_slice(specs);
1515 ret
1516 }
1517
1518 pub fn get_local_specs(&self) -> &[SideMetadataSpec] {
1519 &self.local
1520 }
1521
1522 #[cfg(debug_assertions)]
1523 pub fn assert_metadata_ranges_in_reserved_range(
1524 &self,
1525 start: Address,
1526 size: usize,
1527 space_name: &str,
1528 ) {
1529 let reserved = {
1530 let base = super::layout::global_side_metadata_base_address();
1531 let bytes = super::layout::side_metadata_reserved_bytes();
1532 base..(base + bytes)
1533 };
1534 let check_spec = |spec: &SideMetadataSpec| {
1535 if !spec.uses_contiguous_side_metadata() {
1536 return;
1537 }
1538 let metadata_start = address_to_meta_address(spec, start);
1539 let mmap_start = metadata_start.align_down(BYTES_IN_PAGE);
1540 let metadata_size = data_to_meta_size_round_up(spec, size);
1541 let mmap_end = (metadata_start + metadata_size).align_up(BYTES_IN_PAGE);
1542 debug_assert!(
1543 mmap_start >= reserved.start && mmap_end <= reserved.end,
1544 "Side metadata range for spec {} in space {} is outside reserved range: [{}, {}) vs [{}, {})",
1545 spec.name,
1546 space_name,
1547 mmap_start,
1548 mmap_end,
1549 reserved.start,
1550 reserved.end
1551 );
1552 };
1553 self.global.iter().for_each(check_spec);
1554 self.local.iter().for_each(check_spec);
1555 }
1556
1557 pub fn calculate_reserved_pages(&self, data_pages: usize) -> usize {
1564 let mut total = 0;
1565 for spec in self.global.iter() {
1566 total += data_to_meta_size_round_up(spec, data_pages);
1570 }
1571 for spec in self.local.iter() {
1572 total += data_to_meta_size_round_up(spec, data_pages);
1573 }
1574 total
1575 }
1576
1577 pub fn try_map_metadata_space(
1583 &self,
1584 start: Address,
1585 size: usize,
1586 space_name: &str,
1587 ) -> MmapResult<()> {
1588 debug!(
1589 "try_map_metadata_space({}, 0x{:x}, {}, {})",
1590 start,
1591 size,
1592 self.global.len(),
1593 self.local.len()
1594 );
1595 debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1597 debug_assert!(size % BYTES_IN_PAGE == 0);
1598 self.map_metadata_internal(start, size, false, space_name)
1599 }
1600
1601 pub fn try_map_metadata_address_range(
1606 &self,
1607 start: Address,
1608 size: usize,
1609 name: &str,
1610 ) -> MmapResult<()> {
1611 debug!(
1612 "try_map_metadata_address_range({}, 0x{:x}, {}, {})",
1613 start,
1614 size,
1615 self.global.len(),
1616 self.local.len()
1617 );
1618 debug_assert!(start.is_aligned_to(BYTES_IN_CHUNK));
1620 debug_assert!(size % BYTES_IN_CHUNK == 0);
1621 self.map_metadata_internal(start, size, true, name)
1622 }
1623
1624 fn map_metadata_internal(
1632 &self,
1633 start: Address,
1634 size: usize,
1635 no_reserve: bool,
1636 space_name: &str,
1637 ) -> MmapResult<()> {
1638 for spec in self.global.iter() {
1639 let anno = MmapAnnotation::SideMeta {
1640 space: space_name,
1641 meta: spec.name,
1642 };
1643 try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1644 }
1645
1646 #[cfg(target_pointer_width = "32")]
1647 let mut lsize: usize = 0;
1648
1649 for spec in self.local.iter() {
1650 #[cfg(target_pointer_width = "64")]
1662 {
1663 let anno = MmapAnnotation::SideMeta {
1664 space: space_name,
1665 meta: spec.name,
1666 };
1667 try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1668 }
1669 #[cfg(target_pointer_width = "32")]
1670 {
1671 lsize += metadata_bytes_per_chunk(spec.log_bytes_in_region, spec.log_num_of_bits);
1672 }
1673 }
1674
1675 #[cfg(target_pointer_width = "32")]
1676 if lsize > 0 {
1677 let max = BYTES_IN_CHUNK >> super::layout::LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO;
1678 debug_assert!(
1679 lsize <= max,
1680 "local side metadata per chunk (0x{:x}) must be less than (0x{:x})",
1681 lsize,
1682 max
1683 );
1684 let anno = MmapAnnotation::SideMeta {
1687 space: space_name,
1688 meta: "all",
1689 };
1690 try_map_per_chunk_metadata_space(start, size, lsize, no_reserve, &anno)?;
1691 }
1692
1693 Ok(())
1694 }
1695
1696 #[cfg(test)]
1704 pub fn ensure_unmap_metadata_space(&self, start: Address, size: usize) {
1705 trace!("ensure_unmap_metadata_space({}, 0x{:x})", start, size);
1706 debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1707 debug_assert!(size % BYTES_IN_PAGE == 0);
1708
1709 for spec in self.global.iter() {
1710 ensure_munmap_contiguous_metadata_space(start, size, spec);
1711 }
1712
1713 for spec in self.local.iter() {
1714 #[cfg(target_pointer_width = "64")]
1715 {
1716 ensure_munmap_contiguous_metadata_space(start, size, spec);
1717 }
1718 #[cfg(target_pointer_width = "32")]
1719 {
1720 ensure_munmap_chunked_metadata_space(start, size, spec);
1721 }
1722 }
1723 }
1724}
1725
1726pub struct MetadataByteArrayRef<const ENTRIES: usize> {
1728 #[cfg(feature = "extreme_assertions")]
1729 heap_range_start: Address,
1730 #[cfg(feature = "extreme_assertions")]
1731 spec: SideMetadataSpec,
1732 data: &'static [u8; ENTRIES],
1733}
1734
1735impl<const ENTRIES: usize> MetadataByteArrayRef<ENTRIES> {
1736 pub fn new(metadata_spec: &SideMetadataSpec, start: Address, bytes: usize) -> Self {
1745 debug_assert_eq!(
1746 metadata_spec.log_num_of_bits, LOG_BITS_IN_BYTE as usize,
1747 "Each heap entry should map to a byte in side-metadata"
1748 );
1749 debug_assert_eq!(
1750 bytes >> metadata_spec.log_bytes_in_region,
1751 ENTRIES,
1752 "Heap range size and MetadataByteArray size does not match"
1753 );
1754 Self {
1755 #[cfg(feature = "extreme_assertions")]
1756 heap_range_start: start,
1757 #[cfg(feature = "extreme_assertions")]
1758 spec: *metadata_spec,
1759 data: unsafe { &*address_to_meta_address(metadata_spec, start).to_ptr() },
1762 }
1763 }
1764
1765 #[allow(clippy::len_without_is_empty)]
1767 pub const fn len(&self) -> usize {
1768 ENTRIES
1769 }
1770
1771 #[allow(clippy::let_and_return)]
1773 pub fn get(&self, index: usize) -> u8 {
1774 #[cfg(feature = "extreme_assertions")]
1775 let _lock = sanity::SANITY_LOCK.lock().unwrap();
1776 let value = self.data[index];
1777 #[cfg(feature = "extreme_assertions")]
1778 {
1779 let data_addr = self.heap_range_start + (index << self.spec.log_bytes_in_region);
1780 sanity::verify_load::<u8>(&self.spec, data_addr, value);
1781 }
1782 value
1783 }
1784}
1785
1786#[cfg(test)]
1787mod tests {
1788 use super::*;
1789 use crate::mmap_anno_test;
1790 use crate::util::metadata::side_metadata::SideMetadataContext;
1791
1792 pub const ZERO_OFFSET: usize = 0;
1794
1795 #[test]
1796 fn calculate_reserved_pages_one_spec() {
1797 let spec = SideMetadataSpec {
1799 name: "test_spec",
1800 is_global: true,
1801 offset: ZERO_OFFSET,
1802 log_num_of_bits: 0,
1803 log_bytes_in_region: 3,
1804 };
1805 let side_metadata = SideMetadataContext {
1806 global: vec![spec],
1807 local: vec![],
1808 };
1809 assert_eq!(side_metadata.calculate_reserved_pages(0), 0);
1810 assert_eq!(side_metadata.calculate_reserved_pages(63), 1);
1811 assert_eq!(side_metadata.calculate_reserved_pages(64), 1);
1812 assert_eq!(side_metadata.calculate_reserved_pages(65), 2);
1813 assert_eq!(side_metadata.calculate_reserved_pages(1024), 16);
1814 }
1815
1816 #[test]
1817 fn calculate_reserved_pages_multi_specs() {
1818 let gspec = SideMetadataSpec {
1820 name: "gspec",
1821 is_global: true,
1822 offset: ZERO_OFFSET,
1823 log_num_of_bits: 0,
1824 log_bytes_in_region: 3,
1825 };
1826 let lspec = SideMetadataSpec {
1828 name: "lspec",
1829 is_global: false,
1830 offset: ZERO_OFFSET,
1831 log_num_of_bits: 1,
1832 log_bytes_in_region: 12,
1833 };
1834 let side_metadata = SideMetadataContext {
1835 global: vec![gspec],
1836 local: vec![lspec],
1837 };
1838 assert_eq!(side_metadata.calculate_reserved_pages(1024), 16 + 1);
1839 }
1840
1841 use crate::util::heap::layout::vm_layout;
1842 use crate::util::test_util::{serial_test, with_cleanup};
1843 use paste::paste;
1844
1845 const TEST_LOG_BYTES_IN_REGION: usize = 12;
1846
1847 fn test_side_metadata(
1848 log_bits: usize,
1849 f: impl Fn(&SideMetadataSpec, Address, Address) + std::panic::RefUnwindSafe,
1850 ) {
1851 serial_test(|| {
1852 core_test_initialize_side_metadata();
1853
1854 let spec = SideMetadataSpec {
1855 name: "Test Spec $tname",
1856 is_global: true,
1857 offset: 0,
1858 log_num_of_bits: log_bits,
1859 log_bytes_in_region: TEST_LOG_BYTES_IN_REGION, };
1861 let context = SideMetadataContext {
1862 global: vec![spec],
1863 local: vec![],
1864 };
1865 let mut sanity = SideMetadataSanity::new();
1866 sanity.verify_metadata_context("TestPolicy", &context);
1867
1868 let data_addr = vm_layout::vm_layout().heap_start;
1869 crate::MMAPPER
1871 .ensure_mapped(
1872 data_addr,
1873 1,
1874 HugePageSupport::No,
1875 MmapProtection::ReadWrite,
1876 mmap_anno_test!(),
1877 )
1878 .unwrap();
1879 let meta_addr = address_to_meta_address(&spec, data_addr);
1880 with_cleanup(
1881 || {
1882 let mmap_result =
1883 context.try_map_metadata_space(data_addr, BYTES_IN_PAGE, "test_space");
1884 assert!(mmap_result.is_ok(), "{:?}", mmap_result);
1885
1886 f(&spec, data_addr, meta_addr);
1887 },
1888 || {
1889 assert!(log_bits <= 6);
1891 let meta_ptr: *mut u64 = meta_addr.to_mut_ptr();
1892 unsafe { *meta_ptr = 0 };
1893
1894 sanity::reset();
1895 },
1896 )
1897 })
1898 }
1899
1900 fn max_value(log_bits: usize) -> u64 {
1901 (0..(1 << log_bits)).fold(0, |accum, x| accum + (1 << x))
1902 }
1903 #[test]
1904 fn test_max_value() {
1905 assert_eq!(max_value(0), 1);
1906 assert_eq!(max_value(1), 0b11);
1907 assert_eq!(max_value(2), 0b1111);
1908 assert_eq!(max_value(3), 255);
1909 assert_eq!(max_value(4), 65535);
1910 }
1911
1912 macro_rules! test_side_metadata_access {
1913 ($tname: ident, $type: ty, $log_bits: expr) => {
1914 paste!{
1915 #[test]
1916 fn [<$tname _load>]() {
1917 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1918 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1919
1920 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1922 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
1923
1924 let max_value: $type = max_value($log_bits) as _;
1926 unsafe { spec.store::<$type>(data_addr, max_value); }
1927 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, max_value);
1928 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
1929 assert_eq!(unsafe { *meta_ptr }, max_value);
1930 });
1931 }
1932
1933 #[test]
1934 fn [<$tname _store>]() {
1935 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1936 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1937 let max_value: $type = max_value($log_bits) as _;
1938
1939 unsafe { *meta_ptr = <$type>::MAX; }
1941 unsafe { spec.store::<$type>(data_addr, 0); }
1943 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1944 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1946 });
1947 }
1948
1949 #[test]
1950 fn [<$tname _atomic_store>]() {
1951 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1952 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1953 let max_value: $type = max_value($log_bits) as _;
1954
1955 unsafe { *meta_ptr = <$type>::MAX; }
1957 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1959 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1960 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1962 });
1963 }
1964
1965 #[test]
1966 fn [<$tname _compare_exchange_success>]() {
1967 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1968 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1969 let max_value: $type = max_value($log_bits) as _;
1970 unsafe { *meta_ptr = <$type>::MAX; }
1972 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1974
1975 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1976 assert_eq!(old_val, 1);
1977
1978 let new_val = 0;
1979 let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
1980 assert!(res.is_ok());
1981 assert_eq!(res.unwrap(), old_val, "old vals do not match");
1982
1983 let after_update = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1984 assert_eq!(after_update, new_val);
1985 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1987 });
1988 }
1989
1990 #[test]
1991 fn [<$tname _compare_exchange_fail>]() {
1992 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1993 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1994 unsafe { *meta_ptr = <$type>::MAX; }
1996 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1998
1999 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2000 assert_eq!(old_val, 1);
2001
2002 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2004 let bits_before_cas = unsafe { *meta_ptr };
2005
2006 let new_val = 0;
2007 let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
2008 assert!(res.is_err());
2009 assert_eq!(res.err().unwrap(), 0);
2010 let bits_after_cas = unsafe { *meta_ptr };
2011 assert_eq!(bits_before_cas, bits_after_cas);
2012 });
2013 }
2014
2015 #[test]
2016 fn [<$tname _fetch_add_1>]() {
2017 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2018 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2019 unsafe { *meta_ptr = <$type>::MAX; }
2021 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2023
2024 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2025
2026 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2027 assert_eq!(old_val_from_fetch, old_val);
2028
2029 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2030 assert_eq!(new_val, 1);
2031 });
2032 }
2033
2034 #[test]
2035 fn [<$tname _fetch_add_max>]() {
2036 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2037 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2038 let max_value: $type = max_value($log_bits) as _;
2039 unsafe { *meta_ptr = <$type>::MAX; }
2041 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2043
2044 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2045
2046 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2047 assert_eq!(old_val_from_fetch, old_val);
2048
2049 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2050 assert_eq!(new_val, max_value);
2051 });
2052 }
2053
2054 #[test]
2055 fn [<$tname _fetch_add_overflow>]() {
2056 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2057 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2058 let max_value: $type = max_value($log_bits) as _;
2059 unsafe { *meta_ptr = <$type>::MAX; }
2061 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2063
2064 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2065
2066 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2068 assert_eq!(old_val_from_fetch, old_val);
2069
2070 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2071 assert_eq!(new_val, 0);
2072 });
2073 }
2074
2075 #[test]
2076 fn [<$tname _fetch_sub_1>]() {
2077 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2078 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2079 unsafe { *meta_ptr = <$type>::MAX; }
2081 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2083
2084 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2085
2086 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2087 assert_eq!(old_val_from_fetch, old_val);
2088
2089 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2090 assert_eq!(new_val, 0);
2091 });
2092 }
2093
2094 #[test]
2095 fn [<$tname _fetch_sub_max>]() {
2096 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2097 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2098 let max_value: $type = max_value($log_bits) as _;
2099 unsafe { *meta_ptr = <$type>::MAX; }
2101 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2103
2104 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2105
2106 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2107 assert_eq!(old_val_from_fetch, old_val);
2108
2109 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2110 assert_eq!(new_val, 0);
2111 });
2112 }
2113
2114 #[test]
2115 fn [<$tname _fetch_sub_overflow>]() {
2116 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2117 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2118 let max_value: $type = max_value($log_bits) as _;
2119 unsafe { *meta_ptr = <$type>::MAX; }
2121 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2123
2124 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2125
2126 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2128 assert_eq!(old_val_from_fetch, old_val);
2129
2130 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2131 assert_eq!(new_val, max_value);
2132 });
2133 }
2134
2135 #[test]
2136 fn [<$tname _fetch_and>]() {
2137 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2138 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2139 let max_value: $type = max_value($log_bits) as _;
2140 unsafe { *meta_ptr = <$type>::MAX; }
2142 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2144
2145 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2147 let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2148 assert_eq!(old_val_from_fetch, old_val, "old values do not match");
2149 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value, "load values do not match");
2150 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX, "raw values do not match");
2151
2152 let last_bit_zero = max_value - 1;
2154 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2155 let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, last_bit_zero, Ordering::SeqCst);
2156 assert_eq!(old_val_from_fetch, old_val);
2157 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), last_bit_zero);
2158 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX - 1);
2159 });
2160 }
2161
2162 #[test]
2163 fn [<$tname _fetch_or>]() {
2164 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2165 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2166 let max_value: $type = max_value($log_bits) as _;
2167 unsafe { *meta_ptr = 0; }
2169 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2171
2172 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2174 let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2175 assert_eq!(old_val_from_fetch, old_val);
2176 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2177 assert_eq!(unsafe { *meta_ptr }, 0);
2178
2179 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2181 let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2182 assert_eq!(old_val_from_fetch, old_val);
2183 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2184 assert_eq!(unsafe { *meta_ptr }, max_value);
2185 });
2186 }
2187
2188 #[test]
2189 fn [<$tname _fetch_update_success>]() {
2190 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2191 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2192 let max_value: $type = max_value($log_bits) as _;
2193 unsafe { *meta_ptr = <$type>::MAX; }
2195 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2197
2198 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2200 let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| Some(0));
2201 assert!(fetch_res.is_ok());
2202 assert_eq!(fetch_res.unwrap(), old_val);
2203 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2204 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
2206 });
2207 }
2208
2209 #[test]
2210 fn [<$tname _fetch_update_fail>]() {
2211 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2212 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2213 let max_value: $type = max_value($log_bits) as _;
2214 unsafe { *meta_ptr = <$type>::MAX; }
2216 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2218
2219 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2221 let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| None);
2222 assert!(fetch_res.is_err());
2223 assert_eq!(fetch_res.err().unwrap(), old_val);
2224 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2225 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX);
2227 });
2228 }
2229
2230 #[test]
2231 fn [<$tname _find_prev_non_zero_value_easy>]() {
2232 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2233 let max_value: $type = max_value($log_bits) as _;
2234 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2236
2237 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(data_addr, 8) };
2240 assert!(res_addr.is_some());
2241 assert_eq!(res_addr.unwrap(), data_addr);
2242 });
2243 }
2244
2245 #[test]
2246 fn [<$tname _find_prev_non_zero_value_arbitrary_bytes>]() {
2247 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2248 let max_value: $type = max_value($log_bits) as _;
2249 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2251
2252 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2254 for len in 1..(test_region*4) {
2255 let start_addr = data_addr + len;
2256 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2258 assert!(res_addr.is_some());
2259 assert_eq!(res_addr.unwrap(), data_addr);
2260 }
2261 });
2262 }
2263
2264 #[test]
2265 fn [<$tname _find_prev_non_zero_value_arbitrary_start>]() {
2266 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2267 let max_value: $type = max_value($log_bits) as _;
2268
2269 for offset in 0..7usize {
2271 let test_data_addr = data_addr + offset;
2273 spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2274
2275 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(test_data_addr, 4096) };
2277 assert!(res_addr.is_some());
2278 assert_eq!(res_addr.unwrap(), data_addr);
2279
2280 spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2282 }
2283 });
2284 }
2285
2286 #[test]
2287 fn [<$tname _find_prev_non_zero_value_no_find>]() {
2288 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2289 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2291
2292 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2294 for len in 1..(test_region*4) {
2295 let start_addr = data_addr + len;
2296 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2298 assert!(res_addr.is_none());
2299 }
2300 });
2301 }
2302
2303 #[test]
2304 fn [<$tname _find_next_non_zero_value_easy>]() {
2305 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2306 let max_value: $type = max_value($log_bits) as _;
2307 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2309
2310 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(data_addr, 8) };
2313 assert!(res_addr.is_some());
2314 assert_eq!(res_addr.unwrap(), data_addr);
2315 });
2316 }
2317
2318 #[test]
2319 fn [<$tname _find_next_non_zero_value_arbitrary_bytes>]() {
2320 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2321 let max_value: $type = max_value($log_bits) as _;
2322 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2323
2324 let data_addr = data_addr + test_region*4;
2327
2328 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2330 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2331
2332 for len in 1..(test_region*4) {
2334 let start_addr = data_addr - len;
2335 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2337 assert!(res_addr.is_some());
2338 assert_eq!(res_addr.unwrap(), data_addr);
2339 }
2340 });
2341 }
2342
2343 #[test]
2344 fn [<$tname _find_next_non_zero_value_arbitrary_start>]() {
2345 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2346 let max_value: $type = max_value($log_bits) as _;
2347
2348 for offset in 0..7usize {
2350 let test_data_addr = data_addr + offset;
2352 spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2353
2354 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(test_data_addr, 4096) };
2356 assert!(res_addr.is_some());
2357 assert_eq!(res_addr.unwrap(), data_addr);
2358
2359 spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2361 }
2362 });
2363 }
2364
2365 #[test]
2366 fn [<$tname _find_next_non_zero_value_no_find>]() {
2367 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2368 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2370
2371 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2373 for len in 1..(test_region*4) {
2374 let start_addr = data_addr - len;
2375 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2377 assert!(res_addr.is_none());
2378 }
2379 });
2380 }
2381 }
2382 }
2383 }
2384
2385 test_side_metadata_access!(test_u1, u8, 0);
2386 test_side_metadata_access!(test_u2, u8, 1);
2387 test_side_metadata_access!(test_u4, u8, 2);
2388 test_side_metadata_access!(test_u8, u8, 3);
2389 test_side_metadata_access!(test_u16, u16, 4);
2390 test_side_metadata_access!(test_u32, u32, 5);
2391 test_side_metadata_access!(test_u64, u64, 6);
2392 test_side_metadata_access!(
2393 test_usize,
2394 usize,
2395 if cfg!(target_pointer_width = "64") {
2396 6
2397 } else if cfg!(target_pointer_width = "32") {
2398 5
2399 } else {
2400 unreachable!()
2401 }
2402 );
2403
2404 #[test]
2405 fn test_bulk_update_meta_bits() {
2406 let raw_mem =
2407 unsafe { std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(8, 8).unwrap()) };
2408 let addr = Address::from_mut_ptr(raw_mem);
2409
2410 SideMetadataSpec::set_meta_bits(addr, 0, addr, 4);
2411 assert_eq!(unsafe { addr.load::<u64>() }, 0b1111);
2412
2413 SideMetadataSpec::zero_meta_bits(addr, 1, addr, 3);
2414 assert_eq!(unsafe { addr.load::<u64>() }, 0b1001);
2415
2416 SideMetadataSpec::set_meta_bits(addr, 2, addr, 6);
2417 assert_eq!(unsafe { addr.load::<u64>() }, 0b0011_1101);
2418
2419 SideMetadataSpec::zero_meta_bits(addr, 0, addr + 1usize, 0);
2420 assert_eq!(unsafe { addr.load::<u64>() }, 0b0);
2421
2422 SideMetadataSpec::set_meta_bits(addr, 2, addr + 1usize, 2);
2423 assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1100);
2424
2425 SideMetadataSpec::set_meta_bits(addr, 0, addr + 1usize, 2);
2426 assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1111);
2427 }
2428}