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}