mmtk/util/alloc/
allocators.rs

1use std::mem::MaybeUninit;
2use std::sync::Arc;
3
4use memoffset::offset_of;
5
6use crate::policy::largeobjectspace::LargeObjectSpace;
7use crate::policy::marksweepspace::malloc_ms::MallocSpace;
8use crate::policy::marksweepspace::native_ms::MarkSweepSpace;
9use crate::policy::space::Space;
10use crate::util::alloc::LargeObjectAllocator;
11use crate::util::alloc::MallocAllocator;
12use crate::util::alloc::{Allocator, BumpAllocator, ImmixAllocator};
13use crate::util::VMMutatorThread;
14use crate::vm::VMBinding;
15use crate::Mutator;
16use crate::MMTK;
17
18use super::allocator::AllocatorContext;
19use super::FreeListAllocator;
20use super::MarkCompactAllocator;
21
22pub(crate) const MAX_BUMP_ALLOCATORS: usize = 6;
23pub(crate) const MAX_LARGE_OBJECT_ALLOCATORS: usize = 2;
24pub(crate) const MAX_MALLOC_ALLOCATORS: usize = 1;
25pub(crate) const MAX_IMMIX_ALLOCATORS: usize = 2;
26pub(crate) const MAX_FREE_LIST_ALLOCATORS: usize = 2;
27pub(crate) const MAX_MARK_COMPACT_ALLOCATORS: usize = 1;
28
29// The allocators set owned by each mutator. We provide a fixed number of allocators for each allocator type in the mutator,
30// and each plan will select part of the allocators to use.
31// Note that this struct is part of the Mutator struct.
32// 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.
33#[repr(C)]
34pub struct Allocators<VM: VMBinding> {
35    pub bump_pointer: [MaybeUninit<BumpAllocator<VM>>; MAX_BUMP_ALLOCATORS],
36    pub large_object: [MaybeUninit<LargeObjectAllocator<VM>>; MAX_LARGE_OBJECT_ALLOCATORS],
37    pub malloc: [MaybeUninit<MallocAllocator<VM>>; MAX_MALLOC_ALLOCATORS],
38    pub immix: [MaybeUninit<ImmixAllocator<VM>>; MAX_IMMIX_ALLOCATORS],
39    pub free_list: [MaybeUninit<FreeListAllocator<VM>>; MAX_FREE_LIST_ALLOCATORS],
40    pub markcompact: [MaybeUninit<MarkCompactAllocator<VM>>; MAX_MARK_COMPACT_ALLOCATORS],
41}
42
43impl<VM: VMBinding> Allocators<VM> {
44    /// # Safety
45    /// The selector needs to be valid, and points to an allocator that has been initialized.
46    pub unsafe fn get_allocator(&self, selector: AllocatorSelector) -> &dyn Allocator<VM> {
47        match selector {
48            AllocatorSelector::BumpPointer(index) => {
49                self.bump_pointer[index as usize].assume_init_ref()
50            }
51            AllocatorSelector::LargeObject(index) => {
52                self.large_object[index as usize].assume_init_ref()
53            }
54            AllocatorSelector::Malloc(index) => self.malloc[index as usize].assume_init_ref(),
55            AllocatorSelector::Immix(index) => self.immix[index as usize].assume_init_ref(),
56            AllocatorSelector::FreeList(index) => self.free_list[index as usize].assume_init_ref(),
57            AllocatorSelector::MarkCompact(index) => {
58                self.markcompact[index as usize].assume_init_ref()
59            }
60            AllocatorSelector::None => panic!("Allocator mapping is not initialized"),
61        }
62    }
63
64    /// # Safety
65    /// The selector needs to be valid, and points to an allocator that has been initialized.
66    pub unsafe fn get_typed_allocator<T: Allocator<VM>>(&self, selector: AllocatorSelector) -> &T {
67        self.get_allocator(selector).downcast_ref().unwrap()
68    }
69
70    /// # Safety
71    /// The selector needs to be valid, and points to an allocator that has been initialized.
72    pub unsafe fn get_allocator_mut(
73        &mut self,
74        selector: AllocatorSelector,
75    ) -> &mut dyn Allocator<VM> {
76        match selector {
77            AllocatorSelector::BumpPointer(index) => {
78                self.bump_pointer[index as usize].assume_init_mut()
79            }
80            AllocatorSelector::LargeObject(index) => {
81                self.large_object[index as usize].assume_init_mut()
82            }
83            AllocatorSelector::Malloc(index) => self.malloc[index as usize].assume_init_mut(),
84            AllocatorSelector::Immix(index) => self.immix[index as usize].assume_init_mut(),
85            AllocatorSelector::FreeList(index) => self.free_list[index as usize].assume_init_mut(),
86            AllocatorSelector::MarkCompact(index) => {
87                self.markcompact[index as usize].assume_init_mut()
88            }
89            AllocatorSelector::None => panic!("Allocator mapping is not initialized"),
90        }
91    }
92
93    /// # Safety
94    /// The selector needs to be valid, and points to an allocator that has been initialized.
95    pub unsafe fn get_typed_allocator_mut<T: Allocator<VM>>(
96        &mut self,
97        selector: AllocatorSelector,
98    ) -> &mut T {
99        self.get_allocator_mut(selector).downcast_mut().unwrap()
100    }
101
102    pub fn new(
103        mutator_tls: VMMutatorThread,
104        mmtk: &MMTK<VM>,
105        space_mapping: &[(AllocatorSelector, &'static dyn Space<VM>)],
106    ) -> Self {
107        let mut ret = Allocators {
108            bump_pointer: unsafe { MaybeUninit::uninit().assume_init() },
109            large_object: unsafe { MaybeUninit::uninit().assume_init() },
110            malloc: unsafe { MaybeUninit::uninit().assume_init() },
111            immix: unsafe { MaybeUninit::uninit().assume_init() },
112            free_list: unsafe { MaybeUninit::uninit().assume_init() },
113            markcompact: unsafe { MaybeUninit::uninit().assume_init() },
114        };
115        let context = Arc::new(AllocatorContext::new(mmtk));
116
117        for &(selector, space) in space_mapping.iter() {
118            match selector {
119                AllocatorSelector::BumpPointer(index) => {
120                    ret.bump_pointer[index as usize].write(BumpAllocator::new(
121                        mutator_tls.0,
122                        space,
123                        context.clone(),
124                    ));
125                }
126                AllocatorSelector::LargeObject(index) => {
127                    ret.large_object[index as usize].write(LargeObjectAllocator::new(
128                        mutator_tls.0,
129                        space.downcast_ref::<LargeObjectSpace<VM>>().unwrap(),
130                        context.clone(),
131                    ));
132                }
133                AllocatorSelector::Malloc(index) => {
134                    ret.malloc[index as usize].write(MallocAllocator::new(
135                        mutator_tls.0,
136                        space.downcast_ref::<MallocSpace<VM>>().unwrap(),
137                        context.clone(),
138                    ));
139                }
140                AllocatorSelector::Immix(index) => {
141                    ret.immix[index as usize].write(ImmixAllocator::new(
142                        mutator_tls.0,
143                        Some(space),
144                        context.clone(),
145                        false,
146                    ));
147                }
148                AllocatorSelector::FreeList(index) => {
149                    ret.free_list[index as usize].write(FreeListAllocator::new(
150                        mutator_tls.0,
151                        space.downcast_ref::<MarkSweepSpace<VM>>().unwrap(),
152                        context.clone(),
153                    ));
154                }
155                AllocatorSelector::MarkCompact(index) => {
156                    ret.markcompact[index as usize].write(MarkCompactAllocator::new(
157                        mutator_tls.0,
158                        space,
159                        context.clone(),
160                    ));
161                }
162                AllocatorSelector::None => panic!("Allocator mapping is not initialized"),
163            }
164        }
165
166        ret
167    }
168}
169
170/// This type describe an allocator in the [`crate::Mutator`].
171/// For some VM bindings, they may need to access this type from native code. This type is equivalent to the following native types:
172/// #[repr(C)]
173/// struct AllocatorSelector {
174///   tag: AllocatorSelectorTag,
175///   payload: u8,
176/// }
177/// #[repr(u8)]
178/// enum AllocatorSelectorTag {
179///   BumpPointer,
180///   LargeObject,
181///   ...
182/// }
183#[repr(C, u8)]
184#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
185pub enum AllocatorSelector {
186    /// Represents a [`crate::util::alloc::bumpallocator::BumpAllocator`].
187    BumpPointer(u8),
188    /// Represents a [`crate::util::alloc::large_object_allocator::LargeObjectAllocator`].
189    LargeObject(u8),
190    /// Represents a [`crate::util::alloc::malloc_allocator::MallocAllocator`].
191    Malloc(u8),
192    /// Represents a [`crate::util::alloc::immix_allocator::ImmixAllocator`].
193    Immix(u8),
194    /// Represents a [`crate::util::alloc::markcompact_allocator::MarkCompactAllocator`].
195    MarkCompact(u8),
196    /// Represents a [`crate::util::alloc::free_list_allocator::FreeListAllocator`].
197    FreeList(u8),
198    /// No allocator found.
199    #[default]
200    None,
201}
202
203/// This type describes allocator information. It is used to
204/// generate fast paths for the GC. All offset fields are relative to [`Mutator`].
205#[repr(C, u8)]
206#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
207pub enum AllocatorInfo {
208    /// This allocator uses a [`crate::util::alloc::bumpallocator::BumpPointer`] as its fastpath.
209    BumpPointer {
210        /// The byte offset from the mutator's pointer to the [`crate::util::alloc::bumpallocator::BumpPointer`].
211        bump_pointer_offset: usize,
212    },
213    /// This allocator uses a fastpath, but we haven't implemented it yet.
214    // FIXME: Add free-list fast-path
215    Unimplemented,
216    /// This allocator does not have a fastpath.
217    #[default]
218    None,
219}
220
221impl AllocatorInfo {
222    /// Return an AllocatorInfo for the given allocator selector. This method is provided
223    /// so that VM compilers may generate allocator fast-path and load fields for the fast-path.
224    ///
225    /// Arguments:
226    /// * `selector`: The allocator selector to query.
227    pub fn new<VM: VMBinding>(selector: AllocatorSelector) -> AllocatorInfo {
228        let base_offset = Mutator::<VM>::get_allocator_base_offset(selector);
229        match selector {
230            AllocatorSelector::BumpPointer(_) => {
231                let bump_pointer_offset = offset_of!(BumpAllocator<VM>, bump_pointer);
232
233                AllocatorInfo::BumpPointer {
234                    bump_pointer_offset: base_offset + bump_pointer_offset,
235                }
236            }
237
238            AllocatorSelector::Immix(_) => {
239                let bump_pointer_offset = offset_of!(ImmixAllocator<VM>, bump_pointer);
240
241                AllocatorInfo::BumpPointer {
242                    bump_pointer_offset: base_offset + bump_pointer_offset,
243                }
244            }
245
246            AllocatorSelector::MarkCompact(_) => {
247                let bump_offset =
248                    base_offset + offset_of!(MarkCompactAllocator<VM>, bump_allocator);
249                let bump_pointer_offset = offset_of!(BumpAllocator<VM>, bump_pointer);
250
251                AllocatorInfo::BumpPointer {
252                    bump_pointer_offset: bump_offset + bump_pointer_offset,
253                }
254            }
255
256            AllocatorSelector::FreeList(_) => AllocatorInfo::Unimplemented,
257            _ => AllocatorInfo::None,
258        }
259    }
260}