1use crate::util::copy::*;
2use crate::util::metadata::MetadataSpec;
3use crate::util::{constants, ObjectReference};
4use crate::vm::ObjectModel;
5use crate::vm::VMBinding;
6use std::sync::atomic::Ordering;
7
8const FORWARDING_NOT_TRIGGERED_YET: u8 = 0b00;
9const BEING_FORWARDED: u8 = 0b10;
10const FORWARDED: u8 = 0b11;
11const FORWARDING_MASK: u8 = 0b11;
12#[allow(unused)]
13const FORWARDING_BITS: usize = 2;
14
15#[cfg(target_pointer_width = "64")]
17const FORWARDING_POINTER_MASK: usize = 0x00ff_ffff_ffff_fff8;
18#[cfg(target_pointer_width = "32")]
19const FORWARDING_POINTER_MASK: usize = 0xffff_fffc;
20
21pub fn attempt_to_forward<VM: VMBinding>(object: ObjectReference) -> u8 {
24 loop {
25 let old_value = get_forwarding_status::<VM>(object);
26 if old_value != FORWARDING_NOT_TRIGGERED_YET
27 || VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC
28 .compare_exchange_metadata::<VM, u8>(
29 object,
30 old_value,
31 BEING_FORWARDED,
32 None,
33 Ordering::SeqCst,
34 Ordering::Relaxed,
35 )
36 .is_ok()
37 {
38 return old_value;
39 }
40 }
41}
42
43pub fn spin_and_get_forwarded_object<VM: VMBinding>(
53 object: ObjectReference,
54 forwarding_bits: u8,
55) -> ObjectReference {
56 let mut forwarding_bits = forwarding_bits;
57 while forwarding_bits == BEING_FORWARDED {
58 forwarding_bits = get_forwarding_status::<VM>(object);
59 }
60
61 if forwarding_bits == FORWARDED {
62 read_forwarding_pointer::<VM>(object)
63 } else {
64 debug_assert!(
68 forwarding_bits == FORWARDING_NOT_TRIGGERED_YET,
69 "Invalid/Corrupted forwarding word {:x} for object {}",
70 forwarding_bits,
71 object,
72 );
73 object
74 }
75}
76
77pub fn try_forward_object<VM: VMBinding>(
78 object: ObjectReference,
79 semantics: CopySemantics,
80 copy_context: &mut GCWorkerCopyContext<VM>,
81 on_after_forwarding: impl FnOnce(ObjectReference),
82) -> Option<ObjectReference> {
83 let new_object = VM::VMObjectModel::try_copy(object, semantics, copy_context)?;
84 on_after_forwarding(new_object);
85 if let Some(shift) = forwarding_bits_offset_in_forwarding_pointer::<VM>() {
86 VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::<VM, usize>(
87 object,
88 new_object.to_raw_address().as_usize() | ((FORWARDED as usize) << shift),
89 None,
90 Ordering::SeqCst,
91 )
92 } else {
93 write_forwarding_pointer::<VM>(object, new_object);
94 VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.store_atomic::<VM, u8>(
95 object,
96 FORWARDED,
97 None,
98 Ordering::SeqCst,
99 );
100 }
101 Some(new_object)
102}
103
104pub fn forward_object<VM: VMBinding>(
121 object: ObjectReference,
122 semantics: CopySemantics,
123 copy_context: &mut GCWorkerCopyContext<VM>,
124 on_after_forwarding: impl FnOnce(ObjectReference),
125) -> ObjectReference {
126 let new_object = VM::VMObjectModel::copy(object, semantics, copy_context);
127 on_after_forwarding(new_object);
128 if let Some(shift) = forwarding_bits_offset_in_forwarding_pointer::<VM>() {
129 VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::<VM, usize>(
130 object,
131 new_object.to_raw_address().as_usize() | ((FORWARDED as usize) << shift),
132 None,
133 Ordering::SeqCst,
134 )
135 } else {
136 write_forwarding_pointer::<VM>(object, new_object);
137 VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.store_atomic::<VM, u8>(
138 object,
139 FORWARDED,
140 None,
141 Ordering::SeqCst,
142 );
143 }
144 new_object
145}
146
147pub fn get_forwarding_status<VM: VMBinding>(object: ObjectReference) -> u8 {
149 VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.load_atomic::<VM, u8>(
150 object,
151 None,
152 Ordering::SeqCst,
153 )
154}
155
156pub fn is_forwarded<VM: VMBinding>(object: ObjectReference) -> bool {
157 get_forwarding_status::<VM>(object) == FORWARDED
158}
159
160pub fn is_being_forwarded<VM: VMBinding>(object: ObjectReference) -> bool {
161 get_forwarding_status::<VM>(object) == BEING_FORWARDED
162}
163
164pub fn is_forwarded_or_being_forwarded<VM: VMBinding>(object: ObjectReference) -> bool {
165 get_forwarding_status::<VM>(object) != FORWARDING_NOT_TRIGGERED_YET
166}
167
168pub fn state_is_forwarded_or_being_forwarded(forwarding_bits: u8) -> bool {
169 forwarding_bits != FORWARDING_NOT_TRIGGERED_YET
170}
171
172pub fn state_is_being_forwarded(forwarding_bits: u8) -> bool {
173 forwarding_bits == BEING_FORWARDED
174}
175
176pub fn clear_forwarding_bits<VM: VMBinding>(object: ObjectReference) {
179 VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.store_atomic::<VM, u8>(
180 object,
181 0,
182 None,
183 Ordering::SeqCst,
184 )
185}
186
187pub fn read_forwarding_pointer<VM: VMBinding>(object: ObjectReference) -> ObjectReference {
190 debug_assert!(
191 is_forwarded_or_being_forwarded::<VM>(object),
192 "read_forwarding_pointer called for object {:?} that has not started forwarding!",
193 object,
194 );
195
196 unsafe {
198 ObjectReference::from_raw_address_unchecked(crate::util::Address::from_usize(
201 VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.load_atomic::<VM, usize>(
202 object,
203 Some(FORWARDING_POINTER_MASK),
204 Ordering::SeqCst,
205 ),
206 ))
207 }
208}
209
210pub fn write_forwarding_pointer<VM: VMBinding>(
213 object: ObjectReference,
214 new_object: ObjectReference,
215) {
216 debug_assert!(
217 is_being_forwarded::<VM>(object),
218 "write_forwarding_pointer called for object {:?} that is not being forwarded! Forwarding state = 0x{:x}",
219 object,
220 get_forwarding_status::<VM>(object),
221 );
222
223 trace!("write_forwarding_pointer({}, {})", object, new_object);
224 VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.store_atomic::<VM, usize>(
225 object,
226 new_object.to_raw_address().as_usize(),
227 Some(FORWARDING_POINTER_MASK),
228 Ordering::SeqCst,
229 )
230}
231
232#[cfg(target_endian = "little")]
240pub(super) fn forwarding_bits_offset_in_forwarding_pointer<VM: VMBinding>() -> Option<isize> {
241 use std::ops::Deref;
242 match (
244 VM::VMObjectModel::LOCAL_FORWARDING_POINTER_SPEC.deref(),
245 VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.deref(),
246 ) {
247 (MetadataSpec::InHeader(fp), MetadataSpec::InHeader(fb)) => {
248 let maybe_shift = fb.bit_offset - fp.bit_offset;
249 if maybe_shift >= 0 && maybe_shift < constants::BITS_IN_WORD as isize {
250 Some(maybe_shift)
251 } else {
252 None
253 }
254 }
255 _ => None,
256 }
257}
258
259#[cfg(target_endian = "big")]
260pub(super) fn forwarding_bits_offset_in_forwarding_pointer<VM: VMBinding>() -> Option<isize> {
261 unimplemented!()
262}
263
264pub(crate) fn debug_print_object_forwarding_info<VM: VMBinding>(object: ObjectReference) {
265 let forwarding_bits = get_forwarding_status::<VM>(object);
266 println!(
267 "forwarding bits = {:?}, forwarding pointer = {:?}",
268 forwarding_bits,
269 if state_is_forwarded_or_being_forwarded(forwarding_bits) {
270 Some(read_forwarding_pointer::<VM>(object))
271 } else {
272 None
273 }
274 )
275}