1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
use crate::plan::VectorObjectQueue;
use crate::scheduler::GCWorker;
use crate::util::*;
use crate::vm::VMBinding;
use std::marker::PhantomData;

/// Space Function Table (SFT).
///
/// This trait captures functions that reflect _space-specific per-object
/// semantics_.   These functions are implemented for each object via a special
/// space-based dynamic dispatch mechanism where the semantics are _not_
/// determined by the object's _type_, but rather, are determined by the _space_
/// that the object is in.
///
/// The underlying mechanism exploits the fact that spaces use the address space
/// at an MMTk chunk granularity with the consequence that each chunk maps to
/// exactluy one space, so knowing the chunk for an object reveals its space.
/// The dispatch then works by performing simple address arithmetic on the object
/// reference to find a chunk index which is used to index a table which returns
/// the space.   The relevant function is then dispatched against that space
/// object.
///
/// We use the SFT trait to simplify typing for Rust, so our table is a
/// table of SFT rather than Space.
pub trait SFT {
    /// The space name
    fn name(&self) -> &'static str;

    /// Get forwarding pointer if the object is forwarded.
    fn get_forwarded_object(&self, _object: ObjectReference) -> Option<ObjectReference> {
        None
    }

    /// Is the object live, determined by the policy?
    fn is_live(&self, object: ObjectReference) -> bool;

    /// Is the object reachable, determined by the policy?
    /// Note: Objects in ImmortalSpace may have `is_live = true` but are actually unreachable.
    fn is_reachable(&self, object: ObjectReference) -> bool {
        self.is_live(object)
    }

    // Functions for pinning/unpining and checking if an object is pinned
    // For non moving policies, all the objects are considered as forever pinned,
    // thus attempting to pin or unpin them will not succeed and will always return false.
    // For policies where moving is compusory, pin/unpin is impossible and will panic (is_object_pinned will return false).
    // For policies that support pinning (eg. Immix), pin/unpin will return a boolean indicating that the
    // pinning/unpinning action has been performed by the function, and is_object_pinned will return whether the object
    // is currently pinned.
    #[cfg(feature = "object_pinning")]
    fn pin_object(&self, object: ObjectReference) -> bool;
    #[cfg(feature = "object_pinning")]
    fn unpin_object(&self, object: ObjectReference) -> bool;
    #[cfg(feature = "object_pinning")]
    fn is_object_pinned(&self, object: ObjectReference) -> bool;

    /// Is the object movable, determined by the policy? E.g. the policy is non-moving,
    /// or the object is pinned.
    fn is_movable(&self) -> bool;

    /// Is the object sane? A policy should return false if there is any abnormality about
    /// object - the sanity checker will fail if an object is not sane.
    #[cfg(feature = "sanity")]
    fn is_sane(&self) -> bool;

    /// Is the object managed by MMTk? For most cases, if we find the sft for an object, that means
    /// the object is in the space and managed by MMTk. However, for some spaces, like MallocSpace,
    /// we mark the entire chunk in the SFT table as a malloc space, but only some of the addresses
    /// in the space contain actual MMTk objects. So they need a further check.
    fn is_in_space(&self, _object: ObjectReference) -> bool {
        true
    }

    /// Is `addr` a valid object reference to an object allocated in this space?
    /// This default implementation works for all spaces that use MMTk's mapper to allocate memory.
    /// Some spaces, like `MallocSpace`, use third-party libraries to allocate memory.
    /// Such spaces needs to override this method.
    #[cfg(feature = "is_mmtk_object")]
    fn is_mmtk_object(&self, addr: Address) -> Option<ObjectReference>;

    #[cfg(feature = "is_mmtk_object")]
    fn find_object_from_internal_pointer(
        &self,
        ptr: Address,
        max_search_bytes: usize,
    ) -> Option<ObjectReference>;

    /// Initialize object metadata (in the header, or in the side metadata).
    fn initialize_object_metadata(&self, object: ObjectReference, alloc: bool);

    /// Trace objects through SFT. This along with [`SFTProcessEdges`](mmtk/scheduler/gc_work/SFTProcessEdges)
    /// provides an easy way for most plans to trace objects without the need to implement any plan-specific
    /// code. However, tracing objects for some policies are more complicated, and they do not provide an
    /// implementation of this method. For example, mark compact space requires trace twice in each GC.
    /// Immix has defrag trace and fast trace.
    fn sft_trace_object(
        &self,
        // We use concrete type for `queue` because SFT doesn't support generic parameters,
        // and SFTProcessEdges uses `VectorObjectQueue`.
        queue: &mut VectorObjectQueue,
        object: ObjectReference,
        worker: GCWorkerMutRef,
    ) -> ObjectReference;
}

// Create erased VM refs for these types that will be used in `sft_trace_object()`.
// In this way, we can store the refs with <VM> in SFT (which cannot have parameters with generic type parameters)

use crate::util::erase_vm::define_erased_vm_mut_ref;
define_erased_vm_mut_ref!(GCWorkerMutRef = GCWorker<VM>);

/// Print debug info for SFT. Should be false when committed.
pub const DEBUG_SFT: bool = cfg!(debug_assertions) && false;

/// An empty entry for SFT.
#[derive(Debug)]
pub struct EmptySpaceSFT {}

pub const EMPTY_SFT_NAME: &str = "empty";
pub const EMPTY_SPACE_SFT: EmptySpaceSFT = EmptySpaceSFT {};

impl SFT for EmptySpaceSFT {
    fn name(&self) -> &'static str {
        EMPTY_SFT_NAME
    }
    fn is_live(&self, object: ObjectReference) -> bool {
        panic!(
            "Called is_live() on {:x}, which maps to an empty space",
            object
        )
    }
    #[cfg(feature = "sanity")]
    fn is_sane(&self) -> bool {
        warn!("Object in empty space!");
        false
    }
    #[cfg(feature = "object_pinning")]
    fn pin_object(&self, _object: ObjectReference) -> bool {
        panic!("Cannot pin/unpin objects of EmptySpace.")
    }
    #[cfg(feature = "object_pinning")]
    fn unpin_object(&self, _object: ObjectReference) -> bool {
        panic!("Cannot pin/unpin objects of EmptySpace.")
    }
    #[cfg(feature = "object_pinning")]
    fn is_object_pinned(&self, _object: ObjectReference) -> bool {
        false
    }
    fn is_movable(&self) -> bool {
        /*
         * FIXME steveb I think this should panic (ie the function should not
         * be invoked on an empty space).   However, JikesRVM currently does
         * call this in an unchecked way and expects 'false' for out of bounds
         * addresses.  So until that is fixed upstream, we'll return false here.
         *
         * panic!("called is_movable() on empty space")
         */
        false
    }
    fn is_in_space(&self, _object: ObjectReference) -> bool {
        false
    }
    #[cfg(feature = "is_mmtk_object")]
    fn is_mmtk_object(&self, _addr: Address) -> Option<ObjectReference> {
        None
    }
    #[cfg(feature = "is_mmtk_object")]
    fn find_object_from_internal_pointer(
        &self,
        _ptr: Address,
        _max_search_bytes: usize,
    ) -> Option<ObjectReference> {
        None
    }

    fn initialize_object_metadata(&self, object: ObjectReference, _alloc: bool) {
        panic!(
            "Called initialize_object_metadata() on {:x}, which maps to an empty space",
            object
        )
    }

    fn sft_trace_object(
        &self,
        _queue: &mut VectorObjectQueue,
        object: ObjectReference,
        _worker: GCWorkerMutRef,
    ) -> ObjectReference {
        // We do not have the `VM` type parameter here, so we cannot forward the call to the VM.
        panic!(
            "Call trace_object() on {}, which maps to an empty space. SFTProcessEdges does not support the fallback to vm_trace_object().",
            object,
        )
    }
}