mmtk/policy/immix/
block.rs1use super::defrag::Histogram;
2use super::line::Line;
3use super::ImmixSpace;
4use crate::util::constants::*;
5use crate::util::heap::blockpageresource::BlockPool;
6use crate::util::heap::chunk_map::Chunk;
7use crate::util::linear_scan::{Region, RegionIterator};
8use crate::util::metadata::side_metadata::{MetadataByteArrayRef, SideMetadataSpec};
9#[cfg(feature = "vo_bit")]
10use crate::util::metadata::vo_bit;
11#[cfg(feature = "object_pinning")]
12use crate::util::metadata::MetadataSpec;
13use crate::util::object_enum::BlockMayHaveObjects;
14use crate::util::Address;
15use crate::vm::*;
16use std::sync::atomic::Ordering;
17
18#[derive(Debug, PartialEq, Clone, Copy)]
20pub enum BlockState {
21 Unallocated,
23 Unmarked,
25 Marked,
27 Reusable { unavailable_lines: u8 },
29}
30
31impl BlockState {
32 const MARK_UNALLOCATED: u8 = 0;
34 const MARK_UNMARKED: u8 = u8::MAX;
36 const MARK_MARKED: u8 = u8::MAX - 1;
38}
39
40impl From<u8> for BlockState {
41 fn from(state: u8) -> Self {
42 match state {
43 Self::MARK_UNALLOCATED => BlockState::Unallocated,
44 Self::MARK_UNMARKED => BlockState::Unmarked,
45 Self::MARK_MARKED => BlockState::Marked,
46 unavailable_lines => BlockState::Reusable { unavailable_lines },
47 }
48 }
49}
50
51impl From<BlockState> for u8 {
52 fn from(state: BlockState) -> Self {
53 match state {
54 BlockState::Unallocated => BlockState::MARK_UNALLOCATED,
55 BlockState::Unmarked => BlockState::MARK_UNMARKED,
56 BlockState::Marked => BlockState::MARK_MARKED,
57 BlockState::Reusable { unavailable_lines } => unavailable_lines,
58 }
59 }
60}
61
62impl BlockState {
63 pub const fn is_reusable(&self) -> bool {
65 matches!(self, BlockState::Reusable { .. })
66 }
67}
68
69#[repr(transparent)]
71#[derive(Debug, Clone, Copy, PartialOrd, PartialEq)]
72pub struct Block(Address);
73
74impl Region for Block {
75 #[cfg(not(feature = "immix_smaller_block"))]
76 const LOG_BYTES: usize = 15;
77 #[cfg(feature = "immix_smaller_block")]
78 const LOG_BYTES: usize = 13;
79
80 fn from_aligned_address(address: Address) -> Self {
81 debug_assert!(address.is_aligned_to(Self::BYTES));
82 Self(address)
83 }
84
85 fn start(&self) -> Address {
86 self.0
87 }
88}
89
90impl BlockMayHaveObjects for Block {
91 fn may_have_objects(&self) -> bool {
92 self.get_state() != BlockState::Unallocated
93 }
94}
95
96impl Block {
97 pub const LOG_PAGES: usize = Self::LOG_BYTES - LOG_BYTES_IN_PAGE as usize;
99 pub const PAGES: usize = 1 << Self::LOG_PAGES;
101 pub const LOG_LINES: usize = Self::LOG_BYTES - Line::LOG_BYTES;
103 pub const LINES: usize = 1 << Self::LOG_LINES;
105
106 pub const DEFRAG_STATE_TABLE: SideMetadataSpec =
108 crate::util::metadata::side_metadata::spec_defs::IX_BLOCK_DEFRAG;
109
110 pub const MARK_TABLE: SideMetadataSpec =
112 crate::util::metadata::side_metadata::spec_defs::IX_BLOCK_MARK;
113
114 pub fn chunk(&self) -> Chunk {
116 Chunk::from_unaligned_address(self.0)
117 }
118
119 #[allow(clippy::assertions_on_constants)]
121 pub fn line_mark_table(&self) -> MetadataByteArrayRef<{ Block::LINES }> {
122 debug_assert!(!super::BLOCK_ONLY);
123 MetadataByteArrayRef::<{ Block::LINES }>::new(&Line::MARK_TABLE, self.start(), Self::BYTES)
124 }
125
126 pub fn get_state(&self) -> BlockState {
128 let byte = Self::MARK_TABLE.load_atomic::<u8>(self.start(), Ordering::SeqCst);
129 byte.into()
130 }
131
132 pub fn set_state(&self, state: BlockState) {
134 let state = u8::from(state);
135 Self::MARK_TABLE.store_atomic::<u8>(self.start(), state, Ordering::SeqCst);
136 }
137
138 const DEFRAG_SOURCE_STATE: u8 = u8::MAX;
141
142 pub fn is_defrag_source(&self) -> bool {
144 let byte = Self::DEFRAG_STATE_TABLE.load_atomic::<u8>(self.start(), Ordering::SeqCst);
145 byte == Self::DEFRAG_SOURCE_STATE
148 }
149
150 pub fn set_as_defrag_source(&self, defrag: bool) {
152 let byte = if defrag { Self::DEFRAG_SOURCE_STATE } else { 0 };
153 Self::DEFRAG_STATE_TABLE.store_atomic::<u8>(self.start(), byte, Ordering::SeqCst);
154 }
155
156 pub fn set_holes(&self, holes: usize) {
158 Self::DEFRAG_STATE_TABLE.store_atomic::<u8>(self.start(), holes as u8, Ordering::SeqCst);
159 }
160
161 pub fn get_holes(&self) -> usize {
163 let byte = Self::DEFRAG_STATE_TABLE.load_atomic::<u8>(self.start(), Ordering::SeqCst);
164 debug_assert_ne!(byte, Self::DEFRAG_SOURCE_STATE);
165 byte as usize
166 }
167
168 pub fn init(&self, copy: bool) {
170 self.set_state(if copy {
171 BlockState::Marked
172 } else {
173 BlockState::Unmarked
174 });
175 Self::DEFRAG_STATE_TABLE.store_atomic::<u8>(self.start(), 0, Ordering::SeqCst);
176 }
177
178 pub fn deinit(&self) {
180 self.set_state(BlockState::Unallocated);
181 }
182
183 pub fn start_line(&self) -> Line {
184 Line::from_aligned_address(self.start())
185 }
186
187 pub fn end_line(&self) -> Line {
188 Line::from_aligned_address(self.end())
189 }
190
191 #[allow(clippy::assertions_on_constants)]
193 pub fn lines(&self) -> RegionIterator<Line> {
194 debug_assert!(!super::BLOCK_ONLY);
195 RegionIterator::<Line>::new(self.start_line(), self.end_line())
196 }
197
198 pub fn sweep<VM: VMBinding>(
200 &self,
201 space: &ImmixSpace<VM>,
202 mark_histogram: &mut Histogram,
203 line_mark_state: Option<u8>,
204 ) -> BlockSweepResult {
205 if super::BLOCK_ONLY {
206 match self.get_state() {
207 BlockState::Unallocated => unreachable!("Must not sweep unallocated block."),
208 BlockState::Unmarked => {
209 #[cfg(feature = "vo_bit")]
210 vo_bit::helper::on_region_swept::<VM, _>(self, false);
211
212 #[cfg(feature = "object_pinning")]
217 if let MetadataSpec::OnSide(side) = *VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC {
218 side.bzero_metadata(self.start(), Block::BYTES);
219 }
220
221 space.release_block(*self);
223 BlockSweepResult::Swept
224 }
225 BlockState::Marked => {
226 #[cfg(feature = "vo_bit")]
227 vo_bit::helper::on_region_swept::<VM, _>(self, true);
228
229 BlockSweepResult::NoReuse
231 }
232 _ => unreachable!(),
233 }
234 } else {
235 let mut marked_lines = 0;
237 let mut holes = 0;
238 let mut prev_line_is_marked = true;
239 let line_mark_state = line_mark_state.unwrap();
240
241 for line in self.lines() {
242 if line.is_marked(line_mark_state) {
243 marked_lines += 1;
244 prev_line_is_marked = true;
245 } else {
246 if prev_line_is_marked {
247 holes += 1;
248 }
249 if line_mark_state > Line::MAX_MARK_STATE - 2 {
252 line.mark(0);
253 }
254 #[cfg(feature = "immix_zero_on_release")]
255 crate::util::memory::zero(line.start(), Line::BYTES);
256
257 #[cfg(feature = "object_pinning")]
259 if let MetadataSpec::OnSide(side) = *VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC {
260 side.bzero_metadata(line.start(), Line::BYTES);
261 }
262
263 prev_line_is_marked = false;
264 }
265 }
266
267 if marked_lines == 0 {
268 #[cfg(feature = "vo_bit")]
269 vo_bit::helper::on_region_swept::<VM, _>(self, false);
270
271 space.release_block(*self);
273 BlockSweepResult::Swept
274 } else {
275 let is_reusable = marked_lines != Block::LINES;
277 if is_reusable {
278 self.set_state(BlockState::Reusable {
280 unavailable_lines: marked_lines as _,
281 });
282 space.reusable_blocks.push(*self)
283 } else {
284 self.set_state(BlockState::Unmarked);
286 }
287 mark_histogram[holes] += marked_lines;
289 self.set_holes(holes);
291
292 #[cfg(feature = "vo_bit")]
293 vo_bit::helper::on_region_swept::<VM, _>(self, true);
294
295 if is_reusable {
296 BlockSweepResult::Reused
297 } else {
298 BlockSweepResult::NoReuse
299 }
300 }
301 }
302 }
303
304 #[cfg(feature = "vo_bit")]
309 pub fn clear_vo_bits_for_unmarked_regions(&self, line_mark_state: Option<u8>) {
310 match line_mark_state {
311 None => {
312 match self.get_state() {
313 BlockState::Unmarked => {
314 vo_bit::bzero_vo_bit(self.start(), Self::BYTES);
316 }
317 BlockState::Marked => {
318 }
320 _ => unreachable!(),
321 }
322 }
323 Some(state) => {
324 for line in self.lines() {
326 if !line.is_marked(state) {
327 vo_bit::bzero_vo_bit(line.start(), Line::BYTES);
329 }
330 }
331 }
332 }
333 }
334}
335
336pub struct ReusableBlockPool {
338 queue: BlockPool<Block>,
339 num_workers: usize,
340}
341
342impl ReusableBlockPool {
343 pub fn new(num_workers: usize) -> Self {
345 Self {
346 queue: BlockPool::new(num_workers),
347 num_workers,
348 }
349 }
350
351 pub fn len(&self) -> usize {
353 self.queue.len()
354 }
355
356 pub fn push(&self, block: Block) {
358 self.queue.push(block)
359 }
360
361 pub fn pop(&self) -> Option<Block> {
363 self.queue.pop()
364 }
365
366 pub fn reset(&mut self) {
368 self.queue = BlockPool::new(self.num_workers);
369 }
370
371 pub fn iterate_blocks(&self, mut f: impl FnMut(Block)) {
373 self.queue.iterate_blocks(&mut f);
374 }
375
376 pub fn flush_all(&self) {
378 self.queue.flush_all();
379 }
380}
381
382pub enum BlockSweepResult {
384 Swept,
386 Reused,
388 NoReuse,
391}