mmtk/util/metadata/vo_bit/
helper.rs

1//! This module updates of VO bits during GC.  It is used for spaces that do not clear the metadata
2//! of some dead objects during GC.  Currently, only ImmixSpace is affected.
3//!
4//! | Policy            | When are VO bits of dead objects cleared                      |
5//! |-------------------|---------------------------------------------------------------|
6//! | MarkSweepSpace    | when sweeping cells of dead objects                           |
7//! | MarkCompactSpace  | when compacting                                               |
8//! | CopySpace         | when releasing the space                                      |
9//!
10//! The policies listed above trivially clear the VO bits for dead objects (individually or in
11//! bulk), and make the VO bits available during tracing.
12//!
13//! For ImmixSpace, if a line contains both live and dead objects, live objects will be traced,
14//! but dead objects will not be visited.  Therefore we cannot clear the VO bits of individual
15//! dead objects.  We cannot clear all VO bits for the line in bulk because it contains live
16//! objects.  This module updates the VO bits for such regions (e.g. Immix lines, or Immix blocks
17//! if Immix is configured to be block-only).
18//!
19//! We implement several strategies depending on whether mmtk-core or the VM binding also requires
20//! the VO bits to also be available during tracing.
21//!
22//! The handling is very sensitive to `VOBitUpdateStrategy`, and may be a bit verbose.
23//! We abstract VO-bit-related code out of the main GC algorithms (such as Immix) to make it more
24//! readable.
25
26use atomic::Ordering;
27
28use crate::{
29    util::{
30        linear_scan::Region,
31        metadata::{vo_bit, MetadataSpec},
32        ObjectReference,
33    },
34    vm::{ObjectModel, VMBinding},
35};
36
37/// The strategy to update the valid object (VO) bits.
38///
39/// Each stategy has its strength and limitation.  We should choose a strategy according to the
40/// configuration of the VM binding.
41///
42/// Current experiments show that the `CopyFromMarkBits` strategy is faster while also makes the
43/// VO bits available during tracing.  We also include the `ClearAndReconstruct` strategy because
44///
45/// 1.  It was the strategy described in the original paper that described the algorithm for
46///     filtering roots using VO bits for stack-conservative GC.  See: *Fast Conservative Garbage
47///     Collection* published in OOPSLA'14 <https://dl.acm.org/doi/10.1145/2660193.2660198>
48/// 2.  It does not require mark bits to be on the side.  It will be needed if we implement
49///     in-header mark bits in the future.
50#[derive(Debug)]
51enum VOBitUpdateStrategy {
52    /// Clear all VO bits after stacks are scanned, and reconstruct the VO bits during tracing.
53    ///
54    /// Pros:
55    /// -   Proven to work by published paper.
56    ///
57    /// Cons:
58    /// -   VO bits are not available during tracing.
59    ClearAndReconstruct,
60    /// Copy the mark bits metadata over to the VO bits metadata after tracing.
61    ///
62    /// Pros:
63    /// -   VO bits are available during tracing.
64    /// -   Faster according to current experiment.
65    ///
66    /// Cons:
67    /// -   Requires marking bits to be on the side.
68    CopyFromMarkBits,
69}
70
71impl VOBitUpdateStrategy {
72    /// Return `true` if the VO bit metadata is available during tracing.
73    pub fn vo_bit_available_during_tracing(&self) -> bool {
74        match *self {
75            VOBitUpdateStrategy::ClearAndReconstruct => false,
76            VOBitUpdateStrategy::CopyFromMarkBits => true,
77        }
78    }
79}
80
81/// Select a strategy for the VM.  It is a `const` function so it always returns the same strategy
82/// for a given VM.
83const fn strategy<VM: VMBinding>() -> VOBitUpdateStrategy {
84    // CopyFromMarkBits performs better than ClearAndReconstruct, and it also allows using
85    // VO bits during tracing. We use it as the default strategy.
86    // TODO: Revisit this choice in the future if non-trivial changes are made and the performance
87    // characterestics may change for the strategies.
88    match VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.as_spec() {
89        // Note that currently ImmixSpace doesn't support in-header mark bits,
90        // but the DummyVM for testing declares mark bits to be "in header" as a place holder
91        // because it never runs GC.
92        MetadataSpec::InHeader(_) => VOBitUpdateStrategy::ClearAndReconstruct,
93        MetadataSpec::OnSide(_) => VOBitUpdateStrategy::CopyFromMarkBits,
94    }
95}
96
97pub(crate) fn validate_config<VM: VMBinding>() {
98    assert!(
99        !(VM::VMObjectModel::NEED_VO_BITS_DURING_TRACING
100            && VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
101                .as_spec()
102                .is_in_header()),
103        "The VM binding needs VO bits during tracing but also has in-header mark bits.  \
104We currently don't have an appropriate strategy for this case."
105    );
106
107    let s = strategy::<VM>();
108    match s {
109        VOBitUpdateStrategy::ClearAndReconstruct => {
110            // Always valid
111        }
112        VOBitUpdateStrategy::CopyFromMarkBits => {
113            let mark_bit_spec = VM::VMObjectModel::LOCAL_MARK_BIT_SPEC;
114            assert!(
115                mark_bit_spec.is_on_side(),
116                "The {s:?} strategy requires the mark bits to be on the side."
117            );
118
119            let mark_bit_meta = mark_bit_spec.extract_side_spec();
120            let vo_bit_meta = vo_bit::VO_BIT_SIDE_METADATA_SPEC;
121
122            assert_eq!(
123                mark_bit_meta.log_bytes_in_region,
124                vo_bit_meta.log_bytes_in_region,
125                "The {s:?} strategy requires the mark bits to have the same granularity as the VO bits."
126            );
127            assert_eq!(mark_bit_meta.log_num_of_bits, vo_bit_meta.log_num_of_bits,
128                "The {s:?} strategy requires the mark bits to have the same number of bits per object as the VO bits.");
129        }
130    }
131}
132
133pub(crate) fn need_to_clear_vo_bits_before_tracing<VM: VMBinding>() -> bool {
134    match strategy::<VM>() {
135        VOBitUpdateStrategy::ClearAndReconstruct => true,
136        VOBitUpdateStrategy::CopyFromMarkBits => false,
137    }
138}
139
140pub(crate) fn on_trace_object<VM: VMBinding>(object: ObjectReference) {
141    if strategy::<VM>().vo_bit_available_during_tracing() {
142        // If the VO bits are available during tracing,
143        // we validate the objects we trace using the VO bits.
144        debug_assert!(
145            vo_bit::is_vo_bit_set(object),
146            "{:x}: VO bit not set",
147            object
148        );
149    }
150}
151
152pub(crate) fn on_object_marked<VM: VMBinding>(object: ObjectReference) {
153    match strategy::<VM>() {
154        VOBitUpdateStrategy::ClearAndReconstruct => {
155            // In this strategy, we set the VO bit when an object is marked.
156            vo_bit::set_vo_bit(object);
157        }
158        VOBitUpdateStrategy::CopyFromMarkBits => {
159            // VO bit was not cleared before tracing in this strategy.  Do nothing.
160        }
161    }
162}
163
164pub(crate) fn on_object_forwarded<VM: VMBinding>(new_object: ObjectReference) {
165    match strategy::<VM>() {
166        VOBitUpdateStrategy::ClearAndReconstruct => {
167            // In this strategy, we set the VO bit of the to-space object when forwarded.
168            vo_bit::set_vo_bit(new_object);
169        }
170        VOBitUpdateStrategy::CopyFromMarkBits => {
171            // In this strategy, we will copy mark bits to VO bits.
172            // We need to set mark bits for to-space objects, too.
173            VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.store_atomic::<VM, u8>(
174                new_object,
175                1,
176                None,
177                Ordering::SeqCst,
178            );
179
180            // We set the VO bit for the to-space object eagerly.
181            vo_bit::set_vo_bit(new_object);
182        }
183    }
184}
185
186pub(crate) fn on_region_swept<VM: VMBinding, R: Region>(region: &R, is_occupied: bool) {
187    match strategy::<VM>() {
188        VOBitUpdateStrategy::ClearAndReconstruct => {
189            // Do nothing.  The VO bit metadata is already reconstructed.
190        }
191        VOBitUpdateStrategy::CopyFromMarkBits => {
192            // In this strategy, we need to update the VO bits state after marking.
193            if is_occupied {
194                // If the block has live objects, copy the VO bits from mark bits.
195                vo_bit::bcopy_vo_bit_from_mark_bit::<VM>(region.start(), R::BYTES);
196            } else {
197                // If the block has no live objects, simply clear the VO bits.
198                vo_bit::bzero_vo_bit(region.start(), R::BYTES);
199            }
200        }
201    }
202}