mmtk/plan/
mutator_context.rs

1//! Mutator context for each application thread.
2
3use 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
21/// A place-holder implementation for `MutatorConfig::prepare_func` that should not be called.
22/// It is the most often used by plans that sets `PlanConstraints::needs_prepare_mutator` to
23/// `false`.  It is also used by `NoGC` because it must not trigger GC.
24pub(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/// An mutator prepare implementation for plans that use [`crate::plan::global::CommonPlan`].
32#[allow(unused_variables)]
33pub(crate) fn common_prepare_func<VM: VMBinding>(mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {
34    // Prepare the free list allocator used for non moving
35    #[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
44/// A place-holder implementation for `MutatorConfig::release_func` that should not be called.
45/// Currently only used by `NoGC`.
46pub(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/// An mutator release implementation for plans that use [`crate::plan::global::CommonPlan`].
54#[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            // Release the free list allocator used for non moving
59            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            // Do nothig for the bump pointer allocator
64        } else {
65            // Reset the Immix allocator
66            unsafe { mutator.allocator_impl_mut_for_semantic::<crate::util::alloc::ImmixAllocator<VM>>(
67                AllocationSemantics::NonMoving,
68            )}.reset();
69        }
70    }
71}
72
73/// A place-holder implementation for `MutatorConfig::release_func` that does nothing.
74#[allow(dead_code)]
75pub(crate) fn no_op_release_func<VM: VMBinding>(_mutator: &mut Mutator<VM>, _tls: VMWorkerThread) {}
76
77// This struct is part of the Mutator struct.
78// We are trying to make it fixed-sized so that VM bindings can easily define a Mutator type to have the exact same layout as our Mutator struct.
79#[repr(C)]
80pub struct MutatorConfig<VM: VMBinding> {
81    /// Mapping between allocation semantics and allocator selector
82    pub allocator_mapping: &'static EnumMap<AllocationSemantics, AllocatorSelector>,
83    /// Mapping between allocator selector and spaces. Each pair represents a mapping.
84    /// Put this behind a box, so it is a pointer-sized field.
85    #[allow(clippy::box_collection)]
86    pub space_mapping: Box<SpaceMapping<VM>>,
87    /// Plan-specific code for mutator prepare. The VMWorkerThread is the worker thread that executes this prepare function.
88    pub prepare_func: &'static (dyn Fn(&mut Mutator<VM>, VMWorkerThread) + Send + Sync),
89    /// Plan-specific code for mutator release. The VMWorkerThread is the worker thread that executes this release function.
90    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
119/// Used to build a mutator struct
120pub struct MutatorBuilder<VM: VMBinding> {
121    barrier: Box<dyn Barrier<VM>>,
122    /// The mutator thread that is bound with this Mutator struct.
123    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/// A mutator is a per-thread data structure that manages allocations and barriers. It is usually highly coupled with the language VM.
163/// It is recommended for MMTk users 1) to have a mutator struct of the same layout in the thread local storage that can be accessed efficiently,
164/// and 2) to implement fastpath allocation and barriers for the mutator in the VM side.
165// We are trying to make this struct fixed-sized so that VM bindings can easily define a type to have the exact same layout as this struct.
166// Currently Mutator is fixed sized, and we should try keep this invariant:
167// - Allocators are fixed-length arrays of allocators.
168// - MutatorConfig only has pointers/refs (including fat pointers), and is fixed sized.
169#[repr(C)]
170pub struct Mutator<VM: VMBinding> {
171    pub(crate) allocators: Allocators<VM>,
172    /// Holds some thread-local states for the barrier.
173    pub barrier: Box<dyn Barrier<VM>>,
174    /// The mutator thread that is bound with this Mutator struct.
175    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    // Note that this method is slow, and we expect VM bindings that care about performance to implement allocation fastpath sequence in their bindings.
189    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        // The value should be default/unset at the beginning of an allocation request.
201        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        // The value should be default/unset at the beginning of an allocation request.
218        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        // The value should be default/unset at the beginning of an allocation request.
234        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        // The value should be default/unset at the beginning of an allocation request.
251        debug_assert!(allocator.get_context().get_alloc_options().is_default());
252        allocator.alloc_slow_with_options(size, align, offset, options)
253    }
254
255    // Note that this method is slow, and we expect VM bindings that care about performance to implement allocation fastpath sequence in their bindings.
256    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    /// Get all the valid allocator selector (no duplicate)
276    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    /// Inform each allocator about destroying. Call allocator-specific on destroy methods.
289    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    /// Get the allocator for the selector.
296    ///
297    /// # Safety
298    /// The selector needs to be valid, and points to an allocator that has been initialized.
299    /// [`crate::memory_manager::get_allocator_mapping`] can be used to get a selector.
300    pub unsafe fn allocator(&self, selector: AllocatorSelector) -> &dyn Allocator<VM> {
301        self.allocators.get_allocator(selector)
302    }
303
304    /// Get the mutable allocator for the selector.
305    ///
306    /// # Safety
307    /// The selector needs to be valid, and points to an allocator that has been initialized.
308    /// [`crate::memory_manager::get_allocator_mapping`] can be used to get a selector.
309    pub unsafe fn allocator_mut(&mut self, selector: AllocatorSelector) -> &mut dyn Allocator<VM> {
310        self.allocators.get_allocator_mut(selector)
311    }
312
313    /// Get the allocator of a concrete type for the selector.
314    ///
315    /// # Safety
316    /// The selector needs to be valid, and points to an allocator that has been initialized.
317    /// [`crate::memory_manager::get_allocator_mapping`] can be used to get a selector.
318    pub unsafe fn allocator_impl<T: Allocator<VM>>(&self, selector: AllocatorSelector) -> &T {
319        self.allocators.get_typed_allocator(selector)
320    }
321
322    /// Get the mutable allocator of a concrete type for the selector.
323    ///
324    /// # Safety
325    /// The selector needs to be valid, and points to an allocator that has been initialized.
326    /// [`crate::memory_manager::get_allocator_mapping`] can be used to get a selector.
327    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    /// Get the allocator of a concrete type for the semantic.
335    ///
336    /// # Safety
337    /// The semantic needs to match the allocator type.
338    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    /// Get the mutable allocator of a concrete type for the semantic.
346    ///
347    /// # Safety
348    /// The semantic needs to match the allocator type.
349    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    /// Return the base offset from a mutator pointer to the allocator specified by the selector.
357    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    /// Reset NonMoving space allocator in current mutator.
391    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
409/// Each GC plan should provide their implementation of a MutatorContext. *Note that this trait is no longer needed as we removed
410/// per-plan mutator implementation and we will remove this trait as well in the future.*
411// TODO: We should be able to remove this trait, as we removed per-plan mutator implementation, and there is no other type that implements this trait.
412// The Mutator struct above is the only type that implements this trait. We should be able to merge them.
413pub trait MutatorContext<VM: VMBinding>: Send + 'static {
414    /// Do the prepare work for this mutator.
415    fn prepare(&mut self, tls: VMWorkerThread);
416    /// Do the release work for this mutator.
417    fn release(&mut self, tls: VMWorkerThread);
418    /// Allocate memory for an object. This function will trigger a GC on failed allocation.
419    ///
420    /// Arguments:
421    /// * `size`: the number of bytes required for the object.
422    /// * `align`: required alignment for the object.
423    /// * `offset`: offset associated with the alignment. The result plus the offset will be aligned to the given alignment.
424    /// * `allocator`: the allocation semantic used for this object.
425    fn alloc(
426        &mut self,
427        size: usize,
428        align: usize,
429        offset: usize,
430        allocator: AllocationSemantics,
431    ) -> Address;
432    /// Allocate memory for an object with more options to control this allocation request, e.g. not triggering a GC on fail.
433    ///
434    /// Arguments:
435    /// * `size`: the number of bytes required for the object.
436    /// * `align`: required alignment for the object.
437    /// * `offset`: offset associated with the alignment. The result plus the offset will be aligned to the given alignment.
438    /// * `allocator`: the allocation semantic used for this object.
439    /// * `options`: the allocation options to change the default allocation behavior for this request.
440    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    /// The slow path allocation for [`MutatorContext::alloc`]. This function will trigger a GC on failed allocation.
449    ///
450    ///  This is only useful when the binding
451    /// implements the fast path allocation, and would like to explicitly
452    /// call the slow path after the fast path allocation fails.
453    fn alloc_slow(
454        &mut self,
455        size: usize,
456        align: usize,
457        offset: usize,
458        allocator: AllocationSemantics,
459    ) -> Address;
460    /// The slow path allocation for [`MutatorContext::alloc_with_options`].
461    ///
462    /// This is only useful when the binding
463    /// implements the fast path allocation, and would like to explicitly
464    /// call the slow path after the fast path allocation fails.
465    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    /// Perform post-allocation actions.  For many allocators none are
474    /// required.
475    ///
476    /// Arguments:
477    /// * `refer`: the newly allocated object.
478    /// * `bytes`: the size of the space allocated (in bytes).
479    /// * `allocator`: the allocation semantic used.
480    fn post_alloc(&mut self, refer: ObjectReference, bytes: usize, allocator: AllocationSemantics);
481    /// Flush per-mutator remembered sets and create GC work for the remembered sets.
482    fn flush_remembered_sets(&mut self) {
483        self.barrier().flush();
484    }
485    /// Flush the mutator context.
486    fn flush(&mut self) {
487        self.flush_remembered_sets();
488    }
489    /// Get the mutator thread for this mutator context. This is the same value as the argument supplied in
490    /// [`crate::memory_manager::bind_mutator`] when this mutator is created.
491    fn get_tls(&self) -> VMMutatorThread;
492    /// Get active barrier trait object
493    fn barrier(&mut self) -> &mut dyn Barrier<VM>;
494}
495
496/// This is used for plans to indicate the number of allocators reserved for the plan.
497/// This is used as a parameter for creating allocator/space mapping.
498/// A plan is required to reserve the first few allocators. For example, if n_bump_pointer is 1,
499/// it means the first bump pointer allocator will be reserved for the plan (and the plan should
500/// initialize its mapping itself), and the spaces in common/base plan will use the following bump
501/// pointer allocators.
502#[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    /// check if the number of each allocator is okay. Panics if any allocator exceeds the max number.
523    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    // We may add more allocators from common/base plan after reserved allocators.
552
553    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
589/// Create an allocator mapping for spaces in Common/BasePlan for a plan. A plan should reserve its own allocators.
590///
591/// # Arguments
592/// * `reserved`: the number of reserved allocators for the plan specific policies.
593/// * `include_common_plan`: whether the plan uses common plan. If a plan uses CommonPlan, we will initialize allocator mapping for spaces in CommonPlan.
594pub(crate) fn create_allocator_mapping(
595    mut reserved: ReservedAllocators,
596    include_common_plan: bool,
597) -> EnumMap<AllocationSemantics, AllocatorSelector> {
598    // If we need to add new allocators, or new spaces, we need to make sure the allocator we assign here matches the allocator
599    // we used in create_space_mapping(). The easiest way is to add the space/allocator mapping in the same order. So for any modification to this
600    // function, please check the other function.
601
602    let mut map = EnumMap::<AllocationSemantics, AllocatorSelector>::default();
603
604    // spaces in base plan
605
606    #[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    // spaces in common plan
618
619    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
635/// Create a space mapping for spaces in Common/BasePlan for a plan. A plan should reserve its own allocators.
636///
637/// # Arguments
638/// * `reserved`: the number of reserved allocators for the plan specific policies.
639/// * `include_common_plan`: whether the plan uses common plan. If a plan uses CommonPlan, we will initialize allocator mapping for spaces in CommonPlan.
640/// * `plan`: the reference to the plan.
641pub(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    // If we need to add new allocators, or new spaces, we need to make sure the allocator we assign here matches the allocator
647    // we used in create_space_mapping(). The easiest way is to add the space/allocator mapping in the same order. So for any modification to this
648    // function, please check the other function.
649
650    let mut vec: Vec<(AllocatorSelector, &'static dyn Space<VM>)> = vec![];
651
652    // spaces in BasePlan
653
654    #[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    // spaces in CommonPlan
670
671    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}