mmtk/policy/sft.rs
1use crate::plan::VectorObjectQueue;
2use crate::scheduler::GCWorker;
3use crate::util::*;
4use crate::vm::VMBinding;
5use std::marker::PhantomData;
6
7/// Space Function Table (SFT).
8///
9/// This trait captures functions that reflect _space-specific per-object
10/// semantics_. These functions are implemented for each object via a special
11/// space-based dynamic dispatch mechanism where the semantics are _not_
12/// determined by the object's _type_, but rather, are determined by the _space_
13/// that the object is in.
14///
15/// The underlying mechanism exploits the fact that spaces use the address space
16/// at an MMTk chunk granularity with the consequence that each chunk maps to
17/// exactluy one space, so knowing the chunk for an object reveals its space.
18/// The dispatch then works by performing simple address arithmetic on the object
19/// reference to find a chunk index which is used to index a table which returns
20/// the space. The relevant function is then dispatched against that space
21/// object.
22///
23/// We use the SFT trait to simplify typing for Rust, so our table is a
24/// table of SFT rather than Space.
25pub trait SFT {
26 /// The space name
27 fn name(&self) -> &'static str;
28
29 /// Get forwarding pointer if the object is forwarded.
30 fn get_forwarded_object(&self, _object: ObjectReference) -> Option<ObjectReference> {
31 None
32 }
33
34 /// Is the object live, determined by the policy?
35 fn is_live(&self, object: ObjectReference) -> bool;
36
37 /// Is the object reachable, determined by the policy?
38 /// Note: Objects in ImmortalSpace may have `is_live = true` but are actually unreachable.
39 fn is_reachable(&self, object: ObjectReference) -> bool {
40 self.is_live(object)
41 }
42
43 // Functions for pinning/unpining and checking if an object is pinned
44 // For non moving policies, all the objects are considered as forever pinned,
45 // thus attempting to pin or unpin them will not succeed and will always return false.
46 // For policies where moving is compusory, pin/unpin is impossible and will panic (is_object_pinned will return false).
47 // For policies that support pinning (eg. Immix), pin/unpin will return a boolean indicating that the
48 // pinning/unpinning action has been performed by the function, and is_object_pinned will return whether the object
49 // is currently pinned.
50 #[cfg(feature = "object_pinning")]
51 fn pin_object(&self, object: ObjectReference) -> bool;
52 #[cfg(feature = "object_pinning")]
53 fn unpin_object(&self, object: ObjectReference) -> bool;
54 #[cfg(feature = "object_pinning")]
55 fn is_object_pinned(&self, object: ObjectReference) -> bool;
56
57 /// Is the object movable, determined by the policy? E.g. the policy is non-moving,
58 /// or the object is pinned.
59 fn is_movable(&self) -> bool;
60
61 /// Is the object sane? A policy should return false if there is any abnormality about
62 /// object - the sanity checker will fail if an object is not sane.
63 #[cfg(feature = "sanity")]
64 fn is_sane(&self) -> bool;
65
66 /// Is the object managed by MMTk? For most cases, if we find the sft for an object, that means
67 /// the object is in the space and managed by MMTk. However, for some spaces, like MallocSpace,
68 /// we mark the entire chunk in the SFT table as a malloc space, but only some of the addresses
69 /// in the space contain actual MMTk objects. So they need a further check.
70 fn is_in_space(&self, _object: ObjectReference) -> bool {
71 true
72 }
73
74 /// Is `addr` a valid object reference to an object allocated in this space?
75 /// This default implementation works for all spaces that use MMTk's mapper to allocate memory.
76 /// Some spaces, like `MallocSpace`, use third-party libraries to allocate memory.
77 /// Such spaces needs to override this method.
78 #[cfg(feature = "is_mmtk_object")]
79 fn is_mmtk_object(&self, addr: Address) -> Option<ObjectReference>;
80
81 #[cfg(feature = "is_mmtk_object")]
82 fn find_object_from_internal_pointer(
83 &self,
84 ptr: Address,
85 max_search_bytes: usize,
86 ) -> Option<ObjectReference>;
87
88 /// Initialize object metadata (in the header, or in the side metadata).
89 ///
90 /// This method is called after an object is allocated. Specifically,
91 /// - The VM binding calls [`crate::MMTK::initialize_vm_space_object`] which calls this method
92 /// to set the metadata for the VM space.
93 /// - Objects in other spaces are allocated by mutators using an MMTk allocator.
94 /// `Mutator::post_alloc` will call this method after allocation.
95 fn initialize_object_metadata(&self, object: ObjectReference);
96
97 /// Trace objects through SFT. This along with [`SFTProcessEdges`](mmtk/scheduler/gc_work/SFTProcessEdges)
98 /// provides an easy way for most plans to trace objects without the need to implement any plan-specific
99 /// code. However, tracing objects for some policies are more complicated, and they do not provide an
100 /// implementation of this method. For example, mark compact space requires trace twice in each GC.
101 /// Immix has defrag trace and fast trace.
102 fn sft_trace_object(
103 &self,
104 // We use concrete type for `queue` because SFT doesn't support generic parameters,
105 // and SFTProcessEdges uses `VectorObjectQueue`.
106 queue: &mut VectorObjectQueue,
107 object: ObjectReference,
108 worker: GCWorkerMutRef,
109 ) -> ObjectReference;
110
111 /// Print debug info for the object. The implementer should print one line at a time so in case of an unexpected error,
112 /// we still print something.
113 fn debug_print_object_info(&self, _object: ObjectReference) {
114 println!("This policy does not implement debug_print_object_info.");
115 }
116}
117
118// Create erased VM refs for these types that will be used in `sft_trace_object()`.
119// In this way, we can store the refs with <VM> in SFT (which cannot have parameters with generic type parameters)
120
121use crate::util::erase_vm::define_erased_vm_mut_ref;
122define_erased_vm_mut_ref!(GCWorkerMutRef = GCWorker<VM>);
123
124/// Print debug info for SFT. Should be false when committed.
125pub const DEBUG_SFT: bool = cfg!(debug_assertions) && false;
126
127/// An empty entry for SFT.
128#[derive(Debug)]
129pub struct EmptySpaceSFT {}
130
131pub const EMPTY_SFT_NAME: &str = "empty";
132pub const EMPTY_SPACE_SFT: EmptySpaceSFT = EmptySpaceSFT {};
133
134impl SFT for EmptySpaceSFT {
135 fn name(&self) -> &'static str {
136 EMPTY_SFT_NAME
137 }
138 fn is_live(&self, object: ObjectReference) -> bool {
139 panic!(
140 "Called is_live() on {:x}, which maps to an empty space",
141 object
142 )
143 }
144 #[cfg(feature = "sanity")]
145 fn is_sane(&self) -> bool {
146 warn!("Object in empty space!");
147 false
148 }
149 #[cfg(feature = "object_pinning")]
150 fn pin_object(&self, _object: ObjectReference) -> bool {
151 panic!("Cannot pin/unpin objects of EmptySpace.")
152 }
153 #[cfg(feature = "object_pinning")]
154 fn unpin_object(&self, _object: ObjectReference) -> bool {
155 panic!("Cannot pin/unpin objects of EmptySpace.")
156 }
157 #[cfg(feature = "object_pinning")]
158 fn is_object_pinned(&self, _object: ObjectReference) -> bool {
159 false
160 }
161 fn is_movable(&self) -> bool {
162 /*
163 * FIXME steveb I think this should panic (ie the function should not
164 * be invoked on an empty space). However, JikesRVM currently does
165 * call this in an unchecked way and expects 'false' for out of bounds
166 * addresses. So until that is fixed upstream, we'll return false here.
167 *
168 * panic!("called is_movable() on empty space")
169 */
170 false
171 }
172 fn is_in_space(&self, _object: ObjectReference) -> bool {
173 false
174 }
175 #[cfg(feature = "is_mmtk_object")]
176 fn is_mmtk_object(&self, _addr: Address) -> Option<ObjectReference> {
177 None
178 }
179 #[cfg(feature = "is_mmtk_object")]
180 fn find_object_from_internal_pointer(
181 &self,
182 _ptr: Address,
183 _max_search_bytes: usize,
184 ) -> Option<ObjectReference> {
185 None
186 }
187
188 fn initialize_object_metadata(&self, object: ObjectReference) {
189 panic!(
190 "Called initialize_object_metadata() on {:x}, which maps to an empty space",
191 object
192 )
193 }
194
195 fn sft_trace_object(
196 &self,
197 _queue: &mut VectorObjectQueue,
198 object: ObjectReference,
199 _worker: GCWorkerMutRef,
200 ) -> ObjectReference {
201 // We do not have the `VM` type parameter here, so we cannot forward the call to the VM.
202 panic!(
203 "Call trace_object() on {}, which maps to an empty space. SFTProcessEdges does not support the fallback to vm_trace_object().",
204 object,
205 )
206 }
207}