1use crate::plan::barriers::Barrier;
4use crate::plan::global::Plan;
5use crate::plan::AllocationSemantics;
6use crate::policy::space::Space;
7use crate::util::alloc::allocator::AllocationOptions;
8use crate::util::alloc::allocators::{AllocatorSelector, Allocators};
9use crate::util::alloc::Allocator;
10use crate::util::{Address, ObjectReference};
11use crate::util::{VMMutatorThread, VMWorkerThread};
12use crate::vm::VMBinding;
13use crate::MMTK;
14
15use enum_map::EnumMap;
16
17use super::barriers::NoBarrier;
18
19pub(crate) type SpaceMapping<VM> = Vec<(AllocatorSelector, &'static dyn Space<VM>)>;
20
21pub(crate) fn unreachable_prepare_func<VM: VMBinding>(
25 _mutator: &mut Mutator<VM>,
26 _tls: VMWorkerThread,
27) {
28 unreachable!("`MutatorConfig::prepare_func` must not be called for the current plan.")
29}
30
31#[allow(unused_variables)]
33pub(crate) fn common_prepare_func<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
34 #[cfg(feature = "marksweep_as_nonmoving")]
36 unsafe {
37 mutator.allocator_impl_mut_for_semantic::<crate::util::alloc::FreeListAllocator<VM>>(
38 AllocationSemantics::NonMoving,
39 )
40 }
41 .prepare();
42}
43
44pub(crate) fn unreachable_release_func<VM: VMBinding>(
47 _mutator: &mut Mutator<VM>,
48 _tls: VMWorkerThread,
49) {
50 unreachable!("`MutatorConfig::release_func` must not be called for the current plan.")
51}
52
53#[allow(unused_variables)]
55pub(crate) fn common_release_func<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
56 cfg_if::cfg_if! {
57 if #[cfg(feature = "marksweep_as_nonmoving")] {
58 unsafe { mutator.allocator_impl_mut_for_semantic::<crate::util::alloc::FreeListAllocator<VM>>(
60 AllocationSemantics::NonMoving,
61 )}.release();
62 } else if #[cfg(feature = "immortal_as_nonmoving")] {
63 } else {
65 unsafe { mutator.allocator_impl_mut_for_semantic::<crate::util::alloc::ImmixAllocator<VM>>(
67 AllocationSemantics::NonMoving,
68 )}.reset();
69 }
70 }
71}
72
73#[allow(dead_code)]
75pub(crate) fn no_op_release_func<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {}
76
77#[repr(C)]
80pub struct MutatorConfig<VM: VMBinding> {
81 pub allocator_mapping: &'static EnumMap<AllocationSemantics, AllocatorSelector>,
83 #[allow(clippy::box_collection)]
86 pub space_mapping: Box<SpaceMapping<VM>>,
87 pub prepare_func: &'static (dyn Fn(&mut Mutator<VM>, VMWorkerThread) + Send + Sync),
89 pub release_func: &'static (dyn Fn(&mut Mutator<VM>, VMWorkerThread) + Send + Sync),
91}
92
93impl<VM: VMBinding> std::fmt::Debug for MutatorConfig<VM> {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 f.write_str("MutatorConfig:\n")?;
96 f.write_str("Semantics mapping:\n")?;
97 for (semantic, selector) in self.allocator_mapping.iter() {
98 let space_name: &str = match self
99 .space_mapping
100 .iter()
101 .find(|(selector_to_find, _)| selector_to_find == selector)
102 {
103 Some((_, space)) => space.name(),
104 None => "!!!missing space here!!!",
105 };
106 f.write_fmt(format_args!(
107 "- {:?} = {:?} ({:?})\n",
108 semantic, selector, space_name
109 ))?;
110 }
111 f.write_str("Space mapping:\n")?;
112 for (selector, space) in self.space_mapping.iter() {
113 f.write_fmt(format_args!("- {:?} = {:?}\n", selector, space.name()))?;
114 }
115 Ok(())
116 }
117}
118
119pub struct MutatorBuilder<VM: VMBinding> {
121 barrier: Box<dyn Barrier<VM>>,
122 mutator_tls: VMMutatorThread,
124 mmtk: &'static MMTK<VM>,
125 config: MutatorConfig<VM>,
126}
127
128impl<VM: VMBinding> MutatorBuilder<VM> {
129 pub fn new(
130 mutator_tls: VMMutatorThread,
131 mmtk: &'static MMTK<VM>,
132 config: MutatorConfig<VM>,
133 ) -> Self {
134 MutatorBuilder {
135 barrier: Box::new(NoBarrier),
136 mutator_tls,
137 mmtk,
138 config,
139 }
140 }
141
142 pub fn barrier(mut self, barrier: Box<dyn Barrier<VM>>) -> Self {
143 self.barrier = barrier;
144 self
145 }
146
147 pub fn build(self) -> Mutator<VM> {
148 Mutator {
149 allocators: Allocators::<VM>::new(
150 self.mutator_tls,
151 self.mmtk,
152 &self.config.space_mapping,
153 ),
154 barrier: self.barrier,
155 mutator_tls: self.mutator_tls,
156 plan: self.mmtk.get_plan(),
157 config: self.config,
158 }
159 }
160}
161
162#[repr(C)]
170pub struct Mutator<VM: VMBinding> {
171 pub(crate) allocators: Allocators<VM>,
172 pub barrier: Box<dyn Barrier<VM>>,
174 pub mutator_tls: VMMutatorThread,
176 pub(crate) plan: &'static dyn Plan<VM = VM>,
177 pub(crate) config: MutatorConfig<VM>,
178}
179
180impl<VM: VMBinding> MutatorContext<VM> for Mutator<VM> {
181 fn prepare(&mut self, tls: VMWorkerThread) {
182 (*self.config.prepare_func)(self, tls)
183 }
184 fn release(&mut self, tls: VMWorkerThread) {
185 (*self.config.release_func)(self, tls)
186 }
187
188 fn alloc(
190 &mut self,
191 size: usize,
192 align: usize,
193 offset: usize,
194 allocator: AllocationSemantics,
195 ) -> Address {
196 let allocator = unsafe {
197 self.allocators
198 .get_allocator_mut(self.config.allocator_mapping[allocator])
199 };
200 debug_assert!(allocator.get_context().get_alloc_options().is_default());
202 allocator.alloc(size, align, offset)
203 }
204
205 fn alloc_with_options(
206 &mut self,
207 size: usize,
208 align: usize,
209 offset: usize,
210 allocator: AllocationSemantics,
211 options: AllocationOptions,
212 ) -> Address {
213 let allocator = unsafe {
214 self.allocators
215 .get_allocator_mut(self.config.allocator_mapping[allocator])
216 };
217 debug_assert!(allocator.get_context().get_alloc_options().is_default());
219 allocator.alloc_with_options(size, align, offset, options)
220 }
221
222 fn alloc_slow(
223 &mut self,
224 size: usize,
225 align: usize,
226 offset: usize,
227 allocator: AllocationSemantics,
228 ) -> Address {
229 let allocator = unsafe {
230 self.allocators
231 .get_allocator_mut(self.config.allocator_mapping[allocator])
232 };
233 debug_assert!(allocator.get_context().get_alloc_options().is_default());
235 allocator.alloc_slow(size, align, offset)
236 }
237
238 fn alloc_slow_with_options(
239 &mut self,
240 size: usize,
241 align: usize,
242 offset: usize,
243 allocator: AllocationSemantics,
244 options: AllocationOptions,
245 ) -> Address {
246 let allocator = unsafe {
247 self.allocators
248 .get_allocator_mut(self.config.allocator_mapping[allocator])
249 };
250 debug_assert!(allocator.get_context().get_alloc_options().is_default());
252 allocator.alloc_slow_with_options(size, align, offset, options)
253 }
254
255 fn post_alloc(&mut self, refer: ObjectReference, bytes: usize, allocator: AllocationSemantics) {
257 unsafe {
258 self.allocators
259 .get_allocator_mut(self.config.allocator_mapping[allocator])
260 }
261 .get_space()
262 .initialize_object_metadata(refer, bytes)
263 }
264
265 fn get_tls(&self) -> VMMutatorThread {
266 self.mutator_tls
267 }
268
269 fn barrier(&mut self) -> &mut dyn Barrier<VM> {
270 &mut *self.barrier
271 }
272}
273
274impl<VM: VMBinding> Mutator<VM> {
275 fn get_all_allocator_selectors(&self) -> Vec<AllocatorSelector> {
277 use itertools::Itertools;
278 self.config
279 .allocator_mapping
280 .iter()
281 .map(|(_, selector)| *selector)
282 .sorted()
283 .dedup()
284 .filter(|selector| *selector != AllocatorSelector::None)
285 .collect()
286 }
287
288 pub fn on_destroy(&mut self) {
290 for selector in self.get_all_allocator_selectors() {
291 unsafe { self.allocators.get_allocator_mut(selector) }.on_mutator_destroy();
292 }
293 }
294
295 pub unsafe fn allocator(&self, selector: AllocatorSelector) -> &dyn Allocator<VM> {
301 self.allocators.get_allocator(selector)
302 }
303
304 pub unsafe fn allocator_mut(&mut self, selector: AllocatorSelector) -> &mut dyn Allocator<VM> {
310 self.allocators.get_allocator_mut(selector)
311 }
312
313 pub unsafe fn allocator_impl<T: Allocator<VM>>(&self, selector: AllocatorSelector) -> &T {
319 self.allocators.get_typed_allocator(selector)
320 }
321
322 pub unsafe fn allocator_impl_mut<T: Allocator<VM>>(
328 &mut self,
329 selector: AllocatorSelector,
330 ) -> &mut T {
331 self.allocators.get_typed_allocator_mut(selector)
332 }
333
334 pub unsafe fn allocator_impl_for_semantic<T: Allocator<VM>>(
339 &self,
340 semantic: AllocationSemantics,
341 ) -> &T {
342 self.allocator_impl::<T>(self.config.allocator_mapping[semantic])
343 }
344
345 pub unsafe fn allocator_impl_mut_for_semantic<T: Allocator<VM>>(
350 &mut self,
351 semantic: AllocationSemantics,
352 ) -> &mut T {
353 self.allocator_impl_mut::<T>(self.config.allocator_mapping[semantic])
354 }
355
356 pub fn get_allocator_base_offset(selector: AllocatorSelector) -> usize {
358 use crate::util::alloc::*;
359 use std::mem::{offset_of, size_of};
360 offset_of!(Mutator<VM>, allocators)
361 + match selector {
362 AllocatorSelector::BumpPointer(index) => {
363 offset_of!(Allocators<VM>, bump_pointer)
364 + size_of::<BumpAllocator<VM>>() * index as usize
365 }
366 AllocatorSelector::FreeList(index) => {
367 offset_of!(Allocators<VM>, free_list)
368 + size_of::<FreeListAllocator<VM>>() * index as usize
369 }
370 AllocatorSelector::Immix(index) => {
371 offset_of!(Allocators<VM>, immix)
372 + size_of::<ImmixAllocator<VM>>() * index as usize
373 }
374 AllocatorSelector::LargeObject(index) => {
375 offset_of!(Allocators<VM>, large_object)
376 + size_of::<LargeObjectAllocator<VM>>() * index as usize
377 }
378 AllocatorSelector::Malloc(index) => {
379 offset_of!(Allocators<VM>, malloc)
380 + size_of::<MallocAllocator<VM>>() * index as usize
381 }
382 AllocatorSelector::Lisp2(index) => {
383 offset_of!(Allocators<VM>, lisp2)
384 + size_of::<Lisp2Allocator<VM>>() * index as usize
385 }
386 AllocatorSelector::None => panic!("Expect a valid AllocatorSelector, found None"),
387 }
388 }
389
390 pub(crate) fn reset_nonmoving_allocator(&mut self) {
392 let allocator = unsafe {
393 self.allocators
394 .get_allocator_mut(self.config.allocator_mapping[AllocationSemantics::NonMoving])
395 };
396
397 cfg_if::cfg_if! {
398 if #[cfg(feature = "immortal_as_nonmoving")] {
399 allocator.downcast_mut::<crate::util::alloc::BumpAllocator<VM>>().unwrap().reset();
400 } else if #[cfg(feature = "marksweep_as_nonmoving")] {
401 let _ = allocator;
402 } else {
403 allocator.downcast_mut::<crate::util::alloc::ImmixAllocator<VM>>().unwrap().reset();
404 }
405 }
406 }
407}
408
409pub trait MutatorContext<VM: VMBinding>: Send + 'static {
414 fn prepare(&mut self, tls: VMWorkerThread);
416 fn release(&mut self, tls: VMWorkerThread);
418 fn alloc(
426 &mut self,
427 size: usize,
428 align: usize,
429 offset: usize,
430 allocator: AllocationSemantics,
431 ) -> Address;
432 fn alloc_with_options(
441 &mut self,
442 size: usize,
443 align: usize,
444 offset: usize,
445 allocator: AllocationSemantics,
446 options: AllocationOptions,
447 ) -> Address;
448 fn alloc_slow(
454 &mut self,
455 size: usize,
456 align: usize,
457 offset: usize,
458 allocator: AllocationSemantics,
459 ) -> Address;
460 fn alloc_slow_with_options(
466 &mut self,
467 size: usize,
468 align: usize,
469 offset: usize,
470 allocator: AllocationSemantics,
471 options: AllocationOptions,
472 ) -> Address;
473 fn post_alloc(&mut self, refer: ObjectReference, bytes: usize, allocator: AllocationSemantics);
481 fn flush_remembered_sets(&mut self) {
483 self.barrier().flush();
484 }
485 fn flush(&mut self) {
487 self.flush_remembered_sets();
488 }
489 fn get_tls(&self) -> VMMutatorThread;
492 fn barrier(&mut self) -> &mut dyn Barrier<VM>;
494}
495
496#[allow(dead_code)]
503#[derive(Default)]
504pub(crate) struct ReservedAllocators {
505 pub n_bump_pointer: u8,
506 pub n_large_object: u8,
507 pub n_malloc: u8,
508 pub n_immix: u8,
509 pub n_lisp2: u8,
510 pub n_free_list: u8,
511}
512
513impl ReservedAllocators {
514 pub const DEFAULT: Self = ReservedAllocators {
515 n_bump_pointer: 0,
516 n_large_object: 0,
517 n_malloc: 0,
518 n_immix: 0,
519 n_lisp2: 0,
520 n_free_list: 0,
521 };
522 fn validate(&self) {
524 use crate::util::alloc::allocators::*;
525 assert!(
526 self.n_bump_pointer as usize <= MAX_BUMP_ALLOCATORS,
527 "Allocator mapping declared more bump pointer allocators than the max allowed."
528 );
529 assert!(
530 self.n_large_object as usize <= MAX_LARGE_OBJECT_ALLOCATORS,
531 "Allocator mapping declared more large object allocators than the max allowed."
532 );
533 assert!(
534 self.n_malloc as usize <= MAX_MALLOC_ALLOCATORS,
535 "Allocator mapping declared more malloc allocators than the max allowed."
536 );
537 assert!(
538 self.n_immix as usize <= MAX_IMMIX_ALLOCATORS,
539 "Allocator mapping declared more immix allocators than the max allowed."
540 );
541 assert!(
542 self.n_lisp2 as usize <= MAX_LISP2_ALLOCATORS,
543 "Allocator mapping declared more mark compact allocators than the max allowed."
544 );
545 assert!(
546 self.n_free_list as usize <= MAX_FREE_LIST_ALLOCATORS,
547 "Allocator mapping declared more free list allocators than the max allowed."
548 );
549 }
550
551 fn add_bump_pointer_allocator(&mut self) -> AllocatorSelector {
554 let selector = AllocatorSelector::BumpPointer(self.n_bump_pointer);
555 self.n_bump_pointer += 1;
556 selector
557 }
558 fn add_large_object_allocator(&mut self) -> AllocatorSelector {
559 let selector = AllocatorSelector::LargeObject(self.n_large_object);
560 self.n_large_object += 1;
561 selector
562 }
563 #[allow(dead_code)]
564 fn add_malloc_allocator(&mut self) -> AllocatorSelector {
565 let selector = AllocatorSelector::Malloc(self.n_malloc);
566 self.n_malloc += 1;
567 selector
568 }
569 #[allow(dead_code)]
570 fn add_immix_allocator(&mut self) -> AllocatorSelector {
571 let selector = AllocatorSelector::Immix(self.n_immix);
572 self.n_immix += 1;
573 selector
574 }
575 #[allow(dead_code)]
576 fn add_lisp2_allocator(&mut self) -> AllocatorSelector {
577 let selector = AllocatorSelector::Lisp2(self.n_lisp2);
578 self.n_lisp2 += 1;
579 selector
580 }
581 #[allow(dead_code)]
582 fn add_free_list_allocator(&mut self) -> AllocatorSelector {
583 let selector = AllocatorSelector::FreeList(self.n_free_list);
584 self.n_free_list += 1;
585 selector
586 }
587}
588
589pub(crate) fn create_allocator_mapping(
595 mut reserved: ReservedAllocators,
596 include_common_plan: bool,
597) -> EnumMap<AllocationSemantics, AllocatorSelector> {
598 let mut map = EnumMap::<AllocationSemantics, AllocatorSelector>::default();
603
604 #[cfg(feature = "code_space")]
607 {
608 map[AllocationSemantics::Code] = reserved.add_bump_pointer_allocator();
609 map[AllocationSemantics::LargeCode] = reserved.add_bump_pointer_allocator();
610 }
611
612 #[cfg(feature = "ro_space")]
613 {
614 map[AllocationSemantics::ReadOnly] = reserved.add_bump_pointer_allocator();
615 }
616
617 if include_common_plan {
620 map[AllocationSemantics::Immortal] = reserved.add_bump_pointer_allocator();
621 map[AllocationSemantics::Los] = reserved.add_large_object_allocator();
622 map[AllocationSemantics::NonMoving] = if cfg!(feature = "marksweep_as_nonmoving") {
623 reserved.add_free_list_allocator()
624 } else if cfg!(feature = "immortal_as_nonmoving") {
625 reserved.add_bump_pointer_allocator()
626 } else {
627 reserved.add_immix_allocator()
628 };
629 }
630
631 reserved.validate();
632 map
633}
634
635pub(crate) fn create_space_mapping<VM: VMBinding>(
642 mut reserved: ReservedAllocators,
643 include_common_plan: bool,
644 plan: &'static dyn Plan<VM = VM>,
645) -> Vec<(AllocatorSelector, &'static dyn Space<VM>)> {
646 let mut vec: Vec<(AllocatorSelector, &'static dyn Space<VM>)> = vec![];
651
652 #[cfg(feature = "code_space")]
655 {
656 vec.push((
657 reserved.add_bump_pointer_allocator(),
658 &plan.base().code_space,
659 ));
660 vec.push((
661 reserved.add_bump_pointer_allocator(),
662 &plan.base().code_lo_space,
663 ));
664 }
665
666 #[cfg(feature = "ro_space")]
667 vec.push((reserved.add_bump_pointer_allocator(), &plan.base().ro_space));
668
669 if include_common_plan {
672 vec.push((
673 reserved.add_bump_pointer_allocator(),
674 plan.common().get_immortal(),
675 ));
676 vec.push((
677 reserved.add_large_object_allocator(),
678 plan.common().get_los(),
679 ));
680 vec.push((
681 if cfg!(feature = "marksweep_as_nonmoving") {
682 reserved.add_free_list_allocator()
683 } else if cfg!(feature = "immortal_as_nonmoving") {
684 reserved.add_bump_pointer_allocator()
685 } else {
686 reserved.add_immix_allocator()
687 },
688 plan.common().get_nonmoving(),
689 ));
690 }
691
692 reserved.validate();
693 vec
694}