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}