mmtk/plan/
barriers.rs

1//! Read/Write barrier implementations.
2
3use crate::vm::slot::{MemorySlice, Slot};
4use crate::vm::ObjectModel;
5use crate::{
6    util::{metadata::MetadataSpec, *},
7    vm::VMBinding,
8};
9use atomic::Ordering;
10use downcast_rs::Downcast;
11
12/// BarrierSelector describes which barrier to use.
13///
14/// This is used as an *indicator* for each plan to enable the correct barrier.
15/// For example, immix can use this selector to enable different barriers for analysis.
16///
17/// VM bindings may also use this to enable the correct fast-path, if the fast-path is implemented in the binding.
18#[derive(Copy, Clone, Debug, PartialEq)]
19pub enum BarrierSelector {
20    /// No barrier is used.
21    NoBarrier,
22    /// Object remembering post-write barrier is used.
23    ObjectBarrier,
24    /// Object remembering pre-write barrier with weak reference loading barrier.
25    // TODO: We might be able to generalize this to object remembering pre-write barrier.
26    SATBBarrier,
27}
28
29impl BarrierSelector {
30    /// A const function to check if two barrier selectors are the same.
31    pub const fn equals(&self, other: BarrierSelector) -> bool {
32        // cast enum to u8 then compare. Otherwise, we cannot do it in a const fn.
33        *self as u8 == other as u8
34    }
35}
36
37/// A barrier is a combination of fast-path behaviour + slow-path semantics.
38/// This trait exposes generic barrier interfaces. The implementations will define their
39/// own fast-path code and slow-path semantics.
40///
41/// Normally, a binding will call these generic barrier interfaces (`object_reference_write` and `memory_region_copy`) for subsuming barrier calls.
42///
43/// If a subsuming barrier cannot be easily deployed due to platform limitations, the binding may chosse to call both `object_reference_write_pre` and `object_reference_write_post`
44/// barrier before and after the store operation.
45///
46/// As a performance optimization, the binding may also choose to port the fast-path to the VM side,
47/// and call the slow-path (`object_reference_write_slow`) only if necessary.
48pub trait Barrier<VM: VMBinding>: 'static + Send + Downcast {
49    /// Flush thread-local states like buffers or remembered sets.
50    fn flush(&mut self) {}
51
52    /// Weak reference loading barrier.  A mutator should call this when loading from a weak
53    /// reference field, for example, when executing  `java.lang.ref.Reference.get()` in JVM, or
54    /// loading from a global weak table in CRuby.
55    ///
56    /// Note: Merely loading from a field holding weak reference into a local variable will create a
57    /// strong reference from the stack to the referent, changing its reachablilty from weakly
58    /// reachable to strongly reachable.  Concurrent garbage collectors may need to handle such
59    /// events specially.  See [SATBBarrier::load_weak_reference] for a concrete example.
60    ///
61    /// Arguments:
62    /// *   `referent`: The referent object which the weak reference is pointing to.
63    fn load_weak_reference(&mut self, _referent: ObjectReference) {}
64
65    /// Subsuming barrier for object reference write
66    fn object_reference_write(
67        &mut self,
68        src: ObjectReference,
69        slot: VM::VMSlot,
70        target: ObjectReference,
71    ) {
72        self.object_reference_write_pre(src, slot, Some(target));
73        slot.store(target);
74        self.object_reference_write_post(src, slot, Some(target));
75    }
76
77    /// Full pre-barrier for object reference write
78    fn object_reference_write_pre(
79        &mut self,
80        _src: ObjectReference,
81        _slot: VM::VMSlot,
82        _target: Option<ObjectReference>,
83    ) {
84    }
85
86    /// Full post-barrier for object reference write
87    fn object_reference_write_post(
88        &mut self,
89        _src: ObjectReference,
90        _slot: VM::VMSlot,
91        _target: Option<ObjectReference>,
92    ) {
93    }
94
95    /// Object reference write slow-path call.
96    /// This can be called either before or after the store, depend on the concrete barrier implementation.
97    fn object_reference_write_slow(
98        &mut self,
99        _src: ObjectReference,
100        _slot: VM::VMSlot,
101        _target: Option<ObjectReference>,
102    ) {
103    }
104
105    /// Subsuming barrier for array copy
106    fn memory_region_copy(&mut self, src: VM::VMMemorySlice, dst: VM::VMMemorySlice) {
107        self.memory_region_copy_pre(src.clone(), dst.clone());
108        VM::VMMemorySlice::copy(&src, &dst);
109        self.memory_region_copy_post(src, dst);
110    }
111
112    /// Full pre-barrier for array copy
113    fn memory_region_copy_pre(&mut self, _src: VM::VMMemorySlice, _dst: VM::VMMemorySlice) {}
114
115    /// Full post-barrier for array copy
116    fn memory_region_copy_post(&mut self, _src: VM::VMMemorySlice, _dst: VM::VMMemorySlice) {}
117
118    /// A pre-barrier indicating that some fields of the object will probably be modified soon.
119    /// Specifically, the caller should ensure that:
120    ///     * The barrier must called before any field modification.
121    ///     * Some fields (unknown at the time of calling this barrier) might be modified soon, without a write barrier.
122    ///     * There are no safepoints between the barrier call and the field writes.
123    ///
124    /// **Example use case for mmtk-openjdk:**
125    ///
126    /// The OpenJDK C2 slowpath allocation code
127    /// can do deoptimization after the allocation and before returning to C2 compiled code.
128    /// The deoptimization itself contains a safepoint. For generational plans, if a GC
129    /// happens at this safepoint, the allocated object will be promoted, and all the
130    /// subsequent field initialization should be recorded.
131    ///
132    // TODO: Review any potential use cases for other VM bindings.
133    fn object_probable_write(&mut self, _obj: ObjectReference) {}
134}
135
136impl_downcast!(Barrier<VM> where VM: VMBinding);
137
138/// Empty barrier implementation.
139/// For GCs that do not need any barriers
140///
141/// Note that since NoBarrier noes nothing but the object field write itself, it has no slow-path semantics (i.e. an no-op slow-path).
142pub struct NoBarrier;
143
144impl<VM: VMBinding> Barrier<VM> for NoBarrier {}
145
146/// A barrier semantics defines the barrier slow-path behaviour. For example, how an object barrier processes it's modbufs.
147/// Specifically, it defines the slow-path call interfaces and a call to flush buffers.
148///
149/// A barrier is a combination of fast-path behaviour + slow-path semantics.
150/// The fast-path code will decide whether to call the slow-path calls.
151pub trait BarrierSemantics: 'static + Send {
152    type VM: VMBinding;
153
154    const UNLOG_BIT_SPEC: MetadataSpec =
155        *<Self::VM as VMBinding>::VMObjectModel::GLOBAL_LOG_BIT_SPEC.as_spec();
156
157    /// Flush thread-local buffers or remembered sets.
158    /// Normally this is called by the slow-path implementation whenever the thread-local buffers are full.
159    /// This will also be called externally by the VM, when the thread is being destroyed.
160    fn flush(&mut self);
161
162    /// Slow-path call for object field write operations.
163    fn object_reference_write_slow(
164        &mut self,
165        src: ObjectReference,
166        slot: <Self::VM as VMBinding>::VMSlot,
167        target: Option<ObjectReference>,
168    );
169
170    /// Slow-path call for mempry slice copy operations. For example, array-copy operations.
171    fn memory_region_copy_slow(
172        &mut self,
173        src: <Self::VM as VMBinding>::VMMemorySlice,
174        dst: <Self::VM as VMBinding>::VMMemorySlice,
175    );
176
177    /// Object will probably be modified
178    fn object_probable_write_slow(&mut self, _obj: ObjectReference) {}
179
180    /// Loading from a weak reference field
181    fn load_weak_reference(&mut self, _o: ObjectReference) {}
182}
183
184/// Generic object barrier with a type argument defining it's slow-path behaviour.
185pub struct ObjectBarrier<S: BarrierSemantics> {
186    semantics: S,
187}
188
189impl<S: BarrierSemantics> ObjectBarrier<S> {
190    /// Create a new ObjectBarrier with the given semantics.
191    pub fn new(semantics: S) -> Self {
192        Self { semantics }
193    }
194
195    /// Returns true if the object is not logged.
196    fn object_is_unlogged(&self, object: ObjectReference) -> bool {
197        S::UNLOG_BIT_SPEC.load_atomic::<S::VM, u8>(object, None, Ordering::SeqCst) != 0
198    }
199
200    /// Attempt to atomically log an object.
201    /// Returns true if the object is not logged previously.
202    fn log_object(&self, object: ObjectReference) -> bool {
203        #[cfg(all(feature = "vo_bit", feature = "extreme_assertions"))]
204        debug_assert!(
205            crate::util::metadata::vo_bit::is_vo_bit_set(object),
206            "object bit is unset"
207        );
208        loop {
209            let old_value =
210                S::UNLOG_BIT_SPEC.load_atomic::<S::VM, u8>(object, None, Ordering::SeqCst);
211            if old_value == 0 {
212                return false;
213            }
214            if S::UNLOG_BIT_SPEC
215                .compare_exchange_metadata::<S::VM, u8>(
216                    object,
217                    1,
218                    0,
219                    None,
220                    Ordering::SeqCst,
221                    Ordering::SeqCst,
222                )
223                .is_ok()
224            {
225                return true;
226            }
227        }
228    }
229}
230
231impl<S: BarrierSemantics> Barrier<S::VM> for ObjectBarrier<S> {
232    fn flush(&mut self) {
233        self.semantics.flush();
234    }
235
236    fn object_reference_write_post(
237        &mut self,
238        src: ObjectReference,
239        slot: <S::VM as VMBinding>::VMSlot,
240        target: Option<ObjectReference>,
241    ) {
242        if self.object_is_unlogged(src) {
243            self.object_reference_write_slow(src, slot, target);
244        }
245    }
246
247    fn object_reference_write_slow(
248        &mut self,
249        src: ObjectReference,
250        slot: <S::VM as VMBinding>::VMSlot,
251        target: Option<ObjectReference>,
252    ) {
253        if self.log_object(src) {
254            self.semantics
255                .object_reference_write_slow(src, slot, target);
256        }
257    }
258
259    fn memory_region_copy_post(
260        &mut self,
261        src: <S::VM as VMBinding>::VMMemorySlice,
262        dst: <S::VM as VMBinding>::VMMemorySlice,
263    ) {
264        self.semantics.memory_region_copy_slow(src, dst);
265    }
266
267    fn object_probable_write(&mut self, obj: ObjectReference) {
268        if self.object_is_unlogged(obj) {
269            self.semantics.object_probable_write_slow(obj);
270        }
271    }
272}
273
274/// A SATB (Snapshot-At-The-Beginning) barrier implementation.
275/// This barrier is basically a pre-write object barrier with a weak reference loading barrier.
276pub struct SATBBarrier<S: BarrierSemantics> {
277    weak_ref_barrier_enabled: bool,
278    semantics: S,
279}
280
281impl<S: BarrierSemantics> SATBBarrier<S> {
282    /// Create a new SATBBarrier with the given semantics.
283    pub fn new(semantics: S) -> Self {
284        Self {
285            weak_ref_barrier_enabled: false,
286            semantics,
287        }
288    }
289
290    pub(crate) fn set_weak_ref_barrier_enabled(&mut self, value: bool) {
291        self.weak_ref_barrier_enabled = value;
292    }
293
294    fn object_is_unlogged(&self, object: ObjectReference) -> bool {
295        S::UNLOG_BIT_SPEC.load_atomic::<S::VM, u8>(object, None, Ordering::SeqCst) != 0
296    }
297}
298
299impl<S: BarrierSemantics> Barrier<S::VM> for SATBBarrier<S> {
300    fn flush(&mut self) {
301        self.semantics.flush();
302    }
303
304    fn load_weak_reference(&mut self, o: ObjectReference) {
305        if self.weak_ref_barrier_enabled {
306            self.semantics.load_weak_reference(o)
307        }
308    }
309
310    fn object_probable_write(&mut self, obj: ObjectReference) {
311        self.semantics.object_probable_write_slow(obj);
312    }
313
314    fn object_reference_write_pre(
315        &mut self,
316        src: ObjectReference,
317        slot: <S::VM as VMBinding>::VMSlot,
318        target: Option<ObjectReference>,
319    ) {
320        if self.object_is_unlogged(src) {
321            self.semantics
322                .object_reference_write_slow(src, slot, target);
323        }
324    }
325
326    fn object_reference_write_post(
327        &mut self,
328        _src: ObjectReference,
329        _slot: <S::VM as VMBinding>::VMSlot,
330        _target: Option<ObjectReference>,
331    ) {
332        unimplemented!()
333    }
334
335    fn object_reference_write_slow(
336        &mut self,
337        src: ObjectReference,
338        slot: <S::VM as VMBinding>::VMSlot,
339        target: Option<ObjectReference>,
340    ) {
341        self.semantics
342            .object_reference_write_slow(src, slot, target);
343    }
344
345    fn memory_region_copy_pre(
346        &mut self,
347        src: <S::VM as VMBinding>::VMMemorySlice,
348        dst: <S::VM as VMBinding>::VMMemorySlice,
349    ) {
350        self.semantics.memory_region_copy_slow(src, dst);
351    }
352
353    fn memory_region_copy_post(
354        &mut self,
355        _src: <S::VM as VMBinding>::VMMemorySlice,
356        _dst: <S::VM as VMBinding>::VMMemorySlice,
357    ) {
358        unimplemented!()
359    }
360}