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
287impl<S: BarrierSemantics> Barrier<S::VM> for FieldBarrier<S> {
288    fn flush(&mut self) {
289        self.semantics.flush();
290    }
291
292    fn load_weak_reference(&mut self, o: ObjectReference) {
293        self.semantics.load_weak_reference(o)
294    }
295
296    fn object_probable_write(&mut self, obj: ObjectReference) {
297        self.semantics.object_probable_write_slow(obj);
298    }
299
300    fn object_reference_write_pre(
301        &mut self,
302        src: ObjectReference,
303        slot: <S::VM as VMBinding>::VMSlot,
304        target: Option<ObjectReference>,
305    ) {
306        self.semantics
307            .object_reference_write_slow(src, slot, target);
308    }
309
310    fn object_reference_write_post(
311        &mut self,
312        _src: ObjectReference,
313        _slot: <S::VM as VMBinding>::VMSlot,
314        _target: Option<ObjectReference>,
315    ) {
316        unimplemented!()
317    }
318
319    fn object_reference_write_slow(
320        &mut self,
321        src: ObjectReference,
322        slot: <S::VM as VMBinding>::VMSlot,
323        target: Option<ObjectReference>,
324    ) {
325        self.semantics
326            .object_reference_write_slow(src, slot, target);
327    }
328
329    fn memory_region_copy_pre(
330        &mut self,
331        src: <S::VM as VMBinding>::VMMemorySlice,
332        dst: <S::VM as VMBinding>::VMMemorySlice,
333    ) {
334        self.semantics.memory_region_copy_slow(src, dst);
335    }
336
337    fn memory_region_copy_post(
338        &mut self,
339        _src: <S::VM as VMBinding>::VMMemorySlice,
340        _dst: <S::VM as VMBinding>::VMMemorySlice,
341    ) {
342        unimplemented!()
343    }
344}
345
346/// A SATB (Snapshot-At-The-Beginning) barrier implementation.
347/// This barrier is basically a pre-write object barrier with a weak reference loading barrier.
348pub struct SATBBarrier<S: BarrierSemantics> {
349    weak_ref_barrier_enabled: bool,
350    semantics: S,
351}
352
353impl<S: BarrierSemantics> SATBBarrier<S> {
354    /// Create a new SATBBarrier with the given semantics.
355    pub fn new(semantics: S) -> Self {
356        Self {
357            weak_ref_barrier_enabled: false,
358            semantics,
359        }
360    }
361
362    pub(crate) fn set_weak_ref_barrier_enabled(&mut self, value: bool) {
363        self.weak_ref_barrier_enabled = value;
364    }
365
366    fn object_is_unlogged(&self, object: ObjectReference) -> bool {
367        S::UNLOG_BIT_SPEC.load_atomic::<S::VM, u8>(object, None, Ordering::SeqCst) != 0
368    }
369}
370
371impl<S: BarrierSemantics> Barrier<S::VM> for SATBBarrier<S> {
372    fn flush(&mut self) {
373        self.semantics.flush();
374    }
375
376    fn load_weak_reference(&mut self, o: ObjectReference) {
377        if self.weak_ref_barrier_enabled {
378            self.semantics.load_weak_reference(o)
379        }
380    }
381
382    fn object_probable_write(&mut self, obj: ObjectReference) {
383        self.semantics.object_probable_write_slow(obj);
384    }
385
386    fn object_reference_write_pre(
387        &mut self,
388        src: ObjectReference,
389        slot: <S::VM as VMBinding>::VMSlot,
390        target: Option<ObjectReference>,
391    ) {
392        if self.object_is_unlogged(src) {
393            self.semantics
394                .object_reference_write_slow(src, slot, target);
395        }
396    }
397
398    fn object_reference_write_post(
399        &mut self,
400        _src: ObjectReference,
401        _slot: <S::VM as VMBinding>::VMSlot,
402        _target: Option<ObjectReference>,
403    ) {
404        unimplemented!()
405    }
406
407    fn object_reference_write_slow(
408        &mut self,
409        src: ObjectReference,
410        slot: <S::VM as VMBinding>::VMSlot,
411        target: Option<ObjectReference>,
412    ) {
413        self.semantics
414            .object_reference_write_slow(src, slot, target);
415    }
416
417    fn memory_region_copy_pre(
418        &mut self,
419        src: <S::VM as VMBinding>::VMMemorySlice,
420        dst: <S::VM as VMBinding>::VMMemorySlice,
421    ) {
422        self.semantics.memory_region_copy_slow(src, dst);
423    }
424
425    fn memory_region_copy_post(
426        &mut self,
427        _src: <S::VM as VMBinding>::VMMemorySlice,
428        _dst: <S::VM as VMBinding>::VMMemorySlice,
429    ) {
430        unimplemented!()
431    }
432}