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::MarkCompact(index) => {
383                    offset_of!(Allocators<VM>, markcompact)
384                        + size_of::<MarkCompactAllocator<VM>>() * index as usize
385                }
386                AllocatorSelector::None => panic!("Expect a valid AllocatorSelector, found None"),
387            }
388    }
389}
390
391/// Each GC plan should provide their implementation of a MutatorContext. *Note that this trait is no longer needed as we removed
392/// per-plan mutator implementation and we will remove this trait as well in the future.*
393// 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.
394// The Mutator struct above is the only type that implements this trait. We should be able to merge them.
395pub trait MutatorContext<VM: VMBinding>: Send + 'static {
396    /// Do the prepare work for this mutator.
397    fn prepare(&mut self, tls: VMWorkerThread);
398    /// Do the release work for this mutator.
399    fn release(&mut self, tls: VMWorkerThread);
400    /// Allocate memory for an object. This function will trigger a GC on failed allocation.
401    ///
402    /// Arguments:
403    /// * `size`: the number of bytes required for the object.
404    /// * `align`: required alignment for the object.
405    /// * `offset`: offset associated with the alignment. The result plus the offset will be aligned to the given alignment.
406    /// * `allocator`: the allocation semantic used for this object.
407    fn alloc(
408        &mut self,
409        size: usize,
410        align: usize,
411        offset: usize,
412        allocator: AllocationSemantics,
413    ) -> Address;
414    /// Allocate memory for an object with more options to control this allocation request, e.g. not triggering a GC on fail.
415    ///
416    /// Arguments:
417    /// * `size`: the number of bytes required for the object.
418    /// * `align`: required alignment for the object.
419    /// * `offset`: offset associated with the alignment. The result plus the offset will be aligned to the given alignment.
420    /// * `allocator`: the allocation semantic used for this object.
421    /// * `options`: the allocation options to change the default allocation behavior for this request.
422    fn alloc_with_options(
423        &mut self,
424        size: usize,
425        align: usize,
426        offset: usize,
427        allocator: AllocationSemantics,
428        options: AllocationOptions,
429    ) -> Address;
430    /// The slow path allocation for [`MutatorContext::alloc`]. This function will trigger a GC on failed allocation.
431    ///
432    ///  This is only useful when the binding
433    /// implements the fast path allocation, and would like to explicitly
434    /// call the slow path after the fast path allocation fails.
435    fn alloc_slow(
436        &mut self,
437        size: usize,
438        align: usize,
439        offset: usize,
440        allocator: AllocationSemantics,
441    ) -> Address;
442    /// The slow path allocation for [`MutatorContext::alloc_with_options`].
443    ///
444    /// This is only useful when the binding
445    /// implements the fast path allocation, and would like to explicitly
446    /// call the slow path after the fast path allocation fails.
447    fn alloc_slow_with_options(
448        &mut self,
449        size: usize,
450        align: usize,
451        offset: usize,
452        allocator: AllocationSemantics,
453        options: AllocationOptions,
454    ) -> Address;
455    /// Perform post-allocation actions.  For many allocators none are
456    /// required.
457    ///
458    /// Arguments:
459    /// * `refer`: the newly allocated object.
460    /// * `bytes`: the size of the space allocated (in bytes).
461    /// * `allocator`: the allocation semantic used.
462    fn post_alloc(&mut self, refer: ObjectReference, bytes: usize, allocator: AllocationSemantics);
463    /// Flush per-mutator remembered sets and create GC work for the remembered sets.
464    fn flush_remembered_sets(&mut self) {
465        self.barrier().flush();
466    }
467    /// Flush the mutator context.
468    fn flush(&mut self) {
469        self.flush_remembered_sets();
470    }
471    /// Get the mutator thread for this mutator context. This is the same value as the argument supplied in
472    /// [`crate::memory_manager::bind_mutator`] when this mutator is created.
473    fn get_tls(&self) -> VMMutatorThread;
474    /// Get active barrier trait object
475    fn barrier(&mut self) -> &mut dyn Barrier<VM>;
476}
477
478/// This is used for plans to indicate the number of allocators reserved for the plan.
479/// This is used as a parameter for creating allocator/space mapping.
480/// A plan is required to reserve the first few allocators. For example, if n_bump_pointer is 1,
481/// it means the first bump pointer allocator will be reserved for the plan (and the plan should
482/// initialize its mapping itself), and the spaces in common/base plan will use the following bump
483/// pointer allocators.
484#[allow(dead_code)]
485#[derive(Default)]
486pub(crate) struct ReservedAllocators {
487    pub n_bump_pointer: u8,
488    pub n_large_object: u8,
489    pub n_malloc: u8,
490    pub n_immix: u8,
491    pub n_mark_compact: u8,
492    pub n_free_list: u8,
493}
494
495impl ReservedAllocators {
496    pub const DEFAULT: Self = ReservedAllocators {
497        n_bump_pointer: 0,
498        n_large_object: 0,
499        n_malloc: 0,
500        n_immix: 0,
501        n_mark_compact: 0,
502        n_free_list: 0,
503    };
504    /// check if the number of each allocator is okay. Panics if any allocator exceeds the max number.
505    fn validate(&self) {
506        use crate::util::alloc::allocators::*;
507        assert!(
508            self.n_bump_pointer as usize <= MAX_BUMP_ALLOCATORS,
509            "Allocator mapping declared more bump pointer allocators than the max allowed."
510        );
511        assert!(
512            self.n_large_object as usize <= MAX_LARGE_OBJECT_ALLOCATORS,
513            "Allocator mapping declared more large object allocators than the max allowed."
514        );
515        assert!(
516            self.n_malloc as usize <= MAX_MALLOC_ALLOCATORS,
517            "Allocator mapping declared more malloc allocators than the max allowed."
518        );
519        assert!(
520            self.n_immix as usize <= MAX_IMMIX_ALLOCATORS,
521            "Allocator mapping declared more immix allocators than the max allowed."
522        );
523        assert!(
524            self.n_mark_compact as usize <= MAX_MARK_COMPACT_ALLOCATORS,
525            "Allocator mapping declared more mark compact allocators than the max allowed."
526        );
527        assert!(
528            self.n_free_list as usize <= MAX_FREE_LIST_ALLOCATORS,
529            "Allocator mapping declared more free list allocators than the max allowed."
530        );
531    }
532
533    // We may add more allocators from common/base plan after reserved allocators.
534
535    fn add_bump_pointer_allocator(&mut self) -> AllocatorSelector {
536        let selector = AllocatorSelector::BumpPointer(self.n_bump_pointer);
537        self.n_bump_pointer += 1;
538        selector
539    }
540    fn add_large_object_allocator(&mut self) -> AllocatorSelector {
541        let selector = AllocatorSelector::LargeObject(self.n_large_object);
542        self.n_large_object += 1;
543        selector
544    }
545    #[allow(dead_code)]
546    fn add_malloc_allocator(&mut self) -> AllocatorSelector {
547        let selector = AllocatorSelector::Malloc(self.n_malloc);
548        self.n_malloc += 1;
549        selector
550    }
551    #[allow(dead_code)]
552    fn add_immix_allocator(&mut self) -> AllocatorSelector {
553        let selector = AllocatorSelector::Immix(self.n_immix);
554        self.n_immix += 1;
555        selector
556    }
557    #[allow(dead_code)]
558    fn add_mark_compact_allocator(&mut self) -> AllocatorSelector {
559        let selector = AllocatorSelector::MarkCompact(self.n_mark_compact);
560        self.n_mark_compact += 1;
561        selector
562    }
563    #[allow(dead_code)]
564    fn add_free_list_allocator(&mut self) -> AllocatorSelector {
565        let selector = AllocatorSelector::FreeList(self.n_free_list);
566        self.n_free_list += 1;
567        selector
568    }
569}
570
571/// Create an allocator mapping for spaces in Common/BasePlan for a plan. A plan should reserve its own allocators.
572///
573/// # Arguments
574/// * `reserved`: the number of reserved allocators for the plan specific policies.
575/// * `include_common_plan`: whether the plan uses common plan. If a plan uses CommonPlan, we will initialize allocator mapping for spaces in CommonPlan.
576pub(crate) fn create_allocator_mapping(
577    mut reserved: ReservedAllocators,
578    include_common_plan: bool,
579) -> EnumMap<AllocationSemantics, AllocatorSelector> {
580    // If we need to add new allocators, or new spaces, we need to make sure the allocator we assign here matches the allocator
581    // 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
582    // function, please check the other function.
583
584    let mut map = EnumMap::<AllocationSemantics, AllocatorSelector>::default();
585
586    // spaces in base plan
587
588    #[cfg(feature = "code_space")]
589    {
590        map[AllocationSemantics::Code] = reserved.add_bump_pointer_allocator();
591        map[AllocationSemantics::LargeCode] = reserved.add_bump_pointer_allocator();
592    }
593
594    #[cfg(feature = "ro_space")]
595    {
596        map[AllocationSemantics::ReadOnly] = reserved.add_bump_pointer_allocator();
597    }
598
599    // spaces in common plan
600
601    if include_common_plan {
602        map[AllocationSemantics::Immortal] = reserved.add_bump_pointer_allocator();
603        map[AllocationSemantics::Los] = reserved.add_large_object_allocator();
604        map[AllocationSemantics::NonMoving] = if cfg!(feature = "marksweep_as_nonmoving") {
605            reserved.add_free_list_allocator()
606        } else if cfg!(feature = "immortal_as_nonmoving") {
607            reserved.add_bump_pointer_allocator()
608        } else {
609            reserved.add_immix_allocator()
610        };
611    }
612
613    reserved.validate();
614    map
615}
616
617/// Create a space mapping for spaces in Common/BasePlan for a plan. A plan should reserve its own allocators.
618///
619/// # Arguments
620/// * `reserved`: the number of reserved allocators for the plan specific policies.
621/// * `include_common_plan`: whether the plan uses common plan. If a plan uses CommonPlan, we will initialize allocator mapping for spaces in CommonPlan.
622/// * `plan`: the reference to the plan.
623pub(crate) fn create_space_mapping<VM: VMBinding>(
624    mut reserved: ReservedAllocators,
625    include_common_plan: bool,
626    plan: &'static dyn Plan<VM = VM>,
627) -> Vec<(AllocatorSelector, &'static dyn Space<VM>)> {
628    // If we need to add new allocators, or new spaces, we need to make sure the allocator we assign here matches the allocator
629    // 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
630    // function, please check the other function.
631
632    let mut vec: Vec<(AllocatorSelector, &'static dyn Space<VM>)> = vec![];
633
634    // spaces in BasePlan
635
636    #[cfg(feature = "code_space")]
637    {
638        vec.push((
639            reserved.add_bump_pointer_allocator(),
640            &plan.base().code_space,
641        ));
642        vec.push((
643            reserved.add_bump_pointer_allocator(),
644            &plan.base().code_lo_space,
645        ));
646    }
647
648    #[cfg(feature = "ro_space")]
649    vec.push((reserved.add_bump_pointer_allocator(), &plan.base().ro_space));
650
651    // spaces in CommonPlan
652
653    if include_common_plan {
654        vec.push((
655            reserved.add_bump_pointer_allocator(),
656            plan.common().get_immortal(),
657        ));
658        vec.push((
659            reserved.add_large_object_allocator(),
660            plan.common().get_los(),
661        ));
662        vec.push((
663            if cfg!(feature = "marksweep_as_nonmoving") {
664                reserved.add_free_list_allocator()
665            } else if cfg!(feature = "immortal_as_nonmoving") {
666                reserved.add_bump_pointer_allocator()
667            } else {
668                reserved.add_immix_allocator()
669            },
670            plan.common().get_nonmoving(),
671        ));
672    }
673
674    reserved.validate();
675    vec
676}