mmtk/util/metadata/mark_bit.rs
1use crate::util::Address;
2use crate::util::ObjectReference;
3use crate::vm::ObjectModel;
4use crate::vm::VMBinding;
5use crate::vm::VMLocalMarkBitSpec;
6use std::sync::atomic::Ordering;
7
8impl VMLocalMarkBitSpec {
9 /// Set the mark bit for the object to 1
10 pub fn mark<VM: VMBinding>(&self, object: ObjectReference, ordering: Ordering) {
11 self.store_atomic::<VM, u8>(object, 1, None, ordering);
12 }
13
14 /// Test if the mark bit for the object is set (1)
15 pub fn is_marked<VM: VMBinding>(&self, object: ObjectReference, ordering: Ordering) -> bool {
16 self.load_atomic::<VM, u8>(object, None, ordering) == 1
17 }
18}
19
20/// This provides an abstraction of the mark bit. It abstracts over the difference between side mark bits and in-header mark bits,
21/// and provides efficient implementation for each case.
22///
23/// The key difference between side and in-header mark bit is what the mark state represents.
24///
25/// * Side mark bit
26/// We always use 1 for the marked state. So we do not need to set mark bit for new objects (mark bit 0). In each GC, we do bulk zeroing
27/// to reset the mark bit to 0 before tracing or after tracing. During tracing, we mark objects with the state 1 as usual.
28/// * In-header mark bit
29/// We flip the mark state in every GC. For example, if 1 means marked in the current GC, 1 will mean unmarked in the next GC.
30/// With this approach, we do not need to reset mark bit for each object, as the value represents unmarked in the next GC.
31/// However, with in-header mark bit, we have to set mark bit for newly allocated objects.
32///
33/// A policy could use this struct instead of the raw mark bit. It has to call all the methods prefixed with `on_`
34/// such as `on_object_metadata_initialization()`, `on_global_prepare()`, `on_block_prepare()`, and `on_global_release()`.
35// TODO: Currently only ImmortalSpace uses this struct. Any policy that needs mark bit can use this (immix, mark compact, mark sweep).
36// We should do some refactoring for other policies as well.
37pub struct MarkState {
38 /// This value represents the marked state. If the mark bit is this value, the object is considered as marked.
39 /// If the mark bit is on side, we always use 1 as the marked state. We do bulk zeroing to reset mark bits before GCs
40 /// If the mark bit is in header, we flip the marked state in every GC, so we do not need to reset the mark bit for live objects.
41 state: u8,
42}
43
44impl MarkState {
45 pub fn new() -> Self {
46 Self { state: 1 }
47 }
48
49 fn unmarked_state(&self) -> u8 {
50 self.state ^ 1
51 }
52
53 /// Check if the object is marked
54 pub fn is_marked<VM: VMBinding>(&self, object: ObjectReference) -> bool {
55 let state = VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.load_atomic::<VM, u8>(
56 object,
57 None,
58 Ordering::SeqCst,
59 );
60 state == self.state
61 }
62
63 /// Attempt to mark an object. If the object is marked by this invocation, return true.
64 /// Otherwise return false -- the object was marked by others.
65 pub fn test_and_mark<VM: VMBinding>(&self, object: ObjectReference) -> bool {
66 loop {
67 let old_value = VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.load_atomic::<VM, u8>(
68 object,
69 None,
70 Ordering::SeqCst,
71 );
72 if old_value == self.state {
73 return false;
74 }
75
76 if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
77 .compare_exchange_metadata::<VM, u8>(
78 object,
79 old_value,
80 self.state,
81 None,
82 Ordering::SeqCst,
83 Ordering::SeqCst,
84 )
85 .is_ok()
86 {
87 break;
88 }
89 }
90 true
91 }
92
93 /// This has to be called during object initialization.
94 pub fn on_object_metadata_initialization<VM: VMBinding>(&self, object: ObjectReference) {
95 // If it is in header, we have to set the mark bit for every newly allocated object
96 if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_in_header() {
97 VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.store_atomic::<VM, u8>(
98 object,
99 self.unmarked_state(),
100 None,
101 Ordering::SeqCst,
102 );
103 }
104 }
105
106 /// This has to be called in the global preparation of a space
107 pub fn on_global_prepare<VM: VMBinding>(&mut self) {}
108
109 /// This has to be called when a space resets its memory regions. This can be either called before the GC tracing, or
110 /// after a GC tracing (eagerly). This method will reset the mark bit. The policy should not use the mark bit before
111 /// doing another tracing.
112 pub fn on_block_reset<VM: VMBinding>(&self, start: Address, size: usize) {
113 if let crate::util::metadata::MetadataSpec::OnSide(side) =
114 *VM::VMObjectModel::LOCAL_MARK_BIT_SPEC
115 {
116 side.bzero_metadata(start, size);
117 }
118 }
119
120 /// This has to be called in the global release of a space
121 pub fn on_global_release<VM: VMBinding>(&mut self) {
122 if VM::VMObjectModel::LOCAL_MARK_BIT_SPEC.is_in_header() {
123 // If it is in header, we flip it. In this case, we do not need to reset the bits for marked objects
124 self.state = self.unmarked_state()
125 }
126 }
127}