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)]
1033 pub unsafe fn find_prev_non_zero_value<T: MetadataValue>(
1034 &self,
1035 data_addr: Address,
1036 search_limit_bytes: usize,
1037 ) -> Option<Address> {
1038 debug_assert!(search_limit_bytes > 0);
1039
1040 if self.uses_contiguous_side_metadata() {
1041 let result = self.find_prev_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1043 #[cfg(debug_assertions)]
1044 {
1045 let result2 =
1047 self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1048 assert_eq!(result, result2, "find_prev_non_zero_value_fast returned a diffrent result from the naive implementation.");
1049 }
1050 result
1051 } else {
1052 warn!("We are trying to search non zero bits in an discontiguous side metadata. The performance is slow, as MMTk does not optimize for this case.");
1056 self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1057 }
1058 }
1059
1060 fn find_prev_non_zero_value_simple<T: MetadataValue>(
1061 &self,
1062 data_addr: Address,
1063 search_limit_bytes: usize,
1064 ) -> Option<Address> {
1065 let region_bytes = 1 << self.log_bytes_in_region;
1066 let start_addr = data_addr.align_down(region_bytes);
1068 let end_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1069
1070 let mmap_granularity = MMAPPER.granularity();
1071 let mut mapped_grain = Address::MAX;
1072
1073 let mut cursor = start_addr;
1074 while cursor >= end_addr {
1075 if cursor < mapped_grain {
1078 if cursor.is_mapped() {
1079 mapped_grain = cursor.align_down(mmap_granularity);
1080 } else {
1081 return None;
1083 }
1084 }
1085 if !unsafe { self.load::<T>(cursor).is_zero() } {
1087 return Some(cursor);
1088 }
1089 cursor -= region_bytes;
1090 }
1091 None
1092 }
1093
1094 #[allow(clippy::let_and_return)]
1095 fn find_prev_non_zero_value_fast<T: MetadataValue>(
1096 &self,
1097 data_addr: Address,
1098 search_limit_bytes: usize,
1099 ) -> Option<Address> {
1100 debug_assert!(self.uses_contiguous_side_metadata());
1101
1102 if !data_addr.is_mapped() {
1104 return None;
1105 }
1106 if !unsafe { self.load::<T>(data_addr).is_zero() } {
1108 return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1109 }
1110
1111 let start_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1113 let end_addr = data_addr;
1114
1115 let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1119 let start_meta_shift = meta_byte_lshift(self, start_addr);
1120 let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1121 let end_meta_shift = meta_byte_lshift(self, end_addr);
1122
1123 let mut res = None;
1124
1125 let mut visitor = |range: BitByteRange| {
1126 match range {
1127 BitByteRange::Bytes { start, end } => {
1128 match helpers::find_last_non_zero_bit_in_metadata_bytes(start, end) {
1129 helpers::FindMetaBitResult::Found { addr, bit } => {
1130 let (addr, bit) = align_metadata_address(self, addr, bit);
1131 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1132 true
1134 }
1135 helpers::FindMetaBitResult::UnmappedMetadata => true,
1137 helpers::FindMetaBitResult::NotFound => false,
1139 }
1140 }
1141 BitByteRange::BitsInByte {
1142 addr,
1143 bit_start,
1144 bit_end,
1145 } => {
1146 match helpers::find_last_non_zero_bit_in_metadata_bits(addr, bit_start, bit_end)
1147 {
1148 helpers::FindMetaBitResult::Found { addr, bit } => {
1149 let (addr, bit) = align_metadata_address(self, addr, bit);
1150 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1151 true
1153 }
1154 helpers::FindMetaBitResult::UnmappedMetadata => true,
1156 helpers::FindMetaBitResult::NotFound => false,
1158 }
1159 }
1160 }
1161 };
1162
1163 ranges::break_bit_range(
1164 start_meta_addr,
1165 start_meta_shift,
1166 end_meta_addr,
1167 end_meta_shift,
1168 false,
1169 &mut visitor,
1170 );
1171
1172 res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1177 .filter(|addr| *addr >= start_addr && *addr < end_addr)
1178 }
1179
1180 #[allow(clippy::let_and_return)]
1194 pub unsafe fn find_next_non_zero_value<T: MetadataValue>(
1195 &self,
1196 data_addr: Address,
1197 search_limit_bytes: usize,
1198 ) -> Option<Address> {
1199 debug_assert!(search_limit_bytes > 0);
1200
1201 if self.uses_contiguous_side_metadata() {
1202 let result = self.find_next_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1204 #[cfg(debug_assertions)]
1205 {
1206 let result2 =
1208 self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1209 assert_eq!(
1210 result,
1211 result2,
1212 "find_next_non_zero_value_fast returned a different result from the naive implementation. data_addr {}, search_limit_bytes {}",
1213 data_addr, search_limit_bytes,
1214 );
1215 }
1216 result
1217 } else {
1218 warn!("We are trying to search non zero bits in an discontiguous side metadata. The performance is slow, as MMTk does not optimize for this case.");
1222 self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1223 }
1224 }
1225
1226 fn find_next_non_zero_value_simple<T: MetadataValue>(
1227 &self,
1228 data_addr: Address,
1229 search_limit_bytes: usize,
1230 ) -> Option<Address> {
1231 let region_bytes = 1 << self.log_bytes_in_region;
1232 let start_addr = data_addr.align_down(region_bytes);
1234 let end_addr = data_addr + search_limit_bytes;
1235
1236 let mmap_granularity = MMAPPER.granularity();
1237 let mut mapped_grain = Address::ZERO;
1238
1239 let mut cursor = start_addr;
1240 while cursor < end_addr {
1241 if cursor > mapped_grain {
1244 if cursor.is_mapped() {
1245 mapped_grain = cursor.align_up(mmap_granularity) - 0x1;
1246 } else {
1247 return None;
1249 }
1250 }
1251 if !unsafe { self.load::<T>(cursor).is_zero() } {
1253 return Some(cursor);
1254 }
1255 cursor += region_bytes;
1256 }
1257 None
1258 }
1259
1260 fn find_next_non_zero_value_fast<T: MetadataValue>(
1261 &self,
1262 data_addr: Address,
1263 search_limit_bytes: usize,
1264 ) -> Option<Address> {
1265 debug_assert!(self.uses_contiguous_side_metadata());
1266
1267 if !data_addr.is_mapped() {
1269 return None;
1270 }
1271 if !unsafe { self.load::<T>(data_addr).is_zero() } {
1273 return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1274 }
1275
1276 let start_addr = data_addr.align_down(1 << self.log_bytes_in_region);
1278 let end_addr = (data_addr + search_limit_bytes).align_up(1 << self.log_bytes_in_region);
1283
1284 let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1288 let start_meta_shift = meta_byte_lshift(self, start_addr);
1289 let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1290 let end_meta_shift = meta_byte_lshift(self, end_addr);
1291
1292 let mut res = None;
1293
1294 let mut visitor = |range: BitByteRange| {
1295 match range {
1296 BitByteRange::Bytes { start, end } => {
1297 match helpers::find_first_non_zero_bit_in_metadata_bytes(start, end) {
1298 helpers::FindMetaBitResult::Found { addr, bit } => {
1299 let (addr, bit) = align_metadata_address(self, addr, bit);
1300 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1301 true
1303 }
1304 helpers::FindMetaBitResult::UnmappedMetadata => true,
1306 helpers::FindMetaBitResult::NotFound => false,
1308 }
1309 }
1310 BitByteRange::BitsInByte {
1311 addr,
1312 bit_start,
1313 bit_end,
1314 } => {
1315 match helpers::find_first_non_zero_bit_in_metadata_bits(
1316 addr, bit_start, bit_end,
1317 ) {
1318 helpers::FindMetaBitResult::Found { addr, bit } => {
1319 let (addr, bit) = align_metadata_address(self, addr, bit);
1320 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1321 true
1323 }
1324 helpers::FindMetaBitResult::UnmappedMetadata => true,
1326 helpers::FindMetaBitResult::NotFound => false,
1328 }
1329 }
1330 }
1331 };
1332
1333 ranges::break_bit_range(
1334 start_meta_addr,
1335 start_meta_shift,
1336 end_meta_addr,
1337 end_meta_shift,
1338 true,
1339 &mut visitor,
1340 );
1341
1342 res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1347 .filter(|addr| *addr >= start_addr && *addr < end_addr)
1348 }
1349
1350 pub fn scan_non_zero_values<T: MetadataValue>(
1360 &self,
1361 data_start_addr: Address,
1362 data_end_addr: Address,
1363 visit_data: &mut impl FnMut(Address),
1364 ) {
1365 if self.uses_contiguous_side_metadata() && self.log_num_of_bits == 0 {
1366 self.scan_non_zero_values_fast(data_start_addr, data_end_addr, visit_data);
1371 } else {
1372 warn!(
1377 "We are trying to search for non zero bits in a discontiguous side metadata \
1378 or the metadata has more than one bit per region. \
1379 The performance is slow, as MMTk does not optimize for this case."
1380 );
1381 self.scan_non_zero_values_simple::<T>(data_start_addr, data_end_addr, visit_data);
1382 }
1383 }
1384
1385 fn scan_non_zero_values_simple<T: MetadataValue>(
1386 &self,
1387 data_start_addr: Address,
1388 data_end_addr: Address,
1389 visit_data: &mut impl FnMut(Address),
1390 ) {
1391 let region_bytes = 1usize << self.log_bytes_in_region;
1392
1393 let mut cursor = data_start_addr;
1394 while cursor < data_end_addr {
1395 debug_assert!(cursor.is_mapped());
1396
1397 if !unsafe { self.load::<T>(cursor).is_zero() } {
1399 visit_data(cursor);
1400 }
1401 cursor += region_bytes;
1402 }
1403 }
1404
1405 fn scan_non_zero_values_fast(
1406 &self,
1407 data_start_addr: Address,
1408 data_end_addr: Address,
1409 visit_data: &mut impl FnMut(Address),
1410 ) {
1411 debug_assert!(self.uses_contiguous_side_metadata());
1412 debug_assert_eq!(self.log_num_of_bits, 0);
1413
1414 let start_meta_addr = address_to_contiguous_meta_address(self, data_start_addr);
1416 let start_meta_shift = meta_byte_lshift(self, data_start_addr);
1417 let end_meta_addr = address_to_contiguous_meta_address(self, data_end_addr);
1418 let end_meta_shift = meta_byte_lshift(self, data_end_addr);
1419
1420 let mut visitor = |range| {
1421 match range {
1422 BitByteRange::Bytes { start, end } => {
1423 helpers::scan_non_zero_bits_in_metadata_bytes(start, end, &mut |addr, bit| {
1424 visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1425 });
1426 }
1427 BitByteRange::BitsInByte {
1428 addr,
1429 bit_start,
1430 bit_end,
1431 } => helpers::scan_non_zero_bits_in_metadata_bits(
1432 addr,
1433 bit_start,
1434 bit_end,
1435 &mut |addr, bit| {
1436 visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1437 },
1438 ),
1439 }
1440 false
1441 };
1442
1443 ranges::break_bit_range(
1444 start_meta_addr,
1445 start_meta_shift,
1446 end_meta_addr,
1447 end_meta_shift,
1448 true,
1449 &mut visitor,
1450 );
1451 }
1452}
1453
1454impl fmt::Debug for SideMetadataSpec {
1455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1456 f.write_fmt(format_args!(
1457 "SideMetadataSpec {} {{ \
1458 **is_global: {:?} \
1459 **offset: 0x{:x} \
1460 **log_num_of_bits: 0x{:x} \
1461 **log_bytes_in_region: 0x{:x} \
1462 }}",
1463 self.name, self.is_global, self.offset, self.log_num_of_bits, self.log_bytes_in_region
1464 ))
1465 }
1466}
1467
1468pub const fn side_metadata_offset_after(spec: &SideMetadataSpec) -> usize {
1471 raw_align_up(spec.upper_bound_offset(), BYTES_IN_WORD)
1480}
1481
1482pub(crate) struct SideMetadataContext {
1485 pub global: Vec<SideMetadataSpec>,
1487 pub local: Vec<SideMetadataSpec>,
1489}
1490
1491impl SideMetadataContext {
1492 #[allow(clippy::vec_init_then_push)] pub fn new_global_specs(specs: &[SideMetadataSpec]) -> Vec<SideMetadataSpec> {
1494 let mut ret = vec![];
1495
1496 #[cfg(feature = "vo_bit")]
1497 ret.push(VO_BIT_SIDE_METADATA_SPEC);
1498
1499 if let Some(spec) = crate::mmtk::SFT_MAP.get_side_metadata() {
1500 if spec.is_global {
1501 ret.push(*spec);
1502 }
1503 }
1504
1505 ret.push(crate::util::heap::chunk_map::ChunkMap::ALLOC_TABLE);
1509
1510 ret.extend_from_slice(specs);
1511 ret
1512 }
1513
1514 pub fn get_local_specs(&self) -> &[SideMetadataSpec] {
1515 &self.local
1516 }
1517
1518 #[cfg(debug_assertions)]
1519 pub fn assert_metadata_ranges_in_reserved_range(
1520 &self,
1521 start: Address,
1522 size: usize,
1523 space_name: &str,
1524 ) {
1525 let reserved = {
1526 let base = super::layout::global_side_metadata_base_address();
1527 let bytes = super::layout::side_metadata_reserved_bytes();
1528 base..(base + bytes)
1529 };
1530 let check_spec = |spec: &SideMetadataSpec| {
1531 if !spec.uses_contiguous_side_metadata() {
1532 return;
1533 }
1534 let metadata_start = address_to_meta_address(spec, start);
1535 let mmap_start = metadata_start.align_down(BYTES_IN_PAGE);
1536 let metadata_size = data_to_meta_size_round_up(spec, size);
1537 let mmap_end = (metadata_start + metadata_size).align_up(BYTES_IN_PAGE);
1538 debug_assert!(
1539 mmap_start >= reserved.start && mmap_end <= reserved.end,
1540 "Side metadata range for spec {} in space {} is outside reserved range: [{}, {}) vs [{}, {})",
1541 spec.name,
1542 space_name,
1543 mmap_start,
1544 mmap_end,
1545 reserved.start,
1546 reserved.end
1547 );
1548 };
1549 self.global.iter().for_each(check_spec);
1550 self.local.iter().for_each(check_spec);
1551 }
1552
1553 pub fn calculate_reserved_pages(&self, data_pages: usize) -> usize {
1560 let mut total = 0;
1561 for spec in self.global.iter() {
1562 total += data_to_meta_size_round_up(spec, data_pages);
1566 }
1567 for spec in self.local.iter() {
1568 total += data_to_meta_size_round_up(spec, data_pages);
1569 }
1570 total
1571 }
1572
1573 pub fn try_map_metadata_space(
1579 &self,
1580 start: Address,
1581 size: usize,
1582 space_name: &str,
1583 ) -> MmapResult<()> {
1584 debug!(
1585 "try_map_metadata_space({}, 0x{:x}, {}, {})",
1586 start,
1587 size,
1588 self.global.len(),
1589 self.local.len()
1590 );
1591 debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1593 debug_assert!(size % BYTES_IN_PAGE == 0);
1594 self.map_metadata_internal(start, size, false, space_name)
1595 }
1596
1597 pub fn try_map_metadata_address_range(
1602 &self,
1603 start: Address,
1604 size: usize,
1605 name: &str,
1606 ) -> MmapResult<()> {
1607 debug!(
1608 "try_map_metadata_address_range({}, 0x{:x}, {}, {})",
1609 start,
1610 size,
1611 self.global.len(),
1612 self.local.len()
1613 );
1614 debug_assert!(start.is_aligned_to(BYTES_IN_CHUNK));
1616 debug_assert!(size % BYTES_IN_CHUNK == 0);
1617 self.map_metadata_internal(start, size, true, name)
1618 }
1619
1620 fn map_metadata_internal(
1628 &self,
1629 start: Address,
1630 size: usize,
1631 no_reserve: bool,
1632 space_name: &str,
1633 ) -> MmapResult<()> {
1634 for spec in self.global.iter() {
1635 let anno = MmapAnnotation::SideMeta {
1636 space: space_name,
1637 meta: spec.name,
1638 };
1639 try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1640 }
1641
1642 #[cfg(target_pointer_width = "32")]
1643 let mut lsize: usize = 0;
1644
1645 for spec in self.local.iter() {
1646 #[cfg(target_pointer_width = "64")]
1658 {
1659 let anno = MmapAnnotation::SideMeta {
1660 space: space_name,
1661 meta: spec.name,
1662 };
1663 try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1664 }
1665 #[cfg(target_pointer_width = "32")]
1666 {
1667 lsize += metadata_bytes_per_chunk(spec.log_bytes_in_region, spec.log_num_of_bits);
1668 }
1669 }
1670
1671 #[cfg(target_pointer_width = "32")]
1672 if lsize > 0 {
1673 let max = BYTES_IN_CHUNK >> super::layout::LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO;
1674 debug_assert!(
1675 lsize <= max,
1676 "local side metadata per chunk (0x{:x}) must be less than (0x{:x})",
1677 lsize,
1678 max
1679 );
1680 let anno = MmapAnnotation::SideMeta {
1683 space: space_name,
1684 meta: "all",
1685 };
1686 try_map_per_chunk_metadata_space(start, size, lsize, no_reserve, &anno)?;
1687 }
1688
1689 Ok(())
1690 }
1691
1692 #[cfg(test)]
1700 pub fn ensure_unmap_metadata_space(&self, start: Address, size: usize) {
1701 trace!("ensure_unmap_metadata_space({}, 0x{:x})", start, size);
1702 debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1703 debug_assert!(size % BYTES_IN_PAGE == 0);
1704
1705 for spec in self.global.iter() {
1706 ensure_munmap_contiguous_metadata_space(start, size, spec);
1707 }
1708
1709 for spec in self.local.iter() {
1710 #[cfg(target_pointer_width = "64")]
1711 {
1712 ensure_munmap_contiguous_metadata_space(start, size, spec);
1713 }
1714 #[cfg(target_pointer_width = "32")]
1715 {
1716 ensure_munmap_chunked_metadata_space(start, size, spec);
1717 }
1718 }
1719 }
1720}
1721
1722pub struct MetadataByteArrayRef<const ENTRIES: usize> {
1724 #[cfg(feature = "extreme_assertions")]
1725 heap_range_start: Address,
1726 #[cfg(feature = "extreme_assertions")]
1727 spec: SideMetadataSpec,
1728 data: &'static [u8; ENTRIES],
1729}
1730
1731impl<const ENTRIES: usize> MetadataByteArrayRef<ENTRIES> {
1732 pub fn new(metadata_spec: &SideMetadataSpec, start: Address, bytes: usize) -> Self {
1741 debug_assert_eq!(
1742 metadata_spec.log_num_of_bits, LOG_BITS_IN_BYTE as usize,
1743 "Each heap entry should map to a byte in side-metadata"
1744 );
1745 debug_assert_eq!(
1746 bytes >> metadata_spec.log_bytes_in_region,
1747 ENTRIES,
1748 "Heap range size and MetadataByteArray size does not match"
1749 );
1750 Self {
1751 #[cfg(feature = "extreme_assertions")]
1752 heap_range_start: start,
1753 #[cfg(feature = "extreme_assertions")]
1754 spec: *metadata_spec,
1755 data: unsafe { &*address_to_meta_address(metadata_spec, start).to_ptr() },
1758 }
1759 }
1760
1761 #[allow(clippy::len_without_is_empty)]
1763 pub const fn len(&self) -> usize {
1764 ENTRIES
1765 }
1766
1767 #[allow(clippy::let_and_return)]
1769 pub fn get(&self, index: usize) -> u8 {
1770 #[cfg(feature = "extreme_assertions")]
1771 let _lock = sanity::SANITY_LOCK.lock().unwrap();
1772 let value = self.data[index];
1773 #[cfg(feature = "extreme_assertions")]
1774 {
1775 let data_addr = self.heap_range_start + (index << self.spec.log_bytes_in_region);
1776 sanity::verify_load::<u8>(&self.spec, data_addr, value);
1777 }
1778 value
1779 }
1780}
1781
1782#[cfg(test)]
1783mod tests {
1784 use super::*;
1785 use crate::mmap_anno_test;
1786 use crate::util::metadata::side_metadata::SideMetadataContext;
1787
1788 pub const ZERO_OFFSET: usize = 0;
1790
1791 #[test]
1792 fn calculate_reserved_pages_one_spec() {
1793 let spec = SideMetadataSpec {
1795 name: "test_spec",
1796 is_global: true,
1797 offset: ZERO_OFFSET,
1798 log_num_of_bits: 0,
1799 log_bytes_in_region: 3,
1800 };
1801 let side_metadata = SideMetadataContext {
1802 global: vec![spec],
1803 local: vec![],
1804 };
1805 assert_eq!(side_metadata.calculate_reserved_pages(0), 0);
1806 assert_eq!(side_metadata.calculate_reserved_pages(63), 1);
1807 assert_eq!(side_metadata.calculate_reserved_pages(64), 1);
1808 assert_eq!(side_metadata.calculate_reserved_pages(65), 2);
1809 assert_eq!(side_metadata.calculate_reserved_pages(1024), 16);
1810 }
1811
1812 #[test]
1813 fn calculate_reserved_pages_multi_specs() {
1814 let gspec = SideMetadataSpec {
1816 name: "gspec",
1817 is_global: true,
1818 offset: ZERO_OFFSET,
1819 log_num_of_bits: 0,
1820 log_bytes_in_region: 3,
1821 };
1822 let lspec = SideMetadataSpec {
1824 name: "lspec",
1825 is_global: false,
1826 offset: ZERO_OFFSET,
1827 log_num_of_bits: 1,
1828 log_bytes_in_region: 12,
1829 };
1830 let side_metadata = SideMetadataContext {
1831 global: vec![gspec],
1832 local: vec![lspec],
1833 };
1834 assert_eq!(side_metadata.calculate_reserved_pages(1024), 16 + 1);
1835 }
1836
1837 use crate::util::heap::layout::vm_layout;
1838 use crate::util::test_util::{serial_test, with_cleanup};
1839 use paste::paste;
1840
1841 const TEST_LOG_BYTES_IN_REGION: usize = 12;
1842
1843 fn test_side_metadata(
1844 log_bits: usize,
1845 f: impl Fn(&SideMetadataSpec, Address, Address) + std::panic::RefUnwindSafe,
1846 ) {
1847 serial_test(|| {
1848 core_test_initialize_side_metadata();
1849
1850 let spec = SideMetadataSpec {
1851 name: "Test Spec $tname",
1852 is_global: true,
1853 offset: 0,
1854 log_num_of_bits: log_bits,
1855 log_bytes_in_region: TEST_LOG_BYTES_IN_REGION, };
1857 let context = SideMetadataContext {
1858 global: vec![spec],
1859 local: vec![],
1860 };
1861 let mut sanity = SideMetadataSanity::new();
1862 sanity.verify_metadata_context("TestPolicy", &context);
1863
1864 let data_addr = vm_layout::vm_layout().heap_start;
1865 crate::MMAPPER
1867 .ensure_mapped(
1868 data_addr,
1869 1,
1870 HugePageSupport::No,
1871 MmapProtection::ReadWrite,
1872 mmap_anno_test!(),
1873 )
1874 .unwrap();
1875 let meta_addr = address_to_meta_address(&spec, data_addr);
1876 with_cleanup(
1877 || {
1878 let mmap_result =
1879 context.try_map_metadata_space(data_addr, BYTES_IN_PAGE, "test_space");
1880 assert!(mmap_result.is_ok(), "{:?}", mmap_result);
1881
1882 f(&spec, data_addr, meta_addr);
1883 },
1884 || {
1885 assert!(log_bits <= 6);
1887 let meta_ptr: *mut u64 = meta_addr.to_mut_ptr();
1888 unsafe { *meta_ptr = 0 };
1889
1890 sanity::reset();
1891 },
1892 )
1893 })
1894 }
1895
1896 fn max_value(log_bits: usize) -> u64 {
1897 (0..(1 << log_bits)).fold(0, |accum, x| accum + (1 << x))
1898 }
1899 #[test]
1900 fn test_max_value() {
1901 assert_eq!(max_value(0), 1);
1902 assert_eq!(max_value(1), 0b11);
1903 assert_eq!(max_value(2), 0b1111);
1904 assert_eq!(max_value(3), 255);
1905 assert_eq!(max_value(4), 65535);
1906 }
1907
1908 macro_rules! test_side_metadata_access {
1909 ($tname: ident, $type: ty, $log_bits: expr) => {
1910 paste!{
1911 #[test]
1912 fn [<$tname _load>]() {
1913 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1914 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1915
1916 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1918 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
1919
1920 let max_value: $type = max_value($log_bits) as _;
1922 unsafe { spec.store::<$type>(data_addr, max_value); }
1923 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, max_value);
1924 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
1925 assert_eq!(unsafe { *meta_ptr }, max_value);
1926 });
1927 }
1928
1929 #[test]
1930 fn [<$tname _store>]() {
1931 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1932 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1933 let max_value: $type = max_value($log_bits) as _;
1934
1935 unsafe { *meta_ptr = <$type>::MAX; }
1937 unsafe { spec.store::<$type>(data_addr, 0); }
1939 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1940 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1942 });
1943 }
1944
1945 #[test]
1946 fn [<$tname _atomic_store>]() {
1947 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1948 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1949 let max_value: $type = max_value($log_bits) as _;
1950
1951 unsafe { *meta_ptr = <$type>::MAX; }
1953 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1955 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1956 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1958 });
1959 }
1960
1961 #[test]
1962 fn [<$tname _compare_exchange_success>]() {
1963 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1964 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1965 let max_value: $type = max_value($log_bits) as _;
1966 unsafe { *meta_ptr = <$type>::MAX; }
1968 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1970
1971 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1972 assert_eq!(old_val, 1);
1973
1974 let new_val = 0;
1975 let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
1976 assert!(res.is_ok());
1977 assert_eq!(res.unwrap(), old_val, "old vals do not match");
1978
1979 let after_update = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1980 assert_eq!(after_update, new_val);
1981 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1983 });
1984 }
1985
1986 #[test]
1987 fn [<$tname _compare_exchange_fail>]() {
1988 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1989 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1990 unsafe { *meta_ptr = <$type>::MAX; }
1992 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1994
1995 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1996 assert_eq!(old_val, 1);
1997
1998 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2000 let bits_before_cas = unsafe { *meta_ptr };
2001
2002 let new_val = 0;
2003 let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
2004 assert!(res.is_err());
2005 assert_eq!(res.err().unwrap(), 0);
2006 let bits_after_cas = unsafe { *meta_ptr };
2007 assert_eq!(bits_before_cas, bits_after_cas);
2008 });
2009 }
2010
2011 #[test]
2012 fn [<$tname _fetch_add_1>]() {
2013 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2014 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2015 unsafe { *meta_ptr = <$type>::MAX; }
2017 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2019
2020 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2021
2022 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2023 assert_eq!(old_val_from_fetch, old_val);
2024
2025 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2026 assert_eq!(new_val, 1);
2027 });
2028 }
2029
2030 #[test]
2031 fn [<$tname _fetch_add_max>]() {
2032 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2033 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2034 let max_value: $type = max_value($log_bits) as _;
2035 unsafe { *meta_ptr = <$type>::MAX; }
2037 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2039
2040 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2041
2042 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2043 assert_eq!(old_val_from_fetch, old_val);
2044
2045 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2046 assert_eq!(new_val, max_value);
2047 });
2048 }
2049
2050 #[test]
2051 fn [<$tname _fetch_add_overflow>]() {
2052 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2053 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2054 let max_value: $type = max_value($log_bits) as _;
2055 unsafe { *meta_ptr = <$type>::MAX; }
2057 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2059
2060 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2061
2062 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2064 assert_eq!(old_val_from_fetch, old_val);
2065
2066 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2067 assert_eq!(new_val, 0);
2068 });
2069 }
2070
2071 #[test]
2072 fn [<$tname _fetch_sub_1>]() {
2073 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2074 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2075 unsafe { *meta_ptr = <$type>::MAX; }
2077 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2079
2080 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2081
2082 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2083 assert_eq!(old_val_from_fetch, old_val);
2084
2085 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2086 assert_eq!(new_val, 0);
2087 });
2088 }
2089
2090 #[test]
2091 fn [<$tname _fetch_sub_max>]() {
2092 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2093 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2094 let max_value: $type = max_value($log_bits) as _;
2095 unsafe { *meta_ptr = <$type>::MAX; }
2097 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2099
2100 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2101
2102 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2103 assert_eq!(old_val_from_fetch, old_val);
2104
2105 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2106 assert_eq!(new_val, 0);
2107 });
2108 }
2109
2110 #[test]
2111 fn [<$tname _fetch_sub_overflow>]() {
2112 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2113 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2114 let max_value: $type = max_value($log_bits) as _;
2115 unsafe { *meta_ptr = <$type>::MAX; }
2117 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2119
2120 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2121
2122 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2124 assert_eq!(old_val_from_fetch, old_val);
2125
2126 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2127 assert_eq!(new_val, max_value);
2128 });
2129 }
2130
2131 #[test]
2132 fn [<$tname _fetch_and>]() {
2133 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2134 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2135 let max_value: $type = max_value($log_bits) as _;
2136 unsafe { *meta_ptr = <$type>::MAX; }
2138 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2140
2141 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2143 let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2144 assert_eq!(old_val_from_fetch, old_val, "old values do not match");
2145 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value, "load values do not match");
2146 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX, "raw values do not match");
2147
2148 let last_bit_zero = max_value - 1;
2150 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2151 let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, last_bit_zero, Ordering::SeqCst);
2152 assert_eq!(old_val_from_fetch, old_val);
2153 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), last_bit_zero);
2154 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX - 1);
2155 });
2156 }
2157
2158 #[test]
2159 fn [<$tname _fetch_or>]() {
2160 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2161 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2162 let max_value: $type = max_value($log_bits) as _;
2163 unsafe { *meta_ptr = 0; }
2165 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2167
2168 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2170 let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2171 assert_eq!(old_val_from_fetch, old_val);
2172 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2173 assert_eq!(unsafe { *meta_ptr }, 0);
2174
2175 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2177 let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2178 assert_eq!(old_val_from_fetch, old_val);
2179 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2180 assert_eq!(unsafe { *meta_ptr }, max_value);
2181 });
2182 }
2183
2184 #[test]
2185 fn [<$tname _fetch_update_success>]() {
2186 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2187 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2188 let max_value: $type = max_value($log_bits) as _;
2189 unsafe { *meta_ptr = <$type>::MAX; }
2191 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2193
2194 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2196 let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| Some(0));
2197 assert!(fetch_res.is_ok());
2198 assert_eq!(fetch_res.unwrap(), old_val);
2199 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2200 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
2202 });
2203 }
2204
2205 #[test]
2206 fn [<$tname _fetch_update_fail>]() {
2207 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2208 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2209 let max_value: $type = max_value($log_bits) as _;
2210 unsafe { *meta_ptr = <$type>::MAX; }
2212 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2214
2215 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2217 let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| None);
2218 assert!(fetch_res.is_err());
2219 assert_eq!(fetch_res.err().unwrap(), old_val);
2220 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2221 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX);
2223 });
2224 }
2225
2226 #[test]
2227 fn [<$tname _find_prev_non_zero_value_easy>]() {
2228 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2229 let max_value: $type = max_value($log_bits) as _;
2230 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2232
2233 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(data_addr, 8) };
2236 assert!(res_addr.is_some());
2237 assert_eq!(res_addr.unwrap(), data_addr);
2238 });
2239 }
2240
2241 #[test]
2242 fn [<$tname _find_prev_non_zero_value_arbitrary_bytes>]() {
2243 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2244 let max_value: $type = max_value($log_bits) as _;
2245 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2247
2248 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2250 for len in 1..(test_region*4) {
2251 let start_addr = data_addr + len;
2252 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2254 assert!(res_addr.is_some());
2255 assert_eq!(res_addr.unwrap(), data_addr);
2256 }
2257 });
2258 }
2259
2260 #[test]
2261 fn [<$tname _find_prev_non_zero_value_arbitrary_start>]() {
2262 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2263 let max_value: $type = max_value($log_bits) as _;
2264
2265 for offset in 0..7usize {
2267 let test_data_addr = data_addr + offset;
2269 spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2270
2271 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(test_data_addr, 4096) };
2273 assert!(res_addr.is_some());
2274 assert_eq!(res_addr.unwrap(), data_addr);
2275
2276 spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2278 }
2279 });
2280 }
2281
2282 #[test]
2283 fn [<$tname _find_prev_non_zero_value_no_find>]() {
2284 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2285 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2287
2288 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2290 for len in 1..(test_region*4) {
2291 let start_addr = data_addr + len;
2292 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2294 assert!(res_addr.is_none());
2295 }
2296 });
2297 }
2298
2299 #[test]
2300 fn [<$tname _find_next_non_zero_value_easy>]() {
2301 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2302 let max_value: $type = max_value($log_bits) as _;
2303 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2305
2306 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(data_addr, 8) };
2309 assert!(res_addr.is_some());
2310 assert_eq!(res_addr.unwrap(), data_addr);
2311 });
2312 }
2313
2314 #[test]
2315 fn [<$tname _find_next_non_zero_value_arbitrary_bytes>]() {
2316 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2317 let max_value: $type = max_value($log_bits) as _;
2318 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2319
2320 let data_addr = data_addr + test_region*4;
2323
2324 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2326 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2327
2328 for len in 1..(test_region*4) {
2330 let start_addr = data_addr - len;
2331 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2333 assert!(res_addr.is_some());
2334 assert_eq!(res_addr.unwrap(), data_addr);
2335 }
2336 });
2337 }
2338
2339 #[test]
2340 fn [<$tname _find_next_non_zero_value_arbitrary_start>]() {
2341 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2342 let max_value: $type = max_value($log_bits) as _;
2343
2344 for offset in 0..7usize {
2346 let test_data_addr = data_addr + offset;
2348 spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2349
2350 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(test_data_addr, 4096) };
2352 assert!(res_addr.is_some());
2353 assert_eq!(res_addr.unwrap(), data_addr);
2354
2355 spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2357 }
2358 });
2359 }
2360
2361 #[test]
2362 fn [<$tname _find_next_non_zero_value_no_find>]() {
2363 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2364 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2366
2367 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2369 for len in 1..(test_region*4) {
2370 let start_addr = data_addr - len;
2371 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2373 assert!(res_addr.is_none());
2374 }
2375 });
2376 }
2377 }
2378 }
2379 }
2380
2381 test_side_metadata_access!(test_u1, u8, 0);
2382 test_side_metadata_access!(test_u2, u8, 1);
2383 test_side_metadata_access!(test_u4, u8, 2);
2384 test_side_metadata_access!(test_u8, u8, 3);
2385 test_side_metadata_access!(test_u16, u16, 4);
2386 test_side_metadata_access!(test_u32, u32, 5);
2387 test_side_metadata_access!(test_u64, u64, 6);
2388 test_side_metadata_access!(
2389 test_usize,
2390 usize,
2391 if cfg!(target_pointer_width = "64") {
2392 6
2393 } else if cfg!(target_pointer_width = "32") {
2394 5
2395 } else {
2396 unreachable!()
2397 }
2398 );
2399
2400 #[test]
2401 fn test_bulk_update_meta_bits() {
2402 let raw_mem =
2403 unsafe { std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(8, 8).unwrap()) };
2404 let addr = Address::from_mut_ptr(raw_mem);
2405
2406 SideMetadataSpec::set_meta_bits(addr, 0, addr, 4);
2407 assert_eq!(unsafe { addr.load::<u64>() }, 0b1111);
2408
2409 SideMetadataSpec::zero_meta_bits(addr, 1, addr, 3);
2410 assert_eq!(unsafe { addr.load::<u64>() }, 0b1001);
2411
2412 SideMetadataSpec::set_meta_bits(addr, 2, addr, 6);
2413 assert_eq!(unsafe { addr.load::<u64>() }, 0b0011_1101);
2414
2415 SideMetadataSpec::zero_meta_bits(addr, 0, addr + 1usize, 0);
2416 assert_eq!(unsafe { addr.load::<u64>() }, 0b0);
2417
2418 SideMetadataSpec::set_meta_bits(addr, 2, addr + 1usize, 2);
2419 assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1100);
2420
2421 SideMetadataSpec::set_meta_bits(addr, 0, addr + 1usize, 2);
2422 assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1111);
2423 }
2424}