1use std::sync::atomic::Ordering;
2use std::sync::Arc;
3
4use super::allocator::{align_allocation_no_fill, fill_alignment_gap, AllocatorContext};
5use super::BumpPointer;
6use crate::policy::immix::block::Block;
7use crate::policy::immix::line::*;
8use crate::policy::immix::ImmixSpace;
9use crate::policy::space::Space;
10use crate::util::alloc::allocator::get_maximum_aligned_size;
11use crate::util::alloc::Allocator;
12use crate::util::linear_scan::Region;
13use crate::util::opaque_pointer::VMThread;
14use crate::util::rust_util::unlikely;
15use crate::util::Address;
16use crate::vm::*;
17
18#[repr(C)]
20pub struct ImmixAllocator<VM: VMBinding> {
21 pub tls: VMThread,
23 pub bump_pointer: BumpPointer,
25 space: &'static ImmixSpace<VM>,
27 context: Arc<AllocatorContext<VM>>,
28 hot: bool,
30 copy: bool,
32 pub(in crate::util::alloc) large_bump_pointer: BumpPointer,
34 request_for_large: bool,
36 line: Option<Line>,
38}
39
40impl<VM: VMBinding> ImmixAllocator<VM> {
41 pub(crate) fn reset(&mut self) {
42 self.bump_pointer.reset(Address::ZERO, Address::ZERO);
43 self.large_bump_pointer.reset(Address::ZERO, Address::ZERO);
44 self.request_for_large = false;
45 self.line = None;
46 }
47}
48
49impl<VM: VMBinding> Allocator<VM> for ImmixAllocator<VM> {
50 fn get_space(&self) -> &'static dyn Space<VM> {
51 self.space as _
52 }
53
54 fn get_context(&self) -> &AllocatorContext<VM> {
55 &self.context
56 }
57
58 fn does_thread_local_allocation(&self) -> bool {
59 true
60 }
61
62 fn get_thread_local_buffer_granularity(&self) -> usize {
63 crate::policy::immix::block::Block::BYTES
64 }
65
66 fn alloc(&mut self, size: usize, align: usize, offset: usize) -> Address {
67 debug_assert!(
68 size <= crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
69 "Trying to allocate a {} bytes object, which is larger than MAX_IMMIX_OBJECT_SIZE {}",
70 size,
71 crate::policy::immix::MAX_IMMIX_OBJECT_SIZE
72 );
73
74 let result = align_allocation_no_fill::<VM>(self.bump_pointer.cursor, align, offset);
75 let new_cursor = result + size;
76
77 if new_cursor > self.bump_pointer.limit {
78 trace!(
79 "{:?}: Thread local buffer used up, go to alloc slow path",
80 self.tls
81 );
82 if get_maximum_aligned_size::<VM>(size, align) > Line::BYTES {
83 self.overflow_alloc(size, align, offset)
85 } else {
86 self.alloc_slow_hot(size, align, offset)
88 }
89 } else {
90 fill_alignment_gap::<VM>(self.bump_pointer.cursor, result);
92 self.bump_pointer.cursor = new_cursor;
93 trace!(
94 "{:?}: Bump allocation size: {}, result: {}, new_cursor: {}, limit: {}",
95 self.tls,
96 size,
97 result,
98 self.bump_pointer.cursor,
99 self.bump_pointer.limit
100 );
101 result
102 }
103 }
104
105 fn alloc_slow_once(&mut self, size: usize, align: usize, offset: usize) -> Address {
107 trace!("{:?}: alloc_slow_once", self.tls);
108 self.acquire_clean_block(size, align, offset)
109 }
110
111 fn alloc_slow_once_precise_stress(
116 &mut self,
117 size: usize,
118 align: usize,
119 offset: usize,
120 need_poll: bool,
121 ) -> Address {
122 trace!("{:?}: alloc_slow_once_precise_stress", self.tls);
123 if need_poll {
126 trace!(
127 "{:?}: alloc_slow_once_precise_stress going to poll",
128 self.tls
129 );
130 let ret = self.acquire_clean_block(size, align, offset);
131 self.set_limit_for_stress();
134 trace!(
135 "{:?}: alloc_slow_once_precise_stress done - forced stress poll",
136 self.tls
137 );
138 return ret;
139 }
140
141 self.restore_limit_for_stress();
146 let ret = if self.require_new_block(size, align, offset) {
147 trace!(
150 "{:?}: alloc_slow_once_precise_stress - acquire new block",
151 self.tls
152 );
153 self.acquire_clean_block(size, align, offset)
154 } else {
155 trace!("{:?}: alloc_slow_once_precise_stress - alloc()", self.tls,);
158 self.alloc(size, align, offset)
159 };
160 self.set_limit_for_stress();
162 ret
163 }
164
165 fn get_tls(&self) -> VMThread {
166 self.tls
167 }
168}
169
170impl<VM: VMBinding> ImmixAllocator<VM> {
171 pub(crate) fn new(
172 tls: VMThread,
173 space: Option<&'static dyn Space<VM>>,
174 context: Arc<AllocatorContext<VM>>,
175 copy: bool,
176 ) -> Self {
177 ImmixAllocator {
178 tls,
179 space: space.unwrap().downcast_ref::<ImmixSpace<VM>>().unwrap(),
180 context,
181 bump_pointer: BumpPointer::default(),
182 hot: false,
183 copy,
184 large_bump_pointer: BumpPointer::default(),
185 request_for_large: false,
186 line: None,
187 }
188 }
189
190 pub(crate) fn immix_space(&self) -> &'static ImmixSpace<VM> {
191 self.space
192 }
193
194 fn overflow_alloc(&mut self, size: usize, align: usize, offset: usize) -> Address {
196 trace!("{:?}: overflow_alloc", self.tls);
197 let start = align_allocation_no_fill::<VM>(self.large_bump_pointer.cursor, align, offset);
198 let end = start + size;
199 if end > self.large_bump_pointer.limit {
200 self.request_for_large = true;
201 let rtn = self.alloc_slow_inline(size, align, offset);
202 self.request_for_large = false;
203 rtn
204 } else {
205 fill_alignment_gap::<VM>(self.large_bump_pointer.cursor, start);
206 self.large_bump_pointer.cursor = end;
207 start
208 }
209 }
210
211 fn alloc_slow_hot(&mut self, size: usize, align: usize, offset: usize) -> Address {
213 trace!("{:?}: alloc_slow_hot", self.tls);
214 if self.acquire_recyclable_lines(size, align, offset) {
215 let stress_test = self.context.options.is_stress_test_gc_enabled();
226 let precise_stress = *self.context.options.precise_stress;
227 if unlikely(stress_test && precise_stress) {
228 self.alloc_slow_inline(size, align, offset)
229 } else {
230 self.alloc(size, align, offset)
231 }
232 } else {
233 self.alloc_slow_inline(size, align, offset)
234 }
235 }
236
237 fn acquire_recyclable_lines(&mut self, size: usize, align: usize, offset: usize) -> bool {
239 while self.line.is_some() || self.acquire_recyclable_block() {
240 let line = self.line.unwrap();
241 if let Some((start_line, end_line)) =
242 self.immix_space().get_next_available_lines(self.copy, line)
243 {
244 self.bump_pointer.cursor = start_line.start();
246 self.bump_pointer.limit = end_line.start();
247 trace!(
248 "{:?}: acquire_recyclable_lines -> {:?} [{:?}, {:?}) {:?}",
249 self.tls,
250 self.line,
251 start_line,
252 end_line,
253 self.tls
254 );
255 crate::util::memory::zero(
256 self.bump_pointer.cursor,
257 self.bump_pointer.limit - self.bump_pointer.cursor,
258 );
259 debug_assert!(
260 align_allocation_no_fill::<VM>(self.bump_pointer.cursor, align, offset) + size
261 <= self.bump_pointer.limit
262 );
263 let block = line.block();
264 self.line = if end_line == block.end_line() {
265 None
267 } else {
268 Some(end_line)
270 };
271 if self.immix_space().should_allocate_as_live() {
273 let state = self.space.line_mark_state.load(Ordering::Acquire);
274 Line::eager_mark_lines::<VM>(state, start_line..end_line);
275 if self.immix_space().common().needs_log_bit {
277 VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC.bulk_mark_as_logged(
278 start_line.start(),
279 end_line.start() - start_line.start(),
280 );
281 }
282 }
283 return true;
284 } else {
285 self.line = None;
287 }
288 }
289 false
290 }
291
292 fn acquire_recyclable_block(&mut self) -> bool {
294 match self.immix_space().get_reusable_block(self.copy) {
295 Some(block) => {
296 trace!("{:?}: acquire_recyclable_block -> {:?}", self.tls, block);
297 self.line = Some(block.start_line());
299 true
300 }
301 _ => false,
302 }
303 }
304
305 fn acquire_clean_block(&mut self, size: usize, align: usize, offset: usize) -> Address {
307 match self.immix_space().get_clean_block(
308 self.tls,
309 self.copy,
310 self.get_context().get_alloc_options(),
311 ) {
312 None => Address::ZERO,
313 Some(block) => {
314 trace!(
315 "{:?}: Acquired a new block {:?} -> {:?}",
316 self.tls,
317 block.start(),
318 block.end()
319 );
320 if !self.immix_space().rc_enabled {
322 Line::MARK_TABLE
324 .bzero_metadata(block.start(), crate::policy::immix::block::Block::BYTES);
325 if self.immix_space().should_allocate_as_live() {
327 let state = self.space.line_mark_state.load(Ordering::Acquire);
328 Line::eager_mark_lines::<VM>(state, block.start_line()..block.end_line());
329 if self.immix_space().common().needs_log_bit {
331 VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC
332 .bulk_mark_as_logged(block.start(), Block::BYTES);
333 }
334 }
335 }
336 if self.request_for_large {
337 self.large_bump_pointer.cursor = block.start();
338 self.large_bump_pointer.limit = block.end();
339 } else {
340 self.bump_pointer.cursor = block.start();
341 self.bump_pointer.limit = block.end();
342 }
343 self.alloc(size, align, offset)
344 }
345 }
346 }
347
348 fn require_new_block(&mut self, size: usize, align: usize, offset: usize) -> bool {
352 let result = align_allocation_no_fill::<VM>(self.bump_pointer.cursor, align, offset);
353 let new_cursor = result + size;
354 let insufficient_space = new_cursor > self.bump_pointer.limit;
355
356 if insufficient_space && get_maximum_aligned_size::<VM>(size, align) > Line::BYTES {
361 let start =
362 align_allocation_no_fill::<VM>(self.large_bump_pointer.cursor, align, offset);
363 let end = start + size;
364 end > self.large_bump_pointer.limit
365 } else {
366 insufficient_space && !self.acquire_recyclable_lines(size, align, offset)
368 }
369 }
370
371 fn set_limit_for_stress(&mut self) {
375 if self.bump_pointer.cursor < self.bump_pointer.limit {
376 let old_limit = self.bump_pointer.limit;
377 let new_limit =
378 unsafe { Address::from_usize(self.bump_pointer.limit - self.bump_pointer.cursor) };
379 self.bump_pointer.limit = new_limit;
380 trace!(
381 "{:?}: set_limit_for_stress. normal c {} l {} -> {}",
382 self.tls,
383 self.bump_pointer.cursor,
384 old_limit,
385 new_limit,
386 );
387 }
388
389 if self.large_bump_pointer.cursor < self.large_bump_pointer.limit {
390 let old_lg_limit = self.large_bump_pointer.limit;
391 let new_lg_limit = unsafe {
392 Address::from_usize(self.large_bump_pointer.limit - self.large_bump_pointer.cursor)
393 };
394 self.large_bump_pointer.limit = new_lg_limit;
395 trace!(
396 "{:?}: set_limit_for_stress. large c {} l {} -> {}",
397 self.tls,
398 self.large_bump_pointer.cursor,
399 old_lg_limit,
400 new_lg_limit,
401 );
402 }
403 }
404
405 fn restore_limit_for_stress(&mut self) {
410 if self.bump_pointer.limit < self.bump_pointer.cursor {
411 let old_limit = self.bump_pointer.limit;
412 let new_limit = self.bump_pointer.cursor + self.bump_pointer.limit.as_usize();
413 self.bump_pointer.limit = new_limit;
414 trace!(
415 "{:?}: restore_limit_for_stress. normal c {} l {} -> {}",
416 self.tls,
417 self.bump_pointer.cursor,
418 old_limit,
419 new_limit,
420 );
421 }
422
423 if self.large_bump_pointer.limit < self.large_bump_pointer.cursor {
424 let old_lg_limit = self.large_bump_pointer.limit;
425 let new_lg_limit =
426 self.large_bump_pointer.cursor + self.large_bump_pointer.limit.as_usize();
427 self.large_bump_pointer.limit = new_lg_limit;
428 trace!(
429 "{:?}: restore_limit_for_stress. large c {} l {} -> {}",
430 self.tls,
431 self.large_bump_pointer.cursor,
432 old_lg_limit,
433 new_lg_limit,
434 );
435 }
436 }
437}