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