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_atomic<T: MetadataValue>(&self, data_addr: Address, order: Ordering) -> T {
543 self.side_metadata_access::<true, T, _, _, _>(
544 data_addr,
545 None,
546 || {
547 let meta_addr = address_to_meta_address(self, data_addr);
548 let bits_num_log = self.log_num_of_bits;
549 if bits_num_log < 3 {
550 let lshift = meta_byte_lshift(self, data_addr);
551 let mask = meta_byte_mask(self) << lshift;
552 let byte_val = unsafe { meta_addr.atomic_load::<AtomicU8>(order) };
553 FromPrimitive::from_u8((byte_val & mask) >> lshift).unwrap()
554 } else {
555 unsafe { T::load_atomic(meta_addr, order) }
556 }
557 },
558 |_v| {
559 #[cfg(feature = "extreme_assertions")]
560 sanity::verify_load(self, data_addr, _v);
561 },
562 )
563 }
564
565 pub fn store_atomic<T: MetadataValue>(&self, data_addr: Address, metadata: T, order: Ordering) {
568 self.side_metadata_access::<true, T, _, _, _>(
569 data_addr,
570 Some(metadata),
571 || {
572 let meta_addr = address_to_meta_address(self, data_addr);
573 let bits_num_log = self.log_num_of_bits;
574 if bits_num_log < 3 {
575 let lshift = meta_byte_lshift(self, data_addr);
576 let mask = meta_byte_mask(self) << lshift;
577 let metadata_u8 = metadata.to_u8().unwrap();
578 let _ = unsafe {
579 <u8 as MetadataValue>::fetch_update(meta_addr, order, order, |v: u8| {
580 Some((v & !mask) | (metadata_u8 << lshift))
581 })
582 };
583 } else {
584 unsafe {
585 T::store_atomic(meta_addr, metadata, order);
586 }
587 }
588 },
589 |_| {
590 #[cfg(feature = "extreme_assertions")]
591 sanity::verify_store(self, data_addr, metadata);
592 },
593 )
594 }
595
596 pub unsafe fn set_zero(&self, data_addr: Address) {
606 use num_traits::Zero;
607 match self.log_num_of_bits {
608 0..=3 => self.store(data_addr, u8::zero()),
609 4 => self.store(data_addr, u16::zero()),
610 5 => self.store(data_addr, u32::zero()),
611 6 => self.store(data_addr, u64::zero()),
612 _ => unreachable!(),
613 }
614 }
615
616 pub fn set_zero_atomic(&self, data_addr: Address, order: Ordering) {
619 use num_traits::Zero;
620 match self.log_num_of_bits {
621 0..=3 => self.store_atomic(data_addr, u8::zero(), order),
622 4 => self.store_atomic(data_addr, u16::zero(), order),
623 5 => self.store_atomic(data_addr, u32::zero(), order),
624 6 => self.store_atomic(data_addr, u64::zero(), order),
625 _ => unreachable!(),
626 }
627 }
628
629 pub unsafe fn set_raw_byte_atomic(&self, data_addr: Address, order: Ordering) {
640 debug_assert!(self.log_num_of_bits < 3);
641 cfg_if::cfg_if! {
642 if #[cfg(feature = "extreme_assertions")] {
643 self.store_atomic::<u8>(data_addr, 1, order)
645 } else {
646 self.side_metadata_access::<false, u8, _, _, _>(
647 data_addr,
648 Some(1u8),
649 || {
650 let meta_addr = address_to_meta_address(self, data_addr);
651 u8::store_atomic(meta_addr, 0xffu8, order);
652 },
653 |_| {}
654 )
655 }
656 }
657 }
658
659 pub unsafe fn load_raw_byte(&self, data_addr: Address) -> u8 {
667 debug_assert!(self.log_num_of_bits < 3);
668 self.side_metadata_access::<false, u8, _, _, _>(
669 data_addr,
670 None,
671 || {
672 let meta_addr = address_to_meta_address(self, data_addr);
673 meta_addr.load::<u8>()
674 },
675 |_| {},
676 )
677 }
678
679 pub unsafe fn load_raw_word(&self, data_addr: Address) -> usize {
687 use crate::util::constants::*;
688 debug_assert!(self.log_num_of_bits < (LOG_BITS_IN_BYTE + LOG_BYTES_IN_ADDRESS) as usize);
689 self.side_metadata_access::<false, usize, _, _, _>(
690 data_addr,
691 None,
692 || {
693 let meta_addr = address_to_meta_address(self, data_addr);
694 let aligned_meta_addr = meta_addr.align_down(BYTES_IN_ADDRESS);
695 aligned_meta_addr.load::<usize>()
696 },
697 |_| {},
698 )
699 }
700
701 pub fn compare_exchange_atomic<T: MetadataValue>(
706 &self,
707 data_addr: Address,
708 old_metadata: T,
709 new_metadata: T,
710 success_order: Ordering,
711 failure_order: Ordering,
712 ) -> std::result::Result<T, T> {
713 self.side_metadata_access::<true, T, _, _, _>(
714 data_addr,
715 Some(new_metadata),
716 || {
717 let meta_addr = address_to_meta_address(self, data_addr);
718 let bits_num_log = self.log_num_of_bits;
719 if bits_num_log < 3 {
720 let lshift = meta_byte_lshift(self, data_addr);
721 let mask = meta_byte_mask(self) << lshift;
722
723 let real_old_byte = unsafe { meta_addr.atomic_load::<AtomicU8>(success_order) };
724 let expected_old_byte =
725 (real_old_byte & !mask) | ((old_metadata.to_u8().unwrap()) << lshift);
726 let expected_new_byte =
727 (expected_old_byte & !mask) | ((new_metadata.to_u8().unwrap()) << lshift);
728
729 unsafe {
730 meta_addr.compare_exchange::<AtomicU8>(
731 expected_old_byte,
732 expected_new_byte,
733 success_order,
734 failure_order,
735 )
736 }
737 .map(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
738 .map_err(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
739 } else {
740 unsafe {
741 T::compare_exchange(
742 meta_addr,
743 old_metadata,
744 new_metadata,
745 success_order,
746 failure_order,
747 )
748 }
749 }
750 },
751 |_res| {
752 #[cfg(feature = "extreme_assertions")]
753 if _res.is_ok() {
754 sanity::verify_store(self, data_addr, new_metadata);
755 }
756 },
757 )
758 }
759
760 fn fetch_ops_on_bits<F: Fn(u8) -> u8>(
763 &self,
764 data_addr: Address,
765 meta_addr: Address,
766 set_order: Ordering,
767 fetch_order: Ordering,
768 update: F,
769 ) -> u8 {
770 let lshift = meta_byte_lshift(self, data_addr);
771 let mask = meta_byte_mask(self) << lshift;
772
773 let old_raw_byte = unsafe {
774 <u8 as MetadataValue>::fetch_update(
775 meta_addr,
776 set_order,
777 fetch_order,
778 |raw_byte: u8| {
779 let old_val = (raw_byte & mask) >> lshift;
780 let new_val = update(old_val);
781 let new_raw_byte = (raw_byte & !mask) | ((new_val << lshift) & mask);
782 Some(new_raw_byte)
783 },
784 )
785 }
786 .unwrap();
787 (old_raw_byte & mask) >> lshift
788 }
789
790 pub fn fetch_add_atomic<T: MetadataValue>(
794 &self,
795 data_addr: Address,
796 val: T,
797 order: Ordering,
798 ) -> T {
799 self.side_metadata_access::<true, T, _, _, _>(
800 data_addr,
801 Some(val),
802 || {
803 let meta_addr = address_to_meta_address(self, data_addr);
804 let bits_num_log = self.log_num_of_bits;
805 if bits_num_log < 3 {
806 FromPrimitive::from_u8(self.fetch_ops_on_bits(
807 data_addr,
808 meta_addr,
809 order,
810 order,
811 |x: u8| x.wrapping_add(val.to_u8().unwrap()),
812 ))
813 .unwrap()
814 } else {
815 unsafe { T::fetch_add(meta_addr, val, order) }
816 }
817 },
818 |_old_val| {
819 #[cfg(feature = "extreme_assertions")]
820 sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.wrapping_add(&val))
821 },
822 )
823 }
824
825 pub fn fetch_sub_atomic<T: MetadataValue>(
829 &self,
830 data_addr: Address,
831 val: T,
832 order: Ordering,
833 ) -> T {
834 self.side_metadata_access::<true, T, _, _, _>(
835 data_addr,
836 Some(val),
837 || {
838 let meta_addr = address_to_meta_address(self, data_addr);
839 if self.log_num_of_bits < 3 {
840 FromPrimitive::from_u8(self.fetch_ops_on_bits(
841 data_addr,
842 meta_addr,
843 order,
844 order,
845 |x: u8| x.wrapping_sub(val.to_u8().unwrap()),
846 ))
847 .unwrap()
848 } else {
849 unsafe { T::fetch_sub(meta_addr, val, order) }
850 }
851 },
852 |_old_val| {
853 #[cfg(feature = "extreme_assertions")]
854 sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.wrapping_sub(&val))
855 },
856 )
857 }
858
859 pub fn fetch_and_atomic<T: MetadataValue>(
863 &self,
864 data_addr: Address,
865 val: T,
866 order: Ordering,
867 ) -> T {
868 self.side_metadata_access::<true, T, _, _, _>(
869 data_addr,
870 Some(val),
871 || {
872 let meta_addr = address_to_meta_address(self, data_addr);
873 if self.log_num_of_bits < 3 {
874 let lshift = meta_byte_lshift(self, data_addr);
875 let mask = meta_byte_mask(self) << lshift;
876 let rhs = (val.to_u8().unwrap() << lshift) | !mask;
878 let old_raw_byte =
879 unsafe { <u8 as MetadataValue>::fetch_and(meta_addr, rhs, order) };
880 let old_val = (old_raw_byte & mask) >> lshift;
881 FromPrimitive::from_u8(old_val).unwrap()
882 } else {
883 unsafe { T::fetch_and(meta_addr, val, order) }
884 }
885 },
886 |_old_val| {
887 #[cfg(feature = "extreme_assertions")]
888 sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.bitand(val))
889 },
890 )
891 }
892
893 pub fn fetch_or_atomic<T: MetadataValue>(
897 &self,
898 data_addr: Address,
899 val: T,
900 order: Ordering,
901 ) -> T {
902 self.side_metadata_access::<true, T, _, _, _>(
903 data_addr,
904 Some(val),
905 || {
906 let meta_addr = address_to_meta_address(self, data_addr);
907 if self.log_num_of_bits < 3 {
908 let lshift = meta_byte_lshift(self, data_addr);
909 let mask = meta_byte_mask(self) << lshift;
910 let rhs = (val.to_u8().unwrap() << lshift) & mask;
912 let old_raw_byte =
913 unsafe { <u8 as MetadataValue>::fetch_or(meta_addr, rhs, order) };
914 let old_val = (old_raw_byte & mask) >> lshift;
915 FromPrimitive::from_u8(old_val).unwrap()
916 } else {
917 unsafe { T::fetch_or(meta_addr, val, order) }
918 }
919 },
920 |_old_val| {
921 #[cfg(feature = "extreme_assertions")]
922 sanity::verify_update::<T>(self, data_addr, _old_val, _old_val.bitor(val))
923 },
924 )
925 }
926
927 pub fn fetch_update_atomic<T: MetadataValue, F: FnMut(T) -> Option<T> + Copy>(
931 &self,
932 data_addr: Address,
933 set_order: Ordering,
934 fetch_order: Ordering,
935 mut f: F,
936 ) -> std::result::Result<T, T> {
937 self.side_metadata_access::<true, T, _, _, _>(
938 data_addr,
939 None,
940 move || -> std::result::Result<T, T> {
941 let meta_addr = address_to_meta_address(self, data_addr);
942 if self.log_num_of_bits < 3 {
943 let lshift = meta_byte_lshift(self, data_addr);
944 let mask = meta_byte_mask(self) << lshift;
945
946 unsafe {
947 <u8 as MetadataValue>::fetch_update(
948 meta_addr,
949 set_order,
950 fetch_order,
951 |raw_byte: u8| {
952 let old_val = (raw_byte & mask) >> lshift;
953 f(FromPrimitive::from_u8(old_val).unwrap()).map(|new_val| {
954 (raw_byte & !mask)
955 | ((new_val.to_u8().unwrap() << lshift) & mask)
956 })
957 },
958 )
959 }
960 .map(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
961 .map_err(|x| FromPrimitive::from_u8((x & mask) >> lshift).unwrap())
962 } else {
963 unsafe { T::fetch_update(meta_addr, set_order, fetch_order, f) }
964 }
965 },
966 |_result| {
967 #[cfg(feature = "extreme_assertions")]
968 if let Ok(old_val) = _result {
969 sanity::verify_update::<T>(self, data_addr, old_val, f(old_val).unwrap())
970 }
971 },
972 )
973 }
974
975 #[allow(clippy::let_and_return)]
989 pub unsafe fn find_prev_non_zero_value<T: MetadataValue>(
990 &self,
991 data_addr: Address,
992 search_limit_bytes: usize,
993 ) -> Option<Address> {
994 debug_assert!(search_limit_bytes > 0);
995
996 if self.uses_contiguous_side_metadata() {
997 let result = self.find_prev_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
999 #[cfg(debug_assertions)]
1000 {
1001 let result2 =
1003 self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1004 assert_eq!(result, result2, "find_prev_non_zero_value_fast returned a diffrent result from the naive implementation.");
1005 }
1006 result
1007 } else {
1008 warn!("We are trying to search non zero bits in an discontiguous side metadata. The performance is slow, as MMTk does not optimize for this case.");
1012 self.find_prev_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1013 }
1014 }
1015
1016 fn find_prev_non_zero_value_simple<T: MetadataValue>(
1017 &self,
1018 data_addr: Address,
1019 search_limit_bytes: usize,
1020 ) -> Option<Address> {
1021 let region_bytes = 1 << self.log_bytes_in_region;
1022 let start_addr = data_addr.align_down(region_bytes);
1024 let end_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1025
1026 let mmap_granularity = MMAPPER.granularity();
1027 let mut mapped_grain = Address::MAX;
1028
1029 let mut cursor = start_addr;
1030 while cursor >= end_addr {
1031 if cursor < mapped_grain {
1034 if cursor.is_mapped() {
1035 mapped_grain = cursor.align_down(mmap_granularity);
1036 } else {
1037 return None;
1039 }
1040 }
1041 if !unsafe { self.load::<T>(cursor).is_zero() } {
1043 return Some(cursor);
1044 }
1045 cursor -= region_bytes;
1046 }
1047 None
1048 }
1049
1050 #[allow(clippy::let_and_return)]
1051 fn find_prev_non_zero_value_fast<T: MetadataValue>(
1052 &self,
1053 data_addr: Address,
1054 search_limit_bytes: usize,
1055 ) -> Option<Address> {
1056 debug_assert!(self.uses_contiguous_side_metadata());
1057
1058 if !data_addr.is_mapped() {
1060 return None;
1061 }
1062 if !unsafe { self.load::<T>(data_addr).is_zero() } {
1064 return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1065 }
1066
1067 let start_addr = data_addr.saturating_sub(search_limit_bytes) + 1usize;
1069 let end_addr = data_addr;
1070
1071 let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1075 let start_meta_shift = meta_byte_lshift(self, start_addr);
1076 let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1077 let end_meta_shift = meta_byte_lshift(self, end_addr);
1078
1079 let mut res = None;
1080
1081 let mut visitor = |range: BitByteRange| {
1082 match range {
1083 BitByteRange::Bytes { start, end } => {
1084 match helpers::find_last_non_zero_bit_in_metadata_bytes(start, end) {
1085 helpers::FindMetaBitResult::Found { addr, bit } => {
1086 let (addr, bit) = align_metadata_address(self, addr, bit);
1087 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1088 true
1090 }
1091 helpers::FindMetaBitResult::UnmappedMetadata => true,
1093 helpers::FindMetaBitResult::NotFound => false,
1095 }
1096 }
1097 BitByteRange::BitsInByte {
1098 addr,
1099 bit_start,
1100 bit_end,
1101 } => {
1102 match helpers::find_last_non_zero_bit_in_metadata_bits(addr, bit_start, bit_end)
1103 {
1104 helpers::FindMetaBitResult::Found { addr, bit } => {
1105 let (addr, bit) = align_metadata_address(self, addr, bit);
1106 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1107 true
1109 }
1110 helpers::FindMetaBitResult::UnmappedMetadata => true,
1112 helpers::FindMetaBitResult::NotFound => false,
1114 }
1115 }
1116 }
1117 };
1118
1119 ranges::break_bit_range(
1120 start_meta_addr,
1121 start_meta_shift,
1122 end_meta_addr,
1123 end_meta_shift,
1124 false,
1125 &mut visitor,
1126 );
1127
1128 res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1133 .filter(|addr| *addr >= start_addr && *addr < end_addr)
1134 }
1135
1136 #[allow(clippy::let_and_return)]
1150 pub unsafe fn find_next_non_zero_value<T: MetadataValue>(
1151 &self,
1152 data_addr: Address,
1153 search_limit_bytes: usize,
1154 ) -> Option<Address> {
1155 debug_assert!(search_limit_bytes > 0);
1156
1157 if self.uses_contiguous_side_metadata() {
1158 let result = self.find_next_non_zero_value_fast::<T>(data_addr, search_limit_bytes);
1160 #[cfg(debug_assertions)]
1161 {
1162 let result2 =
1164 self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes);
1165 assert_eq!(
1166 result,
1167 result2,
1168 "find_next_non_zero_value_fast returned a different result from the naive implementation. data_addr {}, search_limit_bytes {}",
1169 data_addr, search_limit_bytes,
1170 );
1171 }
1172 result
1173 } else {
1174 warn!("We are trying to search non zero bits in an discontiguous side metadata. The performance is slow, as MMTk does not optimize for this case.");
1178 self.find_next_non_zero_value_simple::<T>(data_addr, search_limit_bytes)
1179 }
1180 }
1181
1182 fn find_next_non_zero_value_simple<T: MetadataValue>(
1183 &self,
1184 data_addr: Address,
1185 search_limit_bytes: usize,
1186 ) -> Option<Address> {
1187 let region_bytes = 1 << self.log_bytes_in_region;
1188 let start_addr = data_addr.align_down(region_bytes);
1190 let end_addr = data_addr + search_limit_bytes;
1191
1192 let mmap_granularity = MMAPPER.granularity();
1193 let mut mapped_grain = Address::ZERO;
1194
1195 let mut cursor = start_addr;
1196 while cursor < end_addr {
1197 if cursor > mapped_grain {
1200 if cursor.is_mapped() {
1201 mapped_grain = cursor.align_up(mmap_granularity) - 0x1;
1202 } else {
1203 return None;
1205 }
1206 }
1207 if !unsafe { self.load::<T>(cursor).is_zero() } {
1209 return Some(cursor);
1210 }
1211 cursor += region_bytes;
1212 }
1213 None
1214 }
1215
1216 fn find_next_non_zero_value_fast<T: MetadataValue>(
1217 &self,
1218 data_addr: Address,
1219 search_limit_bytes: usize,
1220 ) -> Option<Address> {
1221 debug_assert!(self.uses_contiguous_side_metadata());
1222
1223 if !data_addr.is_mapped() {
1225 return None;
1226 }
1227 if !unsafe { self.load::<T>(data_addr).is_zero() } {
1229 return Some(data_addr.align_down(1 << self.log_bytes_in_region));
1230 }
1231
1232 let start_addr = data_addr.align_down(1 << self.log_bytes_in_region);
1234 let end_addr = (data_addr + search_limit_bytes).align_up(1 << self.log_bytes_in_region);
1239
1240 let start_meta_addr = address_to_contiguous_meta_address(self, start_addr);
1244 let start_meta_shift = meta_byte_lshift(self, start_addr);
1245 let end_meta_addr = address_to_contiguous_meta_address(self, end_addr);
1246 let end_meta_shift = meta_byte_lshift(self, end_addr);
1247
1248 let mut res = None;
1249
1250 let mut visitor = |range: BitByteRange| {
1251 match range {
1252 BitByteRange::Bytes { start, end } => {
1253 match helpers::find_first_non_zero_bit_in_metadata_bytes(start, end) {
1254 helpers::FindMetaBitResult::Found { addr, bit } => {
1255 let (addr, bit) = align_metadata_address(self, addr, bit);
1256 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1257 true
1259 }
1260 helpers::FindMetaBitResult::UnmappedMetadata => true,
1262 helpers::FindMetaBitResult::NotFound => false,
1264 }
1265 }
1266 BitByteRange::BitsInByte {
1267 addr,
1268 bit_start,
1269 bit_end,
1270 } => {
1271 match helpers::find_first_non_zero_bit_in_metadata_bits(
1272 addr, bit_start, bit_end,
1273 ) {
1274 helpers::FindMetaBitResult::Found { addr, bit } => {
1275 let (addr, bit) = align_metadata_address(self, addr, bit);
1276 res = Some(contiguous_meta_address_to_address(self, addr, bit));
1277 true
1279 }
1280 helpers::FindMetaBitResult::UnmappedMetadata => true,
1282 helpers::FindMetaBitResult::NotFound => false,
1284 }
1285 }
1286 }
1287 };
1288
1289 ranges::break_bit_range(
1290 start_meta_addr,
1291 start_meta_shift,
1292 end_meta_addr,
1293 end_meta_shift,
1294 true,
1295 &mut visitor,
1296 );
1297
1298 res.map(|addr| addr.align_down(1 << self.log_bytes_in_region))
1303 .filter(|addr| *addr >= start_addr && *addr < end_addr)
1304 }
1305
1306 pub fn scan_non_zero_values<T: MetadataValue>(
1316 &self,
1317 data_start_addr: Address,
1318 data_end_addr: Address,
1319 visit_data: &mut impl FnMut(Address),
1320 ) {
1321 if self.uses_contiguous_side_metadata() && self.log_num_of_bits == 0 {
1322 self.scan_non_zero_values_fast(data_start_addr, data_end_addr, visit_data);
1327 } else {
1328 warn!(
1333 "We are trying to search for non zero bits in a discontiguous side metadata \
1334 or the metadata has more than one bit per region. \
1335 The performance is slow, as MMTk does not optimize for this case."
1336 );
1337 self.scan_non_zero_values_simple::<T>(data_start_addr, data_end_addr, visit_data);
1338 }
1339 }
1340
1341 fn scan_non_zero_values_simple<T: MetadataValue>(
1342 &self,
1343 data_start_addr: Address,
1344 data_end_addr: Address,
1345 visit_data: &mut impl FnMut(Address),
1346 ) {
1347 let region_bytes = 1usize << self.log_bytes_in_region;
1348
1349 let mut cursor = data_start_addr;
1350 while cursor < data_end_addr {
1351 debug_assert!(cursor.is_mapped());
1352
1353 if !unsafe { self.load::<T>(cursor).is_zero() } {
1355 visit_data(cursor);
1356 }
1357 cursor += region_bytes;
1358 }
1359 }
1360
1361 fn scan_non_zero_values_fast(
1362 &self,
1363 data_start_addr: Address,
1364 data_end_addr: Address,
1365 visit_data: &mut impl FnMut(Address),
1366 ) {
1367 debug_assert!(self.uses_contiguous_side_metadata());
1368 debug_assert_eq!(self.log_num_of_bits, 0);
1369
1370 let start_meta_addr = address_to_contiguous_meta_address(self, data_start_addr);
1372 let start_meta_shift = meta_byte_lshift(self, data_start_addr);
1373 let end_meta_addr = address_to_contiguous_meta_address(self, data_end_addr);
1374 let end_meta_shift = meta_byte_lshift(self, data_end_addr);
1375
1376 let mut visitor = |range| {
1377 match range {
1378 BitByteRange::Bytes { start, end } => {
1379 helpers::scan_non_zero_bits_in_metadata_bytes(start, end, &mut |addr, bit| {
1380 visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1381 });
1382 }
1383 BitByteRange::BitsInByte {
1384 addr,
1385 bit_start,
1386 bit_end,
1387 } => helpers::scan_non_zero_bits_in_metadata_bits(
1388 addr,
1389 bit_start,
1390 bit_end,
1391 &mut |addr, bit| {
1392 visit_data(helpers::contiguous_meta_address_to_address(self, addr, bit));
1393 },
1394 ),
1395 }
1396 false
1397 };
1398
1399 ranges::break_bit_range(
1400 start_meta_addr,
1401 start_meta_shift,
1402 end_meta_addr,
1403 end_meta_shift,
1404 true,
1405 &mut visitor,
1406 );
1407 }
1408}
1409
1410impl fmt::Debug for SideMetadataSpec {
1411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1412 f.write_fmt(format_args!(
1413 "SideMetadataSpec {} {{ \
1414 **is_global: {:?} \
1415 **offset: 0x{:x} \
1416 **log_num_of_bits: 0x{:x} \
1417 **log_bytes_in_region: 0x{:x} \
1418 }}",
1419 self.name, self.is_global, self.offset, self.log_num_of_bits, self.log_bytes_in_region
1420 ))
1421 }
1422}
1423
1424pub const fn side_metadata_offset_after(spec: &SideMetadataSpec) -> usize {
1427 raw_align_up(spec.upper_bound_offset(), BYTES_IN_WORD)
1436}
1437
1438pub(crate) struct SideMetadataContext {
1441 pub global: Vec<SideMetadataSpec>,
1443 pub local: Vec<SideMetadataSpec>,
1445}
1446
1447impl SideMetadataContext {
1448 #[allow(clippy::vec_init_then_push)] pub fn new_global_specs(specs: &[SideMetadataSpec]) -> Vec<SideMetadataSpec> {
1450 let mut ret = vec![];
1451
1452 #[cfg(feature = "vo_bit")]
1453 ret.push(VO_BIT_SIDE_METADATA_SPEC);
1454
1455 if let Some(spec) = crate::mmtk::SFT_MAP.get_side_metadata() {
1456 if spec.is_global {
1457 ret.push(*spec);
1458 }
1459 }
1460
1461 ret.push(crate::util::heap::chunk_map::ChunkMap::ALLOC_TABLE);
1465
1466 ret.extend_from_slice(specs);
1467 ret
1468 }
1469
1470 pub fn get_local_specs(&self) -> &[SideMetadataSpec] {
1471 &self.local
1472 }
1473
1474 #[cfg(debug_assertions)]
1475 pub fn assert_metadata_ranges_in_reserved_range(
1476 &self,
1477 start: Address,
1478 size: usize,
1479 space_name: &str,
1480 ) {
1481 let reserved = {
1482 let base = super::layout::global_side_metadata_base_address();
1483 let bytes = super::layout::side_metadata_reserved_bytes();
1484 base..(base + bytes)
1485 };
1486 let check_spec = |spec: &SideMetadataSpec| {
1487 if !spec.uses_contiguous_side_metadata() {
1488 return;
1489 }
1490 let metadata_start = address_to_meta_address(spec, start);
1491 let mmap_start = metadata_start.align_down(BYTES_IN_PAGE);
1492 let metadata_size = data_to_meta_size_round_up(spec, size);
1493 let mmap_end = (metadata_start + metadata_size).align_up(BYTES_IN_PAGE);
1494 debug_assert!(
1495 mmap_start >= reserved.start && mmap_end <= reserved.end,
1496 "Side metadata range for spec {} in space {} is outside reserved range: [{}, {}) vs [{}, {})",
1497 spec.name,
1498 space_name,
1499 mmap_start,
1500 mmap_end,
1501 reserved.start,
1502 reserved.end
1503 );
1504 };
1505 self.global.iter().for_each(check_spec);
1506 self.local.iter().for_each(check_spec);
1507 }
1508
1509 pub fn calculate_reserved_pages(&self, data_pages: usize) -> usize {
1516 let mut total = 0;
1517 for spec in self.global.iter() {
1518 total += data_to_meta_size_round_up(spec, data_pages);
1522 }
1523 for spec in self.local.iter() {
1524 total += data_to_meta_size_round_up(spec, data_pages);
1525 }
1526 total
1527 }
1528
1529 pub fn try_map_metadata_space(
1535 &self,
1536 start: Address,
1537 size: usize,
1538 space_name: &str,
1539 ) -> MmapResult<()> {
1540 debug!(
1541 "try_map_metadata_space({}, 0x{:x}, {}, {})",
1542 start,
1543 size,
1544 self.global.len(),
1545 self.local.len()
1546 );
1547 debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1549 debug_assert!(size % BYTES_IN_PAGE == 0);
1550 self.map_metadata_internal(start, size, false, space_name)
1551 }
1552
1553 pub fn try_map_metadata_address_range(
1558 &self,
1559 start: Address,
1560 size: usize,
1561 name: &str,
1562 ) -> MmapResult<()> {
1563 debug!(
1564 "try_map_metadata_address_range({}, 0x{:x}, {}, {})",
1565 start,
1566 size,
1567 self.global.len(),
1568 self.local.len()
1569 );
1570 debug_assert!(start.is_aligned_to(BYTES_IN_CHUNK));
1572 debug_assert!(size % BYTES_IN_CHUNK == 0);
1573 self.map_metadata_internal(start, size, true, name)
1574 }
1575
1576 fn map_metadata_internal(
1584 &self,
1585 start: Address,
1586 size: usize,
1587 no_reserve: bool,
1588 space_name: &str,
1589 ) -> MmapResult<()> {
1590 for spec in self.global.iter() {
1591 let anno = MmapAnnotation::SideMeta {
1592 space: space_name,
1593 meta: spec.name,
1594 };
1595 try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1596 }
1597
1598 #[cfg(target_pointer_width = "32")]
1599 let mut lsize: usize = 0;
1600
1601 for spec in self.local.iter() {
1602 #[cfg(target_pointer_width = "64")]
1614 {
1615 let anno = MmapAnnotation::SideMeta {
1616 space: space_name,
1617 meta: spec.name,
1618 };
1619 try_mmap_contiguous_metadata_space(start, size, spec, no_reserve, &anno)?;
1620 }
1621 #[cfg(target_pointer_width = "32")]
1622 {
1623 lsize += metadata_bytes_per_chunk(spec.log_bytes_in_region, spec.log_num_of_bits);
1624 }
1625 }
1626
1627 #[cfg(target_pointer_width = "32")]
1628 if lsize > 0 {
1629 let max = BYTES_IN_CHUNK >> super::layout::LOG_LOCAL_SIDE_METADATA_WORST_CASE_RATIO;
1630 debug_assert!(
1631 lsize <= max,
1632 "local side metadata per chunk (0x{:x}) must be less than (0x{:x})",
1633 lsize,
1634 max
1635 );
1636 let anno = MmapAnnotation::SideMeta {
1639 space: space_name,
1640 meta: "all",
1641 };
1642 try_map_per_chunk_metadata_space(start, size, lsize, no_reserve, &anno)?;
1643 }
1644
1645 Ok(())
1646 }
1647
1648 #[cfg(test)]
1656 pub fn ensure_unmap_metadata_space(&self, start: Address, size: usize) {
1657 trace!("ensure_unmap_metadata_space({}, 0x{:x})", start, size);
1658 debug_assert!(start.is_aligned_to(BYTES_IN_PAGE));
1659 debug_assert!(size % BYTES_IN_PAGE == 0);
1660
1661 for spec in self.global.iter() {
1662 ensure_munmap_contiguous_metadata_space(start, size, spec);
1663 }
1664
1665 for spec in self.local.iter() {
1666 #[cfg(target_pointer_width = "64")]
1667 {
1668 ensure_munmap_contiguous_metadata_space(start, size, spec);
1669 }
1670 #[cfg(target_pointer_width = "32")]
1671 {
1672 ensure_munmap_chunked_metadata_space(start, size, spec);
1673 }
1674 }
1675 }
1676}
1677
1678pub struct MetadataByteArrayRef<const ENTRIES: usize> {
1680 #[cfg(feature = "extreme_assertions")]
1681 heap_range_start: Address,
1682 #[cfg(feature = "extreme_assertions")]
1683 spec: SideMetadataSpec,
1684 data: &'static [u8; ENTRIES],
1685}
1686
1687impl<const ENTRIES: usize> MetadataByteArrayRef<ENTRIES> {
1688 pub fn new(metadata_spec: &SideMetadataSpec, start: Address, bytes: usize) -> Self {
1697 debug_assert_eq!(
1698 metadata_spec.log_num_of_bits, LOG_BITS_IN_BYTE as usize,
1699 "Each heap entry should map to a byte in side-metadata"
1700 );
1701 debug_assert_eq!(
1702 bytes >> metadata_spec.log_bytes_in_region,
1703 ENTRIES,
1704 "Heap range size and MetadataByteArray size does not match"
1705 );
1706 Self {
1707 #[cfg(feature = "extreme_assertions")]
1708 heap_range_start: start,
1709 #[cfg(feature = "extreme_assertions")]
1710 spec: *metadata_spec,
1711 data: unsafe { &*address_to_meta_address(metadata_spec, start).to_ptr() },
1714 }
1715 }
1716
1717 #[allow(clippy::len_without_is_empty)]
1719 pub const fn len(&self) -> usize {
1720 ENTRIES
1721 }
1722
1723 #[allow(clippy::let_and_return)]
1725 pub fn get(&self, index: usize) -> u8 {
1726 #[cfg(feature = "extreme_assertions")]
1727 let _lock = sanity::SANITY_LOCK.lock().unwrap();
1728 let value = self.data[index];
1729 #[cfg(feature = "extreme_assertions")]
1730 {
1731 let data_addr = self.heap_range_start + (index << self.spec.log_bytes_in_region);
1732 sanity::verify_load::<u8>(&self.spec, data_addr, value);
1733 }
1734 value
1735 }
1736}
1737
1738#[cfg(test)]
1739mod tests {
1740 use super::*;
1741 use crate::mmap_anno_test;
1742 use crate::util::metadata::side_metadata::SideMetadataContext;
1743
1744 pub const ZERO_OFFSET: usize = 0;
1746
1747 #[test]
1748 fn calculate_reserved_pages_one_spec() {
1749 let spec = SideMetadataSpec {
1751 name: "test_spec",
1752 is_global: true,
1753 offset: ZERO_OFFSET,
1754 log_num_of_bits: 0,
1755 log_bytes_in_region: 3,
1756 };
1757 let side_metadata = SideMetadataContext {
1758 global: vec![spec],
1759 local: vec![],
1760 };
1761 assert_eq!(side_metadata.calculate_reserved_pages(0), 0);
1762 assert_eq!(side_metadata.calculate_reserved_pages(63), 1);
1763 assert_eq!(side_metadata.calculate_reserved_pages(64), 1);
1764 assert_eq!(side_metadata.calculate_reserved_pages(65), 2);
1765 assert_eq!(side_metadata.calculate_reserved_pages(1024), 16);
1766 }
1767
1768 #[test]
1769 fn calculate_reserved_pages_multi_specs() {
1770 let gspec = SideMetadataSpec {
1772 name: "gspec",
1773 is_global: true,
1774 offset: ZERO_OFFSET,
1775 log_num_of_bits: 0,
1776 log_bytes_in_region: 3,
1777 };
1778 let lspec = SideMetadataSpec {
1780 name: "lspec",
1781 is_global: false,
1782 offset: ZERO_OFFSET,
1783 log_num_of_bits: 1,
1784 log_bytes_in_region: 12,
1785 };
1786 let side_metadata = SideMetadataContext {
1787 global: vec![gspec],
1788 local: vec![lspec],
1789 };
1790 assert_eq!(side_metadata.calculate_reserved_pages(1024), 16 + 1);
1791 }
1792
1793 use crate::util::heap::layout::vm_layout;
1794 use crate::util::test_util::{serial_test, with_cleanup};
1795 use paste::paste;
1796
1797 const TEST_LOG_BYTES_IN_REGION: usize = 12;
1798
1799 fn test_side_metadata(
1800 log_bits: usize,
1801 f: impl Fn(&SideMetadataSpec, Address, Address) + std::panic::RefUnwindSafe,
1802 ) {
1803 serial_test(|| {
1804 core_test_initialize_side_metadata();
1805
1806 let spec = SideMetadataSpec {
1807 name: "Test Spec $tname",
1808 is_global: true,
1809 offset: 0,
1810 log_num_of_bits: log_bits,
1811 log_bytes_in_region: TEST_LOG_BYTES_IN_REGION, };
1813 let context = SideMetadataContext {
1814 global: vec![spec],
1815 local: vec![],
1816 };
1817 let mut sanity = SideMetadataSanity::new();
1818 sanity.verify_metadata_context("TestPolicy", &context);
1819
1820 let data_addr = vm_layout::vm_layout().heap_start;
1821 crate::MMAPPER
1823 .ensure_mapped(
1824 data_addr,
1825 1,
1826 HugePageSupport::No,
1827 MmapProtection::ReadWrite,
1828 mmap_anno_test!(),
1829 )
1830 .unwrap();
1831 let meta_addr = address_to_meta_address(&spec, data_addr);
1832 with_cleanup(
1833 || {
1834 let mmap_result =
1835 context.try_map_metadata_space(data_addr, BYTES_IN_PAGE, "test_space");
1836 assert!(mmap_result.is_ok(), "{:?}", mmap_result);
1837
1838 f(&spec, data_addr, meta_addr);
1839 },
1840 || {
1841 assert!(log_bits <= 6);
1843 let meta_ptr: *mut u64 = meta_addr.to_mut_ptr();
1844 unsafe { *meta_ptr = 0 };
1845
1846 sanity::reset();
1847 },
1848 )
1849 })
1850 }
1851
1852 fn max_value(log_bits: usize) -> u64 {
1853 (0..(1 << log_bits)).fold(0, |accum, x| accum + (1 << x))
1854 }
1855 #[test]
1856 fn test_max_value() {
1857 assert_eq!(max_value(0), 1);
1858 assert_eq!(max_value(1), 0b11);
1859 assert_eq!(max_value(2), 0b1111);
1860 assert_eq!(max_value(3), 255);
1861 assert_eq!(max_value(4), 65535);
1862 }
1863
1864 macro_rules! test_side_metadata_access {
1865 ($tname: ident, $type: ty, $log_bits: expr) => {
1866 paste!{
1867 #[test]
1868 fn [<$tname _load>]() {
1869 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1870 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1871
1872 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1874 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
1875
1876 let max_value: $type = max_value($log_bits) as _;
1878 unsafe { spec.store::<$type>(data_addr, max_value); }
1879 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, max_value);
1880 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
1881 assert_eq!(unsafe { *meta_ptr }, max_value);
1882 });
1883 }
1884
1885 #[test]
1886 fn [<$tname _store>]() {
1887 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1888 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1889 let max_value: $type = max_value($log_bits) as _;
1890
1891 unsafe { *meta_ptr = <$type>::MAX; }
1893 unsafe { spec.store::<$type>(data_addr, 0); }
1895 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1896 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1898 });
1899 }
1900
1901 #[test]
1902 fn [<$tname _atomic_store>]() {
1903 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1904 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1905 let max_value: $type = max_value($log_bits) as _;
1906
1907 unsafe { *meta_ptr = <$type>::MAX; }
1909 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1911 assert_eq!(unsafe { spec.load::<$type>(data_addr) }, 0);
1912 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1914 });
1915 }
1916
1917 #[test]
1918 fn [<$tname _compare_exchange_success>]() {
1919 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1920 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1921 let max_value: $type = max_value($log_bits) as _;
1922 unsafe { *meta_ptr = <$type>::MAX; }
1924 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1926
1927 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1928 assert_eq!(old_val, 1);
1929
1930 let new_val = 0;
1931 let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
1932 assert!(res.is_ok());
1933 assert_eq!(res.unwrap(), old_val, "old vals do not match");
1934
1935 let after_update = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1936 assert_eq!(after_update, new_val);
1937 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
1939 });
1940 }
1941
1942 #[test]
1943 fn [<$tname _compare_exchange_fail>]() {
1944 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1945 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1946 unsafe { *meta_ptr = <$type>::MAX; }
1948 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1950
1951 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1952 assert_eq!(old_val, 1);
1953
1954 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1956 let bits_before_cas = unsafe { *meta_ptr };
1957
1958 let new_val = 0;
1959 let res = spec.compare_exchange_atomic::<$type>(data_addr, old_val, new_val, Ordering::SeqCst, Ordering::SeqCst);
1960 assert!(res.is_err());
1961 assert_eq!(res.err().unwrap(), 0);
1962 let bits_after_cas = unsafe { *meta_ptr };
1963 assert_eq!(bits_before_cas, bits_after_cas);
1964 });
1965 }
1966
1967 #[test]
1968 fn [<$tname _fetch_add_1>]() {
1969 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1970 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1971 unsafe { *meta_ptr = <$type>::MAX; }
1973 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1975
1976 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1977
1978 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
1979 assert_eq!(old_val_from_fetch, old_val);
1980
1981 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1982 assert_eq!(new_val, 1);
1983 });
1984 }
1985
1986 #[test]
1987 fn [<$tname _fetch_add_max>]() {
1988 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
1989 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
1990 let max_value: $type = max_value($log_bits) as _;
1991 unsafe { *meta_ptr = <$type>::MAX; }
1993 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
1995
1996 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
1997
1998 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
1999 assert_eq!(old_val_from_fetch, old_val);
2000
2001 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2002 assert_eq!(new_val, max_value);
2003 });
2004 }
2005
2006 #[test]
2007 fn [<$tname _fetch_add_overflow>]() {
2008 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2009 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2010 let max_value: $type = max_value($log_bits) as _;
2011 unsafe { *meta_ptr = <$type>::MAX; }
2013 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2015
2016 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2017
2018 let old_val_from_fetch = spec.fetch_add_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2020 assert_eq!(old_val_from_fetch, old_val);
2021
2022 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2023 assert_eq!(new_val, 0);
2024 });
2025 }
2026
2027 #[test]
2028 fn [<$tname _fetch_sub_1>]() {
2029 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2030 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2031 unsafe { *meta_ptr = <$type>::MAX; }
2033 spec.store_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2035
2036 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2037
2038 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2039 assert_eq!(old_val_from_fetch, old_val);
2040
2041 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2042 assert_eq!(new_val, 0);
2043 });
2044 }
2045
2046 #[test]
2047 fn [<$tname _fetch_sub_max>]() {
2048 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2049 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2050 let max_value: $type = max_value($log_bits) as _;
2051 unsafe { *meta_ptr = <$type>::MAX; }
2053 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2055
2056 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2057
2058 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2059 assert_eq!(old_val_from_fetch, old_val);
2060
2061 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2062 assert_eq!(new_val, 0);
2063 });
2064 }
2065
2066 #[test]
2067 fn [<$tname _fetch_sub_overflow>]() {
2068 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2069 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2070 let max_value: $type = max_value($log_bits) as _;
2071 unsafe { *meta_ptr = <$type>::MAX; }
2073 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2075
2076 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2077
2078 let old_val_from_fetch = spec.fetch_sub_atomic::<$type>(data_addr, 1, Ordering::SeqCst);
2080 assert_eq!(old_val_from_fetch, old_val);
2081
2082 let new_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2083 assert_eq!(new_val, max_value);
2084 });
2085 }
2086
2087 #[test]
2088 fn [<$tname _fetch_and>]() {
2089 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2090 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2091 let max_value: $type = max_value($log_bits) as _;
2092 unsafe { *meta_ptr = <$type>::MAX; }
2094 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2096
2097 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2099 let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2100 assert_eq!(old_val_from_fetch, old_val, "old values do not match");
2101 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value, "load values do not match");
2102 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX, "raw values do not match");
2103
2104 let last_bit_zero = max_value - 1;
2106 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2107 let old_val_from_fetch = spec.fetch_and_atomic::<$type>(data_addr, last_bit_zero, Ordering::SeqCst);
2108 assert_eq!(old_val_from_fetch, old_val);
2109 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), last_bit_zero);
2110 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX - 1);
2111 });
2112 }
2113
2114 #[test]
2115 fn [<$tname _fetch_or>]() {
2116 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2117 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2118 let max_value: $type = max_value($log_bits) as _;
2119 unsafe { *meta_ptr = 0; }
2121 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2123
2124 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2126 let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2127 assert_eq!(old_val_from_fetch, old_val);
2128 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2129 assert_eq!(unsafe { *meta_ptr }, 0);
2130
2131 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2133 let old_val_from_fetch = spec.fetch_or_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2134 assert_eq!(old_val_from_fetch, old_val);
2135 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2136 assert_eq!(unsafe { *meta_ptr }, max_value);
2137 });
2138 }
2139
2140 #[test]
2141 fn [<$tname _fetch_update_success>]() {
2142 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2143 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2144 let max_value: $type = max_value($log_bits) as _;
2145 unsafe { *meta_ptr = <$type>::MAX; }
2147 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2149
2150 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2152 let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| Some(0));
2153 assert!(fetch_res.is_ok());
2154 assert_eq!(fetch_res.unwrap(), old_val);
2155 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), 0);
2156 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX & (!max_value));
2158 });
2159 }
2160
2161 #[test]
2162 fn [<$tname _fetch_update_fail>]() {
2163 test_side_metadata($log_bits, |spec, data_addr, meta_addr| {
2164 let meta_ptr: *mut $type = meta_addr.to_mut_ptr();
2165 let max_value: $type = max_value($log_bits) as _;
2166 unsafe { *meta_ptr = <$type>::MAX; }
2168 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2170
2171 let old_val = spec.load_atomic::<$type>(data_addr, Ordering::SeqCst);
2173 let fetch_res = spec.fetch_update_atomic::<$type, _>(data_addr, Ordering::SeqCst, Ordering::SeqCst, |_x: $type| None);
2174 assert!(fetch_res.is_err());
2175 assert_eq!(fetch_res.err().unwrap(), old_val);
2176 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2177 assert_eq!(unsafe { *meta_ptr }, <$type>::MAX);
2179 });
2180 }
2181
2182 #[test]
2183 fn [<$tname _find_prev_non_zero_value_easy>]() {
2184 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2185 let max_value: $type = max_value($log_bits) as _;
2186 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2188
2189 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(data_addr, 8) };
2192 assert!(res_addr.is_some());
2193 assert_eq!(res_addr.unwrap(), data_addr);
2194 });
2195 }
2196
2197 #[test]
2198 fn [<$tname _find_prev_non_zero_value_arbitrary_bytes>]() {
2199 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2200 let max_value: $type = max_value($log_bits) as _;
2201 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2203
2204 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2206 for len in 1..(test_region*4) {
2207 let start_addr = data_addr + len;
2208 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2210 assert!(res_addr.is_some());
2211 assert_eq!(res_addr.unwrap(), data_addr);
2212 }
2213 });
2214 }
2215
2216 #[test]
2217 fn [<$tname _find_prev_non_zero_value_arbitrary_start>]() {
2218 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2219 let max_value: $type = max_value($log_bits) as _;
2220
2221 for offset in 0..7usize {
2223 let test_data_addr = data_addr + offset;
2225 spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2226
2227 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(test_data_addr, 4096) };
2229 assert!(res_addr.is_some());
2230 assert_eq!(res_addr.unwrap(), data_addr);
2231
2232 spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2234 }
2235 });
2236 }
2237
2238 #[test]
2239 fn [<$tname _find_prev_non_zero_value_no_find>]() {
2240 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2241 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2243
2244 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2246 for len in 1..(test_region*4) {
2247 let start_addr = data_addr + len;
2248 let res_addr = unsafe { spec.find_prev_non_zero_value::<$type>(start_addr, len + 1) };
2250 assert!(res_addr.is_none());
2251 }
2252 });
2253 }
2254
2255 #[test]
2256 fn [<$tname _find_next_non_zero_value_easy>]() {
2257 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2258 let max_value: $type = max_value($log_bits) as _;
2259 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2261
2262 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(data_addr, 8) };
2265 assert!(res_addr.is_some());
2266 assert_eq!(res_addr.unwrap(), data_addr);
2267 });
2268 }
2269
2270 #[test]
2271 fn [<$tname _find_next_non_zero_value_arbitrary_bytes>]() {
2272 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2273 let max_value: $type = max_value($log_bits) as _;
2274 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2275
2276 let data_addr = data_addr + test_region*4;
2279
2280 spec.store_atomic::<$type>(data_addr, max_value, Ordering::SeqCst);
2282 assert_eq!(spec.load_atomic::<$type>(data_addr, Ordering::SeqCst), max_value);
2283
2284 for len in 1..(test_region*4) {
2286 let start_addr = data_addr - len;
2287 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2289 assert!(res_addr.is_some());
2290 assert_eq!(res_addr.unwrap(), data_addr);
2291 }
2292 });
2293 }
2294
2295 #[test]
2296 fn [<$tname _find_next_non_zero_value_arbitrary_start>]() {
2297 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2298 let max_value: $type = max_value($log_bits) as _;
2299
2300 for offset in 0..7usize {
2302 let test_data_addr = data_addr + offset;
2304 spec.store_atomic::<$type>(test_data_addr, max_value, Ordering::SeqCst);
2305
2306 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(test_data_addr, 4096) };
2308 assert!(res_addr.is_some());
2309 assert_eq!(res_addr.unwrap(), data_addr);
2310
2311 spec.store_atomic::<$type>(test_data_addr, 0, Ordering::SeqCst);
2313 }
2314 });
2315 }
2316
2317 #[test]
2318 fn [<$tname _find_next_non_zero_value_no_find>]() {
2319 test_side_metadata($log_bits, |spec, data_addr, _meta_addr| {
2320 spec.store_atomic::<$type>(data_addr, 0, Ordering::SeqCst);
2322
2323 let test_region = (1 << TEST_LOG_BYTES_IN_REGION);
2325 for len in 1..(test_region*4) {
2326 let start_addr = data_addr - len;
2327 let res_addr = unsafe { spec.find_next_non_zero_value::<$type>(start_addr, len + 1) };
2329 assert!(res_addr.is_none());
2330 }
2331 });
2332 }
2333 }
2334 }
2335 }
2336
2337 test_side_metadata_access!(test_u1, u8, 0);
2338 test_side_metadata_access!(test_u2, u8, 1);
2339 test_side_metadata_access!(test_u4, u8, 2);
2340 test_side_metadata_access!(test_u8, u8, 3);
2341 test_side_metadata_access!(test_u16, u16, 4);
2342 test_side_metadata_access!(test_u32, u32, 5);
2343 test_side_metadata_access!(test_u64, u64, 6);
2344 test_side_metadata_access!(
2345 test_usize,
2346 usize,
2347 if cfg!(target_pointer_width = "64") {
2348 6
2349 } else if cfg!(target_pointer_width = "32") {
2350 5
2351 } else {
2352 unreachable!()
2353 }
2354 );
2355
2356 #[test]
2357 fn test_bulk_update_meta_bits() {
2358 let raw_mem =
2359 unsafe { std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(8, 8).unwrap()) };
2360 let addr = Address::from_mut_ptr(raw_mem);
2361
2362 SideMetadataSpec::set_meta_bits(addr, 0, addr, 4);
2363 assert_eq!(unsafe { addr.load::<u64>() }, 0b1111);
2364
2365 SideMetadataSpec::zero_meta_bits(addr, 1, addr, 3);
2366 assert_eq!(unsafe { addr.load::<u64>() }, 0b1001);
2367
2368 SideMetadataSpec::set_meta_bits(addr, 2, addr, 6);
2369 assert_eq!(unsafe { addr.load::<u64>() }, 0b0011_1101);
2370
2371 SideMetadataSpec::zero_meta_bits(addr, 0, addr + 1usize, 0);
2372 assert_eq!(unsafe { addr.load::<u64>() }, 0b0);
2373
2374 SideMetadataSpec::set_meta_bits(addr, 2, addr + 1usize, 2);
2375 assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1100);
2376
2377 SideMetadataSpec::set_meta_bits(addr, 0, addr + 1usize, 2);
2378 assert_eq!(unsafe { addr.load::<u64>() }, 0b11_1111_1111);
2379 }
2380}