mmtk/util/copy/
mod.rs

1use std::mem::MaybeUninit;
2use std::sync::Arc;
3
4use crate::plan::PlanConstraints;
5use crate::policy::copy_context::PolicyCopyContext;
6use crate::policy::copyspace::CopySpace;
7use crate::policy::copyspace::CopySpaceCopyContext;
8use crate::policy::immix::ImmixSpace;
9use crate::policy::immix::{ImmixCopyContext, ImmixHybridCopyContext};
10use crate::policy::space::Space;
11use crate::util::object_forwarding;
12use crate::util::opaque_pointer::VMWorkerThread;
13use crate::util::{Address, ObjectReference};
14use crate::vm::ObjectModel;
15use crate::vm::VMBinding;
16use crate::MMTK;
17
18use enum_map::Enum;
19use enum_map::EnumMap;
20
21use super::alloc::allocator::AllocatorContext;
22
23const MAX_COPYSPACE_COPY_ALLOCATORS: usize = 1;
24const MAX_IMMIX_COPY_ALLOCATORS: usize = 1;
25const MAX_IMMIX_HYBRID_COPY_ALLOCATORS: usize = 1;
26
27type CopySpaceMapping<VM> = Vec<(CopySelector, &'static dyn Space<VM>)>;
28
29/// A configuration for GCWorkerCopyContext.
30/// Similar to a `MutatorConfig`,
31/// We expect each copying plan to provide a CopyConfig.
32pub struct CopyConfig<VM: VMBinding> {
33    /// Mapping CopySemantics to the actual copying allocators (CopySelector)
34    pub(crate) copy_mapping: EnumMap<CopySemantics, CopySelector>,
35    /// Mapping copying allocators with space
36    pub(crate) space_mapping: CopySpaceMapping<VM>,
37    /// A reference to the plan constraints.
38    /// GCWorkerCopyContext may have plan-specific behaviors dependson the plan constraints.
39    pub(crate) constraints: &'static PlanConstraints,
40}
41
42impl<VM: VMBinding> Default for CopyConfig<VM> {
43    fn default() -> Self {
44        CopyConfig {
45            copy_mapping: EnumMap::default(),
46            space_mapping: vec![],
47            constraints: &crate::plan::DEFAULT_PLAN_CONSTRAINTS,
48        }
49    }
50}
51
52/// The thread local struct for each GC worker for copying. Each GC worker should include
53/// one instance of this struct for copying operations.
54pub struct GCWorkerCopyContext<VM: VMBinding> {
55    /// Copy allocators for CopySpace
56    pub copy: [MaybeUninit<CopySpaceCopyContext<VM>>; MAX_COPYSPACE_COPY_ALLOCATORS],
57    /// Copy allocators for ImmixSpace
58    pub immix: [MaybeUninit<ImmixCopyContext<VM>>; MAX_IMMIX_COPY_ALLOCATORS],
59    /// Copy allocators for ImmixSpace
60    pub immix_hybrid: [MaybeUninit<ImmixHybridCopyContext<VM>>; MAX_IMMIX_HYBRID_COPY_ALLOCATORS],
61    /// The config for the plan
62    config: CopyConfig<VM>,
63}
64
65impl<VM: VMBinding> GCWorkerCopyContext<VM> {
66    /// Allocate for the object for GC copying.
67    ///
68    /// Arguments:
69    /// * `original`: The original object that will be copied.
70    /// * `bytes`: The size in bytes for the allocation.
71    /// * `align`: The alignment in bytes for the allocation.
72    /// * `offset`: The offset in bytes for the allocation.
73    /// * `semantics`: The copy semantic for this coying allocation.
74    ///   It determins which copy allocator will be used for the copying.
75    pub fn alloc_copy(
76        &mut self,
77        original: ObjectReference,
78        bytes: usize,
79        align: usize,
80        offset: usize,
81        semantics: CopySemantics,
82    ) -> Address {
83        #[cfg(debug_assertions)]
84        if bytes > self.config.constraints.max_non_los_default_alloc_bytes {
85            warn!(
86                "Attempted to copy an object of {} bytes (> {}) which should be allocated with LOS and not be copied.",
87                bytes, self.config.constraints.max_non_los_default_alloc_bytes
88            );
89        }
90        match self.config.copy_mapping[semantics] {
91            CopySelector::CopySpace(index) => {
92                unsafe { self.copy[index as usize].assume_init_mut() }
93                    .alloc_copy(original, bytes, align, offset)
94            }
95            CopySelector::Immix(index) => unsafe { self.immix[index as usize].assume_init_mut() }
96                .alloc_copy(original, bytes, align, offset),
97            CopySelector::ImmixHybrid(index) => {
98                unsafe { self.immix_hybrid[index as usize].assume_init_mut() }
99                    .alloc_copy(original, bytes, align, offset)
100            }
101            CopySelector::Unused => unreachable!(),
102        }
103    }
104
105    /// Post allocation after allocating an object.
106    ///
107    /// Arguments:
108    /// * `object`: The newly allocated object (the new object after copying).
109    /// * `bytes`: The size of the object in bytes.
110    /// * `semantics`: The copy semantic used for the copying.
111    pub fn post_copy(&mut self, object: ObjectReference, bytes: usize, semantics: CopySemantics) {
112        if VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.is_in_header() {
113            // Clear forwarding bits if the forwarding bits are in the header.
114            object_forwarding::clear_forwarding_bits::<VM>(object);
115        } else {
116            // We ensure no stale side forwarding bits exist before tracing.
117            debug_assert!(!object_forwarding::is_forwarded_or_being_forwarded::<VM>(
118                object
119            ));
120        }
121        // Policy specific post copy.
122        match self.config.copy_mapping[semantics] {
123            CopySelector::CopySpace(index) => {
124                unsafe { self.copy[index as usize].assume_init_mut() }.post_copy(object, bytes)
125            }
126            CopySelector::Immix(index) => {
127                unsafe { self.immix[index as usize].assume_init_mut() }.post_copy(object, bytes)
128            }
129            CopySelector::ImmixHybrid(index) => {
130                unsafe { self.immix_hybrid[index as usize].assume_init_mut() }
131                    .post_copy(object, bytes)
132            }
133            CopySelector::Unused => unreachable!(),
134        }
135    }
136
137    /// Prepare the copying allocators.
138    pub fn prepare(&mut self) {
139        // Delegate to prepare() for each policy copy context
140        for (_, selector) in self.config.copy_mapping.iter() {
141            match selector {
142                CopySelector::CopySpace(index) => {
143                    unsafe { self.copy[*index as usize].assume_init_mut() }.prepare()
144                }
145                CopySelector::Immix(index) => {
146                    unsafe { self.immix[*index as usize].assume_init_mut() }.prepare()
147                }
148                CopySelector::ImmixHybrid(index) => {
149                    unsafe { self.immix_hybrid[*index as usize].assume_init_mut() }.prepare()
150                }
151                CopySelector::Unused => {}
152            }
153        }
154    }
155
156    /// Release the copying allocators.
157    pub fn release(&mut self) {
158        // Delegate to release() for each policy copy context
159        for (_, selector) in self.config.copy_mapping.iter() {
160            match selector {
161                CopySelector::CopySpace(index) => {
162                    unsafe { self.copy[*index as usize].assume_init_mut() }.release()
163                }
164                CopySelector::Immix(index) => {
165                    unsafe { self.immix[*index as usize].assume_init_mut() }.release()
166                }
167                CopySelector::ImmixHybrid(index) => {
168                    unsafe { self.immix_hybrid[*index as usize].assume_init_mut() }.release()
169                }
170                CopySelector::Unused => {}
171            }
172        }
173    }
174
175    /// Create a GCWorkerCopyContext based on the configuration for a copying plan.
176    ///
177    /// Arguments:
178    /// * `worker_tls`: The worker thread for this copy context.
179    /// * `plan`: A reference to the current plan.
180    /// * `config`: The configuration for the copy context.
181    pub fn new(worker_tls: VMWorkerThread, mmtk: &MMTK<VM>, config: CopyConfig<VM>) -> Self {
182        let mut ret = GCWorkerCopyContext {
183            copy: unsafe { MaybeUninit::uninit().assume_init() },
184            immix: unsafe { MaybeUninit::uninit().assume_init() },
185            immix_hybrid: unsafe { MaybeUninit::uninit().assume_init() },
186            config,
187        };
188        let context = Arc::new(AllocatorContext::new(mmtk));
189
190        // Initiate the copy context for each policy based on the space mapping.
191        for &(selector, space) in ret.config.space_mapping.iter() {
192            match selector {
193                CopySelector::CopySpace(index) => {
194                    ret.copy[index as usize].write(CopySpaceCopyContext::new(
195                        worker_tls,
196                        context.clone(),
197                        space.downcast_ref::<CopySpace<VM>>().unwrap(),
198                    ));
199                }
200                CopySelector::Immix(index) => {
201                    ret.immix[index as usize].write(ImmixCopyContext::new(
202                        worker_tls,
203                        context.clone(),
204                        space.downcast_ref::<ImmixSpace<VM>>().unwrap(),
205                    ));
206                }
207                CopySelector::ImmixHybrid(index) => {
208                    ret.immix_hybrid[index as usize].write(ImmixHybridCopyContext::new(
209                        worker_tls,
210                        context.clone(),
211                        space.downcast_ref::<ImmixSpace<VM>>().unwrap(),
212                    ));
213                }
214                CopySelector::Unused => unreachable!(),
215            }
216        }
217
218        ret
219    }
220
221    /// Create a stub GCWorkerCopyContext for non copying plans.
222    pub fn new_non_copy() -> Self {
223        GCWorkerCopyContext {
224            copy: unsafe { MaybeUninit::uninit().assume_init() },
225            immix: unsafe { MaybeUninit::uninit().assume_init() },
226            immix_hybrid: unsafe { MaybeUninit::uninit().assume_init() },
227            config: CopyConfig::default(),
228        }
229    }
230}
231
232/// CopySemantics describes the copying operation. It depends on
233/// the kinds of GC, and the space. For example, in a mature/major GC in
234/// a generational plan, the nursery should have `PromoteToMature` while
235/// the mature space should have `Mature`.
236/// This enum may be expanded in the future to describe more semantics.
237#[derive(Clone, Copy, Enum, Debug)]
238pub enum CopySemantics {
239    /// The default copy behavior.
240    DefaultCopy,
241    /// Copy in nursery generation.
242    Nursery,
243    /// Promote an object from nursery to mature spaces.
244    PromoteToMature,
245    /// Copy in mature generation.
246    Mature,
247}
248
249impl CopySemantics {
250    /// Are we copying to a mature space?
251    pub fn is_mature(&self) -> bool {
252        matches!(self, CopySemantics::PromoteToMature | CopySemantics::Mature)
253    }
254}
255
256#[repr(C, u8)]
257#[derive(Copy, Clone, Debug, Default)]
258pub(crate) enum CopySelector {
259    CopySpace(u8),
260    Immix(u8),
261    ImmixHybrid(u8),
262    #[default]
263    Unused,
264}