|
| 1 | +use crate::api::MMTK_SIDE_VO_BIT_BASE_ADDRESS; |
| 2 | +use core::sync::atomic::Ordering; |
| 3 | +use mmtk::util::Address; |
| 4 | +use std::sync::atomic::AtomicU8; |
| 5 | + |
| 6 | +// This module is a duplicate of MMTK core's side metadata to allow bulk setting for VO bit. |
| 7 | +// The problem is that VO bit is internal to MMTk core, and we cannot access VO bit. |
| 8 | +// FIXME: We should consider refactoring MMTk core to either expose `SideMetadataSpec` for VO bit, |
| 9 | +// or allow the binding to construct `SideMetadataSpec` for VO bit. For either case, we can |
| 10 | +// remove this module and remove this code duplication. |
| 11 | + |
| 12 | +// Functions to set the side metadata for the VO bit (copied from mmtk-core) |
| 13 | +pub const VO_BIT_LOG_NUM_OF_BITS: i32 = 0; |
| 14 | +pub const VO_BIT_LOG_BYTES_PER_REGION: usize = mmtk::util::constants::LOG_MIN_OBJECT_SIZE as usize; |
| 15 | + |
| 16 | +pub fn bulk_update_vo_bit(start: Address, size: usize) { |
| 17 | + // Update bits for a contiguous side metadata spec. We can simply calculate the data end address, and |
| 18 | + // calculate the metadata address for the data end. |
| 19 | + let update_contiguous = |data_start: Address, data_bytes: usize| { |
| 20 | + if data_bytes == 0 { |
| 21 | + return; |
| 22 | + } |
| 23 | + let meta_start = address_to_meta_address(data_start); |
| 24 | + let meta_start_shift = meta_byte_lshift(data_start); |
| 25 | + let meta_end = address_to_meta_address(data_start + data_bytes); |
| 26 | + let meta_end_shift = meta_byte_lshift(data_start + data_bytes); |
| 27 | + set_meta_bits(meta_start, meta_start_shift, meta_end, meta_end_shift); |
| 28 | + }; |
| 29 | + |
| 30 | + // VO bit is global |
| 31 | + update_contiguous(start, size); |
| 32 | +} |
| 33 | + |
| 34 | +/// Performs the translation of data address (`data_addr`) to metadata address for the specified metadata (`metadata_spec`). |
| 35 | +pub fn address_to_meta_address(data_addr: Address) -> Address { |
| 36 | + #[cfg(target_pointer_width = "32")] |
| 37 | + let res = { |
| 38 | + if metadata_spec.is_global { |
| 39 | + address_to_contiguous_meta_address(metadata_spec, data_addr) |
| 40 | + } else { |
| 41 | + address_to_chunked_meta_address(metadata_spec, data_addr) |
| 42 | + } |
| 43 | + }; |
| 44 | + #[cfg(target_pointer_width = "64")] |
| 45 | + let res = { address_to_contiguous_meta_address(data_addr) }; |
| 46 | + |
| 47 | + res |
| 48 | +} |
| 49 | + |
| 50 | +/// Performs address translation in contiguous metadata spaces (e.g. global and policy-specific in 64-bits, and global in 32-bits) |
| 51 | +pub fn address_to_contiguous_meta_address(data_addr: Address) -> Address { |
| 52 | + let rshift = (mmtk::util::constants::LOG_BITS_IN_BYTE as i32) - VO_BIT_LOG_NUM_OF_BITS; |
| 53 | + |
| 54 | + if rshift >= 0 { |
| 55 | + MMTK_SIDE_VO_BIT_BASE_ADDRESS + ((data_addr >> VO_BIT_LOG_BYTES_PER_REGION) >> rshift) |
| 56 | + } else { |
| 57 | + MMTK_SIDE_VO_BIT_BASE_ADDRESS + ((data_addr >> VO_BIT_LOG_BYTES_PER_REGION) << (-rshift)) |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +pub fn meta_byte_lshift(data_addr: Address) -> u8 { |
| 62 | + if VO_BIT_LOG_NUM_OF_BITS >= 3 { |
| 63 | + return 0; |
| 64 | + } |
| 65 | + let rem_shift = mmtk::util::constants::BITS_IN_WORD as i32 |
| 66 | + - ((mmtk::util::constants::LOG_BITS_IN_BYTE as i32) - VO_BIT_LOG_NUM_OF_BITS); |
| 67 | + ((((data_addr >> VO_BIT_LOG_BYTES_PER_REGION) << rem_shift) >> rem_shift) |
| 68 | + << VO_BIT_LOG_NUM_OF_BITS) as u8 |
| 69 | +} |
| 70 | + |
| 71 | +/// This method is used for bulk updating side metadata for a data address range. As we cannot guarantee |
| 72 | +/// that the data address range can be mapped to whole metadata bytes, we have to deal with cases that |
| 73 | +/// we need to mask and zero certain bits in a metadata byte. The end address and the end bit are exclusive. |
| 74 | +/// The end bit for update_bits could be 8, so overflowing needs to be taken care of. |
| 75 | +pub fn update_meta_bits( |
| 76 | + meta_start_addr: Address, |
| 77 | + meta_start_bit: u8, |
| 78 | + meta_end_addr: Address, |
| 79 | + meta_end_bit: u8, |
| 80 | + update_bytes: &impl Fn(Address, Address), |
| 81 | + update_bits: &impl Fn(Address, u8, u8), |
| 82 | +) { |
| 83 | + // Start/end is the same, we don't need to do anything. |
| 84 | + if meta_start_addr == meta_end_addr && meta_start_bit == meta_end_bit { |
| 85 | + return; |
| 86 | + } |
| 87 | + |
| 88 | + // zeroing bytes |
| 89 | + if meta_start_bit == 0 && meta_end_bit == 0 { |
| 90 | + update_bytes(meta_start_addr, meta_end_addr); |
| 91 | + return; |
| 92 | + } |
| 93 | + |
| 94 | + if meta_start_addr == meta_end_addr { |
| 95 | + // Update bits in the same byte between start and end bit |
| 96 | + update_bits(meta_start_addr, meta_start_bit, meta_end_bit); |
| 97 | + } else if meta_start_addr + 1usize == meta_end_addr && meta_end_bit == 0 { |
| 98 | + // Update bits in the same byte after the start bit (between start bit and 8) |
| 99 | + update_bits(meta_start_addr, meta_start_bit, 8); |
| 100 | + } else { |
| 101 | + // update bits in the first byte |
| 102 | + update_meta_bits( |
| 103 | + meta_start_addr, |
| 104 | + meta_start_bit, |
| 105 | + meta_start_addr + 1usize, |
| 106 | + 0, |
| 107 | + update_bytes, |
| 108 | + update_bits, |
| 109 | + ); |
| 110 | + // update bytes in the middle |
| 111 | + update_meta_bits( |
| 112 | + meta_start_addr + 1usize, |
| 113 | + 0, |
| 114 | + meta_end_addr, |
| 115 | + 0, |
| 116 | + update_bytes, |
| 117 | + update_bits, |
| 118 | + ); |
| 119 | + // update bits in the last byte |
| 120 | + update_meta_bits( |
| 121 | + meta_end_addr, |
| 122 | + 0, |
| 123 | + meta_end_addr, |
| 124 | + meta_end_bit, |
| 125 | + update_bytes, |
| 126 | + update_bits, |
| 127 | + ); |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +/// This method is used for bulk setting side metadata for a data address range. |
| 132 | +pub fn set_meta_bits( |
| 133 | + meta_start_addr: Address, |
| 134 | + meta_start_bit: u8, |
| 135 | + meta_end_addr: Address, |
| 136 | + meta_end_bit: u8, |
| 137 | +) { |
| 138 | + let set_bytes = |start: Address, end: Address| { |
| 139 | + set(start, 0xff, end - start); |
| 140 | + }; |
| 141 | + let set_bits = |addr: Address, start_bit: u8, end_bit: u8| { |
| 142 | + // we are setting selected bits in one byte |
| 143 | + let mask: u8 = !(u8::MAX.checked_shl(end_bit.into()).unwrap_or(0)) & (u8::MAX << start_bit); // Get a mask that the bits we need to set are 1, and the other bits are 0. |
| 144 | + unsafe { addr.as_ref::<AtomicU8>() }.fetch_or(mask, Ordering::SeqCst); |
| 145 | + }; |
| 146 | + update_meta_bits( |
| 147 | + meta_start_addr, |
| 148 | + meta_start_bit, |
| 149 | + meta_end_addr, |
| 150 | + meta_end_bit, |
| 151 | + &set_bytes, |
| 152 | + &set_bits, |
| 153 | + ); |
| 154 | +} |
| 155 | + |
| 156 | +/// Set a range of memory to the given value. Similar to memset. |
| 157 | +pub fn set(start: Address, val: u8, len: usize) { |
| 158 | + unsafe { |
| 159 | + std::ptr::write_bytes::<u8>(start.to_mut_ptr(), val, len); |
| 160 | + } |
| 161 | +} |
0 commit comments