use crate::plan::barriers::Barrier;
use crate::plan::global::Plan;
use crate::plan::AllocationSemantics;
use crate::policy::space::Space;
use crate::util::alloc::allocators::{AllocatorSelector, Allocators};
use crate::util::{Address, ObjectReference};
use crate::util::{VMMutatorThread, VMWorkerThread};
use crate::vm::VMBinding;
use enum_map::EnumMap;
pub(crate) type SpaceMapping<VM> = Vec<(AllocatorSelector, &'static dyn Space<VM>)>;
#[repr(C)]
pub struct MutatorConfig<VM: VMBinding> {
pub allocator_mapping: &'static EnumMap<AllocationSemantics, AllocatorSelector>,
#[allow(clippy::box_collection)]
pub space_mapping: Box<SpaceMapping<VM>>,
pub prepare_func: &'static (dyn Fn(&mut Mutator<VM>, VMWorkerThread) + Send + Sync),
pub release_func: &'static (dyn Fn(&mut Mutator<VM>, VMWorkerThread) + Send + Sync),
}
impl<VM: VMBinding> std::fmt::Debug for MutatorConfig<VM> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("MutatorConfig:\n")?;
f.write_str("Semantics mapping:\n")?;
for (semantic, selector) in self.allocator_mapping.iter() {
let space_name: &str = match self
.space_mapping
.iter()
.find(|(selector_to_find, _)| selector_to_find == selector)
{
Some((_, space)) => space.name(),
None => "!!!missing space here!!!",
};
f.write_fmt(format_args!(
"- {:?} = {:?} ({:?})\n",
semantic, selector, space_name
))?;
}
f.write_str("Space mapping:\n")?;
for (selector, space) in self.space_mapping.iter() {
f.write_fmt(format_args!("- {:?} = {:?}\n", selector, space.name()))?;
}
Ok(())
}
}
#[repr(C)]
pub struct Mutator<VM: VMBinding> {
pub allocators: Allocators<VM>,
pub barrier: Box<dyn Barrier<VM>>,
pub mutator_tls: VMMutatorThread,
pub plan: &'static dyn Plan<VM = VM>,
pub config: MutatorConfig<VM>,
}
impl<VM: VMBinding> MutatorContext<VM> for Mutator<VM> {
fn prepare(&mut self, tls: VMWorkerThread) {
(*self.config.prepare_func)(self, tls)
}
fn release(&mut self, tls: VMWorkerThread) {
(*self.config.release_func)(self, tls)
}
fn alloc(
&mut self,
size: usize,
align: usize,
offset: isize,
allocator: AllocationSemantics,
) -> Address {
unsafe {
self.allocators
.get_allocator_mut(self.config.allocator_mapping[allocator])
}
.alloc(size, align, offset)
}
fn post_alloc(
&mut self,
refer: ObjectReference,
_bytes: usize,
allocator: AllocationSemantics,
) {
unsafe {
self.allocators
.get_allocator_mut(self.config.allocator_mapping[allocator])
}
.get_space()
.initialize_object_metadata(refer, true)
}
fn get_tls(&self) -> VMMutatorThread {
self.mutator_tls
}
#[inline(always)]
unsafe fn barrier_impl<B: Barrier<VM>>(&mut self) -> &mut B {
debug_assert!(self.barrier().is::<B>());
let (payload, _vptr) = std::mem::transmute::<_, (*mut B, *mut ())>(self.barrier());
&mut *payload
}
#[inline(always)]
fn barrier(&mut self) -> &mut dyn Barrier<VM> {
&mut *self.barrier
}
}
impl<VM: VMBinding> Mutator<VM> {
fn get_all_allocator_selectors(&self) -> Vec<AllocatorSelector> {
use itertools::Itertools;
self.config
.allocator_mapping
.iter()
.map(|(_, selector)| *selector)
.sorted()
.dedup()
.filter(|selector| *selector != AllocatorSelector::None)
.collect()
}
pub fn on_destroy(&mut self) {
for selector in self.get_all_allocator_selectors() {
unsafe { self.allocators.get_allocator_mut(selector) }.on_mutator_destroy();
}
}
}
pub trait MutatorContext<VM: VMBinding>: Send + 'static {
fn prepare(&mut self, tls: VMWorkerThread);
fn release(&mut self, tls: VMWorkerThread);
fn alloc(
&mut self,
size: usize,
align: usize,
offset: isize,
allocator: AllocationSemantics,
) -> Address;
fn post_alloc(&mut self, refer: ObjectReference, bytes: usize, allocator: AllocationSemantics);
fn flush_remembered_sets(&mut self) {
self.barrier().flush();
}
fn flush(&mut self) {
self.flush_remembered_sets();
}
fn get_tls(&self) -> VMMutatorThread;
fn barrier(&mut self) -> &mut dyn Barrier<VM>;
unsafe fn barrier_impl<B: Barrier<VM>>(&mut self) -> &mut B;
}
#[allow(dead_code)]
#[derive(Default)]
pub(crate) struct ReservedAllocators {
pub n_bump_pointer: u8,
pub n_large_object: u8,
pub n_malloc: u8,
pub n_immix: u8,
pub n_mark_compact: u8,
pub n_free_list: u8,
}
impl ReservedAllocators {
pub const DEFAULT: Self = ReservedAllocators {
n_bump_pointer: 0,
n_large_object: 0,
n_malloc: 0,
n_immix: 0,
n_mark_compact: 0,
n_free_list: 0,
};
fn validate(&self) {
use crate::util::alloc::allocators::*;
assert!(
self.n_bump_pointer as usize <= MAX_BUMP_ALLOCATORS,
"Allocator mapping declared more bump pointer allocators than the max allowed."
);
assert!(
self.n_large_object as usize <= MAX_LARGE_OBJECT_ALLOCATORS,
"Allocator mapping declared more large object allocators than the max allowed."
);
assert!(
self.n_malloc as usize <= MAX_MALLOC_ALLOCATORS,
"Allocator mapping declared more malloc allocators than the max allowed."
);
assert!(
self.n_immix as usize <= MAX_IMMIX_ALLOCATORS,
"Allocator mapping declared more immix allocators than the max allowed."
);
assert!(
self.n_mark_compact as usize <= MAX_MARK_COMPACT_ALLOCATORS,
"Allocator mapping declared more mark compact allocators than the max allowed."
);
assert!(
self.n_free_list as usize <= MAX_FREE_LIST_ALLOCATORS,
"Allocator mapping declared more free list allocators than the max allowed."
);
}
}
pub(crate) fn create_allocator_mapping(
mut reserved: ReservedAllocators,
include_common_plan: bool,
) -> EnumMap<AllocationSemantics, AllocatorSelector> {
let mut map = EnumMap::<AllocationSemantics, AllocatorSelector>::default();
#[cfg(feature = "code_space")]
{
map[AllocationSemantics::Code] = AllocatorSelector::BumpPointer(reserved.n_bump_pointer);
reserved.n_bump_pointer += 1;
map[AllocationSemantics::LargeCode] =
AllocatorSelector::BumpPointer(reserved.n_bump_pointer);
reserved.n_bump_pointer += 1;
}
#[cfg(feature = "ro_space")]
{
map[AllocationSemantics::ReadOnly] =
AllocatorSelector::BumpPointer(reserved.n_bump_pointer);
reserved.n_bump_pointer += 1;
}
if include_common_plan {
map[AllocationSemantics::Immortal] =
AllocatorSelector::BumpPointer(reserved.n_bump_pointer);
reserved.n_bump_pointer += 1;
map[AllocationSemantics::Los] = AllocatorSelector::LargeObject(reserved.n_large_object);
reserved.n_large_object += 1;
map[AllocationSemantics::NonMoving] =
AllocatorSelector::BumpPointer(reserved.n_bump_pointer);
reserved.n_bump_pointer += 1;
}
reserved.validate();
map
}
pub(crate) fn create_space_mapping<VM: VMBinding>(
mut reserved: ReservedAllocators,
include_common_plan: bool,
plan: &'static dyn Plan<VM = VM>,
) -> Vec<(AllocatorSelector, &'static dyn Space<VM>)> {
let mut vec: Vec<(AllocatorSelector, &'static dyn Space<VM>)> = vec![];
#[cfg(feature = "code_space")]
{
vec.push((
AllocatorSelector::BumpPointer(reserved.n_bump_pointer),
&plan.base().code_space,
));
reserved.n_bump_pointer += 1;
vec.push((
AllocatorSelector::BumpPointer(reserved.n_bump_pointer),
&plan.base().code_lo_space,
));
reserved.n_bump_pointer += 1;
}
#[cfg(feature = "ro_space")]
{
vec.push((
AllocatorSelector::BumpPointer(reserved.n_bump_pointer),
&plan.base().ro_space,
));
reserved.n_bump_pointer += 1;
}
if include_common_plan {
vec.push((
AllocatorSelector::BumpPointer(reserved.n_bump_pointer),
plan.common().get_immortal(),
));
reserved.n_bump_pointer += 1;
vec.push((
AllocatorSelector::LargeObject(reserved.n_large_object),
plan.common().get_los(),
));
reserved.n_large_object += 1;
vec.push((
AllocatorSelector::BumpPointer(reserved.n_bump_pointer),
plan.common().get_nonmoving(),
));
reserved.n_bump_pointer += 1;
}
reserved.validate();
vec
}