Skip to content

Commit 2d5bae7

Browse files
authored
Add missing docs for the vm module (#1021)
This PR is a step towards #309. * Add some tests and documents to clarify `HeaderMetadata.bit_offset` (related discussion: https://mmtk.zulipchat.com/#narrow/stream/315620-Porting/topic/ScalaNative.2FMMTK/near/398587245). * Modify the macro `define_vm_metadata_spec!` to allow adding docs for the generated types. * Add missing docs for public items in the `vm` module. Please feel free to make edits to the PR if there is any issue.
1 parent 6a5e5ff commit 2d5bae7

File tree

6 files changed

+158
-6
lines changed

6 files changed

+158
-6
lines changed

src/util/metadata/header_metadata.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,20 @@ const BITS_IN_U64: usize = 1 << LOG_BITS_IN_U64;
2222
/// For performance reasons, objects of this struct should be constants.
2323
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
2424
pub struct HeaderMetadataSpec {
25+
/// `bit_offset` is the index of the starting bit from which the data should be read or written.
26+
/// It is counted from the right (least significant bit) of the byte.
27+
/// Positive values refer to the bit positions within the current byte, starting with 0 for the
28+
/// least significant bit (rightmost) up to 7 for the most significant bit (leftmost).
29+
/// Negative values are used to refer to bit positions in the previous bytes, where -1 indicates
30+
/// the most significant bit (leftmost) of the byte immediately before the current one.
2531
pub bit_offset: isize,
32+
/// `num_of_bits` specifies the number of consecutive bits to be read or written starting from the `bit_offset`.
33+
/// This value is used to define the size of the data field in bits. For instance, if `num_of_bits` is set to 1,
34+
/// only a single bit is considered, whereas a value of 8 would indicate a full byte.
35+
/// This field must be a positive integer and typically should not exceed the size of the data type that
36+
/// will hold the extracted value (for example, 8 bits for a `u8`, 16 bits for a `u16`, etc.).
37+
/// The `num_of_bits` together with the `bit_offset` enables the extraction of bit fields of arbitrary
38+
/// length and position, facilitating bit-level data manipulation.
2639
pub num_of_bits: usize,
2740
}
2841

@@ -500,6 +513,59 @@ mod tests {
500513
spec.assert_spec::<u8>();
501514
}
502515

516+
#[test]
517+
fn test_negative_bit_offset() {
518+
let spec = HeaderMetadataSpec {
519+
bit_offset: -1,
520+
num_of_bits: 1,
521+
};
522+
spec.assert_spec::<u8>();
523+
assert_eq!(spec.get_shift_and_mask_for_bits(), (7, 0b1000_0000));
524+
assert_eq!(spec.byte_offset(), -1);
525+
assert_eq!(spec.get_bits_from_u8(0b1000_0000), 1);
526+
assert_eq!(spec.get_bits_from_u8(0b0111_1111), 0);
527+
528+
let spec = HeaderMetadataSpec {
529+
bit_offset: -2,
530+
num_of_bits: 1,
531+
};
532+
spec.assert_spec::<u8>();
533+
assert_eq!(spec.get_shift_and_mask_for_bits(), (6, 0b0100_0000));
534+
assert_eq!(spec.byte_offset(), -1);
535+
assert_eq!(spec.get_bits_from_u8(0b0100_0000), 1);
536+
assert_eq!(spec.get_bits_from_u8(0b1011_1111), 0);
537+
538+
let spec = HeaderMetadataSpec {
539+
bit_offset: -7,
540+
num_of_bits: 1,
541+
};
542+
spec.assert_spec::<u8>();
543+
assert_eq!(spec.get_shift_and_mask_for_bits(), (1, 0b0000_0010));
544+
assert_eq!(spec.byte_offset(), -1);
545+
assert_eq!(spec.get_bits_from_u8(0b0000_0010), 1);
546+
assert_eq!(spec.get_bits_from_u8(0b1111_1101), 0);
547+
548+
let spec = HeaderMetadataSpec {
549+
bit_offset: -8,
550+
num_of_bits: 1,
551+
};
552+
spec.assert_spec::<u8>();
553+
assert_eq!(spec.get_shift_and_mask_for_bits(), (0, 0b0000_0001));
554+
assert_eq!(spec.byte_offset(), -1);
555+
assert_eq!(spec.get_bits_from_u8(0b0000_0001), 1);
556+
assert_eq!(spec.get_bits_from_u8(0b1111_1110), 0);
557+
558+
let spec = HeaderMetadataSpec {
559+
bit_offset: -9,
560+
num_of_bits: 1,
561+
};
562+
spec.assert_spec::<u8>();
563+
assert_eq!(spec.get_shift_and_mask_for_bits(), (7, 0b1000_0000));
564+
assert_eq!(spec.byte_offset(), -2);
565+
assert_eq!(spec.get_bits_from_u8(0b1000_0000), 1);
566+
assert_eq!(spec.get_bits_from_u8(0b0111_1111), 0);
567+
}
568+
503569
#[test]
504570
fn test_get_bits_from_u8() {
505571
// 1 bit
@@ -508,6 +574,7 @@ mod tests {
508574
num_of_bits: 1,
509575
};
510576
assert_eq!(spec.get_shift_and_mask_for_bits(), (0, 0b1));
577+
assert_eq!(spec.byte_offset(), 0);
511578
assert_eq!(spec.get_bits_from_u8(0b0000_0001), 1);
512579
assert_eq!(spec.get_bits_from_u8(0b1111_1110), 0);
513580

src/vm/collection.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use crate::{scheduler::*, Mutator};
55

66
/// Thread context for the spawned GC thread. It is used by spawn_gc_thread.
77
pub enum GCThreadContext<VM: VMBinding> {
8+
/// The GC thread to spawn is a controller thread. There is only one controller thread.
89
Controller(Box<GCController<VM>>),
10+
/// The GC thread to spawn is a worker thread. There can be multiple worker threads.
911
Worker(Box<GCWorker<VM>>),
1012
}
1113

src/vm/edge_shape.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ fn a_simple_edge_should_have_the_same_size_as_a_pointer() {
126126

127127
/// A abstract memory slice represents a piece of **heap** memory.
128128
pub trait MemorySlice: Send + Debug + PartialEq + Eq + Clone + Hash {
129+
/// The associate type to define how to access edges from a memory slice.
129130
type Edge: Edge;
131+
/// The associate type to define how to iterate edges in a memory slice.
130132
type EdgeIterator: Iterator<Item = Self::Edge>;
131133
/// Iterate object edges within the slice. If there are non-reference values in the slice, the iterator should skip them.
132134
fn iter_edges(&self) -> Self::EdgeIterator;

src/vm/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::util::constants::*;
1919

2020
mod active_plan;
2121
mod collection;
22+
/// Allows MMTk to access edges in a VM-defined way.
2223
pub mod edge_shape;
2324
pub(crate) mod object_model;
2425
mod reference_glue;
@@ -44,10 +45,15 @@ pub trait VMBinding
4445
where
4546
Self: Sized + 'static + Send + Sync + Default,
4647
{
48+
/// The binding's implementation of [`crate::vm::ObjectModel`].
4749
type VMObjectModel: ObjectModel<Self>;
50+
/// The binding's implementation of [`crate::vm::Scanning`].
4851
type VMScanning: Scanning<Self>;
52+
/// The binding's implementation of [`crate::vm::Collection`].
4953
type VMCollection: Collection<Self>;
54+
/// The binding's implementation of [`crate::vm::ActivePlan`].
5055
type VMActivePlan: ActivePlan<Self>;
56+
/// The binding's implementation of [`crate::vm::ReferenceGlue`].
5157
type VMReferenceGlue: ReferenceGlue<Self>;
5258

5359
/// The type of edges in this VM.

src/vm/object_model.rs

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -488,17 +488,36 @@ pub mod specs {
488488
// This macro is invoked in define_vm_metadata_global_spec or define_vm_metadata_local_spec.
489489
// Use those two to define a new VM metadata spec.
490490
macro_rules! define_vm_metadata_spec {
491-
($spec_name: ident, $is_global: expr, $log_num_bits: expr, $side_min_obj_size: expr) => {
491+
($(#[$outer:meta])*$spec_name: ident, $is_global: expr, $log_num_bits: expr, $side_min_obj_size: expr) => {
492+
$(#[$outer])*
492493
pub struct $spec_name(MetadataSpec);
493494
impl $spec_name {
495+
/// The number of bits (in log2) that are needed for the spec.
494496
pub const LOG_NUM_BITS: usize = $log_num_bits;
497+
498+
/// Whether this spec is global or local. For side metadata, the binding needs to make sure
499+
/// global specs are laid out after another global spec, and local specs are laid
500+
/// out after another local spec. Otherwise, there will be an assertion failure.
495501
pub const IS_GLOBAL: bool = $is_global;
502+
503+
/// Declare that the VM uses in-header metadata for this metadata type.
504+
/// For the specification of the `bit_offset` argument, please refer to
505+
/// the document of `[crate::util::metadata::header_metadata::HeaderMetadataSpec.bit_offset]`.
506+
/// The binding needs to make sure that the bits used for a spec in the header do not conflict with
507+
/// the bits of another spec (unless it is specified that some bits may be reused).
496508
pub const fn in_header(bit_offset: isize) -> Self {
497509
Self(MetadataSpec::InHeader(HeaderMetadataSpec {
498510
bit_offset,
499511
num_of_bits: 1 << Self::LOG_NUM_BITS,
500512
}))
501513
}
514+
515+
/// Declare that the VM uses side metadata for this metadata type,
516+
/// and the side metadata is the first of its kind (global or local).
517+
/// The first global or local side metadata should be declared with `side_first()`,
518+
/// and the rest side metadata should be declared with `side_after()` after a defined
519+
/// side metadata of the same kind (global or local). Logically, all the declarations
520+
/// create two list of side metadata, one for global, and one for local.
502521
pub const fn side_first() -> Self {
503522
if Self::IS_GLOBAL {
504523
Self(MetadataSpec::OnSide(SideMetadataSpec {
@@ -518,6 +537,13 @@ pub mod specs {
518537
}))
519538
}
520539
}
540+
541+
/// Declare that the VM uses side metadata for this metadata type,
542+
/// and the side metadata should be laid out after the given side metadata spec.
543+
/// The first global or local side metadata should be declared with `side_first()`,
544+
/// and the rest side metadata should be declared with `side_after()` after a defined
545+
/// side metadata of the same kind (global or local). Logically, all the declarations
546+
/// create two list of side metadata, one for global, and one for local.
521547
pub const fn side_after(spec: &MetadataSpec) -> Self {
522548
assert!(spec.is_on_side());
523549
let side_spec = spec.extract_side_spec();
@@ -530,9 +556,13 @@ pub mod specs {
530556
log_bytes_in_region: $side_min_obj_size as usize,
531557
}))
532558
}
559+
560+
/// Return the inner `[crate::util::metadata::MetadataSpec]` for the metadata type.
533561
pub const fn as_spec(&self) -> &MetadataSpec {
534562
&self.0
535563
}
564+
565+
/// Return the number of bits for the metadata type.
536566
pub const fn num_bits(&self) -> usize {
537567
1 << $log_num_bits
538568
}
@@ -547,20 +577,57 @@ pub mod specs {
547577
}
548578

549579
// Log bit: 1 bit per object, global
550-
define_vm_metadata_spec!(VMGlobalLogBitSpec, true, 0, LOG_MIN_OBJECT_SIZE);
580+
define_vm_metadata_spec!(
581+
#[doc = "1-bit global metadata to log an object."]
582+
VMGlobalLogBitSpec,
583+
true,
584+
0,
585+
LOG_MIN_OBJECT_SIZE
586+
);
551587
// Forwarding pointer: word size per object, local
552588
define_vm_metadata_spec!(
589+
#[doc = "1-word local metadata for spaces that may copy objects."]
590+
#[doc = "This metadata has to be stored in the header."]
591+
#[doc = "This metadata can be defined at a position within the object payload."]
592+
#[doc = "As a forwarding pointer is only stored in dead objects which is not"]
593+
#[doc = "accessible by the language, it is okay that store a forwarding pointer overwrites object payload"]
553594
VMLocalForwardingPointerSpec,
554595
false,
555596
LOG_BITS_IN_WORD,
556597
LOG_MIN_OBJECT_SIZE
557598
);
558599
// Forwarding bits: 2 bits per object, local
559-
define_vm_metadata_spec!(VMLocalForwardingBitsSpec, false, 1, LOG_MIN_OBJECT_SIZE);
600+
define_vm_metadata_spec!(
601+
#[doc = "2-bit local metadata for spaces that store a forwarding state for objects."]
602+
#[doc = "If this spec is defined in the header, it can be defined with a position of the lowest 2 bits in the forwarding pointer."]
603+
VMLocalForwardingBitsSpec,
604+
false,
605+
1,
606+
LOG_MIN_OBJECT_SIZE
607+
);
560608
// Mark bit: 1 bit per object, local
561-
define_vm_metadata_spec!(VMLocalMarkBitSpec, false, 0, LOG_MIN_OBJECT_SIZE);
609+
define_vm_metadata_spec!(
610+
#[doc = "1-bit local metadata for spaces that need to mark an object."]
611+
VMLocalMarkBitSpec,
612+
false,
613+
0,
614+
LOG_MIN_OBJECT_SIZE
615+
);
562616
// Pinning bit: 1 bit per object, local
563-
define_vm_metadata_spec!(VMLocalPinningBitSpec, false, 0, LOG_MIN_OBJECT_SIZE);
617+
define_vm_metadata_spec!(
618+
#[doc = "1-bit local metadata for spaces that support pinning."]
619+
VMLocalPinningBitSpec,
620+
false,
621+
0,
622+
LOG_MIN_OBJECT_SIZE
623+
);
564624
// Mark&nursery bits for LOS: 2 bit per page, local
565-
define_vm_metadata_spec!(VMLocalLOSMarkNurserySpec, false, 1, LOG_BYTES_IN_PAGE);
625+
define_vm_metadata_spec!(
626+
#[doc = "2-bits local metadata for the large object space. The two bits serve as"]
627+
#[doc = "the mark bit and the nursery bit."]
628+
VMLocalLOSMarkNurserySpec,
629+
false,
630+
1,
631+
LOG_BYTES_IN_PAGE
632+
);
566633
}

src/vm/scanning.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,14 @@ pub trait Scanning<VM: VMBinding> {
242242
/// Return whether the VM supports return barriers. This is unused at the moment.
243243
fn supports_return_barrier() -> bool;
244244

245+
/// Prepare for another round of root scanning in the same GC. Some GC algorithms
246+
/// need multiple transitive closures, and each transitive closure starts from
247+
/// root scanning. We expect the binding to provide the same root set for every
248+
/// round of root scanning in the same GC. Bindings can use this call to get
249+
/// ready for another round of root scanning to make sure that the same root
250+
/// set will be returned in the upcoming calls of root scanning methods,
251+
/// such as [`crate::vm::Scanning::scan_roots_in_mutator_thread`] and
252+
/// [`crate::vm::Scanning::scan_vm_specific_roots`].
245253
fn prepare_for_roots_re_scanning();
246254

247255
/// Process weak references.

0 commit comments

Comments
 (0)