mmtk/policy/
sft.rs

1use crate::plan::tracing::OptionObjectQueue;
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    /// Pin a given object. Return if this call pinned the given object.
44    ///
45    /// Note that this may be a no-op (i.e. always return `false`) for some
46    /// policies (such as immortal or non-moving) and may panic for policies
47    /// where pinning is unsupported (such as fully copying spaces like
48    /// `CopySpace`).
49    #[cfg(feature = "object_pinning")]
50    fn pin_object(&self, object: ObjectReference) -> bool;
51
52    /// Unpin a given object. Return if this call unpinned the given object.
53    ///
54    /// Note that this may be a no-op (i.e. always return `false`) for some
55    /// policies (such as immortal or non-moving) and may panic for policies
56    /// where pinning is unsupported (such as fully copying spaces like
57    /// `CopySpace`).
58    #[cfg(feature = "object_pinning")]
59    fn unpin_object(&self, object: ObjectReference) -> bool;
60
61    /// Return if the given object is pinned.
62    ///
63    /// Note that this may be a no-op (i.e. always return `true`) for some
64    /// policies (such as immortal or non-moving) and may always return `false`
65    /// for policies where pinnning is unsupported (such as fully copying spaces
66    /// like `CopySpace`).
67    #[cfg(feature = "object_pinning")]
68    fn is_object_pinned(&self, object: ObjectReference) -> bool;
69
70    /// Is the object movable, determined by the policy? E.g. the policy is non-moving,
71    /// or the object is pinned.
72    fn is_movable(&self) -> bool;
73
74    /// Is the object sane? A policy should return false if there is any abnormality about
75    /// object - the sanity checker will fail if an object is not sane.
76    #[cfg(feature = "sanity")]
77    fn is_sane(&self) -> bool;
78
79    /// Is the object managed by MMTk? For most cases, if we find the sft for an object, that means
80    /// the object is in the space and managed by MMTk. However, for some spaces, like MallocSpace,
81    /// we mark the entire chunk in the SFT table as a malloc space, but only some of the addresses
82    /// in the space contain actual MMTk objects. So they need a further check.
83    fn is_in_space(&self, _object: ObjectReference) -> bool {
84        true
85    }
86
87    /// Is `addr` a valid object reference to an object allocated in this space?
88    /// This default implementation works for all spaces that use MMTk's mapper to allocate memory.
89    /// Some spaces, like `MallocSpace`, use third-party libraries to allocate memory.
90    /// Such spaces needs to override this method.
91    #[cfg(feature = "vo_bit")]
92    fn is_mmtk_object(&self, addr: Address) -> Option<ObjectReference>;
93
94    #[cfg(feature = "vo_bit")]
95    fn find_object_from_internal_pointer(
96        &self,
97        ptr: Address,
98        max_search_bytes: usize,
99    ) -> Option<ObjectReference>;
100
101    /// Initialize object metadata (in the header, or in the side metadata).
102    ///
103    /// This method is called after an object is allocated.  Specifically,
104    /// -   The VM binding calls [`crate::MMTK::initialize_vm_space_object`] which calls this method
105    ///     to set the metadata for the VM space.
106    /// -   Objects in other spaces are allocated by mutators using an MMTk allocator.
107    ///     `Mutator::post_alloc` will call this method after allocation.
108    fn initialize_object_metadata(&self, object: ObjectReference, _bytes: usize);
109
110    /// Trace objects through SFT. This along with [`crate::plan::tracing::SFTTrace`]
111    /// provides an easy way for most plans to trace objects without the need to implement any plan-specific
112    /// code. However, tracing objects for some policies are more complicated, and they do not provide an
113    /// implementation of this method. For example, mark compact space requires trace twice in each GC.
114    /// Immix has defrag trace and fast trace.
115    fn sft_trace_object(
116        &self,
117        // We use `OptionObjectQueue`, the simplest `ObjectQueue` implementation, for `queue`
118        // because SFT doesn't support generic parameters.  The generic `SFTTrace::trace_object`
119        // method wraps `SFT::sft_trace_object` and forwards the enqueued object to the actual
120        // queue.
121        queue: &mut OptionObjectQueue,
122        object: ObjectReference,
123        worker: GCWorkerMutRef,
124    ) -> ObjectReference;
125
126    /// Print debug info for the object. The implementer should print one line at a time so in case of an unexpected error,
127    /// we still print something.
128    fn debug_print_object_info(&self, _object: ObjectReference) {
129        println!("This policy does not implement debug_print_object_info.");
130    }
131}
132
133// Create erased VM refs for these types that will be used in `sft_trace_object()`.
134// In this way, we can store the refs with <VM> in SFT (which cannot have parameters with generic type parameters)
135
136use crate::util::erase_vm::define_erased_vm_mut_ref;
137define_erased_vm_mut_ref!(GCWorkerMutRef = GCWorker<VM>);
138
139/// Print debug info for SFT. Should be false when committed.
140pub const DEBUG_SFT: bool = cfg!(debug_assertions) && false;
141
142/// An empty entry for SFT.
143#[derive(Debug)]
144pub struct EmptySpaceSFT {}
145
146pub const EMPTY_SFT_NAME: &str = "empty";
147pub const EMPTY_SPACE_SFT: EmptySpaceSFT = EmptySpaceSFT {};
148
149impl SFT for EmptySpaceSFT {
150    fn name(&self) -> &'static str {
151        EMPTY_SFT_NAME
152    }
153    fn is_live(&self, object: ObjectReference) -> bool {
154        panic!(
155            "Called is_live() on {:x}, which maps to an empty space",
156            object
157        )
158    }
159    #[cfg(feature = "sanity")]
160    fn is_sane(&self) -> bool {
161        warn!("Object in empty space!");
162        false
163    }
164    #[cfg(feature = "object_pinning")]
165    fn pin_object(&self, _object: ObjectReference) -> bool {
166        panic!("Cannot pin/unpin objects of EmptySpace.")
167    }
168    #[cfg(feature = "object_pinning")]
169    fn unpin_object(&self, _object: ObjectReference) -> bool {
170        panic!("Cannot pin/unpin objects of EmptySpace.")
171    }
172    #[cfg(feature = "object_pinning")]
173    fn is_object_pinned(&self, _object: ObjectReference) -> bool {
174        false
175    }
176    fn is_movable(&self) -> bool {
177        /*
178         * FIXME steveb I think this should panic (ie the function should not
179         * be invoked on an empty space).   However, JikesRVM currently does
180         * call this in an unchecked way and expects 'false' for out of bounds
181         * addresses.  So until that is fixed upstream, we'll return false here.
182         *
183         * panic!("called is_movable() on empty space")
184         */
185        false
186    }
187    fn is_in_space(&self, _object: ObjectReference) -> bool {
188        false
189    }
190    #[cfg(feature = "vo_bit")]
191    fn is_mmtk_object(&self, _addr: Address) -> Option<ObjectReference> {
192        None
193    }
194    #[cfg(feature = "vo_bit")]
195    fn find_object_from_internal_pointer(
196        &self,
197        _ptr: Address,
198        _max_search_bytes: usize,
199    ) -> Option<ObjectReference> {
200        None
201    }
202
203    fn initialize_object_metadata(&self, object: ObjectReference, _bytes: usize) {
204        panic!(
205            "Called initialize_object_metadata() on {:x}, which maps to an empty space",
206            object
207        )
208    }
209
210    fn sft_trace_object(
211        &self,
212        _queue: &mut OptionObjectQueue,
213        object: ObjectReference,
214        _worker: GCWorkerMutRef,
215    ) -> ObjectReference {
216        // We do not have the `VM` type parameter here, so we cannot forward the call to the VM.
217        panic!(
218            "Call trace_object() on {}, which maps to an empty space. SFTTrace does not support the fallback to vm_trace_object().",
219            object,
220        )
221    }
222}