use std::mem::MaybeUninit;
use std::sync::Arc;
use crate::plan::PlanConstraints;
use crate::policy::copy_context::PolicyCopyContext;
use crate::policy::copyspace::CopySpace;
use crate::policy::copyspace::CopySpaceCopyContext;
use crate::policy::immix::ImmixSpace;
use crate::policy::immix::{ImmixCopyContext, ImmixHybridCopyContext};
use crate::policy::space::Space;
use crate::util::object_forwarding;
use crate::util::opaque_pointer::VMWorkerThread;
use crate::util::{Address, ObjectReference};
use crate::vm::ObjectModel;
use crate::vm::VMBinding;
use crate::MMTK;
use std::sync::atomic::Ordering;
use enum_map::Enum;
use enum_map::EnumMap;
use super::alloc::allocator::AllocatorContext;
const MAX_COPYSPACE_COPY_ALLOCATORS: usize = 1;
const MAX_IMMIX_COPY_ALLOCATORS: usize = 1;
const MAX_IMMIX_HYBRID_COPY_ALLOCATORS: usize = 1;
type CopySpaceMapping<VM> = Vec<(CopySelector, &'static dyn Space<VM>)>;
pub struct CopyConfig<VM: VMBinding> {
pub(crate) copy_mapping: EnumMap<CopySemantics, CopySelector>,
pub(crate) space_mapping: CopySpaceMapping<VM>,
pub(crate) constraints: &'static PlanConstraints,
}
impl<VM: VMBinding> Default for CopyConfig<VM> {
fn default() -> Self {
CopyConfig {
copy_mapping: EnumMap::default(),
space_mapping: vec![],
constraints: &crate::plan::DEFAULT_PLAN_CONSTRAINTS,
}
}
}
pub struct GCWorkerCopyContext<VM: VMBinding> {
pub copy: [MaybeUninit<CopySpaceCopyContext<VM>>; MAX_COPYSPACE_COPY_ALLOCATORS],
pub immix: [MaybeUninit<ImmixCopyContext<VM>>; MAX_IMMIX_COPY_ALLOCATORS],
pub immix_hybrid: [MaybeUninit<ImmixHybridCopyContext<VM>>; MAX_IMMIX_HYBRID_COPY_ALLOCATORS],
config: CopyConfig<VM>,
}
impl<VM: VMBinding> GCWorkerCopyContext<VM> {
pub fn alloc_copy(
&mut self,
original: ObjectReference,
bytes: usize,
align: usize,
offset: usize,
semantics: CopySemantics,
) -> Address {
#[cfg(debug_assertions)]
if bytes > self.config.constraints.max_non_los_default_alloc_bytes {
warn!(
"Attempted to copy an object of {} bytes (> {}) which should be allocated with LOS and not be copied.",
bytes, self.config.constraints.max_non_los_default_alloc_bytes
);
}
match self.config.copy_mapping[semantics] {
CopySelector::CopySpace(index) => {
unsafe { self.copy[index as usize].assume_init_mut() }
.alloc_copy(original, bytes, align, offset)
}
CopySelector::Immix(index) => unsafe { self.immix[index as usize].assume_init_mut() }
.alloc_copy(original, bytes, align, offset),
CopySelector::ImmixHybrid(index) => {
unsafe { self.immix_hybrid[index as usize].assume_init_mut() }
.alloc_copy(original, bytes, align, offset)
}
CopySelector::Unused => unreachable!(),
}
}
pub fn post_copy(&mut self, object: ObjectReference, bytes: usize, semantics: CopySemantics) {
if VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.is_in_header() {
object_forwarding::clear_forwarding_bits::<VM>(object);
} else {
debug_assert!(!object_forwarding::is_forwarded_or_being_forwarded::<VM>(
object
));
}
if semantics.is_mature() && self.config.constraints.needs_log_bit {
VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC
.mark_byte_as_unlogged::<VM>(object, Ordering::Relaxed);
}
match self.config.copy_mapping[semantics] {
CopySelector::CopySpace(index) => {
unsafe { self.copy[index as usize].assume_init_mut() }.post_copy(object, bytes)
}
CopySelector::Immix(index) => {
unsafe { self.immix[index as usize].assume_init_mut() }.post_copy(object, bytes)
}
CopySelector::ImmixHybrid(index) => {
unsafe { self.immix_hybrid[index as usize].assume_init_mut() }
.post_copy(object, bytes)
}
CopySelector::Unused => unreachable!(),
}
}
pub fn prepare(&mut self) {
for (_, selector) in self.config.copy_mapping.iter() {
match selector {
CopySelector::CopySpace(index) => {
unsafe { self.copy[*index as usize].assume_init_mut() }.prepare()
}
CopySelector::Immix(index) => {
unsafe { self.immix[*index as usize].assume_init_mut() }.prepare()
}
CopySelector::ImmixHybrid(index) => {
unsafe { self.immix_hybrid[*index as usize].assume_init_mut() }.prepare()
}
CopySelector::Unused => {}
}
}
}
pub fn release(&mut self) {
for (_, selector) in self.config.copy_mapping.iter() {
match selector {
CopySelector::CopySpace(index) => {
unsafe { self.copy[*index as usize].assume_init_mut() }.release()
}
CopySelector::Immix(index) => {
unsafe { self.immix[*index as usize].assume_init_mut() }.release()
}
CopySelector::ImmixHybrid(index) => {
unsafe { self.immix_hybrid[*index as usize].assume_init_mut() }.release()
}
CopySelector::Unused => {}
}
}
}
pub fn new(worker_tls: VMWorkerThread, mmtk: &MMTK<VM>, config: CopyConfig<VM>) -> Self {
let mut ret = GCWorkerCopyContext {
copy: unsafe { MaybeUninit::uninit().assume_init() },
immix: unsafe { MaybeUninit::uninit().assume_init() },
immix_hybrid: unsafe { MaybeUninit::uninit().assume_init() },
config,
};
let context = Arc::new(AllocatorContext::new(mmtk));
for &(selector, space) in ret.config.space_mapping.iter() {
match selector {
CopySelector::CopySpace(index) => {
ret.copy[index as usize].write(CopySpaceCopyContext::new(
worker_tls,
context.clone(),
space.downcast_ref::<CopySpace<VM>>().unwrap(),
));
}
CopySelector::Immix(index) => {
ret.immix[index as usize].write(ImmixCopyContext::new(
worker_tls,
context.clone(),
space.downcast_ref::<ImmixSpace<VM>>().unwrap(),
));
}
CopySelector::ImmixHybrid(index) => {
ret.immix_hybrid[index as usize].write(ImmixHybridCopyContext::new(
worker_tls,
context.clone(),
space.downcast_ref::<ImmixSpace<VM>>().unwrap(),
));
}
CopySelector::Unused => unreachable!(),
}
}
ret
}
pub fn new_non_copy() -> Self {
GCWorkerCopyContext {
copy: unsafe { MaybeUninit::uninit().assume_init() },
immix: unsafe { MaybeUninit::uninit().assume_init() },
immix_hybrid: unsafe { MaybeUninit::uninit().assume_init() },
config: CopyConfig::default(),
}
}
}
#[derive(Clone, Copy, Enum, Debug)]
pub enum CopySemantics {
DefaultCopy,
Nursery,
PromoteToMature,
Mature,
}
impl CopySemantics {
pub fn is_mature(&self) -> bool {
matches!(self, CopySemantics::PromoteToMature | CopySemantics::Mature)
}
}
#[repr(C, u8)]
#[derive(Copy, Clone, Debug, Default)]
pub(crate) enum CopySelector {
CopySpace(u8),
Immix(u8),
ImmixHybrid(u8),
#[default]
Unused,
}