mmtk/policy/marksweepspace/native_ms/
block.rs1use atomic::Ordering;
4
5use super::BlockList;
6use super::MarkSweepSpace;
7use crate::util::constants::LOG_BYTES_IN_PAGE;
8use crate::util::heap::chunk_map::*;
9use crate::util::linear_scan::Region;
10use crate::util::linear_scan::UnstraddlableRegion;
11use crate::util::object_enum::BlockMayHaveObjects;
12use crate::vm::ObjectModel;
13use crate::{
14 util::{
15 metadata::side_metadata::SideMetadataSpec, Address, ObjectReference, OpaquePointer,
16 VMThread,
17 },
18 vm::VMBinding,
19};
20
21use std::num::NonZeroUsize;
22
23#[derive(Clone, Copy, PartialOrd, PartialEq)]
30#[repr(transparent)]
31pub struct Block(NonZeroUsize);
32
33impl std::fmt::Debug for Block {
34 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
35 write!(f, "Block(0x{:x})", self.0)
36 }
37}
38
39impl Region for Block {
40 const LOG_BYTES: usize = 16;
41
42 fn from_aligned_address(address: Address) -> Self {
43 debug_assert!(address.is_aligned_to(Self::BYTES));
44 debug_assert!(!address.is_zero());
45 Self(unsafe { NonZeroUsize::new_unchecked(address.as_usize()) })
46 }
47
48 fn start(&self) -> Address {
49 unsafe { Address::from_usize(self.0.get()) }
50 }
51}
52
53impl UnstraddlableRegion for Block {}
55
56impl BlockMayHaveObjects for Block {
57 fn may_have_objects(&self) -> bool {
58 self.get_state() != BlockState::Unallocated
59 }
60}
61
62impl Block {
63 pub const LOG_PAGES: usize = Self::LOG_BYTES - LOG_BYTES_IN_PAGE as usize;
65
66 pub const METADATA_SPECS: [SideMetadataSpec; 7] = [
67 Self::MARK_TABLE,
68 Self::NEXT_BLOCK_TABLE,
69 Self::PREV_BLOCK_TABLE,
70 Self::FREE_LIST_TABLE,
71 Self::SIZE_TABLE,
72 Self::BLOCK_LIST_TABLE,
73 Self::TLS_TABLE,
74 ];
75
76 pub const MARK_TABLE: SideMetadataSpec =
78 crate::util::metadata::side_metadata::spec_defs::MS_BLOCK_MARK;
79
80 pub const NEXT_BLOCK_TABLE: SideMetadataSpec =
81 crate::util::metadata::side_metadata::spec_defs::MS_BLOCK_NEXT;
82
83 pub const PREV_BLOCK_TABLE: SideMetadataSpec =
84 crate::util::metadata::side_metadata::spec_defs::MS_BLOCK_PREV;
85
86 pub const FREE_LIST_TABLE: SideMetadataSpec =
87 crate::util::metadata::side_metadata::spec_defs::MS_FREE;
88
89 #[cfg(feature = "malloc_native_mimalloc")]
91 pub const LOCAL_FREE_LIST_TABLE: SideMetadataSpec =
92 crate::util::metadata::side_metadata::spec_defs::MS_LOCAL_FREE;
93
94 #[cfg(feature = "malloc_native_mimalloc")]
95 pub const THREAD_FREE_LIST_TABLE: SideMetadataSpec =
96 crate::util::metadata::side_metadata::spec_defs::MS_THREAD_FREE;
97
98 pub const SIZE_TABLE: SideMetadataSpec =
99 crate::util::metadata::side_metadata::spec_defs::MS_BLOCK_SIZE;
100
101 pub const BLOCK_LIST_TABLE: SideMetadataSpec =
102 crate::util::metadata::side_metadata::spec_defs::MS_BLOCK_LIST;
103
104 pub const TLS_TABLE: SideMetadataSpec =
105 crate::util::metadata::side_metadata::spec_defs::MS_BLOCK_TLS;
106
107 pub fn load_free_list(&self) -> Address {
108 unsafe { Address::from_usize(Block::FREE_LIST_TABLE.load::<usize>(self.start())) }
109 }
110
111 pub fn store_free_list(&self, free_list: Address) {
112 unsafe { Block::FREE_LIST_TABLE.store::<usize>(self.start(), free_list.as_usize()) }
113 }
114
115 #[cfg(feature = "malloc_native_mimalloc")]
116 pub fn load_local_free_list(&self) -> Address {
117 unsafe { Address::from_usize(Block::LOCAL_FREE_LIST_TABLE.load::<usize>(self.start())) }
118 }
119
120 #[cfg(feature = "malloc_native_mimalloc")]
121 pub fn store_local_free_list(&self, local_free: Address) {
122 unsafe { Block::LOCAL_FREE_LIST_TABLE.store::<usize>(self.start(), local_free.as_usize()) }
123 }
124
125 #[cfg(feature = "malloc_native_mimalloc")]
126 pub fn load_thread_free_list(&self) -> Address {
127 unsafe {
128 Address::from_usize(
129 Block::THREAD_FREE_LIST_TABLE.load_atomic::<usize>(self.start(), Ordering::SeqCst),
130 )
131 }
132 }
133
134 #[cfg(feature = "malloc_native_mimalloc")]
135 pub fn store_thread_free_list(&self, thread_free: Address) {
136 unsafe {
137 Block::THREAD_FREE_LIST_TABLE.store::<usize>(self.start(), thread_free.as_usize())
138 }
139 }
140
141 #[cfg(feature = "malloc_native_mimalloc")]
142 pub fn cas_thread_free_list(&self, old_thread_free: Address, new_thread_free: Address) -> bool {
143 Block::THREAD_FREE_LIST_TABLE
144 .compare_exchange_atomic::<usize>(
145 self.start(),
146 old_thread_free.as_usize(),
147 new_thread_free.as_usize(),
148 Ordering::SeqCst,
149 Ordering::SeqCst,
150 )
151 .is_ok()
152 }
153
154 pub fn load_prev_block(&self) -> Option<Block> {
155 let prev = unsafe { Block::PREV_BLOCK_TABLE.load::<usize>(self.start()) };
156 NonZeroUsize::new(prev).map(Block)
157 }
158
159 pub fn load_next_block(&self) -> Option<Block> {
160 let next = unsafe { Block::NEXT_BLOCK_TABLE.load::<usize>(self.start()) };
161 NonZeroUsize::new(next).map(Block)
162 }
163
164 pub fn store_next_block(&self, next: Block) {
165 unsafe {
166 Block::NEXT_BLOCK_TABLE.store::<usize>(self.start(), next.start().as_usize());
167 }
168 }
169
170 pub fn clear_next_block(&self) {
171 unsafe {
172 Block::NEXT_BLOCK_TABLE.store::<usize>(self.start(), 0);
173 }
174 }
175
176 pub fn store_prev_block(&self, prev: Block) {
177 unsafe {
178 Block::PREV_BLOCK_TABLE.store::<usize>(self.start(), prev.start().as_usize());
179 }
180 }
181
182 pub fn clear_prev_block(&self) {
183 unsafe {
184 Block::PREV_BLOCK_TABLE.store::<usize>(self.start(), 0);
185 }
186 }
187
188 pub fn store_block_list(&self, block_list: &BlockList) {
189 let block_list_usize: usize = block_list as *const BlockList as usize;
190 unsafe {
191 Block::BLOCK_LIST_TABLE.store::<usize>(self.start(), block_list_usize);
192 }
193 }
194
195 pub fn load_block_list(&self) -> *mut BlockList {
196 let block_list =
197 Block::BLOCK_LIST_TABLE.load_atomic::<usize>(self.start(), Ordering::SeqCst);
198 block_list as *mut BlockList
199 }
200
201 pub fn load_block_cell_size(&self) -> usize {
202 Block::SIZE_TABLE.load_atomic::<usize>(self.start(), Ordering::SeqCst)
203 }
204
205 pub fn store_block_cell_size(&self, size: usize) {
206 debug_assert_ne!(size, 0);
207 unsafe { Block::SIZE_TABLE.store::<usize>(self.start(), size) }
208 }
209
210 pub fn store_tls(&self, tls: VMThread) {
211 let tls_usize: usize = tls.0.to_address().as_usize();
212 unsafe { Block::TLS_TABLE.store(self.start(), tls_usize) }
213 }
214
215 pub fn load_tls(&self) -> VMThread {
216 let tls = Block::TLS_TABLE.load_atomic::<usize>(self.start(), Ordering::SeqCst);
217 VMThread(OpaquePointer::from_address(unsafe {
218 Address::from_usize(tls)
219 }))
220 }
221
222 pub fn has_free_cells(&self) -> bool {
223 !self.load_free_list().is_zero()
224 }
225
226 pub fn get_state(&self) -> BlockState {
228 let byte = Self::MARK_TABLE.load_atomic::<u8>(self.start(), Ordering::SeqCst);
229 byte.into()
230 }
231
232 pub fn set_state(&self, state: BlockState) {
234 let state = u8::from(state);
235 Self::MARK_TABLE.store_atomic::<u8>(self.start(), state, Ordering::SeqCst);
236 }
237
238 pub fn attempt_release<VM: VMBinding>(self, space: &MarkSweepSpace<VM>) -> bool {
240 match self.get_state() {
241 BlockState::Unallocated => unreachable!(),
243 BlockState::Unmarked => {
244 let block_list = self.load_block_list();
245 unsafe { &mut *block_list }.remove(self);
246 space.release_block(self);
247 true
248 }
249 BlockState::Marked => {
250 false
252 }
253 }
254 }
255
256 pub fn sweep<VM: VMBinding>(&self) {
258 if cfg!(feature = "malloc_native_mimalloc") {
264 unimplemented!()
265 }
266
267 if !VM::USE_ALLOCATION_OFFSET
272 && VM::MAX_ALIGNMENT == VM::MIN_ALIGNMENT
273 && crate::util::conversions::raw_is_aligned(
274 self.load_block_cell_size(),
275 VM::MAX_ALIGNMENT,
276 )
277 && VM::VMObjectModel::UNIFIED_OBJECT_REFERENCE_ADDRESS
278 {
279 self.simple_sweep::<VM>()
281 } else {
282 self.naive_brute_force_sweep::<VM>()
284 }
285 }
286
287 fn simple_sweep<VM: VMBinding>(&self) {
291 let cell_size = self.load_block_cell_size();
292 debug_assert_ne!(cell_size, 0);
293 let mut cell = self.start();
294 let mut last = unsafe { Address::zero() };
295 while cell + cell_size <= self.start() + Block::BYTES {
296 let potential_object = unsafe { ObjectReference::from_raw_address_unchecked(cell) };
300
301 if !VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
302 .is_marked::<VM>(potential_object, Ordering::SeqCst)
303 {
304 #[cfg(feature = "vo_bit")]
307 crate::util::metadata::vo_bit::unset_vo_bit_nocheck(potential_object);
308 unsafe {
309 cell.store::<Address>(last);
310 }
311 last = cell;
312 }
313 cell += cell_size;
314 }
315
316 self.store_free_list(last);
317 }
318
319 fn naive_brute_force_sweep<VM: VMBinding>(&self) {
324 use crate::util::constants::MIN_OBJECT_SIZE;
325
326 let cell_size = self.load_block_cell_size();
328 let mut cell = self.start();
330 let mut last = Address::ZERO;
332 let mut cursor = cell;
334
335 debug!("Sweep block {:?}, cell size {}", self, cell_size);
336
337 while cell + cell_size <= self.end() {
338 let potential_object_ref = unsafe {
340 ObjectReference::from_raw_address_unchecked(
342 cursor + VM::VMObjectModel::OBJECT_REF_OFFSET_LOWER_BOUND,
343 )
344 };
345 trace!(
346 "{:?}: cell = {}, last cell in free list = {}, cursor = {}, potential object = {}",
347 self,
348 cell,
349 last,
350 cursor,
351 potential_object_ref
352 );
353
354 if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
355 .is_marked::<VM>(potential_object_ref, Ordering::SeqCst)
356 {
357 debug!("{:?} Live cell: {}", self, cell);
358 cell += cell_size;
361 cursor = cell;
362 } else {
363 cursor += MIN_OBJECT_SIZE;
365
366 if cursor >= cell + cell_size {
367 debug!(
369 "{:?} Free cell: {}, last cell in freelist is {}",
370 self, cell, last
371 );
372
373 #[cfg(feature = "vo_bit")]
375 crate::util::metadata::vo_bit::bzero_vo_bit(cell, cell_size);
376
377 debug_assert!(last.is_zero() || (last >= self.start() && last < self.end()));
379 unsafe {
380 cell.store::<Address>(last);
381 }
382 last = cell;
383 cell += cell_size;
384 debug_assert_eq!(cursor, cell);
385 }
386 }
387 }
388
389 self.store_free_list(last);
390 }
391
392 pub fn chunk(&self) -> Chunk {
394 Chunk::from_unaligned_address(self.start())
395 }
396
397 pub fn init(&self) {
399 self.set_state(BlockState::Unmarked);
400 }
401
402 pub fn deinit(&self) {
404 self.set_state(BlockState::Unallocated);
405 }
406}
407
408#[derive(Debug, PartialEq, Clone, Copy)]
410pub enum BlockState {
411 Unallocated,
413 Unmarked,
415 Marked,
417}
418
419impl BlockState {
420 const MARK_UNALLOCATED: u8 = 0;
422 const MARK_UNMARKED: u8 = u8::MAX;
424 const MARK_MARKED: u8 = u8::MAX - 1;
426}
427
428impl From<u8> for BlockState {
429 fn from(state: u8) -> Self {
430 match state {
431 Self::MARK_UNALLOCATED => BlockState::Unallocated,
432 Self::MARK_UNMARKED => BlockState::Unmarked,
433 Self::MARK_MARKED => BlockState::Marked,
434 _ => unreachable!(),
435 }
436 }
437}
438
439impl From<BlockState> for u8 {
440 fn from(state: BlockState) -> Self {
441 match state {
442 BlockState::Unallocated => BlockState::MARK_UNALLOCATED,
443 BlockState::Unmarked => BlockState::MARK_UNMARKED,
444 BlockState::Marked => BlockState::MARK_MARKED,
445 }
446 }
447}