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