mmtk/vm/
object_model.rs

1use atomic::Ordering;
2
3use self::specs::*;
4use crate::util::copy::*;
5use crate::util::metadata::header_metadata::HeaderMetadataSpec;
6use crate::util::metadata::MetadataValue;
7use crate::util::{Address, ObjectReference};
8use crate::vm::VMBinding;
9
10/// VM-specific methods for object model.
11///
12/// This trait includes 3 parts:
13///
14/// 1. Specifications for per object metadata: a binding needs to specify the location for each per object metadata spec.
15///    A binding can choose between `in_header()` or `side()`, e.g. `VMGlobalLogBitSpec::side()`.
16///    * in_header: a binding needs to specify the bit offset to an object reference that can be used for the per object metadata spec.
17///      The actual number of bits required for a spec can be obtained from the `num_bits()` method of the spec type.
18///    * side: a binding does not need to provide any specific storage for metadata in the header. Instead, MMTk
19///      will use side tables to store the metadata. The following section Side Specs Layout will discuss how to correctly create
20///      side metadata specs.
21/// 2. In header metadata access: A binding
22///    need to further define the functions with suffix _metadata about how to access the bits in the header. We provide default implementations
23///    for those methods, assuming the bits in the spec are always available to MMTk. A binding could implement their
24///    own routines to access the bits if VM specific treatment is needed (e.g. some bits are not always available to MMTk).
25/// 3. VM-specific object info needed by MMTk: MMTk does not know object info as it is VM specific. However, MMTk needs
26///    some object information for GC. A binding needs to implement them correctly.
27///
28/// Note that depending on the selected GC plan, only a subset of the methods provided here will be used.
29///
30/// # Side Specs Layout
31///
32/// ## Short version
33///
34/// * For *global* side metadata:
35///   * The first spec: VMGlobalXXXSpec::side_first()
36///   * The following specs: VMGlobalXXXSpec::side_after(FIRST_GLOAL.as_spec())
37/// * For *local* side metadata:
38///   * The first spec: VMLocalXXXSpec::side_first()
39///   * The following specs: VMLocalXXXSpec::side_after(FIRST_LOCAL.as_spec())
40///
41/// ## Detailed explanation
42///
43/// There are two types of side metadata layout in MMTk:
44///
45/// 1. Contiguous layout: is the layout in which the whole metadata space for a SideMetadataSpec is contiguous.
46/// 2. Chunked layout: is the layout in which the whole metadata memory space, that is shared between MMTk policies, is divided into metadata-chunks. Each metadata-chunk stores all of the metadata for all `SideMetadataSpec`s which apply to a source-data chunk.
47///
48/// In 64-bits targets, both Global and PolicySpecific side metadata are contiguous.
49/// Also, in 32-bits targets, the Global side metadata is contiguous.
50/// This means if the starting address (variable named `offset`) of the metadata space for a SideMetadataSpec (`SPEC1`) is `BASE1`, the starting address (`offset`) of the next SideMetadataSpec (`SPEC2`) will be `BASE1 + total_metadata_space_size(SPEC1)`, which is located immediately after the end of the whole metadata space of `SPEC1`.
51/// Now, if we add a third SideMetadataSpec (`SPEC3`), its starting address (`offset`) will be `BASE2 + total_metadata_space_size(SPEC2)`, which is located immediately after the end of the whole metadata space of `SPEC2`.
52///
53/// In 32-bits targets, the PolicySpecific side metadata is chunked.
54/// This means for each chunk (2^22 Bytes) of data, which, by definition, is managed by exactly one MMTk policy, there is a metadata chunk (2^22 * some_fixed_ratio Bytes) that contains all of its PolicySpecific metadata.
55/// This means if a policy has one SideMetadataSpec (`LS1`), the `offset` of that spec will be `0` (= at the start of a metadata chunk).
56/// If there is a second SideMetadataSpec (`LS2`) for this specific policy, the `offset` for that spec will be `0 + required_metadata_space_per_chunk(LS1)`,
57/// and for a third SideMetadataSpec (`LS3`), the `offset` will be `BASE(LS2) + required_metadata_space_per_chunk(LS2)`.
58///
59/// For all other policies, the `offset` starts from zero. This is safe because no two policies ever manage one chunk, so there will be no overlap.
60///
61/// # Object Layout Addresses
62///
63/// MMTk tries to be general to cope with different language implementations and different object models. Thus it does not assume the internal of the object model.
64/// Instead, MMTk only uses the following addresses for an object. If you find the MMTk's approach does not work for your language in practice, you are welcome to submit an issue
65/// or engage with MMTk team on Zulip to disucss further.
66///
67/// ## (Raw) Object Reference
68///
69/// See [`crate::util::address::ObjectReference`]. This is a special address that represents the
70/// object. MMTk refers to an object by its object reference. An object reference cannot be NULL,
71/// must be inside the address range of the object, and must be word aligned
72/// ([`crate::util::address::ObjectReference::ALIGNMENT`]).
73///
74/// ## Object Start Address
75///
76/// This address is returned by an allocation call [`crate::memory_manager::alloc`]. This is the start of the address range of the allocation.
77/// [`ObjectModel::ref_to_object_start`] should return this address for a given object.
78///
79/// ## Object header address
80///
81/// If a binding allows MMTk to use its header bits for object metadata, it needs to supply an object header
82/// address ([`ObjectModel::ref_to_header`]). MMTk will access header bits using this address.
83pub trait ObjectModel<VM: VMBinding> {
84    // Per-object Metadata Spec definitions go here
85    //
86    // Note a number of Global and PolicySpecific side metadata specifications are already reserved by mmtk-core.
87    // Any side metadata offset calculation must consider these to prevent overlaps. A binding should start their
88    // side metadata from GLOBAL_SIDE_METADATA_VM_BASE_ADDRESS or LOCAL_SIDE_METADATA_VM_BASE_ADDRESS.
89
90    /// A global 1-bit metadata used by generational plans to track cross-generational pointers. It is generally
91    /// located in side metadata.
92    ///
93    /// Note that for this bit, 0 represents logged (default), and 1 represents unlogged.
94    /// This bit is also referred to as unlogged bit in Java MMTk for this reason.
95    const GLOBAL_LOG_BIT_SPEC: VMGlobalLogBitSpec;
96
97    /// A local word-size metadata for the forwarding pointer, used by copying plans. It is almost always
98    /// located in the object header as it is fine to destroy an object header in order to copy it.
99    const LOCAL_FORWARDING_POINTER_SPEC: VMLocalForwardingPointerSpec;
100
101    /// A local 2-bit metadata for the forwarding status bits, used by copying plans. If your runtime requires
102    /// word-aligned addresses (i.e. 4- or 8-bytes), you can use the last two bits in the object header to store
103    /// the forwarding bits. Note that you must be careful if you place this in the header as the runtime may
104    /// be using those bits for some other reason.
105    const LOCAL_FORWARDING_BITS_SPEC: VMLocalForwardingBitsSpec;
106
107    /// A local 1-bit metadata for the mark bit, used by most plans that need to mark live objects. Like with the
108    /// [forwarding bits](crate::vm::ObjectModel::LOCAL_FORWARDING_BITS_SPEC), you can often steal the last bit in
109    /// the object header (due to alignment requirements) for the mark bit. Though some bindings such as the
110    /// OpenJDK binding prefer to have the mark bits in side metadata to allow for bulk operations.
111    const LOCAL_MARK_BIT_SPEC: VMLocalMarkBitSpec;
112
113    #[cfg(feature = "object_pinning")]
114    /// A local 1-bit metadata specification for the pinning bit, used by plans that need to pin objects. It is
115    /// generally in side metadata.
116    const LOCAL_PINNING_BIT_SPEC: VMLocalPinningBitSpec;
117
118    /// A local 2-bit metadata used by the large object space to mark objects and set objects as "newly allocated".
119    /// Used by any plan with large object allocation. It is generally in the header as we can add an extra word
120    /// before the large object to store this metadata. This is fine as the metadata size is insignificant in
121    /// comparison to the object size.
122    //
123    // TODO: Cleanup and place the LOS mark and nursery bits in the header. See here: https://github.com/mmtk/mmtk-core/issues/847
124    const LOCAL_LOS_MARK_NURSERY_SPEC: VMLocalLOSMarkNurserySpec;
125
126    /// Set this to true if the VM binding requires the valid object (VO) bits to be available
127    /// during tracing. If this constant is set to `false`, it is undefined behavior if the binding
128    /// attempts to access VO bits during tracing.
129    ///
130    /// Note that the VO bits is always available during root scanning even if this flag is false,
131    /// which is suitable for using VO bits (and the `is_mmtk_object()` method) for conservative
132    /// stack scanning. However, if a binding is also conservative in finding references during
133    /// object scanning, they need to set this constant to `true`. See the comments of individual
134    /// methods in the `Scanning` trait.
135    ///
136    /// Depending on the internal implementation of mmtk-core, different strategies for handling
137    /// VO bits have different time/space overhead.  mmtk-core will choose the best strategy
138    /// according to the configuration of the VM binding, including this flag.  Currently, setting
139    /// this flag to true does not impose any additional overhead.
140    #[cfg(feature = "vo_bit")]
141    const NEED_VO_BITS_DURING_TRACING: bool = false;
142
143    /// A function to non-atomically load the specified per-object metadata's content.
144    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
145    /// Returns the metadata value.
146    ///
147    /// # Arguments:
148    ///
149    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
150    /// * `object`: is a reference to the target object.
151    /// * `mask`: is an optional mask value for the metadata. This value is used in cases like the forwarding pointer metadata, where some of the bits are reused by other metadata such as the forwarding bits.
152    ///
153    /// # Safety
154    /// This is a non-atomic load, thus not thread-safe.
155    unsafe fn load_metadata<T: MetadataValue>(
156        metadata_spec: &HeaderMetadataSpec,
157        object: ObjectReference,
158        mask: Option<T>,
159    ) -> T {
160        metadata_spec.load::<T>(object.to_header::<VM>(), mask)
161    }
162
163    /// A function to atomically load the specified per-object metadata's content.
164    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
165    /// Returns the metadata value.
166    ///
167    /// # Arguments:
168    ///
169    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
170    /// * `object`: is a reference to the target object.
171    /// * `mask`: is an optional mask value for the metadata. This value is used in cases like the forwarding pointer metadata, where some of the bits are reused by other metadata such as the forwarding bits.
172    /// * `atomic_ordering`: is the atomic ordering for the load operation.
173    fn load_metadata_atomic<T: MetadataValue>(
174        metadata_spec: &HeaderMetadataSpec,
175        object: ObjectReference,
176        mask: Option<T>,
177        ordering: Ordering,
178    ) -> T {
179        metadata_spec.load_atomic::<T>(object.to_header::<VM>(), mask, ordering)
180    }
181
182    /// A function to non-atomically store a value to the specified per-object metadata.
183    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
184    ///
185    /// # Arguments:
186    ///
187    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
188    /// * `object`: is a reference to the target object.
189    /// * `val`: is the new metadata value to be stored.
190    /// * `mask`: is an optional mask value for the metadata. This value is used in cases like the forwarding pointer metadata, where some of the bits are reused by other metadata such as the forwarding bits.
191    ///
192    /// # Safety
193    /// This is a non-atomic store, thus not thread-safe.
194    unsafe fn store_metadata<T: MetadataValue>(
195        metadata_spec: &HeaderMetadataSpec,
196        object: ObjectReference,
197        val: T,
198        mask: Option<T>,
199    ) {
200        metadata_spec.store::<T>(object.to_header::<VM>(), val, mask)
201    }
202
203    /// A function to atomically store a value to the specified per-object metadata.
204    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
205    ///
206    /// # Arguments:
207    ///
208    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
209    /// * `object`: is a reference to the target object.
210    /// * `val`: is the new metadata value to be stored.
211    /// * `mask`: is an optional mask value for the metadata. This value is used in cases like the forwarding pointer metadata, where some of the bits are reused by other metadata such as the forwarding bits.
212    /// * `atomic_ordering`: is the optional atomic ordering for the store operation.
213    fn store_metadata_atomic<T: MetadataValue>(
214        metadata_spec: &HeaderMetadataSpec,
215        object: ObjectReference,
216        val: T,
217        mask: Option<T>,
218        ordering: Ordering,
219    ) {
220        metadata_spec.store_atomic::<T>(object.to_header::<VM>(), val, mask, ordering)
221    }
222
223    /// A function to atomically compare-and-exchange the specified per-object metadata's content.
224    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
225    /// Returns `true` if the operation is successful, and `false` otherwise.
226    ///
227    /// # Arguments:
228    ///
229    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
230    /// * `object`: is a reference to the target object.
231    /// * `old_val`: is the expected current value of the metadata.
232    /// * `new_val`: is the new metadata value to be stored if the compare-and-exchange operation is successful.
233    /// * `mask`: is an optional mask value for the metadata. This value is used in cases like the forwarding pointer metadata, where some of the bits are reused by other metadata such as the forwarding bits.
234    /// * `success_order`: is the atomic ordering used if the operation is successful.
235    /// * `failure_order`: is the atomic ordering used if the operation fails.
236    fn compare_exchange_metadata<T: MetadataValue>(
237        metadata_spec: &HeaderMetadataSpec,
238        object: ObjectReference,
239        old_val: T,
240        new_val: T,
241        mask: Option<T>,
242        success_order: Ordering,
243        failure_order: Ordering,
244    ) -> std::result::Result<T, T> {
245        metadata_spec.compare_exchange::<T>(
246            object.to_header::<VM>(),
247            old_val,
248            new_val,
249            mask,
250            success_order,
251            failure_order,
252        )
253    }
254
255    /// A function to atomically perform an add operation on the specified per-object metadata's content.
256    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
257    /// This is a wrapping add.
258    /// # Returns the old metadata value.
259    ///
260    /// # Arguments:
261    ///
262    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
263    /// * `object`: is a reference to the target object.
264    /// * `val`: is the value to be added to the current value of the metadata.
265    /// * `order`: is the atomic ordering of the fetch-and-add operation.
266    fn fetch_add_metadata<T: MetadataValue>(
267        metadata_spec: &HeaderMetadataSpec,
268        object: ObjectReference,
269        val: T,
270        order: Ordering,
271    ) -> T {
272        metadata_spec.fetch_add::<T>(object.to_header::<VM>(), val, order)
273    }
274
275    /// A function to atomically perform a subtract operation on the specified per-object metadata's content.
276    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
277    /// This is a wrapping sub.
278    /// Returns the old metadata value.
279    ///
280    /// # Arguments:
281    ///
282    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
283    /// * `object`: is a reference to the target object.
284    /// * `val`: is the value to be subtracted from the current value of the metadata.
285    /// * `order`: is the atomic ordering of the fetch-and-add operation.
286    fn fetch_sub_metadata<T: MetadataValue>(
287        metadata_spec: &HeaderMetadataSpec,
288        object: ObjectReference,
289        val: T,
290        order: Ordering,
291    ) -> T {
292        metadata_spec.fetch_sub::<T>(object.to_header::<VM>(), val, order)
293    }
294
295    /// A function to atomically perform a bit-and operation on the specified per-object metadata's content.
296    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
297    /// Returns the old metadata value.
298    ///
299    /// # Arguments:
300    ///
301    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
302    /// * `object`: is a reference to the target object.
303    /// * `val`: is the value to bit-and with the current value of the metadata.
304    /// * `order`: is the atomic ordering of the fetch-and-add operation.
305    fn fetch_and_metadata<T: MetadataValue>(
306        metadata_spec: &HeaderMetadataSpec,
307        object: ObjectReference,
308        val: T,
309        order: Ordering,
310    ) -> T {
311        metadata_spec.fetch_and::<T>(object.to_header::<VM>(), val, order)
312    }
313
314    /// A function to atomically perform a bit-or operation on the specified per-object metadata's content.
315    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
316    /// Returns the old metadata value.
317    ///
318    /// # Arguments:
319    ///
320    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
321    /// * `object`: is a reference to the target object.
322    /// * `val`: is the value to bit-or with the current value of the metadata.
323    /// * `order`: is the atomic ordering of the fetch-and-add operation.
324    fn fetch_or_metadata<T: MetadataValue>(
325        metadata_spec: &HeaderMetadataSpec,
326        object: ObjectReference,
327        val: T,
328        order: Ordering,
329    ) -> T {
330        metadata_spec.fetch_or::<T>(object.to_header::<VM>(), val, order)
331    }
332
333    /// A function to atomically perform an update operation on the specified per-object metadata's content.
334    /// The default implementation assumes the bits defined by the spec are always avilable for MMTk to use. If that is not the case, a binding should override this method, and provide their implementation.
335    /// The semantics of this method are the same as the `fetch_update()` on Rust atomic types.
336    ///
337    /// # Arguments:
338    ///
339    /// * `metadata_spec`: is the header metadata spec that tries to perform the operation.
340    /// * `object`: is a reference to the target object.
341    /// * `val`: is the value to bit-and with the current value of the metadata.
342    /// * `order`: is the atomic ordering of the fetch-and-add operation.
343    ///
344    /// # Returns the old metadata value.
345    fn fetch_update_metadata<T: MetadataValue, F: FnMut(T) -> Option<T> + Copy>(
346        metadata_spec: &HeaderMetadataSpec,
347        object: ObjectReference,
348        set_order: Ordering,
349        fetch_order: Ordering,
350        f: F,
351    ) -> std::result::Result<T, T> {
352        metadata_spec.fetch_update::<T, F>(object.to_header::<VM>(), set_order, fetch_order, f)
353    }
354
355    /// Copy an object and return the address of the new object. Usually in the implementation of this method,
356    /// `alloc_copy()` and `post_copy()` from [`GCWorkerCopyContext`](util/copy/struct.GCWorkerCopyContext.html)
357    /// are used for copying.
358    ///
359    /// Arguments:
360    /// * `from`: The address of the object to be copied.
361    /// * `semantics`: The copy semantic to use.
362    /// * `copy_context`: The `GCWorkerCopyContext` for the GC thread.
363    fn copy(
364        from: ObjectReference,
365        semantics: CopySemantics,
366        copy_context: &mut GCWorkerCopyContext<VM>,
367    ) -> ObjectReference;
368
369    /// Copy an object. This is required
370    /// for delayed-copy collectors such as compacting collectors. During the
371    /// collection, MMTk reserves a region in the heap for an object as per
372    /// requirements found from `ObjectModel` and then asks `ObjectModel` to
373    /// determine what the object's reference will be post-copy. Return the address
374    /// past the end of the copied object.
375    ///
376    /// Arguments:
377    /// * `from`: The address of the object to be copied.
378    /// * `to`: The target location.
379    /// * `region: The start of the region that was reserved for this object.
380    fn copy_to(from: ObjectReference, to: ObjectReference, region: Address) -> Address;
381
382    /// Return the reference that an object will be referred to after it is copied
383    /// to the specified region. Used in delayed-copy collectors such as compacting
384    /// collectors.
385    ///
386    /// Arguments:
387    /// * `from`: The object to be copied.
388    /// * `to`: The start of the region to be copied to.
389    fn get_reference_when_copied_to(from: ObjectReference, to: Address) -> ObjectReference;
390
391    /// Return the size used by an object.
392    ///
393    /// Arguments:
394    /// * `object`: The object to be queried.
395    fn get_current_size(object: ObjectReference) -> usize;
396
397    /// Return the size when an object is copied.
398    ///
399    /// Arguments:
400    /// * `object`: The object to be queried.
401    fn get_size_when_copied(object: ObjectReference) -> usize;
402
403    /// Return the alignment when an object is copied.
404    ///
405    /// Arguments:
406    /// * `object`: The object to be queried.
407    fn get_align_when_copied(object: ObjectReference) -> usize;
408
409    /// Return the alignment offset when an object is copied.
410    ///
411    /// Arguments:
412    /// * `object`: The object to be queried.
413    fn get_align_offset_when_copied(object: ObjectReference) -> usize;
414
415    /// Get the type descriptor for an object.
416    ///
417    /// FIXME: Do we need this? If so, determine lifetime, return byte[]
418    ///
419    /// Arguments:
420    /// * `reference`: The object to be queried.
421    fn get_type_descriptor(reference: ObjectReference) -> &'static [i8];
422
423    /// This is the worst case expansion that can occur due to object size increasing while
424    /// copying. This constant is used to calculate whether a nursery has grown larger than the
425    /// mature space for generational plans.
426    const VM_WORST_CASE_COPY_EXPANSION: f64 = 1.5;
427
428    /// If this is true, the binding guarantees that the object reference's raw address and the
429    /// object start are always the same address.  In other words, an object reference's raw
430    /// address is always equal to the return value of the `ref_to_object_start` method,
431    ///
432    /// This is a very strong guarantee, but it is also helpful for MMTk to
433    /// make some assumptions and optimize for this case.
434    /// If a binding sets this to true, and the related methods return inconsistent results, this is an undefined behavior. MMTk may panic
435    /// if any assertion catches this error, but may also fail silently.
436    const UNIFIED_OBJECT_REFERENCE_ADDRESS: bool = false;
437
438    /// For our allocation result (object_start), the binding may have an offset between the allocation result
439    /// and the raw address of their object reference, i.e. object ref's raw address = object_start + offset.
440    /// The offset could be zero. The offset is not necessary to be
441    /// constant for all the objects. This constant defines the smallest possible offset.
442    ///
443    /// This is used as an indication for MMTk to predict where object references may point to in some algorithms.
444    ///
445    /// We should have the invariant:
446    /// * object ref >= object_start + OBJECT_REF_OFFSET_LOWER_BOUND
447    const OBJECT_REF_OFFSET_LOWER_BOUND: isize;
448
449    /// Return the lowest address of the storage associated with an object. This should be
450    /// the address that a binding gets by an allocation call ([`crate::memory_manager::alloc`]).
451    ///
452    /// Note that the return value needs to satisfy the invariant mentioned in the doc comment of
453    /// [`Self::OBJECT_REF_OFFSET_LOWER_BOUND`].
454    ///
455    /// Arguments:
456    /// * `object`: The object to be queried.
457    fn ref_to_object_start(object: ObjectReference) -> Address;
458
459    /// Return the header base address from an object reference. Any object header metadata
460    /// in the [`crate::vm::ObjectModel`] declares a piece of header metadata with an offset
461    /// from this address. If a binding does not use any header metadata for MMTk, this method
462    /// will not be called, and the binding can simply use `unreachable!()` for the method.
463    ///
464    /// Arguments:
465    /// * `object`: The object to be queried.
466    fn ref_to_header(object: ObjectReference) -> Address;
467
468    /// Dump debugging information for an object.
469    ///
470    /// Arguments:
471    /// * `object`: The object to be dumped.
472    fn dump_object(object: ObjectReference);
473
474    /// Return if an object is valid from the runtime point of view. This is used
475    /// to debug MMTk.
476    fn is_object_sane(_object: ObjectReference) -> bool {
477        true
478    }
479}
480
481pub mod specs {
482    use crate::util::constants::LOG_BITS_IN_WORD;
483    use crate::util::constants::LOG_BYTES_IN_PAGE;
484    use crate::util::constants::LOG_MIN_OBJECT_SIZE;
485    use crate::util::metadata::side_metadata::*;
486    use crate::util::metadata::{
487        header_metadata::HeaderMetadataSpec,
488        side_metadata::{SideMetadataOffset, SideMetadataSpec},
489        MetadataSpec,
490    };
491
492    // This macro is invoked in define_vm_metadata_global_spec or define_vm_metadata_local_spec.
493    // Use those two to define a new VM metadata spec.
494    macro_rules! define_vm_metadata_spec {
495        ($(#[$outer:meta])*$spec_name: ident, $is_global: expr, $log_num_bits: expr, $side_min_obj_size: expr) => {
496            $(#[$outer])*
497            pub struct $spec_name(MetadataSpec);
498            impl $spec_name {
499                /// The number of bits (in log2) that are needed for the spec.
500                pub const LOG_NUM_BITS: usize = $log_num_bits;
501
502                /// Whether this spec is global or local. For side metadata, the binding needs to make sure
503                /// global specs are laid out after another global spec, and local specs are laid
504                /// out after another local spec. Otherwise, there will be an assertion failure.
505                pub const IS_GLOBAL: bool = $is_global;
506
507                /// Declare that the VM uses in-header metadata for this metadata type.
508                /// For the specification of the `bit_offset` argument, please refer to
509                /// the document of `[crate::util::metadata::header_metadata::HeaderMetadataSpec.bit_offset]`.
510                /// The binding needs to make sure that the bits used for a spec in the header do not conflict with
511                /// the bits of another spec (unless it is specified that some bits may be reused).
512                pub const fn in_header(bit_offset: isize) -> Self {
513                    Self(MetadataSpec::InHeader(HeaderMetadataSpec {
514                        bit_offset,
515                        num_of_bits: 1 << Self::LOG_NUM_BITS,
516                    }))
517                }
518
519                /// Declare that the VM uses side metadata for this metadata type,
520                /// and the side metadata is the first of its kind (global or local).
521                /// The first global or local side metadata should be declared with `side_first()`,
522                /// and the rest side metadata should be declared with `side_after()` after a defined
523                /// side metadata of the same kind (global or local). Logically, all the declarations
524                /// create two list of side metadata, one for global, and one for local.
525                pub const fn side_first() -> Self {
526                    if Self::IS_GLOBAL {
527                        Self(MetadataSpec::OnSide(SideMetadataSpec {
528                            name: stringify!($spec_name),
529                            is_global: Self::IS_GLOBAL,
530                            offset: GLOBAL_SIDE_METADATA_VM_BASE_OFFSET,
531                            log_num_of_bits: Self::LOG_NUM_BITS,
532                            log_bytes_in_region: $side_min_obj_size as usize,
533                        }))
534                    } else {
535                        Self(MetadataSpec::OnSide(SideMetadataSpec {
536                            name: stringify!($spec_name),
537                            is_global: Self::IS_GLOBAL,
538                            offset: LOCAL_SIDE_METADATA_VM_BASE_OFFSET,
539                            log_num_of_bits: Self::LOG_NUM_BITS,
540                            log_bytes_in_region: $side_min_obj_size as usize,
541                        }))
542                    }
543                }
544
545                /// Declare that the VM uses side metadata for this metadata type,
546                /// and the side metadata should be laid out after the given side metadata spec.
547                /// The first global or local side metadata should be declared with `side_first()`,
548                /// and the rest side metadata should be declared with `side_after()` after a defined
549                /// side metadata of the same kind (global or local). Logically, all the declarations
550                /// create two list of side metadata, one for global, and one for local.
551                pub const fn side_after(spec: &MetadataSpec) -> Self {
552                    assert!(spec.is_on_side());
553                    let side_spec = spec.extract_side_spec();
554                    assert!(side_spec.is_global == Self::IS_GLOBAL);
555                    Self(MetadataSpec::OnSide(SideMetadataSpec {
556                        name: stringify!($spec_name),
557                        is_global: Self::IS_GLOBAL,
558                        offset: SideMetadataOffset::layout_after(side_spec),
559                        log_num_of_bits: Self::LOG_NUM_BITS,
560                        log_bytes_in_region: $side_min_obj_size as usize,
561                    }))
562                }
563
564                /// Return the inner `[crate::util::metadata::MetadataSpec]` for the metadata type.
565                pub const fn as_spec(&self) -> &MetadataSpec {
566                    &self.0
567                }
568
569                /// Return the number of bits for the metadata type.
570                pub const fn num_bits(&self) -> usize {
571                    1 << $log_num_bits
572                }
573            }
574            impl std::ops::Deref for $spec_name {
575                type Target = MetadataSpec;
576                fn deref(&self) -> &Self::Target {
577                    self.as_spec()
578                }
579            }
580        };
581    }
582
583    // Log bit: 1 bit per object, global
584    define_vm_metadata_spec!(
585        /// 1-bit global metadata to log an object.
586        VMGlobalLogBitSpec,
587        true,
588        0,
589        LOG_MIN_OBJECT_SIZE
590    );
591    // Forwarding pointer: word size per object, local
592    define_vm_metadata_spec!(
593        /// 1-word local metadata for spaces that may copy objects.
594        /// This metadata has to be stored in the header.
595        /// This metadata can be defined at a position within the object payload.
596        /// As a forwarding pointer is only stored in dead objects which is not
597        /// accessible by the language, it is okay that store a forwarding pointer overwrites object payload
598        VMLocalForwardingPointerSpec,
599        false,
600        LOG_BITS_IN_WORD,
601        LOG_MIN_OBJECT_SIZE
602    );
603    // Forwarding bits: 2 bits per object, local
604    define_vm_metadata_spec!(
605        /// 2-bit local metadata for spaces that store a forwarding state for objects.
606        /// If this spec is defined in the header, it can be defined with a position of the lowest 2 bits in the forwarding pointer.
607        VMLocalForwardingBitsSpec,
608        false,
609        1,
610        LOG_MIN_OBJECT_SIZE
611    );
612    // Mark bit: 1 bit per object, local
613    define_vm_metadata_spec!(
614        /// 1-bit local metadata for spaces that need to mark an object.
615        VMLocalMarkBitSpec,
616        false,
617        0,
618        LOG_MIN_OBJECT_SIZE
619    );
620    // Pinning bit: 1 bit per object, local
621    define_vm_metadata_spec!(
622        /// 1-bit local metadata for spaces that support pinning.
623        VMLocalPinningBitSpec,
624        false,
625        0,
626        LOG_MIN_OBJECT_SIZE
627    );
628    // Mark&nursery bits for LOS: 2 bit per page, local
629    define_vm_metadata_spec!(
630        /// 2-bits local metadata for the large object space. The two bits serve as
631        /// the mark bit and the nursery bit.
632        VMLocalLOSMarkNurserySpec,
633        false,
634        1,
635        LOG_BYTES_IN_PAGE
636    );
637}