mmtk/util/alloc/
allocators.rs

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